@streamscloud/kit 0.89.1 → 0.90.1
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/README.md +2 -1
- package/dist/core/validation/form-validation-handler/form-validation-handler.svelte.d.ts +4 -2
- package/dist/core/validation/form-validation-handler/form-validation-handler.svelte.js +1 -1
- package/dist/ui/_internal/form-field-label-context/form-field-label-context.svelte.d.ts +17 -0
- package/dist/ui/_internal/form-field-label-context/form-field-label-context.svelte.js +22 -0
- package/dist/ui/_internal/form-field-label-context/index.d.ts +1 -0
- package/dist/ui/_internal/form-field-label-context/index.js +1 -0
- package/dist/ui/checkbox/cmp.checkbox.svelte +11 -2
- package/dist/ui/chip-group/cmp.chip-group.svelte +10 -3
- package/dist/ui/chip-group/cmp.chip-group.svelte.d.ts +4 -2
- package/dist/ui/color-picker/cmp.color-picker.svelte +15 -2
- package/dist/ui/color-picker/cmp.color-picker.svelte.d.ts +1 -3
- package/dist/ui/date-picker/cmp.date-picker.svelte +11 -6
- package/dist/ui/date-picker/cmp.date-picker.svelte.d.ts +2 -9
- package/dist/ui/date-picker/cmp.date-time-picker.svelte +19 -3
- package/dist/ui/date-picker/cmp.date-time-picker.svelte.d.ts +3 -5
- package/dist/ui/date-picker/cmp.time-picker.svelte +3 -1
- package/dist/ui/date-picker/cmp.time-picker.svelte.d.ts +1 -4
- package/dist/ui/document-tabs/types.d.ts +0 -1
- package/dist/ui/form-field/cmp.form-field-validatable.svelte +4 -4
- package/dist/ui/form-field/cmp.form-field-validatable.svelte.d.ts +4 -7
- package/dist/ui/form-field/cmp.form-field.svelte +31 -79
- package/dist/ui/form-field/cmp.form-field.svelte.d.ts +11 -10
- package/dist/ui/form-field/form-field-label.svelte.d.ts +6 -0
- package/dist/ui/form-field/form-field-label.svelte.js +19 -0
- package/dist/ui/form-field/index.d.ts +1 -1
- package/dist/ui/icon-picker/cmp.icon-picker.svelte +3 -1
- package/dist/ui/icon-picker/cmp.icon-picker.svelte.d.ts +3 -1
- package/dist/ui/input/cmp.input.svelte +5 -1
- package/dist/ui/input/types.d.ts +0 -3
- package/dist/ui/numeral-input/cmp.numeral-input.svelte +6 -2
- package/dist/ui/numeral-input/cmp.numeral-input.svelte.d.ts +0 -4
- package/dist/ui/option-pill/cmp.option-pill.svelte.d.ts +0 -2
- package/dist/ui/page-layout/cmp.page-layout.svelte.d.ts +1 -2
- package/dist/ui/pin-input/cmp.pin-input.svelte +11 -3
- package/dist/ui/pin-input/cmp.pin-input.svelte.d.ts +1 -1
- package/dist/ui/popover/cmp.popover.svelte +6 -3
- package/dist/ui/popover/cmp.popover.svelte.d.ts +3 -6
- package/dist/ui/radio/cmp.radio.svelte +12 -3
- package/dist/ui/rich-text-input/cmp.rich-text-input.svelte +19 -7
- package/dist/ui/rich-text-input/cmp.rich-text-input.svelte.d.ts +1 -2
- package/dist/ui/segmented-control/cmp.segmented-control.svelte +5 -3
- package/dist/ui/segmented-control/cmp.segmented-control.svelte.d.ts +1 -2
- package/dist/ui/select/cmp.input-suggest.svelte +5 -1
- package/dist/ui/select/cmp.input-suggest.svelte.d.ts +0 -3
- package/dist/ui/select/cmp.multiselect-tree.svelte +2 -1
- package/dist/ui/select/cmp.multiselect-tree.svelte.d.ts +1 -3
- package/dist/ui/select/cmp.singleselect-async.svelte +9 -3
- package/dist/ui/select/cmp.singleselect-async.svelte.d.ts +0 -2
- package/dist/ui/select/cmp.singleselect.svelte +3 -1
- package/dist/ui/select/cmp.singleselect.svelte.d.ts +0 -2
- package/dist/ui/select/multiselect-base.svelte +10 -3
- package/dist/ui/select/select-core.svelte.js +0 -2
- package/dist/ui/select/select-listbox.svelte +4 -7
- package/dist/ui/select/types.d.ts +2 -6
- package/dist/ui/slider/cmp.slider.svelte +6 -2
- package/dist/ui/slider/cmp.slider.svelte.d.ts +1 -0
- package/dist/ui/tabs/types.d.ts +0 -1
- package/dist/ui/textarea/cmp.textarea.svelte +5 -1
- package/dist/ui/textarea/cmp.textarea.svelte.d.ts +0 -2
- package/dist/ui/toggle/cmp.toggle.svelte +4 -2
- package/dist/ui/tree/cmp.tree-node-view.svelte +13 -15
- package/dist/ui/validatable/cmp.validatable.svelte +1 -2
- package/dist/ui/validatable/cmp.validatable.svelte.d.ts +1 -2
- package/dist/ui/video/cmp.video.svelte.d.ts +0 -1
- package/dist/ui/visual-select/cmp.visual-select.svelte +11 -3
- package/dist/ui/visual-select/cmp.visual-select.svelte.d.ts +2 -1
- package/package.json +3 -2
|
@@ -17,15 +17,12 @@ type Props = {
|
|
|
17
17
|
size?: 'sm' | 'md' | 'lg';
|
|
18
18
|
placeholder?: string;
|
|
19
19
|
disabled?: boolean;
|
|
20
|
-
/** Non-editable but selectable; the popover never opens. */
|
|
21
20
|
readonly?: boolean;
|
|
22
|
-
/** Visual display mode — non-focusable, no hover/focus animations. */
|
|
23
21
|
inert?: boolean;
|
|
24
22
|
/** Visual error state — also sets `aria-invalid="true"`. */
|
|
25
23
|
error?: boolean;
|
|
26
24
|
/** Strip border + background + focus underline. */
|
|
27
25
|
borderless?: boolean;
|
|
28
|
-
/** Show the × clear button when the value is non-empty. */
|
|
29
26
|
clearable?: boolean;
|
|
30
27
|
/**
|
|
31
28
|
* Strip leading / trailing whitespace when the value is committed — focus leaving the control or
|
|
@@ -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', createOptions, icon, chevronIcon, optionSnippet, selectionSnippet, id, name, autocomplete = 'off', 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, on } = $props();
|
|
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-labelledby': ariaLabelledby, '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.
|
|
@@ -70,6 +70,7 @@ $effect(() => {
|
|
|
70
70
|
name={name}
|
|
71
71
|
autocomplete={autocomplete}
|
|
72
72
|
aria-label={ariaLabel}
|
|
73
|
+
aria-labelledby={ariaLabelledby}
|
|
73
74
|
aria-describedby={ariaDescribedby}
|
|
74
75
|
aria-required={ariaRequired}
|
|
75
76
|
on={{
|
|
@@ -21,11 +21,8 @@ declare function $$render<T>(): {
|
|
|
21
21
|
size?: "sm" | "md" | "lg";
|
|
22
22
|
/** Placeholder shown only when nothing is selected and the user is not typing. @default '' */
|
|
23
23
|
placeholder?: string;
|
|
24
|
-
/** Fully disabled — no focus, no interaction, dimmed styling. @default false */
|
|
25
24
|
disabled?: boolean;
|
|
26
|
-
/** Read-only — value visible, but selection cannot change. @default false */
|
|
27
25
|
readonly?: boolean;
|
|
28
|
-
/** Inert subtree — non-interactive but unstyled. @default false */
|
|
29
26
|
inert?: boolean;
|
|
30
27
|
/** Visual error state (accent border + aria-invalid). @default false */
|
|
31
28
|
error?: boolean;
|
|
@@ -81,6 +78,7 @@ declare function $$render<T>(): {
|
|
|
81
78
|
name?: string;
|
|
82
79
|
autocomplete?: HTMLInputAttributes["autocomplete"];
|
|
83
80
|
'aria-label'?: string;
|
|
81
|
+
'aria-labelledby'?: string;
|
|
84
82
|
'aria-describedby'?: string;
|
|
85
83
|
'aria-required'?: boolean;
|
|
86
84
|
on?: {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts" module>export {};
|
|
2
2
|
</script>
|
|
3
3
|
|
|
4
|
-
<script lang="ts" generics="T">import {
|
|
4
|
+
<script lang="ts" generics="T">import { claimFormFieldLabel, getFormFieldLabel, resolveAriaLabelledby } from '../_internal/form-field-label-context';
|
|
5
|
+
import { InputClearButton } from '../_internal/input-clear-button';
|
|
5
6
|
import { Spinner } from '../_internal/spinner';
|
|
6
7
|
import { Icon, IconSlot } from '../icon';
|
|
7
8
|
import { createSelectCore } from './select-core.svelte';
|
|
@@ -9,7 +10,11 @@ import { defaultCompare } from './select-internals';
|
|
|
9
10
|
import { default as SelectListbox } from './select-listbox.svelte';
|
|
10
11
|
import { SingleselectLocalization } from './singleselect-localization';
|
|
11
12
|
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, createOptions, icon, chevronIcon, optionSnippet, selectionSnippet, listbox, id, name, autocomplete = 'off', 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, ...mode } = $props();
|
|
13
|
+
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-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, ...mode } = $props();
|
|
14
|
+
const formFieldLabel = getFormFieldLabel();
|
|
15
|
+
const fallbackId = $props.id();
|
|
16
|
+
const controlId = $derived(id ?? fallbackId);
|
|
17
|
+
claimFormFieldLabel(() => controlId);
|
|
13
18
|
const localization = new SingleselectLocalization();
|
|
14
19
|
const selectedOption = $derived(mode.value ?? null);
|
|
15
20
|
const selectionLabel = $derived(selectedOption?.label ?? '');
|
|
@@ -101,10 +106,11 @@ const showClear = $derived(mode.clearable && hasValue && isInteractive && !core.
|
|
|
101
106
|
aria-activedescendant={core.isOpen ? `${listboxId}-row-${core.highlight}` : undefined}
|
|
102
107
|
aria-autocomplete="list"
|
|
103
108
|
aria-label={ariaLabel ?? name}
|
|
109
|
+
aria-labelledby={resolveAriaLabelledby(formFieldLabel, ariaLabel, ariaLabelledby)}
|
|
104
110
|
aria-describedby={ariaDescribedby}
|
|
105
111
|
aria-required={ariaRequired}
|
|
106
112
|
aria-invalid={error ? 'true' : undefined}
|
|
107
|
-
id={
|
|
113
|
+
id={controlId}
|
|
108
114
|
name={name}
|
|
109
115
|
autocomplete={autocomplete}
|
|
110
116
|
placeholder={inputPlaceholder}
|
|
@@ -15,7 +15,6 @@ declare function $$render<T>(): {
|
|
|
15
15
|
/** Internal — set by the sync `Singleselect` wrapper. Async typeahead is always searchable. @default true */
|
|
16
16
|
searchable?: boolean;
|
|
17
17
|
} & {
|
|
18
|
-
/** Discriminator — `true` enables the X clear button and allows `value` to be `null`. */
|
|
19
18
|
clearable: true;
|
|
20
19
|
/** Currently selected option (pre-resolved — labels render before the first fetch lands). `null` when nothing is picked. */
|
|
21
20
|
value: SelectOption<T> | null | undefined;
|
|
@@ -33,7 +32,6 @@ declare function $$render<T>(): {
|
|
|
33
32
|
/** Internal — set by the sync `Singleselect` wrapper. Async typeahead is always searchable. @default true */
|
|
34
33
|
searchable?: boolean;
|
|
35
34
|
} & {
|
|
36
|
-
/** Discriminator — `false` removes the X clear button; `value` must always be a non-null `SelectOption<T>`. */
|
|
37
35
|
clearable: false;
|
|
38
36
|
/** Currently selected option — non-null. */
|
|
39
37
|
value: SelectOption<T>;
|
|
@@ -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', createOptions, icon, chevronIcon, optionSnippet, selectionSnippet, listbox, id, name, autocomplete = 'off', 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, ...mode } = $props();
|
|
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-labelledby': ariaLabelledby, '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(() => {
|
|
@@ -64,6 +64,7 @@ $effect(() => {
|
|
|
64
64
|
name={name}
|
|
65
65
|
autocomplete={autocomplete}
|
|
66
66
|
aria-label={ariaLabel}
|
|
67
|
+
aria-labelledby={ariaLabelledby}
|
|
67
68
|
aria-describedby={ariaDescribedby}
|
|
68
69
|
aria-required={ariaRequired}
|
|
69
70
|
on={{
|
|
@@ -97,6 +98,7 @@ $effect(() => {
|
|
|
97
98
|
name={name}
|
|
98
99
|
autocomplete={autocomplete}
|
|
99
100
|
aria-label={ariaLabel}
|
|
101
|
+
aria-labelledby={ariaLabelledby}
|
|
100
102
|
aria-describedby={ariaDescribedby}
|
|
101
103
|
aria-required={ariaRequired}
|
|
102
104
|
on={{
|
|
@@ -11,7 +11,6 @@ declare function $$render<T>(): {
|
|
|
11
11
|
/** When true, typing filters the options; when false, it is a click/keyboard-only picker. @default true */
|
|
12
12
|
searchable?: boolean;
|
|
13
13
|
} & {
|
|
14
|
-
/** Discriminator — `true` enables the X clear button and allows `value` to be `null`. */
|
|
15
14
|
clearable: true;
|
|
16
15
|
/** Current selection by value. `null` when nothing is picked. */
|
|
17
16
|
value: T | null | undefined;
|
|
@@ -27,7 +26,6 @@ declare function $$render<T>(): {
|
|
|
27
26
|
/** When true, typing filters the options; when false, it is a click/keyboard-only picker. @default true */
|
|
28
27
|
searchable?: boolean;
|
|
29
28
|
} & {
|
|
30
|
-
/** Discriminator — `false` hides the X button; `value` must always be a non-null `T`. */
|
|
31
29
|
clearable: false;
|
|
32
30
|
/** Current selection by value — non-null. */
|
|
33
31
|
value: T;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts" module>export {};
|
|
2
2
|
</script>
|
|
3
3
|
|
|
4
|
-
<script lang="ts" generics="T">import {
|
|
4
|
+
<script lang="ts" generics="T">import { claimFormFieldLabel, getFormFieldLabel, resolveAriaLabelledby, stopFormFieldLabelHere } from '../_internal/form-field-label-context';
|
|
5
|
+
import { Spinner } from '../_internal/spinner';
|
|
5
6
|
import { Button } from '../button';
|
|
6
7
|
import { Icon, IconSlot } from '../icon';
|
|
7
8
|
import { default as Singleselect } from './cmp.singleselect.svelte';
|
|
@@ -13,7 +14,12 @@ import { default as SelectListbox } from './select-listbox.svelte';
|
|
|
13
14
|
import IconCheckmark from '@fluentui/svg-icons/icons/checkmark_16_regular.svg?raw';
|
|
14
15
|
import IconChevronDown from '@fluentui/svg-icons/icons/chevron_down_16_regular.svg?raw';
|
|
15
16
|
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', 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
|
+
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-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, on } = $props();
|
|
18
|
+
const formFieldLabel = getFormFieldLabel();
|
|
19
|
+
const fallbackId = $props.id();
|
|
20
|
+
const controlId = $derived(id ?? fallbackId);
|
|
21
|
+
claimFormFieldLabel(() => controlId);
|
|
22
|
+
stopFormFieldLabelHere();
|
|
17
23
|
const localization = new MultiselectLocalization();
|
|
18
24
|
const isPicked = (v) => value.some((existing) => compare(existing.value, v));
|
|
19
25
|
const hasValue = $derived(value.length > 0);
|
|
@@ -225,10 +231,11 @@ const handleDndFinalize = (e) => {
|
|
|
225
231
|
aria-activedescendant={core.isOpen ? `${listboxId}-row-${core.highlight}` : undefined}
|
|
226
232
|
aria-autocomplete="list"
|
|
227
233
|
aria-label={ariaLabel ?? name}
|
|
234
|
+
aria-labelledby={resolveAriaLabelledby(formFieldLabel, ariaLabel, ariaLabelledby)}
|
|
228
235
|
aria-describedby={ariaDescribedby}
|
|
229
236
|
aria-required={ariaRequired}
|
|
230
237
|
aria-invalid={error ? 'true' : undefined}
|
|
231
|
-
id={
|
|
238
|
+
id={controlId}
|
|
232
239
|
name={name}
|
|
233
240
|
autocomplete={autocomplete}
|
|
234
241
|
placeholder={!hasValue && !core.isFiltering ? placeholder : ''}
|
|
@@ -286,8 +286,6 @@ export function createSelectCore(config) {
|
|
|
286
286
|
};
|
|
287
287
|
const handleFocus = () => undefined;
|
|
288
288
|
const handleClick = (e) => {
|
|
289
|
-
// Cancels FormField's <label> activation — else its synthetic click re-toggles the popover on every pick.
|
|
290
|
-
e.preventDefault();
|
|
291
289
|
if (!config.getInteractive() || isFiltering) {
|
|
292
290
|
return;
|
|
293
291
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
<script lang="ts" generics="T">import {
|
|
1
|
+
<script lang="ts" generics="T">import { isInsideLabelOf } from '../_internal/form-field-label-context';
|
|
2
|
+
import { Icon } from '../icon';
|
|
2
3
|
import { SelectListboxLocalization } from './select-listbox-localization';
|
|
3
4
|
import { autoUpdate, computePosition, flip, shift, size as floatingSize } from '@floating-ui/dom';
|
|
4
5
|
import IconAdd from '@fluentui/svg-icons/icons/add_16_regular.svg?raw';
|
|
@@ -71,7 +72,7 @@ $effect(() => {
|
|
|
71
72
|
if (!target) {
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
74
|
-
if (triggerEl?.contains(target) || panelEl?.contains(target)) {
|
|
75
|
+
if (triggerEl?.contains(target) || panelEl?.contains(target) || isInsideLabelOf(target, triggerEl)) {
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
on.dismiss();
|
|
@@ -119,11 +120,7 @@ $effect(() => {
|
|
|
119
120
|
role="listbox"
|
|
120
121
|
id={listboxId}
|
|
121
122
|
tabindex="-1"
|
|
122
|
-
onclick={(e) =>
|
|
123
|
-
// preventDefault cancels an ancestor <label>'s activation — stopPropagation alone doesn't. FormField guards its own; a consumer's plain <label> does not.
|
|
124
|
-
e.preventDefault();
|
|
125
|
-
e.stopPropagation();
|
|
126
|
-
}}
|
|
123
|
+
onclick={(e) => e.stopPropagation()}
|
|
127
124
|
onmousedown={(e) => {
|
|
128
125
|
// Keeps the trigger root's mousedown preventDefault (select-core) off panel presses: without it a nested input in the panel never takes the caret and text selection dies.
|
|
129
126
|
e.stopPropagation();
|
|
@@ -70,11 +70,8 @@ export type SingleselectCommonProps<T> = {
|
|
|
70
70
|
size?: 'sm' | 'md' | 'lg';
|
|
71
71
|
/** Placeholder shown only when nothing is selected and the user is not typing. @default '' */
|
|
72
72
|
placeholder?: string;
|
|
73
|
-
/** Fully disabled — no focus, no interaction, dimmed styling. @default false */
|
|
74
73
|
disabled?: boolean;
|
|
75
|
-
/** Read-only — focusable, value visible, but selection cannot change. @default false */
|
|
76
74
|
readonly?: boolean;
|
|
77
|
-
/** Inert subtree — non-interactive but unstyled (use for "view-only" parents). @default false */
|
|
78
75
|
inert?: boolean;
|
|
79
76
|
/** Visual error state (accent border + aria-invalid). @default false */
|
|
80
77
|
error?: boolean;
|
|
@@ -119,6 +116,7 @@ export type SingleselectCommonProps<T> = {
|
|
|
119
116
|
name?: string;
|
|
120
117
|
autocomplete?: HTMLInputAttributes['autocomplete'];
|
|
121
118
|
'aria-label'?: string;
|
|
119
|
+
'aria-labelledby'?: string;
|
|
122
120
|
'aria-describedby'?: string;
|
|
123
121
|
'aria-required'?: boolean;
|
|
124
122
|
};
|
|
@@ -134,11 +132,8 @@ export type MultiselectBaseProps<T> = {
|
|
|
134
132
|
size?: 'sm' | 'md' | 'lg';
|
|
135
133
|
/** Placeholder shown only when nothing is selected and the user is not typing. @default '' */
|
|
136
134
|
placeholder?: string;
|
|
137
|
-
/** Fully disabled — no focus, no interaction, dimmed styling. @default false */
|
|
138
135
|
disabled?: boolean;
|
|
139
|
-
/** Read-only — value visible, but selection cannot change. @default false */
|
|
140
136
|
readonly?: boolean;
|
|
141
|
-
/** Inert subtree — non-interactive but unstyled. @default false */
|
|
142
137
|
inert?: boolean;
|
|
143
138
|
/** Visual error state (accent border + aria-invalid). @default false */
|
|
144
139
|
error?: boolean;
|
|
@@ -216,6 +211,7 @@ export type MultiselectBaseProps<T> = {
|
|
|
216
211
|
name?: string;
|
|
217
212
|
autocomplete?: HTMLInputAttributes['autocomplete'];
|
|
218
213
|
'aria-label'?: string;
|
|
214
|
+
'aria-labelledby'?: string;
|
|
219
215
|
'aria-describedby'?: string;
|
|
220
216
|
'aria-required'?: boolean;
|
|
221
217
|
on?: {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
let { value, min = 0, max = 100, step = 1, disabled = false, showValue = false, withInput = false, ticks = [], valueSuffix = '', on } = $props();
|
|
1
|
+
<script lang="ts">import { claimFormFieldLabel } from '../_internal/form-field-label-context';
|
|
2
|
+
let { value, min = 0, max = 100, step = 1, disabled = false, showValue = false, withInput = false, ticks = [], valueSuffix = '', id, on } = $props();
|
|
3
|
+
const fallbackId = $props.id();
|
|
4
|
+
const controlId = $derived(id ?? fallbackId);
|
|
5
|
+
claimFormFieldLabel(() => controlId);
|
|
3
6
|
let focused = $state(false);
|
|
4
7
|
const fillPct = $derived(max === min ? 0 : ((value - min) / (max - min)) * 100);
|
|
5
8
|
const handleInput = (e) => {
|
|
@@ -25,6 +28,7 @@ const handleNumeric = (e) => {
|
|
|
25
28
|
<input
|
|
26
29
|
type="range"
|
|
27
30
|
class="slider__input"
|
|
31
|
+
id={controlId}
|
|
28
32
|
min={min}
|
|
29
33
|
max={max}
|
|
30
34
|
step={step}
|
package/dist/ui/tabs/types.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<script lang="ts">import { HtmlHelper, StringHelper } from '../../core/utils';
|
|
2
|
+
import { claimFormFieldLabel } from '../_internal/form-field-label-context';
|
|
2
3
|
import { InputEmojiPicker } from '../_internal/input-emoji-picker';
|
|
3
4
|
const { value, size = 'md', disabled = false, readonly = false, inert = false, error = false, borderless = false, emoji = false, singleLine = false, submitOnEnter = false, trim = true, placeholder = '', name = '', id = null, autofocus = false, maxLength = null, rows = 3, autoresize = true, maxRows = 12, autocomplete, spellcheck, 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel, 'aria-required': ariaRequired, on } = $props();
|
|
5
|
+
const fallbackId = $props.id();
|
|
6
|
+
const controlId = $derived(id ?? fallbackId);
|
|
7
|
+
claimFormFieldLabel(() => controlId);
|
|
4
8
|
let textareaEl = $state.raw(undefined);
|
|
5
9
|
const cleanText = $derived(singleLine ? StringHelper.toSingleLineText : StringHelper.toMultilineText);
|
|
6
10
|
const valueParsed = $derived(singleLine && value ? cleanText(value) : (value ?? ''));
|
|
@@ -107,7 +111,7 @@ const focusTextarea = () => textareaEl?.focus();
|
|
|
107
111
|
use:initTextarea
|
|
108
112
|
class="textarea__el"
|
|
109
113
|
name={name}
|
|
110
|
-
id={
|
|
114
|
+
id={controlId}
|
|
111
115
|
placeholder={placeholder}
|
|
112
116
|
disabled={disabled}
|
|
113
117
|
readonly={readonly}
|
|
@@ -4,9 +4,7 @@ type Props = {
|
|
|
4
4
|
/** @default 'md' */
|
|
5
5
|
size?: 'sm' | 'md' | 'lg';
|
|
6
6
|
disabled?: boolean;
|
|
7
|
-
/** Non-editable but selectable. */
|
|
8
7
|
readonly?: boolean;
|
|
9
|
-
/** Visual display mode — non-focusable, no hover/focus animations. */
|
|
10
8
|
inert?: boolean;
|
|
11
9
|
/** Visual error state — also sets `aria-invalid="true"`. */
|
|
12
10
|
error?: boolean;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script lang="ts">import { getFormFieldLabel } from '../_internal/form-field-label-context';
|
|
2
|
+
const { checked, size = 'md', disabled = false, presentational = false, layout = 'spread', label, sublabel, title, name, on } = $props();
|
|
2
3
|
const hasLabel = $derived(label !== undefined || sublabel !== undefined);
|
|
3
4
|
const ariaLabel = $derived(typeof label === 'string' ? label : name);
|
|
5
|
+
const formFieldLabel = getFormFieldLabel();
|
|
4
6
|
const handleClick = (event) => {
|
|
5
7
|
event.stopPropagation();
|
|
6
8
|
if (disabled) {
|
|
@@ -8,7 +10,6 @@ const handleClick = (event) => {
|
|
|
8
10
|
}
|
|
9
11
|
on?.change?.(!checked);
|
|
10
12
|
};
|
|
11
|
-
export {};
|
|
12
13
|
</script>
|
|
13
14
|
|
|
14
15
|
{#snippet switchControl()}
|
|
@@ -25,6 +26,7 @@ export {};
|
|
|
25
26
|
role="switch"
|
|
26
27
|
aria-checked={checked}
|
|
27
28
|
aria-label={ariaLabel}
|
|
29
|
+
aria-labelledby={hasLabel || ariaLabel ? undefined : formFieldLabel?.labelId}
|
|
28
30
|
title={hasLabel ? undefined : title}
|
|
29
31
|
disabled={disabled}
|
|
30
32
|
onclick={handleClick}>
|
|
@@ -195,6 +195,7 @@ const handleActionsClosed = () => {
|
|
|
195
195
|
class:tree-node__leaf--draggable={node.drag}
|
|
196
196
|
class:tree-node__leaf--expandable={children.length}
|
|
197
197
|
class:tree-node__leaf--drag-over={dragOver}
|
|
198
|
+
class:tree-node__leaf--with-actions={actionsShown}
|
|
198
199
|
draggable={!!node.drag && !node.renaming}
|
|
199
200
|
ondragstart={handleDragStart}
|
|
200
201
|
ondragend={handleDragEnd}
|
|
@@ -307,7 +308,6 @@ const handleActionsClosed = () => {
|
|
|
307
308
|
--_tree-node--padding-inline: var(--sc-kit--tree-node--padding-inline, 0.5rem);
|
|
308
309
|
--_tree-node--padding-block: var(--sc-kit--tree-node--padding-block, 0.375rem);
|
|
309
310
|
--_tree-node--indent: var(--sc-kit--tree-node--indent, 1rem);
|
|
310
|
-
--_tree-node--action-opacity: 0;
|
|
311
311
|
position: relative;
|
|
312
312
|
width: 100%;
|
|
313
313
|
color: var(--sc-kit--color--text--primary);
|
|
@@ -325,17 +325,25 @@ const handleActionsClosed = () => {
|
|
|
325
325
|
transition: background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), color var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
326
326
|
}
|
|
327
327
|
.tree-node__leaf:hover {
|
|
328
|
-
--_tree-node--action-opacity: 1;
|
|
329
328
|
color: var(--sc-kit--color--text--primary);
|
|
330
329
|
background: var(--sc-kit--color--bg--hover);
|
|
331
330
|
}
|
|
332
331
|
.tree-node__leaf--active {
|
|
333
|
-
--_tree-node--action-opacity: 1;
|
|
334
332
|
color: var(--sc-kit--color--text--primary);
|
|
335
333
|
background: var(--sc-kit--color--bg--active);
|
|
336
334
|
}
|
|
337
|
-
|
|
338
|
-
--
|
|
335
|
+
@media (any-hover: hover) {
|
|
336
|
+
.tree-node__leaf--with-actions:not(:hover, :has(:global(:focus-visible)), :has(.node-actions--opened)) .node-actions, .tree-node__leaf--with-actions:is(:hover, :has(:global(:focus-visible)), :has(.node-actions--opened)) .tree-node__count {
|
|
337
|
+
position: absolute;
|
|
338
|
+
width: 1px;
|
|
339
|
+
height: 1px;
|
|
340
|
+
margin: -1px;
|
|
341
|
+
padding: 0;
|
|
342
|
+
overflow: hidden;
|
|
343
|
+
clip-path: inset(50%);
|
|
344
|
+
white-space: nowrap;
|
|
345
|
+
border: 0;
|
|
346
|
+
}
|
|
339
347
|
}
|
|
340
348
|
.tree-node__leaf:has(.tree-node__anchor:focus-visible) {
|
|
341
349
|
outline: 2px solid var(--sc-kit--color--border--focus);
|
|
@@ -433,11 +441,6 @@ const handleActionsClosed = () => {
|
|
|
433
441
|
flex-shrink: 0;
|
|
434
442
|
align-items: center;
|
|
435
443
|
margin-left: var(--sc-kit--space--1);
|
|
436
|
-
opacity: var(--_tree-node--action-opacity);
|
|
437
|
-
transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
438
|
-
}
|
|
439
|
-
.node-actions--opened {
|
|
440
|
-
opacity: 1;
|
|
441
444
|
}
|
|
442
445
|
.node-actions__trigger {
|
|
443
446
|
--sc-kit--icon--size: 1rem;
|
|
@@ -452,9 +455,4 @@ const handleActionsClosed = () => {
|
|
|
452
455
|
.node-actions__trigger:hover {
|
|
453
456
|
background: var(--sc-kit--color--bg--active);
|
|
454
457
|
color: var(--sc-kit--color--text--primary);
|
|
455
|
-
}
|
|
456
|
-
@media (any-hover: none) {
|
|
457
|
-
.node-actions {
|
|
458
|
-
opacity: 1;
|
|
459
|
-
}
|
|
460
458
|
}</style>
|
|
@@ -73,6 +73,5 @@ const field = $derived({
|
|
|
73
73
|
<!--
|
|
74
74
|
@component
|
|
75
75
|
Binds any kit input to a `FormValidationHandler` through a render-prop snippet that receives the wired field, and renders the error message below
|
|
76
|
-
it per `errorPlacement`. Inputs stay unaware of validation;
|
|
77
|
-
field's name.
|
|
76
|
+
it per `errorPlacement`. Inputs stay unaware of validation; with a label, `FormFieldValidatable` composes it with `FormField`.
|
|
78
77
|
-->
|
|
@@ -77,8 +77,7 @@ interface $$IsomorphicComponent {
|
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
79
|
* Binds any kit input to a `FormValidationHandler` through a render-prop snippet that receives the wired field, and renders the error message below
|
|
80
|
-
* it per `errorPlacement`. Inputs stay unaware of validation;
|
|
81
|
-
* field's name.
|
|
80
|
+
* it per `errorPlacement`. Inputs stay unaware of validation; with a label, `FormFieldValidatable` composes it with `FormField`.
|
|
82
81
|
*/
|
|
83
82
|
declare const Cmp: $$IsomorphicComponent;
|
|
84
83
|
type Cmp<T extends Record<string, unknown>, K extends keyof T & string> = InstanceType<typeof Cmp<T, K>>;
|
|
@@ -11,7 +11,6 @@ type Props = {
|
|
|
11
11
|
loop?: boolean;
|
|
12
12
|
/** Scroll ancestor for the IntersectionObserver */
|
|
13
13
|
intersectionContainer?: HTMLElement;
|
|
14
|
-
/** Make the video non-interactive */
|
|
15
14
|
inert?: boolean;
|
|
16
15
|
/** Begin loading the video source before it scrolls into view */
|
|
17
16
|
allowPreloading?: boolean;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
<script lang="ts" generics="T">import {
|
|
2
|
-
|
|
1
|
+
<script lang="ts" generics="T">import { getFormFieldLabel, resolveAriaLabelledby } from '../_internal/form-field-label-context';
|
|
2
|
+
import { Icon } from '../icon';
|
|
3
|
+
const { options, cols = 3, disabled = false, compare, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...mode } = $props();
|
|
4
|
+
const formFieldLabel = getFormFieldLabel();
|
|
3
5
|
const eq = (a, b) => (compare ? compare(a, b) : a === b);
|
|
4
6
|
const isSelected = (o) => {
|
|
5
7
|
if (mode.multi) {
|
|
@@ -23,7 +25,13 @@ const toggle = (o) => {
|
|
|
23
25
|
};
|
|
24
26
|
</script>
|
|
25
27
|
|
|
26
|
-
<div
|
|
28
|
+
<div
|
|
29
|
+
class="visual-select"
|
|
30
|
+
class:visual-select--disabled={disabled}
|
|
31
|
+
style:--_cols={cols}
|
|
32
|
+
role={mode.multi ? 'group' : 'radiogroup'}
|
|
33
|
+
aria-label={ariaLabel}
|
|
34
|
+
aria-labelledby={resolveAriaLabelledby(formFieldLabel, ariaLabel, ariaLabelledby)}>
|
|
27
35
|
{#each options as o (o.value)}
|
|
28
36
|
{@const selected = isSelected(o)}
|
|
29
37
|
<button
|
|
@@ -14,10 +14,11 @@ declare function $$render<T>(): {
|
|
|
14
14
|
}[];
|
|
15
15
|
/** Tiles per row in the auto-grid. @default 3 */
|
|
16
16
|
cols?: number;
|
|
17
|
-
/** Global disabled — every tile is non-interactive. */
|
|
18
17
|
disabled?: boolean;
|
|
19
18
|
/** Optional equality fn — defaults to `===`. Useful when `T` is a non-primitive. */
|
|
20
19
|
compare?: (a: T, b: T) => boolean;
|
|
20
|
+
'aria-label'?: string;
|
|
21
|
+
'aria-labelledby'?: string;
|
|
21
22
|
} & ({
|
|
22
23
|
multi: false;
|
|
23
24
|
value: T | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamscloud/kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.90.1",
|
|
4
4
|
"author": "StreamsCloud",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"pack": "npm run build && npm pack",
|
|
18
18
|
"preview": "vite preview",
|
|
19
19
|
"check": "svelte-check --tsconfig ./tsconfig.app.json --fail-on-warnings --output machine && tsc -p tsconfig.node.json && eslint . --format codeframe --max-warnings 0 && prettier --check --plugin prettier-plugin-svelte --cache .",
|
|
20
|
-
"
|
|
20
|
+
"check:fix": "npm run lint:fix && npm run check",
|
|
21
|
+
"lint:fix": "eslint --fix . && prettier --write --plugin prettier-plugin-svelte --cache ."
|
|
21
22
|
},
|
|
22
23
|
"files": [
|
|
23
24
|
"dist"
|