@sewadah/selaras 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +4 -1
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +21 -6
  4. package/dist/runtime/components/AlertDialog.d.vue.ts +5 -3
  5. package/dist/runtime/components/AlertDialog.vue +17 -9
  6. package/dist/runtime/components/AlertDialog.vue.d.ts +5 -3
  7. package/dist/runtime/components/App.vue +2 -0
  8. package/dist/runtime/components/Autocomplete.d.vue.ts +2 -2
  9. package/dist/runtime/components/Autocomplete.vue +4 -2
  10. package/dist/runtime/components/Autocomplete.vue.d.ts +2 -2
  11. package/dist/runtime/components/Button.d.vue.ts +7 -5
  12. package/dist/runtime/components/Button.vue +14 -3
  13. package/dist/runtime/components/Button.vue.d.ts +7 -5
  14. package/dist/runtime/components/Calendar.d.vue.ts +69 -0
  15. package/dist/runtime/components/Calendar.vue +163 -0
  16. package/dist/runtime/components/Calendar.vue.d.ts +69 -0
  17. package/dist/runtime/components/Callout.vue +12 -1
  18. package/dist/runtime/components/CodeGroup.vue +13 -3
  19. package/dist/runtime/components/CodeTree.d.vue.ts +2 -0
  20. package/dist/runtime/components/CodeTree.vue +52 -1
  21. package/dist/runtime/components/CodeTree.vue.d.ts +2 -0
  22. package/dist/runtime/components/ColorModeSwitch.d.vue.ts +18 -0
  23. package/dist/runtime/components/ColorModeSwitch.vue +59 -0
  24. package/dist/runtime/components/ColorModeSwitch.vue.d.ts +18 -0
  25. package/dist/runtime/components/ColorPicker.d.vue.ts +8 -0
  26. package/dist/runtime/components/ColorPicker.vue +66 -45
  27. package/dist/runtime/components/ColorPicker.vue.d.ts +8 -0
  28. package/dist/runtime/components/Column.js +1 -1
  29. package/dist/runtime/components/CommandPalette.d.vue.ts +3 -1
  30. package/dist/runtime/components/CommandPalette.vue +5 -0
  31. package/dist/runtime/components/CommandPalette.vue.d.ts +3 -1
  32. package/dist/runtime/components/ConfirmRenderer.d.vue.ts +3 -0
  33. package/dist/runtime/components/ConfirmRenderer.vue +39 -0
  34. package/dist/runtime/components/ConfirmRenderer.vue.d.ts +3 -0
  35. package/dist/runtime/components/ContentNavigation.d.vue.ts +8 -1
  36. package/dist/runtime/components/ContentNavigation.vue +9 -2
  37. package/dist/runtime/components/ContentNavigation.vue.d.ts +8 -1
  38. package/dist/runtime/components/ContextMenu.d.vue.ts +8 -1
  39. package/dist/runtime/components/ContextMenu.vue +20 -6
  40. package/dist/runtime/components/ContextMenu.vue.d.ts +8 -1
  41. package/dist/runtime/components/DatePicker.d.vue.ts +10 -3
  42. package/dist/runtime/components/DatePicker.vue +24 -16
  43. package/dist/runtime/components/DatePicker.vue.d.ts +10 -3
  44. package/dist/runtime/components/Drawer.vue +3 -2
  45. package/dist/runtime/components/Dropdown.d.vue.ts +33 -6
  46. package/dist/runtime/components/Dropdown.vue +146 -12
  47. package/dist/runtime/components/Dropdown.vue.d.ts +33 -6
  48. package/dist/runtime/components/FileTree.d.vue.ts +2 -0
  49. package/dist/runtime/components/FileTree.vue +8 -4
  50. package/dist/runtime/components/FileTree.vue.d.ts +2 -0
  51. package/dist/runtime/components/FormField.d.vue.ts +4 -0
  52. package/dist/runtime/components/FormField.vue +19 -4
  53. package/dist/runtime/components/FormField.vue.d.ts +4 -0
  54. package/dist/runtime/components/Input.vue +3 -2
  55. package/dist/runtime/components/Kbd.vue +12 -2
  56. package/dist/runtime/components/Modal.vue +3 -2
  57. package/dist/runtime/components/NavigationMenu.d.vue.ts +10 -10
  58. package/dist/runtime/components/NavigationMenu.vue +13 -9
  59. package/dist/runtime/components/NavigationMenu.vue.d.ts +10 -10
  60. package/dist/runtime/components/NavigationMenuAccordionItem.vue +5 -1
  61. package/dist/runtime/components/NavigationMenuFlyoutList.vue +7 -4
  62. package/dist/runtime/components/Popover.d.vue.ts +8 -2
  63. package/dist/runtime/components/Popover.vue +13 -6
  64. package/dist/runtime/components/Popover.vue.d.ts +8 -2
  65. package/dist/runtime/components/ProsePre.d.vue.ts +2 -0
  66. package/dist/runtime/components/ProsePre.vue +2 -1
  67. package/dist/runtime/components/ProsePre.vue.d.ts +2 -0
  68. package/dist/runtime/components/Select.d.vue.ts +8 -4
  69. package/dist/runtime/components/Select.vue +4 -2
  70. package/dist/runtime/components/Select.vue.d.ts +8 -4
  71. package/dist/runtime/components/Slideover.vue +3 -2
  72. package/dist/runtime/components/SplitterPanel.d.vue.ts +2 -2
  73. package/dist/runtime/components/SplitterPanel.vue.d.ts +2 -2
  74. package/dist/runtime/components/Table.vue +95 -26
  75. package/dist/runtime/components/Tabs.d.vue.ts +44 -4
  76. package/dist/runtime/components/Tabs.vue +171 -18
  77. package/dist/runtime/components/Tabs.vue.d.ts +44 -4
  78. package/dist/runtime/components/Textarea.vue +3 -2
  79. package/dist/runtime/components/Timeline.d.vue.ts +19 -0
  80. package/dist/runtime/components/Timeline.vue +99 -0
  81. package/dist/runtime/components/Timeline.vue.d.ts +19 -0
  82. package/dist/runtime/components/Toast.d.vue.ts +10 -1
  83. package/dist/runtime/components/Toast.vue +94 -8
  84. package/dist/runtime/components/Toast.vue.d.ts +10 -1
  85. package/dist/runtime/components/Tooltip.d.vue.ts +8 -2
  86. package/dist/runtime/components/Tooltip.vue +13 -6
  87. package/dist/runtime/components/Tooltip.vue.d.ts +8 -2
  88. package/dist/runtime/composables/use-confirm.d.ts +14 -0
  89. package/dist/runtime/composables/use-confirm.js +28 -0
  90. package/dist/runtime/composables/use-form-field.d.ts +1 -0
  91. package/dist/runtime/composables/use-table.d.ts +3 -0
  92. package/dist/runtime/composables/use-table.js +10 -4
  93. package/dist/runtime/composables/use-toast.js +5 -1
  94. package/dist/runtime/directives/hotkey.d.ts +13 -0
  95. package/dist/runtime/directives/hotkey.js +107 -0
  96. package/dist/runtime/directives/index.d.ts +2 -0
  97. package/dist/runtime/directives/index.js +2 -0
  98. package/dist/runtime/directives/mask.d.ts +30 -0
  99. package/dist/runtime/directives/mask.js +165 -0
  100. package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +41 -36
  101. package/dist/runtime/internal/ComboboxSelectBase.vue +119 -26
  102. package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +41 -36
  103. package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +23 -17
  104. package/dist/runtime/internal/ComboboxSelectBody.vue +50 -11
  105. package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +23 -17
  106. package/dist/runtime/internal/DatePickerCalendarBody.vue +1 -1
  107. package/dist/runtime/internal/DatePickerRangeCalendarBody.vue +1 -1
  108. package/dist/runtime/internal/ShortcutHint.d.vue.ts +6 -0
  109. package/dist/runtime/internal/ShortcutHint.vue +13 -0
  110. package/dist/runtime/internal/ShortcutHint.vue.d.ts +6 -0
  111. package/dist/runtime/internal/ToastItemRenderer.d.vue.ts +9 -1
  112. package/dist/runtime/internal/ToastItemRenderer.vue +55 -8
  113. package/dist/runtime/internal/ToastItemRenderer.vue.d.ts +9 -1
  114. package/dist/runtime/internal/combobox-select.d.ts +19 -18
  115. package/dist/runtime/internal/combobox-select.js +38 -15
  116. package/dist/runtime/internal/programmatic-services.d.ts +13 -0
  117. package/dist/runtime/internal/programmatic-services.js +9 -0
  118. package/dist/runtime/internal/toast-stack.d.ts +36 -0
  119. package/dist/runtime/internal/toast-stack.js +36 -0
  120. package/dist/runtime/structural.css +1 -1
  121. package/dist/runtime/table.d.ts +1 -1
  122. package/dist/runtime/theme/alert-dialog.js +1 -1
  123. package/dist/runtime/theme/badge.d.ts +9 -0
  124. package/dist/runtime/theme/badge.js +2 -1
  125. package/dist/runtime/theme/button.d.ts +18 -0
  126. package/dist/runtime/theme/button.js +4 -1
  127. package/dist/runtime/theme/calendar.d.ts +86 -0
  128. package/dist/runtime/theme/calendar.js +19 -0
  129. package/dist/runtime/theme/callout.d.ts +0 -12
  130. package/dist/runtime/theme/callout.js +8 -8
  131. package/dist/runtime/theme/code-tree.js +1 -1
  132. package/dist/runtime/theme/command-palette.js +1 -1
  133. package/dist/runtime/theme/content-navigation.d.ts +3 -0
  134. package/dist/runtime/theme/content-navigation.js +1 -0
  135. package/dist/runtime/theme/drawer.js +1 -1
  136. package/dist/runtime/theme/dropdown.d.ts +21 -0
  137. package/dist/runtime/theme/dropdown.js +15 -2
  138. package/dist/runtime/theme/file-tree.js +1 -1
  139. package/dist/runtime/theme/form-field.d.ts +18 -0
  140. package/dist/runtime/theme/form-field.js +6 -1
  141. package/dist/runtime/theme/modal.js +1 -1
  142. package/dist/runtime/theme/slideover.js +1 -1
  143. package/dist/runtime/theme/table.d.ts +27 -0
  144. package/dist/runtime/theme/table.js +11 -2
  145. package/dist/runtime/theme/tabs.d.ts +87 -3
  146. package/dist/runtime/theme/tabs.js +46 -22
  147. package/dist/runtime/theme/timeline.d.ts +251 -0
  148. package/dist/runtime/theme/timeline.js +84 -0
  149. package/dist/runtime/theme/toast.d.ts +92 -0
  150. package/dist/runtime/theme/toast.js +29 -7
  151. package/dist/runtime/theme-config.d.ts +13 -2
  152. package/dist/runtime/types/index.d.ts +10 -2
  153. package/dist/runtime/utils/arrow.d.ts +21 -0
  154. package/dist/runtime/utils/arrow.js +10 -0
  155. package/dist/runtime/utils/autocomplete-contracts.d.ts +4 -4
  156. package/dist/runtime/utils/file-icons.js +3 -0
  157. package/dist/runtime/utils/icons.d.ts +2 -0
  158. package/dist/runtime/utils/icons.js +2 -0
  159. package/dist/runtime/utils/messages.d.ts +7 -0
  160. package/dist/runtime/utils/messages.js +7 -0
  161. package/dist/runtime/utils/navigation-menu.d.ts +7 -0
  162. package/dist/runtime/utils/navigation-menu.js +3 -0
  163. package/dist/runtime/utils/overlay.d.ts +17 -0
  164. package/dist/runtime/utils/overlay.js +7 -0
  165. package/dist/runtime/utils/select-contracts.d.ts +25 -11
  166. package/dist/runtime/utils/table-columns.js +1 -1
  167. package/dist/runtime/utils/table-contracts.d.ts +9 -1
  168. package/dist/runtime/utils/timeline-contracts.d.ts +42 -0
  169. package/dist/runtime/utils/timeline-contracts.js +0 -0
  170. package/dist/runtime/utils/ui.d.ts +11 -0
  171. package/dist/runtime/utils/ui.js +54 -1
  172. package/package.json +6 -5
