@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.
Files changed (176) hide show
  1. package/README.md +36 -7
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +31 -15
  4. package/dist/runtime/components/Autocomplete.vue +3 -1
  5. package/dist/runtime/components/Badge.d.vue.ts +1 -1
  6. package/dist/runtime/components/Badge.vue.d.ts +1 -1
  7. package/dist/runtime/components/Calendar.d.vue.ts +69 -0
  8. package/dist/runtime/components/Calendar.vue +163 -0
  9. package/dist/runtime/components/Calendar.vue.d.ts +69 -0
  10. package/dist/runtime/components/Callout.vue +12 -1
  11. package/dist/runtime/components/CodeGroup.vue +13 -3
  12. package/dist/runtime/components/CodeTree.d.vue.ts +2 -0
  13. package/dist/runtime/components/CodeTree.vue +52 -1
  14. package/dist/runtime/components/CodeTree.vue.d.ts +2 -0
  15. package/dist/runtime/components/ColorPicker.d.vue.ts +8 -0
  16. package/dist/runtime/components/ColorPicker.vue +66 -45
  17. package/dist/runtime/components/ColorPicker.vue.d.ts +8 -0
  18. package/dist/runtime/components/Column.js +1 -1
  19. package/dist/runtime/components/CommandPalette.d.vue.ts +3 -1
  20. package/dist/runtime/components/CommandPalette.vue +5 -0
  21. package/dist/runtime/components/CommandPalette.vue.d.ts +3 -1
  22. package/dist/runtime/components/ContentNavigation.d.vue.ts +8 -1
  23. package/dist/runtime/components/ContentNavigation.vue +9 -2
  24. package/dist/runtime/components/ContentNavigation.vue.d.ts +8 -1
  25. package/dist/runtime/components/ContentToc.vue +14 -7
  26. package/dist/runtime/components/ContextMenu.d.vue.ts +8 -1
  27. package/dist/runtime/components/ContextMenu.vue +16 -4
  28. package/dist/runtime/components/ContextMenu.vue.d.ts +8 -1
  29. package/dist/runtime/components/DatePicker.d.vue.ts +10 -3
  30. package/dist/runtime/components/DatePicker.vue +24 -16
  31. package/dist/runtime/components/DatePicker.vue.d.ts +10 -3
  32. package/dist/runtime/components/Dropdown.d.vue.ts +33 -6
  33. package/dist/runtime/components/Dropdown.vue +143 -11
  34. package/dist/runtime/components/Dropdown.vue.d.ts +33 -6
  35. package/dist/runtime/components/FileTree.d.vue.ts +2 -0
  36. package/dist/runtime/components/FileTree.vue +8 -4
  37. package/dist/runtime/components/FileTree.vue.d.ts +2 -0
  38. package/dist/runtime/components/FileUpload.vue +1 -1
  39. package/dist/runtime/components/FormField.d.vue.ts +4 -0
  40. package/dist/runtime/components/FormField.vue +19 -4
  41. package/dist/runtime/components/FormField.vue.d.ts +4 -0
  42. package/dist/runtime/components/Input.vue +3 -2
  43. package/dist/runtime/components/Kbd.vue +12 -2
  44. package/dist/runtime/components/NavigationMenu.d.vue.ts +10 -10
  45. package/dist/runtime/components/NavigationMenu.vue +13 -7
  46. package/dist/runtime/components/NavigationMenu.vue.d.ts +10 -10
  47. package/dist/runtime/components/NavigationMenuAccordionItem.vue +5 -1
  48. package/dist/runtime/components/NavigationMenuFlyoutList.vue +7 -4
  49. package/dist/runtime/components/PinInput.vue +1 -1
  50. package/dist/runtime/components/Popover.d.vue.ts +8 -2
  51. package/dist/runtime/components/Popover.vue +9 -4
  52. package/dist/runtime/components/Popover.vue.d.ts +8 -2
  53. package/dist/runtime/components/ProsePre.d.vue.ts +6 -2
  54. package/dist/runtime/components/ProsePre.vue +11 -4
  55. package/dist/runtime/components/ProsePre.vue.d.ts +6 -2
  56. package/dist/runtime/components/Select.vue +3 -1
  57. package/dist/runtime/components/SplitterPanel.d.vue.ts +2 -2
  58. package/dist/runtime/components/SplitterPanel.vue.d.ts +2 -2
  59. package/dist/runtime/components/Table.vue +95 -26
  60. package/dist/runtime/components/Tabs.d.vue.ts +44 -4
  61. package/dist/runtime/components/Tabs.vue +171 -18
  62. package/dist/runtime/components/Tabs.vue.d.ts +44 -4
  63. package/dist/runtime/components/Textarea.vue +3 -2
  64. package/dist/runtime/components/Timeline.d.vue.ts +19 -0
  65. package/dist/runtime/components/Timeline.vue +99 -0
  66. package/dist/runtime/components/Timeline.vue.d.ts +19 -0
  67. package/dist/runtime/components/Toast.d.vue.ts +10 -1
  68. package/dist/runtime/components/Toast.vue +94 -8
  69. package/dist/runtime/components/Toast.vue.d.ts +10 -1
  70. package/dist/runtime/components/Tooltip.d.vue.ts +8 -2
  71. package/dist/runtime/components/Tooltip.vue +9 -4
  72. package/dist/runtime/components/Tooltip.vue.d.ts +8 -2
  73. package/dist/runtime/composables/use-form-field.d.ts +1 -0
  74. package/dist/runtime/composables/use-table.d.ts +3 -0
  75. package/dist/runtime/composables/use-table.js +10 -4
  76. package/dist/runtime/composables/use-toast.js +5 -1
  77. package/dist/runtime/default-colors.css +1 -1
  78. package/dist/runtime/directives/hotkey.d.ts +13 -0
  79. package/dist/runtime/directives/hotkey.js +107 -0
  80. package/dist/runtime/directives/index.d.ts +2 -0
  81. package/dist/runtime/directives/index.js +2 -0
  82. package/dist/runtime/directives/mask.d.ts +30 -0
  83. package/dist/runtime/directives/mask.js +165 -0
  84. package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +29 -25
  85. package/dist/runtime/internal/ComboboxSelectBase.vue +104 -20
  86. package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +29 -25
  87. package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +16 -11
  88. package/dist/runtime/internal/ComboboxSelectBody.vue +49 -10
  89. package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +16 -11
  90. package/dist/runtime/internal/DatePickerCalendarBody.vue +1 -1
  91. package/dist/runtime/internal/DatePickerRangeCalendarBody.vue +1 -1
  92. package/dist/runtime/internal/ShortcutHint.d.vue.ts +6 -0
  93. package/dist/runtime/internal/ShortcutHint.vue +13 -0
  94. package/dist/runtime/internal/ShortcutHint.vue.d.ts +6 -0
  95. package/dist/runtime/internal/ToastItemRenderer.d.vue.ts +9 -1
  96. package/dist/runtime/internal/ToastItemRenderer.vue +55 -8
  97. package/dist/runtime/internal/ToastItemRenderer.vue.d.ts +9 -1
  98. package/dist/runtime/internal/combobox-select.js +2 -1
  99. package/dist/runtime/internal/programmatic-services.d.ts +2 -0
  100. package/dist/runtime/internal/programmatic-services.js +8 -0
  101. package/dist/runtime/internal/toast-stack.d.ts +36 -0
  102. package/dist/runtime/internal/toast-stack.js +36 -0
  103. package/dist/runtime/structural.css +1 -1
  104. package/dist/runtime/table.d.ts +1 -1
  105. package/dist/runtime/theme/alert-dialog.js +1 -1
  106. package/dist/runtime/theme/avatar.js +7 -7
  107. package/dist/runtime/theme/badge.js +2 -2
  108. package/dist/runtime/theme/calendar.d.ts +86 -0
  109. package/dist/runtime/theme/calendar.js +19 -0
  110. package/dist/runtime/theme/callout.d.ts +0 -12
  111. package/dist/runtime/theme/callout.js +8 -8
  112. package/dist/runtime/theme/checkbox.js +1 -1
  113. package/dist/runtime/theme/code-tree.js +1 -1
  114. package/dist/runtime/theme/command-palette.js +1 -1
  115. package/dist/runtime/theme/content-navigation.d.ts +3 -0
  116. package/dist/runtime/theme/content-navigation.js +1 -0
  117. package/dist/runtime/theme/date-picker.js +1 -1
  118. package/dist/runtime/theme/drawer.js +1 -1
  119. package/dist/runtime/theme/dropdown.d.ts +21 -0
  120. package/dist/runtime/theme/dropdown.js +15 -2
  121. package/dist/runtime/theme/file-tree.js +1 -1
  122. package/dist/runtime/theme/file-upload.js +2 -2
  123. package/dist/runtime/theme/form-field.d.ts +18 -0
  124. package/dist/runtime/theme/form-field.js +6 -1
  125. package/dist/runtime/theme/icon.js +1 -1
  126. package/dist/runtime/theme/input-number.js +1 -1
  127. package/dist/runtime/theme/input.js +1 -1
  128. package/dist/runtime/theme/modal.js +1 -1
  129. package/dist/runtime/theme/pin-input.js +1 -1
  130. package/dist/runtime/theme/progress.js +2 -2
  131. package/dist/runtime/theme/prose.d.ts +6 -0
  132. package/dist/runtime/theme/prose.js +2 -0
  133. package/dist/runtime/theme/radio-group.js +4 -4
  134. package/dist/runtime/theme/rating.js +1 -1
  135. package/dist/runtime/theme/select.js +2 -2
  136. package/dist/runtime/theme/separator.js +1 -1
  137. package/dist/runtime/theme/slideover.js +1 -1
  138. package/dist/runtime/theme/slider.js +2 -2
  139. package/dist/runtime/theme/splitter-resize-handle.js +1 -1
  140. package/dist/runtime/theme/switch.js +1 -1
  141. package/dist/runtime/theme/table.d.ts +27 -0
  142. package/dist/runtime/theme/table.js +11 -2
  143. package/dist/runtime/theme/tabs.d.ts +87 -3
  144. package/dist/runtime/theme/tabs.js +46 -22
  145. package/dist/runtime/theme/textarea.js +1 -1
  146. package/dist/runtime/theme/timeline.d.ts +251 -0
  147. package/dist/runtime/theme/timeline.js +84 -0
  148. package/dist/runtime/theme/toast.d.ts +92 -0
  149. package/dist/runtime/theme/toast.js +29 -7
  150. package/dist/runtime/theme-config.d.ts +12 -1
  151. package/dist/runtime/types/index.d.ts +6 -1
  152. package/dist/runtime/utils/arrow.d.ts +21 -0
  153. package/dist/runtime/utils/arrow.js +10 -0
  154. package/dist/runtime/utils/color-registry.d.ts +3 -1
  155. package/dist/runtime/utils/color-registry.js +4 -0
  156. package/dist/runtime/utils/file-icons.d.ts +8 -0
  157. package/dist/runtime/utils/file-icons.js +133 -0
  158. package/dist/runtime/utils/functional-tokens.d.ts +1 -1
  159. package/dist/runtime/utils/functional-tokens.js +1 -1
  160. package/dist/runtime/utils/icons.d.ts +2 -0
  161. package/dist/runtime/utils/icons.js +2 -0
  162. package/dist/runtime/utils/messages.d.ts +6 -0
  163. package/dist/runtime/utils/messages.js +6 -0
  164. package/dist/runtime/utils/navigation-menu.d.ts +10 -0
  165. package/dist/runtime/utils/navigation-menu.js +3 -0
  166. package/dist/runtime/utils/overlay.d.ts +17 -0
  167. package/dist/runtime/utils/overlay.js +7 -0
  168. package/dist/runtime/utils/seed-colors.d.ts +2 -1
  169. package/dist/runtime/utils/seed-colors.js +24 -16
  170. package/dist/runtime/utils/select-contracts.d.ts +7 -1
  171. package/dist/runtime/utils/table-columns.js +1 -1
  172. package/dist/runtime/utils/table-contracts.d.ts +9 -1
  173. package/dist/runtime/utils/timeline-contracts.d.ts +42 -0
  174. package/dist/runtime/utils/timeline-contracts.js +0 -0
  175. package/dist/tokens/default-colors.tokens.json +100 -100
  176. 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 ui = computed(() => theme.value());
