pull/463/head
Cory LaViska 2021-06-02 08:47:55 -04:00
rodzic afc4dfaf50
commit b0921b5be0
17 zmienionych plików z 104 dodań i 276 usunięć

Wyświetl plik

@ -15,177 +15,3 @@ Selects allow you to choose one or more items from a dropdown menu.
<sl-menu-item value="option-6">Option 6</sl-menu-item>
</sl-select>
```
?> This component doesn't work with standard forms. Use [`<sl-form>`](/components/form) instead.
## Examples
### Placeholders
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-select>
```
### Clearable
Use the `clearable` attribute to make the control clearable.
```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>
```
### Pill
Use the `pill` prop 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-select>
```
### Disabled
Use the `disabled` prop 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-select>
```
### Multiple
To allow multiple options to be selected, use the `multiple` attribute. It's a good practice to use `clearable` when this option is enabled. When using this option, `value` will be an array instead of a string.
```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-menu-divider></sl-menu-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>
```
### Grouping Options
Options can be grouped visually using menu labels and menu 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-menu-divider></sl-menu-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>
```
### 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>
<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>
<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>
```
### Selecting Options Programmatically
The `value` prop is bound to the current selection. As the selection changes, so will the value. To programmatically manage the selection, update the `value` property.
```html preview
<div class="selecting-example">
<sl-select placeholder="">
<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>
<br>
<sl-button data-option="option-1">Set 1</sl-button>
<sl-button data-option="option-2">Set 2</sl-button>
<sl-button data-option="option-3">Set 3</sl-button>
</div>
<script>
const container = document.querySelector('.selecting-example');
const select = container.querySelector('sl-select');
[...container.querySelectorAll('sl-button')].map(button => {
button.addEventListener('click', () => {
select.value = button.dataset.option;
});
});
</script>
```
### Labels
Use the `label` attribute to give the select an accessible label. For labels that contain HTML, use the `label` slot instead.
```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-select>
```
### Help Text
Add descriptive help text to a select with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html preview
<sl-select
label="Experience"
help-text="Please tell us your skill level."
>
<sl-menu-item value="option-1">Novice</sl-menu-item>
<sl-menu-item value="option-2">Intermediate</sl-menu-item>
<sl-menu-item value="option-3">Advanced</sl-menu-item>
</sl-select>
```
[component-metadata:sl-select]

Wyświetl plik

@ -11,6 +11,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
- Added `?` to optional arguments in methods tables
- Added the `scrollPosition()` method to `sl-textarea` to get/set scroll position
- Fixed a bug in `sl-tab-group` where scrollable tab icons were not displaying correctly
- Fixed lifecycle bugs in a number of components [#451](https://github.com/shoelace-style/shoelace/issues/451)
- Removed `fill: both` from internal animate utility so styles won't "stick" by default [#450](https://github.com/shoelace-style/shoelace/issues/450)
## 2.0.0-beta.42

Wyświetl plik

@ -64,13 +64,12 @@ export default class SlAlert extends LitElement {
/** Emitted after the alert closes and all transitions are complete. */
@event('sl-after-hide') slAfterHide: EventEmitter<void>;
async firstUpdated() {
firstUpdated() {
// Set initial visibility
this.base.hidden = !this.open;
// Set the initialized flag after the first update is complete
await this.updateComplete;
this.hasInitialized = true;
// Set the initialized flag after the first render is complete
this.updateComplete.then(() => (this.hasInitialized = true));
}
/** Shows the alert. */

Wyświetl plik

@ -60,15 +60,17 @@ export default class SlDetails extends LitElement {
/** Emitted after the details closes and all transitions are complete. */
@event('sl-after-hide') slAfterHide: EventEmitter<void>;
async firstUpdated() {
focusVisible.observe(this.details);
connectedCallback() {
super.connectedCallback();
this.updateComplete.then(() => focusVisible.observe(this.details));
}
firstUpdated() {
this.body.hidden = !this.open;
this.body.style.height = this.open ? 'auto' : '0';
// Set the initialized flag after the first update is complete
await this.updateComplete;
this.hasInitialized = true;
// Set the initialized flag after the first render is complete
this.updateComplete.then(() => (this.hasInitialized = true));
}
disconnectedCallback() {

Wyświetl plik

@ -103,13 +103,12 @@ export default class SlDialog extends LitElement {
this.handleSlotChange();
}
async firstUpdated() {
firstUpdated() {
// Set initial visibility
this.dialog.hidden = !this.open;
// Set the initialized flag after the first update is complete
await this.updateComplete;
this.hasInitialized = true;
// Set the initialized flag after the first render is complete
this.updateComplete.then(() => (this.hasInitialized = true));
}
disconnectedCallback() {

Wyświetl plik

@ -117,13 +117,12 @@ export default class SlDrawer extends LitElement {
this.handleSlotChange();
}
async firstUpdated() {
firstUpdated() {
// Set initial visibility
this.drawer.hidden = !this.open;
// Set the initialized flag after the first update is complete
await this.updateComplete;
this.hasInitialized = true;
// Set the initialized flag after the first render is complete
this.updateComplete.then(() => (this.hasInitialized = true));
}
disconnectedCallback() {

Wyświetl plik

@ -128,13 +128,12 @@ export default class SlDropdown extends LitElement {
});
}
async firstUpdated() {
firstUpdated() {
// Set initial visibility
this.panel.hidden = !this.open;
// Set the initialized flag after the first update is complete
await this.updateComplete;
this.hasInitialized = true;
// Set the initialized flag after the first render is complete
this.updateComplete.then(() => (this.hasInitialized = true));
}
disconnectedCallback() {

Wyświetl plik

@ -37,8 +37,9 @@ export default class SlIconButton extends LitElement {
/** Disables the button. */
@property({ type: Boolean, reflect: true }) disabled = false;
firstUpdated() {
focusVisible.observe(this.button);
connectedCallback() {
super.connectedCallback();
this.updateComplete.then(() => focusVisible.observe(this.button));
}
disconnectedCallback() {

Wyświetl plik

@ -149,14 +149,9 @@ export default class SlInput extends LitElement {
connectedCallback() {
super.connectedCallback();
this.handleSlotChange = this.handleSlotChange.bind(this);
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
}
firstUpdated() {
this.handleSlotChange();
}
disconnectedCallback() {
super.disconnectedCallback();
this.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);

Wyświetl plik

@ -85,6 +85,7 @@ export default class SlRange extends LitElement {
connectedCallback() {
super.connectedCallback();
this.handleSlotChange = this.handleSlotChange.bind(this);
this.resizeObserver = new ResizeObserver(() => this.syncTooltip());
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
if (this.value === undefined || this.value === null) this.value = this.min;
@ -92,15 +93,16 @@ export default class SlRange extends LitElement {
if (this.value > this.max) this.value = this.max;
this.handleSlotChange();
}
firstUpdated() {
this.syncTooltip();
this.resizeObserver = new ResizeObserver(() => this.syncTooltip());
this.updateComplete.then(() => {
this.syncTooltip();
this.resizeObserver.observe(this.input);
});
}
disconnectedCallback() {
super.disconnectedCallback();
this.resizeObserver.unobserve(this.input);
this.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
}
@ -131,14 +133,12 @@ export default class SlRange extends LitElement {
this.hasFocus = false;
this.hasTooltip = false;
this.slBlur.emit();
this.resizeObserver.unobserve(this.input);
}
handleFocus() {
this.hasFocus = true;
this.hasTooltip = true;
this.slFocus.emit();
this.resizeObserver.observe(this.input);
}
@watch('label')

Wyświetl plik

@ -62,8 +62,9 @@ export default class SlRating extends LitElement {
this.rating.blur();
}
firstUpdated() {
focusVisible.observe(this.rating);
connectedCallback() {
super.connectedCallback();
this.updateComplete.then(() => focusVisible.observe(this.rating));
}
disconnectedCallback() {

Wyświetl plik

@ -32,7 +32,8 @@ export default class SlRelativeTime extends LitElement {
/** Keep the displayed value up to date as time passes. */
@property({ type: Boolean }) sync = false;
firstUpdated() {
connectedCallback() {
super.connectedCallback();
this.updateTime();
}

Wyświetl plik

@ -123,18 +123,18 @@ export default class SlSelect extends LitElement {
connectedCallback() {
super.connectedCallback();
this.handleSlotChange = this.handleSlotChange.bind(this);
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
this.handleSlotChange();
}
firstUpdated() {
this.resizeObserver = new ResizeObserver(() => this.resizeMenu());
this.syncItemsFromValue();
this.updateComplete.then(() => {
this.resizeObserver.observe(this);
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
this.syncItemsFromValue();
});
}
disconnectedCallback() {
super.disconnectedCallback();
this.resizeObserver.unobserve(this);
this.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
}
@ -256,12 +256,10 @@ export default class SlSelect extends LitElement {
handleMenuShow() {
this.resizeMenu();
this.resizeObserver.observe(this);
this.isOpen = true;
}
handleMenuHide() {
this.resizeObserver.unobserve(this);
this.isOpen = false;
}

Wyświetl plik

@ -62,49 +62,49 @@ export default class SlTabGroup extends LitElement {
/** Emitted when a tab is hidden. */
@event('sl-tab-hide') slTabHide: EventEmitter<{ tab: string }>;
firstUpdated() {
this.syncTabsAndPanels();
// Set initial tab state when the tabs first become visible
const observer = new IntersectionObserver((entries, observer) => {
if (entries[0].intersectionRatio > 0) {
this.setAriaLabels();
this.setActiveTab(this.getActiveTab() || this.tabs[0], false);
observer.unobserve(entries[0].target);
}
});
observer.observe(this);
focusVisible.observe(this.tabGroup);
connectedCallback() {
super.connectedCallback();
this.resizeObserver = new ResizeObserver(() => {
this.preventIndicatorTransition();
this.repositionIndicator();
this.updateScrollControls();
});
this.resizeObserver.observe(this.nav);
requestAnimationFrame(() => this.updateScrollControls());
this.mutationObserver = new MutationObserver(mutations => {
// Update aria labels when the DOM changes
if (
mutations.some(mutation => !['aria-labelledby', 'aria-controls'].includes(mutation.attributeName as string))
) {
if (mutations.some(m => !['aria-labelledby', 'aria-controls'].includes(m.attributeName as string))) {
setTimeout(() => this.setAriaLabels());
}
// Sync tabs when disabled states change
if (mutations.some(mutation => mutation.attributeName === 'disabled')) {
if (mutations.some(m => m.attributeName === 'disabled')) {
this.syncTabsAndPanels();
}
});
this.mutationObserver.observe(this, { attributes: true, childList: true, subtree: true });
this.updateComplete.then(() => {
this.syncTabsAndPanels();
this.mutationObserver.observe(this, { attributes: true, childList: true, subtree: true });
this.resizeObserver.observe(this.nav);
focusVisible.observe(this.tabGroup);
// Set initial tab state when the tabs first become visible
const intersectionObserver = new IntersectionObserver((entries, observer) => {
if (entries[0].intersectionRatio > 0) {
this.setAriaLabels();
this.setActiveTab(this.getActiveTab() || this.tabs[0], { emitEvents: false });
observer.unobserve(entries[0].target);
}
});
intersectionObserver.observe(this.tabGroup);
});
}
disconnectedCallback() {
this.mutationObserver.disconnect();
focusVisible.unobserve(this.tabGroup);
this.resizeObserver.unobserve(this.nav);
focusVisible.unobserve(this.tabGroup);
}
/** Shows the specified tab panel. */
@ -112,7 +112,7 @@ export default class SlTabGroup extends LitElement {
const tab = this.tabs.find(el => el.panel === panel) as SlTab;
if (tab) {
this.setActiveTab(tab);
this.setActiveTab(tab, { scrollBehavior: 'smooth' });
}
}
@ -148,7 +148,7 @@ export default class SlTabGroup extends LitElement {
}
if (tab) {
this.setActiveTab(tab);
this.setActiveTab(tab, { scrollBehavior: 'smooth' });
}
}
@ -165,7 +165,7 @@ export default class SlTabGroup extends LitElement {
// Activate a tab
if (['Enter', ' '].includes(event.key)) {
if (tab) {
this.setActiveTab(tab);
this.setActiveTab(tab, { scrollBehavior: 'smooth' });
event.preventDefault();
}
}
@ -190,7 +190,7 @@ export default class SlTabGroup extends LitElement {
this.tabs[index].focus({ preventScroll: true });
if (this.activation === 'auto') {
this.setActiveTab(this.tabs[index]);
this.setActiveTab(this.tabs[index], { scrollBehavior: 'smooth' });
}
if (['top', 'bottom'].includes(this.placement)) {
@ -226,7 +226,15 @@ export default class SlTabGroup extends LitElement {
}
}
setActiveTab(tab: SlTab, emitEvents = true) {
setActiveTab(tab: SlTab, options?: { emitEvents?: boolean; scrollBehavior?: 'auto' | 'smooth' }) {
options = Object.assign(
{
emitEvents: true,
scrollBehavior: 'auto'
},
options
);
if (tab && tab !== this.activeTab && !tab.disabled) {
const previousTab = this.activeTab;
this.activeTab = tab;
@ -237,11 +245,11 @@ export default class SlTabGroup extends LitElement {
this.syncIndicator();
if (['top', 'bottom'].includes(this.placement)) {
scrollIntoView(this.activeTab, this.nav, 'horizontal');
scrollIntoView(this.activeTab, this.nav, 'horizontal', options.scrollBehavior);
}
// Emit events
if (emitEvents) {
if (options.emitEvents) {
if (previousTab) {
this.slTabHide.emit({ detail: { name: previousTab.panel } });
}

Wyświetl plik

@ -24,7 +24,8 @@ export default class SlTabPanel extends LitElement {
/** When true, the tab panel will be shown. */
@property({ type: Boolean, reflect: true }) active = false;
firstUpdated() {
connectedCallback() {
super.connectedCallback();
this.id = this.id || this.componentId;
}

Wyświetl plik

@ -130,15 +130,14 @@ export default class SlTextarea extends LitElement {
connectedCallback() {
super.connectedCallback();
this.handleSlotChange = this.handleSlotChange.bind(this);
this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
this.handleSlotChange();
}
firstUpdated() {
this.setTextareaHeight();
this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
this.resizeObserver.observe(this.input);
this.updateComplete.then(() => {
this.setTextareaHeight();
this.resizeObserver.observe(this.input);
});
}
disconnectedCallback() {

Wyświetl plik

@ -93,47 +93,46 @@ export default class SlTooltip extends LitElement {
connectedCallback() {
super.connectedCallback();
this.handleBlur = this.handleBlur.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleFocus = this.handleFocus.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleMouseOut = this.handleMouseOut.bind(this);
this.updateComplete.then(() => {
this.addEventListener('blur', this.handleBlur, true);
this.addEventListener('focus', this.handleFocus, true);
this.addEventListener('click', this.handleClick);
this.addEventListener('keydown', this.handleKeyDown);
this.addEventListener('mouseover', this.handleMouseOver);
this.addEventListener('mouseout', this.handleMouseOut);
this.target = this.getTarget();
this.syncOptions();
});
}
async firstUpdated() {
this.target = this.getTarget();
this.syncOptions();
this.addEventListener('blur', this.handleBlur, true);
this.addEventListener('focus', this.handleFocus, true);
this.addEventListener('click', this.handleClick);
this.addEventListener('keydown', this.handleKeyDown);
this.addEventListener('mouseover', this.handleMouseOver);
this.addEventListener('mouseout', this.handleMouseOut);
firstUpdated() {
// Set initial visibility
this.tooltip.hidden = !this.open;
// Set the initialized flag after the first update is complete
await this.updateComplete;
this.hasInitialized = true;
// Set the initialized flag after the first render is complete
this.updateComplete.then(() => (this.hasInitialized = true));
}
disconnectedCallback() {
super.disconnectedCallback();
if (this.popover) {
this.popover.destroy();
}
this.removeEventListener('blur', this.handleBlur, true);
this.removeEventListener('focus', this.handleFocus, true);
this.removeEventListener('click', this.handleClick);
this.removeEventListener('keydown', this.handleKeyDown);
this.removeEventListener('mouseover', this.handleMouseOver);
this.removeEventListener('mouseout', this.handleMouseOut);
if (this.popover) {
this.popover.destroy();
}
}
/** Shows the tooltip. */