mirror of
https://github.com/shoelace-style/shoelace.git
synced 2026-01-12 02:59:13 +00:00
Merge branch 'next' of github.com:nathangray/shoelace into nathangray-next
This commit is contained in:
@@ -454,3 +454,58 @@ const App = () => (
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Custom Tags
|
||||
|
||||
When multiple options can be selected, you can provide custom tags by passing a function to the `getTag` property.
|
||||
Your `getTag(option, index)` function can return a string, a Lit <a href="https://lit.dev/docs/templates/overview/">Template</a>,
|
||||
or an HTMLElement.
|
||||
|
||||
```html:preview
|
||||
<sl-select placeholder="Select" value="option-1 option-2" class="custom-tag" multiple clearable>
|
||||
<sl-option value="option-1">
|
||||
<sl-icon slot="prefix" name="envelope"></sl-icon>
|
||||
Email
|
||||
<sl-icon slot="suffix" name="patch-check"></sl-icon>
|
||||
</sl-option>
|
||||
|
||||
<sl-option value="option-2">
|
||||
<sl-icon slot="prefix" name="telephone"></sl-icon>
|
||||
Phone
|
||||
<sl-icon slot="suffix" name="patch-check"></sl-icon>
|
||||
</sl-option>
|
||||
|
||||
<sl-option value="option-3">
|
||||
<sl-icon slot="prefix" name="chat-dots"></sl-icon>
|
||||
Chat
|
||||
<sl-icon slot="suffix" name="patch-check"></sl-icon>
|
||||
</sl-option>
|
||||
<sl-option value="option-4">Option 4</sl-option>
|
||||
<sl-option value="option-5">Option 5</sl-option>
|
||||
</sl-select>
|
||||
|
||||
<script type="module">
|
||||
import {html} from "https://cdn.jsdelivr.net/npm/lit@2.8.0/+esm";
|
||||
|
||||
const select = document.querySelector('.custom-tag');
|
||||
select.getTag = (option, index) => {
|
||||
const icons = {'option-1': 'envelope', 'option-2': 'telephone', 'option-3': 'chat-dots'};
|
||||
if(typeof icons[option.value] == "undefined") {
|
||||
// Return a string
|
||||
return "<sl-tag removable>"+
|
||||
"<sl-icon part=\"tag-icon\" name=\"patch-question\"></sl-icon>" +
|
||||
option.getTextLabel() +
|
||||
"</sl-tag>"
|
||||
} else {
|
||||
// Return a Lit template
|
||||
return html`<sl-tag removable>
|
||||
<sl-icon part="tag-icon" name="${ icons[option.value] }"></sl-icon>
|
||||
${ option.getTextLabel() }
|
||||
</sl-tag>`;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.custom-tag::part(tag-icon) { padding-right: var(--sl-spacing-small); }
|
||||
</style>
|
||||
```
|
||||
|
||||
@@ -8,6 +8,7 @@ import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { scrollIntoView } from '../../internal/scroll.js';
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import ShoelaceElement from '../../internal/shoelace-element.js';
|
||||
@@ -15,7 +16,7 @@ import SlIcon from '../icon/icon.component.js';
|
||||
import SlPopup from '../popup/popup.component.js';
|
||||
import SlTag from '../tag/tag.component.js';
|
||||
import styles from './select.styles.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type { CSSResultGroup, TemplateResult } from 'lit';
|
||||
import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
|
||||
import type SlOption from '../option/option.component.js';
|
||||
import type SlRemoveEvent from '../../events/sl-remove.js';
|
||||
@@ -172,6 +173,31 @@ export default class SlSelect extends ShoelaceElement implements ShoelaceFormCon
|
||||
/** The select's required attribute. */
|
||||
@property({ type: Boolean, reflect: true }) required = false;
|
||||
|
||||
/**
|
||||
* A function that customizes the tags to be rendered when multiple=true. The first argument is the option, the second
|
||||
* is the current tag's index. The function should return either a Lit TemplateResult or a string containing trusted HTML of the symbol to render at
|
||||
* the specified value.
|
||||
*/
|
||||
@property() getTag: (option: SlOption, index: number) => TemplateResult | string | HTMLElement = option => {
|
||||
return html`
|
||||
<sl-tag
|
||||
part="tag"
|
||||
exportparts="
|
||||
base:tag__base,
|
||||
content:tag__content,
|
||||
remove-button:tag__remove-button,
|
||||
remove-button__base:tag__remove-button__base
|
||||
"
|
||||
?pill=${this.pill}
|
||||
size=${this.size}
|
||||
removable
|
||||
@sl-remove=${(event: SlRemoveEvent) => this.handleTagRemove(event, option)}
|
||||
>
|
||||
${option.getTextLabel()}
|
||||
</sl-tag>
|
||||
`;
|
||||
};
|
||||
|
||||
/** Gets the validity state object */
|
||||
get validity() {
|
||||
return this.valueInput.validity;
|
||||
@@ -547,6 +573,21 @@ export default class SlSelect extends ShoelaceElement implements ShoelaceFormCon
|
||||
this.formControlController.updateValidity();
|
||||
});
|
||||
}
|
||||
protected get tags() {
|
||||
return this.selectedOptions.map((option, index) => {
|
||||
if (index < this.maxOptionsVisible || this.maxOptionsVisible <= 0) {
|
||||
const tag = this.getTag(option, index);
|
||||
// Wrap so we can handle the remove
|
||||
return html`<div @sl-remove=${(e: SlRemoveEvent) => this.handleTagRemove(e, option)}>
|
||||
${typeof tag === 'string' ? unsafeHTML(tag) : tag}
|
||||
</div>`;
|
||||
} else if (index === this.maxOptionsVisible) {
|
||||
// Hit tag limit
|
||||
return html`<sl-tag>+${this.selectedOptions.length - index}</sl-tag>`;
|
||||
}
|
||||
return html``;
|
||||
});
|
||||
}
|
||||
|
||||
private handleInvalid(event: Event) {
|
||||
this.formControlController.setValidity(false);
|
||||
@@ -755,37 +796,7 @@ export default class SlSelect extends ShoelaceElement implements ShoelaceFormCon
|
||||
@blur=${this.handleBlur}
|
||||
/>
|
||||
|
||||
${this.multiple
|
||||
? html`
|
||||
<div part="tags" class="select__tags">
|
||||
${this.selectedOptions.map((option, index) => {
|
||||
if (index < this.maxOptionsVisible || this.maxOptionsVisible <= 0) {
|
||||
return html`
|
||||
<sl-tag
|
||||
part="tag"
|
||||
exportparts="
|
||||
base:tag__base,
|
||||
content:tag__content,
|
||||
remove-button:tag__remove-button,
|
||||
remove-button__base:tag__remove-button__base
|
||||
"
|
||||
?pill=${this.pill}
|
||||
size=${this.size}
|
||||
removable
|
||||
@sl-remove=${(event: SlRemoveEvent) => this.handleTagRemove(event, option)}
|
||||
>
|
||||
${option.getTextLabel()}
|
||||
</sl-tag>
|
||||
`;
|
||||
} else if (index === this.maxOptionsVisible) {
|
||||
return html` <sl-tag size=${this.size}> +${this.selectedOptions.length - index} </sl-tag> `;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
${this.multiple ? html`<div part="tags" class="select__tags">${this.tags}</div>` : ''}
|
||||
|
||||
<input
|
||||
class="select__value-input"
|
||||
|
||||
Reference in New Issue
Block a user