mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-11 20:08:56 +00:00
Combobox (supporting contributions in free) (#1838)
* hold the mustard! let's make it yellower * words * update sidebar * update changelog * fix icon cropping * add combobox support * preserve user-selected order * add quick pro flag * move import to the top * fix custom tag example
This commit is contained in:
@@ -105,6 +105,7 @@
|
|||||||
"keydown",
|
"keydown",
|
||||||
"keyframes",
|
"keyframes",
|
||||||
"keymaker",
|
"keymaker",
|
||||||
|
"Kickstarter",
|
||||||
"Konnor",
|
"Konnor",
|
||||||
"Kool",
|
"Kool",
|
||||||
"labelledby",
|
"labelledby",
|
||||||
@@ -117,6 +118,7 @@
|
|||||||
"lowercasing",
|
"lowercasing",
|
||||||
"Lucide",
|
"Lucide",
|
||||||
"maxlength",
|
"maxlength",
|
||||||
|
"mdash",
|
||||||
"Menlo",
|
"Menlo",
|
||||||
"menuitemcheckbox",
|
"menuitemcheckbox",
|
||||||
"menuitemradio",
|
"menuitemradio",
|
||||||
@@ -130,6 +132,7 @@
|
|||||||
"mouseout",
|
"mouseout",
|
||||||
"mouseup",
|
"mouseup",
|
||||||
"multiselectable",
|
"multiselectable",
|
||||||
|
"nbsp",
|
||||||
"nextjs",
|
"nextjs",
|
||||||
"nocheck",
|
"nocheck",
|
||||||
"noindex",
|
"noindex",
|
||||||
@@ -179,6 +182,7 @@
|
|||||||
"shadowrootmode",
|
"shadowrootmode",
|
||||||
"Shortcode",
|
"Shortcode",
|
||||||
"Shortcodes",
|
"Shortcodes",
|
||||||
|
"signup",
|
||||||
"sitedir",
|
"sitedir",
|
||||||
"slotchange",
|
"slotchange",
|
||||||
"smartquotes",
|
"smartquotes",
|
||||||
|
|||||||
@@ -81,7 +81,15 @@
|
|||||||
<li><span class="is-planned wa-split">Charts <span><a href="https://github.com/shoelace-style/webawesome/issues/1073" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
<li><span class="is-planned wa-split">Charts <span><a href="https://github.com/shoelace-style/webawesome/issues/1073" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
||||||
<li><a href="/docs/components/checkbox/">Checkbox</a></li>
|
<li><a href="/docs/components/checkbox/">Checkbox</a></li>
|
||||||
<li><a href="/docs/components/color-picker/">Color Picker</a></li>
|
<li><a href="/docs/components/color-picker/">Color Picker</a></li>
|
||||||
<li><span class="is-planned wa-split">Combobox <span><a href="https://github.com/shoelace-style/webawesome/issues/1074" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
<li>
|
||||||
|
<span class="wa-split">
|
||||||
|
<span>
|
||||||
|
<a href="/docs/components/combobox">Combobox</a>
|
||||||
|
<wa-icon name="flask" aria-hidden="true" class="icon-shrink"></wa-icon>
|
||||||
|
</span>
|
||||||
|
{{ proBadge() }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
<li><a href="/docs/components/comparison/">Comparison</a></li>
|
<li><a href="/docs/components/comparison/">Comparison</a></li>
|
||||||
<li>
|
<li>
|
||||||
<a class="wa-cluster wa-gap-xs" href="/docs/components/copy-button/">
|
<a class="wa-cluster wa-gap-xs" href="/docs/components/copy-button/">
|
||||||
@@ -90,7 +98,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li><span class="is-planned wa-split">Data Grid <span><a href="https://github.com/shoelace-style/webawesome/issues/1072" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
<li><span class="is-planned wa-split">Data Grid <span><a href="https://github.com/shoelace-style/webawesome/issues/1072" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
||||||
<li><span class="is-planned wa-split">Datepicker <span><a href="https://github.com/shoelace-style/webawesome/issues/1075" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
<li><span class="is-planned wa-split">Date Picker <span><a href="https://github.com/shoelace-style/webawesome/issues/1075" target="_blank">{{ plannedBadge("A Web Awesome Kickstarter stretch goal!") }}</a>{{ proBadge({ description: "This will require access to Web Awesome Pro" }) }}</span></span></li>
|
||||||
<li><a href="/docs/components/details/">Details</a></li>
|
<li><a href="/docs/components/details/">Details</a></li>
|
||||||
<li><a href="/docs/components/dialog/">Dialog</a></li>
|
<li><a href="/docs/components/dialog/">Dialog</a></li>
|
||||||
<li><a href="/docs/components/divider/">Divider</a></li>
|
<li><a href="/docs/components/divider/">Divider</a></li>
|
||||||
|
|||||||
@@ -8,10 +8,13 @@
|
|||||||
<wa-badge variant="neutral">Since {{ component.since }}</wa-badge>
|
<wa-badge variant="neutral">Since {{ component.since }}</wa-badge>
|
||||||
<wa-badge
|
<wa-badge
|
||||||
{% if component.status == 'stable' %}variant="brand"{% endif %}
|
{% if component.status == 'stable' %}variant="brand"{% endif %}
|
||||||
{% if component.status == 'experimental' %}variant="warning"{% endif %}
|
{% if component.status == 'experimental' %}variant="warning" appearance="filled"{% endif %}
|
||||||
>
|
>
|
||||||
{{ component.status }}
|
{{ component.status }}
|
||||||
</wa-badge>
|
</wa-badge>
|
||||||
|
{% if isProComponent %}
|
||||||
|
<wa-badge class="pro">Pro</wa-badge>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<p class="component-summary">
|
<p class="component-summary">
|
||||||
{{ component.summary | inlineMarkdown | safe }}
|
{{ component.summary | inlineMarkdown | safe }}
|
||||||
@@ -20,6 +23,37 @@
|
|||||||
|
|
||||||
{# Component API #}
|
{# Component API #}
|
||||||
{% block afterContent %}
|
{% block afterContent %}
|
||||||
|
{# Importing #}
|
||||||
|
<h2>Importing</h2>
|
||||||
|
<p>
|
||||||
|
Autoloading components via <a href="/docs/#using-a-project">projects</a> is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% set componentName = component.tagName | stripPrefix %}
|
||||||
|
{% set componentPath = ["components/", componentName, "/", componentName, ".js"] | join("") %}
|
||||||
|
<wa-tab-group label="How would you like to import this component?">
|
||||||
|
<wa-tab panel="cdn">CDN</wa-tab>
|
||||||
|
<wa-tab panel="npm">npm</wa-tab>
|
||||||
|
<wa-tab panel="react">React</wa-tab>
|
||||||
|
<wa-tab-panel name="cdn">
|
||||||
|
<p>
|
||||||
|
Let your project code do the work! <a href="/signup">Sign up for free</a> to use a project with your very own CDN — it's the fastest and easiest way to use Web Awesome.
|
||||||
|
</p>
|
||||||
|
</wa-tab-panel>
|
||||||
|
<wa-tab-panel name="npm">
|
||||||
|
<p>
|
||||||
|
To manually import this component from NPM, use the following code.
|
||||||
|
</p>
|
||||||
|
<pre><code class="language-js">import '@awesome.me/webawesome/dist/{{ componentPath }}';</code></pre>
|
||||||
|
</wa-tab-panel>
|
||||||
|
<wa-tab-panel name="react">
|
||||||
|
<p>
|
||||||
|
To manually import this component from React, use the following code.
|
||||||
|
</p>
|
||||||
|
<pre><code class="language-js">import {{ component.name }} from '@awesome.me/webawesome/dist/react/{{ componentName }}';</code></pre>
|
||||||
|
</wa-tab-panel>
|
||||||
|
</wa-tab-group>
|
||||||
|
|
||||||
{# Slots #}
|
{# Slots #}
|
||||||
{% if component.slots.length %}
|
{% if component.slots.length %}
|
||||||
<h2>Slots</h2>
|
<h2>Slots</h2>
|
||||||
@@ -270,38 +304,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Importing #}
|
|
||||||
<h2>Importing</h2>
|
|
||||||
<p>
|
|
||||||
Autoloading components via <a href="/docs/#using-a-project">projects</a> is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
{% set componentName = component.tagName | stripPrefix %}
|
|
||||||
{% set componentPath = ["components/", componentName, "/", componentName, ".js"] | join("") %}
|
|
||||||
<wa-tab-group label="How would you like to import this component?">
|
|
||||||
<wa-tab panel="cdn">CDN</wa-tab>
|
|
||||||
<wa-tab panel="npm">npm</wa-tab>
|
|
||||||
<wa-tab panel="react">React</wa-tab>
|
|
||||||
<wa-tab-panel name="cdn">
|
|
||||||
<p>
|
|
||||||
Let your project code do the work! <a href="/signup">Sign up for free</a> to use a project with your very own CDN — it's the fastest and easiest way to use Web Awesome.
|
|
||||||
</p>
|
|
||||||
</wa-tab-panel>
|
|
||||||
<wa-tab-panel name="npm">
|
|
||||||
<p>
|
|
||||||
To manually import this component from NPM, use the following code.
|
|
||||||
</p>
|
|
||||||
<pre><code class="language-js">import '@awesome.me/webawesome/dist/{{ componentPath }}';</code></pre>
|
|
||||||
</wa-tab-panel>
|
|
||||||
<wa-tab-panel name="react">
|
|
||||||
<p>
|
|
||||||
To manually import this component from React, use the following code.
|
|
||||||
</p>
|
|
||||||
<pre><code class="language-js">import {{ component.name }} from '@awesome.me/webawesome/dist/react/{{ componentName }}';</code></pre>
|
|
||||||
</wa-tab-panel>
|
|
||||||
</wa-tab-group>
|
|
||||||
|
|
||||||
<wa-divider></wa-divider>
|
<wa-divider></wa-divider>
|
||||||
|
|
||||||
<div class="component-help">
|
<div class="component-help">
|
||||||
|
|||||||
@@ -285,9 +285,10 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
|
|||||||
const name = option.querySelector('wa-icon[slot="start"]').name;
|
const name = option.querySelector('wa-icon[slot="start"]').name;
|
||||||
|
|
||||||
// You can return a string, a Lit Template, or an HTMLElement here
|
// You can return a string, a Lit Template, or an HTMLElement here
|
||||||
|
// Important: include data-value so the tag can be removed properly!
|
||||||
return `
|
return `
|
||||||
<wa-tag with-remove>
|
<wa-tag with-remove data-value="${option.value}">
|
||||||
<wa-icon name="${name}" style="padding-inline-end: .5rem;"></wa-icon>
|
<wa-icon name="${name}"></wa-icon>
|
||||||
${option.label}
|
${option.label}
|
||||||
</wa-tag>
|
</wa-tag>
|
||||||
`;
|
`;
|
||||||
@@ -299,6 +300,10 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
|
|||||||
Be sure you trust the content you are outputting! Passing unsanitized user input to `getTag()` can result in XSS vulnerabilities.
|
Be sure you trust the content you are outputting! Passing unsanitized user input to `getTag()` can result in XSS vulnerabilities.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
:::info
|
||||||
|
When using custom tags with `with-remove`, you must include the `data-value` attribute set to the option's value. This allows the select to identify which option to deselect when the tag's remove button is clicked.
|
||||||
|
:::
|
||||||
|
|
||||||
### Lazy loading options
|
### Lazy loading options
|
||||||
|
|
||||||
Lazy loading options works similarly to native `<select>` elements. The select component handles various scenarios intelligently:
|
Lazy loading options works similarly to native `<select>` elements. The select component handles various scenarios intelligently:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
|
|||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
|
- Added `<wa-combobox>` as an experimental pro component [issue:1074]
|
||||||
- Added `layers.css` to define cascade layer order and updated palettes, themes, native styles, and utilities to import the new rule for more fail-safe modularity [pr:1793]
|
- Added `layers.css` to define cascade layer order and updated palettes, themes, native styles, and utilities to import the new rule for more fail-safe modularity [pr:1793]
|
||||||
- Fixed a bug in `<wa-slider>` that caused some touch devices to end up with the incorrect value [issue:1703]
|
- Fixed a bug in `<wa-slider>` that caused some touch devices to end up with the incorrect value [issue:1703]
|
||||||
- Fixed a bug in `<wa-card>` that prevented some slots from being detected correctly [discuss:1450]
|
- Fixed a bug in `<wa-card>` that prevented some slots from being detected correctly [discuss:1450]
|
||||||
@@ -21,6 +22,8 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
|
|||||||
- Fixed a bug in `<wa-tree-item>` that caused the spinner to not show when lazy loading [issue:1678]
|
- Fixed a bug in `<wa-tree-item>` that caused the spinner to not show when lazy loading [issue:1678]
|
||||||
- Fixed a bug in `<wa-dropdown>` that caused the browser to hang when cancelling the `wa-hide` event [issue:1483]
|
- Fixed a bug in `<wa-dropdown>` that caused the browser to hang when cancelling the `wa-hide` event [issue:1483]
|
||||||
- Fixed a bug in `<wa-dropdown-item>` that prevented the icon dependency from being imported [issue:1825]
|
- Fixed a bug in `<wa-dropdown-item>` that prevented the icon dependency from being imported [issue:1825]
|
||||||
|
- Fixed a bug in `<wa-select>` that prevented clicks on the tag's remove button from removing options in multiple mode
|
||||||
|
- Fixed a bug in `<wa-select>` that caused tags to appear in alphabetical order instead of selection order when using `multiple`
|
||||||
- Improved performance of `<wa-icon>` so initial rendering occurs faster, especially with multiple icons on the page [issue:1729]
|
- Improved performance of `<wa-icon>` so initial rendering occurs faster, especially with multiple icons on the page [issue:1729]
|
||||||
- Improved performance of all components by fixing how CSS is imported and reused [issue:1812]
|
- Improved performance of all components by fixing how CSS is imported and reused [issue:1812]
|
||||||
- Modified the default `transition` styles of `<wa-dropdown-item>` to use design tokens [pr:1693]
|
- Modified the default `transition` styles of `<wa-dropdown-item>` to use design tokens [pr:1693]
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import getText from '../../internal/get-text.js';
|
|||||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||||
import { LocalizeController } from '../../utilities/localize.js';
|
import { LocalizeController } from '../../utilities/localize.js';
|
||||||
import '../icon/icon.js';
|
import '../icon/icon.js';
|
||||||
|
import type WaSelect from '../select/select.js';
|
||||||
import styles from './option.styles.js';
|
import styles from './option.styles.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,7 +110,7 @@ export default class WaOption extends WebAwesomeElement {
|
|||||||
this.updateDefaultLabel();
|
this.updateDefaultLabel();
|
||||||
|
|
||||||
if (this.isInitialized) {
|
if (this.isInitialized) {
|
||||||
// When the label changes, tell the controller to update
|
// When the label changes, tell the parent <wa-select> to update
|
||||||
customElements.whenDefined('wa-select').then(() => {
|
customElements.whenDefined('wa-select').then(() => {
|
||||||
const controller = this.closest('wa-select');
|
const controller = this.closest('wa-select');
|
||||||
if (controller) {
|
if (controller) {
|
||||||
@@ -117,6 +118,16 @@ export default class WaOption extends WebAwesomeElement {
|
|||||||
controller.selectionChanged?.();
|
controller.selectionChanged?.();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// When the label changes, tell the parent <wa-combobox> to update
|
||||||
|
customElements.whenDefined('wa-combobox').then(() => {
|
||||||
|
// We cast to <wa-select> because it shares the same API as combobox
|
||||||
|
const controller = this.closest<WaSelect>('wa-combobox');
|
||||||
|
if (controller) {
|
||||||
|
controller.handleDefaultSlotChange();
|
||||||
|
controller.selectionChanged?.();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
}
|
}
|
||||||
@@ -134,7 +145,8 @@ export default class WaOption extends WebAwesomeElement {
|
|||||||
|
|
||||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||||
if (changedProperties.has('defaultSelected')) {
|
if (changedProperties.has('defaultSelected')) {
|
||||||
if (!this.closest('wa-select')?.hasInteracted) {
|
// We cast to <wa-select> because it shares the same API as combobox
|
||||||
|
if (!this.closest<WaSelect>('wa-combobox, wa-select')?.hasInteracted) {
|
||||||
const oldVal = this.selected;
|
const oldVal = this.selected;
|
||||||
this.selected = this.defaultSelected;
|
this.selected = this.defaultSelected;
|
||||||
this.requestUpdate('selected', oldVal);
|
this.requestUpdate('selected', oldVal);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { WaAfterHideEvent } from '../../events/after-hide.js';
|
|||||||
import { WaAfterShowEvent } from '../../events/after-show.js';
|
import { WaAfterShowEvent } from '../../events/after-show.js';
|
||||||
import { WaClearEvent } from '../../events/clear.js';
|
import { WaClearEvent } from '../../events/clear.js';
|
||||||
import { WaHideEvent } from '../../events/hide.js';
|
import { WaHideEvent } from '../../events/hide.js';
|
||||||
import type { WaRemoveEvent } from '../../events/remove.js';
|
import { WaRemoveEvent } from '../../events/remove.js';
|
||||||
import { WaShowEvent } from '../../events/show.js';
|
import { WaShowEvent } from '../../events/show.js';
|
||||||
import { animateWithClass } from '../../internal/animate.js';
|
import { animateWithClass } from '../../internal/animate.js';
|
||||||
import { waitForEvent } from '../../internal/event.js';
|
import { waitForEvent } from '../../internal/event.js';
|
||||||
@@ -99,6 +99,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
|||||||
|
|
||||||
private readonly hasSlotController = new HasSlotController(this, 'hint', 'label');
|
private readonly hasSlotController = new HasSlotController(this, 'hint', 'label');
|
||||||
private readonly localize = new LocalizeController(this);
|
private readonly localize = new LocalizeController(this);
|
||||||
|
private selectionOrder: Map<string, number> = new Map();
|
||||||
private typeToSelectString = '';
|
private typeToSelectString = '';
|
||||||
private typeToSelectTimeout: number;
|
private typeToSelectTimeout: number;
|
||||||
|
|
||||||
@@ -285,6 +286,8 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
|||||||
?pill=${this.pill}
|
?pill=${this.pill}
|
||||||
size=${this.size}
|
size=${this.size}
|
||||||
with-remove
|
with-remove
|
||||||
|
data-value=${option.value}
|
||||||
|
@wa-remove=${(event: WaRemoveEvent) => this.handleTagRemove(event, option)}
|
||||||
>
|
>
|
||||||
${option.label}
|
${option.label}
|
||||||
</wa-tag>
|
</wa-tag>
|
||||||
@@ -520,6 +523,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (this.value !== null) {
|
if (this.value !== null) {
|
||||||
|
this.selectionOrder.clear();
|
||||||
this.setSelectedOptions([]);
|
this.setSelectedOptions([]);
|
||||||
this.displayInput.focus({ preventScroll: true });
|
this.displayInput.focus({ preventScroll: true });
|
||||||
|
|
||||||
@@ -603,24 +607,20 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
|||||||
|
|
||||||
if (this.disabled) return;
|
if (this.disabled) return;
|
||||||
|
|
||||||
|
// Mark as interacted so selectionChanged() uses the correct filter logic
|
||||||
|
this.hasInteracted = true;
|
||||||
|
this.valueHasChanged = true;
|
||||||
|
|
||||||
// Use the directly provided option if available (from getTag method)
|
// Use the directly provided option if available (from getTag method)
|
||||||
let option = directOption;
|
let option = directOption;
|
||||||
|
|
||||||
// If no direct option was provided, find the option from the event path
|
// If no direct option was provided, find the option from the data-value attribute
|
||||||
if (!option) {
|
if (!option) {
|
||||||
const tagElement = (event.target as Element).closest('wa-tag[part~=tag]');
|
const tagElement = (event.target as Element).closest('wa-tag[data-value]') as HTMLElement | null;
|
||||||
|
|
||||||
if (tagElement) {
|
if (tagElement) {
|
||||||
// Find the index of this tag among all tags
|
const value = tagElement.dataset.value;
|
||||||
const tagsContainer = this.shadowRoot?.querySelector('[part="tags"]');
|
option = this.selectedOptions.find(opt => opt.value === value);
|
||||||
if (tagsContainer) {
|
|
||||||
const allTags = Array.from(tagsContainer.children);
|
|
||||||
const index = allTags.indexOf(tagElement as HTMLElement);
|
|
||||||
|
|
||||||
if (index >= 0 && index < this.selectedOptions.length) {
|
|
||||||
option = this.selectedOptions[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,7 +707,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
|||||||
const options = this.getAllOptions();
|
const options = this.getAllOptions();
|
||||||
|
|
||||||
// Update selected options cache
|
// Update selected options cache
|
||||||
this.selectedOptions = options.filter(el => {
|
const newSelectedOptions = options.filter(el => {
|
||||||
if (!this.hasInteracted && !this.valueHasChanged) {
|
if (!this.hasInteracted && !this.valueHasChanged) {
|
||||||
const defaultValue = this.defaultValue;
|
const defaultValue = this.defaultValue;
|
||||||
const defaultValues = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
|
const defaultValues = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
|
||||||
@@ -717,6 +717,32 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
|||||||
return el.selected;
|
return el.selected;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update the selection order map
|
||||||
|
const newSelectedValues = new Set(newSelectedOptions.map(el => el.value));
|
||||||
|
|
||||||
|
// Remove deselected options from the order map
|
||||||
|
for (const value of this.selectionOrder.keys()) {
|
||||||
|
if (!newSelectedValues.has(value)) {
|
||||||
|
this.selectionOrder.delete(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add newly selected options
|
||||||
|
const maxOrder = this.selectionOrder.size > 0 ? Math.max(...this.selectionOrder.values()) : -1;
|
||||||
|
let nextOrder = maxOrder + 1;
|
||||||
|
for (const option of newSelectedOptions) {
|
||||||
|
if (!this.selectionOrder.has(option.value)) {
|
||||||
|
this.selectionOrder.set(option.value, nextOrder++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort options by selection order
|
||||||
|
this.selectedOptions = newSelectedOptions.sort((a, b) => {
|
||||||
|
const orderA = this.selectionOrder.get(a.value) ?? 0;
|
||||||
|
const orderB = this.selectionOrder.get(b.value) ?? 0;
|
||||||
|
return orderA - orderB;
|
||||||
|
});
|
||||||
|
|
||||||
let selectedValues = new Set(this.selectedOptions.map(el => el.value));
|
let selectedValues = new Set(this.selectedOptions.map(el => el.value));
|
||||||
|
|
||||||
// Toggle values present in the DOM from this.value, while preserving options NOT present in the DOM (for lazy loading)
|
// Toggle values present in the DOM from this.value, while preserving options NOT present in the DOM (for lazy loading)
|
||||||
@@ -888,6 +914,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
formResetCallback() {
|
formResetCallback() {
|
||||||
|
this.selectionOrder.clear();
|
||||||
this.value = this.defaultValue;
|
this.value = this.defaultValue;
|
||||||
super.formResetCallback();
|
super.formResetCallback();
|
||||||
this.handleValueChange();
|
this.handleValueChange();
|
||||||
|
|||||||
Reference in New Issue
Block a user