15
- const viewportProps = computed(() => resolveSlot(ui.value.viewport, props.ui?.viewport));
16
- onUnmounted(dispose);
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 toasts"
91
+ v-for="toast in rendered"
22
92
  :key="toast.id"
23
93
  :snapshot="toast._theme"
24
94
  >
25
- <ToastItemRenderer :toast="toast" :ui="props.ui" @remove="remove" />
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 v-bind="viewportProps" />
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 little pointer triangle. */
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 arrowProps = computed(() => resolveSlot(ui.value.arrow, props.ui?.arrow));
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 little pointer triangle. */
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
  }
@@ -4,6 +4,7 @@ export interface FormFieldContext {
4
4
  labelId?: string;
5
5
  name?: string;
6
6
  size?: 'sm' | 'md' | 'lg';
7
+ floatingLabel: ComputedRef<boolean>;
7
8
  invalid: ComputedRef<boolean>;
8
9
  describedBy: ComputedRef<string | undefined>;
9
10
  }
@@ -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
- // Row expansion otherwise only allows expanding rows that already have
95
- // real hierarchical subRows - this table's own expansion is manual
96
- // (detail content via the `expanded` slot), not a subRow tree.
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
- throw new Error("[useToast] add() is client-only. Render server-visible status content declaratively.");
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-amber-50:oklch(0.985 0.022 88);--color-selaras-amber-100:oklch(0.96 0.055 88);--color-selaras-amber-200:oklch(0.915 0.105 88);--color-selaras-amber-300:oklch(0.86 0.145 88);--color-selaras-amber-400:oklch(0.81 0.165 88);--color-selaras-amber-500:oklch(0.755 0.165 88);--color-selaras-amber-600:oklch(0.665 0.155 88);--color-selaras-amber-700:oklch(0.565 0.135 88);--color-selaras-amber-800:oklch(0.465 0.11 88);--color-selaras-amber-900:oklch(0.375 0.085 88);--color-selaras-amber-950:oklch(0.255 0.055 88);--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-emerald-50:oklch(0.98 0.02 148);--color-selaras-emerald-100:oklch(0.95 0.045 148);--color-selaras-emerald-200:oklch(0.895 0.08 148);--color-selaras-emerald-300:oklch(0.82 0.12 148);--color-selaras-emerald-400:oklch(0.735 0.155 148);--color-selaras-emerald-500:oklch(0.64 0.175 148);--color-selaras-emerald-600:oklch(0.555 0.16 148);--color-selaras-emerald-700:oklch(0.47 0.14 148);--color-selaras-emerald-800:oklch(0.39 0.115 148);--color-selaras-emerald-900:oklch(0.315 0.085 148);--color-selaras-emerald-950:oklch(0.225 0.055 148);--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-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.975 0.015 28);--color-selaras-red-100:oklch(0.945 0.035 28);--color-selaras-red-200:oklch(0.89 0.07 28);--color-selaras-red-300:oklch(0.805 0.12 28);--color-selaras-red-400:oklch(0.7 0.175 28);--color-selaras-red-500:oklch(0.6 0.205 28);--color-selaras-red-600:oklch(0.525 0.195 28);--color-selaras-red-700:oklch(0.445 0.17 28);--color-selaras-red-800:oklch(0.365 0.135 28);--color-selaras-red-900:oklch(0.295 0.1 28);--color-selaras-red-950:oklch(0.205 0.065 28)}@layer theme{:root,[data-selaras-theme]{--_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-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)}}
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
+ };
@@ -1 +1,3 @@
1
+ export * from './hotkey.js';
2
+ export * from './mask.js';
1
3
  export * from './ripple.js';
@@ -1 +1,3 @@
1
+ export * from "./hotkey.js";
2
+ export * from "./mask.js";
1
3
  export * from "./ripple.js";
@@ -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>;