@@ -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,31 +2,38 @@
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 { resolveSlot, useComponentTheme, useThemeBindings } from "../utils/ui";
5
+ import { arrowContentProps, arrowElementProps } from "../utils/arrow";
6
+ import { overlayPortalProps } from "../utils/overlay";
7
+ import { resolveSlot, useComponentTheme, useFallthroughProps, useThemeBindings } from "../utils/ui";
8
+ defineOptions({ inheritAttrs: false });
6
9
  const props = defineProps({
7
10
  text: { type: String, required: false },
8
11
  side: { type: String, required: false, default: "top" },
12
+ positioning: { type: Object, required: false },
13
+ portal: { type: [Boolean, String], required: false, skipCheck: true, default: void 0 },
9
14
  delayDuration: { type: Number, required: false, default: 200 },
10
15
  kbds: { type: Array, required: false },
11
- arrow: { type: Boolean, required: false, default: true },
16
+ arrow: { type: [Boolean, Object], required: false, default: true },
12
17
  disabled: { type: Boolean, required: false },
13
18
  ui: { type: Object, required: false }
14
19
  });
15
20
  const theme = useComponentTheme("tooltip", tooltipTheme);
16
21
  const themeBindings = useThemeBindings();
17
22
  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));
23
+ const triggerProps = useFallthroughProps();
24
+ const contentProps = computed(() => ({ ...resolveSlot(ui.value.content, props.ui?.content), ...arrowContentProps(props.arrow), ...props.positioning }));
25
+ const portalProps = computed(() => overlayPortalProps(props.portal));
26
+ const arrowProps = computed(() => ({ ...resolveSlot(ui.value.arrow, props.ui?.arrow), ...arrowElementProps(props.arrow) }));
20
27
  const kbdsProps = computed(() => resolveSlot(ui.value.kbds, props.ui?.kbds));
