fluentui-svelte 0.6.0 → 0.7.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/components/app-surface/app-surface.svelte +96 -0
- package/dist/components/app-surface/app-surface.svelte.d.ts +22 -0
- package/dist/components/app-surface/docs/llms.md +73 -0
- package/dist/components/app-surface/types.d.ts +19 -0
- package/dist/components/app-surface/types.js +1 -0
- package/dist/components/app-window/app-window.d.ts +3 -0
- package/dist/components/app-window/app-window.js +3 -0
- package/dist/components/app-window/app-window.svelte +193 -0
- package/dist/components/app-window/app-window.svelte.d.ts +25 -0
- package/dist/components/app-window/docs/llms.md +119 -0
- package/dist/components/app-window/types.d.ts +66 -0
- package/dist/components/app-window/types.js +1 -0
- package/dist/components/auto-suggest-box/auto-suggest-box.svelte +47 -31
- package/dist/components/auto-suggest-box/auto-suggest-box.svelte.d.ts +2 -2
- package/dist/components/auto-suggest-box/docs/llms.md +34 -33
- package/dist/components/auto-suggest-box/option.svelte +6 -4
- package/dist/components/auto-suggest-box/types.d.ts +13 -16
- package/dist/components/avatar/avatar.svelte +4 -3
- package/dist/components/avatar/docs/llms.md +1 -1
- package/dist/components/avatar/type.d.ts +1 -1
- package/dist/components/badge/badge-icon.svelte +2 -2
- package/dist/components/badge/badge.svelte +2 -2
- package/dist/components/badge/docs/llms.md +2 -2
- package/dist/components/badge/types.d.ts +2 -2
- package/dist/components/breadcrumb/breadcrumb-button.svelte +2 -2
- package/dist/components/breadcrumb/breadcrumb-divider.svelte +2 -2
- package/dist/components/breadcrumb/breadcrumb-item.svelte +2 -2
- package/dist/components/breadcrumb/breadcrumb.svelte +10 -5
- package/dist/components/breadcrumb/docs/llms.md +11 -11
- package/dist/components/breadcrumb/types.d.ts +7 -7
- package/dist/components/button/button.svelte +2 -2
- package/dist/components/button/docs/llms.md +17 -17
- package/dist/components/button/split-button.svelte +7 -7
- package/dist/components/button/types.d.ts +5 -5
- package/dist/components/calendar-date-picker/calendar-date-picker.svelte +98 -10
- package/dist/components/calendar-date-picker/calendar-date-picker.svelte.d.ts +1 -1
- package/dist/components/calendar-date-picker/docs/llms.md +19 -14
- package/dist/components/calendar-date-picker/types.d.ts +22 -8
- package/dist/components/calendar-view/calendar-view-days.svelte +3 -2
- package/dist/components/calendar-view/calendar-view-grid.js +8 -3
- package/dist/components/calendar-view/calendar-view-item.svelte +10 -4
- package/dist/components/calendar-view/calendar-view-months.svelte +3 -2
- package/dist/components/calendar-view/calendar-view-years.svelte +3 -2
- package/dist/components/calendar-view/calendar-view.svelte +11 -4
- package/dist/components/calendar-view/calendar-view.svelte.js +5 -0
- package/dist/components/calendar-view/docs/llms.md +20 -19
- package/dist/components/calendar-view/types.d.ts +6 -1
- package/dist/components/card/card-footer.svelte +2 -2
- package/dist/components/card/card-header.svelte +5 -4
- package/dist/components/card/card-preview.svelte +13 -2
- package/dist/components/card/card.svelte +5 -5
- package/dist/components/card/docs/llms.md +22 -21
- package/dist/components/card/types.d.ts +8 -4
- package/dist/components/checkbox/checkbox.svelte +47 -26
- package/dist/components/checkbox/checkbox.svelte.d.ts +3 -24
- package/dist/components/checkbox/docs/llms.md +24 -23
- package/dist/components/checkbox/types.d.ts +10 -23
- package/dist/components/date-picker/date-picker.svelte +98 -27
- package/dist/components/date-picker/date-picker.svelte.d.ts +1 -1
- package/dist/components/date-picker/docs/llms.md +23 -20
- package/dist/components/date-picker/types.d.ts +24 -15
- package/dist/components/dialog/dialog-actions.svelte +2 -2
- package/dist/components/dialog/dialog-content.svelte +2 -2
- package/dist/components/dialog/dialog-surface.svelte +102 -9
- package/dist/components/dialog/dialog-title.svelte +2 -2
- package/dist/components/dialog/dialog-trigger.svelte +2 -2
- package/dist/components/dialog/dialog.svelte +22 -11
- package/dist/components/dialog/docs/llms.md +56 -19
- package/dist/components/dialog/types.d.ts +25 -7
- package/dist/components/divider/divider.svelte +6 -4
- package/dist/components/divider/types.d.ts +1 -1
- package/dist/components/dropdown/docs/llms.md +30 -28
- package/dist/components/dropdown/dropdown-option.svelte +17 -5
- package/dist/components/dropdown/dropdown.svelte +182 -84
- package/dist/components/dropdown/dropdown.svelte.d.ts +1 -1
- package/dist/components/dropdown/types.d.ts +26 -20
- package/dist/components/dropdown-select/docs/llms.md +24 -22
- package/dist/components/dropdown-select/dropdown-select-option.svelte +2 -2
- package/dist/components/dropdown-select/dropdown-select.svelte +42 -7
- package/dist/components/dropdown-select/types.d.ts +4 -4
- package/dist/components/expander/docs/llms.md +3 -3
- package/dist/components/expander/expander.svelte +13 -8
- package/dist/components/expander/types.d.ts +5 -5
- package/dist/components/flyout/docs/llms.md +1 -1
- package/dist/components/flyout/flyout.svelte +17 -6
- package/dist/components/flyout/types.d.ts +1 -1
- package/dist/components/hyperlink/docs/llms.md +6 -6
- package/dist/components/hyperlink/hyperlink.svelte +13 -4
- package/dist/components/hyperlink/types.d.ts +1 -1
- package/dist/components/info-bar/docs/llms.md +11 -10
- package/dist/components/info-bar/info-bar.svelte +3 -2
- package/dist/components/info-bar/types.d.ts +4 -0
- package/dist/components/label/docs/llms.md +75 -22
- package/dist/components/label/label-context.d.ts +14 -0
- package/dist/components/label/label-context.js +10 -0
- package/dist/components/label/label.svelte +153 -30
- package/dist/components/label/label.svelte.d.ts +1 -1
- package/dist/components/label/types.d.ts +48 -4
- package/dist/components/list-view/docs/llms.md +103 -33
- package/dist/components/list-view/list-view-item-branch.svelte +257 -0
- package/dist/components/list-view/list-view-item-branch.svelte.d.ts +4 -0
- package/dist/components/list-view/list-view-item.svelte +164 -46
- package/dist/components/list-view/list-view-item.svelte.d.ts +5 -5
- package/dist/components/list-view/list-view.svelte +73 -25
- package/dist/components/list-view/types.d.ts +71 -8
- package/dist/components/list-view/utils.d.ts +10 -0
- package/dist/components/list-view/utils.js +17 -1
- package/dist/components/menu/docs/llms.md +98 -15
- package/dist/components/menu/menu-command-bar-item.svelte +61 -0
- package/dist/components/menu/menu-command-bar-item.svelte.d.ts +14 -0
- package/dist/components/menu/menu-command-bar.svelte +218 -0
- package/dist/components/menu/menu-command-bar.svelte.d.ts +21 -0
- package/dist/components/menu/menu-group.svelte +6 -3
- package/dist/components/menu/menu-item-checkbox.svelte +5 -4
- package/dist/components/menu/menu-item-radio.svelte +5 -4
- package/dist/components/menu/menu-item-switch.svelte +5 -4
- package/dist/components/menu/menu-item.svelte +5 -5
- package/dist/components/menu/menu-item.svelte.d.ts +2 -3
- package/dist/components/menu/menu-list.svelte +10 -1
- package/dist/components/menu/menu-popover.svelte +9 -5
- package/dist/components/menu/menu-trigger.svelte +2 -1
- package/dist/components/menu/menu.svelte.js +1 -0
- package/dist/components/menu/types.d.ts +62 -7
- package/dist/components/number-box/docs/llms.md +6 -4
- package/dist/components/number-box/number-box.svelte +35 -29
- package/dist/components/number-box/number-box.svelte.d.ts +1 -1
- package/dist/components/number-box/types.d.ts +1 -1
- package/dist/components/persona/docs/llms.md +29 -25
- package/dist/components/persona/persona.svelte +8 -6
- package/dist/components/persona/types.d.ts +4 -3
- package/dist/components/progress-bar/docs/llms.md +13 -13
- package/dist/components/progress-bar/progress-bar.svelte +6 -6
- package/dist/components/progress-bar/types.d.ts +4 -4
- package/dist/components/progress-ring/docs/llms.md +14 -14
- package/dist/components/progress-ring/progress-ring.svelte +5 -5
- package/dist/components/progress-ring/types.d.ts +3 -3
- package/dist/components/radio-button/docs/llms.md +11 -8
- package/dist/components/radio-button/radio-button.svelte +38 -12
- package/dist/components/radio-button/radio-button.svelte.d.ts +1 -1
- package/dist/components/radio-button/types.d.ts +11 -8
- package/dist/components/rating/docs/llms.md +15 -14
- package/dist/components/rating/rating.svelte +66 -29
- package/dist/components/rating/rating.svelte.d.ts +1 -1
- package/dist/components/rating/types.d.ts +8 -8
- package/dist/components/side-nav/docs/llms.md +110 -0
- package/dist/components/side-nav/side-nav-branch.svelte +43 -0
- package/dist/components/side-nav/side-nav-branch.svelte.d.ts +4 -0
- package/dist/components/side-nav/side-nav-item.svelte +75 -0
- package/dist/components/side-nav/side-nav-item.svelte.d.ts +25 -0
- package/dist/components/side-nav/side-nav-list.svelte +32 -0
- package/dist/components/side-nav/side-nav-list.svelte.d.ts +7 -0
- package/dist/components/side-nav/side-nav.d.ts +2 -0
- package/dist/components/side-nav/side-nav.js +2 -0
- package/dist/components/side-nav/side-nav.svelte +355 -0
- package/dist/components/side-nav/side-nav.svelte.d.ts +24 -0
- package/dist/components/side-nav/types.d.ts +149 -0
- package/dist/components/side-nav/types.js +1 -0
- package/dist/components/skeleton/docs/llms.md +1 -1
- package/dist/components/skeleton/skeleton.svelte +2 -2
- package/dist/components/skeleton/types.d.ts +1 -1
- package/dist/components/slider/docs/llms.md +31 -28
- package/dist/components/slider/slider.svelte +126 -61
- package/dist/components/slider/slider.svelte.d.ts +1 -3
- package/dist/components/slider/types.d.ts +21 -14
- package/dist/components/tab-view/docs/llms.md +7 -7
- package/dist/components/tab-view/tab-view-tab.svelte +16 -14
- package/dist/components/tab-view/tab-view.svelte +16 -9
- package/dist/components/tab-view/types.d.ts +6 -6
- package/dist/components/table/docs/llms.md +38 -33
- package/dist/components/table/table-body.svelte +2 -2
- package/dist/components/table/table-cell-actions.svelte +8 -2
- package/dist/components/table/table-cell-layout.svelte +2 -2
- package/dist/components/table/table-cell.svelte +2 -2
- package/dist/components/table/table-header-cell.svelte +2 -2
- package/dist/components/table/table-header.svelte +2 -2
- package/dist/components/table/table-row.svelte +2 -2
- package/dist/components/table/table-selection-cell.svelte +6 -6
- package/dist/components/table/table.svelte +2 -2
- package/dist/components/table/types.d.ts +11 -11
- package/dist/components/tag/docs/llms.md +26 -26
- package/dist/components/tag/tag-group.svelte +2 -2
- package/dist/components/tag/tag.svelte +13 -12
- package/dist/components/tag/types.d.ts +6 -4
- package/dist/components/tag-picker/docs/llms.md +61 -55
- package/dist/components/tag-picker/option-group.svelte +17 -4
- package/dist/components/tag-picker/option-group.svelte.d.ts +1 -1
- package/dist/components/tag-picker/option.svelte +15 -4
- package/dist/components/tag-picker/tag-picker.svelte +137 -65
- package/dist/components/tag-picker/tag-picker.svelte.d.ts +2 -2
- package/dist/components/tag-picker/types.d.ts +36 -16
- package/dist/components/text-box/docs/llms.md +31 -4
- package/dist/components/text-box/text-box-button.svelte +3 -3
- package/dist/components/text-box/text-box-button.svelte.d.ts +1 -1
- package/dist/components/text-box/text-box.svelte +41 -14
- package/dist/components/text-box/types.d.ts +5 -5
- package/dist/components/textarea/docs/llms.md +4 -3
- package/dist/components/textarea/textarea.svelte +14 -6
- package/dist/components/textarea/types.d.ts +2 -4
- package/dist/components/time-picker/docs/llms.md +18 -15
- package/dist/components/time-picker/time-picker.svelte +133 -33
- package/dist/components/time-picker/time-picker.svelte.d.ts +1 -1
- package/dist/components/time-picker/types.d.ts +23 -13
- package/dist/components/title-bar/docs/llms.md +382 -0
- package/dist/components/title-bar/title-bar-left-controls.svelte +215 -0
- package/dist/components/title-bar/title-bar-left-controls.svelte.d.ts +22 -0
- package/dist/components/title-bar/title-bar-right-controls.svelte +190 -0
- package/dist/components/title-bar/title-bar-right-controls.svelte.d.ts +27 -0
- package/dist/components/title-bar/title-bar.d.ts +3 -0
- package/dist/components/title-bar/title-bar.js +4 -0
- package/dist/components/title-bar/title-bar.svelte +224 -0
- package/dist/components/title-bar/title-bar.svelte.d.ts +26 -0
- package/dist/components/title-bar/types.d.ts +165 -0
- package/dist/components/title-bar/types.js +1 -0
- package/dist/components/toggle-switch/docs/llms.md +11 -9
- package/dist/components/toggle-switch/toggle-switch.svelte +23 -14
- package/dist/components/toggle-switch/toggle-switch.svelte.d.ts +1 -1
- package/dist/components/toggle-switch/types.d.ts +12 -10
- package/dist/components/tooltip/docs/llms.md +1 -1
- package/dist/components/tooltip/tooltip.svelte +5 -4
- package/dist/components/tooltip/types.d.ts +1 -1
- package/dist/components/top-nav/docs/llms.md +1 -1
- package/dist/components/top-nav/top-nav-item.svelte +2 -2
- package/dist/components/top-nav/top-nav.svelte +2 -2
- package/dist/components/top-nav/types.d.ts +2 -2
- package/dist/components/tree-view/docs/llms.md +11 -11
- package/dist/components/tree-view/tree-view-item-content.svelte +3 -3
- package/dist/components/tree-view/tree-view-item.svelte +13 -5
- package/dist/components/tree-view/tree-view.svelte +20 -5
- package/dist/components/tree-view/tree-view.svelte.d.ts +1 -1
- package/dist/components/tree-view/tree-view.svelte.js +31 -3
- package/dist/components/tree-view/types.d.ts +7 -3
- package/dist/index.d.ts +18 -0
- package/dist/index.js +20 -0
- package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte +26 -5
- package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte.d.ts +2 -0
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal/index.js +1 -0
- package/dist/internal/native-field.svelte.d.ts +44 -0
- package/dist/internal/native-field.svelte.js +85 -0
- package/dist/internal/utils.d.ts +16 -2
- package/dist/internal/utils.js +25 -3
- package/dist/types/index.d.ts +3 -3
- package/package.json +50 -36
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
<script lang="ts" generics="T extends 'li' | 'a' | 'div' = 'li'">
|
|
1
|
+
<script lang="ts" generics="T extends 'li' | 'a' | 'button' | 'div' = 'li'">
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
attachTabspot,
|
|
5
|
+
COMPONENT_NAME,
|
|
6
|
+
getListViewContext,
|
|
7
|
+
ITEM_TAG,
|
|
8
|
+
LIST_VIEW_SKIP,
|
|
9
|
+
setListViewItemContext
|
|
10
|
+
} from './utils.js';
|
|
4
11
|
import Checkbox from '../checkbox/checkbox.svelte';
|
|
5
12
|
import { invokeHandlers, isObjectEmpty } from '../../internal/index.js';
|
|
6
13
|
import type { ListViewItemProps, ListViewItemDOM } from './types.js';
|
|
7
|
-
import { getTabspotAttributes } from 'tabspot';
|
|
14
|
+
import { getTabspotAttributes, type TabspotNodeOptions } from 'tabspot';
|
|
8
15
|
|
|
9
16
|
const ID = $props.id();
|
|
10
17
|
|
|
11
18
|
let {
|
|
12
19
|
as = 'li' as T,
|
|
13
|
-
ref = $bindable(),
|
|
20
|
+
ref = $bindable(null),
|
|
14
21
|
shape,
|
|
15
22
|
disabled,
|
|
16
23
|
active: _active,
|
|
@@ -20,6 +27,8 @@
|
|
|
20
27
|
class: classes,
|
|
21
28
|
onAction,
|
|
22
29
|
onfocus,
|
|
30
|
+
onclick,
|
|
31
|
+
onkeydown,
|
|
23
32
|
children,
|
|
24
33
|
...attributes
|
|
25
34
|
}: ListViewItemProps<T> = $props();
|
|
@@ -30,6 +39,9 @@
|
|
|
30
39
|
*/
|
|
31
40
|
const _as = $derived.by(() => {
|
|
32
41
|
if (!ITEM_TAG.includes(as)) throw new Error(`Invalid tag: ${as}. Must be one of ${ITEM_TAG.join(', ')}`);
|
|
42
|
+
if (as === 'button' && (checkmark || role === 'row')) {
|
|
43
|
+
throw new Error('ListViewItem buttons cannot contain checkmarks or composite row controls. Use li or div.');
|
|
44
|
+
}
|
|
33
45
|
return as;
|
|
34
46
|
});
|
|
35
47
|
|
|
@@ -41,75 +53,176 @@
|
|
|
41
53
|
const { state: _state } = CONTEXT;
|
|
42
54
|
const { handleSelection, registerItem, unregisterItem, getChildrenRole } = CONTEXT.methods;
|
|
43
55
|
const role = $derived(_role || getChildrenRole(as));
|
|
56
|
+
const wrapped = $derived((as === 'a' || as === 'button') && CONTEXT.config.as !== 'div');
|
|
57
|
+
const nativeAction = $derived(as === 'a' || as === 'button');
|
|
58
|
+
const interactive = $derived(
|
|
59
|
+
nativeAction || !!_role || CONTEXT.config.selectionMode !== 'none' || !!onAction || !!onclick
|
|
60
|
+
);
|
|
61
|
+
const tabspotDisabled = $derived(CONTEXT.config.disableTabspot);
|
|
62
|
+
setListViewItemContext({
|
|
63
|
+
get as() {
|
|
64
|
+
return as;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
44
67
|
|
|
45
68
|
let active = $derived(_state.selectedItems.includes(value));
|
|
46
|
-
let checkmarkRef = $state<HTMLDivElement>();
|
|
69
|
+
let checkmarkRef = $state<HTMLDivElement | null>(null);
|
|
47
70
|
|
|
48
|
-
// A
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
// A grid cell contains the secondary controls, independently of the primary row action.
|
|
72
|
+
const cellConfig: TabspotNodeOptions | undefined = $derived(
|
|
73
|
+
role === 'row' && !tabspotDisabled
|
|
74
|
+
? {
|
|
75
|
+
// Checkbox inputs are transparent while their visible wrappers supply the focus ring.
|
|
53
76
|
mover: {
|
|
54
|
-
axis: '
|
|
77
|
+
axis: 'horizontal',
|
|
78
|
+
skip: LIST_VIEW_SKIP
|
|
55
79
|
},
|
|
56
80
|
grouper: { enterDirection: 'right', exitDirection: 'left' }
|
|
57
|
-
}
|
|
58
|
-
:
|
|
81
|
+
}
|
|
82
|
+
: undefined
|
|
83
|
+
);
|
|
84
|
+
const cellAttrs = $derived(cellConfig && getTabspotAttributes(cellConfig));
|
|
85
|
+
// A roving list has one tab stop. Tabspot sets it on mount, so the server HTML gives it to the first item, as Tabspot will.
|
|
86
|
+
// svelte-ignore state_referenced_locally
|
|
87
|
+
const holdsTabStop = !CONTEXT.config.roving || (interactive && !disabled && CONTEXT.methods.claimTabStop(ID));
|
|
88
|
+
const focusAttributes = $derived(
|
|
89
|
+
nativeAction
|
|
90
|
+
? disabled && as === 'a'
|
|
91
|
+
? { tabindex: -1 }
|
|
92
|
+
: {}
|
|
93
|
+
: interactive && !tabspotDisabled
|
|
94
|
+
? { tabindex: disabled || !holdsTabStop ? -1 : 0 }
|
|
95
|
+
: {}
|
|
59
96
|
);
|
|
60
97
|
|
|
98
|
+
function handleClick(e: MouseEvent) {
|
|
99
|
+
const target = e.target instanceof Element ? e.target : null;
|
|
100
|
+
if (target?.closest('.fs-list-view-item') !== e.currentTarget) return;
|
|
101
|
+
const fromCheckmark = !!checkmarkRef?.contains(target);
|
|
102
|
+
const control = target?.closest(
|
|
103
|
+
'a, button, input, select, textarea, [contenteditable="true"], [role="button"], [role="link"]'
|
|
104
|
+
);
|
|
105
|
+
if (!fromCheckmark && control && control !== e.currentTarget) return;
|
|
106
|
+
invokeHandlers(e, disabled, [
|
|
107
|
+
onclick,
|
|
108
|
+
(role !== 'row' || fromCheckmark) && ((event: MouseEvent) => handleSelection(event, value, fromCheckmark)),
|
|
109
|
+
!fromCheckmark && ((event: MouseEvent) => onAction?.(event, value))
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
114
|
+
invokeHandlers(e, disabled, [onkeydown]);
|
|
115
|
+
if (e.defaultPrevented || nativeAction || e.target !== e.currentTarget || !['Enter', ' '].includes(e.key)) return;
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
(e.currentTarget as HTMLElement).dispatchEvent(
|
|
118
|
+
new MouseEvent('click', {
|
|
119
|
+
bubbles: true,
|
|
120
|
+
ctrlKey: e.ctrlKey,
|
|
121
|
+
metaKey: e.metaKey,
|
|
122
|
+
shiftKey: e.shiftKey
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
61
127
|
onMount(() => {
|
|
62
|
-
registerItem
|
|
63
|
-
return () => unregisterItem
|
|
128
|
+
registerItem(ID, () => ({ value, disabled: !!disabled, element: ref ?? null }));
|
|
129
|
+
return () => unregisterItem(ID);
|
|
64
130
|
});
|
|
65
131
|
</script>
|
|
66
132
|
|
|
67
|
-
|
|
68
|
-
this={_as}
|
|
69
|
-
{role}
|
|
70
|
-
data-value={value}
|
|
71
|
-
aria-selected={role === 'option' ? active : undefined}
|
|
72
|
-
aria-disabled={disabled || undefined}
|
|
73
|
-
bind:this={ref as ListViewItemDOM[T]}
|
|
74
|
-
class={[
|
|
75
|
-
'fs-list-view-item',
|
|
76
|
-
shape ? shape : _shape,
|
|
77
|
-
classes,
|
|
78
|
-
{ disabled },
|
|
79
|
-
role !== 'row' && 'indicator',
|
|
80
|
-
(_active || active) && 'active',
|
|
81
|
-
!disabled && role && 'interactive'
|
|
82
|
-
]}
|
|
83
|
-
{...tabspotAttrs}
|
|
84
|
-
onfocus={(e: FocusEvent) => invokeHandlers(e, disabled, [(event: FocusEvent) => onfocus?.(event, value || '')])}
|
|
85
|
-
onclick={(e: MouseEvent) => {
|
|
86
|
-
// The checkmark only toggles the selection, even on a row. The primary action is for the rest of the item.
|
|
87
|
-
const fromCheckmark = !!checkmarkRef?.contains(e.target as Node);
|
|
88
|
-
invokeHandlers(e, disabled, [
|
|
89
|
-
(role !== 'row' || fromCheckmark) && ((event: MouseEvent) => handleSelection?.(event, value, fromCheckmark)),
|
|
90
|
-
!fromCheckmark && ((event: MouseEvent) => onAction?.(event, value || ''))
|
|
91
|
-
]);
|
|
92
|
-
}}
|
|
93
|
-
{...attributes}
|
|
94
|
-
>
|
|
133
|
+
{#snippet content()}
|
|
95
134
|
{#if checkmark}
|
|
96
135
|
<Checkbox
|
|
136
|
+
{...!isObjectEmpty(checkmark) ? checkmark : {}}
|
|
97
137
|
{disabled}
|
|
98
138
|
bind:checked={active}
|
|
99
139
|
bind:wrapperRef={checkmarkRef}
|
|
100
|
-
tabindex={-1}
|
|
101
|
-
{
|
|
140
|
+
tabindex={tabspotDisabled ? undefined : -1}
|
|
141
|
+
aria-label={checkmark?.['aria-label'] ?? attributes['aria-label'] ?? value}
|
|
142
|
+
inert={role === 'option' || undefined}
|
|
102
143
|
/>
|
|
103
144
|
{/if}
|
|
104
145
|
{@render children?.()}
|
|
105
|
-
|
|
146
|
+
{/snippet}
|
|
147
|
+
|
|
148
|
+
{#snippet item()}
|
|
149
|
+
<svelte:element
|
|
150
|
+
this={_as}
|
|
151
|
+
{...attributes}
|
|
152
|
+
role={wrapped || (nativeAction && role === 'listitem') ? undefined : role}
|
|
153
|
+
type={as === 'button' ? 'button' : undefined}
|
|
154
|
+
disabled={as === 'button' ? disabled : undefined}
|
|
155
|
+
data-value={value}
|
|
156
|
+
data-list-view-interactive={interactive || undefined}
|
|
157
|
+
aria-selected={!wrapped && (role === 'option' || role === 'row') ? active : undefined}
|
|
158
|
+
aria-current={role !== 'option' && role !== 'row' && (_active || active) ? 'true' : undefined}
|
|
159
|
+
aria-disabled={disabled || undefined}
|
|
160
|
+
bind:this={ref as ListViewItemDOM[T]}
|
|
161
|
+
class={[
|
|
162
|
+
'fs-list-view-item',
|
|
163
|
+
shape ? shape : _shape,
|
|
164
|
+
classes,
|
|
165
|
+
{ disabled },
|
|
166
|
+
role !== 'row' && 'indicator',
|
|
167
|
+
(_active || active) && 'active',
|
|
168
|
+
!disabled && interactive && 'interactive'
|
|
169
|
+
]}
|
|
170
|
+
{...focusAttributes}
|
|
171
|
+
onfocus={(e: FocusEvent) => {
|
|
172
|
+
if (e.target === e.currentTarget) invokeHandlers(e, disabled, [() => onfocus?.(e, value)]);
|
|
173
|
+
}}
|
|
174
|
+
onclick={handleClick}
|
|
175
|
+
onkeydown={handleKeydown}
|
|
176
|
+
>
|
|
177
|
+
{#if role === 'row'}
|
|
178
|
+
<div role="gridcell" {...cellAttrs} {@attach attachTabspot(cellConfig)}>
|
|
179
|
+
{@render content()}
|
|
180
|
+
</div>
|
|
181
|
+
{:else}
|
|
182
|
+
{@render content()}
|
|
183
|
+
{/if}
|
|
184
|
+
</svelte:element>
|
|
185
|
+
{/snippet}
|
|
186
|
+
|
|
187
|
+
{#if wrapped}
|
|
188
|
+
<li {role} aria-selected={role === 'option' || role === 'row' ? active : undefined} class="fs-list-view-item-wrapper">
|
|
189
|
+
{@render item()}
|
|
190
|
+
</li>
|
|
191
|
+
{:else}
|
|
192
|
+
{@render item()}
|
|
193
|
+
{/if}
|
|
106
194
|
|
|
107
195
|
<style>
|
|
196
|
+
.fs-list-view-item-wrapper {
|
|
197
|
+
list-style: none;
|
|
198
|
+
& > :global(.fs-list-view-item) {
|
|
199
|
+
width: 100%;
|
|
200
|
+
box-sizing: border-box;
|
|
201
|
+
text-align: start;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
.fs-list-view-item-wrapper,
|
|
205
|
+
.fs-list-view-item:has(> :global(.fs-list-view)) {
|
|
206
|
+
& > :global(.fs-list-view) {
|
|
207
|
+
padding-inline-start: 1rem;
|
|
208
|
+
box-sizing: border-box;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
.fs-list-view-item:has(> :global(.fs-list-view)) {
|
|
212
|
+
display: block;
|
|
213
|
+
}
|
|
214
|
+
[role='gridcell'] {
|
|
215
|
+
display: flex;
|
|
216
|
+
gap: 0.5rem;
|
|
217
|
+
align-items: center;
|
|
218
|
+
width: 100%;
|
|
219
|
+
}
|
|
108
220
|
.fs-list-view-item {
|
|
109
221
|
border-radius: var(--fs-control-border-radius);
|
|
110
222
|
font-size: var(--fs-body2-font-size);
|
|
111
223
|
color: var(--fs-text-primary);
|
|
112
224
|
border: none;
|
|
225
|
+
background: var(--fs-subtle-fill-transparent);
|
|
113
226
|
padding: 0.275rem 0.713rem 0.4rem 0.713rem;
|
|
114
227
|
position: relative;
|
|
115
228
|
outline: none;
|
|
@@ -174,4 +287,9 @@
|
|
|
174
287
|
border-radius: 50vw;
|
|
175
288
|
}
|
|
176
289
|
}
|
|
290
|
+
@media (prefers-reduced-motion: reduce) {
|
|
291
|
+
.fs-list-view-item.interactive.indicator.active::after {
|
|
292
|
+
transition: none;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
177
295
|
</style>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ListViewItemProps } from './types.ts';
|
|
2
|
-
declare function $$render<T extends 'li' | 'a' | 'div' = 'li'>(): {
|
|
2
|
+
declare function $$render<T extends 'li' | 'a' | 'button' | 'div' = 'li'>(): {
|
|
3
3
|
props: ListViewItemProps<T>;
|
|
4
4
|
exports: {};
|
|
5
5
|
bindings: "ref";
|
|
6
6
|
slots: {};
|
|
7
7
|
events: {};
|
|
8
8
|
};
|
|
9
|
-
declare class __sveltets_Render<T extends 'li' | 'a' | 'div' = 'li'> {
|
|
9
|
+
declare class __sveltets_Render<T extends 'li' | 'a' | 'button' | 'div' = 'li'> {
|
|
10
10
|
props(): ReturnType<typeof $$render<T>>['props'];
|
|
11
11
|
events(): ReturnType<typeof $$render<T>>['events'];
|
|
12
12
|
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
@@ -14,12 +14,12 @@ declare class __sveltets_Render<T extends 'li' | 'a' | 'div' = 'li'> {
|
|
|
14
14
|
exports(): {};
|
|
15
15
|
}
|
|
16
16
|
interface $$IsomorphicComponent {
|
|
17
|
-
new <T extends 'li' | 'a' | 'div' = 'li'>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
17
|
+
new <T extends 'li' | 'a' | 'button' | 'div' = 'li'>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
18
18
|
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
19
19
|
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
20
|
-
<T extends 'li' | 'a' | 'div' = 'li'>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
20
|
+
<T extends 'li' | 'a' | 'button' | 'div' = 'li'>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
21
21
|
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
22
22
|
}
|
|
23
23
|
declare const ListViewItem: $$IsomorphicComponent;
|
|
24
|
-
type ListViewItem<T extends 'li' | 'a' | 'div' = 'li'> = InstanceType<typeof ListViewItem<T>>;
|
|
24
|
+
type ListViewItem<T extends 'li' | 'a' | 'button' | 'div' = 'li'> = InstanceType<typeof ListViewItem<T>>;
|
|
25
25
|
export default ListViewItem;
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
<script lang="ts" generics="Tag extends 'ul' | 'ol' | 'div' = 'ul'">
|
|
2
2
|
import type { ListViewContext, ListViewProps, ListViewDOM } from './types.js';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
attachTabspot,
|
|
5
|
+
getListViewContext,
|
|
6
|
+
getListViewItemContext,
|
|
7
|
+
getRole,
|
|
8
|
+
LIST_VIEW_SKIP,
|
|
9
|
+
TAG,
|
|
10
|
+
setListViewContext
|
|
11
|
+
} from './utils.js';
|
|
12
|
+
import { getTabspotAttributes, type TabspotNodeOptions } from 'tabspot';
|
|
5
13
|
|
|
6
14
|
let {
|
|
7
15
|
as = 'ul' as Tag,
|
|
8
|
-
ref = $bindable(),
|
|
16
|
+
ref = $bindable(null),
|
|
9
17
|
selectionMode = 'none',
|
|
10
18
|
navigationMode = 'items',
|
|
19
|
+
manageSpecialKeys = true,
|
|
11
20
|
shape = 'rounded',
|
|
12
21
|
selectedItems = $bindable([]),
|
|
13
22
|
onSelectionChange,
|
|
14
23
|
disableTabspot = false,
|
|
15
24
|
toggleOnClick = false,
|
|
16
25
|
children,
|
|
26
|
+
class: classes,
|
|
17
27
|
...attributes
|
|
18
28
|
}: ListViewProps<Tag> = $props();
|
|
19
29
|
|
|
@@ -21,40 +31,69 @@
|
|
|
21
31
|
* The tag the invariant rules over. The markup renders it through here, so it is checked
|
|
22
32
|
* during SSR and on every prop update rather than only once on mount.
|
|
23
33
|
*/
|
|
34
|
+
const itemContext = getListViewItemContext();
|
|
24
35
|
const _as = $derived.by(() => {
|
|
25
36
|
if (!TAG.includes(as)) throw new Error(`Invalid tag: ${as}. Must be one of ${TAG.join(', ')}`);
|
|
37
|
+
if (itemContext?.as === 'a' || itemContext?.as === 'button') {
|
|
38
|
+
throw new Error('Nested ListView must be inside a li or div ListViewItem, outside links and buttons.');
|
|
39
|
+
}
|
|
26
40
|
return as;
|
|
27
41
|
});
|
|
28
42
|
|
|
29
|
-
const
|
|
43
|
+
const parent = getListViewContext();
|
|
44
|
+
const tabspotDisabled = $derived(disableTabspot || !!parent?.config.disableTabspot);
|
|
45
|
+
const ROLE = $derived(attributes.role ?? getRole(as, selectionMode, navigationMode));
|
|
30
46
|
|
|
31
47
|
const MULTISELECTABLE = $derived(selectionMode === 'multiselect' || selectionMode === 'extended');
|
|
32
48
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
const tabspotConfig: TabspotNodeOptions | undefined = $derived(
|
|
50
|
+
tabspotDisabled
|
|
51
|
+
? undefined
|
|
52
|
+
: {
|
|
53
|
+
...(parent
|
|
54
|
+
? { grouper: { enterDirection: 'right', exitDirection: 'left' } }
|
|
55
|
+
: { root: { manageSpecialKeys } }),
|
|
56
|
+
mover: {
|
|
57
|
+
axis: 'vertical',
|
|
58
|
+
items:
|
|
59
|
+
'.fs-list-view-item[data-list-view-interactive], .fs-list-view-item:not([data-list-view-interactive]) :is(a[href], button), .fs-list-view-item-branch-trigger',
|
|
60
|
+
visibilityAware: 'Visible',
|
|
61
|
+
skip: LIST_VIEW_SKIP,
|
|
62
|
+
...(!parent && { activation: { mode: 'focus', roving: navigationMode === 'composite' } })
|
|
63
|
+
}
|
|
42
64
|
}
|
|
43
|
-
})
|
|
44
65
|
);
|
|
66
|
+
const tabspotAttrs = $derived(tabspotConfig && getTabspotAttributes(tabspotConfig));
|
|
45
67
|
|
|
46
68
|
let anchorIndex: number | null = $state(null);
|
|
47
69
|
|
|
48
70
|
let items: {
|
|
49
71
|
id: string;
|
|
50
|
-
value: string;
|
|
51
|
-
disabled: boolean;
|
|
72
|
+
getItem: () => { value: string; disabled: boolean; element: HTMLElement | null };
|
|
52
73
|
}[] = $state([]);
|
|
53
74
|
|
|
75
|
+
// The item that holds the tab stop in the server HTML, before Tabspot runs. Plain, since only the first render reads it.
|
|
76
|
+
let tabStopId: string | null = null;
|
|
77
|
+
|
|
54
78
|
const CONTEXT: ListViewContext = $state({
|
|
55
79
|
config: {
|
|
56
80
|
get shape() {
|
|
57
81
|
return shape;
|
|
82
|
+
},
|
|
83
|
+
get as() {
|
|
84
|
+
return as;
|
|
85
|
+
},
|
|
86
|
+
get selectionMode() {
|
|
87
|
+
return selectionMode;
|
|
88
|
+
},
|
|
89
|
+
get role() {
|
|
90
|
+
return ROLE;
|
|
91
|
+
},
|
|
92
|
+
get disableTabspot() {
|
|
93
|
+
return tabspotDisabled;
|
|
94
|
+
},
|
|
95
|
+
get roving() {
|
|
96
|
+
return !parent && navigationMode === 'composite';
|
|
58
97
|
}
|
|
59
98
|
},
|
|
60
99
|
state: {
|
|
@@ -73,19 +112,27 @@
|
|
|
73
112
|
},
|
|
74
113
|
events: null,
|
|
75
114
|
methods: {
|
|
115
|
+
claimTabStop: (id) => (tabStopId ??= id) === id,
|
|
76
116
|
handleSelection: (e: MouseEvent, value: string, toggle = false) => {
|
|
77
117
|
if (selectionMode === 'none') return;
|
|
78
118
|
|
|
79
|
-
|
|
119
|
+
// Registration order can differ from the DOM after keyed items are reordered.
|
|
120
|
+
const orderedItems = items
|
|
121
|
+
.map((item) => item.getItem())
|
|
122
|
+
.sort((a, b) => {
|
|
123
|
+
if (!a.element || !b.element) return 0;
|
|
124
|
+
return a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
125
|
+
});
|
|
126
|
+
const itemIndex = orderedItems.findIndex((item) => item.value === value);
|
|
80
127
|
if (itemIndex === -1) return;
|
|
81
128
|
|
|
82
|
-
const item =
|
|
129
|
+
const item = orderedItems[itemIndex];
|
|
83
130
|
if (item.disabled) return;
|
|
84
131
|
|
|
85
132
|
if (selectionMode === 'extended' && e.shiftKey && anchorIndex !== null) {
|
|
86
133
|
const start = Math.min(anchorIndex, itemIndex);
|
|
87
134
|
const end = Math.max(anchorIndex, itemIndex);
|
|
88
|
-
const rangeValues =
|
|
135
|
+
const rangeValues = orderedItems
|
|
89
136
|
.slice(start, end + 1)
|
|
90
137
|
.filter((i) => !i.disabled)
|
|
91
138
|
.map((i) => i.value);
|
|
@@ -101,13 +148,13 @@
|
|
|
101
148
|
}
|
|
102
149
|
onSelectionChange?.(e, selectedItems);
|
|
103
150
|
},
|
|
104
|
-
registerItem: (id
|
|
105
|
-
items.push({ id,
|
|
151
|
+
registerItem: (id, getItem) => {
|
|
152
|
+
items.push({ id, getItem });
|
|
106
153
|
},
|
|
107
154
|
unregisterItem: (id: string) => {
|
|
108
155
|
items = items.filter((item) => item.id !== id);
|
|
109
156
|
},
|
|
110
|
-
getChildrenRole: (_tag: 'li' | 'a' | 'div') => {
|
|
157
|
+
getChildrenRole: (_tag: 'li' | 'a' | 'button' | 'div') => {
|
|
111
158
|
if (ROLE === 'grid') return 'row';
|
|
112
159
|
if (ROLE === 'listbox') return 'option';
|
|
113
160
|
return _tag !== 'li' ? 'listitem' : undefined;
|
|
@@ -120,12 +167,13 @@
|
|
|
120
167
|
|
|
121
168
|
<svelte:element
|
|
122
169
|
this={_as}
|
|
170
|
+
{...attributes}
|
|
123
171
|
bind:this={ref as ListViewDOM[Tag]}
|
|
124
|
-
class=
|
|
172
|
+
class={['fs-list-view', classes]}
|
|
125
173
|
role={ROLE}
|
|
126
|
-
aria-multiselectable={MULTISELECTABLE || undefined}
|
|
174
|
+
aria-multiselectable={((ROLE === 'grid' || ROLE === 'listbox') && MULTISELECTABLE) || undefined}
|
|
127
175
|
{...tabspotAttrs}
|
|
128
|
-
{
|
|
176
|
+
{@attach attachTabspot(tabspotConfig)}
|
|
129
177
|
>
|
|
130
178
|
{@render children?.()}
|
|
131
179
|
</svelte:element>
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { CheckboxProps } from '../checkbox/types.ts';
|
|
2
2
|
import type { FSContext } from '../../internal/index.js';
|
|
3
3
|
import type { PolymorphicProps, Shapes } from '../../types/index.js';
|
|
4
|
+
import type { AriaRole, HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
|
5
|
+
import type { Component, Snippet } from 'svelte';
|
|
6
|
+
import type { TabspotRootOptions } from 'tabspot';
|
|
4
7
|
export type SelectionMode = 'none' | 'single' | 'multiselect' | 'extended';
|
|
5
8
|
export type NavigationMode = 'items' | 'composite';
|
|
6
9
|
/** @propsmith ListViewProps */
|
|
7
10
|
export type ListViewProps<T extends 'ul' | 'ol' | 'div'> = {
|
|
11
|
+
/** Overrides the list role and the roles derived for its items. Use `list` for nested navigation with independent selection. */
|
|
12
|
+
role?: AriaRole;
|
|
8
13
|
/** How many items can be selected, and how the selection is made.
|
|
9
14
|
* @default 'none'
|
|
10
15
|
*/
|
|
@@ -13,6 +18,11 @@ export type ListViewProps<T extends 'ul' | 'ol' | 'div'> = {
|
|
|
13
18
|
* @default 'items'
|
|
14
19
|
*/
|
|
15
20
|
navigationMode?: NavigationMode;
|
|
21
|
+
/** Which special keys Tabspot handles at the root. Set individual keys to leave Escape to an enclosing panel. Omitted keys in an object are off.
|
|
22
|
+
* @type boolean | TabspotRootOptions['manageSpecialKeys']
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
manageSpecialKeys?: TabspotRootOptions['manageSpecialKeys'];
|
|
16
26
|
/** The values of the selected items.
|
|
17
27
|
* @bindable
|
|
18
28
|
*/
|
|
@@ -36,7 +46,7 @@ export type ListViewProps<T extends 'ul' | 'ol' | 'div'> = {
|
|
|
36
46
|
* @type HTMLUListElement | HTMLOListElement | HTMLDivElement
|
|
37
47
|
* @bindable
|
|
38
48
|
*/
|
|
39
|
-
ref?: ListViewDOM[T];
|
|
49
|
+
ref?: ListViewDOM[T] | null;
|
|
40
50
|
/** Opts the list out of the tabspot focus management, to wire your own.
|
|
41
51
|
* @default false
|
|
42
52
|
*/
|
|
@@ -48,12 +58,14 @@ export type ListViewDOM = {
|
|
|
48
58
|
div: HTMLDivElement;
|
|
49
59
|
};
|
|
50
60
|
/** @propsmith ListViewItemProps */
|
|
51
|
-
export type ListViewItemProps<T extends 'li' | 'a' | 'div' = 'li'> = {
|
|
61
|
+
export type ListViewItemProps<T extends 'li' | 'a' | 'button' | 'div' = 'li'> = {
|
|
52
62
|
/** The HTML element to render the item as.
|
|
53
|
-
*
|
|
63
|
+
* Links and buttons are wrapped in a list item when the list renders as `ul` or `ol`.
|
|
64
|
+
* Use `li` or `div` for nested lists and content with multiple controls. Buttons cannot contain checkmarks or composite rows.
|
|
65
|
+
* @type 'li' | 'a' | 'button' | 'div'
|
|
54
66
|
* @default 'li'
|
|
55
67
|
*/
|
|
56
|
-
as?: T | 'a' | 'div';
|
|
68
|
+
as?: T | 'a' | 'button' | 'div';
|
|
57
69
|
/** The item can have a circular, rounded or square shape. Inherited from the list by default. */
|
|
58
70
|
shape?: Shapes;
|
|
59
71
|
/** The value reported to the list when the item is selected. Falls back to a generated id. */
|
|
@@ -73,18 +85,63 @@ export type ListViewItemProps<T extends 'li' | 'a' | 'div' = 'li'> = {
|
|
|
73
85
|
/** Called when the item receives the focus. */
|
|
74
86
|
onfocus?: (e: Event, value: string) => void;
|
|
75
87
|
/** The DOM reference of the item element.
|
|
76
|
-
* @type HTMLLIElement | HTMLAnchorElement | HTMLDivElement
|
|
88
|
+
* @type HTMLLIElement | HTMLAnchorElement | HTMLButtonElement | HTMLDivElement
|
|
77
89
|
* @bindable
|
|
78
90
|
*/
|
|
79
91
|
ref?: ListViewItemDOM[T] | null;
|
|
80
|
-
} & PolymorphicProps<T>;
|
|
92
|
+
} & PolymorphicProps<T, 'onfocus'>;
|
|
81
93
|
export type ListViewItemDOM = {
|
|
82
94
|
li: HTMLLIElement;
|
|
83
95
|
a: HTMLAnchorElement;
|
|
96
|
+
button: HTMLButtonElement;
|
|
84
97
|
div: HTMLDivElement;
|
|
85
98
|
};
|
|
99
|
+
/** @propsmith ListViewItemBranchProps */
|
|
100
|
+
export type ListViewItemBranchProps = {
|
|
101
|
+
/** The accessible label rendered in the disclosure button. */
|
|
102
|
+
label: string;
|
|
103
|
+
/** The decorative icon before the label.
|
|
104
|
+
* @type Snippet | Component
|
|
105
|
+
*/
|
|
106
|
+
icon?: Snippet | Component;
|
|
107
|
+
/** Whether the nested content is visible. Branches only expand or collapse and are never selectable.
|
|
108
|
+
* @default false
|
|
109
|
+
* @bindable
|
|
110
|
+
*/
|
|
111
|
+
expanded?: boolean;
|
|
112
|
+
/** Called when a user expands or collapses the branch. */
|
|
113
|
+
onExpandedChange?: (e: Event, expanded: boolean) => void;
|
|
114
|
+
/** Disables the disclosure button.
|
|
115
|
+
* @default false
|
|
116
|
+
*/
|
|
117
|
+
disabled?: boolean;
|
|
118
|
+
/** The disclosure button shape. Inherited from the list by default. */
|
|
119
|
+
shape?: Shapes;
|
|
120
|
+
/** The DOM reference of the native disclosure button. Additional HTML attributes, including class and id, also target this button.
|
|
121
|
+
* @type HTMLButtonElement
|
|
122
|
+
* @bindable
|
|
123
|
+
*/
|
|
124
|
+
ref?: HTMLButtonElement | null;
|
|
125
|
+
/** The DOM reference of the wrapper element, the list item that holds the button and the nested list.
|
|
126
|
+
* @type HTMLLIElement | HTMLDivElement
|
|
127
|
+
* @bindable
|
|
128
|
+
*/
|
|
129
|
+
wrapperRef?: HTMLLIElement | HTMLDivElement | null;
|
|
130
|
+
/** The attributes to spread on the wrapper element.
|
|
131
|
+
* @type HTMLAttributes
|
|
132
|
+
*/
|
|
133
|
+
wrapperAttributes?: HTMLAttributes<HTMLLIElement | HTMLDivElement>;
|
|
134
|
+
/** Nested ListView content, kept mounted while collapsed to preserve its selection. */
|
|
135
|
+
children?: Snippet;
|
|
136
|
+
} & Omit<HTMLButtonAttributes, 'children' | 'role' | 'type' | 'value' | 'aria-expanded' | 'aria-controls'>;
|
|
86
137
|
export type ListViewContextConfig = {
|
|
87
138
|
shape: Shapes;
|
|
139
|
+
as: 'ul' | 'ol' | 'div';
|
|
140
|
+
selectionMode: SelectionMode;
|
|
141
|
+
role: AriaRole | undefined;
|
|
142
|
+
disableTabspot: boolean;
|
|
143
|
+
/** Whether the list keeps a single tab stop that the arrow keys move. */
|
|
144
|
+
roving: boolean;
|
|
88
145
|
};
|
|
89
146
|
export type ListViewContextState = {
|
|
90
147
|
selectedItems: string[];
|
|
@@ -92,8 +149,14 @@ export type ListViewContextState = {
|
|
|
92
149
|
};
|
|
93
150
|
export type ListViewContextMethods = {
|
|
94
151
|
handleSelection: (e: MouseEvent, value: string, toggle?: boolean) => void;
|
|
95
|
-
getChildrenRole: (tag: 'li' | 'a' | 'div') => 'listitem' | 'option' | 'row' | undefined;
|
|
96
|
-
registerItem: (id: string,
|
|
152
|
+
getChildrenRole: (tag: 'li' | 'a' | 'button' | 'div') => 'listitem' | 'option' | 'row' | undefined;
|
|
153
|
+
registerItem: (id: string, getItem: () => {
|
|
154
|
+
value: string;
|
|
155
|
+
disabled: boolean;
|
|
156
|
+
element: HTMLElement | null;
|
|
157
|
+
}) => void;
|
|
97
158
|
unregisterItem: (id: string) => void;
|
|
159
|
+
/** True for the first item to ask: it holds the tab stop until Tabspot takes the stop over on mount. */
|
|
160
|
+
claimTabStop: (id: string) => boolean;
|
|
98
161
|
};
|
|
99
162
|
export type ListViewContext = FSContext<ListViewContextConfig, ListViewContextState, null, ListViewContextMethods>;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { ListViewContext, NavigationMode, SelectionMode } from './types.ts';
|
|
2
|
+
import type { Attachment } from 'svelte/attachments';
|
|
3
|
+
import { type TabspotNodeOptions, type TabspotEventListener, type TabspotInstance } from 'tabspot';
|
|
2
4
|
export declare const COMPONENT_NAME = "fslist-view-";
|
|
3
5
|
export declare const TAG: string[];
|
|
4
6
|
export declare const ITEM_TAG: string[];
|
|
7
|
+
export declare const LIST_VIEW_SKIP = "[disabled], [aria-disabled=\"true\"], [hidden], [hidden] *, [inert], [inert] *, [aria-hidden=\"true\"], [aria-hidden=\"true\"] *";
|
|
8
|
+
export declare const getListViewItemContext: () => {
|
|
9
|
+
readonly as: "li" | "a" | "button" | "div";
|
|
10
|
+
} | null, setListViewItemContext: (context: {
|
|
11
|
+
readonly as: "li" | "a" | "button" | "div";
|
|
12
|
+
}) => void;
|
|
13
|
+
/** Register roots mounted after the provider starts, and release managed focus on updates/unmount. */
|
|
14
|
+
export declare function attachTabspot(config: TabspotNodeOptions | undefined, onNavigate?: TabspotEventListener, instance?: TabspotInstance): Attachment<HTMLElement>;
|
|
5
15
|
export declare function getRole(tag: 'ul' | 'ol' | 'div', selectionMode: SelectionMode, navigationMode: NavigationMode): 'grid' | 'listbox' | 'list' | undefined;
|
|
6
16
|
export declare const getListViewContext: () => ListViewContext | null, setListViewContext: (context: ListViewContext) => void;
|
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { PREFIX } from '../../internal/constants.js';
|
|
2
2
|
import { createFSContext } from '../../internal/index.js';
|
|
3
|
+
import { setTabspotAttributes } from 'tabspot';
|
|
3
4
|
export const COMPONENT_NAME = `${PREFIX}list-view-`;
|
|
4
5
|
export const TAG = ['ul', 'ol', 'div'];
|
|
5
|
-
export const ITEM_TAG = ['li', 'a', 'div'];
|
|
6
|
+
export const ITEM_TAG = ['li', 'a', 'button', 'div'];
|
|
7
|
+
export const LIST_VIEW_SKIP = '[disabled], [aria-disabled="true"], [hidden], [hidden] *, [inert], [inert] *, [aria-hidden="true"], [aria-hidden="true"] *';
|
|
8
|
+
export const [getListViewItemContext, setListViewItemContext] = createFSContext();
|
|
9
|
+
/** Register roots mounted after the provider starts, and release managed focus on updates/unmount. */
|
|
10
|
+
export function attachTabspot(config, onNavigate, instance) {
|
|
11
|
+
return (element) => {
|
|
12
|
+
if (!config)
|
|
13
|
+
return;
|
|
14
|
+
const result = setTabspotAttributes({ element, config });
|
|
15
|
+
const detach = onNavigate && (result.ok ? (result.instance ?? instance) : undefined)?.subscribe(onNavigate);
|
|
16
|
+
return () => {
|
|
17
|
+
detach?.();
|
|
18
|
+
setTabspotAttributes({ element, config: {} });
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
|
6
22
|
export function getRole(tag, selectionMode, navigationMode) {
|
|
7
23
|
if (selectionMode !== 'none') {
|
|
8
24
|
return navigationMode === 'composite' ? 'grid' : 'listbox';
|