@sewadah/selaras 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -7
- package/dist/module.json +1 -1
- package/dist/module.mjs +31 -15
- package/dist/runtime/components/Autocomplete.vue +3 -1
- package/dist/runtime/components/Badge.d.vue.ts +1 -1
- package/dist/runtime/components/Badge.vue.d.ts +1 -1
- 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/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/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/ContentToc.vue +14 -7
- package/dist/runtime/components/ContextMenu.d.vue.ts +8 -1
- package/dist/runtime/components/ContextMenu.vue +16 -4
- 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/Dropdown.d.vue.ts +33 -6
- package/dist/runtime/components/Dropdown.vue +143 -11
- 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/FileUpload.vue +1 -1
- 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/NavigationMenu.d.vue.ts +10 -10
- package/dist/runtime/components/NavigationMenu.vue +13 -7
- 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/PinInput.vue +1 -1
- package/dist/runtime/components/Popover.d.vue.ts +8 -2
- package/dist/runtime/components/Popover.vue +9 -4
- package/dist/runtime/components/Popover.vue.d.ts +8 -2
- package/dist/runtime/components/ProsePre.d.vue.ts +6 -2
- package/dist/runtime/components/ProsePre.vue +11 -4
- package/dist/runtime/components/ProsePre.vue.d.ts +6 -2
- package/dist/runtime/components/Select.vue +3 -1
- 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 +9 -4
- package/dist/runtime/components/Tooltip.vue.d.ts +8 -2
- 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/default-colors.css +1 -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 +29 -25
- package/dist/runtime/internal/ComboboxSelectBase.vue +104 -20
- package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +29 -25
- package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +16 -11
- package/dist/runtime/internal/ComboboxSelectBody.vue +49 -10
- package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +16 -11
- 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.js +2 -1
- package/dist/runtime/internal/programmatic-services.d.ts +2 -0
- package/dist/runtime/internal/programmatic-services.js +8 -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/avatar.js +7 -7
- package/dist/runtime/theme/badge.js +2 -2
- 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/checkbox.js +1 -1
- 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/date-picker.js +1 -1
- 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/file-upload.js +2 -2
- package/dist/runtime/theme/form-field.d.ts +18 -0
- package/dist/runtime/theme/form-field.js +6 -1
- package/dist/runtime/theme/icon.js +1 -1
- package/dist/runtime/theme/input-number.js +1 -1
- package/dist/runtime/theme/input.js +1 -1
- package/dist/runtime/theme/modal.js +1 -1
- package/dist/runtime/theme/pin-input.js +1 -1
- package/dist/runtime/theme/progress.js +2 -2
- package/dist/runtime/theme/prose.d.ts +6 -0
- package/dist/runtime/theme/prose.js +2 -0
- package/dist/runtime/theme/radio-group.js +4 -4
- package/dist/runtime/theme/rating.js +1 -1
- package/dist/runtime/theme/select.js +2 -2
- package/dist/runtime/theme/separator.js +1 -1
- package/dist/runtime/theme/slideover.js +1 -1
- package/dist/runtime/theme/slider.js +2 -2
- package/dist/runtime/theme/splitter-resize-handle.js +1 -1
- package/dist/runtime/theme/switch.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/textarea.js +1 -1
- 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 +12 -1
- package/dist/runtime/types/index.d.ts +6 -1
- package/dist/runtime/utils/arrow.d.ts +21 -0
- package/dist/runtime/utils/arrow.js +10 -0
- package/dist/runtime/utils/color-registry.d.ts +3 -1
- package/dist/runtime/utils/color-registry.js +4 -0
- package/dist/runtime/utils/file-icons.d.ts +8 -0
- package/dist/runtime/utils/file-icons.js +133 -0
- package/dist/runtime/utils/functional-tokens.d.ts +1 -1
- package/dist/runtime/utils/functional-tokens.js +1 -1
- package/dist/runtime/utils/icons.d.ts +2 -0
- package/dist/runtime/utils/icons.js +2 -0
- package/dist/runtime/utils/messages.d.ts +6 -0
- package/dist/runtime/utils/messages.js +6 -0
- package/dist/runtime/utils/navigation-menu.d.ts +10 -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/seed-colors.d.ts +2 -1
- package/dist/runtime/utils/seed-colors.js +24 -16
- package/dist/runtime/utils/select-contracts.d.ts +7 -1
- 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/tokens/default-colors.tokens.json +100 -100
- package/package.json +21 -9
|
@@ -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>
|
|
@@ -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,13 +1,19 @@
|
|
|
1
1
|
import type { TooltipThemeSlots } from '../theme/tooltip.js';
|
|
2
|
+
import type { ArrowConfig } from '../utils/arrow.js';
|
|
3
|
+
import type { OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
|
|
2
4
|
import type { UiProp } from '../utils/ui.js';
|
|
3
5
|
export interface TooltipProps {
|
|
4
6
|
text?: string;
|
|
5
7
|
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
8
|
+
/** Anchored tooltip placement. Overrides `side` when supplied. */
|
|
9
|
+
positioning?: OverlayPositioning;
|
|
10
|
+
/** Teleport target, or `false` to render inline. Defaults to `body`. */
|
|
11
|
+
portal?: OverlayPortal;
|
|
6
12
|
delayDuration?: number;
|
|
7
13
|
/** Keyboard shortcut hint shown alongside the text, e.g. `['⌘', 'K']` - each entry renders as its own small key badge. Takes the literal display strings, not semantic key names - there's no platform-specific symbol mapping. */
|
|
8
14
|
kbds?: string[];
|
|
9
|
-
/** Set `false` to hide the
|
|
10
|
-
arrow?: boolean;
|
|
15
|
+
/** Set `false` to hide the pointer; an object configures its size and edge clearance. */
|
|
16
|
+
arrow?: boolean | ArrowConfig;
|
|
11
17
|
disabled?: boolean;
|
|
12
18
|
ui?: UiProp<TooltipThemeSlots>;
|
|
13
19
|
}
|
|
@@ -2,21 +2,26 @@
|
|
|
2
2
|
import { TooltipArrow, TooltipContent, TooltipPortal, TooltipRoot, TooltipTrigger } from "reka-ui";
|
|
3
3
|
import { computed } from "vue";
|
|
4
4
|
import { tooltipTheme } from "../theme/tooltip";
|
|
5
|
+
import { arrowContentProps, arrowElementProps } from "../utils/arrow";
|
|
6
|
+
import { overlayPortalProps } from "../utils/overlay";
|
|
5
7
|
import { resolveSlot, useComponentTheme, useThemeBindings } from "../utils/ui";
|
|
6
8
|
const props = defineProps({
|
|
7
9
|
text: { type: String, required: false },
|
|
8
10
|
side: { type: String, required: false, default: "top" },
|
|
11
|
+
positioning: { type: Object, required: false },
|
|
12
|
+
portal: { type: [Boolean, String], required: false, skipCheck: true, default: void 0 },
|
|
9
13
|
delayDuration: { type: Number, required: false, default: 200 },
|
|
10
14
|
kbds: { type: Array, required: false },
|
|
11
|
-
arrow: { type: Boolean, required: false, default: true },
|
|
15
|
+
arrow: { type: [Boolean, Object], required: false, default: true },
|
|
12
16
|
disabled: { type: Boolean, required: false },
|
|
13
17
|
ui: { type: Object, required: false }
|
|
14
18
|
});
|
|
15
19
|
const theme = useComponentTheme("tooltip", tooltipTheme);
|
|
16
20
|
const themeBindings = useThemeBindings();
|
|
17
21
|
const ui = computed(() => theme.value());
|
|
18
|
-
const contentProps = computed(() => resolveSlot(ui.value.content, props.ui?.content));
|
|
19
|
-
const
|
|
22
|
+
const contentProps = computed(() => ({ ...resolveSlot(ui.value.content, props.ui?.content), ...arrowContentProps(props.arrow), ...props.positioning }));
|
|
23
|
+
const portalProps = computed(() => overlayPortalProps(props.portal));
|
|
24
|
+
const arrowProps = computed(() => ({ ...resolveSlot(ui.value.arrow, props.ui?.arrow), ...arrowElementProps(props.arrow) }));
|
|
20
25
|
const kbdsProps = computed(() => resolveSlot(ui.value.kbds, props.ui?.kbds));
|
|
21
26
|
const kbdProps = computed(() => resolveSlot(ui.value.kbd, props.ui?.kbd));
|
|
22
27
|
</script>
|
|
@@ -26,7 +31,7 @@ const kbdProps = computed(() => resolveSlot(ui.value.kbd, props.ui?.kbd));
|
|
|
26
31
|
<TooltipTrigger as-child>
|
|
27
32
|
<slot />
|
|
28
33
|
</TooltipTrigger>
|
|
29
|
-
<TooltipPortal>
|
|
34
|
+
<TooltipPortal v-bind="portalProps">
|
|
30
35
|
<TooltipContent :side="side" :side-offset="6" :data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" v-bind="contentProps">
|
|
31
36
|
<slot name="content">
|
|
32
37
|
{{ text }}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import type { TooltipThemeSlots } from '../theme/tooltip.js';
|
|
2
|
+
import type { ArrowConfig } from '../utils/arrow.js';
|
|
3
|
+
import type { OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
|
|
2
4
|
import type { UiProp } from '../utils/ui.js';
|
|
3
5
|
export interface TooltipProps {
|
|
4
6
|
text?: string;
|
|
5
7
|
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
8
|
+
/** Anchored tooltip placement. Overrides `side` when supplied. */
|
|
9
|
+
positioning?: OverlayPositioning;
|
|
10
|
+
/** Teleport target, or `false` to render inline. Defaults to `body`. */
|
|
11
|
+
portal?: OverlayPortal;
|
|
6
12
|
delayDuration?: number;
|
|
7
13
|
/** Keyboard shortcut hint shown alongside the text, e.g. `['⌘', 'K']` - each entry renders as its own small key badge. Takes the literal display strings, not semantic key names - there's no platform-specific symbol mapping. */
|
|
8
14
|
kbds?: string[];
|
|
9
|
-
/** Set `false` to hide the
|
|
10
|
-
arrow?: boolean;
|
|
15
|
+
/** Set `false` to hide the pointer; an object configures its size and edge clearance. */
|
|
16
|
+
arrow?: boolean | ArrowConfig;
|
|
11
17
|
disabled?: boolean;
|
|
12
18
|
ui?: UiProp<TooltipThemeSlots>;
|
|
13
19
|
}
|
|
@@ -34,6 +34,7 @@ export type TableRowSelectionState = RowSelectionState;
|
|
|
34
34
|
export type TableExpandedState = ExpandedState;
|
|
35
35
|
export type TableColumnVisibilityState = ColumnVisibilityState;
|
|
36
36
|
export type TableGetRowId<TData extends RowData = RowData> = (row: TData, index: number) => string;
|
|
37
|
+
export type TableGetSubRows<TData extends RowData = RowData> = (row: TData, index: number) => readonly TData[] | undefined;
|
|
37
38
|
/** Creates TanStack v9 column definitions bound to STable's fixed feature set. */
|
|
38
39
|
export declare const createTableColumnHelper: <TData extends RowData>() => import("@tanstack/vue-table").AppColumnHelper<{
|
|
39
40
|
rowSortingFeature: import("@tanstack/vue-table").TableFeature;
|
|
@@ -68,6 +69,8 @@ interface UseTableProps<TData extends RowData> {
|
|
|
68
69
|
expanded?: TableExpandedState;
|
|
69
70
|
columnVisibility?: TableColumnVisibilityState;
|
|
70
71
|
getRowId?: TableGetRowId<TData>;
|
|
72
|
+
tree?: boolean;
|
|
73
|
+
getSubRows?: TableGetSubRows<TData>;
|
|
71
74
|
manualSorting?: boolean;
|
|
72
75
|
manualFiltering?: boolean;
|
|
73
76
|
manualPagination?: boolean;
|
|
@@ -89,12 +89,18 @@ export function useTable(props, emit, columns, columnPinning) {
|
|
|
89
89
|
const table = useAppTable({
|
|
90
90
|
data: computed(() => props.data),
|
|
91
91
|
columns,
|
|
92
|
+
// TanStack enables sorting by default for accessor columns. Selaras opts
|
|
93
|
+
// columns into sorting explicitly, matching the declarative SColumn API.
|
|
94
|
+
defaultColumn: { enableSorting: false },
|
|
92
95
|
enableRowSelection: computed(() => !!props.selectable),
|
|
93
96
|
getRowId: props.getRowId,
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
getRowCanExpand: () => true,
|
|
97
|
+
getSubRows: props.tree ? props.getSubRows ?? ((row) => row.subRows) : () => void 0,
|
|
98
|
+
// Tree rows expand when they have children; detail expansion remains
|
|
99
|
+
// available on every row when tree mode is off.
|
|
100
|
+
getRowCanExpand: (row) => props.tree ? !!row.subRows?.length : true,
|
|
101
|
+
// Paginate tree roots as a unit so expanding one root does not hide later
|
|
102
|
+
// roots simply because its children consume the page-size row count.
|
|
103
|
+
paginateExpandedRows: false,
|
|
98
104
|
// Opts out of the local sorted/filtered/paginated row models - state
|
|
99
105
|
// still lives here (sorting/globalFilter/pageIndex above, still
|
|
100
106
|
// emitting their own update:* events the normal way), only the row
|
|
@@ -5,9 +5,13 @@ export function useToast() {
|
|
|
5
5
|
const snapshotTheme = useProgrammaticThemeSnapshot();
|
|
6
6
|
function add(toast) {
|
|
7
7
|
if (import.meta.server)
|
|
8
|
-
|
|
8
|
+
return -1;
|
|
9
9
|
const id = service.nextId();
|
|
10
|
+
if (service.max.value <= 0)
|
|
11
|
+
return id;
|
|
10
12
|
service.toasts.value.push({ ...toast, id, _theme: snapshotTheme() });
|
|
13
|
+
if (service.toasts.value.length > service.max.value)
|
|
14
|
+
service.toasts.value.splice(0, service.toasts.value.length - service.max.value);
|
|
11
15
|
return id;
|
|
12
16
|
}
|
|
13
17
|
return { add, remove: service.remove };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@theme static{--color-selaras-
|
|
1
|
+
@theme static{--color-selaras-blue-50:oklch(0.975 0.018 245);--color-selaras-blue-100:oklch(0.945 0.04 245);--color-selaras-blue-200:oklch(0.895 0.075 245);--color-selaras-blue-300:oklch(0.82 0.115 245);--color-selaras-blue-400:oklch(0.715 0.155 245);--color-selaras-blue-500:oklch(0.62 0.175 245);--color-selaras-blue-600:oklch(0.535 0.165 245);--color-selaras-blue-700:oklch(0.455 0.145 245);--color-selaras-blue-800:oklch(0.375 0.115 245);--color-selaras-blue-900:oklch(0.3 0.085 245);--color-selaras-blue-950:oklch(0.215 0.055 245);--color-selaras-gray-25:oklch(0.995 0.0015 280);--color-selaras-gray-50:oklch(0.98 0.003 280);--color-selaras-gray-100:oklch(0.945 0.0065 280);--color-selaras-gray-200:oklch(0.9 0.01 280);--color-selaras-gray-300:oklch(0.84 0.014 280);--color-selaras-gray-400:oklch(0.7 0.018 280);--color-selaras-gray-500:oklch(0.555 0.02 280);--color-selaras-gray-600:oklch(0.445 0.019 280);--color-selaras-gray-700:oklch(0.365 0.017 280);--color-selaras-gray-800:oklch(0.275 0.014 280);--color-selaras-gray-900:oklch(0.205 0.011 280);--color-selaras-gray-950:oklch(0.145 0.008 280);--color-selaras-green-50:oklch(0.982 0.018 155.826);--color-selaras-green-100:oklch(0.962 0.044 156.743);--color-selaras-green-200:oklch(0.925 0.084 155.995);--color-selaras-green-300:oklch(0.871 0.15 154.449);--color-selaras-green-400:oklch(0.792 0.209 151.711);--color-selaras-green-500:oklch(0.723 0.219 149.579);--color-selaras-green-600:oklch(0.627 0.194 149.214);--color-selaras-green-700:oklch(0.527 0.154 150.069);--color-selaras-green-800:oklch(0.448 0.119 151.328);--color-selaras-green-900:oklch(0.393 0.095 152.535);--color-selaras-green-950:oklch(0.266 0.065 152.934);--color-selaras-indigo-50:oklch(0.97 0.012 279.99);--color-selaras-indigo-100:oklch(0.93 0.028 279.99);--color-selaras-indigo-200:oklch(0.86 0.055 279.99);--color-selaras-indigo-300:oklch(0.78 0.1 279.99);--color-selaras-indigo-400:oklch(0.64 0.165 279.99);--color-selaras-indigo-500:oklch(0.4755 0.2026 279.99);--color-selaras-indigo-600:oklch(0.415 0.185 279.99);--color-selaras-indigo-700:oklch(0.355 0.158 279.99);--color-selaras-indigo-800:oklch(0.295 0.128 279.99);--color-selaras-indigo-900:oklch(0.235 0.095 279.99);--color-selaras-indigo-950:oklch(0.17 0.06 279.99);--color-selaras-plum-50:oklch(0.975 0.012 325);--color-selaras-plum-100:oklch(0.945 0.03 325);--color-selaras-plum-200:oklch(0.89 0.06 325);--color-selaras-plum-300:oklch(0.81 0.1 325);--color-selaras-plum-400:oklch(0.69 0.145 325);--color-selaras-plum-500:oklch(0.565 0.175 325);--color-selaras-plum-600:oklch(0.495 0.165 325);--color-selaras-plum-700:oklch(0.42 0.145 325);--color-selaras-plum-800:oklch(0.345 0.115 325);--color-selaras-plum-900:oklch(0.275 0.085 325);--color-selaras-plum-950:oklch(0.19 0.055 325);--color-selaras-red-50:oklch(0.971 0.013 17.38);--color-selaras-red-100:oklch(0.936 0.032 17.717);--color-selaras-red-200:oklch(0.885 0.062 18.334);--color-selaras-red-300:oklch(0.808 0.114 19.571);--color-selaras-red-400:oklch(0.704 0.191 22.216);--color-selaras-red-500:oklch(0.637 0.237 25.331);--color-selaras-red-600:oklch(0.577 0.245 27.325);--color-selaras-red-700:oklch(0.505 0.213 27.518);--color-selaras-red-800:oklch(0.444 0.177 26.899);--color-selaras-red-900:oklch(0.396 0.141 25.723);--color-selaras-red-950:oklch(0.258 0.092 26.042);--color-selaras-yellow-50:oklch(0.987 0.026 102.212);--color-selaras-yellow-100:oklch(0.973 0.071 103.193);--color-selaras-yellow-200:oklch(0.945 0.129 101.54);--color-selaras-yellow-300:oklch(0.905 0.182 98.111);--color-selaras-yellow-400:oklch(0.852 0.199 91.936);--color-selaras-yellow-500:oklch(0.795 0.184 86.047);--color-selaras-yellow-600:oklch(0.681 0.162 75.834);--color-selaras-yellow-700:oklch(0.554 0.135 66.442);--color-selaras-yellow-800:oklch(0.476 0.114 61.907);--color-selaras-yellow-900:oklch(0.421 0.095 57.708);--color-selaras-yellow-950:oklch(0.286 0.066 53.813)}@layer theme{:root,[data-selaras-theme]{--_selaras-default-surface-canvas:#fff;--_selaras-default-surface-default:--theme(--color-selaras-gray-25);--_selaras-default-surface-elevated:--theme(--color-selaras-gray-50);--_selaras-default-surface-inverted:--theme(--color-selaras-gray-950);--_selaras-default-text-default:--theme(--color-selaras-gray-950);--_selaras-default-text-muted:--theme(--color-selaras-gray-600);--_selaras-default-text-inverted:--theme(--color-selaras-gray-50);--_selaras-default-border-default:--theme(--color-selaras-gray-200);--_selaras-default-border-muted:--theme(--color-selaras-gray-100);--_selaras-default-border-hover:--theme(--color-selaras-gray-300)}:root.dark,:root.dark [data-selaras-theme]:where(:not([data-selaras-mode]),[data-selaras-mode=root]),[data-selaras-theme]:where([data-selaras-mode=dark]){--_selaras-default-surface-canvas:#0c0c0d;--_selaras-default-surface-default:--theme(--color-selaras-gray-950);--_selaras-default-surface-elevated:--theme(--color-selaras-gray-900);--_selaras-default-surface-inverted:--theme(--color-selaras-gray-50);--_selaras-default-text-default:--theme(--color-selaras-gray-50);--_selaras-default-text-muted:--theme(--color-selaras-gray-400);--_selaras-default-text-inverted:--theme(--color-selaras-gray-950);--_selaras-default-border-default:--theme(--color-selaras-gray-800);--_selaras-default-border-muted:--theme(--color-selaras-gray-900);--_selaras-default-border-hover:--theme(--color-selaras-gray-700)}}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Directive } from 'vue';
|
|
2
|
+
export interface HotkeyOptions<T extends HTMLElement = HTMLElement> {
|
|
3
|
+
/** A key chord such as `mod+k`, `ctrl+shift+p`, or `escape`. */
|
|
4
|
+
keys: string;
|
|
5
|
+
/** Runs instead of clicking the bound element. */
|
|
6
|
+
handler?: (event: KeyboardEvent) => void;
|
|
7
|
+
/** Gate activation on current application state. */
|
|
8
|
+
when?: boolean | ((element: T, event: KeyboardEvent) => boolean);
|
|
9
|
+
/** Allow shortcuts while focus is in an editable control. Defaults to false. */
|
|
10
|
+
allowInEditable?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export type HotkeyValue<T extends HTMLElement = HTMLElement> = string | HotkeyOptions<T> | false | null | undefined;
|
|
13
|
+
export declare const vHotkey: Directive<HTMLElement, HotkeyValue>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const aliases = {
|
|
2
|
+
esc: "escape",
|
|
3
|
+
return: "enter",
|
|
4
|
+
spacebar: " ",
|
|
5
|
+
space: " ",
|
|
6
|
+
up: "arrowup",
|
|
7
|
+
down: "arrowdown",
|
|
8
|
+
left: "arrowleft",
|
|
9
|
+
right: "arrowright",
|
|
10
|
+
del: "delete",
|
|
11
|
+
ctrl: "control",
|
|
12
|
+
cmd: "meta",
|
|
13
|
+
command: "meta",
|
|
14
|
+
option: "alt"
|
|
15
|
+
};
|
|
16
|
+
function isMacPlatform() {
|
|
17
|
+
if (typeof navigator === "undefined")
|
|
18
|
+
return false;
|
|
19
|
+
const platform = navigator.platform ?? "";
|
|
20
|
+
return /mac|iphone|ipad|ipod/i.test(platform);
|
|
21
|
+
}
|
|
22
|
+
function canonicalKey(key) {
|
|
23
|
+
const normalized = key.trim().toLowerCase();
|
|
24
|
+
return aliases[normalized] ?? normalized;
|
|
25
|
+
}
|
|
26
|
+
function parseHotkey(keys) {
|
|
27
|
+
const parts = keys.split("+").map(canonicalKey).filter(Boolean);
|
|
28
|
+
const key = parts.find((part) => !["control", "meta", "alt", "shift", "mod"].includes(part));
|
|
29
|
+
if (!key)
|
|
30
|
+
return void 0;
|
|
31
|
+
const hasMod = parts.includes("mod");
|
|
32
|
+
const mac = isMacPlatform();
|
|
33
|
+
return {
|
|
34
|
+
key,
|
|
35
|
+
ctrl: parts.includes("control") || hasMod && !mac,
|
|
36
|
+
meta: parts.includes("meta") || hasMod && mac,
|
|
37
|
+
alt: parts.includes("alt"),
|
|
38
|
+
shift: parts.includes("shift"),
|
|
39
|
+
focusScoped: key.length === 1 && !parts.some((part) => ["control", "meta", "alt", "shift", "mod"].includes(part))
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function isEditable(target) {
|
|
43
|
+
if (!(target instanceof Element))
|
|
44
|
+
return false;
|
|
45
|
+
return Boolean(target.closest('input, textarea, select, [contenteditable]:not([contenteditable="false"])'));
|
|
46
|
+
}
|
|
47
|
+
function physicalKey(code) {
|
|
48
|
+
return /^Key([A-Z])$/.exec(code)?.[1]?.toLowerCase() ?? /^Digit(\d)$/.exec(code)?.[1];
|
|
49
|
+
}
|
|
50
|
+
function matches(event, hotkey) {
|
|
51
|
+
const keyMatches = canonicalKey(event.key) === hotkey.key || event.altKey && physicalKey(event.code) === hotkey.key;
|
|
52
|
+
const symbol = hotkey.key.length === 1 && hotkey.key !== " " && !/[a-z0-9]/.test(hotkey.key);
|
|
53
|
+
return keyMatches && event.ctrlKey === hotkey.ctrl && event.metaKey === hotkey.meta && event.altKey === hotkey.alt && (symbol && !hotkey.shift ? true : event.shiftKey === hotkey.shift);
|
|
54
|
+
}
|
|
55
|
+
function normalizeBinding(value) {
|
|
56
|
+
if (value === false || value == null)
|
|
57
|
+
return void 0;
|
|
58
|
+
return typeof value === "string" ? { keys: value } : value;
|
|
59
|
+
}
|
|
60
|
+
function install(element, value) {
|
|
61
|
+
const options = normalizeBinding(value);
|
|
62
|
+
const parsed = options && parseHotkey(options.keys);
|
|
63
|
+
if (!options || !parsed)
|
|
64
|
+
return void 0;
|
|
65
|
+
const listener = (event) => {
|
|
66
|
+
if (event.defaultPrevented || !matches(event, parsed))
|
|
67
|
+
return;
|
|
68
|
+
if (typeof options.when === "boolean" && !options.when)
|
|
69
|
+
return;
|
|
70
|
+
if (typeof options.when === "function" && !options.when(element, event))
|
|
71
|
+
return;
|
|
72
|
+
const activeElement = element.ownerDocument.activeElement;
|
|
73
|
+
const eventTarget = event.target;
|
|
74
|
+
if (parsed.focusScoped && !element.contains(activeElement) && !(eventTarget && (eventTarget === element || element.contains(eventTarget)))) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!options.allowInEditable && isEditable(event.target))
|
|
78
|
+
return;
|
|
79
|
+
event.preventDefault();
|
|
80
|
+
if (options.handler)
|
|
81
|
+
options.handler(event);
|
|
82
|
+
else
|
|
83
|
+
element.click();
|
|
84
|
+
};
|
|
85
|
+
document.addEventListener("keydown", listener);
|
|
86
|
+
return () => document.removeEventListener("keydown", listener);
|
|
87
|
+
}
|
|
88
|
+
export const vHotkey = {
|
|
89
|
+
mounted(element, binding) {
|
|
90
|
+
const cleanup = install(element, binding.value);
|
|
91
|
+
if (cleanup)
|
|
92
|
+
element.__selarasHotkeyCleanup = cleanup;
|
|
93
|
+
},
|
|
94
|
+
updated(element, binding) {
|
|
95
|
+
const host = element;
|
|
96
|
+
host.__selarasHotkeyCleanup?.();
|
|
97
|
+
delete host.__selarasHotkeyCleanup;
|
|
98
|
+
const cleanup = install(element, binding.value);
|
|
99
|
+
if (cleanup)
|
|
100
|
+
host.__selarasHotkeyCleanup = cleanup;
|
|
101
|
+
},
|
|
102
|
+
unmounted(element) {
|
|
103
|
+
const host = element;
|
|
104
|
+
host.__selarasHotkeyCleanup?.();
|
|
105
|
+
delete host.__selarasHotkeyCleanup;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Directive } from 'vue';
|
|
2
|
+
/** A mask placeholder character and the input characters it accepts. */
|
|
3
|
+
export interface MaskToken {
|
|
4
|
+
pattern: RegExp;
|
|
5
|
+
/** Rewrites an accepted character, e.g. to uppercase it. */
|
|
6
|
+
transform?: (char: string) => string;
|
|
7
|
+
}
|
|
8
|
+
/** Values reported by `v-mask` after each mask update. */
|
|
9
|
+
export interface MaskDetail {
|
|
10
|
+
/** The value without mask literals. This is also the value `v-model` receives. */
|
|
11
|
+
value: string;
|
|
12
|
+
/** The formatted value shown in the input. */
|
|
13
|
+
maskedValue: string;
|
|
14
|
+
/** Whether every placeholder in the mask has been filled. */
|
|
15
|
+
completed: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface MaskOptions {
|
|
18
|
+
mask: string;
|
|
19
|
+
/** Extra or overriding placeholder tokens, merged over the defaults (`#`, `@`, `*`). */
|
|
20
|
+
tokens?: Record<string, MaskToken>;
|
|
21
|
+
onMask?: (detail: MaskDetail) => void;
|
|
22
|
+
}
|
|
23
|
+
/** A mask pattern string, or options for custom tokens and the `onMask` callback. */
|
|
24
|
+
export type MaskValue = string | MaskOptions | null | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Formats a native input or the input inside a single-input component.
|
|
27
|
+
* `v-model` receives the raw value; the field shows the formatted one. Use
|
|
28
|
+
* `onMask` (or the bubbling `mask` event) to also read the formatted value.
|
|
29
|
+
*/
|
|
30
|
+
export declare const vMask: Directive<HTMLElement, MaskValue>;
|