@xleddyl/443-components-vue 0.5.0 → 0.6.2
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 +25 -6
- package/dist/chart/BarChart.vue +11 -4
- package/dist/chart/BarChart.vue.d.ts +2 -0
- package/dist/chart/ChartLegend.vue +43 -4
- package/dist/chart/ChartLegend.vue.d.ts +4 -2
- package/dist/chart/ChartPanel.vue +10 -10
- package/dist/chart/ChartPanel.vue.d.ts +11 -8
- package/dist/chart/ChartSummary.vue +8 -3
- package/dist/chart/ChartSummary.vue.d.ts +3 -1
- package/dist/chart/DoughnutChart.vue +11 -4
- package/dist/chart/LineChart.vue +8 -2
- package/dist/chart/LineChart.vue.d.ts +2 -0
- package/dist/chart/chart.css +19 -0
- package/dist/chart/patterns.d.ts +6 -0
- package/dist/chart/patterns.mjs +79 -0
- package/dist/chart/types.d.ts +5 -0
- package/dist/chart/useChartColors.mjs +1 -1
- package/dist/components/core/Button.vue +11 -1
- package/dist/components/core/Button.vue.d.ts +1 -1
- package/dist/components/core/FieldMessage.vue +6 -0
- package/dist/components/core/FieldMessage.vue.d.ts +1 -1
- package/dist/components/core/Form.vue +18 -1
- package/dist/components/core/FormAlerts.vue +41 -3
- package/dist/components/core/FormField.vue +43 -24
- package/dist/components/core/Input.vue.d.ts +6 -6
- package/dist/components/core/Root.vue +6 -1
- package/dist/components/core/Root.vue.d.ts +2 -2
- package/dist/components/core/Spinner.vue +6 -11
- package/dist/components/core/Spinner.vue.d.ts +2 -2
- package/dist/components/core/Textarea.vue.d.ts +2 -2
- package/dist/components/core/VisuallyHidden.vue.d.ts +2 -2
- package/dist/components/data/Avatar.vue +8 -5
- package/dist/components/data/AvatarGroup.vue +6 -3
- package/dist/components/data/Badge.vue.d.ts +1 -1
- package/dist/components/data/FilterPills.vue +18 -2
- package/dist/components/data/Pagination.vue +24 -18
- package/dist/components/data/Pagination.vue.d.ts +2 -2
- package/dist/components/data/SelectionBar.vue +32 -3
- package/dist/components/data/Stat.vue +7 -1
- package/dist/components/data/Stat.vue.d.ts +2 -2
- package/dist/components/data/Table.vue +62 -14
- package/dist/components/data/Tag.vue +24 -4
- package/dist/components/data/Tag.vue.d.ts +3 -3
- package/dist/components/data/Timeline.vue +12 -1
- package/dist/components/data/Tree.vue +137 -36
- package/dist/components/data/Tree.vue.d.ts +13 -14
- package/dist/components/data/types.d.ts +2 -1
- package/dist/components/feedback/Alert.vue +18 -9
- package/dist/components/feedback/Alert.vue.d.ts +2 -2
- package/dist/components/feedback/LoadingOverlay.vue +35 -3
- package/dist/components/feedback/Meter.vue +28 -3
- package/dist/components/feedback/Progress.vue +2 -7
- package/dist/components/feedback/Progress.vue.d.ts +1 -1
- package/dist/components/feedback/ProgressRing.vue +2 -6
- package/dist/components/feedback/ProgressRing.vue.d.ts +1 -1
- package/dist/components/forms/Calendar.vue +49 -26
- package/dist/components/forms/Calendar.vue.d.ts +9 -5
- package/dist/components/forms/CheckboxGroup.vue +14 -4
- package/dist/components/forms/CheckboxGroup.vue.d.ts +1 -0
- package/dist/components/forms/ColorInput.vue +68 -15
- package/dist/components/forms/ColorInput.vue.d.ts +16 -13
- package/dist/components/forms/Combobox.vue +18 -4
- package/dist/components/forms/DateInput.vue.d.ts +6 -6
- package/dist/components/forms/DatePicker.vue +23 -7
- package/dist/components/forms/DatePicker.vue.d.ts +4 -2
- package/dist/components/forms/FileInput.vue +31 -13
- package/dist/components/forms/FileInput.vue.d.ts +2 -2
- package/dist/components/forms/InputAddon.vue +18 -2
- package/dist/components/forms/InputGroup.vue +27 -0
- package/dist/components/forms/InputGroup.vue.d.ts +2 -0
- package/dist/components/forms/NumberInput.vue +6 -7
- package/dist/components/forms/NumberInput.vue.d.ts +6 -6
- package/dist/components/forms/PasswordInput.vue +12 -6
- package/dist/components/forms/PasswordInput.vue.d.ts +6 -7
- package/dist/components/forms/PinInput.vue +11 -1
- package/dist/components/forms/PinInput.vue.d.ts +8 -6
- package/dist/components/forms/RadioGroup.vue +15 -5
- package/dist/components/forms/RadioGroup.vue.d.ts +1 -0
- package/dist/components/forms/SegmentedControl.vue +15 -10
- package/dist/components/forms/SegmentedControl.vue.d.ts +1 -0
- package/dist/components/forms/Slider.vue +9 -0
- package/dist/components/forms/Slider.vue.d.ts +8 -6
- package/dist/components/forms/Switch.vue +7 -0
- package/dist/components/forms/Switch.vue.d.ts +2 -2
- package/dist/components/forms/TimeInput.vue.d.ts +6 -6
- package/dist/components/forms/types.d.ts +4 -0
- package/dist/components/layout/Accordion.vue.d.ts +2 -2
- package/dist/components/layout/AppShell.vue +40 -18
- package/dist/components/layout/AppShell.vue.d.ts +11 -9
- package/dist/components/layout/Card.vue +50 -16
- package/dist/components/layout/Card.vue.d.ts +13 -11
- package/dist/components/layout/CopyButton.vue +13 -5
- package/dist/components/layout/CopyButton.vue.d.ts +3 -3
- package/dist/components/layout/NavLink.vue +10 -1
- package/dist/components/layout/NavLink.vue.d.ts +2 -2
- package/dist/components/layout/PageHeader.vue +2 -2
- package/dist/components/layout/Separator.vue +6 -3
- package/dist/components/layout/Sidebar.vue +14 -7
- package/dist/components/layout/Sidebar.vue.d.ts +0 -2
- package/dist/components/layout/SidebarNav.vue +16 -44
- package/dist/components/layout/Stack.vue.d.ts +2 -2
- package/dist/components/layout/Tabs.vue +10 -18
- package/dist/components/layout/Tabs.vue.d.ts +2 -2
- package/dist/components/layout/Toolbar.vue +10 -4
- package/dist/components/layout/Toolbar.vue.d.ts +1 -0
- package/dist/components/layout/types.d.ts +1 -0
- package/dist/components/overlays/CommandPalette.vue +8 -4
- package/dist/components/overlays/CommandPalette.vue.d.ts +3 -8
- package/dist/components/overlays/ConfirmDialog.vue +7 -5
- package/dist/components/overlays/ConfirmDialog.vue.d.ts +12 -8
- package/dist/components/overlays/ConfirmHost.vue +22 -19
- package/dist/components/overlays/ConfirmHost.vue.d.ts +8 -6
- package/dist/components/overlays/ContextMenu.vue +9 -11
- package/dist/components/overlays/ContextMenu.vue.d.ts +2 -2
- package/dist/components/overlays/Drawer.vue +2 -0
- package/dist/components/overlays/Drawer.vue.d.ts +4 -2
- package/dist/components/overlays/DropdownMenu.vue +13 -21
- package/dist/components/overlays/DropdownMenu.vue.d.ts +2 -2
- package/dist/components/overlays/HoverCard.vue +8 -2
- package/dist/components/overlays/HoverCard.vue.d.ts +2 -2
- package/dist/components/overlays/Modal.vue +2 -0
- package/dist/components/overlays/Modal.vue.d.ts +4 -2
- package/dist/components/overlays/Popover.vue +19 -4
- package/dist/components/overlays/Popover.vue.d.ts +2 -2
- package/dist/components/overlays/Toaster.vue +39 -7
- package/dist/components/overlays/Tooltip.vue +39 -14
- package/dist/components/overlays/types.d.ts +6 -0
- package/dist/components/templates/AccountMenu.vue.d.ts +2 -2
- package/dist/components/templates/AppearanceSettings.vue +83 -77
- package/dist/components/templates/AppearanceSettings.vue.d.ts +2 -2
- package/dist/components/templates/AuthLayout.vue +2 -2
- package/dist/components/templates/Brand.vue.d.ts +2 -2
- package/dist/components/templates/DashboardLayout.vue +3 -7
- package/dist/components/templates/DashboardLayout.vue.d.ts +8 -18
- package/dist/components/templates/DetailPage.vue +3 -1
- package/dist/components/templates/DetailPage.vue.d.ts +2 -2
- package/dist/components/templates/ErrorPage.vue +11 -5
- package/dist/components/templates/ErrorPage.vue.d.ts +10 -6
- package/dist/components/templates/FormPage.vue +16 -3
- package/dist/components/templates/ListPage.vue +19 -6
- package/dist/components/templates/ListPage.vue.d.ts +1 -0
- package/dist/components/templates/NotFoundPage.vue +2 -0
- package/dist/components/templates/NotFoundPage.vue.d.ts +4 -0
- package/dist/components/templates/SaveBar.vue +18 -3
- package/dist/components/templates/SaveBar.vue.d.ts +5 -5
- package/dist/components/templates/SettingsGroup.vue +1 -1
- package/dist/components/templates/SettingsGroup.vue.d.ts +3 -1
- package/dist/components/templates/SettingsModal.vue +73 -46
- package/dist/components/templates/SettingsModal.vue.d.ts +9 -11
- package/dist/components/templates/SettingsPage.vue.d.ts +2 -2
- package/dist/components/templates/SettingsSection.vue.d.ts +4 -2
- package/dist/components/templates/types.d.ts +4 -0
- package/dist/composables/useAlert.mjs +5 -10
- package/dist/composables/useConfirm.mjs +5 -5
- package/dist/composables/useField.d.ts +4 -0
- package/dist/composables/useField.mjs +16 -5
- package/dist/composables/useFileDrop.mjs +2 -1
- package/dist/composables/useOverlay.mjs +2 -1
- package/dist/composables/usePreset.mjs +4 -10
- package/dist/composables/usePrompt.mjs +6 -13
- package/dist/composables/useScrollLock.mjs +4 -3
- package/dist/composables/useTheme.mjs +3 -11
- package/dist/composables/useToast.mjs +2 -1
- package/dist/editor/RichTextEditor.vue +31 -6
- package/dist/editor/RichTextEditor.vue.d.ts +2 -2
- package/dist/editor/rich-text-editor.css +9 -4
- package/dist/grid/DataGrid.vue +72 -12
- package/dist/grid/GridToolbar.vue +1 -1
- package/dist/grid/GridToolbar.vue.d.ts +2 -2
- package/dist/grid/cells/BadgeCell.vue +3 -1
- package/dist/grid/cells/BadgeCell.vue.d.ts +17 -3
- package/dist/grid/cells/DateCell.vue +3 -1
- package/dist/grid/cells/DateCell.vue.d.ts +17 -3
- package/dist/grid/cells/EmptyCell.vue +15 -0
- package/dist/grid/cells/EmptyCell.vue.d.ts +392 -0
- package/dist/grid/cells/LinkCell.vue +9 -2
- package/dist/grid/cells/LinkCell.vue.d.ts +17 -3
- package/dist/grid/data-grid.css +5 -5
- package/dist/grid/types.d.ts +2 -0
- package/dist/internal/core/ButtonContent.vue +3 -0
- package/dist/internal/core/LiveRegion.vue +26 -0
- package/dist/internal/core/LiveRegion.vue.d.ts +393 -0
- package/dist/internal/core/copy.d.ts +6 -0
- package/dist/internal/core/copy.mjs +14 -0
- package/dist/internal/core/focus.d.ts +6 -0
- package/dist/internal/core/focus.mjs +46 -0
- package/dist/internal/core/intl.d.ts +4 -2
- package/dist/internal/core/intl.mjs +23 -22
- package/dist/internal/core/live.d.ts +6 -0
- package/dist/internal/core/live.mjs +22 -0
- package/dist/internal/core/preset.d.ts +0 -1
- package/dist/internal/core/preset.mjs +4 -14
- package/dist/internal/core/resize.d.ts +1 -0
- package/dist/internal/core/resize.mjs +11 -0
- package/dist/internal/core/roving.d.ts +7 -0
- package/dist/internal/core/roving.mjs +19 -1
- package/dist/internal/core/scroll-edges.d.ts +5 -0
- package/dist/internal/core/scroll-edges.mjs +63 -0
- package/dist/internal/core/storage.d.ts +2 -0
- package/dist/internal/core/storage.mjs +8 -0
- package/dist/internal/core/theme.d.ts +0 -1
- package/dist/internal/core/theme.mjs +4 -13
- package/dist/internal/core/tone.d.ts +1 -0
- package/dist/internal/core/tone.mjs +4 -0
- package/dist/internal/core/transition.d.ts +1 -0
- package/dist/internal/core/transition.mjs +20 -0
- package/dist/internal/data/rows.mjs +6 -5
- package/dist/internal/data/tree.d.ts +2 -3
- package/dist/internal/feedback/LoadingHost.vue +16 -6
- package/dist/internal/feedback/loading.mjs +2 -1
- package/dist/internal/feedback/progress.d.ts +12 -0
- package/dist/internal/feedback/progress.mjs +13 -0
- package/dist/internal/floating/layer.d.ts +1 -1
- package/dist/internal/floating/layer.mjs +17 -10
- package/dist/internal/forms/keys.d.ts +4 -0
- package/dist/internal/forms/keys.mjs +1 -0
- package/dist/internal/forms/name.d.ts +2 -0
- package/dist/internal/forms/name.mjs +12 -0
- package/dist/internal/forms/standard-schema.d.ts +0 -2
- package/dist/internal/forms/standard-schema.mjs +0 -18
- package/dist/internal/layout/NavGroup.vue +104 -0
- package/dist/internal/layout/NavGroup.vue.d.ts +412 -0
- package/dist/internal/overlays/DialogFrame.vue +53 -6
- package/dist/internal/overlays/DialogFrame.vue.d.ts +5 -2
- package/dist/internal/overlays/MenuList.vue +4 -11
- package/dist/internal/overlays/MenuList.vue.d.ts +6 -6
- package/dist/internal/overlays/OverlayView.vue +2 -0
- package/dist/internal/overlays/OverlayView.vue.d.ts +8 -6
- package/dist/internal/overlays/confirm.d.ts +6 -1
- package/dist/internal/overlays/confirm.mjs +5 -2
- package/dist/internal/overlays/dialog.d.ts +1 -0
- package/dist/internal/overlays/dialog.mjs +9 -9
- package/dist/internal/overlays/hover.d.ts +1 -0
- package/dist/internal/overlays/hover.mjs +20 -3
- package/dist/internal/overlays/menu.d.ts +9 -0
- package/dist/internal/overlays/menu.mjs +15 -0
- package/dist/internal/overlays/stack.d.ts +1 -0
- package/dist/locale/en.mjs +70 -18
- package/dist/locale/index.d.ts +1 -1
- package/dist/locale/index.mjs +5 -11
- package/dist/locale/it.mjs +70 -18
- package/dist/locale/types.d.ts +54 -2
- package/dist/nuxt/runtime/RouteAnnouncer.vue +21 -0
- package/dist/nuxt/runtime/RouteAnnouncer.vue.d.ts +3 -0
- package/dist/nuxt/runtime/layout-head.d.ts +2 -0
- package/dist/nuxt/runtime/layout-head.mjs +13 -0
- package/dist/nuxt/runtime/layouts/auth.vue +4 -0
- package/dist/nuxt/runtime/layouts/auth.vue.d.ts +2 -2
- package/dist/nuxt/runtime/layouts/dashboard.vue +4 -0
- package/dist/nuxt/runtime/layouts/dashboard.vue.d.ts +2 -2
- package/dist/nuxt/runtime/locale.mjs +9 -1
- package/dist/nuxt/runtime/theme.mjs +17 -12
- package/dist/styles/base.css +0 -13
- package/dist/styles/core/button.css +8 -8
- package/dist/styles/core/field-message.css +2 -2
- package/dist/styles/core/form-field.css +11 -2
- package/dist/styles/core/input.css +4 -7
- package/dist/styles/core/kbd.css +0 -1
- package/dist/styles/data/badge.css +8 -4
- package/dist/styles/data/description-list.css +2 -2
- package/dist/styles/data/filter-pills.css +5 -8
- package/dist/styles/data/pagination.css +2 -2
- package/dist/styles/data/stat.css +3 -3
- package/dist/styles/data/table.css +2 -2
- package/dist/styles/data/tag.css +1 -2
- package/dist/styles/data/timeline.css +1 -1
- package/dist/styles/data/tree.css +31 -10
- package/dist/styles/feedback/alert.css +0 -3
- package/dist/styles/feedback/loading-host.css +4 -0
- package/dist/styles/feedback/loading-overlay.css +4 -0
- package/dist/styles/forced-colors.css +215 -0
- package/dist/styles/forms/calendar.css +2 -2
- package/dist/styles/forms/choice.css +2 -2
- package/dist/styles/forms/color-input.css +1 -1
- package/dist/styles/forms/combobox.css +2 -24
- package/dist/styles/forms/date-input.css +2 -2
- package/dist/styles/forms/date-picker.css +3 -7
- package/dist/styles/forms/file-input.css +4 -3
- package/dist/styles/forms/input-group.css +6 -0
- package/dist/styles/forms/number-input.css +2 -2
- package/dist/styles/forms/password-input.css +5 -2
- package/dist/styles/forms/pin-input.css +4 -2
- package/dist/styles/forms/segmented.css +2 -2
- package/dist/styles/forms/select.css +6 -6
- package/dist/styles/forms/slider.css +5 -4
- package/dist/styles/forms/switch.css +5 -1
- package/dist/styles/index.css +1 -0
- package/dist/styles/layout/app-shell.css +15 -3
- package/dist/styles/layout/copy-button.css +1 -1
- package/dist/styles/layout/nav-link.css +1 -33
- package/dist/styles/layout/separator.css +1 -1
- package/dist/styles/layout/sidebar-nav.css +15 -4
- package/dist/styles/layout/sidebar.css +20 -6
- package/dist/styles/layout/tabs.css +9 -4
- package/dist/styles/layout/theme-switch.css +0 -6
- package/dist/styles/overlays/command.css +9 -15
- package/dist/styles/overlays/confirm.css +1 -1
- package/dist/styles/overlays/drawer.css +0 -1
- package/dist/styles/overlays/menu.css +2 -17
- package/dist/styles/overlays/modal.css +29 -6
- package/dist/styles/overlays/toast.css +1 -2
- package/dist/styles/presets.css +4 -4
- package/dist/styles/shared.css +96 -3
- package/dist/styles/templates/account-menu.css +3 -3
- package/dist/styles/templates/auth-layout.css +1 -1
- package/dist/styles/templates/brand.css +3 -3
- package/dist/styles/templates/empty-page.css +1 -1
- package/dist/styles/templates/form-page.css +10 -0
- package/dist/styles/templates/save-bar.css +10 -0
- package/dist/styles/templates/settings-modal.css +42 -44
- package/dist/styles/templates/settings-page.css +3 -3
- package/dist/styles/tokens.css +12 -2
- package/package.json +1 -1
- package/dist/internal/overlays/focus.d.ts +0 -1
- package/dist/internal/overlays/focus.mjs +0 -24
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { onBeforeUnmount, ref, watch } from "vue";
|
|
2
|
+
const EDGE_TOLERANCE = 1;
|
|
3
|
+
export function useScrollEdges(target) {
|
|
4
|
+
const overflowTop = ref(false);
|
|
5
|
+
const overflowBottom = ref(false);
|
|
6
|
+
let element = null;
|
|
7
|
+
let frame = 0;
|
|
8
|
+
let resizeObserver;
|
|
9
|
+
let mutationObserver;
|
|
10
|
+
function measure() {
|
|
11
|
+
frame = 0;
|
|
12
|
+
if (!element) return;
|
|
13
|
+
const { scrollTop, scrollHeight, clientHeight } = element;
|
|
14
|
+
overflowTop.value = scrollTop > EDGE_TOLERANCE;
|
|
15
|
+
overflowBottom.value = scrollHeight - clientHeight - scrollTop > EDGE_TOLERANCE;
|
|
16
|
+
}
|
|
17
|
+
function schedule() {
|
|
18
|
+
if (frame) return;
|
|
19
|
+
frame = requestAnimationFrame(measure);
|
|
20
|
+
}
|
|
21
|
+
function observeChildren() {
|
|
22
|
+
if (!element || !resizeObserver) return;
|
|
23
|
+
resizeObserver.disconnect();
|
|
24
|
+
resizeObserver.observe(element);
|
|
25
|
+
for (const child of element.children) resizeObserver.observe(child);
|
|
26
|
+
}
|
|
27
|
+
function detach() {
|
|
28
|
+
if (frame) cancelAnimationFrame(frame);
|
|
29
|
+
frame = 0;
|
|
30
|
+
element?.removeEventListener("scroll", schedule);
|
|
31
|
+
resizeObserver?.disconnect();
|
|
32
|
+
mutationObserver?.disconnect();
|
|
33
|
+
element = null;
|
|
34
|
+
}
|
|
35
|
+
function attach(next) {
|
|
36
|
+
element = next;
|
|
37
|
+
next.addEventListener("scroll", schedule, { passive: true });
|
|
38
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
39
|
+
resizeObserver = new ResizeObserver(schedule);
|
|
40
|
+
observeChildren();
|
|
41
|
+
}
|
|
42
|
+
if (typeof MutationObserver !== "undefined") {
|
|
43
|
+
mutationObserver = new MutationObserver(() => {
|
|
44
|
+
observeChildren();
|
|
45
|
+
schedule();
|
|
46
|
+
});
|
|
47
|
+
mutationObserver.observe(next, { childList: true });
|
|
48
|
+
}
|
|
49
|
+
measure();
|
|
50
|
+
}
|
|
51
|
+
watch(
|
|
52
|
+
target,
|
|
53
|
+
(next) => {
|
|
54
|
+
if (next === element) return;
|
|
55
|
+
detach();
|
|
56
|
+
if (next) attach(next);
|
|
57
|
+
else overflowTop.value = overflowBottom.value = false;
|
|
58
|
+
},
|
|
59
|
+
{ flush: "post", immediate: true }
|
|
60
|
+
);
|
|
61
|
+
onBeforeUnmount(detach);
|
|
62
|
+
return { overflowTop, overflowBottom, update: schedule };
|
|
63
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { Ref } from 'vue';
|
|
1
2
|
export declare function readStorage(key: string): string | null;
|
|
2
3
|
export declare function writeStorage(key: string, value: string): void;
|
|
3
4
|
export declare function removeStorage(key: string): void;
|
|
4
5
|
export declare function readStorageJson(key: string): unknown;
|
|
5
6
|
export declare function writeStorageJson(key: string, value: unknown): void;
|
|
7
|
+
export declare function createStoredRef<T extends string>(key: string, isValid: (value: unknown) => value is T, fallback: T): Ref<T>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { effectScope, ref, watch } from "vue";
|
|
2
|
+
import { isClient } from "./env.mjs";
|
|
1
3
|
export function readStorage(key) {
|
|
2
4
|
try {
|
|
3
5
|
return localStorage.getItem(key);
|
|
@@ -29,3 +31,9 @@ export function readStorageJson(key) {
|
|
|
29
31
|
export function writeStorageJson(key, value) {
|
|
30
32
|
writeStorage(key, JSON.stringify(value));
|
|
31
33
|
}
|
|
34
|
+
export function createStoredRef(key, isValid, fallback) {
|
|
35
|
+
const stored = isClient() ? readStorage(key) : null;
|
|
36
|
+
const store = ref(isValid(stored) ? stored : fallback);
|
|
37
|
+
if (isClient()) effectScope(true).run(() => watch(store, (value) => writeStorage(key, value)));
|
|
38
|
+
return store;
|
|
39
|
+
}
|
|
@@ -3,7 +3,6 @@ import type { ThemePreference } from '../../composables/useTheme';
|
|
|
3
3
|
export declare const THEME_STORAGE_KEY = "k-theme";
|
|
4
4
|
export declare const THEME_ORDER: readonly ThemePreference[];
|
|
5
5
|
export declare function isThemePreference(value: unknown): value is ThemePreference;
|
|
6
|
-
export declare function injectThemeStore(): Ref<ThemePreference> | null;
|
|
7
6
|
export declare function resolveThemeStore(): Ref<ThemePreference>;
|
|
8
7
|
export declare const systemDark: import("vue").ShallowRef<boolean, boolean>;
|
|
9
8
|
export declare function listenToSystemTheme(): void;
|
|
@@ -1,25 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getCurrentInstance, inject, shallowRef } from "vue";
|
|
2
2
|
import { THEME_KEY } from "../keys.mjs";
|
|
3
3
|
import { DARK_SCHEME_QUERY, isClient } from "./env.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { createStoredRef } from "./storage.mjs";
|
|
5
5
|
export const THEME_STORAGE_KEY = "k-theme";
|
|
6
6
|
export const THEME_ORDER = ["system", "light", "dark"];
|
|
7
7
|
export function isThemePreference(value) {
|
|
8
8
|
return THEME_ORDER.includes(value);
|
|
9
9
|
}
|
|
10
|
-
export function injectThemeStore() {
|
|
11
|
-
return getCurrentInstance() ? inject(THEME_KEY, null) : null;
|
|
12
|
-
}
|
|
13
10
|
let localStore;
|
|
14
|
-
function createLocalStore() {
|
|
15
|
-
const stored = isClient() ? readStorage(THEME_STORAGE_KEY) : null;
|
|
16
|
-
const store = ref(isThemePreference(stored) ? stored : "system");
|
|
17
|
-
if (isClient())
|
|
18
|
-
effectScope(true).run(() => watch(store, (value) => writeStorage(THEME_STORAGE_KEY, value)));
|
|
19
|
-
return store;
|
|
20
|
-
}
|
|
21
11
|
export function resolveThemeStore() {
|
|
22
|
-
|
|
12
|
+
const injected = getCurrentInstance() ? inject(THEME_KEY, null) : null;
|
|
13
|
+
return injected ?? (localStore ??= createStoredRef(THEME_STORAGE_KEY, isThemePreference, "system"));
|
|
23
14
|
}
|
|
24
15
|
export const systemDark = shallowRef(false);
|
|
25
16
|
let systemListening = false;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { Tone } from '../../components/core/types';
|
|
2
2
|
import type { IconName } from '../../composables/useIcons';
|
|
3
3
|
export declare const toneIcons: Record<Tone, IconName>;
|
|
4
|
+
export declare function spokenToneKey(tone: string | undefined): "tone.danger" | "tone.warning" | "tone.info" | "tone.success" | undefined;
|
|
@@ -6,3 +6,7 @@ export const toneIcons = {
|
|
|
6
6
|
warning: "exclamation-triangle",
|
|
7
7
|
danger: "exclamation-circle"
|
|
8
8
|
};
|
|
9
|
+
const SPOKEN_TONES = ["danger", "warning", "info", "success"];
|
|
10
|
+
export function spokenToneKey(tone) {
|
|
11
|
+
return SPOKEN_TONES.includes(tone) ? `tone.${tone}` : void 0;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function withViewTransition(update: () => void, enabled?: boolean): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { nextTick } from "vue";
|
|
2
|
+
import { isClient, prefersReducedMotion } from "./env.mjs";
|
|
3
|
+
function hasTopLayerContent() {
|
|
4
|
+
try {
|
|
5
|
+
return Boolean(document.querySelector(":modal, :popover-open"));
|
|
6
|
+
} catch {
|
|
7
|
+
return Boolean(document.querySelector("dialog[open]"));
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function withViewTransition(update, enabled = true) {
|
|
11
|
+
const canAnimate = enabled && isClient() && typeof document.startViewTransition === "function" && !prefersReducedMotion() && !hasTopLayerContent();
|
|
12
|
+
if (!canAnimate) {
|
|
13
|
+
update();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
document.startViewTransition(async () => {
|
|
17
|
+
update();
|
|
18
|
+
await nextTick();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { collator } from "../core/intl.mjs";
|
|
1
2
|
export function createCollator(locale) {
|
|
2
|
-
return
|
|
3
|
+
return collator(locale, { numeric: true, sensitivity: "base" });
|
|
3
4
|
}
|
|
4
5
|
function isEmptyCell(value) {
|
|
5
6
|
return value === null || value === void 0 || value === "";
|
|
6
7
|
}
|
|
7
|
-
export function compareValues(a, b,
|
|
8
|
+
export function compareValues(a, b, collator2) {
|
|
8
9
|
if (a === b) return 0;
|
|
9
10
|
if (isEmptyCell(a)) return 1;
|
|
10
11
|
if (isEmptyCell(b)) return -1;
|
|
11
12
|
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
12
13
|
if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
|
|
13
14
|
if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
|
|
14
|
-
return
|
|
15
|
+
return collator2.compare(String(a), String(b));
|
|
15
16
|
}
|
|
16
17
|
export function readColumn(row, key, column) {
|
|
17
18
|
return column?.value ? column.value(row) : row[key];
|
|
@@ -23,14 +24,14 @@ export function readCell(row, key, columns) {
|
|
|
23
24
|
columns?.find((column) => column.key === key)
|
|
24
25
|
);
|
|
25
26
|
}
|
|
26
|
-
export function sortRows(rows, sort, columns,
|
|
27
|
+
export function sortRows(rows, sort, columns, collator2) {
|
|
27
28
|
if (!sort) return rows;
|
|
28
29
|
const column = columns?.find((item) => item.key === sort.key);
|
|
29
30
|
const direction = sort.direction === "asc" ? 1 : -1;
|
|
30
31
|
return [...rows].sort((a, b) => {
|
|
31
32
|
const left = readColumn(a, sort.key, column);
|
|
32
33
|
const right = readColumn(b, sort.key, column);
|
|
33
|
-
const order = compareValues(left, right,
|
|
34
|
+
const order = compareValues(left, right, collator2);
|
|
34
35
|
return isEmptyCell(left) || isEmptyCell(right) ? order : order * direction;
|
|
35
36
|
});
|
|
36
37
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { TreeMove,
|
|
2
|
-
type NodeId = TreeNode['id'];
|
|
1
|
+
import type { TreeMove, TreeNodeId } from '../../components/data/types';
|
|
3
2
|
interface TreeShape<T> {
|
|
4
|
-
id:
|
|
3
|
+
id: TreeNodeId;
|
|
5
4
|
children?: T[];
|
|
6
5
|
}
|
|
7
6
|
export declare function applyTreeMove<T extends TreeShape<T>>(nodes: T[], move: TreeMove): T[];
|
|
@@ -17,14 +17,15 @@
|
|
|
17
17
|
ref="screenRef"
|
|
18
18
|
class="k-loading-screen"
|
|
19
19
|
:aria-label="screenLabel"
|
|
20
|
-
|
|
20
|
+
tabindex="-1"
|
|
21
21
|
@cancel.prevent
|
|
22
22
|
>
|
|
23
|
-
<span v-if="screenOpen" class="k-loading-screen-indicator"
|
|
24
|
-
<Spinner
|
|
23
|
+
<span v-if="screenOpen" class="k-loading-screen-indicator">
|
|
24
|
+
<Spinner decorative />
|
|
25
25
|
<span class="k-loading-screen-label" aria-hidden="true">{{ screenLabel }}</span>
|
|
26
26
|
</span>
|
|
27
27
|
</dialog>
|
|
28
|
+
<LiveRegion :message="statusMessage" />
|
|
28
29
|
</template>
|
|
29
30
|
</template>
|
|
30
31
|
|
|
@@ -32,8 +33,10 @@
|
|
|
32
33
|
import { computed, nextTick, onBeforeUnmount, ref, useTemplateRef, watch } from "vue";
|
|
33
34
|
import Spinner from "../../components/core/Spinner.vue";
|
|
34
35
|
import { useLocale } from "../../composables/useLocale";
|
|
36
|
+
import { activeElement } from "../core/focus";
|
|
37
|
+
import LiveRegion from "../core/LiveRegion.vue";
|
|
35
38
|
import { isPopoverOpen, supportsPopover } from "../floating/support";
|
|
36
|
-
import { modalStack } from "../overlays/dialog";
|
|
39
|
+
import { modalStack, releaseModal } from "../overlays/dialog";
|
|
37
40
|
import { useHostClaim } from "../overlays/host";
|
|
38
41
|
import { loadingTasks } from "./loading";
|
|
39
42
|
const props = defineProps({
|
|
@@ -52,6 +55,10 @@ const screenOpen = ref(false);
|
|
|
52
55
|
const busy = computed(() => loadingTasks.length > 0);
|
|
53
56
|
const blockingTask = computed(() => loadingTasks.find((task) => task.blocking));
|
|
54
57
|
const screenLabel = computed(() => blockingTask.value?.label ?? barLabel.value);
|
|
58
|
+
const statusMessage = computed(() => {
|
|
59
|
+
if (phase.value !== "running" || screenOpen.value) return "";
|
|
60
|
+
return loadingTasks.find((task) => task.label)?.label ?? barLabel.value;
|
|
61
|
+
});
|
|
55
62
|
let showTimer;
|
|
56
63
|
let trickleTimer;
|
|
57
64
|
let hideTimer;
|
|
@@ -104,18 +111,21 @@ function openScreen() {
|
|
|
104
111
|
screenTimer = setTimeout(async () => {
|
|
105
112
|
const dialog = screenRef.value;
|
|
106
113
|
if (!dialog || dialog.open || !blockingTask.value) return;
|
|
107
|
-
returnTarget =
|
|
114
|
+
returnTarget = activeElement();
|
|
108
115
|
screenOpen.value = true;
|
|
109
116
|
dialog.showModal();
|
|
117
|
+
releaseModal(dialog);
|
|
118
|
+
modalStack.push(dialog);
|
|
110
119
|
if (phase.value !== "idle") showBar();
|
|
111
120
|
await nextTick();
|
|
112
|
-
dialog.
|
|
121
|
+
dialog.focus({ preventScroll: true });
|
|
113
122
|
}, props.delay);
|
|
114
123
|
}
|
|
115
124
|
function closeScreen() {
|
|
116
125
|
clearTimeout(screenTimer);
|
|
117
126
|
const dialog = screenRef.value;
|
|
118
127
|
screenOpen.value = false;
|
|
128
|
+
releaseModal(dialog);
|
|
119
129
|
if (!dialog?.open) return;
|
|
120
130
|
dialog.close();
|
|
121
131
|
if (returnTarget?.isConnected) returnTarget.focus({ preventScroll: true });
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { shallowReactive } from "vue";
|
|
2
|
+
import { isClient } from "../core/env.mjs";
|
|
2
3
|
export const loadingTasks = shallowReactive([]);
|
|
3
4
|
let counter = 0;
|
|
4
5
|
export function startLoadingTask(options = {}) {
|
|
5
|
-
if (
|
|
6
|
+
if (!isClient()) return () => {
|
|
6
7
|
};
|
|
7
8
|
counter += 1;
|
|
8
9
|
const task = {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ProgressValueSource {
|
|
2
|
+
value?: number | null;
|
|
3
|
+
max: number;
|
|
4
|
+
valueText?: (value: number, max: number) => string;
|
|
5
|
+
}
|
|
6
|
+
export declare function percentText(ratio: number): string;
|
|
7
|
+
export declare function useProgressValue(props: ProgressValueSource): {
|
|
8
|
+
indeterminate: import("vue").ComputedRef<boolean>;
|
|
9
|
+
clamped: import("vue").ComputedRef<number>;
|
|
10
|
+
ratio: import("vue").ComputedRef<number>;
|
|
11
|
+
displayValue: import("vue").ComputedRef<string>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
export function percentText(ratio) {
|
|
3
|
+
return `${Math.round(ratio * 100)}%`;
|
|
4
|
+
}
|
|
5
|
+
export function useProgressValue(props) {
|
|
6
|
+
const indeterminate = computed(() => props.value === null || props.value === void 0);
|
|
7
|
+
const clamped = computed(() => Math.min(Math.max(props.value ?? 0, 0), props.max));
|
|
8
|
+
const ratio = computed(() => props.max > 0 ? clamped.value / props.max : 0);
|
|
9
|
+
const displayValue = computed(
|
|
10
|
+
() => props.valueText ? props.valueText(clamped.value, props.max) : percentText(ratio.value)
|
|
11
|
+
);
|
|
12
|
+
return { indeterminate, clamped, ratio, displayValue };
|
|
13
|
+
}
|
|
@@ -25,4 +25,4 @@ export declare function useFloatingLayer(options: FloatingLayerOptions): {
|
|
|
25
25
|
reposition: () => void;
|
|
26
26
|
};
|
|
27
27
|
export declare const ARROW_OFFSET = 4;
|
|
28
|
-
export declare function syncOpenModel(open: Ref<boolean>,
|
|
28
|
+
export declare function syncOpenModel(open: Ref<boolean>, isOpen: Readonly<Ref<boolean>>, show: () => void, hide: () => void): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { onBeforeUnmount, onMounted, ref, useId, watch } from "vue";
|
|
2
|
+
import { activeElement, isFocusLost } from "../core/focus.mjs";
|
|
2
3
|
import {
|
|
3
4
|
ANCHOR_STYLE_PROPERTIES,
|
|
4
5
|
addAnchorName,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
supportsPopover,
|
|
16
17
|
waitForAnimations
|
|
17
18
|
} from "./support.mjs";
|
|
19
|
+
const fallbackStack = [];
|
|
18
20
|
export function useFloatingLayer(options) {
|
|
19
21
|
const id = useId();
|
|
20
22
|
const anchorName = `--k-anchor-${id.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
@@ -105,7 +107,7 @@ export function useFloatingLayer(options) {
|
|
|
105
107
|
function opening() {
|
|
106
108
|
if (isOpen.value) return;
|
|
107
109
|
isOpen.value = true;
|
|
108
|
-
returnTarget =
|
|
110
|
+
returnTarget = activeElement();
|
|
109
111
|
startPositioning();
|
|
110
112
|
options.onBeforeOpen?.();
|
|
111
113
|
}
|
|
@@ -115,9 +117,7 @@ export function useFloatingLayer(options) {
|
|
|
115
117
|
function closing() {
|
|
116
118
|
const element = options.floating.value;
|
|
117
119
|
if (!element || options.returnFocus?.() === false) return;
|
|
118
|
-
|
|
119
|
-
const focusInside = active instanceof Node && element.contains(active);
|
|
120
|
-
if (!focusInside && active && active !== document.body) return;
|
|
120
|
+
if (!isFocusLost(element)) return;
|
|
121
121
|
const fallback = anchorElement();
|
|
122
122
|
const target = returnTarget && returnTarget.isConnected && !element.contains(returnTarget) ? returnTarget : fallback;
|
|
123
123
|
if (target instanceof HTMLElement) target.focus({ preventScroll: true });
|
|
@@ -153,12 +153,19 @@ export function useFloatingLayer(options) {
|
|
|
153
153
|
if (element.contains(target) || anchorElement()?.contains(target)) return;
|
|
154
154
|
hide();
|
|
155
155
|
};
|
|
156
|
+
const entry = {};
|
|
156
157
|
const onKeyDown = (event) => {
|
|
157
|
-
if (event.key
|
|
158
|
+
if (event.key !== "Escape" || event.defaultPrevented) return;
|
|
159
|
+
if (fallbackStack[fallbackStack.length - 1] !== entry) return;
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
hide();
|
|
158
162
|
};
|
|
163
|
+
fallbackStack.push(entry);
|
|
159
164
|
document.addEventListener("pointerdown", onPointerDown, true);
|
|
160
165
|
document.addEventListener("keydown", onKeyDown);
|
|
161
166
|
removeFallbackListeners = () => {
|
|
167
|
+
const index = fallbackStack.indexOf(entry);
|
|
168
|
+
if (index >= 0) fallbackStack.splice(index, 1);
|
|
162
169
|
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
163
170
|
document.removeEventListener("keydown", onKeyDown);
|
|
164
171
|
};
|
|
@@ -231,13 +238,13 @@ export function useFloatingLayer(options) {
|
|
|
231
238
|
};
|
|
232
239
|
}
|
|
233
240
|
export const ARROW_OFFSET = 4;
|
|
234
|
-
export function syncOpenModel(open,
|
|
235
|
-
watch(
|
|
241
|
+
export function syncOpenModel(open, isOpen, show, hide) {
|
|
242
|
+
watch(isOpen, (value) => {
|
|
236
243
|
if (open.value !== value) open.value = value;
|
|
237
244
|
});
|
|
238
245
|
watch(open, (value) => {
|
|
239
|
-
if (value ===
|
|
240
|
-
if (value)
|
|
241
|
-
else
|
|
246
|
+
if (value === isOpen.value) return;
|
|
247
|
+
if (value) show();
|
|
248
|
+
else hide();
|
|
242
249
|
});
|
|
243
250
|
}
|
|
@@ -24,3 +24,7 @@ export interface RadioGroupContext {
|
|
|
24
24
|
}
|
|
25
25
|
export declare const CHECKBOX_GROUP_KEY: InjectionKey<CheckboxGroupContext>;
|
|
26
26
|
export declare const RADIO_GROUP_KEY: InjectionKey<RadioGroupContext>;
|
|
27
|
+
export interface InputGroupContext {
|
|
28
|
+
register: (id: string) => () => void;
|
|
29
|
+
}
|
|
30
|
+
export declare const INPUT_GROUP_KEY: InjectionKey<InputGroupContext>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { onMounted, warn } from "vue";
|
|
2
|
+
export function hasNameAttrs(attrs) {
|
|
3
|
+
return Boolean(attrs["aria-label"] || attrs["aria-labelledby"]);
|
|
4
|
+
}
|
|
5
|
+
export function warnUnnamed(component, named) {
|
|
6
|
+
onMounted(() => {
|
|
7
|
+
if (!named())
|
|
8
|
+
warn(
|
|
9
|
+
`[@xleddyl/443-components-vue] ${component} needs an accessible name: pass a label prop, wrap it in a FormField with a label, or set aria-label or aria-labelledby.`
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -22,8 +22,6 @@ export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
export declare function isStandardSchema(value: unknown): value is StandardSchemaV1;
|
|
25
|
-
export declare function runStandardSchema(schema: StandardSchemaV1, value: unknown): Promise<readonly StandardSchemaIssue[]>;
|
|
26
|
-
export declare function issuesByField(issues: ReadonlyArray<StandardSchemaIssue>): Record<string, string>;
|
|
27
25
|
export type IssuePath = ReadonlyArray<PropertyKey>;
|
|
28
26
|
export type IssueKeyResolver = (path: IssuePath) => string | ReadonlyArray<string> | undefined;
|
|
29
27
|
export interface GroupedIssues {
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
export function isStandardSchema(value) {
|
|
2
2
|
return typeof value === "object" && value !== null && "~standard" in value;
|
|
3
3
|
}
|
|
4
|
-
function issueField(issue) {
|
|
5
|
-
const segment = issue.path?.[0];
|
|
6
|
-
if (segment === void 0) return void 0;
|
|
7
|
-
const key = typeof segment === "object" ? segment.key : segment;
|
|
8
|
-
return typeof key === "symbol" ? void 0 : String(key);
|
|
9
|
-
}
|
|
10
|
-
export async function runStandardSchema(schema, value) {
|
|
11
|
-
const result = await schema["~standard"].validate(value);
|
|
12
|
-
return result.issues ?? [];
|
|
13
|
-
}
|
|
14
|
-
export function issuesByField(issues) {
|
|
15
|
-
const errors = {};
|
|
16
|
-
for (const issue of issues) {
|
|
17
|
-
const field = issueField(issue);
|
|
18
|
-
if (field !== void 0 && !(field in errors)) errors[field] = issue.message;
|
|
19
|
-
}
|
|
20
|
-
return errors;
|
|
21
|
-
}
|
|
22
4
|
export function issuePath(issue) {
|
|
23
5
|
return (issue.path ?? []).map(
|
|
24
6
|
(segment) => typeof segment === "object" && segment !== null ? segment.key : segment
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="k-navgroup" :class="{ 'is-open': open && !rail }">
|
|
3
|
+
<button
|
|
4
|
+
ref="triggerRef"
|
|
5
|
+
type="button"
|
|
6
|
+
class="k-navlink k-navgroup-trigger"
|
|
7
|
+
:class="{
|
|
8
|
+
'is-child-active': childActive,
|
|
9
|
+
'is-disabled': item.disabled,
|
|
10
|
+
'is-menu-open': rail && menuOpen
|
|
11
|
+
}"
|
|
12
|
+
:disabled="item.disabled"
|
|
13
|
+
:aria-haspopup="rail ? 'menu' : void 0"
|
|
14
|
+
:aria-expanded="(rail ? menuOpen : open) ? 'true' : 'false'"
|
|
15
|
+
:aria-controls="rail ? menuId : panelId"
|
|
16
|
+
:title="rail ? item.label : void 0"
|
|
17
|
+
@click="onClick"
|
|
18
|
+
@keydown="onKeydown"
|
|
19
|
+
@pointerdown="onPointerDown"
|
|
20
|
+
>
|
|
21
|
+
<span v-if="item.icon" class="k-navlink-icon" aria-hidden="true">
|
|
22
|
+
<Icon :name="item.icon" />
|
|
23
|
+
</span>
|
|
24
|
+
<span class="k-navlink-label">{{ item.label }}</span>
|
|
25
|
+
<span v-if="item.badge !== void 0 && item.badge !== ''" class="k-navlink-badge">
|
|
26
|
+
{{ item.badge }}
|
|
27
|
+
</span>
|
|
28
|
+
<Icon name="chevron-right" class="k-navgroup-chevron" />
|
|
29
|
+
</button>
|
|
30
|
+
<div :id="panelId" class="k-navgroup-panel" :inert="open && !rail ? void 0 : true">
|
|
31
|
+
<div class="k-navgroup-list">
|
|
32
|
+
<slot />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<MenuList
|
|
36
|
+
v-if="rail"
|
|
37
|
+
:id="menuId"
|
|
38
|
+
ref="menuRef"
|
|
39
|
+
:items="menuItems"
|
|
40
|
+
:anchor="() => triggerRef"
|
|
41
|
+
placement="right-start"
|
|
42
|
+
:offset="10"
|
|
43
|
+
:label="item.label"
|
|
44
|
+
@open-change="menuOpen = $event"
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script setup>
|
|
50
|
+
import { computed, ref, useTemplateRef, watch } from "vue";
|
|
51
|
+
import Icon from "../../components/core/Icon.vue";
|
|
52
|
+
import { useInvoker } from "../floating/trigger";
|
|
53
|
+
import MenuList from "../overlays/MenuList.vue";
|
|
54
|
+
import { menuTriggerHandlers } from "../overlays/menu";
|
|
55
|
+
const props = defineProps({
|
|
56
|
+
item: { type: Object, required: true },
|
|
57
|
+
open: { type: Boolean, required: true },
|
|
58
|
+
rail: { type: Boolean, required: true },
|
|
59
|
+
childActive: { type: Boolean, required: true },
|
|
60
|
+
panelId: { type: String, required: true }
|
|
61
|
+
});
|
|
62
|
+
const emit = defineEmits(["toggle"]);
|
|
63
|
+
const menuId = computed(() => `${props.panelId}-menu`);
|
|
64
|
+
const triggerRef = useTemplateRef("triggerRef");
|
|
65
|
+
const menuRef = useTemplateRef("menuRef");
|
|
66
|
+
const menuOpen = ref(false);
|
|
67
|
+
const railTrigger = computed(() => props.rail ? triggerRef.value : null);
|
|
68
|
+
const menuElement = computed(() => menuRef.value?.el ?? null);
|
|
69
|
+
function toMenuItem(item) {
|
|
70
|
+
return {
|
|
71
|
+
label: item.label,
|
|
72
|
+
icon: item.icon,
|
|
73
|
+
to: item.to,
|
|
74
|
+
href: item.href,
|
|
75
|
+
target: item.external ? "_blank" : void 0,
|
|
76
|
+
disabled: item.disabled,
|
|
77
|
+
children: item.children?.length ? item.children.map(toMenuItem) : void 0
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const menuItems = computed(() => (props.item.children ?? []).map(toMenuItem));
|
|
81
|
+
useInvoker(railTrigger, menuElement, () => {
|
|
82
|
+
menuRef.value?.toggle("first", triggerRef.value);
|
|
83
|
+
});
|
|
84
|
+
function onClick() {
|
|
85
|
+
if (!props.rail) emit("toggle");
|
|
86
|
+
}
|
|
87
|
+
const menuTrigger = menuTriggerHandlers(
|
|
88
|
+
() => menuRef.value,
|
|
89
|
+
() => triggerRef.value,
|
|
90
|
+
["ArrowRight", "ArrowDown"]
|
|
91
|
+
);
|
|
92
|
+
function onKeydown(event) {
|
|
93
|
+
if (props.rail) menuTrigger.onKeydown(event);
|
|
94
|
+
}
|
|
95
|
+
function onPointerDown(event) {
|
|
96
|
+
if (props.rail) menuTrigger.onPointerDown(event);
|
|
97
|
+
}
|
|
98
|
+
watch(
|
|
99
|
+
() => props.rail,
|
|
100
|
+
(rail) => {
|
|
101
|
+
if (!rail) menuOpen.value = false;
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
</script>
|