@streamscloud/kit 0.25.2 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/select/cmp.multiselect-tree.svelte +2 -2
- package/dist/ui/select/cmp.multiselect-tree.svelte.d.ts +3 -3
- package/dist/ui/select/cmp.singleselect-async.svelte +3 -2
- package/dist/ui/select/cmp.singleselect.svelte +3 -3
- package/dist/ui/select/index.d.ts +1 -1
- package/dist/ui/select/multiselect-base.svelte +4 -3
- package/dist/ui/select/multiselect-base.svelte.d.ts +1 -1
- package/dist/ui/select/select-core.svelte.d.ts +1 -1
- package/dist/ui/select/select-listbox-localization.d.ts +1 -1
- package/dist/ui/select/select-listbox-localization.js +5 -5
- package/dist/ui/select/select-listbox.svelte +5 -3
- package/dist/ui/select/select-listbox.svelte.d.ts +2 -0
- package/dist/ui/select/types.d.ts +12 -5
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<script lang="ts" generics="T">import { default as MultiselectBase } from './multiselect-base.svelte';
|
|
5
5
|
import { buildFuseIndex, defaultCompare, runFuseSearch } from './select-internals';
|
|
6
6
|
import { isSelectGroup } from './types';
|
|
7
|
-
const { options, value, compare = defaultCompare, size = 'md', placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, searchable = false, groupHeader = 'toggle-all', selectionMode = 'children-only', selectedDisplay = 'checkbox',
|
|
7
|
+
const { options, value, compare = defaultCompare, size = 'md', placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, searchable = false, groupHeader = 'toggle-all', selectionMode = 'children-only', selectedDisplay = 'checkbox', createOptions, icon, chevronIcon, optionSnippet, selectionSnippet, id, name, autocomplete = 'off', 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, on } = $props();
|
|
8
8
|
const fuse = $derived(buildFuseIndex(options));
|
|
9
9
|
const loadOptionsShim = (q) => Promise.resolve(runFuseSearch(fuse, q, options));
|
|
10
10
|
// Include flat roots, not only groups — else a childless root can't be picked as a create-parent.
|
|
@@ -60,7 +60,7 @@ $effect(() => {
|
|
|
60
60
|
groupHeader={groupHeader}
|
|
61
61
|
selectionMode={selectionMode}
|
|
62
62
|
selectedDisplay={selectedDisplay}
|
|
63
|
-
|
|
63
|
+
createOptions={createOptions}
|
|
64
64
|
icon={icon}
|
|
65
65
|
chevronIcon={chevronIcon}
|
|
66
66
|
optionSnippet={optionSnippet}
|
|
@@ -4,7 +4,7 @@ export type MultiselectTreeInstance = {
|
|
|
4
4
|
focus: () => void;
|
|
5
5
|
};
|
|
6
6
|
import type { IconProp } from '../icon';
|
|
7
|
-
import { type SelectItem, type SelectOption } from './types';
|
|
7
|
+
import { type SelectCreateOptions, type SelectItem, type SelectOption } from './types';
|
|
8
8
|
import type { Snippet } from 'svelte';
|
|
9
9
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
10
10
|
declare function $$render<T>(): {
|
|
@@ -57,8 +57,8 @@ declare function $$render<T>(): {
|
|
|
57
57
|
selectedDisplay?: "checkmark" | "checkbox" | "highlight";
|
|
58
58
|
/** Equality comparator for `T`. Required when `T` is an object. @default (a, b) => a === b */
|
|
59
59
|
compare?: (a: T, b: T) => boolean;
|
|
60
|
-
/** Enables creatable mode.
|
|
61
|
-
|
|
60
|
+
/** Enables creatable mode. When at least one top-level root has a `value`, the "Create …" UI expands into a section whose parent picker offers those roots (group or flat); otherwise a plain "Create …" row is shown. `createOptions.prefix` applies only to the plain row — the create-with-parent section keeps its own localization. See {@link SelectCreateOptions}. */
|
|
61
|
+
createOptions?: SelectCreateOptions;
|
|
62
62
|
/** Leading icon — string SVG source, `{ src, color?, size? }` object, or custom snippet. */
|
|
63
63
|
icon?: IconProp;
|
|
64
64
|
/** Trailing chevron icon override. Same shape as `icon`. */
|
|
@@ -9,7 +9,7 @@ import { defaultCompare } from './select-internals';
|
|
|
9
9
|
import { default as SelectListbox } from './select-listbox.svelte';
|
|
10
10
|
import { SingleselectLocalization } from './singleselect-localization';
|
|
11
11
|
import IconChevronDown from '@fluentui/svg-icons/icons/chevron_down_16_regular.svg?raw';
|
|
12
|
-
const { loadOptions, debounceMs = 400, size = 'md', placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, searchable = true, groupHeader = 'static', compare = defaultCompare,
|
|
12
|
+
const { loadOptions, debounceMs = 400, size = 'md', placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, searchable = true, groupHeader = 'static', compare = defaultCompare, createOptions, icon, chevronIcon, optionSnippet, selectionSnippet, listbox, id, name, autocomplete = 'off', 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, ...mode } = $props();
|
|
13
13
|
const localization = new SingleselectLocalization();
|
|
14
14
|
const selectedOption = $derived(mode.value ?? null);
|
|
15
15
|
const selectionLabel = $derived(selectedOption?.label ?? '');
|
|
@@ -23,7 +23,7 @@ const core = createSelectCore({
|
|
|
23
23
|
getDebounceMs: () => debounceMs,
|
|
24
24
|
getCompare: () => compare,
|
|
25
25
|
getSearchable: () => searchable,
|
|
26
|
-
getCanCreate: () => canCreate,
|
|
26
|
+
getCanCreate: () => createOptions?.canCreate,
|
|
27
27
|
getGroupHeader: () => groupHeader,
|
|
28
28
|
isPicked: (option) => selectedOption !== null && compare(option.value, selectedOption.value),
|
|
29
29
|
getSelectedOptions: () => (selectedOption ? [selectedOption] : []),
|
|
@@ -140,6 +140,7 @@ const showClear = $derived(mode.clearable && hasValue && isInteractive && !core.
|
|
|
140
140
|
listboxId={listboxId}
|
|
141
141
|
optionSnippet={optionSnippet}
|
|
142
142
|
bodySnippet={listbox}
|
|
143
|
+
createPrefix={createOptions?.prefix}
|
|
143
144
|
on={{ pickRow: core.pickRow, hoverRow: core.setHighlight, dismiss: core.closePopover }} />
|
|
144
145
|
</div>
|
|
145
146
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<script lang="ts" generics="T">import { default as SingleselectAsync } from './cmp.singleselect-async.svelte';
|
|
5
5
|
import { buildFuseIndex, defaultCompare, runFuseSearch } from './select-internals';
|
|
6
6
|
import { isSelectGroup } from './types';
|
|
7
|
-
const { options, compare = defaultCompare, size = 'md', placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, searchable = true, groupHeader = 'static',
|
|
7
|
+
const { options, compare = defaultCompare, size = 'md', placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, searchable = true, groupHeader = 'static', createOptions, icon, chevronIcon, optionSnippet, selectionSnippet, listbox, id, name, autocomplete = 'off', 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, ...mode } = $props();
|
|
8
8
|
const fuse = $derived(buildFuseIndex(options));
|
|
9
9
|
const loadOptionsShim = (q) => Promise.resolve(runFuseSearch(fuse, q, options));
|
|
10
10
|
const resolvedValue = $derived.by(() => {
|
|
@@ -54,7 +54,7 @@ $effect(() => {
|
|
|
54
54
|
borderless={borderless}
|
|
55
55
|
searchable={searchable}
|
|
56
56
|
groupHeader={groupHeader}
|
|
57
|
-
|
|
57
|
+
createOptions={createOptions}
|
|
58
58
|
icon={icon}
|
|
59
59
|
chevronIcon={chevronIcon}
|
|
60
60
|
optionSnippet={optionSnippet}
|
|
@@ -87,7 +87,7 @@ $effect(() => {
|
|
|
87
87
|
borderless={borderless}
|
|
88
88
|
searchable={searchable}
|
|
89
89
|
groupHeader={groupHeader}
|
|
90
|
-
|
|
90
|
+
createOptions={createOptions}
|
|
91
91
|
icon={icon}
|
|
92
92
|
chevronIcon={chevronIcon}
|
|
93
93
|
optionSnippet={optionSnippet}
|
|
@@ -9,4 +9,4 @@ export type { MultiselectAsyncInstance } from './cmp.multiselect-async.svelte';
|
|
|
9
9
|
export { default as MultiselectTree } from './cmp.multiselect-tree.svelte';
|
|
10
10
|
export type { MultiselectTreeInstance } from './cmp.multiselect-tree.svelte';
|
|
11
11
|
export { isSelectGroup } from './types';
|
|
12
|
-
export type { SelectItem, SelectOption, SelectOptionGroup } from './types';
|
|
12
|
+
export type { SelectCreateOptions, SelectItem, SelectOption, SelectOptionGroup } from './types';
|
|
@@ -13,7 +13,7 @@ import { default as SelectListbox } from './select-listbox.svelte';
|
|
|
13
13
|
import IconCheckmark from '@fluentui/svg-icons/icons/checkmark_16_regular.svg?raw';
|
|
14
14
|
import IconChevronDown from '@fluentui/svg-icons/icons/chevron_down_16_regular.svg?raw';
|
|
15
15
|
import { dndzone } from 'svelte-dnd-action';
|
|
16
|
-
const { loadOptions, debounceMs = 400, value, size = 'md', placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, searchable = true, groupHeader = 'static', compare = defaultCompare, selectionMode = 'children-only', selectedDisplay = 'checkmark',
|
|
16
|
+
const { loadOptions, debounceMs = 400, value, size = 'md', placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, searchable = true, groupHeader = 'static', compare = defaultCompare, selectionMode = 'children-only', selectedDisplay = 'checkmark', createOptions, chipMode = 'inline', reorderable = false, maxVisible = Infinity, showFullItem = false, chipVariant, showChevron = true, hideSelected = false, icon, chevronIcon, optionSnippet, chipSnippet, selectionSnippet, parentSource, id, name, autocomplete = 'off', 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, on } = $props();
|
|
17
17
|
const localization = new MultiselectLocalization();
|
|
18
18
|
const isPicked = (v) => value.some((existing) => compare(existing.value, v));
|
|
19
19
|
const hasValue = $derived(value.length > 0);
|
|
@@ -35,7 +35,7 @@ const core = createSelectCore({
|
|
|
35
35
|
getDebounceMs: () => debounceMs,
|
|
36
36
|
getCompare: () => compare,
|
|
37
37
|
getSearchable: () => searchable,
|
|
38
|
-
getCanCreate: () => canCreate,
|
|
38
|
+
getCanCreate: () => createOptions?.canCreate,
|
|
39
39
|
getHideCreateRow: () => showCreateSection,
|
|
40
40
|
getGroupHeader: () => groupHeader,
|
|
41
41
|
getSelectionMode: () => selectionMode,
|
|
@@ -265,6 +265,7 @@ const handleDndFinalize = (e) => {
|
|
|
265
265
|
optionSnippet={optionSnippet}
|
|
266
266
|
headerSnippet={showCreateSection ? createWithParentSection : undefined}
|
|
267
267
|
hideCreateRows={showCreateSection}
|
|
268
|
+
createPrefix={createOptions?.prefix}
|
|
268
269
|
suppressEmpty={hideSelected}
|
|
269
270
|
on={{ pickRow: core.pickRow, hoverRow: core.setHighlight, dismiss: core.closePopover }} />
|
|
270
271
|
</div>
|
|
@@ -307,7 +308,7 @@ Public Multi cmps proxy here:
|
|
|
307
308
|
- `MultiselectTree` (sync grouped) — groups + cascade + create-with-parent.
|
|
308
309
|
|
|
309
310
|
Owns: input + trigger markup, chip rendering (`inline`/`external`/`selectionSnippet`),
|
|
310
|
-
DnD reorder, listbox, create-with-parent section (when `
|
|
311
|
+
DnD reorder, listbox, create-with-parent section (when `createOptions` + groups in items),
|
|
311
312
|
tri-state group headers, race-prevention against in-flight handlers, 600ms spinner gate,
|
|
312
313
|
debounced/Fuse-driven `loadOptions` plumbing through `select-core`.
|
|
313
314
|
|
|
@@ -50,7 +50,7 @@ interface $$IsomorphicComponent {
|
|
|
50
50
|
* - `MultiselectTree` (sync grouped) — groups + cascade + create-with-parent.
|
|
51
51
|
*
|
|
52
52
|
* Owns: input + trigger markup, chip rendering (`inline`/`external`/`selectionSnippet`),
|
|
53
|
-
* DnD reorder, listbox, create-with-parent section (when `
|
|
53
|
+
* DnD reorder, listbox, create-with-parent section (when `createOptions` + groups in items),
|
|
54
54
|
* tri-state group headers, race-prevention against in-flight handlers, 600ms spinner gate,
|
|
55
55
|
* debounced/Fuse-driven `loadOptions` plumbing through `select-core`.
|
|
56
56
|
*
|
|
@@ -13,7 +13,7 @@ export type SelectCoreConfig<T> = {
|
|
|
13
13
|
getCompare: () => (a: T, b: T) => boolean;
|
|
14
14
|
/** When false, typing never enters filtering mode — the popover is a click/keyboard-only picker. @default true */
|
|
15
15
|
getSearchable?: () => boolean;
|
|
16
|
-
/** Optional `canCreate` validator — presence enables creatable mode. */
|
|
16
|
+
/** Optional `createOptions.canCreate` validator — presence enables creatable mode. */
|
|
17
17
|
getCanCreate: () => ((q: string) => boolean) | undefined;
|
|
18
18
|
/** When true, no `kind:'create'` row is emitted (the host renders its own create UI) — keyboard navigation must not reach a row the listbox doesn't show. @default false */
|
|
19
19
|
getHideCreateRow?: () => boolean;
|
|
@@ -2,15 +2,15 @@ import { AppLocale } from '../../core/locale';
|
|
|
2
2
|
const loc = {
|
|
3
3
|
noMatches: { en: 'No matches', no: 'Ingen treff' },
|
|
4
4
|
createLabel: {
|
|
5
|
-
en: (value) =>
|
|
6
|
-
no: (value) =>
|
|
5
|
+
en: (value, prefix = 'Create') => `${prefix} "${value}"`,
|
|
6
|
+
no: (value, prefix = 'Opprett') => `${prefix} "${value}"`
|
|
7
7
|
}
|
|
8
8
|
};
|
|
9
9
|
export class SelectListboxLocalization {
|
|
10
10
|
get noMatches() {
|
|
11
11
|
return loc.noMatches[AppLocale.current];
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
return loc.createLabel[AppLocale.current];
|
|
15
|
-
}
|
|
13
|
+
createLabel = (value, prefix) => {
|
|
14
|
+
return loc.createLabel[AppLocale.current](value, prefix);
|
|
15
|
+
};
|
|
16
16
|
}
|
|
@@ -6,7 +6,7 @@ import IconCheckboxChecked from '@fluentui/svg-icons/icons/checkbox_checked_16_f
|
|
|
6
6
|
import IconCheckboxIndeterminate from '@fluentui/svg-icons/icons/checkbox_indeterminate_16_filled.svg?raw';
|
|
7
7
|
import IconCheckboxUnchecked from '@fluentui/svg-icons/icons/checkbox_unchecked_16_regular.svg?raw';
|
|
8
8
|
import IconCheckmark from '@fluentui/svg-icons/icons/checkmark_16_regular.svg?raw';
|
|
9
|
-
let { triggerEl, isOpen, rows, highlight, loading = false, listboxId, matchTriggerWidth = true, selectedDisplay = 'checkmark', optionSnippet, headerSnippet, bodySnippet, hideCreateRows = false, suppressEmpty = false, on } = $props();
|
|
9
|
+
let { triggerEl, isOpen, rows, highlight, loading = false, listboxId, matchTriggerWidth = true, selectedDisplay = 'checkmark', optionSnippet, headerSnippet, bodySnippet, hideCreateRows = false, createPrefix, suppressEmpty = false, on } = $props();
|
|
10
10
|
const localization = new SelectListboxLocalization();
|
|
11
11
|
// rows keep their index into the full `rows` array — `highlight` and row ids are core-side indices,
|
|
12
12
|
// so filtering without preserving them shifts every row by one and Enter acts on the wrong row
|
|
@@ -149,8 +149,10 @@ $effect(() => {
|
|
|
149
149
|
onclick={() => on.pickRow(row)}
|
|
150
150
|
onkeydown={() => undefined}
|
|
151
151
|
onmouseenter={() => on.hoverRow(index)}>
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
{#if !createPrefix}
|
|
153
|
+
<span class="select-listbox__row-icon"><Icon src={IconAdd} /></span>
|
|
154
|
+
{/if}
|
|
155
|
+
<span class="select-listbox__row-label">{localization.createLabel(row.query, createPrefix)}</span>
|
|
154
156
|
</div>
|
|
155
157
|
{:else if row.kind === 'group-header'}
|
|
156
158
|
{#if row.selectable}
|
|
@@ -37,6 +37,8 @@ declare function $$render<T>(): {
|
|
|
37
37
|
}]>;
|
|
38
38
|
/** When true, `kind:'create'` rows are skipped (used when `headerSnippet` already renders the create UI). @default false */
|
|
39
39
|
hideCreateRows?: boolean;
|
|
40
|
+
/** Text in front of the query on a `kind:'create'` row, replacing the localized default ("Create"). When set, the leading `+` glyph is dropped. */
|
|
41
|
+
createPrefix?: string;
|
|
40
42
|
/** When true, the panel is not rendered at all if there's nothing to show (no rows, no create, not loading) — suppresses the "No matches" empty state. Used by tag-input style hosts. @default false */
|
|
41
43
|
suppressEmpty?: boolean;
|
|
42
44
|
on: {
|
|
@@ -47,6 +47,13 @@ export type SelectRowCreate = {
|
|
|
47
47
|
query: string;
|
|
48
48
|
};
|
|
49
49
|
export type SelectRow<T> = SelectRowOption<T> | SelectRowGroupHeader<T> | SelectRowCreate;
|
|
50
|
+
/** Creatable mode — passing this object to a select enables the create UI. */
|
|
51
|
+
export type SelectCreateOptions = {
|
|
52
|
+
/** Validates each query — only when it returns true does the create UI appear (and the query isn't a duplicate of an existing label). */
|
|
53
|
+
canCreate: (query: string) => boolean;
|
|
54
|
+
/** Replaces the whole localized text in front of the typed query on the create row: `Create "foo"` → `Invite "foo"`, and drops the leading `+` glyph (the label fills the full row width). Not localized by the kit; pass already-translated text. Has no effect on the create-with-parent section, which keeps its own localized wording. */
|
|
55
|
+
prefix?: string;
|
|
56
|
+
};
|
|
50
57
|
/** Common (data-source-agnostic) prop surface shared by `Singleselect` (sync) and `SingleselectAsync`. */
|
|
51
58
|
export type SingleselectCommonProps<T> = {
|
|
52
59
|
/** Trigger height preset. @default 'md' */
|
|
@@ -72,8 +79,8 @@ export type SingleselectCommonProps<T> = {
|
|
|
72
79
|
groupHeader?: 'static' | 'value';
|
|
73
80
|
/** Equality comparator for `T`. Required when `T` is an object. @default (a, b) => a === b */
|
|
74
81
|
compare?: (a: T, b: T) => boolean;
|
|
75
|
-
/** Enables creatable mode
|
|
76
|
-
|
|
82
|
+
/** Enables creatable mode — a "Create …" row for the current query. See {@link SelectCreateOptions}. */
|
|
83
|
+
createOptions?: SelectCreateOptions;
|
|
77
84
|
/** Leading icon — string SVG source, `{ src, color?, size? }` object, or custom snippet. */
|
|
78
85
|
icon?: IconProp;
|
|
79
86
|
/** Trailing chevron icon override. Same shape as `icon`. */
|
|
@@ -156,8 +163,8 @@ export type MultiselectBaseProps<T> = {
|
|
|
156
163
|
* @default 'checkmark'
|
|
157
164
|
*/
|
|
158
165
|
selectedDisplay?: 'checkmark' | 'checkbox' | 'highlight';
|
|
159
|
-
/** Enables creatable mode.
|
|
160
|
-
|
|
166
|
+
/** Enables creatable mode. When `parentSource` is non-empty, a create-with-parent section replaces the simple "Create …" row. See {@link SelectCreateOptions}. */
|
|
167
|
+
createOptions?: SelectCreateOptions;
|
|
161
168
|
/** Where chips render. `'external'` puts them in a row above the trigger and unlocks `reorderable`. @default 'inline' */
|
|
162
169
|
chipMode?: 'inline' | 'external';
|
|
163
170
|
/** Enables drag-and-drop reorder on the external chip row. Ignored unless `chipMode='external'`. @default false */
|
|
@@ -189,7 +196,7 @@ export type MultiselectBaseProps<T> = {
|
|
|
189
196
|
options: SelectOption<T>[];
|
|
190
197
|
}]>;
|
|
191
198
|
/**
|
|
192
|
-
* Enables the create-with-parent section (shown above the listbox when `canCreate` gates true).
|
|
199
|
+
* Enables the create-with-parent section (shown above the listbox when `createOptions.canCreate` gates true).
|
|
193
200
|
* Receives the FULL parent-candidate list independent of current filter — so the parent picker offers
|
|
194
201
|
* every candidate regardless of what's currently visible in the listbox. When undefined or empty, a
|
|
195
202
|
* single "Create …" row is shown instead.
|