select rewrite (incomplete)

This commit is contained in:
Cory LaViska
2022-12-17 11:27:30 -05:00
parent 59cd70ae6f
commit 6afc3ba12e
13 changed files with 884 additions and 852 deletions

View File

@@ -66,6 +66,7 @@
- [Tooltip](/components/tooltip)
- [Tree](/components/tree)
- [Tree Item](/components/tree-item)
- [Option](/components/option)
<!--plop:component-->
- Utilities

21
docs/components/option.md Normal file
View File

@@ -0,0 +1,21 @@
# Option
[component-header:sl-option]
A description of the component goes here.
```html preview
<sl-option></sl-option>
```
## Examples
### First Example
TODO
### Second Example
TODO
[component-metadata:sl-option]

View File

@@ -4,28 +4,42 @@
```html preview
<sl-select>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
<sl-divider></sl-divider>
<sl-menu-item value="option-4">Option 4</sl-menu-item>
<sl-menu-item value="option-5">Option 5</sl-menu-item>
<sl-menu-item value="option-6">Option 6</sl-menu-item>
<sl-option value="option-4">Option 4</sl-option>
<sl-option value="option-5">Option 5</sl-option>
<sl-option value="option-6">Option 6</sl-option>
<sl-option value="option-7">Option 7</sl-option>
<sl-option value="option-8">Option 8</sl-option>
<sl-option value="option-9">Option 9</sl-option>
<sl-option value="option-10">Option 10</sl-option>
<sl-option value="option-11">Option 11</sl-option>
<sl-option value="option-12">Option 12</sl-option>
<sl-option value="option-13">Option 13</sl-option>
<sl-option value="option-14">Option 14</sl-option>
<sl-option value="option-15">Option 15</sl-option>
<sl-option value="option-16">Option 16</sl-option>
<sl-option value="option-17">Option 17</sl-option>
<sl-option value="option-18">Option 18</sl-option>
<sl-option value="option-19">Option 19</sl-option>
<sl-option value="option-20">Option 20</sl-option>
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlDivider, SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
<SlDivider />
<SlMenuItem value="option-4">Option 4</SlMenuItem>
<SlMenuItem value="option-5">Option 5</SlMenuItem>
<SlMenuItem value="option-6">Option 6</SlMenuItem>
<SlOption value="option-4">Option 4</SlOption>
<SlOption value="option-5">Option 5</SlOption>
<SlOption value="option-6">Option 6</SlOption>
</SlSelect>
);
```
@@ -40,20 +54,20 @@ Use the `label` attribute to give the select an accessible label. For labels tha
```html preview
<sl-select label="Select one">
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect label="Select one">
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
@@ -64,20 +78,20 @@ Add descriptive help text to a select with the `help-text` attribute. For help t
```html preview
<sl-select label="Experience" help-text="Please tell us your skill level.">
<sl-menu-item value="1">Novice</sl-menu-item>
<sl-menu-item value="2">Intermediate</sl-menu-item>
<sl-menu-item value="3">Advanced</sl-menu-item>
<sl-option value="1">Novice</sl-option>
<sl-option value="2">Intermediate</sl-option>
<sl-option value="3">Advanced</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect label="Experience" help-text="Please tell us your skill level.">
<SlMenuItem value="1">Novice</SlMenuItem>
<SlMenuItem value="2">Intermediate</SlMenuItem>
<SlMenuItem value="3">Advanced</SlMenuItem>
<SlOption value="1">Novice</SlOption>
<SlOption value="2">Intermediate</SlOption>
<SlOption value="3">Advanced</SlOption>
</SlSelect>
);
```
@@ -88,44 +102,44 @@ Use the `placeholder` attribute to add a placeholder.
```html preview
<sl-select placeholder="Select one">
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Select one">
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Clearable
Use the `clearable` attribute to make the control clearable.
Use the `clearable` attribute to make the control clearable. The clear button only appears when an option is selected.
```html preview
<sl-select placeholder="Clearable" clearable>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-select clearable value="option-1">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Clearable" clearable>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
@@ -136,20 +150,20 @@ Add the `filled` attribute to draw a filled select.
```html preview
<sl-select filled>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect filled>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
@@ -160,20 +174,20 @@ Use the `pill` attribute to give selects rounded edges.
```html preview
<sl-select pill>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect pill>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
@@ -184,58 +198,58 @@ Use the `disabled` attribute to disable a select.
```html preview
<sl-select placeholder="Disabled" disabled>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Disabled" disabled>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Setting the Selection
Use the `value` attribute to set the current selection. When users interact with the control, its `value` will update to reflect the newly selected menu item's value. Note that the value must be an array when using the [`multiple`](#multiple) option.
Use the `value` attribute to set the current selection. When users interact with the control, its `value` will update to reflect the newly selected menu item's value.
```html preview
<sl-select value="option-2">
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlDivider, SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect value="option-2">
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
);
```
### Setting the Selection Imperatively
To programmatically set the selection, update the `value` property as shown below. Note that the value must be an array when using the [`multiple`](#multiple) option.
To programmatically set the selection, update the `value` property as shown below.
```html preview
<div class="selecting-example">
<sl-select>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
@@ -259,7 +273,7 @@ To programmatically set the selection, update the `value` property as shown belo
```jsx react
import { useState } from 'react';
import { SlButton, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlButton, SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [value, setValue] = useState('option-1');
@@ -267,9 +281,9 @@ const App = () => {
return (
<>
<SlSelect value={value} onSlChange={event => setValue(event.target.value)}>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
@@ -284,127 +298,65 @@ const App = () => {
### Multiple
To allow multiple options to be selected, use the `multiple` attribute. With this option, `value` will be an array of strings instead of a string. It's a good practice to use `clearable` when this option is enabled.
```html preview
<sl-select placeholder="Select a few" multiple clearable>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="option-4">Option 4</sl-menu-item>
<sl-menu-item value="option-5">Option 5</sl-menu-item>
<sl-menu-item value="option-6">Option 6</sl-menu-item>
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Select a few" multiple clearable>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlDivider />
<SlMenuItem value="option-4">Option 4</SlMenuItem>
<SlMenuItem value="option-5">Option 5</SlMenuItem>
<SlMenuItem value="option-6">Option 6</SlMenuItem>
</SlSelect>
);
```
?> When using the `multiple` option, the value will be an array instead of a string. You may need to [set the selection imperatively](#setting-the-selection-imperatively) unless you're using a framework that supports binding properties declaratively.
TODO
### Grouping Options
Options can be grouped visually using menu labels and dividers.
```html preview
<sl-select placeholder="Select one">
<sl-menu-label>Group 1</sl-menu-label>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-label>Group 2</sl-menu-label>
<sl-menu-item value="option-4">Option 4</sl-menu-item>
<sl-menu-item value="option-5">Option 5</sl-menu-item>
<sl-menu-item value="option-6">Option 6</sl-menu-item>
</sl-select>
```
```jsx react
import { SlDivider, SlMenuItem, SlMenuLabel, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placeholder="Select one">
<SlMenuLabel>Group 1</SlMenuLabel>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlDivider></SlDivider>
<SlMenuLabel>Group 2</SlMenuLabel>
<SlMenuItem value="option-4">Option 4</SlMenuItem>
<SlMenuItem value="option-5">Option 5</SlMenuItem>
<SlMenuItem value="option-6">Option 6</SlMenuItem>
</SlSelect>
);
```
TODO
### Sizes
Use the `size` attribute to change a select's size.
```html preview
<sl-select placeholder="Small" size="small" multiple>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-select placeholder="Small" size="small">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Medium" size="medium" multiple>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-select placeholder="Medium" size="medium">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Large" size="large" multiple>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-select placeholder="Large" size="large">
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlSelect placeholder="Small" size="small" multiple>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlSelect placeholder="Small" size="small">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Medium" size="medium" multiple>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlSelect placeholder="Medium" size="medium">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Large" size="large" multiple>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlSelect placeholder="Large" size="large">
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
</>
);
@@ -416,84 +368,78 @@ The preferred placement of the select's menu can be set with the `placement` att
```html preview
<sl-select placement="top">
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import {
SlMenuItem,
SlOption,
SlSelect
} from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect placement="top">
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlDropdown>
);
```
### Prefix & Suffix Icons
### Prefix Icons
Use the `prefix` and `suffix` slots to add icons.
Use the `prefix` slot to add an icon.
```html preview
<sl-select placeholder="Small" size="small">
<sl-select placeholder="Small" size="small" clearable>
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-icon name="chat" slot="suffix"></sl-icon>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Medium" size="medium">
<sl-select placeholder="Medium" size="medium" clearable>
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-icon name="chat" slot="suffix"></sl-icon>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
<br />
<sl-select placeholder="Large" size="large">
<sl-select placeholder="Large" size="large" clearable>
<sl-icon name="house" slot="prefix"></sl-icon>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<sl-icon name="chat" slot="suffix"></sl-icon>
<sl-option value="option-1">Option 1</sl-option>
<sl-option value="option-2">Option 2</sl-option>
<sl-option value="option-3">Option 3</sl-option>
</sl-select>
```
```jsx react
import { SlIcon, SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
import { SlIcon, SlOption, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<>
<SlSelect placeholder="Small" size="small">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlIcon name="chat" slot="suffix"></SlIcon>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Medium" size="medium">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlIcon name="chat" slot="suffix"></SlIcon>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
<br />
<SlSelect placeholder="Large" size="large">
<SlIcon name="house" slot="prefix"></SlIcon>
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
<SlIcon name="chat" slot="suffix"></SlIcon>
<SlOption value="option-1">Option 1</SlOption>
<SlOption value="option-2">Option 2</SlOption>
<SlOption value="option-3">Option 3</SlOption>
</SlSelect>
</>
);

View File

@@ -8,6 +8,13 @@ New versions of Shoelace are released as-needed and generally occur when a criti
?> During the beta period, these restrictions may be relaxed in the event of a mission-critical bug. 🐛
## Next
This release includes a complete rewrite of `<sl-select>` to improve accessibility and improve simplify the internal structure.
- 🚨 BREAKING: removed the `multiple` attribute from `<sl-select>` because it was inaccessible and made the getting/setting the value inconsistent and confusing (see the docs for a suggested multiselect pattern)
- 🚨 BREAKING: removed the `suffix` slot from `<sl-select>` because it was confusing to users and its position made the clear button inaccessible
## 2.0.0-beta.87
- 🚨 BREAKING: changed the default size of medium checkboxes, radios, and switches to 18px instead of 16px

View File

@@ -36,7 +36,6 @@ export default css`
font-family: var(--sl-font-sans);
font-size: var(--sl-font-size-medium);
font-weight: var(--sl-font-weight-normal);
color: var(--color);
box-shadow: var(--sl-shadow-large);
border-radius: var(--sl-border-radius-medium);
pointer-events: none;

View File

@@ -88,9 +88,6 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
| 'time'
| 'url' = 'text';
/** The input's size. */
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
/** The name of the input, submitted as a name/value pair with form data. */
@property() name = '';
@@ -100,6 +97,9 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
/** The default value of the form control. Primarily used for resetting the form control. */
@defaultValue() defaultValue = '';
/** The input's size. */
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
/** Draws a filled input. */
@property({ type: Boolean, reflect: true }) filled = false;

View File

@@ -44,7 +44,7 @@ export default class SlMenuItem extends ShoelaceElement {
/** A unique value to store in the menu item. This can be used as a way to identify menu items when selected. */
@property() value = '';
/** Draws the menu item in a disabled state. */
/** Draws the menu item in a disabled state, preventing selection. */
@property({ type: Boolean, reflect: true }) disabled = false;
firstUpdated() {

View File

@@ -0,0 +1,70 @@
import { css } from 'lit';
import componentStyles from '../../styles/component.styles';
export default css`
${componentStyles}
:host {
display: block;
user-select: none;
}
.option {
position: relative;
display: flex;
align-items: stretch;
font-family: var(--sl-font-sans);
font-size: var(--sl-font-size-medium);
font-weight: var(--sl-font-weight-normal);
line-height: var(--sl-line-height-normal);
letter-spacing: var(--sl-letter-spacing-normal);
color: var(--sl-color-neutral-700);
padding: var(--sl-spacing-2x-small) var(--sl-spacing-2x-small);
transition: var(--sl-transition-fast) fill;
user-select: none;
white-space: nowrap;
cursor: pointer;
}
:host(:hover) .option {
background-color: var(--sl-color-neutral-100);
color: var(--sl-color-neutral-1000);
}
:host([aria-selected='true']) .option {
background-color: var(--sl-color-primary-600);
color: var(--sl-color-neutral-0);
}
.option.option--disabled {
outline: none;
opacity: 0.5;
cursor: not-allowed;
}
.option__label {
flex: 1 1 auto;
display: inline-block;
padding: 0 var(--sl-spacing-large);
}
.option__prefix {
flex: 0 0 auto;
display: flex;
align-items: center;
}
.option__prefix::slotted(*) {
margin-inline-end: var(--sl-spacing-x-small);
}
.option__suffix {
flex: 0 0 auto;
display: flex;
align-items: center;
}
.option__suffix::slotted(*) {
margin-inline-start: var(--sl-spacing-x-small);
}
`;

View File

@@ -0,0 +1,9 @@
import { expect, fixture, html } from '@open-wc/testing';
describe('<sl-option>', () => {
it('should render a component', async () => {
const el = await fixture(html` <sl-option></sl-option> `);
expect(el).to.exist;
});
});

View File

@@ -0,0 +1,58 @@
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import ShoelaceElement from '../../internal/shoelace-element';
import { LocalizeController } from '../../utilities/localize';
import styles from './option.styles';
import type { CSSResultGroup } from 'lit';
/**
* @summary Short summary of the component's intended use.
*
* @since 2.0
* @status experimental
*
* @dependency sl-icon
*
* @event sl-event-name - Emitted as an example.
*
* @slot - The default slot.
* @slot example - An example slot.
*
* @csspart base - The component's base wrapper.
*
* @cssproperty --example - An example CSS custom property.
*/
@customElement('sl-option')
export default class SlOption extends ShoelaceElement {
static styles: CSSResultGroup = styles;
private readonly localize = new LocalizeController(this);
/** The option's value. When selected, the containing form control will receive this value. */
@property() value = '';
/** Draws the option in a disabled state, preventing selection. */
@property({ type: Boolean, reflect: true }) disabled = false;
connectedCallback() {
super.connectedCallback();
this.setAttribute('role', 'option');
this.setAttribute('aria-selected', 'false');
}
render() {
return html`
<div class="option">
<slot name="prefix" class="option__prefix"></slot>
<slot class="option__label"></slot>
<slot name="suffix" class="option__suffix"></slot>
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'sl-option': SlOption;
}
}

View File

@@ -10,55 +10,58 @@ export default css`
display: block;
}
/** The popup */
.select {
display: block;
}
.select::part(panel) {
overflow: hidden;
}
.select__control {
display: inline-flex;
align-items: center;
justify-content: start;
flex: 1 1 auto;
display: flex;
position: relative;
}
.select::part(popup) {
z-index: var(--sl-z-index-dropdown);
}
.select--top::part(popup) {
transform-origin: bottom;
}
.select--bottom::part(popup) {
transform-origin: top;
}
/* Combobox */
.select__combobox-wrapper {
flex: 1 0 auto;
display: flex;
width: 100%;
font-family: var(--sl-input-font-family);
font-weight: var(--sl-input-font-weight);
letter-spacing: var(--sl-input-letter-spacing);
vertical-align: middle;
overflow: hidden;
transition: var(--sl-transition-fast) color, var(--sl-transition-fast) border, var(--sl-transition-fast) box-shadow;
cursor: text;
transition: var(--sl-transition-fast) color, var(--sl-transition-fast) border, var(--sl-transition-fast) box-shadow,
var(--sl-transition-fast) background-color;
cursor: pointer;
}
.select::part(panel) {
border-radius: var(--sl-border-radius-medium);
.select__combobox {
flex: 1 0 auto;
display: flex;
align-items: stretch;
justify-content: start;
}
/* Standard selects */
.select--standard .select__control {
background-color: var(--sl-input-background-color);
border: solid var(--sl-input-border-width) var(--sl-input-border-color);
color: var(--sl-input-color);
}
.select--standard:not(.select--disabled) .select__control:hover {
background-color: var(--sl-input-background-color-hover);
border-color: var(--sl-input-border-color-hover);
color: var(--sl-input-color-hover);
}
.select--standard.select--focused:not(.select--disabled) .select__control {
background-color: var(--sl-input-background-color-focus);
border-color: var(--sl-input-border-color-focus);
color: var(--sl-input-color-focus);
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-input-focus-ring-color);
.select__combobox:focus {
outline: none;
}
.select--standard.select--disabled .select__control {
/* Standard selects */
.select--standard .select__combobox-wrapper {
background-color: var(--sl-input-background-color);
border: solid var(--sl-input-border-width) var(--sl-input-border-color);
}
.select--standard.select--disabled .select__combobox-wrapper {
background-color: var(--sl-input-background-color-disabled);
border-color: var(--sl-input-border-color-disabled);
color: var(--sl-input-color-disabled);
@@ -67,64 +70,120 @@ export default css`
outline: none;
}
.select--standard:not(.select--disabled).select--open .select__combobox-wrapper,
.select--standard:not(.select--disabled).select--focused .select__combobox-wrapper {
background-color: var(--sl-input-background-color-focus);
border-color: var(--sl-input-border-color-focus);
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-input-focus-ring-color);
}
/* Filled selects */
.select--filled .select__control {
.select--filled .select__combobox-wrapper {
border: none;
background-color: var(--sl-input-filled-background-color);
color: var(--sl-input-color);
}
.select--filled:hover:not(.select--disabled) .select__control {
.select--filled:hover:not(.select--disabled) .select__combobox-wrapper {
background-color: var(--sl-input-filled-background-color-hover);
}
.select--filled.select--focused:not(.select--disabled) .select__control {
background-color: var(--sl-input-filled-background-color-focus);
outline: var(--sl-focus-ring);
outline-offset: var(--sl-focus-ring-offset);
}
.select--filled.select--disabled .select__control {
.select--filled.select--disabled .select__combobox-wrapper {
background-color: var(--sl-input-filled-background-color-disabled);
opacity: 0.5;
cursor: not-allowed;
}
.select--disabled .select__tags,
.select--disabled .select__clear {
pointer-events: none;
.select--filled:not(.select--disabled).select--open .select__combobox-wrapper,
.select--filled:not(.select--disabled).select--focused .select__combobox-wrapper {
background-color: var(--sl-input-filled-background-color-focus);
outline: var(--sl-focus-ring);
}
/* Sizes */
.select--small .select__combobox-wrapper {
border-radius: var(--sl-input-border-radius-small);
font-size: var(--sl-input-font-size-small);
min-height: var(--sl-input-height-small);
padding: 0 var(--sl-input-spacing-small);
}
.select--small .select__clear {
margin-inline-start: var(--sl-input-spacing-small);
}
.select--small .select__prefix::slotted(*) {
margin-inline-end: var(--sl-input-spacing-small);
}
.select--medium .select__combobox-wrapper {
border-radius: var(--sl-input-border-radius-medium);
font-size: var(--sl-input-font-size-medium);
height: var(--sl-input-height-medium);
padding: 0 var(--sl-input-spacing-medium);
}
.select--medium .select__clear {
margin-inline-start: var(--sl-input-spacing-medium);
}
.select--medium .select__prefix::slotted(*) {
margin-inline-end: var(--sl-input-spacing-medium);
}
.select--large .select__combobox-wrapper {
border-radius: var(--sl-input-border-radius-large);
font-size: var(--sl-input-font-size-large);
min-height: var(--sl-input-height-large);
padding: 0 var(--sl-input-spacing-large);
}
.select--large .select__clear {
margin-inline-start: var(--sl-input-spacing-large);
}
.select--large .select__prefix::slotted(*) {
margin-inline-end: var(--sl-input-spacing-large);
}
/* Pills */
.select--pill.select--small .select__combobox-wrapper {
border-radius: var(--sl-input-height-small);
}
.select--pill.select--medium .select__combobox-wrapper {
border-radius: var(--sl-input-height-medium);
}
.select--pill.select--large .select__combobox-wrapper {
border-radius: var(--sl-input-height-large);
}
/* Display label */
.select__display-label {
flex: 1 1 auto;
display: flex;
align-items: center;
user-select: none;
}
.select--placeholder-visible .select__display-label {
color: var(--sl-input-placeholder-color);
}
/* Prefix */
.select__prefix {
flex: 0;
display: inline-flex;
align-items: center;
color: var(--sl-input-placeholder-color);
}
.select__label {
flex: 1 1 auto;
display: flex;
align-items: center;
user-select: none;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
/* Hide scrollbar in Firefox */
scrollbar-width: none;
}
/* Hide scrollbar in Chrome/Safari */
.select__label::-webkit-scrollbar {
width: 0;
height: 0;
}
/* Clear button */
.select__clear {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
width: 1.25em;
justify-content: center;
font-size: inherit;
color: var(--sl-input-icon-color);
border: none;
@@ -138,197 +197,45 @@ export default css`
color: var(--sl-input-icon-color-hover);
}
.select__suffix {
display: inline-flex;
align-items: center;
color: var(--sl-input-placeholder-color);
.select__clear:focus {
outline: none;
}
.select__icon {
/* Expand icon */
.select__expand-icon {
flex: 0 0 auto;
display: inline-flex;
display: flex;
align-items: center;
transition: var(--sl-transition-medium) rotate ease;
rotate: 0;
margin-inline-start: var(--sl-spacing-small);
}
.select--open .select__icon {
.select--open .select__expand-icon {
rotate: -180deg;
}
/* Placeholder */
.select--placeholder-visible .select__label {
color: var(--sl-input-placeholder-color);
/* Listbox */
.select__listbox {
display: block;
position: relative;
font-family: var(--sl-font-sans);
font-size: var(--sl-font-size-medium);
font-weight: var(--sl-font-weight-normal);
box-shadow: var(--sl-shadow-large);
background: var(--sl-panel-background-color);
border: solid var(--sl-panel-border-width) var(--sl-panel-border-color);
border-radius: var(--sl-border-radius-medium);
padding: var(--sl-spacing-x-small) 0;
overflow: auto;
overscroll-behavior: none;
/* Make sure it adheres to the popup's auto size */
max-width: var(--auto-size-available-width);
max-height: var(--auto-size-available-height);
}
.select--disabled.select--placeholder-visible .select__label {
color: var(--sl-input-placeholder-color-disabled);
}
/* Tags */
.select__tags {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
justify-content: left;
margin-inline-start: var(--sl-spacing-2x-small);
}
/* Hidden input (for form control validation to show) */
.select__hidden-select {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
clip: rect(0 0 0 0);
clip-path: inset(50%);
overflow: hidden;
white-space: nowrap;
}
/*
* Size modifiers
*/
/* Small */
.select--small .select__control {
border-radius: var(--sl-input-border-radius-small);
font-size: var(--sl-input-font-size-small);
min-height: var(--sl-input-height-small);
}
.select--small .select__prefix::slotted(*) {
margin-inline-start: var(--sl-input-spacing-small);
}
.select--small .select__label {
margin: 0 var(--sl-input-spacing-small);
}
.select--small .select__clear {
margin-inline-end: var(--sl-input-spacing-small);
}
.select--small .select__suffix::slotted(*) {
margin-inline-end: var(--sl-input-spacing-small);
}
.select--small .select__icon {
margin-inline-end: var(--sl-input-spacing-small);
}
.select--small .select__tags {
padding-bottom: 2px;
}
.select--small .select__tags sl-tag {
padding-top: 2px;
}
.select--small .select__tags sl-tag:not(:last-of-type) {
margin-inline-end: var(--sl-spacing-2x-small);
}
.select--small.select--has-tags .select__label {
margin-inline-start: 0;
}
/* Medium */
.select--medium .select__control {
border-radius: var(--sl-input-border-radius-medium);
font-size: var(--sl-input-font-size-medium);
min-height: var(--sl-input-height-medium);
}
.select--medium .select__prefix::slotted(*) {
margin-inline-start: var(--sl-input-spacing-medium);
}
.select--medium .select__label {
margin: 0 var(--sl-input-spacing-medium);
}
.select--medium .select__clear {
margin-inline-end: var(--sl-input-spacing-medium);
}
.select--medium .select__suffix::slotted(*) {
margin-inline-end: var(--sl-input-spacing-medium);
}
.select--medium .select__icon {
margin-inline-end: var(--sl-input-spacing-medium);
}
.select--medium .select__tags {
padding-bottom: 3px;
}
.select--medium .select__tags sl-tag {
padding-top: 3px;
}
.select--medium .select__tags sl-tag:not(:last-of-type) {
margin-inline-end: var(--sl-spacing-2x-small);
}
.select--medium.select--has-tags .select__label {
margin-inline-start: 0;
}
/* Large */
.select--large .select__control {
border-radius: var(--sl-input-border-radius-large);
font-size: var(--sl-input-font-size-large);
min-height: var(--sl-input-height-large);
}
.select--large .select__prefix::slotted(*) {
margin-inline-start: var(--sl-input-spacing-large);
}
.select--large .select__label {
margin: 0 var(--sl-input-spacing-large);
}
.select--large .select__clear {
margin-inline-end: var(--sl-input-spacing-large);
}
.select--large .select__suffix::slotted(*) {
margin-inline-end: var(--sl-input-spacing-large);
}
.select--large .select__icon {
margin-inline-end: var(--sl-input-spacing-large);
}
.select--large .select__tags {
padding-bottom: 4px;
}
.select--large .select__tags sl-tag {
padding-top: 4px;
}
.select--large .select__tags sl-tag:not(:last-of-type) {
margin-inline-end: var(--sl-spacing-2x-small);
}
.select--large.select--has-tags .select__label {
margin-inline-start: 0;
}
/*
* Pill modifier
*/
.select--pill.select--small .select__control {
border-radius: var(--sl-input-height-small);
}
.select--pill.select--medium .select__control {
border-radius: var(--sl-input-height-medium);
}
.select--pill.select--large .select__control {
border-radius: var(--sl-input-height-large);
.select__listbox::slotted(sl-divider) {
--spacing: var(--sl-spacing-x-small);
}
`;

File diff suppressed because it is too large Load Diff

View File

@@ -53,6 +53,7 @@ export { default as SlTooltip } from './components/tooltip/tooltip';
export { default as SlTree } from './components/tree/tree';
export { default as SlTreeItem } from './components/tree-item/tree-item';
export { default as SlVisuallyHidden } from './components/visually-hidden/visually-hidden';
export { default as SlOption } from './components/option/option';
/* plop:component */
// Utilities