@svadmin/ui 0.73.0 → 0.75.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/README.md +53 -28
- package/dist/app.css +3733 -4119
- package/dist/app.theme.css +3733 -4119
- package/dist/component-registry.d.ts +14977 -0
- package/dist/component-registry.js +216 -0
- package/dist/components/AboutSettings.svelte +7 -1
- package/dist/components/AboutSettings.svelte.d.ts +4 -1
- package/dist/components/ActivityFeed.svelte +9 -3
- package/dist/components/ActivityFeed.svelte.d.ts +3 -0
- package/dist/components/AdminApp.svelte +24 -15
- package/dist/components/AdminApp.svelte.d.ts +6 -0
- package/dist/components/ApiSettings.svelte +41 -5
- package/dist/components/ApprovalCenter.svelte +283 -0
- package/dist/components/ApprovalCenter.svelte.d.ts +10 -0
- package/dist/components/AuditLogViewer.svelte +3 -1
- package/dist/components/AutoForm.svelte +33 -3
- package/dist/components/AutoForm.svelte.d.ts +1 -0
- package/dist/components/AutoTable.svelte +610 -47
- package/dist/components/AutoTable.svelte.d.ts +17 -1
- package/dist/components/BoundRecordDetailDrawer.svelte +9 -0
- package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +2 -0
- package/dist/components/Breadcrumbs.svelte +11 -1
- package/dist/components/CancelTaskButton.svelte +12 -42
- package/dist/components/ColorPicker.svelte +73 -0
- package/dist/components/ColorPicker.svelte.d.ts +15 -0
- package/dist/components/ComboboxField.svelte +114 -27
- package/dist/components/ComboboxField.svelte.d.ts +1 -0
- package/dist/components/DashboardView.svelte +99 -0
- package/dist/components/DashboardView.svelte.d.ts +14 -0
- package/dist/components/DatePicker.svelte +10 -0
- package/dist/components/DatePicker.svelte.d.ts +6 -0
- package/dist/components/DateRangeInput.svelte +85 -5
- package/dist/components/DateRangeInput.svelte.d.ts +10 -0
- package/dist/components/DateRangePicker.svelte +10 -0
- package/dist/components/DateRangePicker.svelte.d.ts +6 -0
- package/dist/components/DateTimeInput.svelte +85 -16
- package/dist/components/DateTimeInput.svelte.d.ts +19 -11
- package/dist/components/DateTimePicker.svelte +10 -0
- package/dist/components/DateTimePicker.svelte.d.ts +6 -0
- package/dist/components/DevTools.svelte +34 -26
- package/dist/components/DevframePanel.svelte +123 -0
- package/dist/components/DevframePanel.svelte.d.ts +3 -0
- package/dist/components/ErrorSummary.svelte +52 -0
- package/dist/components/ErrorSummary.svelte.d.ts +14 -0
- package/dist/components/FieldRenderer.svelte +82 -81
- package/dist/components/FileUpload.svelte +214 -52
- package/dist/components/FileUpload.svelte.d.ts +11 -0
- package/dist/components/FilterBuilder.svelte +88 -66
- package/dist/components/FilterCollectionInput.svelte +80 -0
- package/dist/components/FilterCollectionInput.svelte.d.ts +10 -0
- package/dist/components/GanttChart.svelte +66 -14
- package/dist/components/GanttChart.svelte.d.ts +6 -0
- package/dist/components/Header.svelte +18 -2
- package/dist/components/ImportWizard.svelte +339 -17
- package/dist/components/ImportWizard.svelte.d.ts +11 -0
- package/dist/components/JsonSchemaForm.svelte +120 -52
- package/dist/components/JsonSchemaForm.svelte.d.ts +7 -1
- package/dist/components/KanbanBoard.svelte +195 -40
- package/dist/components/KanbanBoard.svelte.d.ts +9 -2
- package/dist/components/Layout.svelte +4 -2
- package/dist/components/LazyAutoForm.svelte +28 -0
- package/dist/components/LazyAutoForm.svelte.d.ts +8 -0
- package/dist/components/LazyAutoTable.svelte +28 -0
- package/dist/components/LazyAutoTable.svelte.d.ts +8 -0
- package/dist/components/LazyPage.svelte +3 -3
- package/dist/components/LazyPage.svelte.d.ts +2 -2
- package/dist/components/LazySettingsPage.svelte +28 -0
- package/dist/components/LazySettingsPage.svelte.d.ts +8 -0
- package/dist/components/LazyShowPage.svelte +28 -0
- package/dist/components/LazyShowPage.svelte.d.ts +8 -0
- package/dist/components/ListPage.svelte +5 -1
- package/dist/components/ListPage.svelte.d.ts +3 -0
- package/dist/components/LoginPage.svelte +1 -1
- package/dist/components/MultiTabKeepAlive.svelte +11 -6
- package/dist/components/NotificationsSettings.svelte +69 -2
- package/dist/components/NotificationsSettings.svelte.d.ts +25 -1
- package/dist/components/PasswordInput.svelte +3 -3
- package/dist/components/PasswordInput.svelte.d.ts +2 -1
- package/dist/components/PermissionMatrix.svelte +20 -1
- package/dist/components/PivotExportButton.svelte +157 -0
- package/dist/components/PivotExportButton.svelte.d.ts +16 -0
- package/dist/components/PivotTable.svelte +229 -74
- package/dist/components/PivotTable.svelte.d.ts +19 -2
- package/dist/components/RadioGroup.svelte +121 -0
- package/dist/components/RadioGroup.svelte.d.ts +43 -0
- package/dist/components/RangeSlider.svelte +127 -0
- package/dist/components/RangeSlider.svelte.d.ts +22 -0
- package/dist/components/Rate.svelte +121 -0
- package/dist/components/Rate.svelte.d.ts +16 -0
- package/dist/components/RecordDetailDrawer.svelte +4 -2
- package/dist/components/RecordDetailDrawer.svelte.d.ts +2 -0
- package/dist/components/RelationPicker.svelte +341 -0
- package/dist/components/RelationPicker.svelte.d.ts +26 -0
- package/dist/components/ResourceOperationsPage.svelte +23 -19
- package/dist/components/ResourceOperationsPage.svelte.d.ts +2 -2
- package/dist/components/RetryTaskButton.svelte +12 -42
- package/dist/components/SecuritySettings.svelte +29 -5
- package/dist/components/SegmentedControl.svelte +145 -0
- package/dist/components/SegmentedControl.svelte.d.ts +38 -0
- package/dist/components/SettingsPage.svelte +3 -1
- package/dist/components/SettingsPage.svelte.d.ts +1 -0
- package/dist/components/ShowPage.svelte +18 -3
- package/dist/components/ShowPage.svelte.d.ts +3 -0
- package/dist/components/Sidebar.svelte +2 -2
- package/dist/components/Slider.svelte +100 -0
- package/dist/components/Slider.svelte.d.ts +19 -0
- package/dist/components/SpreadsheetView.svelte +1213 -95
- package/dist/components/SpreadsheetView.svelte.d.ts +19 -0
- package/dist/components/TagsInput.svelte +178 -0
- package/dist/components/TagsInput.svelte.d.ts +17 -0
- package/dist/components/TimePicker.svelte +10 -0
- package/dist/components/TimePicker.svelte.d.ts +6 -0
- package/dist/components/TooltipButton.svelte +3 -4
- package/dist/components/TreeSelect.svelte +267 -53
- package/dist/components/TreeSelect.svelte.d.ts +3 -0
- package/dist/components/TreeTable.svelte +484 -82
- package/dist/components/TreeTable.svelte.d.ts +24 -1
- package/dist/components/TwoFactorAuthPage.svelte +85 -48
- package/dist/components/TwoFactorAuthPage.svelte.d.ts +12 -1
- package/dist/components/VirtualTable.svelte +116 -20
- package/dist/components/VirtualTable.svelte.d.ts +5 -0
- package/dist/components/account/CompanyProfilePage.svelte +74 -38
- package/dist/components/account/GetStartedPage.svelte +2 -1
- package/dist/components/account/ImportMembersPage.svelte +22 -167
- package/dist/components/account/ImportMembersPage.svelte.d.ts +4 -1
- package/dist/components/account/MemberDirectory.svelte +156 -0
- package/dist/components/account/MemberDirectory.svelte.d.ts +19 -0
- package/dist/components/account/MembersStarterPage.svelte +9 -55
- package/dist/components/account/MembersStarterPage.svelte.d.ts +5 -1
- package/dist/components/account/SecurityLogPage.svelte +4 -1
- package/dist/components/account/SettingsPlainPage.svelte +7 -38
- package/dist/components/account/SettingsPlainPage.svelte.d.ts +4 -1
- package/dist/components/account/SettingsSidebarPage.svelte +7 -20
- package/dist/components/account/TeamMembersPage.svelte +4 -20
- package/dist/components/account/TeamMembersPage.svelte.d.ts +5 -1
- package/dist/components/account/UserProfilePage.svelte +3 -25
- package/dist/components/account/UserProfilePage.svelte.d.ts +17 -2
- package/dist/components/buttons/ExportButton.svelte +56 -12
- package/dist/components/buttons/ExportButton.svelte.d.ts +11 -0
- package/dist/components/content/ContentPageHeader.svelte +1 -1
- package/dist/components/content/ContentPageShell.svelte +1 -1
- package/dist/components/content/MetricBlock.svelte +1 -1
- package/dist/components/content/PageToolbar.svelte +1 -1
- package/dist/components/content/ReferenceNotice.svelte +5 -0
- package/dist/components/content/ReferenceNotice.svelte.d.ts +18 -0
- package/dist/components/content/SectionHeader.svelte +1 -1
- package/dist/components/content/SettingsFieldRow.svelte +1 -1
- package/dist/components/content/SettingsGroup.svelte +1 -1
- package/dist/components/content/StatusBadge.svelte +1 -1
- package/dist/components/content/WorkspaceLayout.svelte +1 -1
- package/dist/components/export-reference.d.ts +2 -0
- package/dist/components/export-reference.js +9 -0
- package/dist/components/fieldComponentMap.js +2 -5
- package/dist/components/fields/DateField.svelte +12 -13
- package/dist/components/fields/MarkdownField.svelte +14 -5
- package/dist/components/fields/date-display.d.ts +6 -0
- package/dist/components/fields/date-display.js +24 -0
- package/dist/components/network/TeamCrewTablePage.svelte +6 -1
- package/dist/components/network/UserCardsNFTPage.svelte +2 -0
- package/dist/components/profile/PublicProfilePage.svelte +2 -0
- package/dist/components/saved-list-views.d.ts +74 -1
- package/dist/components/saved-list-views.js +191 -3
- package/dist/components/table-contract.d.ts +12 -1
- package/dist/components/task-action.svelte.d.ts +13 -0
- package/dist/components/task-action.svelte.js +73 -0
- package/dist/components/ui/DesignSystemShowcase.svelte +164 -0
- package/dist/components/ui/DesignSystemShowcase.svelte.d.ts +3 -0
- package/dist/components/ui/badge/badge-variants.d.ts +3 -2
- package/dist/components/ui/badge/badge-variants.js +2 -3
- package/dist/components/ui/badge/badge.svelte +3 -0
- package/dist/components/ui/badge/badge.svelte.d.ts +1 -0
- package/dist/components/ui/button/button-variants.d.ts +5 -4
- package/dist/components/ui/button/button-variants.js +7 -5
- package/dist/components/ui/checkbox/checkbox.svelte +5 -1
- package/dist/components/ui/checkbox/checkbox.svelte.d.ts +5 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +1 -2
- package/dist/components/ui/input/input.svelte +7 -2
- package/dist/components/ui/input/input.svelte.d.ts +4 -1
- package/dist/components/ui/select/select.svelte +4 -1
- package/dist/components/ui/select/select.svelte.d.ts +2 -1
- package/dist/components/ui/textarea/textarea.svelte +2 -2
- package/dist/components.css +33 -2
- package/dist/date-input-policy.d.ts +4 -0
- package/dist/date-input-policy.js +35 -0
- package/dist/design-system-components.d.ts +100 -0
- package/dist/design-system-components.js +101 -0
- package/dist/design-system.js +1 -1
- package/dist/devtools-bridge.d.ts +80 -0
- package/dist/devtools-bridge.js +92 -0
- package/dist/editor-config.svelte.d.ts +4 -2
- package/dist/index.d.ts +37 -2
- package/dist/index.js +23 -2
- package/dist/recipes.d.ts +960 -4
- package/dist/recipes.js +264 -4
- package/dist/tree-table-model.d.ts +24 -0
- package/dist/tree-table-model.js +111 -0
- package/dist/types.d.ts +4 -4
- package/package.json +22 -10
- package/dist/styled-system/css/conditions.js +0 -36
- package/dist/styled-system/css/css.d.ts +0 -22
- package/dist/styled-system/css/css.js +0 -45
- package/dist/styled-system/css/cva.d.ts +0 -6
- package/dist/styled-system/css/cva.js +0 -87
- package/dist/styled-system/css/cx.d.ts +0 -5
- package/dist/styled-system/css/cx.js +0 -15
- package/dist/styled-system/css/index.d.ts +0 -5
- package/dist/styled-system/css/index.js +0 -4
- package/dist/styled-system/css/sva.d.ts +0 -4
- package/dist/styled-system/css/sva.js +0 -46
- package/dist/styled-system/helpers.js +0 -316
- package/dist/styled-system/patterns/aspect-ratio.d.ts +0 -20
- package/dist/styled-system/patterns/aspect-ratio.js +0 -38
- package/dist/styled-system/patterns/bleed.d.ts +0 -21
- package/dist/styled-system/patterns/bleed.js +0 -24
- package/dist/styled-system/patterns/box.d.ts +0 -20
- package/dist/styled-system/patterns/box.js +0 -15
- package/dist/styled-system/patterns/center.d.ts +0 -20
- package/dist/styled-system/patterns/center.js +0 -21
- package/dist/styled-system/patterns/circle.d.ts +0 -20
- package/dist/styled-system/patterns/circle.js +0 -25
- package/dist/styled-system/patterns/container.d.ts +0 -20
- package/dist/styled-system/patterns/container.js +0 -21
- package/dist/styled-system/patterns/cq.d.ts +0 -21
- package/dist/styled-system/patterns/cq.js +0 -21
- package/dist/styled-system/patterns/divider.d.ts +0 -22
- package/dist/styled-system/patterns/divider.js +0 -25
- package/dist/styled-system/patterns/flex.d.ts +0 -26
- package/dist/styled-system/patterns/flex.js +0 -26
- package/dist/styled-system/patterns/float.d.ts +0 -23
- package/dist/styled-system/patterns/float.js +0 -52
- package/dist/styled-system/patterns/grid-item.d.ts +0 -25
- package/dist/styled-system/patterns/grid-item.js +0 -25
- package/dist/styled-system/patterns/grid.d.ts +0 -24
- package/dist/styled-system/patterns/grid.js +0 -27
- package/dist/styled-system/patterns/hstack.d.ts +0 -21
- package/dist/styled-system/patterns/hstack.js +0 -24
- package/dist/styled-system/patterns/index.d.ts +0 -21
- package/dist/styled-system/patterns/index.js +0 -20
- package/dist/styled-system/patterns/link-overlay.d.ts +0 -20
- package/dist/styled-system/patterns/link-overlay.js +0 -24
- package/dist/styled-system/patterns/spacer.d.ts +0 -20
- package/dist/styled-system/patterns/spacer.js +0 -25
- package/dist/styled-system/patterns/square.d.ts +0 -20
- package/dist/styled-system/patterns/square.js +0 -24
- package/dist/styled-system/patterns/stack.d.ts +0 -23
- package/dist/styled-system/patterns/stack.js +0 -24
- package/dist/styled-system/patterns/visually-hidden.d.ts +0 -20
- package/dist/styled-system/patterns/visually-hidden.js +0 -18
- package/dist/styled-system/patterns/vstack.d.ts +0 -21
- package/dist/styled-system/patterns/vstack.js +0 -24
- package/dist/styled-system/patterns/wrap.d.ts +0 -24
- package/dist/styled-system/patterns/wrap.js +0 -25
- package/dist/styled-system/recipes/content-header.d.ts +0 -31
- package/dist/styled-system/recipes/content-header.js +0 -65
- package/dist/styled-system/recipes/content-page.d.ts +0 -34
- package/dist/styled-system/recipes/content-page.js +0 -43
- package/dist/styled-system/recipes/create-recipe.js +0 -82
- package/dist/styled-system/recipes/index.d.ts +0 -17
- package/dist/styled-system/recipes/index.js +0 -16
- package/dist/styled-system/recipes/metric-block.d.ts +0 -34
- package/dist/styled-system/recipes/metric-block.js +0 -76
- package/dist/styled-system/recipes/product-list.d.ts +0 -31
- package/dist/styled-system/recipes/product-list.js +0 -73
- package/dist/styled-system/recipes/product-section.d.ts +0 -31
- package/dist/styled-system/recipes/product-section.js +0 -49
- package/dist/styled-system/recipes/product-settings-row.d.ts +0 -34
- package/dist/styled-system/recipes/product-settings-row.js +0 -58
- package/dist/styled-system/recipes/product-settings.d.ts +0 -31
- package/dist/styled-system/recipes/product-settings.js +0 -57
- package/dist/styled-system/recipes/product-status.d.ts +0 -34
- package/dist/styled-system/recipes/product-status.js +0 -45
- package/dist/styled-system/recipes/product-toolbar.d.ts +0 -31
- package/dist/styled-system/recipes/product-toolbar.js +0 -41
- package/dist/styled-system/recipes/product-workspace.d.ts +0 -34
- package/dist/styled-system/recipes/product-workspace.js +0 -58
- package/dist/styled-system/recipes/surface-metric.d.ts +0 -38
- package/dist/styled-system/recipes/surface-metric.js +0 -63
- package/dist/styled-system/recipes/surface-table.d.ts +0 -34
- package/dist/styled-system/recipes/surface-table.js +0 -62
- package/dist/styled-system/recipes/ui-badge.d.ts +0 -31
- package/dist/styled-system/recipes/ui-badge.js +0 -38
- package/dist/styled-system/recipes/ui-button.d.ts +0 -32
- package/dist/styled-system/recipes/ui-button.js +0 -43
- package/dist/styled-system/recipes/ui-input.d.ts +0 -31
- package/dist/styled-system/recipes/ui-input.js +0 -49
- package/dist/styled-system/recipes/ui-textarea.d.ts +0 -31
- package/dist/styled-system/recipes/ui-textarea.js +0 -24
- package/dist/styled-system/styles.css +0 -1257
- package/dist/styled-system/tokens/index.d.ts +0 -9
- package/dist/styled-system/tokens/index.js +0 -268
- package/dist/styled-system/tokens/tokens.d.ts +0 -36
- package/dist/styled-system/types/composition.d.ts +0 -227
- package/dist/styled-system/types/conditions.d.ts +0 -236
- package/dist/styled-system/types/csstype.d.ts +0 -22570
- package/dist/styled-system/types/index.d.ts +0 -6
- package/dist/styled-system/types/parts.d.ts +0 -8
- package/dist/styled-system/types/pattern.d.ts +0 -78
- package/dist/styled-system/types/prop-type.d.ts +0 -223
- package/dist/styled-system/types/recipe.d.ts +0 -181
- package/dist/styled-system/types/selectors.d.ts +0 -59
- package/dist/styled-system/types/static-css.d.ts +0 -56
- package/dist/styled-system/types/style-props.d.ts +0 -8088
- package/dist/styled-system/types/system-types.d.ts +0 -151
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { definedOptions } from '@svadmin/core/options';
|
|
3
3
|
|
|
4
|
-
import { SvelteSet } from 'svelte/reactivity';
|
|
5
|
-
import { onDestroy } from 'svelte';
|
|
4
|
+
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
|
5
|
+
import { onDestroy, tick, untrack } from 'svelte';
|
|
6
6
|
import { ChevronRight, ChevronDown, Check, X, Search, ChevronsUpDown, Loader2, Minus, RotateCw } from '@lucide/svelte';
|
|
7
7
|
import { cn } from '../utils.js';
|
|
8
8
|
import * as Popover from './ui/popover/index.js';
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
disabled?: boolean;
|
|
32
32
|
allowClear?: boolean;
|
|
33
33
|
loadChildren?: (node: TreeSelectOption) => Promise<TreeSelectOption[]>;
|
|
34
|
+
virtualized?: boolean;
|
|
35
|
+
itemHeight?: number;
|
|
36
|
+
viewportHeight?: number;
|
|
34
37
|
class?: string;
|
|
35
38
|
onchange?: (value: string | number | (string | number)[] | undefined) => void;
|
|
36
39
|
}
|
|
@@ -45,6 +48,9 @@
|
|
|
45
48
|
disabled = false,
|
|
46
49
|
allowClear = true,
|
|
47
50
|
loadChildren,
|
|
51
|
+
virtualized = true,
|
|
52
|
+
itemHeight = 36,
|
|
53
|
+
viewportHeight = 280,
|
|
48
54
|
class: className,
|
|
49
55
|
onchange,
|
|
50
56
|
}: Props = $props();
|
|
@@ -52,13 +58,29 @@
|
|
|
52
58
|
let open = $state(false);
|
|
53
59
|
let searchQuery = $state('');
|
|
54
60
|
let loadingValues = new SvelteSet<string | number>();
|
|
55
|
-
|
|
56
|
-
let expandedNodes = new SvelteSet<string | number>();
|
|
57
|
-
let mounted = true;
|
|
58
|
-
const loads = new Map<string | number, {
|
|
61
|
+
interface ChildLoad {
|
|
59
62
|
node: TreeSelectOption;
|
|
60
63
|
loader: NonNullable<Props['loadChildren']>;
|
|
61
|
-
|
|
64
|
+
treeEpoch: number;
|
|
65
|
+
}
|
|
66
|
+
let loadErrors = new SvelteMap<string | number, ChildLoad>();
|
|
67
|
+
let expandedNodes = new SvelteSet<string | number>();
|
|
68
|
+
let mounted = true;
|
|
69
|
+
let treeRoot: HTMLElement | undefined = $state();
|
|
70
|
+
let treeScrollTop = $state(0);
|
|
71
|
+
let treeRevision = $state(0);
|
|
72
|
+
const loads = new Map<string | number, ChildLoad>();
|
|
73
|
+
let observedOptions = untrack(() => options);
|
|
74
|
+
let observedLoader = untrack(() => loadChildren);
|
|
75
|
+
interface SelectionAttempt {
|
|
76
|
+
key: string | number;
|
|
77
|
+
epoch: number;
|
|
78
|
+
loader: Props['loadChildren'];
|
|
79
|
+
value: Props['value'];
|
|
80
|
+
}
|
|
81
|
+
let selectionAttempt = $state.raw<SelectionAttempt>();
|
|
82
|
+
const selectionErrors = new SvelteSet<string | number>();
|
|
83
|
+
let treeEpoch = 0;
|
|
62
84
|
onDestroy(() => { mounted = false; loads.clear(); });
|
|
63
85
|
|
|
64
86
|
// Map to quickly look up option info by value
|
|
@@ -74,6 +96,20 @@
|
|
|
74
96
|
return map;
|
|
75
97
|
});
|
|
76
98
|
|
|
99
|
+
// 禁用状态沿完整数据树传播,搜索裁剪不能重新启用后代。
|
|
100
|
+
const disabledValues = $derived.by(() => {
|
|
101
|
+
const keys = new Set<string | number>();
|
|
102
|
+
function visit(nodes: TreeSelectOption[], inherited: boolean): void {
|
|
103
|
+
for (const node of nodes) {
|
|
104
|
+
const unavailable = inherited || node.disabled === true;
|
|
105
|
+
if (unavailable) keys.add(node.value);
|
|
106
|
+
if (node.children) visit(node.children, unavailable);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
visit(options, false);
|
|
110
|
+
return keys;
|
|
111
|
+
});
|
|
112
|
+
|
|
77
113
|
// Selected values array normalized
|
|
78
114
|
const selectedValues = $derived.by< (string | number)[]>(() => {
|
|
79
115
|
if (value === undefined || value === null || value === '') return [];
|
|
@@ -85,16 +121,32 @@
|
|
|
85
121
|
});
|
|
86
122
|
|
|
87
123
|
$effect.pre(() => {
|
|
88
|
-
|
|
124
|
+
// 外部替换根数组开启新数据版本;内部补充子节点保留当前版本。
|
|
125
|
+
if (options !== observedOptions) {
|
|
126
|
+
observedOptions = options;
|
|
127
|
+
treeEpoch += 1;
|
|
128
|
+
treeRevision += 1;
|
|
129
|
+
selectionAttempt = undefined;
|
|
130
|
+
selectionErrors.clear();
|
|
131
|
+
}
|
|
89
132
|
const loader = loadChildren;
|
|
90
133
|
const unavailable = disabled;
|
|
134
|
+
if (loader !== observedLoader || unavailable) {
|
|
135
|
+
observedLoader = loader;
|
|
136
|
+
selectionAttempt = undefined;
|
|
137
|
+
selectionErrors.clear();
|
|
138
|
+
}
|
|
139
|
+
if (!multiple || onlyLeafSelectable || selectionAttempt?.value !== value) selectionAttempt = undefined;
|
|
91
140
|
for (const [key, request] of loads) {
|
|
92
|
-
if (unavailable ||
|
|
141
|
+
if (unavailable || request.treeEpoch !== treeEpoch || loader !== request.loader) {
|
|
93
142
|
loads.delete(key);
|
|
94
143
|
loadingValues.delete(key);
|
|
95
144
|
loadErrors.delete(key);
|
|
96
145
|
}
|
|
97
146
|
}
|
|
147
|
+
for (const [key, request] of loadErrors) {
|
|
148
|
+
if (unavailable || request.treeEpoch !== treeEpoch || loader !== request.loader) loadErrors.delete(key);
|
|
149
|
+
}
|
|
98
150
|
});
|
|
99
151
|
|
|
100
152
|
function toggleExpand(nodeValue: string | number, event?: MouseEvent) {
|
|
@@ -116,7 +168,8 @@
|
|
|
116
168
|
}
|
|
117
169
|
|
|
118
170
|
function selectableLeaves(node: TreeSelectOption): TreeSelectOption[] {
|
|
119
|
-
if (
|
|
171
|
+
if (node.disabled) return [];
|
|
172
|
+
if (!hasChildren(node)) return [node];
|
|
120
173
|
if (!node.children) return [];
|
|
121
174
|
return (node.children ?? []).flatMap(selectableLeaves);
|
|
122
175
|
}
|
|
@@ -126,16 +179,24 @@
|
|
|
126
179
|
}
|
|
127
180
|
|
|
128
181
|
function isNodeSelected(node: TreeSelectOption): boolean {
|
|
182
|
+
node = optionMap.get(node.value) ?? node;
|
|
129
183
|
if (!multiple || !hasChildren(node)) return isSelected(node.value);
|
|
130
184
|
const leaves = selectableLeaves(node);
|
|
131
|
-
return leaves.length > 0 && leaves.every((leaf) => isSelected(leaf.value));
|
|
185
|
+
return leaves.length > 0 && !hasUnloadedDescendants(node) && leaves.every((leaf) => isSelected(leaf.value));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function hasUnloadedDescendants(node: TreeSelectOption): boolean {
|
|
189
|
+
if (node.disabled || !hasChildren(node)) return false;
|
|
190
|
+
if (!node.children) return true;
|
|
191
|
+
return node.children.some(hasUnloadedDescendants);
|
|
132
192
|
}
|
|
133
193
|
|
|
134
194
|
function isPartiallySelected(node: TreeSelectOption): boolean {
|
|
195
|
+
node = optionMap.get(node.value) ?? node;
|
|
135
196
|
if (!multiple || !hasChildren(node)) return false;
|
|
136
197
|
const leaves = selectableLeaves(node);
|
|
137
198
|
const count = leaves.filter((leaf) => isSelected(leaf.value)).length;
|
|
138
|
-
return count > 0 && count < leaves.length;
|
|
199
|
+
return count > 0 && (count < leaves.length || hasUnloadedDescendants(node));
|
|
139
200
|
}
|
|
140
201
|
|
|
141
202
|
function updateOptionsChildren(nodes: TreeSelectOption[], target: string | number, children: TreeSelectOption[]): TreeSelectOption[] {
|
|
@@ -155,23 +216,25 @@
|
|
|
155
216
|
}
|
|
156
217
|
|
|
157
218
|
async function ensureChildren(node: TreeSelectOption): Promise<boolean> {
|
|
158
|
-
if (!mounted || disabled || loadingValues.has(node.value)) return false;
|
|
219
|
+
if (!mounted || disabled || disabledValues.has(node.value) || loadingValues.has(node.value)) return false;
|
|
159
220
|
if (!node.hasChildren || node.children) return true;
|
|
160
221
|
const currentNode = optionMap.get(node.value);
|
|
161
222
|
if (!loadChildren || !currentNode) return false;
|
|
162
|
-
const request = { node: currentNode, loader: loadChildren };
|
|
223
|
+
const request = { node: currentNode, loader: loadChildren, treeEpoch };
|
|
163
224
|
loads.set(node.value, request);
|
|
164
225
|
const current = () => mounted && !disabled && loads.get(node.value) === request
|
|
165
|
-
&&
|
|
226
|
+
&& options === observedOptions && treeEpoch === request.treeEpoch
|
|
227
|
+
&& optionMap.has(node.value) && !disabledValues.has(node.value) && loadChildren === request.loader;
|
|
166
228
|
loadingValues.add(node.value);
|
|
167
229
|
loadErrors.delete(node.value);
|
|
168
230
|
try {
|
|
169
231
|
const children = await request.loader(request.node);
|
|
170
232
|
if (!current()) return false;
|
|
171
233
|
options = updateOptionsChildren(options, node.value, children);
|
|
234
|
+
observedOptions = options;
|
|
172
235
|
return true;
|
|
173
236
|
} catch {
|
|
174
|
-
if (current()) loadErrors.
|
|
237
|
+
if (current()) loadErrors.set(node.value, request);
|
|
175
238
|
return false;
|
|
176
239
|
} finally {
|
|
177
240
|
if (mounted && loads.get(node.value) === request) {
|
|
@@ -190,11 +253,13 @@
|
|
|
190
253
|
|
|
191
254
|
function retryChildren(node: TreeSelectOption, event: MouseEvent): void {
|
|
192
255
|
event.stopPropagation();
|
|
193
|
-
void
|
|
256
|
+
if (selectionErrors.has(node.value)) void handleSelect(node);
|
|
257
|
+
else void toggleExpandAsync(node);
|
|
194
258
|
}
|
|
195
259
|
|
|
196
260
|
function setMultipleSelection(node: TreeSelectOption): void {
|
|
197
261
|
const leaves = selectableLeaves(node);
|
|
262
|
+
if (leaves.length === 0) return;
|
|
198
263
|
const leafValues = new Set(leaves.map((leaf) => leaf.value));
|
|
199
264
|
const shouldRemove = leaves.length > 0 && leaves.every((leaf) => isSelected(leaf.value));
|
|
200
265
|
const next = shouldRemove
|
|
@@ -204,8 +269,54 @@
|
|
|
204
269
|
onchange?.(next);
|
|
205
270
|
}
|
|
206
271
|
|
|
272
|
+
function isCurrentSelection(attempt: SelectionAttempt): boolean {
|
|
273
|
+
return mounted && !disabled && multiple && !onlyLeafSelectable
|
|
274
|
+
&& selectionAttempt === attempt && attempt.epoch === treeEpoch
|
|
275
|
+
&& options === observedOptions && attempt.loader === loadChildren && attempt.value === value;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function selectSubtree(node: TreeSelectOption): Promise<void> {
|
|
279
|
+
if (selectionAttempt?.key === node.value && isCurrentSelection(selectionAttempt)) return;
|
|
280
|
+
const attempt: SelectionAttempt = { key: node.value, epoch: treeEpoch, loader: loadChildren, value };
|
|
281
|
+
selectionAttempt = attempt;
|
|
282
|
+
selectionErrors.delete(node.value);
|
|
283
|
+
let visited = 0;
|
|
284
|
+
let requests = 0;
|
|
285
|
+
// 级联加载有界且只在全部成功后写入选择;搜索裁剪不改变选择范围。
|
|
286
|
+
async function loadSubtree(key: string | number, depth: number): Promise<boolean> {
|
|
287
|
+
if (!isCurrentSelection(attempt)) return false;
|
|
288
|
+
const currentNode = optionMap.get(key);
|
|
289
|
+
if (!currentNode || ++visited > 10000 || depth > 64) return false;
|
|
290
|
+
if (currentNode.disabled) return true;
|
|
291
|
+
if (currentNode.hasChildren && !currentNode.children) {
|
|
292
|
+
if (++requests > 100 || !await ensureChildren(currentNode)) return false;
|
|
293
|
+
}
|
|
294
|
+
if (!isCurrentSelection(attempt)) return false;
|
|
295
|
+
const loaded = optionMap.get(key);
|
|
296
|
+
if (!loaded) return false;
|
|
297
|
+
for (const child of loaded.children ?? []) {
|
|
298
|
+
if (!await loadSubtree(child.value, depth + 1)) return false;
|
|
299
|
+
}
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
if (!await loadSubtree(node.value, 0)) {
|
|
304
|
+
if (isCurrentSelection(attempt)) selectionErrors.add(node.value);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (isCurrentSelection(attempt)) {
|
|
308
|
+
const loaded = optionMap.get(node.value);
|
|
309
|
+
if (loaded) setMultipleSelection(loaded);
|
|
310
|
+
}
|
|
311
|
+
} finally {
|
|
312
|
+
if (selectionAttempt === attempt) selectionAttempt = undefined;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
207
316
|
async function handleSelect(node: TreeSelectOption): Promise<void> {
|
|
208
|
-
|
|
317
|
+
node = optionMap.get(node.value) ?? node;
|
|
318
|
+
if (disabled || disabledValues.has(node.value)) return;
|
|
319
|
+
if (selectionAttempt?.key === node.value && isCurrentSelection(selectionAttempt)) return;
|
|
209
320
|
const nodeHasChildren = hasChildren(node);
|
|
210
321
|
if (onlyLeafSelectable && nodeHasChildren) {
|
|
211
322
|
await toggleExpandAsync(node);
|
|
@@ -213,8 +324,16 @@
|
|
|
213
324
|
}
|
|
214
325
|
|
|
215
326
|
if (multiple) {
|
|
216
|
-
if (nodeHasChildren)
|
|
327
|
+
if (nodeHasChildren) {
|
|
328
|
+
if (hasUnloadedDescendants(node)) await selectSubtree(node);
|
|
329
|
+
else {
|
|
330
|
+
selectionAttempt = undefined;
|
|
331
|
+
selectionErrors.delete(node.value);
|
|
332
|
+
setMultipleSelection(node);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
217
335
|
else {
|
|
336
|
+
selectionAttempt = undefined;
|
|
218
337
|
const next = isSelected(node.value)
|
|
219
338
|
? selectedValues.filter((selected) => selected !== node.value)
|
|
220
339
|
: [...selectedValues, node.value];
|
|
@@ -238,19 +357,34 @@
|
|
|
238
357
|
return undefined;
|
|
239
358
|
}
|
|
240
359
|
|
|
241
|
-
function
|
|
242
|
-
|
|
360
|
+
function treeKey(value: string | number): string {
|
|
361
|
+
return `${typeof value}:${String(value)}`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async function focusTreeNode(valueToFocus: string | number): Promise<void> {
|
|
365
|
+
const index = flatVisibleNodes.findIndex(entry => entry.node.value === valueToFocus);
|
|
366
|
+
if (index < 0) return;
|
|
367
|
+
if (shouldVirtualize && treeRoot) {
|
|
368
|
+
const top = index * rowHeight;
|
|
369
|
+
if (top < treeScrollTop || top + rowHeight > treeScrollTop + windowHeight) {
|
|
370
|
+
treeScrollTop = Math.max(0, Math.min(top, totalHeight - windowHeight));
|
|
371
|
+
treeRoot.scrollTop = treeScrollTop;
|
|
372
|
+
await tick();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (!mounted) return;
|
|
376
|
+
const element = treeRoot?.querySelector<HTMLElement>(`[data-tree-key="${CSS.escape(treeKey(valueToFocus))}"]`);
|
|
243
377
|
element?.focus();
|
|
244
378
|
}
|
|
245
379
|
|
|
246
380
|
function focusSibling(nodeValue: string | number, offset: number): void {
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
target?.focus();
|
|
381
|
+
const index = flatVisibleNodes.findIndex(entry => entry.node.value === nodeValue);
|
|
382
|
+
const target = flatVisibleNodes[index + offset];
|
|
383
|
+
if (target) void focusTreeNode(target.node.value);
|
|
251
384
|
}
|
|
252
385
|
|
|
253
386
|
function handleTreeKeydown(event: KeyboardEvent, node: TreeSelectOption, expanded: boolean): void {
|
|
387
|
+
if (event.target !== event.currentTarget) return;
|
|
254
388
|
const nodeHasChildren = hasChildren(node);
|
|
255
389
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
256
390
|
event.preventDefault();
|
|
@@ -263,34 +397,44 @@
|
|
|
263
397
|
focusSibling(node.value, -1);
|
|
264
398
|
} else if (event.key === 'Home') {
|
|
265
399
|
event.preventDefault();
|
|
266
|
-
|
|
400
|
+
const first = flatVisibleNodes[0];
|
|
401
|
+
if (first) void focusTreeNode(first.node.value);
|
|
267
402
|
} else if (event.key === 'End') {
|
|
268
403
|
event.preventDefault();
|
|
269
|
-
const
|
|
270
|
-
|
|
404
|
+
const last = flatVisibleNodes[flatVisibleNodes.length - 1];
|
|
405
|
+
if (last) void focusTreeNode(last.node.value);
|
|
271
406
|
} else if (event.key === 'ArrowRight' && nodeHasChildren) {
|
|
272
407
|
event.preventDefault();
|
|
273
408
|
if (!expanded) void toggleExpandAsync(node);
|
|
274
|
-
else
|
|
409
|
+
else {
|
|
410
|
+
const index = flatVisibleNodes.findIndex(entry => entry.node.value === node.value);
|
|
411
|
+
const current = flatVisibleNodes[index];
|
|
412
|
+
const next = flatVisibleNodes[index + 1];
|
|
413
|
+
if (current && next && next.level > current.level) void focusTreeNode(next.node.value);
|
|
414
|
+
}
|
|
275
415
|
} else if (event.key === 'ArrowLeft') {
|
|
276
416
|
event.preventDefault();
|
|
277
417
|
if (nodeHasChildren && expanded) toggleExpand(node.value);
|
|
278
418
|
else {
|
|
279
419
|
const parent = findParent(options, node.value);
|
|
280
|
-
if (parent) focusTreeNode(parent.value);
|
|
420
|
+
if (parent) void focusTreeNode(parent.value);
|
|
281
421
|
}
|
|
282
422
|
}
|
|
283
423
|
}
|
|
284
424
|
|
|
285
|
-
function clearAll(event?: MouseEvent) {
|
|
425
|
+
function clearAll(event?: MouseEvent | KeyboardEvent) {
|
|
286
426
|
event?.stopPropagation();
|
|
427
|
+
if (disabled) return;
|
|
428
|
+
selectionAttempt = undefined;
|
|
287
429
|
const next = multiple ? [] : undefined;
|
|
288
430
|
value = next;
|
|
289
431
|
onchange?.(next);
|
|
290
432
|
}
|
|
291
433
|
|
|
292
|
-
function removeSingle(val: string | number, event?: MouseEvent) {
|
|
434
|
+
function removeSingle(val: string | number, event?: MouseEvent | KeyboardEvent) {
|
|
293
435
|
event?.stopPropagation();
|
|
436
|
+
if (disabled) return;
|
|
437
|
+
selectionAttempt = undefined;
|
|
294
438
|
if (multiple) {
|
|
295
439
|
const next = selectedValues.filter((v) => v !== val);
|
|
296
440
|
value = next;
|
|
@@ -319,14 +463,54 @@
|
|
|
319
463
|
}
|
|
320
464
|
|
|
321
465
|
const visibleOptions = $derived(filterTree(options, searchQuery));
|
|
466
|
+
|
|
467
|
+
interface VisibleNode {
|
|
468
|
+
node: TreeSelectOption;
|
|
469
|
+
level: number;
|
|
470
|
+
position: number;
|
|
471
|
+
size: number;
|
|
472
|
+
}
|
|
473
|
+
const flatVisibleNodes = $derived.by(() => {
|
|
474
|
+
const result: VisibleNode[] = [];
|
|
475
|
+
function visit(nodes: TreeSelectOption[], level: number): void {
|
|
476
|
+
nodes.forEach((node, index) => {
|
|
477
|
+
result.push({ node, level, position: index + 1, size: nodes.length });
|
|
478
|
+
if (hasChildren(node) && isNodeExpanded(node) && node.children) visit(node.children, level + 1);
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
visit(visibleOptions, 0);
|
|
482
|
+
return result;
|
|
483
|
+
});
|
|
484
|
+
const rowHeight = $derived(Number.isSafeInteger(itemHeight) && itemHeight >= 32 && itemHeight <= 100 ? itemHeight : 36);
|
|
485
|
+
const windowHeight = $derived(Number.isSafeInteger(viewportHeight) && viewportHeight >= 120 && viewportHeight <= 800 ? viewportHeight : 280);
|
|
486
|
+
const shouldVirtualize = $derived(virtualized && flatVisibleNodes.length >= 200);
|
|
487
|
+
const totalHeight = $derived(flatVisibleNodes.length * rowHeight);
|
|
488
|
+
const clampedScrollTop = $derived(Math.max(0, Math.min(treeScrollTop, totalHeight - windowHeight)));
|
|
489
|
+
const virtualStart = $derived(Math.max(0, Math.floor(clampedScrollTop / rowHeight) - 5));
|
|
490
|
+
const virtualEnd = $derived(Math.min(flatVisibleNodes.length, Math.ceil((clampedScrollTop + windowHeight) / rowHeight) + 5));
|
|
491
|
+
|
|
492
|
+
$effect(() => {
|
|
493
|
+
// 搜索和外部数据替换从顶部开始;折叠或数据缩短只钳制窗口。
|
|
494
|
+
void searchQuery;
|
|
495
|
+
void treeRevision;
|
|
496
|
+
treeScrollTop = 0;
|
|
497
|
+
if (treeRoot) treeRoot.scrollTop = 0;
|
|
498
|
+
});
|
|
499
|
+
$effect(() => {
|
|
500
|
+
if (treeScrollTop !== clampedScrollTop) {
|
|
501
|
+
treeScrollTop = clampedScrollTop;
|
|
502
|
+
if (treeRoot) treeRoot.scrollTop = clampedScrollTop;
|
|
503
|
+
}
|
|
504
|
+
});
|
|
322
505
|
</script>
|
|
323
506
|
|
|
324
|
-
{#snippet treeNode(node: TreeSelectOption, level: number)}
|
|
325
|
-
{@const nodeHasChildren = hasChildren(node)}
|
|
507
|
+
{#snippet treeNode(node: TreeSelectOption, level: number, position: number, size: number, recursive = true)}
|
|
508
|
+
{@const nodeHasChildren = hasChildren(optionMap.get(node.value) ?? node)}
|
|
326
509
|
{@const expanded = isNodeExpanded(node)}
|
|
327
510
|
{@const selected = isNodeSelected(node)}
|
|
328
511
|
{@const partial = isPartiallySelected(node)}
|
|
329
|
-
{@const
|
|
512
|
+
{@const busy = selectionAttempt?.key === node.value || loadingValues.has(node.value)}
|
|
513
|
+
{@const unavailable = disabled || disabledValues.has(node.value)}
|
|
330
514
|
|
|
331
515
|
<div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed">
|
|
332
516
|
<div
|
|
@@ -334,30 +518,34 @@
|
|
|
334
518
|
tabindex="0"
|
|
335
519
|
aria-selected={selected}
|
|
336
520
|
aria-checked={multiple ? (partial ? 'mixed' : selected) : undefined}
|
|
521
|
+
aria-disabled={unavailable ? 'true' : undefined}
|
|
522
|
+
aria-busy={busy}
|
|
337
523
|
aria-level={level + 1}
|
|
524
|
+
aria-posinset={position}
|
|
525
|
+
aria-setsize={size}
|
|
338
526
|
aria-expanded={nodeHasChildren ? expanded : undefined}
|
|
339
527
|
data-tree-value={String(node.value)}
|
|
528
|
+
data-tree-key={treeKey(node.value)}
|
|
340
529
|
class={cn(
|
|
341
530
|
'group svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc svadmin-u-58284b4ea568 svadmin-u-421ac2be5045 svadmin-u-d5eab218aa34 svadmin-u-ec0091ee009b svadmin-u-359090c2d529 svadmin-u-ceb69a6b0e5f svadmin-u-34516836730d svadmin-u-7f6912283f11',
|
|
342
531
|
selected ? 'svadmin-u-375dc44df6e9 svadmin-u-20aaf08a7ed1 svadmin-u-2689f3958069' : 'svadmin-u-68646cdcc246 svadmin-u-d4108abe6359',
|
|
343
|
-
|
|
532
|
+
unavailable && 'svadmin-u-0b8c506a0596 svadmin-u-29b733e4c162 svadmin-u-a4326536b8f5'
|
|
344
533
|
)}
|
|
345
|
-
style=
|
|
346
|
-
onclick={() =>
|
|
347
|
-
if (selectable) handleSelect(node);
|
|
348
|
-
}}
|
|
534
|
+
style={`padding-left:${level * 16 + 8}px;${recursive ? '' : `height:${rowHeight}px;min-height:${rowHeight}px;box-sizing:border-box;overflow:hidden;`}`}
|
|
535
|
+
onclick={() => void handleSelect(node)}
|
|
349
536
|
onkeydown={(e) => handleTreeKeydown(e, node, expanded)}
|
|
350
537
|
>
|
|
351
538
|
<div class="svadmin-u-60fbb7713999 svadmin-u-7e0b7cdf1a94 svadmin-u-3960ffc248d9 svadmin-u-58284b4ea568">
|
|
352
539
|
{#if nodeHasChildren}
|
|
353
540
|
<button
|
|
354
541
|
type="button"
|
|
542
|
+
disabled={unavailable}
|
|
355
543
|
aria-label={`${node.label}: ${i18n.t('tree.toggleChildren')}`}
|
|
356
|
-
aria-busy={
|
|
544
|
+
aria-busy={busy}
|
|
357
545
|
class="svadmin-u-60fbb7713999 svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3 svadmin-u-012fbd121f37 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-07389a777c1f svadmin-u-bfa603190748 svadmin-u-8e551981c8d7"
|
|
358
546
|
onclick={(e) => void toggleExpandAsync(node, e)}
|
|
359
547
|
>
|
|
360
|
-
{#if
|
|
548
|
+
{#if busy}
|
|
361
549
|
<Loader2 class="svadmin-u-7fc7f732bf7e svadmin-u-bf600f8e029c svadmin-u-afbdd13a380e" />
|
|
362
550
|
{:else if expanded}
|
|
363
551
|
<ChevronDown class="svadmin-u-7fc7f732bf7e svadmin-u-bf600f8e029c" />
|
|
@@ -372,9 +560,10 @@
|
|
|
372
560
|
<span class="svadmin-u-f283ea9bea0e">{node.label}</span>
|
|
373
561
|
</div>
|
|
374
562
|
|
|
375
|
-
{#if loadErrors.has(node.value)}
|
|
376
|
-
<span role="alert" class="svadmin-u-bfa603190748">{i18n.t('tree.loadFailed')}</span>
|
|
563
|
+
{#if loadErrors.has(node.value) || selectionErrors.has(node.value)}
|
|
564
|
+
<span role="alert" class="svadmin-u-bfa603190748">{i18n.t(selectionErrors.has(node.value) ? 'tree.selectionFailed' : 'tree.loadFailed')}</span>
|
|
377
565
|
<button type="button" aria-label={`${node.label}: ${i18n.t('common.retry')}`}
|
|
566
|
+
disabled={unavailable}
|
|
378
567
|
title={i18n.t('common.retry')} onclick={(e) => retryChildren(node, e)}>
|
|
379
568
|
<RotateCw class="svadmin-u-7fc7f732bf7e svadmin-u-bf600f8e029c" />
|
|
380
569
|
</button>
|
|
@@ -387,10 +576,10 @@
|
|
|
387
576
|
{/if}
|
|
388
577
|
</div>
|
|
389
578
|
|
|
390
|
-
{#if nodeHasChildren && expanded && node.children}
|
|
579
|
+
{#if recursive && nodeHasChildren && expanded && node.children}
|
|
391
580
|
<div role="group" class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed">
|
|
392
|
-
{#each node.children as child (child.value)}
|
|
393
|
-
{@render treeNode(child, level + 1)}
|
|
581
|
+
{#each node.children as child, index (treeKey(child.value))}
|
|
582
|
+
{@render treeNode(child, level + 1, index + 1, node.children.length)}
|
|
394
583
|
{/each}
|
|
395
584
|
</div>
|
|
396
585
|
{/if}
|
|
@@ -424,10 +613,14 @@
|
|
|
424
613
|
<span
|
|
425
614
|
role="button"
|
|
426
615
|
tabindex="0"
|
|
616
|
+
aria-label={i18n.t('tree.removeSelection', { label: opt?.label ?? String(val) })}
|
|
427
617
|
class="svadmin-u-51e95020d6f2 svadmin-u-34516836730d"
|
|
428
618
|
onclick={(e) => removeSingle(val, e)}
|
|
429
619
|
onkeydown={(e) => {
|
|
430
|
-
if (e.key === 'Enter'
|
|
620
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
621
|
+
e.preventDefault();
|
|
622
|
+
removeSingle(val, e);
|
|
623
|
+
}
|
|
431
624
|
}}
|
|
432
625
|
>
|
|
433
626
|
<X class="svadmin-u-6a60c09e6aaa svadmin-u-9cea05671a29" />
|
|
@@ -447,10 +640,14 @@
|
|
|
447
640
|
<span
|
|
448
641
|
role="button"
|
|
449
642
|
tabindex="0"
|
|
643
|
+
aria-label={i18n.t('common.clearSelection')}
|
|
450
644
|
class="svadmin-u-ea7b2e9e070e svadmin-u-34516836730d svadmin-u-de8350a3bbad"
|
|
451
645
|
onclick={clearAll}
|
|
452
646
|
onkeydown={(e) => {
|
|
453
|
-
if (e.key === 'Enter'
|
|
647
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
648
|
+
e.preventDefault();
|
|
649
|
+
clearAll(e);
|
|
650
|
+
}
|
|
454
651
|
}}
|
|
455
652
|
>
|
|
456
653
|
<X class="svadmin-u-7fc7f732bf7e svadmin-u-bf600f8e029c" />
|
|
@@ -475,14 +672,31 @@
|
|
|
475
672
|
</div>
|
|
476
673
|
{/if}
|
|
477
674
|
|
|
478
|
-
<div
|
|
479
|
-
{
|
|
480
|
-
|
|
675
|
+
<div
|
|
676
|
+
bind:this={treeRoot}
|
|
677
|
+
role="tree"
|
|
678
|
+
aria-multiselectable={multiple ? 'true' : undefined}
|
|
679
|
+
class="svadmin-u-67d7e383dca6 svadmin-u-92bf82f493b1 svadmin-u-e2eedc5718f0 svadmin-u-eda955402ba6"
|
|
680
|
+
style={shouldVirtualize ? `height:${windowHeight}px;max-height:${windowHeight}px;overflow-y:auto;padding:0;` : undefined}
|
|
681
|
+
onscroll={(event) => { treeScrollTop = event.currentTarget.scrollTop; }}
|
|
682
|
+
>
|
|
683
|
+
{#if shouldVirtualize}
|
|
684
|
+
<div style={`height:${totalHeight}px;position:relative;`}>
|
|
685
|
+
<div style={`position:absolute;top:${virtualStart * rowHeight}px;left:0;right:0;`}>
|
|
686
|
+
{#each flatVisibleNodes.slice(virtualStart, virtualEnd) as entry (treeKey(entry.node.value))}
|
|
687
|
+
{@render treeNode(entry.node, entry.level, entry.position, entry.size, false)}
|
|
688
|
+
{/each}
|
|
689
|
+
</div>
|
|
690
|
+
</div>
|
|
691
|
+
{:else if visibleOptions.length > 0}
|
|
692
|
+
{#each visibleOptions as rootNode, index (treeKey(rootNode.value))}
|
|
693
|
+
{@render treeNode(rootNode, 0, index + 1, visibleOptions.length)}
|
|
694
|
+
{/each}
|
|
481
695
|
{:else}
|
|
482
696
|
<div class="svadmin-u-940911bf310c svadmin-u-ca6bf63030aa svadmin-u-359090c2d529 svadmin-u-bfa603190748">
|
|
483
697
|
{i18n.t('common.noData', undefined) ?? '无匹配选项'}
|
|
484
698
|
</div>
|
|
485
|
-
{/
|
|
699
|
+
{/if}
|
|
486
700
|
</div>
|
|
487
701
|
</Popover.Content>
|
|
488
702
|
</Popover.Root>
|
|
@@ -16,6 +16,9 @@ interface Props {
|
|
|
16
16
|
disabled?: boolean;
|
|
17
17
|
allowClear?: boolean;
|
|
18
18
|
loadChildren?: (node: TreeSelectOption) => Promise<TreeSelectOption[]>;
|
|
19
|
+
virtualized?: boolean;
|
|
20
|
+
itemHeight?: number;
|
|
21
|
+
viewportHeight?: number;
|
|
19
22
|
class?: string;
|
|
20
23
|
onchange?: (value: string | number | (string | number)[] | undefined) => void;
|
|
21
24
|
}
|