21
28
  const kbdProps = computed(() => resolveSlot(ui.value.kbd, props.ui?.kbd));
22
29
  </script>
23
30
 
24
31
  <template>
25
32
  <TooltipRoot :delay-duration="delayDuration" :disabled="disabled">
26
- <TooltipTrigger as-child>
33
+ <TooltipTrigger as-child v-bind="triggerProps">
27
34
  <slot />
28
35
  </TooltipTrigger>
29
- <TooltipPortal>
36
+ <TooltipPortal v-bind="portalProps">
30
37
  <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
38
  <slot name="content">
32
39
  {{ 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
  }
@@ -0,0 +1,14 @@
1
+ import type { ColorRole } from '../utils/color-registry.js';
2
+ export interface UseConfirmOptions {
3
+ title?: string;
4
+ description?: string;
5
+ confirmLabel?: string;
6
+ cancelLabel?: string;
7
+ confirmColor?: ColorRole;
8
+ icon?: string;
9
+ }
10
+ export interface UseConfirmReturn {
11
+ /** Resolves true only for an explicit confirmation; cancel, Escape, and dismissal resolve false. */
12
+ confirm: (options?: UseConfirmOptions) => Promise<boolean>;
13
+ }
14
+ export declare function useConfirm(): UseConfirmReturn;
@@ -0,0 +1,28 @@
1
+ import { useConfirmService } from "../internal/programmatic-services.js";
2
+ import { useProgrammaticThemeSnapshot } from "../utils/programmatic-theme.js";
3
+ import { useMessages } from "./use-messages.js";
4
+ export function useConfirm() {
5
+ const service = useConfirmService();
6
+ const messages = useMessages();
7
+ const snapshotTheme = useProgrammaticThemeSnapshot();
8
+ function confirm(options = {}) {
9
+ if (import.meta.server)
10
+ return Promise.reject(new Error("[useConfirm] confirm() is client-only. Render SAlertDialog declaratively during SSR."));
11
+ return new Promise((resolve) => {
12
+ service.instances.value.push({
13
+ id: service.nextId(),
14
+ isOpen: true,
15
+ _settled: false,
16
+ resolve: (value) => resolve(value === true),
17
+ title: options.title ?? messages.value.confirmation,
18
+ description: options.description,
19
+ confirmLabel: options.confirmLabel,
20
+ cancelLabel: options.cancelLabel,
21
+ confirmColor: options.confirmColor,
22
+ icon: options.icon,
23
+ _theme: snapshotTheme()
24
+ });
25
+ });
26
+ }
27
+ return { confirm };
28
+ }
@@ -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 };
@@ -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>;
@@ -0,0 +1,165 @@
1
+ const defaultMaskTokens = {
2
+ "#": { pattern: /\d/ },
3
+ "@": { pattern: /[a-z]/i },
4
+ "*": { pattern: /[\da-z]/i }
5
+ };
6
+ function formatMask(input, mask, tokens = defaultMaskTokens, raw = true) {
7
+ let masked = "";
8
+ let unmasked = "";
9
+ let inputIndex = 0;
10
+ let maskIndex = 0;
11
+ let skipped = 0;
12
+ const slotEnds = [];
13
+ while (maskIndex < mask.length && inputIndex < input.length) {
14
+ const maskChar = mask[maskIndex];
15
+ const token = tokens[maskChar];
16
+ const char = input[inputIndex];
17
+ if (token) {
18
+ if (token.pattern.test(char)) {
19
+ const accepted = token.transform ? token.transform(char) : char;
20
+ masked += accepted;
21
+ unmasked += accepted;
22
+ slotEnds.push(masked.length);
23
+ maskIndex++;
24
+ } else {
25
+ skipped++;
26
+ }
27
+ inputIndex++;
28
+ } else {
29
+ masked += maskChar;
30
+ if (!raw && char === maskChar && inputIndex - skipped === maskIndex)
31
+ inputIndex++;
32
+ maskIndex++;
33
+ }
34
+ }
35
+ let slotCount = 0;
36
+ for (const char of mask) {
37
+ if (tokens[char])
38
+ slotCount++;
39
+ }
40
+ return { masked, unmasked, completed: slotEnds.length === slotCount, slotEnds };
41
+ }
42
+ const states = /* @__PURE__ */ new WeakMap();
43
+ let nativeDescriptor;
44
+ function nativeValue() {
45
+ return nativeDescriptor ??= Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");
46
+ }
47
+ function readNative(input) {
48
+ return nativeValue().get.call(input);
49
+ }
50
+ function writeNative(input, value) {
51
+ if (readNative(input) !== value)
52
+ nativeValue().set.call(input, value);
53
+ }
54
+ function resolveInput(element) {
55
+ if (element instanceof HTMLInputElement)
56
+ return element;
57
+ return element.querySelector("input") ?? void 0;
58
+ }
59
+ function normalize(value) {
60
+ if (!value)
61
+ return void 0;
62
+ return typeof value === "string" ? { mask: value } : value.mask ? value : void 0;
63
+ }
64
+ function resolveTokens(options) {
65
+ return options.tokens ? { ...defaultMaskTokens, ...options.tokens } : defaultMaskTokens;
66
+ }
67
+ function commit(state, result) {
68
+ state.masked = result.masked;
69
+ state.unmasked = result.unmasked;
70
+ writeNative(state.input, result.masked);
71
+ if (state.reported === result.masked)
72
+ return;
73
+ state.reported = result.masked;
74
+ const detail = { value: result.unmasked, maskedValue: result.masked, completed: result.completed };
75
+ state.onMask?.(detail);
76
+ state.input.dispatchEvent(new CustomEvent("mask", { bubbles: true, detail }));
77
+ }
78
+ function handleInput(state, event) {
79
+ if (event.isComposing)
80
+ return;
81
+ const { input, mask, tokens } = state;
82
+ const current = readNative(input);
83
+ const caret = input.selectionStart ?? current.length;
84
+ const result = formatMask(current, mask, tokens, false);
85
+ const inputType = event.inputType ?? "";
86
+ if (inputType.startsWith("delete"))
87
+ result.masked = result.masked.slice(0, result.slotEnds.at(-1) ?? 0);
88
+ const position = Math.min(formatMask(current.slice(0, caret), mask, tokens, false).masked.length, result.masked.length);
89
+ commit(state, result);
90
+ if (document.activeElement === input)
91
+ input.setSelectionRange(position, position);
92
+ }
93
+ function install(element, input, options) {
94
+ const state = {
95
+ input,
96
+ mask: options.mask,
97
+ tokens: resolveTokens(options),
98
+ onMask: options.onMask,
99
+ masked: "",
100
+ unmasked: "",
101
+ onInput: (event) => handleInput(state, event)
102
+ };
103
+ Object.defineProperty(input, "value", {
104
+ configurable: true,
105
+ enumerable: nativeValue().enumerable,
106
+ get: () => {
107
+ const current = readNative(input);
108
+ return current === state.masked ? state.unmasked : formatMask(current, state.mask, state.tokens, false).unmasked;
109
+ },
110
+ set: (value) => {
111
+ commit(state, formatMask(value == null ? "" : String(value), state.mask, state.tokens));
112
+ }
113
+ });
114
+ input.addEventListener("input", state.onInput, true);
115
+ states.set(element, state);
116
+ commit(state, formatMask(readNative(input), state.mask, state.tokens));
117
+ }
118
+ function uninstall(element, state) {
119
+ state.input.removeEventListener("input", state.onInput, true);
120
+ Reflect.deleteProperty(state.input, "value");
121
+ states.delete(element);
122
+ }
123
+ function apply(element, value) {
124
+ const options = normalize(value);
125
+ const input = resolveInput(element);
126
+ const state = states.get(element);
127
+ if (state && (!options || state.input !== input))
128
+ uninstall(element, state);
129
+ if (!options || !input)
130
+ return;
131
+ const existing = states.get(element);
132
+ if (!existing) {
133
+ install(element, input, options);
134
+ return;
135
+ }
136
+ existing.onMask = options.onMask;
137
+ const tokens = resolveTokens(options);
138
+ if (existing.mask === options.mask && sameTokens(existing.tokens, tokens))
139
+ return;
140
+ existing.mask = options.mask;
141
+ existing.tokens = tokens;
142
+ commit(existing, formatMask(existing.unmasked, existing.mask, existing.tokens));
143
+ }
144
+ function sameTokens(left, right) {
145
+ if (left === right)
146
+ return true;
147
+ const keys = Object.keys(left);
148
+ return keys.length === Object.keys(right).length && keys.every((key) => right[key] && String(left[key].pattern) === String(right[key].pattern));
149
+ }
150
+ export const vMask = {
151
+ getSSRProps() {
152
+ return void 0;
153
+ },
154
+ mounted(element, binding) {
155
+ apply(element, binding.value);
156
+ },
157
+ updated(element, binding) {
158
+ apply(element, binding.value);
159
+ },
160
+ beforeUnmount(element) {
161
+ const state = states.get(element);
162
+ if (state)
163
+ uninstall(element, state);
164
+ }
165
+ };