@sewadah/selaras 0.3.0 → 0.3.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 +4 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +21 -6
- package/dist/runtime/components/AlertDialog.d.vue.ts +5 -3
- package/dist/runtime/components/AlertDialog.vue +17 -9
- package/dist/runtime/components/AlertDialog.vue.d.ts +5 -3
- package/dist/runtime/components/App.vue +2 -0
- package/dist/runtime/components/Autocomplete.d.vue.ts +2 -2
- package/dist/runtime/components/Autocomplete.vue +4 -2
- package/dist/runtime/components/Autocomplete.vue.d.ts +2 -2
- package/dist/runtime/components/Button.d.vue.ts +7 -5
- package/dist/runtime/components/Button.vue +14 -3
- package/dist/runtime/components/Button.vue.d.ts +7 -5
- package/dist/runtime/components/Calendar.d.vue.ts +69 -0
- package/dist/runtime/components/Calendar.vue +163 -0
- package/dist/runtime/components/Calendar.vue.d.ts +69 -0
- package/dist/runtime/components/Callout.vue +12 -1
- package/dist/runtime/components/CodeGroup.vue +13 -3
- package/dist/runtime/components/CodeTree.d.vue.ts +2 -0
- package/dist/runtime/components/CodeTree.vue +52 -1
- package/dist/runtime/components/CodeTree.vue.d.ts +2 -0
- package/dist/runtime/components/ColorModeSwitch.d.vue.ts +18 -0
- package/dist/runtime/components/ColorModeSwitch.vue +59 -0
- package/dist/runtime/components/ColorModeSwitch.vue.d.ts +18 -0
- package/dist/runtime/components/ColorPicker.d.vue.ts +8 -0
- package/dist/runtime/components/ColorPicker.vue +66 -45
- package/dist/runtime/components/ColorPicker.vue.d.ts +8 -0
- package/dist/runtime/components/Column.js +1 -1
- package/dist/runtime/components/CommandPalette.d.vue.ts +3 -1
- package/dist/runtime/components/CommandPalette.vue +5 -0
- package/dist/runtime/components/CommandPalette.vue.d.ts +3 -1
- package/dist/runtime/components/ConfirmRenderer.d.vue.ts +3 -0
- package/dist/runtime/components/ConfirmRenderer.vue +39 -0
- package/dist/runtime/components/ConfirmRenderer.vue.d.ts +3 -0
- package/dist/runtime/components/ContentNavigation.d.vue.ts +8 -1
- package/dist/runtime/components/ContentNavigation.vue +9 -2
- package/dist/runtime/components/ContentNavigation.vue.d.ts +8 -1
- package/dist/runtime/components/ContextMenu.d.vue.ts +8 -1
- package/dist/runtime/components/ContextMenu.vue +20 -6
- package/dist/runtime/components/ContextMenu.vue.d.ts +8 -1
- package/dist/runtime/components/DatePicker.d.vue.ts +10 -3
- package/dist/runtime/components/DatePicker.vue +24 -16
- package/dist/runtime/components/DatePicker.vue.d.ts +10 -3
- package/dist/runtime/components/Drawer.vue +3 -2
- package/dist/runtime/components/Dropdown.d.vue.ts +33 -6
- package/dist/runtime/components/Dropdown.vue +146 -12
- package/dist/runtime/components/Dropdown.vue.d.ts +33 -6
- package/dist/runtime/components/FileTree.d.vue.ts +2 -0
- package/dist/runtime/components/FileTree.vue +8 -4
- package/dist/runtime/components/FileTree.vue.d.ts +2 -0
- package/dist/runtime/components/FormField.d.vue.ts +4 -0
- package/dist/runtime/components/FormField.vue +19 -4
- package/dist/runtime/components/FormField.vue.d.ts +4 -0
- package/dist/runtime/components/Input.vue +3 -2
- package/dist/runtime/components/Kbd.vue +12 -2
- package/dist/runtime/components/Modal.vue +3 -2
- package/dist/runtime/components/NavigationMenu.d.vue.ts +10 -10
- package/dist/runtime/components/NavigationMenu.vue +13 -9
- package/dist/runtime/components/NavigationMenu.vue.d.ts +10 -10
- package/dist/runtime/components/NavigationMenuAccordionItem.vue +5 -1
- package/dist/runtime/components/NavigationMenuFlyoutList.vue +7 -4
- package/dist/runtime/components/Popover.d.vue.ts +8 -2
- package/dist/runtime/components/Popover.vue +13 -6
- package/dist/runtime/components/Popover.vue.d.ts +8 -2
- package/dist/runtime/components/ProsePre.d.vue.ts +2 -0
- package/dist/runtime/components/ProsePre.vue +2 -1
- package/dist/runtime/components/ProsePre.vue.d.ts +2 -0
- package/dist/runtime/components/Select.d.vue.ts +8 -4
- package/dist/runtime/components/Select.vue +4 -2
- package/dist/runtime/components/Select.vue.d.ts +8 -4
- package/dist/runtime/components/Slideover.vue +3 -2
- package/dist/runtime/components/SplitterPanel.d.vue.ts +2 -2
- package/dist/runtime/components/SplitterPanel.vue.d.ts +2 -2
- package/dist/runtime/components/Table.vue +95 -26
- package/dist/runtime/components/Tabs.d.vue.ts +44 -4
- package/dist/runtime/components/Tabs.vue +171 -18
- package/dist/runtime/components/Tabs.vue.d.ts +44 -4
- package/dist/runtime/components/Textarea.vue +3 -2
- package/dist/runtime/components/Timeline.d.vue.ts +19 -0
- package/dist/runtime/components/Timeline.vue +99 -0
- package/dist/runtime/components/Timeline.vue.d.ts +19 -0
- package/dist/runtime/components/Toast.d.vue.ts +10 -1
- package/dist/runtime/components/Toast.vue +94 -8
- package/dist/runtime/components/Toast.vue.d.ts +10 -1
- package/dist/runtime/components/Tooltip.d.vue.ts +8 -2
- package/dist/runtime/components/Tooltip.vue +13 -6
- package/dist/runtime/components/Tooltip.vue.d.ts +8 -2
- package/dist/runtime/composables/use-confirm.d.ts +14 -0
- package/dist/runtime/composables/use-confirm.js +28 -0
- package/dist/runtime/composables/use-form-field.d.ts +1 -0
- package/dist/runtime/composables/use-table.d.ts +3 -0
- package/dist/runtime/composables/use-table.js +10 -4
- package/dist/runtime/composables/use-toast.js +5 -1
- package/dist/runtime/directives/hotkey.d.ts +13 -0
- package/dist/runtime/directives/hotkey.js +107 -0
- package/dist/runtime/directives/index.d.ts +2 -0
- package/dist/runtime/directives/index.js +2 -0
- package/dist/runtime/directives/mask.d.ts +30 -0
- package/dist/runtime/directives/mask.js +165 -0
- package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +41 -36
- package/dist/runtime/internal/ComboboxSelectBase.vue +119 -26
- package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +41 -36
- package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +23 -17
- package/dist/runtime/internal/ComboboxSelectBody.vue +50 -11
- package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +23 -17
- package/dist/runtime/internal/DatePickerCalendarBody.vue +1 -1
- package/dist/runtime/internal/DatePickerRangeCalendarBody.vue +1 -1
- package/dist/runtime/internal/ShortcutHint.d.vue.ts +6 -0
- package/dist/runtime/internal/ShortcutHint.vue +13 -0
- package/dist/runtime/internal/ShortcutHint.vue.d.ts +6 -0
- package/dist/runtime/internal/ToastItemRenderer.d.vue.ts +9 -1
- package/dist/runtime/internal/ToastItemRenderer.vue +55 -8
- package/dist/runtime/internal/ToastItemRenderer.vue.d.ts +9 -1
- package/dist/runtime/internal/combobox-select.d.ts +19 -18
- package/dist/runtime/internal/combobox-select.js +38 -15
- package/dist/runtime/internal/programmatic-services.d.ts +13 -0
- package/dist/runtime/internal/programmatic-services.js +9 -0
- package/dist/runtime/internal/toast-stack.d.ts +36 -0
- package/dist/runtime/internal/toast-stack.js +36 -0
- package/dist/runtime/structural.css +1 -1
- package/dist/runtime/table.d.ts +1 -1
- package/dist/runtime/theme/alert-dialog.js +1 -1
- package/dist/runtime/theme/badge.d.ts +9 -0
- package/dist/runtime/theme/badge.js +2 -1
- package/dist/runtime/theme/button.d.ts +18 -0
- package/dist/runtime/theme/button.js +4 -1
- package/dist/runtime/theme/calendar.d.ts +86 -0
- package/dist/runtime/theme/calendar.js +19 -0
- package/dist/runtime/theme/callout.d.ts +0 -12
- package/dist/runtime/theme/callout.js +8 -8
- package/dist/runtime/theme/code-tree.js +1 -1
- package/dist/runtime/theme/command-palette.js +1 -1
- package/dist/runtime/theme/content-navigation.d.ts +3 -0
- package/dist/runtime/theme/content-navigation.js +1 -0
- package/dist/runtime/theme/drawer.js +1 -1
- package/dist/runtime/theme/dropdown.d.ts +21 -0
- package/dist/runtime/theme/dropdown.js +15 -2
- package/dist/runtime/theme/file-tree.js +1 -1
- package/dist/runtime/theme/form-field.d.ts +18 -0
- package/dist/runtime/theme/form-field.js +6 -1
- package/dist/runtime/theme/modal.js +1 -1
- package/dist/runtime/theme/slideover.js +1 -1
- package/dist/runtime/theme/table.d.ts +27 -0
- package/dist/runtime/theme/table.js +11 -2
- package/dist/runtime/theme/tabs.d.ts +87 -3
- package/dist/runtime/theme/tabs.js +46 -22
- package/dist/runtime/theme/timeline.d.ts +251 -0
- package/dist/runtime/theme/timeline.js +84 -0
- package/dist/runtime/theme/toast.d.ts +92 -0
- package/dist/runtime/theme/toast.js +29 -7
- package/dist/runtime/theme-config.d.ts +13 -2
- package/dist/runtime/types/index.d.ts +10 -2
- package/dist/runtime/utils/arrow.d.ts +21 -0
- package/dist/runtime/utils/arrow.js +10 -0
- package/dist/runtime/utils/autocomplete-contracts.d.ts +4 -4
- package/dist/runtime/utils/file-icons.js +3 -0
- package/dist/runtime/utils/icons.d.ts +2 -0
- package/dist/runtime/utils/icons.js +2 -0
- package/dist/runtime/utils/messages.d.ts +7 -0
- package/dist/runtime/utils/messages.js +7 -0
- package/dist/runtime/utils/navigation-menu.d.ts +7 -0
- package/dist/runtime/utils/navigation-menu.js +3 -0
- package/dist/runtime/utils/overlay.d.ts +17 -0
- package/dist/runtime/utils/overlay.js +7 -0
- package/dist/runtime/utils/select-contracts.d.ts +25 -11
- package/dist/runtime/utils/table-columns.js +1 -1
- package/dist/runtime/utils/table-contracts.d.ts +9 -1
- package/dist/runtime/utils/timeline-contracts.d.ts +42 -0
- package/dist/runtime/utils/timeline-contracts.js +0 -0
- package/dist/runtime/utils/ui.d.ts +11 -0
- package/dist/runtime/utils/ui.js +54 -1
- package/package.json +6 -5
|
@@ -15,6 +15,12 @@ export interface TabsProps {
|
|
|
15
15
|
variant?: TabsVariants['variant'];
|
|
16
16
|
defaultValue?: string;
|
|
17
17
|
modelValue?: string;
|
|
18
|
+
/** Direction of the tab list and its keyboard navigation. @default 'horizontal' */
|
|
19
|
+
orientation?: 'horizontal' | 'vertical';
|
|
20
|
+
/** Unmount inactive panels. Set to false to preserve their content and local state. @default true */
|
|
21
|
+
unmountOnHide?: boolean;
|
|
22
|
+
/** Enables horizontal scrolling and overflow controls when the tabs do not fit. */
|
|
23
|
+
scrollable?: boolean;
|
|
18
24
|
/** The active tab's text and underline accent. @default 'primary' */
|
|
19
25
|
color?: ColorRole;
|
|
20
26
|
ui?: UiProp<TabsThemeSlots>;
|
|
@@ -22,14 +28,48 @@ export interface TabsProps {
|
|
|
22
28
|
export interface TabsEmits {
|
|
23
29
|
'update:modelValue': [value: string];
|
|
24
30
|
}
|
|
25
|
-
declare
|
|
31
|
+
declare function scrollLeft(): void;
|
|
32
|
+
declare function scrollRight(): void;
|
|
33
|
+
declare var __VLS_10: {
|
|
34
|
+
buttonProps: {
|
|
35
|
+
type: "button";
|
|
36
|
+
disabled: boolean;
|
|
37
|
+
'aria-label': string;
|
|
38
|
+
onClick: typeof scrollLeft;
|
|
39
|
+
};
|
|
40
|
+
scroll: typeof scrollLeft;
|
|
41
|
+
disabled: boolean;
|
|
42
|
+
ariaLabel: string;
|
|
43
|
+
}, __VLS_19: {
|
|
44
|
+
class: unknown[];
|
|
45
|
+
}, __VLS_43: {
|
|
26
46
|
item: TabItem;
|
|
27
47
|
index: number;
|
|
28
|
-
},
|
|
48
|
+
}, __VLS_50: {
|
|
49
|
+
buttonProps: {
|
|
50
|
+
type: "button";
|
|
51
|
+
disabled: boolean;
|
|
52
|
+
'aria-label': string;
|
|
53
|
+
onClick: typeof scrollRight;
|
|
54
|
+
};
|
|
55
|
+
scroll: typeof scrollRight;
|
|
56
|
+
disabled: boolean;
|
|
57
|
+
ariaLabel: string;
|
|
58
|
+
}, __VLS_59: {
|
|
59
|
+
class: unknown[];
|
|
60
|
+
}, __VLS_73: string, __VLS_74: {};
|
|
29
61
|
type __VLS_Slots = {} & {
|
|
30
|
-
[K in NonNullable<typeof
|
|
62
|
+
[K in NonNullable<typeof __VLS_73>]?: (props: typeof __VLS_74) => any;
|
|
63
|
+
} & {
|
|
64
|
+
'scroll-left'?: (props: typeof __VLS_10) => any;
|
|
65
|
+
} & {
|
|
66
|
+
'scroll-left-icon'?: (props: typeof __VLS_19) => any;
|
|
67
|
+
} & {
|
|
68
|
+
label?: (props: typeof __VLS_43) => any;
|
|
69
|
+
} & {
|
|
70
|
+
'scroll-right'?: (props: typeof __VLS_50) => any;
|
|
31
71
|
} & {
|
|
32
|
-
|
|
72
|
+
'scroll-right-icon'?: (props: typeof __VLS_59) => any;
|
|
33
73
|
};
|
|
34
74
|
declare const __VLS_base: import("vue").DefineComponent<TabsProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
35
75
|
"update:modelValue": (value: string) => any;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { TabsContent, TabsIndicator, TabsList, TabsRoot, TabsTrigger } from "reka-ui";
|
|
3
|
-
import { computed } from "vue";
|
|
3
|
+
import { computed, getCurrentInstance, nextTick, onMounted, onUnmounted, ref, watch } from "vue";
|
|
4
|
+
import { useIcons } from "../composables/use-icons";
|
|
5
|
+
import { useMessages } from "../composables/use-messages";
|
|
4
6
|
import { tabsTheme } from "../theme/tabs";
|
|
5
7
|
import { resolveRegisteredColorRole } from "../utils/registered-colors";
|
|
6
8
|
import { resolveSlot, useComponentTheme, useRootProps } from "../utils/ui";
|
|
9
|
+
import Button from "./Button.vue";
|
|
7
10
|
import Icon from "./Icon.vue";
|
|
8
11
|
defineOptions({ inheritAttrs: false });
|
|
9
12
|
const props = defineProps({
|
|
@@ -11,19 +14,131 @@ const props = defineProps({
|
|
|
11
14
|
variant: { type: null, required: false },
|
|
12
15
|
defaultValue: { type: String, required: false },
|
|
13
16
|
modelValue: { type: String, required: false },
|
|
17
|
+
orientation: { type: String, required: false, default: "horizontal" },
|
|
18
|
+
unmountOnHide: { type: Boolean, required: false, default: true },
|
|
19
|
+
scrollable: { type: Boolean, required: false },
|
|
14
20
|
color: { type: null, required: false },
|
|
15
21
|
ui: { type: Object, required: false }
|
|
16
22
|
});
|
|
17
|
-
defineEmits(["update:modelValue"]);
|
|
23
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
18
24
|
const theme = useComponentTheme("tabs", tabsTheme);
|
|
25
|
+
const icons = useIcons();
|
|
26
|
+
const messages = useMessages();
|
|
19
27
|
const effectiveColor = computed(() => resolveRegisteredColorRole(props.color ?? "primary", "primary"));
|
|
20
|
-
const
|
|
28
|
+
const horizontalScrollable = computed(() => props.scrollable && props.orientation === "horizontal");
|
|
29
|
+
const ui = computed(() => theme.value({ variant: props.variant, color: effectiveColor.value, orientation: props.orientation, scrollable: horizontalScrollable.value }));
|
|
21
30
|
const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root);
|
|
31
|
+
const scrollRootProps = computed(() => resolveSlot(ui.value.scrollRoot, props.ui?.scrollRoot));
|
|
32
|
+
const scrollViewportProps = computed(() => resolveSlot(ui.value.scrollViewport, props.ui?.scrollViewport));
|
|
33
|
+
const scrollButtonProps = computed(() => resolveSlot(ui.value.scrollButton, props.ui?.scrollButton));
|
|
34
|
+
const scrollIconProps = computed(() => resolveSlot(ui.value.scrollIcon, props.ui?.scrollIcon));
|
|
22
35
|
const listProps = computed(() => resolveSlot(ui.value.list, props.ui?.list));
|
|
23
36
|
const triggerProps = computed(() => resolveSlot(ui.value.trigger, props.ui?.trigger));
|
|
24
37
|
const iconProps = computed(() => resolveSlot(ui.value.icon, props.ui?.icon));
|
|
25
38
|
const indicatorProps = computed(() => resolveSlot(ui.value.indicator, props.ui?.indicator));
|
|
26
39
|
const contentProps = computed(() => resolveSlot(ui.value.content, props.ui?.content));
|
|
40
|
+
const scrollRootEl = ref();
|
|
41
|
+
const scrollViewportEl = ref();
|
|
42
|
+
const hasOverflow = ref(false);
|
|
43
|
+
const canScrollLeft = ref(false);
|
|
44
|
+
const canScrollRight = ref(false);
|
|
45
|
+
const leftButtonProps = computed(() => ({
|
|
46
|
+
"type": "button",
|
|
47
|
+
"disabled": !canScrollLeft.value,
|
|
48
|
+
"aria-label": messages.value.scrollTabsLeft,
|
|
49
|
+
"onClick": scrollLeft
|
|
50
|
+
}));
|
|
51
|
+
const rightButtonProps = computed(() => ({
|
|
52
|
+
"type": "button",
|
|
53
|
+
"disabled": !canScrollRight.value,
|
|
54
|
+
"aria-label": messages.value.scrollTabsRight,
|
|
55
|
+
"onClick": scrollRight
|
|
56
|
+
}));
|
|
57
|
+
let resizeObserver;
|
|
58
|
+
function tabList() {
|
|
59
|
+
return scrollViewportEl.value?.querySelector('[role="tablist"]');
|
|
60
|
+
}
|
|
61
|
+
function updateScrollState() {
|
|
62
|
+
const root = scrollRootEl.value;
|
|
63
|
+
const viewport = scrollViewportEl.value;
|
|
64
|
+
const list = tabList();
|
|
65
|
+
if (!horizontalScrollable.value || !root || !viewport || !list) {
|
|
66
|
+
hasOverflow.value = false;
|
|
67
|
+
canScrollLeft.value = false;
|
|
68
|
+
canScrollRight.value = false;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
hasOverflow.value = list.scrollWidth > root.clientWidth + 1;
|
|
72
|
+
const listRect = list.getBoundingClientRect();
|
|
73
|
+
const viewportRect = viewport.getBoundingClientRect();
|
|
74
|
+
canScrollLeft.value = hasOverflow.value && listRect.left < viewportRect.left - 1;
|
|
75
|
+
canScrollRight.value = hasOverflow.value && listRect.right > viewportRect.right + 1;
|
|
76
|
+
}
|
|
77
|
+
function scrollBehavior() {
|
|
78
|
+
return window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ? "auto" : "smooth";
|
|
79
|
+
}
|
|
80
|
+
function scrollTabs(direction) {
|
|
81
|
+
const viewport = scrollViewportEl.value;
|
|
82
|
+
if (!viewport)
|
|
83
|
+
return;
|
|
84
|
+
viewport.scrollBy({ left: direction * viewport.clientWidth * 0.8, behavior: scrollBehavior() });
|
|
85
|
+
}
|
|
86
|
+
function scrollLeft() {
|
|
87
|
+
scrollTabs(-1);
|
|
88
|
+
}
|
|
89
|
+
function scrollRight() {
|
|
90
|
+
scrollTabs(1);
|
|
91
|
+
}
|
|
92
|
+
function scrollActiveIntoView(behavior = "auto") {
|
|
93
|
+
if (!horizontalScrollable.value)
|
|
94
|
+
return;
|
|
95
|
+
const viewport = scrollViewportEl.value;
|
|
96
|
+
const active = tabList()?.querySelector('[role="tab"][data-state="active"]');
|
|
97
|
+
if (!viewport || !active)
|
|
98
|
+
return;
|
|
99
|
+
const viewportRect = viewport.getBoundingClientRect();
|
|
100
|
+
const activeRect = active.getBoundingClientRect();
|
|
101
|
+
const delta = activeRect.left < viewportRect.left ? activeRect.left - viewportRect.left : activeRect.right > viewportRect.right ? activeRect.right - viewportRect.right : 0;
|
|
102
|
+
if (delta)
|
|
103
|
+
viewport.scrollBy({ left: delta, behavior });
|
|
104
|
+
}
|
|
105
|
+
const instance = getCurrentInstance();
|
|
106
|
+
const isControlled = () => Object.hasOwn(instance?.vnode.props ?? {}, "modelValue") || Object.hasOwn(instance?.vnode.props ?? {}, "model-value");
|
|
107
|
+
async function onValueChange(value) {
|
|
108
|
+
emit("update:modelValue", value);
|
|
109
|
+
if (isControlled())
|
|
110
|
+
return;
|
|
111
|
+
await nextTick();
|
|
112
|
+
scrollActiveIntoView(scrollBehavior());
|
|
113
|
+
}
|
|
114
|
+
function onResize() {
|
|
115
|
+
updateScrollState();
|
|
116
|
+
scrollActiveIntoView();
|
|
117
|
+
}
|
|
118
|
+
onMounted(async () => {
|
|
119
|
+
await nextTick();
|
|
120
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
121
|
+
resizeObserver = new ResizeObserver(onResize);
|
|
122
|
+
for (const element of [scrollRootEl.value, scrollViewportEl.value, tabList()]) {
|
|
123
|
+
if (element)
|
|
124
|
+
resizeObserver.observe(element);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
window.addEventListener("resize", onResize);
|
|
128
|
+
onResize();
|
|
129
|
+
});
|
|
130
|
+
onUnmounted(() => {
|
|
131
|
+
resizeObserver?.disconnect();
|
|
132
|
+
window.removeEventListener("resize", onResize);
|
|
133
|
+
});
|
|
134
|
+
watch(() => props.modelValue, async () => {
|
|
135
|
+
await nextTick();
|
|
136
|
+
scrollActiveIntoView(scrollBehavior());
|
|
137
|
+
});
|
|
138
|
+
watch(horizontalScrollable, async () => {
|
|
139
|
+
await nextTick();
|
|
140
|
+
onResize();
|
|
141
|
+
});
|
|
27
142
|
function itemValue(item, index) {
|
|
28
143
|
return item.value ?? String(index);
|
|
29
144
|
}
|
|
@@ -33,25 +148,63 @@ function itemValue(item, index) {
|
|
|
33
148
|
<TabsRoot
|
|
34
149
|
:default-value="defaultValue"
|
|
35
150
|
:model-value="modelValue"
|
|
151
|
+
:orientation="orientation"
|
|
152
|
+
:unmount-on-hide="unmountOnHide"
|
|
36
153
|
:data-selaras-color="effectiveColor"
|
|
37
154
|
v-bind="rootProps"
|
|
38
|
-
@update:model-value="(value) =>
|
|
155
|
+
@update:model-value="(value) => onValueChange(value)"
|
|
39
156
|
>
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
v-
|
|
43
|
-
|
|
44
|
-
:
|
|
45
|
-
:
|
|
46
|
-
|
|
157
|
+
<div ref="scrollRootEl" v-bind="scrollRootProps">
|
|
158
|
+
<slot
|
|
159
|
+
v-if="horizontalScrollable && hasOverflow"
|
|
160
|
+
name="scroll-left"
|
|
161
|
+
:button-props="leftButtonProps"
|
|
162
|
+
:scroll="scrollLeft"
|
|
163
|
+
:disabled="leftButtonProps.disabled"
|
|
164
|
+
:aria-label="leftButtonProps['aria-label']"
|
|
165
|
+
>
|
|
166
|
+
<Button variant="ghost" color="neutral" size="sm" square :ui="{ base: scrollButtonProps }" v-bind="leftButtonProps">
|
|
167
|
+
<template #icon="{ class: iconClass }">
|
|
168
|
+
<slot name="scroll-left-icon" :class="[iconClass, scrollIconProps.class]">
|
|
169
|
+
<Icon :name="icons.chevronLeft" aria-hidden="true" v-bind="scrollIconProps" :class="iconClass" />
|
|
170
|
+
</slot>
|
|
171
|
+
</template>
|
|
172
|
+
</Button>
|
|
173
|
+
</slot>
|
|
174
|
+
<div ref="scrollViewportEl" v-bind="scrollViewportProps" @scroll="updateScrollState">
|
|
175
|
+
<TabsList v-bind="listProps">
|
|
176
|
+
<TabsTrigger
|
|
177
|
+
v-for="(item, index) in items"
|
|
178
|
+
:key="itemValue(item, index)"
|
|
179
|
+
:value="itemValue(item, index)"
|
|
180
|
+
:disabled="item.disabled"
|
|
181
|
+
v-bind="triggerProps"
|
|
182
|
+
>
|
|
183
|
+
<Icon v-if="item.icon" :name="item.icon" v-bind="iconProps" />
|
|
184
|
+
<slot name="label" :item="item" :index="index">
|
|
185
|
+
{{ item.label }}
|
|
186
|
+
</slot>
|
|
187
|
+
</TabsTrigger>
|
|
188
|
+
<TabsIndicator aria-hidden="true" v-bind="indicatorProps" />
|
|
189
|
+
</TabsList>
|
|
190
|
+
</div>
|
|
191
|
+
<slot
|
|
192
|
+
v-if="horizontalScrollable && hasOverflow"
|
|
193
|
+
name="scroll-right"
|
|
194
|
+
:button-props="rightButtonProps"
|
|
195
|
+
:scroll="scrollRight"
|
|
196
|
+
:disabled="rightButtonProps.disabled"
|
|
197
|
+
:aria-label="rightButtonProps['aria-label']"
|
|
47
198
|
>
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
199
|
+
<Button variant="ghost" color="neutral" size="sm" square :ui="{ base: scrollButtonProps }" v-bind="rightButtonProps">
|
|
200
|
+
<template #icon="{ class: iconClass }">
|
|
201
|
+
<slot name="scroll-right-icon" :class="[iconClass, scrollIconProps.class]">
|
|
202
|
+
<Icon :name="icons.chevronRight" aria-hidden="true" v-bind="scrollIconProps" :class="iconClass" />
|
|
203
|
+
</slot>
|
|
204
|
+
</template>
|
|
205
|
+
</Button>
|
|
206
|
+
</slot>
|
|
207
|
+
</div>
|
|
55
208
|
<TabsContent
|
|
56
209
|
v-for="(item, index) in items"
|
|
57
210
|
:key="itemValue(item, index)"
|
|
@@ -15,6 +15,12 @@ export interface TabsProps {
|
|
|
15
15
|
variant?: TabsVariants['variant'];
|
|
16
16
|
defaultValue?: string;
|
|
17
17
|
modelValue?: string;
|
|
18
|
+
/** Direction of the tab list and its keyboard navigation. @default 'horizontal' */
|
|
19
|
+
orientation?: 'horizontal' | 'vertical';
|
|
20
|
+
/** Unmount inactive panels. Set to false to preserve their content and local state. @default true */
|
|
21
|
+
unmountOnHide?: boolean;
|
|
22
|
+
/** Enables horizontal scrolling and overflow controls when the tabs do not fit. */
|
|
23
|
+
scrollable?: boolean;
|
|
18
24
|
/** The active tab's text and underline accent. @default 'primary' */
|
|
19
25
|
color?: ColorRole;
|
|
20
26
|
ui?: UiProp<TabsThemeSlots>;
|
|
@@ -22,14 +28,48 @@ export interface TabsProps {
|
|
|
22
28
|
export interface TabsEmits {
|
|
23
29
|
'update:modelValue': [value: string];
|
|
24
30
|
}
|
|
25
|
-
declare
|
|
31
|
+
declare function scrollLeft(): void;
|
|
32
|
+
declare function scrollRight(): void;
|
|
33
|
+
declare var __VLS_10: {
|
|
34
|
+
buttonProps: {
|
|
35
|
+
type: "button";
|
|
36
|
+
disabled: boolean;
|
|
37
|
+
'aria-label': string;
|
|
38
|
+
onClick: typeof scrollLeft;
|
|
39
|
+
};
|
|
40
|
+
scroll: typeof scrollLeft;
|
|
41
|
+
disabled: boolean;
|
|
42
|
+
ariaLabel: string;
|
|
43
|
+
}, __VLS_19: {
|
|
44
|
+
class: unknown[];
|
|
45
|
+
}, __VLS_43: {
|
|
26
46
|
item: TabItem;
|
|
27
47
|
index: number;
|
|
28
|
-
},
|
|
48
|
+
}, __VLS_50: {
|
|
49
|
+
buttonProps: {
|
|
50
|
+
type: "button";
|
|
51
|
+
disabled: boolean;
|
|
52
|
+
'aria-label': string;
|
|
53
|
+
onClick: typeof scrollRight;
|
|
54
|
+
};
|
|
55
|
+
scroll: typeof scrollRight;
|
|
56
|
+
disabled: boolean;
|
|
57
|
+
ariaLabel: string;
|
|
58
|
+
}, __VLS_59: {
|
|
59
|
+
class: unknown[];
|
|
60
|
+
}, __VLS_73: string, __VLS_74: {};
|
|
29
61
|
type __VLS_Slots = {} & {
|
|
30
|
-
[K in NonNullable<typeof
|
|
62
|
+
[K in NonNullable<typeof __VLS_73>]?: (props: typeof __VLS_74) => any;
|
|
63
|
+
} & {
|
|
64
|
+
'scroll-left'?: (props: typeof __VLS_10) => any;
|
|
65
|
+
} & {
|
|
66
|
+
'scroll-left-icon'?: (props: typeof __VLS_19) => any;
|
|
67
|
+
} & {
|
|
68
|
+
label?: (props: typeof __VLS_43) => any;
|
|
69
|
+
} & {
|
|
70
|
+
'scroll-right'?: (props: typeof __VLS_50) => any;
|
|
31
71
|
} & {
|
|
32
|
-
|
|
72
|
+
'scroll-right-icon'?: (props: typeof __VLS_59) => any;
|
|
33
73
|
};
|
|
34
74
|
declare const __VLS_base: import("vue").DefineComponent<TabsProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
35
75
|
"update:modelValue": (value: string) => any;
|
|
@@ -29,6 +29,7 @@ const props = defineProps({
|
|
|
29
29
|
});
|
|
30
30
|
const emit = defineEmits(["update:modelValue"]);
|
|
31
31
|
const field = useFormField();
|
|
32
|
+
const effectivePlaceholder = computed(() => props.placeholder || (field?.floatingLabel.value ? " " : void 0));
|
|
32
33
|
const textareaId = computed(() => props.id ?? field?.id);
|
|
33
34
|
const textareaInvalid = computed(() => props.invalid || (field?.invalid.value ?? false));
|
|
34
35
|
const describedBy = computed(() => field?.describedBy.value);
|
|
@@ -78,7 +79,7 @@ watch(() => props.modelValue, resize);
|
|
|
78
79
|
</script>
|
|
79
80
|
|
|
80
81
|
<template>
|
|
81
|
-
<div :data-selaras-color="textareaInvalid ? 'danger' : effectiveColor" v-bind="rootProps">
|
|
82
|
+
<div :data-selaras-color="textareaInvalid ? 'danger' : effectiveColor" :data-selaras-field-leading="icon ? '' : void 0" v-bind="rootProps">
|
|
82
83
|
<Icon v-if="icon" :name="icon" v-bind="resolveSlot(ui.leadingIcon, props.ui?.leadingIcon)" />
|
|
83
84
|
<textarea
|
|
84
85
|
:id="textareaId"
|
|
@@ -86,7 +87,7 @@ watch(() => props.modelValue, resize);
|
|
|
86
87
|
data-ui-group-item
|
|
87
88
|
:value="modelValue"
|
|
88
89
|
:name="name ?? field?.name"
|
|
89
|
-
:placeholder="
|
|
90
|
+
:placeholder="effectivePlaceholder"
|
|
90
91
|
:rows="rows"
|
|
91
92
|
:disabled="disabled"
|
|
92
93
|
:aria-invalid="textareaInvalid || void 0"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TimelineItem, TimelineProps, TimelineSlots } from '../utils/timeline-contracts.js';
|
|
2
|
+
declare const __VLS_export: <Item extends TimelineItem>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<TimelineProps<Item>> & (typeof globalThis extends {
|
|
4
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
5
|
+
} ? P : {});
|
|
6
|
+
expose: (exposed: {}) => void;
|
|
7
|
+
attrs: any;
|
|
8
|
+
slots: TimelineSlots<Item>;
|
|
9
|
+
emit: {};
|
|
10
|
+
}>) => import("vue").VNode & {
|
|
11
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
12
|
+
};
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
15
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
16
|
+
[K in keyof T]: T[K];
|
|
17
|
+
} : {
|
|
18
|
+
[K in keyof T as K]: T[K];
|
|
19
|
+
}) & {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { timelineTheme } from "../theme/timeline";
|
|
4
|
+
import { resolveRegisteredColorRole } from "../utils/registered-colors";
|
|
5
|
+
import { applyClassPrefix, resolveSlot, useComponentTheme, useRootProps } from "../utils/ui";
|
|
6
|
+
import Icon from "./Icon.vue";
|
|
7
|
+
defineOptions({ inheritAttrs: false });
|
|
8
|
+
const props = defineProps({
|
|
9
|
+
items: { type: Array, required: true },
|
|
10
|
+
orientation: { type: String, required: false, default: "vertical" },
|
|
11
|
+
align: { type: String, required: false, default: "start" },
|
|
12
|
+
size: { type: String, required: false, default: "md" },
|
|
13
|
+
color: { type: null, required: false, default: "primary" },
|
|
14
|
+
ui: { type: Object, required: false }
|
|
15
|
+
});
|
|
16
|
+
defineSlots();
|
|
17
|
+
const theme = useComponentTheme("timeline", timelineTheme);
|
|
18
|
+
const effectiveColor = computed(() => resolveRegisteredColorRole(props.color, "primary"));
|
|
19
|
+
const ui = computed(() => theme.value({
|
|
20
|
+
orientation: props.orientation,
|
|
21
|
+
align: props.align,
|
|
22
|
+
size: props.size,
|
|
23
|
+
color: effectiveColor.value
|
|
24
|
+
}));
|
|
25
|
+
const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root);
|
|
26
|
+
const isHorizontal = computed(() => props.orientation === "horizontal");
|
|
27
|
+
const slotProps = (item, index) => ({ item, index });
|
|
28
|
+
function itemUi(item, slot) {
|
|
29
|
+
return resolveSlot(ui.value[slot], item.ui?.[slot] ?? props.ui?.[slot]);
|
|
30
|
+
}
|
|
31
|
+
function contentPlacement(index) {
|
|
32
|
+
if (props.align !== "alternate")
|
|
33
|
+
return void 0;
|
|
34
|
+
if (isHorizontal.value)
|
|
35
|
+
return applyClassPrefix(index % 2 === 0 ? "row-start-1" : "row-start-3");
|
|
36
|
+
return applyClassPrefix(index % 2 === 0 ? "col-start-3 text-start" : "col-start-1 text-end");
|
|
37
|
+
}
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<ol
|
|
42
|
+
:data-selaras-color="effectiveColor"
|
|
43
|
+
:data-orientation="orientation"
|
|
44
|
+
:data-align="align"
|
|
45
|
+
v-bind="rootProps"
|
|
46
|
+
>
|
|
47
|
+
<li
|
|
48
|
+
v-for="(item, index) in items"
|
|
49
|
+
:key="item.id ?? index"
|
|
50
|
+
:data-selaras-color="resolveRegisteredColorRole(item.color ?? color, 'primary')"
|
|
51
|
+
:class="item.class"
|
|
52
|
+
v-bind="itemUi(item, 'item')"
|
|
53
|
+
>
|
|
54
|
+
<div v-bind="itemUi(item, 'track')">
|
|
55
|
+
<span
|
|
56
|
+
aria-hidden="true"
|
|
57
|
+
:data-marker-type="item.icon ? 'icon' : $slots.marker ? 'custom' : 'dot'"
|
|
58
|
+
v-bind="itemUi(item, 'marker')"
|
|
59
|
+
>
|
|
60
|
+
<slot name="marker" v-bind="slotProps(item, index)">
|
|
61
|
+
<Icon v-if="item.icon" :name="item.icon" v-bind="itemUi(item, 'icon')" />
|
|
62
|
+
</slot>
|
|
63
|
+
</span>
|
|
64
|
+
<template v-if="index < items.length - 1">
|
|
65
|
+
<span aria-hidden="true" v-bind="itemUi(item, 'connector')">
|
|
66
|
+
<slot name="connector" v-bind="slotProps(item, index)" />
|
|
67
|
+
</span>
|
|
68
|
+
</template>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div v-if="item.slot && $slots[item.slot]" :class="contentPlacement(index)" v-bind="itemUi(item, 'content')">
|
|
72
|
+
<slot :name="item.slot" v-bind="slotProps(item, index)" />
|
|
73
|
+
</div>
|
|
74
|
+
<slot v-else-if="$slots.item" name="item" v-bind="slotProps(item, index)" />
|
|
75
|
+
<div v-else :class="contentPlacement(index)" v-bind="itemUi(item, 'content')">
|
|
76
|
+
<slot name="content" v-bind="slotProps(item, index)">
|
|
77
|
+
<div v-if="item.date || $slots.date" v-bind="itemUi(item, 'date')">
|
|
78
|
+
<slot name="date" v-bind="slotProps(item, index)">
|
|
79
|
+
<time v-if="item.datetime" :datetime="item.datetime">{{ item.date }}</time>
|
|
80
|
+
<template v-else>
|
|
81
|
+
{{ item.date }}
|
|
82
|
+
</template>
|
|
83
|
+
</slot>
|
|
84
|
+
</div>
|
|
85
|
+
<div v-if="item.title || $slots.title" v-bind="itemUi(item, 'title')">
|
|
86
|
+
<slot name="title" v-bind="slotProps(item, index)">
|
|
87
|
+
{{ item.title }}
|
|
88
|
+
</slot>
|
|
89
|
+
</div>
|
|
90
|
+
<div v-if="item.description || $slots.description" v-bind="itemUi(item, 'description')">
|
|
91
|
+
<slot name="description" v-bind="slotProps(item, index)">
|
|
92
|
+
{{ item.description }}
|
|
93
|
+
</slot>
|
|
94
|
+
</div>
|
|
95
|
+
</slot>
|
|
96
|
+
</div>
|
|
97
|
+
</li>
|
|
98
|
+
</ol>
|
|
99
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TimelineItem, TimelineProps, TimelineSlots } from '../utils/timeline-contracts.js';
|
|
2
|
+
declare const __VLS_export: <Item extends TimelineItem>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<TimelineProps<Item>> & (typeof globalThis extends {
|
|
4
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
5
|
+
} ? P : {});
|
|
6
|
+
expose: (exposed: {}) => void;
|
|
7
|
+
attrs: any;
|
|
8
|
+
slots: TimelineSlots<Item>;
|
|
9
|
+
emit: {};
|
|
10
|
+
}>) => import("vue").VNode & {
|
|
11
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
12
|
+
};
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
15
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
16
|
+
[K in keyof T]: T[K];
|
|
17
|
+
} : {
|
|
18
|
+
[K in keyof T as K]: T[K];
|
|
19
|
+
}) & {};
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import type { ToastThemeSlots } from '../theme/toast.js';
|
|
1
|
+
import type { ToastPosition as ToastPositionType, ToastThemeSlots } from '../theme/toast.js';
|
|
2
2
|
import type { UiProp } from '../utils/ui.js';
|
|
3
|
+
export type ToastPosition = ToastPositionType;
|
|
3
4
|
export interface ToastProps {
|
|
5
|
+
/** Where the toast viewport is pinned. @default 'bottom-right' */
|
|
6
|
+
position?: ToastPositionType;
|
|
7
|
+
/** Shows every toast separately instead of layering them into a stack that fans open on hover. @default false */
|
|
8
|
+
expand?: boolean;
|
|
9
|
+
/** Default visibility time in milliseconds; a toast's own duration takes precedence. @default 5000 */
|
|
10
|
+
duration?: number;
|
|
11
|
+
/** Maximum number of toasts shown at once; older toasts are removed when the limit is reached. @default 5 */
|
|
12
|
+
max?: number;
|
|
4
13
|
ui?: UiProp<ToastThemeSlots>;
|
|
5
14
|
}
|
|
6
15
|
declare const __VLS_export: import("vue").DefineComponent<ToastProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ToastProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -1,30 +1,116 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ToastPortal, ToastViewport } from "reka-ui";
|
|
3
|
-
import { computed, onUnmounted } from "vue";
|
|
3
|
+
import { computed, onUnmounted, ref, watch } from "vue";
|
|
4
4
|
import { useToastService } from "../internal/programmatic-services";
|
|
5
5
|
import ProgrammaticTheme from "../internal/ProgrammaticTheme.vue";
|
|
6
|
+
import { FALLBACK_TOAST_HEIGHT, layoutToastStack, placeToast, TOAST_EXIT_DURATION } from "../internal/toast-stack";
|
|
6
7
|
import ToastItemRenderer from "../internal/ToastItemRenderer.vue";
|
|
7
8
|
import { toastTheme } from "../theme/toast";
|
|
8
9
|
import { resolveSlot, useComponentTheme } from "../utils/ui";
|
|
9
10
|
const props = defineProps({
|
|
11
|
+
position: { type: null, required: false },
|
|
12
|
+
expand: { type: Boolean, required: false, default: false },
|
|
13
|
+
duration: { type: Number, required: false, default: 5e3 },
|
|
14
|
+
max: { type: Number, required: false, default: 5 },
|
|
10
15
|
ui: { type: Object, required: false }
|
|
11
16
|
});
|
|
12
|
-
const { toasts, remove, dispose } = useToastService();
|
|
17
|
+
const { toasts, remove, setMax, dispose } = useToastService();
|
|
13
18
|
const theme = useComponentTheme("toast", toastTheme);
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
19
|
+
const position = computed(() => props.position ?? "bottom-right");
|
|
20
|
+
const lift = computed(() => position.value.startsWith("top") ? 1 : -1);
|
|
21
|
+
const hovered = ref(false);
|
|
22
|
+
const focused = ref(false);
|
|
23
|
+
const expanded = computed(() => props.expand || hovered.value || focused.value);
|
|
24
|
+
const ui = computed(() => theme.value({ position: position.value, expand: expanded.value }));
|
|
25
|
+
const rendered = ref([]);
|
|
26
|
+
const toastHeights = ref(/* @__PURE__ */ new Map());
|
|
27
|
+
const leaving = ref(/* @__PURE__ */ new Set());
|
|
28
|
+
const exitTimers = /* @__PURE__ */ new Map();
|
|
29
|
+
watch(() => [...toasts.value], (queued) => {
|
|
30
|
+
const queuedIds = new Set(queued.map((toast) => toast.id));
|
|
31
|
+
const departing = rendered.value.filter((toast) => !queuedIds.has(toast.id));
|
|
32
|
+
if (departing.length) {
|
|
33
|
+
const next = new Set(leaving.value);
|
|
34
|
+
for (const toast of departing) {
|
|
35
|
+
next.add(toast.id);
|
|
36
|
+
if (!exitTimers.has(toast.id))
|
|
37
|
+
exitTimers.set(toast.id, setTimeout(finishExit, TOAST_EXIT_DURATION, toast.id));
|
|
38
|
+
}
|
|
39
|
+
leaving.value = next;
|
|
40
|
+
}
|
|
41
|
+
rendered.value = [...queued, ...departing].sort((a, b) => a.id - b.id);
|
|
42
|
+
}, { immediate: true });
|
|
43
|
+
function finishExit(id) {
|
|
44
|
+
exitTimers.delete(id);
|
|
45
|
+
rendered.value = rendered.value.filter((toast) => toast.id !== id);
|
|
46
|
+
const next = new Set(leaving.value);
|
|
47
|
+
next.delete(id);
|
|
48
|
+
leaving.value = next;
|
|
49
|
+
const heights = new Map(toastHeights.value);
|
|
50
|
+
heights.delete(id);
|
|
51
|
+
toastHeights.value = heights;
|
|
52
|
+
}
|
|
53
|
+
const stacked = computed(() => rendered.value.filter((toast) => !leaving.value.has(toast.id)).reverse());
|
|
54
|
+
const layout = computed(() => layoutToastStack(
|
|
55
|
+
stacked.value.map((toast) => toastHeights.value.get(toast.id) ?? FALLBACK_TOAST_HEIGHT),
|
|
56
|
+
expanded.value
|
|
57
|
+
));
|
|
58
|
+
const placements = computed(() => {
|
|
59
|
+
const byId = /* @__PURE__ */ new Map();
|
|
60
|
+
stacked.value.forEach((toast, index) => byId.set(toast.id, placeToast(index, layout.value, expanded.value, lift.value)));
|
|
61
|
+
return byId;
|
|
62
|
+
});
|
|
63
|
+
const viewportProps = computed(() => {
|
|
64
|
+
const resolved = resolveSlot(ui.value.viewport, props.ui?.viewport);
|
|
65
|
+
const height = layout.value.height ? { height: `calc(${layout.value.height}px + 2rem)` } : void 0;
|
|
66
|
+
return { ...resolved, style: [height, resolved.style] };
|
|
67
|
+
});
|
|
68
|
+
function updateToastHeight(id, height) {
|
|
69
|
+
if (!Number.isFinite(height) || height <= 0 || toastHeights.value.get(id) === height)
|
|
70
|
+
return;
|
|
71
|
+
const next = new Map(toastHeights.value);
|
|
72
|
+
next.set(id, height);
|
|
73
|
+
toastHeights.value = next;
|
|
74
|
+
}
|
|
75
|
+
function onFocusOut(event) {
|
|
76
|
+
const viewport = event.currentTarget;
|
|
77
|
+
if (!viewport?.contains(event.relatedTarget))
|
|
78
|
+
focused.value = false;
|
|
79
|
+
}
|
|
80
|
+
watch(() => props.max, setMax, { immediate: true });
|
|
81
|
+
onUnmounted(() => {
|
|
82
|
+
for (const timer of exitTimers.values())
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
exitTimers.clear();
|
|
85
|
+
dispose();
|
|
86
|
+
});
|
|
17
87
|
</script>
|
|
18
88
|
|
|
19
89
|
<template>
|
|
20
90
|
<ProgrammaticTheme
|
|
21
|
-
v-for="toast in
|
|
91
|
+
v-for="toast in rendered"
|
|
22
92
|
:key="toast.id"
|
|
23
93
|
:snapshot="toast._theme"
|
|
24
94
|
>
|
|
25
|
-
<ToastItemRenderer
|
|
95
|
+
<ToastItemRenderer
|
|
96
|
+
:toast="toast"
|
|
97
|
+
:ui="props.ui"
|
|
98
|
+
:duration="props.duration"
|
|
99
|
+
:expand="expanded"
|
|
100
|
+
:position="position"
|
|
101
|
+
:placement="placements.get(toast.id)"
|
|
102
|
+
:leaving="leaving.has(toast.id)"
|
|
103
|
+
@remove="remove"
|
|
104
|
+
@resize="updateToastHeight(toast.id, $event)"
|
|
105
|
+
/>
|
|
26
106
|
</ProgrammaticTheme>
|
|
27
107
|
<ToastPortal>
|
|
28
|
-
<ToastViewport
|
|
108
|
+
<ToastViewport
|
|
109
|
+
v-bind="viewportProps"
|
|
110
|
+
@mouseenter="hovered = true"
|
|
111
|
+
@mouseleave="hovered = false"
|
|
112
|
+
@focusin="focused = true"
|
|
113
|
+
@focusout="onFocusOut"
|
|
114
|
+
/>
|
|
29
115
|
</ToastPortal>
|
|
30
116
|
</template>
|