pull/1129/head
Cory LaViska 2023-01-05 14:50:19 -05:00
rodzic b183a04fba
commit a0fce64fd9
7 zmienionych plików z 86 dodań i 51 usunięć

Wyświetl plik

@ -47,10 +47,10 @@
</sl-button>
<sl-menu>
<sl-menu-label>Toggle <kbd>\\</kbd></sl-menu-label>
<sl-menu-item value="light">Light</sl-menu-item>
<sl-menu-item value="dark">Dark</sl-menu-item>
<sl-menu-item type="checkbox" value="light">Light</sl-menu-item>
<sl-menu-item type="checkbox" value="dark">Dark</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item value="auto">Auto</sl-menu-item>
<sl-menu-item type="checkbox" value="auto">Auto</sl-menu-item>
</sl-menu>
`;
document.querySelector('.sidebar-toggle').insertAdjacentElement('afterend', dropdown);

Wyświetl plik

@ -203,9 +203,9 @@ Dropdown menus can be placed in a prefix or suffix slot to provide additional op
<sl-icon label="More options" name="three-dots"></sl-icon>
</sl-button>
<sl-menu>
<sl-menu-item checked>Web Design</sl-menu-item>
<sl-menu-item>Web Development</sl-menu-item>
<sl-menu-item>Marketing</sl-menu-item>
<sl-menu-item type="checkbox" checked>Web Design</sl-menu-item>
<sl-menu-item type="checkbox">Web Development</sl-menu-item>
<sl-menu-item type="checkbox">Marketing</sl-menu-item>
</sl-menu>
</sl-dropdown>
</sl-breadcrumb-item>
@ -235,9 +235,11 @@ const App = () => (
<SlIcon label="More options" name="three-dots"></SlIcon>
</SlButton>
<SlMenu>
<SlMenuItem checked>Web Design</SlMenuItem>
<SlMenuItem>Web Development</SlMenuItem>
<SlMenuItem>Marketing</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Web Design
</SlMenuItem>
<SlMenuItem type="checkbox">Web Development</SlMenuItem>
<SlMenuItem type="checkbox">Marketing</SlMenuItem>
</SlMenu>
</SlDropdown>
</SlBreadcrumbItem>

Wyświetl plik

@ -14,7 +14,7 @@ Dropdowns are designed to work well with [menus](/components/menu) to provide a
<sl-menu-item>Dropdown Item 2</sl-menu-item>
<sl-menu-item>Dropdown Item 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item checked>Checked</sl-menu-item>
<sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item>
<sl-menu-item disabled>Disabled</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
@ -42,7 +42,9 @@ const App = () => (
<SlMenuItem>Dropdown Item 2</SlMenuItem>
<SlMenuItem>Dropdown Item 3</SlMenuItem>
<SlDivider />
<SlMenuItem checked>Checked</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Checkbox
</SlMenuItem>
<SlMenuItem disabled>Disabled</SlMenuItem>
<SlDivider />
<SlMenuItem>

Wyświetl plik

@ -8,7 +8,7 @@
<sl-menu-item>Option 2</sl-menu-item>
<sl-menu-item>Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item checked>Checked</sl-menu-item>
<sl-menu-item type="checkbox" checked>Checkbox</sl-menu-item>
<sl-menu-item disabled>Disabled</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item>
@ -31,7 +31,9 @@ const App = () => (
<SlMenuItem>Option 2</SlMenuItem>
<SlMenuItem>Option 3</SlMenuItem>
<SlDivider />
<SlMenuItem checked>Checked</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Checkbox
</SlMenuItem>
<SlMenuItem disabled>Disabled</SlMenuItem>
<SlDivider />
<SlMenuItem>
@ -48,30 +50,6 @@ const App = () => (
## Examples
### Checked
Use the `checked` attribute to draw menu items in a checked state.
```html preview
<sl-menu style="max-width: 200px;">
<sl-menu-item>Option 1</sl-menu-item>
<sl-menu-item checked>Option 2</sl-menu-item>
<sl-menu-item>Option 3</sl-menu-item>
</sl-menu>
```
```jsx react
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem>Option 1</SlMenuItem>
<SlMenuItem checked>Option 2</SlMenuItem>
<SlMenuItem>Option 3</SlMenuItem>
</SlMenu>
);
```
### Disabled
Add the `disabled` attribute to disable the menu item so it cannot be selected.
@ -150,6 +128,34 @@ const App = () => (
);
```
### Checkbox Menu Items
Set the `type` attribute to `checkbox` to create a menu item that will toggle on and off when selected. You can use the `checked` attribute to set the initial state.
Checkbox menu items are visually indistinguishable from regular menu items. Their ability to be toggled is primarily inferred from context, much like you'd find in the menu of a native app.
```html preview
<sl-menu style="max-width: 200px;">
<sl-menu-item type="checkbox">Autosave</sl-menu-item>
<sl-menu-item type="checkbox" checked>Check Spelling</sl-menu-item>
<sl-menu-item type="checkbox">Word Wrap</sl-menu-item>
</sl-menu>
```
```jsx react
import { SlMenu, SlMenuItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlMenu style={{ maxWidth: '200px' }}>
<SlMenuItem type="checkbox">Autosave</SlMenuItem>
<SlMenuItem type="checkbox" checked>
Check Spelling
</SlMenuItem>
<SlMenuItem type="checkbox">Word Wrap</SlMenuItem>
</SlMenu>
);
```
### Value & Selection
The `value` attribute can be used to assign a hidden value, such as a unique identifier, to a menu item. When an item is selected, the `sl-select` event will be emitted and a reference to the item will be available at `event.detail.item`. You can use this reference to access the selected item's value, its checked state, and more.
@ -159,6 +165,10 @@ The `value` attribute can be used to assign a hidden value, such as a unique ide
<sl-menu-item value="opt-1">Option 1</sl-menu-item>
<sl-menu-item value="opt-2">Option 2</sl-menu-item>
<sl-menu-item value="opt-3">Option 3</sl-menu-item>
<sl-divider></sl-divider>
<sl-menu-item type="checkbox" value="opt-4">Checkbox 4</sl-menu-item>
<sl-menu-item type="checkbox" value="opt-5">Checkbox 5</sl-menu-item>
<sl-menu-item type="checkbox" value="opt-6">Checkbox 6</sl-menu-item>
</sl-menu>
<script>
@ -167,11 +177,12 @@ The `value` attribute can be used to assign a hidden value, such as a unique ide
menu.addEventListener('sl-select', event => {
const item = event.detail.item;
// Toggle checked state
item.checked = !item.checked;
// Log value
console.log(`Selected value: ${item.value}`);
if (item.type === 'checkbox') {
console.log(`Selected value: ${item.value} (${item.checked ? 'checked' : 'unchecked'})`);
} else {
console.log(`Selected value: ${item.value}`);
}
});
</script>
```

Wyświetl plik

@ -21,6 +21,9 @@ This release includes a complete rewrite of `<sl-select>` to improve accessibili
- 🚨 BREAKING: removed the `sl-label-change` event from `<sl-menu-item>` (listen for `slotchange` instead)
- 🚨 BREAKING: removed type to select logic from `<sl-menu>` (this was added specifically for `<sl-select>` which no longer uses `<sl-menu>`)
- 🚨 BREAKING: swatches in `<sl-color-picker>` are no longer present by default (but you can set them using the `swatches` attribute now)
- 🚨 BREAKING: improved the accessibility of `<sl-menu-item>` so checked items are announced as such
- Checkbox menu items must now have `type="checkbox"` before applying the `checked` attribute
- Checkbox menu items will now toggle their `checked` state on their own when selected
- Added the `<sl-option>` component
- Added Traditional Chinese translation [#1086](https://github.com/shoelace-style/shoelace/pull/1086)
- Added support for `swatches` to be an attribute of `<sl-color-picker>` so swatches can be defined declaratively (it was previously a property; use a `;` to separate color values)

Wyświetl plik

@ -35,6 +35,9 @@ export default class SlMenuItem extends ShoelaceElement {
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
@query('.menu-item') menuItem: HTMLElement;
/** The type of menu item to render. To use `checked`, this value must be set to `checkbox`. */
@property() type: 'normal' | 'checkbox' = 'normal';
/** Draws the item in a checked state. */
@property({ type: Boolean, reflect: true }) checked = false;
@ -44,10 +47,6 @@ export default class SlMenuItem extends ShoelaceElement {
/** Draws the menu item in a disabled state, preventing selection. */
@property({ type: Boolean, reflect: true }) disabled = false;
firstUpdated() {
this.setAttribute('role', 'menuitem');
}
private handleDefaultSlotChange() {
const textLabel = this.getTextLabel();
@ -66,10 +65,12 @@ export default class SlMenuItem extends ShoelaceElement {
@watch('checked')
handleCheckedChange() {
//
// TODO - fix a11y bug
//
// this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
if (this.checked && this.type !== 'checkbox') {
this.checked = false;
console.error('The checked attribute can only be used on menu items with type="checkbox"', this);
}
}
@watch('disabled')
@ -77,6 +78,11 @@ export default class SlMenuItem extends ShoelaceElement {
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
@watch('type')
handleTypeChange() {
this.setAttribute('role', this.type === 'checkbox' ? 'menuitemcheckbox' : 'menuitem');
}
/** Returns a text label based on the contents of the menu item's default slot. */
getTextLabel() {
return getTextContent(this.defaultSlot);

Wyświetl plik

@ -31,7 +31,7 @@ export default class SlMenu extends ShoelaceElement {
private getAllItems(options: { includeDisabled: boolean } = { includeDisabled: true }) {
return [...this.defaultSlot.assignedElements({ flatten: true })].filter((el: HTMLElement) => {
if (el.getAttribute('role') !== 'menuitem') {
if (!this.isMenuItem(el)) {
return false;
}
@ -48,6 +48,10 @@ export default class SlMenu extends ShoelaceElement {
const item = target.closest('sl-menu-item');
if (item?.disabled === false) {
if (item.type === 'checkbox') {
item.checked = !item.checked;
}
this.emit('sl-select', { detail: { item } });
}
}
@ -102,7 +106,7 @@ export default class SlMenu extends ShoelaceElement {
private handleMouseDown(event: MouseEvent) {
const target = event.target as HTMLElement;
if (target.getAttribute('role') === 'menuitem') {
if (this.isMenuItem(target)) {
this.setCurrentItem(target as SlMenuItem);
}
}
@ -116,6 +120,13 @@ export default class SlMenu extends ShoelaceElement {
}
}
private isMenuItem(item: HTMLElement) {
return (
item.tagName.toLowerCase() === 'sl-menu-item' ||
['menuitem', 'menuitemcheckbox', 'menuitemradio'].includes(item.getAttribute('role') ?? '')
);
}
/**
* @internal Gets the current menu item, which is the menu item that has `tabindex="0"` within the roving tab index.
* The menu item may or may not have focus, but for keyboard interaction purposes it's considered the "active" item.