@revojs/ui 0.2.61 → 0.2.62

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 (83) hide show
  1. package/dist/components/accordion.d.ts +13 -0
  2. package/dist/components/accordion.js +1 -0
  3. package/dist/components/checkbox.d.ts +1 -0
  4. package/dist/components/collapsible.d.ts +11 -0
  5. package/dist/components/collapsible.js +1 -0
  6. package/dist/components/confirm.d.ts +12 -0
  7. package/dist/components/confirm.js +1 -0
  8. package/dist/components/counter.d.ts +5 -0
  9. package/dist/components/labels.d.ts +24 -0
  10. package/dist/components/labels.js +1 -0
  11. package/dist/components/menu.d.ts +34 -0
  12. package/dist/components/radio-group.d.ts +17 -0
  13. package/dist/components/radio-group.js +1 -0
  14. package/dist/components/range.d.ts +2 -0
  15. package/dist/components/roving-focus.d.ts +7 -0
  16. package/dist/components/roving-focus.js +1 -0
  17. package/dist/components/select.d.ts +1 -0
  18. package/dist/components/stepper.d.ts +16 -0
  19. package/dist/components/stepper.js +1 -0
  20. package/dist/components/tabs.d.ts +4 -0
  21. package/dist/components/tags-input.d.ts +10 -0
  22. package/dist/components/tags-input.js +1 -0
  23. package/dist/components/toast.d.ts +19 -0
  24. package/dist/components/toast.js +1 -0
  25. package/dist/components/toggle-group.d.ts +5 -2
  26. package/dist/components/toolbar.d.ts +12 -0
  27. package/dist/components/toolbar.js +1 -0
  28. package/dist/components/tooltip.d.ts +1 -0
  29. package/dist/index.d.ts +15 -5
  30. package/dist/style.css +4 -0
  31. package/dist/vue/components/accordion.vue +57 -0
  32. package/dist/vue/components/checkbox.vue +24 -4
  33. package/dist/vue/components/collapsible.vue +72 -0
  34. package/dist/vue/components/collection.vue +5 -2
  35. package/dist/vue/components/confirm-dialog.vue +45 -0
  36. package/dist/vue/components/context-menu.vue +178 -0
  37. package/dist/vue/components/counter.vue +124 -18
  38. package/dist/vue/components/dialog.vue +5 -2
  39. package/dist/vue/components/menu-checkbox-item.vue +52 -0
  40. package/dist/vue/components/menu-group.vue +37 -0
  41. package/dist/vue/components/menu-layer.vue +152 -0
  42. package/dist/vue/components/menu-radio-group.vue +36 -0
  43. package/dist/vue/components/menu-radio-item.vue +50 -0
  44. package/dist/vue/components/menu.vue +16 -46
  45. package/dist/vue/components/pagination.vue +26 -6
  46. package/dist/vue/components/popover.vue +62 -13
  47. package/dist/vue/components/radio-group.vue +46 -0
  48. package/dist/vue/components/range.vue +31 -7
  49. package/dist/vue/components/rating.vue +35 -4
  50. package/dist/vue/components/select.vue +170 -52
  51. package/dist/vue/components/sheet.vue +5 -2
  52. package/dist/vue/components/shell.vue +5 -2
  53. package/dist/vue/components/stepper.vue +96 -0
  54. package/dist/vue/components/tabs.vue +37 -43
  55. package/dist/vue/components/tags-input.vue +141 -0
  56. package/dist/vue/components/toast-region.vue +233 -0
  57. package/dist/vue/components/toggle-group.vue +65 -10
  58. package/dist/vue/components/toolbar.vue +43 -0
  59. package/dist/vue/components/tooltip.vue +101 -15
  60. package/dist/vue/components/upload.vue +7 -4
  61. package/dist/vue/index.d.ts +28 -7
  62. package/dist/vue/index.js +29 -8
  63. package/dist/vue/utilities/labels.d.ts +7 -0
  64. package/dist/vue/utilities/labels.js +62 -0
  65. package/dist/vue/utilities/menu.d.ts +6 -0
  66. package/dist/vue/utilities/menu.js +2 -1
  67. package/dist/vue/utilities/tooltip.d.ts +3 -0
  68. package/dist/vue/utilities/tooltip.js +4 -0
  69. package/dist/vue/utilities/use-anchor.d.ts +6 -0
  70. package/dist/vue/utilities/use-anchor.js +26 -0
  71. package/dist/vue/utilities/use-confirm.d.ts +24 -0
  72. package/dist/vue/utilities/use-confirm.js +27 -0
  73. package/dist/vue/utilities/use-menu-navigation.d.ts +6 -0
  74. package/dist/vue/utilities/use-menu-navigation.js +48 -0
  75. package/dist/vue/utilities/use-roving-focus.d.ts +7 -0
  76. package/dist/vue/utilities/use-roving-focus.js +73 -0
  77. package/dist/vue/utilities/use-toast.d.ts +27 -0
  78. package/dist/vue/utilities/use-toast.js +33 -0
  79. package/dist/vue/utilities/use-typeahead.d.ts +5 -0
  80. package/dist/vue/utilities/use-typeahead.js +37 -0
  81. package/package.json +1 -1
  82. package/dist/vue/utilities/use-focus-trap.d.ts +0 -2
  83. package/dist/vue/utilities/use-focus-trap.js +0 -34
@@ -17,7 +17,7 @@
17
17
  <Stack direction="horizontal" align="center" justify="between" gap="0">
18
18
  <span class="min-w-0 flex-1 p-3">
19
19
  <span class="block truncate px-1">{{
20
- "name" in modelValue ? modelValue.name : "File selected"
20
+ "name" in modelValue ? modelValue.name : labels.fileSelected
21
21
  }}</span>
22
22
  </span>
23
23
 
@@ -26,7 +26,7 @@
26
26
  variant="secondary"
27
27
  :text="text"
28
28
  :icon-prefix="CloseIcon"
29
- aria-label="Remove file"
29
+ :aria-label="labels.removeFile"
30
30
  :disabled="disabled"
31
31
  @click="remove"
32
32
  />
@@ -36,9 +36,9 @@
36
36
  <Stack align="center" gap="3" padding="5" class="text-center">
37
37
  <Icon :is="UploadIcon" :size="24" />
38
38
 
39
- <span class="text-sm opacity-60"> Drag and drop a file here </span>
39
+ <span class="text-sm opacity-60">{{ labels.dropFile }}</span>
40
40
 
41
- <Button label="Browse files" variant="outline" :disabled="disabled" @click="browse" />
41
+ <Button :label="labels.browseFiles" variant="outline" :disabled="disabled" @click="browse" />
42
42
  </Stack>
43
43
  </slot>
44
44
 
@@ -70,6 +70,7 @@ import CloseIcon from "~icons/hugeicons/multiplication-sign";
70
70
  import { computed, ref, useTemplateRef } from "vue";
71
71
  import type { ControlInteraction } from "../../components/control.ts";
72
72
  import type { UploadProps, UploadState, UploadText } from "../../components/upload";
73
+ import { useUiLabels } from "../utilities/labels";
73
74
  import Button from "./button.vue";
74
75
  import Icon from "./icon.vue";
75
76
  import Stack from "./stack.vue";
@@ -94,6 +95,8 @@ defineSlots<{
94
95
  file?: (props: { file: Blob | File; remove: () => void; disabled: boolean }) => unknown;
95
96
  }>();
96
97
 
98
+ const labels = useUiLabels();
99
+
97
100
  const input = useTemplateRef("input");
98
101
 
99
102
  const isOver = ref(false);
@@ -1,42 +1,63 @@
1
+ export { default as Accordion } from "./components/accordion.vue";
1
2
  export { default as Badge } from "./components/badge.vue";
2
3
  export { default as Breadcrumb } from "./components/breadcrumb.vue";
3
4
  export { default as Button } from "./components/button.vue";
4
5
  export { default as Card } from "./components/card.vue";
5
6
  export { default as Checkbox } from "./components/checkbox.vue";
7
+ export { default as Collapsible } from "./components/collapsible.vue";
6
8
  export { default as Collection } from "./components/collection.vue";
9
+ export { default as ConfirmDialog } from "./components/confirm-dialog.vue";
7
10
  export { default as Container } from "./components/container.vue";
11
+ export { default as ContextMenu } from "./components/context-menu.vue";
8
12
  export { default as Counter } from "./components/counter.vue";
9
13
  export { default as Dialog } from "./components/dialog.vue";
14
+ export { default as Entry } from "./components/entry.vue";
10
15
  export { default as Field } from "./components/field.vue";
11
16
  export { default as Heading } from "./components/heading.vue";
12
- export { default as Shell } from "./components/shell.vue";
13
17
  export { default as Icon } from "./components/icon.vue";
14
18
  export { default as Input } from "./components/input.vue";
15
19
  export { default as Link } from "./components/link.vue";
16
20
  export { default as Markdown } from "./components/markdown.vue";
17
21
  export { default as Menu } from "./components/menu.vue";
22
+ export { default as MenuCheckboxItem } from "./components/menu-checkbox-item.vue";
23
+ export { default as MenuGroup } from "./components/menu-group.vue";
18
24
  export { default as MenuItem } from "./components/menu-item.vue";
19
- export { default as Entry } from "./components/entry.vue";
20
- export { default as Notification } from "./components/notification.vue";
25
+ export { default as MenuLayer } from "./components/menu-layer.vue";
26
+ export { default as MenuRadioGroup } from "./components/menu-radio-group.vue";
27
+ export { default as MenuRadioItem } from "./components/menu-radio-item.vue";
21
28
  export { default as Navigate } from "./components/navigate.vue";
22
- export { default as Popover } from "./components/popover.vue";
29
+ export { default as Notification } from "./components/notification.vue";
23
30
  export { default as Pagination } from "./components/pagination.vue";
31
+ export { default as Popover } from "./components/popover.vue";
24
32
  export { default as Progress } from "./components/progress.vue";
25
33
  export { default as Radio } from "./components/radio.vue";
34
+ export { default as RadioGroup } from "./components/radio-group.vue";
26
35
  export { default as Range } from "./components/range.vue";
27
36
  export { default as Rating } from "./components/rating.vue";
28
37
  export { default as Select } from "./components/select.vue";
29
38
  export { default as Separator } from "./components/separator.vue";
30
39
  export { default as Sheet } from "./components/sheet.vue";
40
+ export { default as Shell } from "./components/shell.vue";
31
41
  export { default as Stack } from "./components/stack.vue";
42
+ export { default as Stepper } from "./components/stepper.vue";
32
43
  export { default as Switch } from "./components/switch.vue";
33
44
  export { default as Tabs } from "./components/tabs.vue";
34
- export { default as Upload } from "./components/upload.vue";
45
+ export { default as TagsInput } from "./components/tags-input.vue";
35
46
  export { default as Textarea } from "./components/textarea.vue";
47
+ export { default as ToastRegion } from "./components/toast-region.vue";
36
48
  export { default as ToggleGroup } from "./components/toggle-group.vue";
49
+ export { default as Toolbar } from "./components/toolbar.vue";
37
50
  export { default as Tooltip } from "./components/tooltip.vue";
38
- export { menuCloseKey, menuSizeKey } from "./utilities/menu";
51
+ export { default as Upload } from "./components/upload.vue";
52
+ export { defaultUiLabels, germanUiLabels, provideUiLabels, uiLabelsKey, useUiLabels } from "./utilities/labels";
53
+ export { menuCloseKey, menuRadioKey, menuSizeKey, type MenuRadioContext } from "./utilities/menu";
54
+ export { tooltipState } from "./utilities/tooltip";
55
+ export { useAnchor } from "./utilities/use-anchor";
56
+ export { useConfirm } from "./utilities/use-confirm";
39
57
  export { useControlAttributes } from "./utilities/use-control-attributes";
40
- export { useFocusTrap } from "./utilities/use-focus-trap";
58
+ export { menuItemSelector, useMenuNavigation } from "./utilities/use-menu-navigation";
41
59
  export { useModalDialog } from "./utilities/use-modal-dialog";
60
+ export { useRovingFocus } from "./utilities/use-roving-focus";
42
61
  export { useScrollLock } from "./utilities/use-scroll-lock";
62
+ export { useToast } from "./utilities/use-toast";
63
+ export { isTypeaheadKey, useTypeahead } from "./utilities/use-typeahead";
package/dist/vue/index.js CHANGED
@@ -1,43 +1,64 @@
1
- import { menuCloseKey, menuSizeKey } from "./utilities/menu.js";
1
+ import { defaultUiLabels, germanUiLabels, provideUiLabels, uiLabelsKey, useUiLabels } from "./utilities/labels.js";
2
+ import { menuCloseKey, menuRadioKey, menuSizeKey } from "./utilities/menu.js";
3
+ import { tooltipState } from "./utilities/tooltip.js";
4
+ import { useAnchor } from "./utilities/use-anchor.js";
5
+ import { useConfirm } from "./utilities/use-confirm.js";
2
6
  import { useControlAttributes } from "./utilities/use-control-attributes.js";
3
- import { useFocusTrap } from "./utilities/use-focus-trap.js";
7
+ import { isTypeaheadKey, useTypeahead } from "./utilities/use-typeahead.js";
8
+ import { menuItemSelector, useMenuNavigation } from "./utilities/use-menu-navigation.js";
4
9
  import { useScrollLock } from "./utilities/use-scroll-lock.js";
5
10
  import { useModalDialog } from "./utilities/use-modal-dialog.js";
11
+ import { useRovingFocus } from "./utilities/use-roving-focus.js";
12
+ import { useToast } from "./utilities/use-toast.js";
13
+ import Accordion from "./components/accordion.vue";
6
14
  import Badge from "./components/badge.vue";
7
15
  import Breadcrumb from "./components/breadcrumb.vue";
8
16
  import Button from "./components/button.vue";
9
17
  import Card from "./components/card.vue";
10
18
  import Checkbox from "./components/checkbox.vue";
19
+ import Collapsible from "./components/collapsible.vue";
11
20
  import Collection from "./components/collection.vue";
21
+ import ConfirmDialog from "./components/confirm-dialog.vue";
12
22
  import Container from "./components/container.vue";
23
+ import ContextMenu from "./components/context-menu.vue";
13
24
  import Counter from "./components/counter.vue";
14
25
  import Dialog from "./components/dialog.vue";
26
+ import Entry from "./components/entry.vue";
15
27
  import Field from "./components/field.vue";
16
28
  import Heading from "./components/heading.vue";
17
- import Shell from "./components/shell.vue";
18
29
  import Icon from "./components/icon.vue";
19
30
  import Input from "./components/input.vue";
20
31
  import Link from "./components/link.vue";
21
32
  import Markdown from "./components/markdown.vue";
22
33
  import Menu from "./components/menu.vue";
34
+ import MenuCheckboxItem from "./components/menu-checkbox-item.vue";
35
+ import MenuGroup from "./components/menu-group.vue";
23
36
  import MenuItem from "./components/menu-item.vue";
24
- import Entry from "./components/entry.vue";
25
- import Notification from "./components/notification.vue";
37
+ import MenuLayer from "./components/menu-layer.vue";
38
+ import MenuRadioGroup from "./components/menu-radio-group.vue";
39
+ import MenuRadioItem from "./components/menu-radio-item.vue";
26
40
  import Navigate from "./components/navigate.vue";
27
- import Popover from "./components/popover.vue";
41
+ import Notification from "./components/notification.vue";
28
42
  import Pagination from "./components/pagination.vue";
43
+ import Popover from "./components/popover.vue";
29
44
  import Progress from "./components/progress.vue";
30
45
  import Radio from "./components/radio.vue";
46
+ import RadioGroup from "./components/radio-group.vue";
31
47
  import Range from "./components/range.vue";
32
48
  import Rating from "./components/rating.vue";
33
49
  import Select from "./components/select.vue";
34
50
  import Separator from "./components/separator.vue";
35
51
  import Sheet from "./components/sheet.vue";
52
+ import Shell from "./components/shell.vue";
36
53
  import Stack from "./components/stack.vue";
54
+ import Stepper from "./components/stepper.vue";
37
55
  import Switch from "./components/switch.vue";
38
56
  import Tabs from "./components/tabs.vue";
39
- import Upload from "./components/upload.vue";
57
+ import TagsInput from "./components/tags-input.vue";
40
58
  import Textarea from "./components/textarea.vue";
59
+ import ToastRegion from "./components/toast-region.vue";
41
60
  import ToggleGroup from "./components/toggle-group.vue";
61
+ import Toolbar from "./components/toolbar.vue";
42
62
  import Tooltip from "./components/tooltip.vue";
43
- export { Badge, Breadcrumb, Button, Card, Checkbox, Collection, Container, Counter, Dialog, Entry, Field, Heading, Icon, Input, Link, Markdown, Menu, MenuItem, Navigate, Notification, Pagination, Popover, Progress, Radio, Range, Rating, Select, Separator, Sheet, Shell, Stack, Switch, Tabs, Textarea, ToggleGroup, Tooltip, Upload, menuCloseKey, menuSizeKey, useControlAttributes, useFocusTrap, useModalDialog, useScrollLock };
63
+ import Upload from "./components/upload.vue";
64
+ export { Accordion, Badge, Breadcrumb, Button, Card, Checkbox, Collapsible, Collection, ConfirmDialog, Container, ContextMenu, Counter, Dialog, Entry, Field, Heading, Icon, Input, Link, Markdown, Menu, MenuCheckboxItem, MenuGroup, MenuItem, MenuLayer, MenuRadioGroup, MenuRadioItem, Navigate, Notification, Pagination, Popover, Progress, Radio, RadioGroup, Range, Rating, Select, Separator, Sheet, Shell, Stack, Stepper, Switch, Tabs, TagsInput, Textarea, ToastRegion, ToggleGroup, Toolbar, Tooltip, Upload, defaultUiLabels, germanUiLabels, isTypeaheadKey, menuCloseKey, menuItemSelector, menuRadioKey, menuSizeKey, provideUiLabels, tooltipState, uiLabelsKey, useAnchor, useConfirm, useControlAttributes, useMenuNavigation, useModalDialog, useRovingFocus, useScrollLock, useToast, useTypeahead, useUiLabels };
@@ -0,0 +1,7 @@
1
+ import { type InjectionKey } from "vue";
2
+ import type { UiLabels } from "../../components/labels";
3
+ export declare function useUiLabels(): UiLabels;
4
+ export declare function provideUiLabels(labels: Partial<UiLabels>): void;
5
+ export declare const uiLabelsKey: InjectionKey<UiLabels>;
6
+ export declare const defaultUiLabels: UiLabels;
7
+ export declare const germanUiLabels: UiLabels;
@@ -0,0 +1,62 @@
1
+ import { inject, provide } from "vue";
2
+ //#region src/vue/utilities/labels.ts
3
+ function useUiLabels() {
4
+ return inject(uiLabelsKey, defaultUiLabels);
5
+ }
6
+ function provideUiLabels(labels) {
7
+ provide(uiLabelsKey, {
8
+ ...useUiLabels(),
9
+ ...labels
10
+ });
11
+ }
12
+ var uiLabelsKey = Symbol("ui-labels");
13
+ var defaultUiLabels = {
14
+ open: "Open",
15
+ close: "Close",
16
+ menu: "Menu",
17
+ select: "Select",
18
+ selectRequired: "Please select an option",
19
+ noResults: "No results found",
20
+ empty: "Nothing found",
21
+ previous: "Previous",
22
+ next: "Next",
23
+ pagination: "Pagination",
24
+ pageNavigation: "Page navigation",
25
+ fileSelected: "File selected",
26
+ dropFile: "Drag and drop a file here",
27
+ browseFiles: "Browse files",
28
+ removeFile: "Remove file",
29
+ decrease: "Decrease",
30
+ increase: "Increase",
31
+ rating: (value, max) => `${value} of ${max}`,
32
+ notifications: "Notifications (F8)",
33
+ confirm: "Confirm",
34
+ cancel: "Cancel",
35
+ remove: (label) => `Remove ${label}`
36
+ };
37
+ var germanUiLabels = {
38
+ open: "Öffnen",
39
+ close: "Schließen",
40
+ menu: "Menü",
41
+ select: "Auswählen",
42
+ selectRequired: "Bitte wähle eine Option",
43
+ noResults: "Keine Treffer",
44
+ empty: "Nichts gefunden",
45
+ previous: "Zurück",
46
+ next: "Weiter",
47
+ pagination: "Seiten",
48
+ pageNavigation: "Seitennavigation",
49
+ fileSelected: "Datei ausgewählt",
50
+ dropFile: "Datei hierher ziehen",
51
+ browseFiles: "Datei auswählen",
52
+ removeFile: "Datei entfernen",
53
+ decrease: "Verringern",
54
+ increase: "Erhöhen",
55
+ rating: (value, max) => `${value} von ${max}`,
56
+ notifications: "Mitteilungen (F8)",
57
+ confirm: "Bestätigen",
58
+ cancel: "Abbrechen",
59
+ remove: (label) => `${label} entfernen`
60
+ };
61
+ //#endregion
62
+ export { defaultUiLabels, germanUiLabels, provideUiLabels, uiLabelsKey, useUiLabels };
@@ -1,4 +1,10 @@
1
1
  import type { ComputedRef, InjectionKey } from "vue";
2
2
  import type { ButtonSize } from "../../components/button";
3
+ import type { MenuRadioValue } from "../../components/menu";
4
+ export interface MenuRadioContext {
5
+ value: ComputedRef<MenuRadioValue | undefined>;
6
+ select: (value: MenuRadioValue) => void;
7
+ }
3
8
  export declare const menuCloseKey: InjectionKey<() => void>;
4
9
  export declare const menuSizeKey: InjectionKey<ComputedRef<ButtonSize>>;
10
+ export declare const menuRadioKey: InjectionKey<MenuRadioContext>;
@@ -1,5 +1,6 @@
1
1
  //#region src/vue/utilities/menu.ts
2
2
  var menuCloseKey = Symbol("menu-close");
3
3
  var menuSizeKey = Symbol("menu-size");
4
+ var menuRadioKey = Symbol("menu-radio");
4
5
  //#endregion
5
- export { menuCloseKey, menuSizeKey };
6
+ export { menuCloseKey, menuRadioKey, menuSizeKey };
@@ -0,0 +1,3 @@
1
+ export declare const tooltipState: {
2
+ closedAt: number;
3
+ };
@@ -0,0 +1,4 @@
1
+ //#region src/vue/utilities/tooltip.ts
2
+ var tooltipState = { closedAt: 0 };
3
+ //#endregion
4
+ export { tooltipState };
@@ -0,0 +1,6 @@
1
+ import { type MaybeRefOrGetter, type Ref } from "vue";
2
+ export declare function useAnchor(layer: Ref<HTMLElement | null>, open?: MaybeRefOrGetter<boolean>): {
3
+ anchored: Ref<boolean, boolean>;
4
+ anchorStyle: string;
5
+ layerStyle: string;
6
+ };
@@ -0,0 +1,26 @@
1
+ import { onMounted, ref, toValue, useId, watch } from "vue";
2
+ //#region src/vue/utilities/use-anchor.ts
3
+ function useAnchor(layer, open = true) {
4
+ const name = `--anchor-${useId()}`;
5
+ const anchored = ref(false);
6
+ onMounted(() => {
7
+ anchored.value = typeof HTMLElement.prototype.showPopover === "function" && CSS.supports("anchor-name", name);
8
+ });
9
+ watch([
10
+ layer,
11
+ anchored,
12
+ () => toValue(open)
13
+ ], ([element, supported, value]) => {
14
+ if (!element || !supported) return;
15
+ const shown = element.matches(":popover-open");
16
+ if (value && !shown) element.showPopover();
17
+ else if (!value && shown) element.hidePopover();
18
+ }, { flush: "post" });
19
+ return {
20
+ anchored,
21
+ anchorStyle: `anchor-name: ${name}`,
22
+ layerStyle: `position-anchor: ${name}`
23
+ };
24
+ }
25
+ //#endregion
26
+ export { useAnchor };
@@ -0,0 +1,24 @@
1
+ import type { ConfirmOptions } from "../../components/confirm";
2
+ export declare function useConfirm(): {
3
+ request: Readonly<import("vue").Ref<{
4
+ readonly options: {
5
+ readonly label: string;
6
+ readonly description?: string;
7
+ readonly confirmLabel?: string;
8
+ readonly cancelLabel?: string;
9
+ readonly variant?: import("../..").ConfirmVariant;
10
+ };
11
+ readonly resolve: (value: boolean) => void;
12
+ } | undefined, {
13
+ readonly options: {
14
+ readonly label: string;
15
+ readonly description?: string;
16
+ readonly confirmLabel?: string;
17
+ readonly cancelLabel?: string;
18
+ readonly variant?: import("../..").ConfirmVariant;
19
+ };
20
+ readonly resolve: (value: boolean) => void;
21
+ } | undefined>>;
22
+ confirm: (options: ConfirmOptions) => Promise<boolean>;
23
+ answer: (value: boolean) => void;
24
+ };
@@ -0,0 +1,27 @@
1
+ import { readonly, shallowRef } from "vue";
2
+ //#region src/vue/utilities/use-confirm.ts
3
+ function useConfirm() {
4
+ function confirm(options) {
5
+ request.value?.resolve(false);
6
+ const { promise, resolve } = Promise.withResolvers();
7
+ if (typeof document === "undefined") resolve(false);
8
+ else request.value = {
9
+ options,
10
+ resolve
11
+ };
12
+ return promise;
13
+ }
14
+ function answer(value) {
15
+ const current = request.value;
16
+ request.value = void 0;
17
+ current?.resolve(value);
18
+ }
19
+ return {
20
+ request: readonly(request),
21
+ confirm,
22
+ answer
23
+ };
24
+ }
25
+ var request = shallowRef();
26
+ //#endregion
27
+ export { useConfirm };
@@ -0,0 +1,6 @@
1
+ export declare function useMenuNavigation(): {
2
+ items: (menu: Element | null | undefined) => HTMLElement[];
3
+ focusItem: (menu: Element | null | undefined, index: number) => void;
4
+ handleKeyDown: (event: KeyboardEvent) => void;
5
+ };
6
+ export declare const menuItemSelector = "[role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']";
@@ -0,0 +1,48 @@
1
+ import { isTypeaheadKey, useTypeahead } from "./use-typeahead.js";
2
+ //#region src/vue/utilities/use-menu-navigation.ts
3
+ function useMenuNavigation() {
4
+ const { search, pending } = useTypeahead();
5
+ function items(menu) {
6
+ const list = [];
7
+ for (const item of menu?.querySelectorAll("[role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']") ?? []) if (item.closest("[role='menu']") === menu && item.getAttribute("aria-disabled") !== "true") list.push(item);
8
+ return list;
9
+ }
10
+ function focusItem(menu, index) {
11
+ const list = items(menu);
12
+ list[(index + list.length) % list.length]?.focus();
13
+ }
14
+ function handleKeyDown(event) {
15
+ const target = event.target;
16
+ const menu = target.closest("[role='menu']");
17
+ const list = items(menu);
18
+ const index = list.indexOf(target);
19
+ if (index === -1) return;
20
+ if (event.key === "ArrowDown") {
21
+ event.preventDefault();
22
+ focusItem(menu, index + 1);
23
+ } else if (event.key === "ArrowUp") {
24
+ event.preventDefault();
25
+ focusItem(menu, index - 1);
26
+ } else if (event.key === "Home") {
27
+ event.preventDefault();
28
+ focusItem(menu, 0);
29
+ } else if (event.key === "End") {
30
+ event.preventDefault();
31
+ focusItem(menu, -1);
32
+ } else if (isTypeaheadKey(event) && (event.key !== " " || pending())) {
33
+ const labels = [];
34
+ for (const item of list) labels.push(item.textContent ?? "");
35
+ const match = search(event.key, labels, index);
36
+ event.preventDefault();
37
+ if (match !== -1) list[match].focus();
38
+ }
39
+ }
40
+ return {
41
+ items,
42
+ focusItem,
43
+ handleKeyDown
44
+ };
45
+ }
46
+ var menuItemSelector = "[role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']";
47
+ //#endregion
48
+ export { menuItemSelector, useMenuNavigation };
@@ -0,0 +1,7 @@
1
+ import { type MaybeRefOrGetter, type Ref } from "vue";
2
+ import type { RovingFocusOptions } from "../../components/roving-focus";
3
+ export declare function useRovingFocus(container: Ref<HTMLElement | null>, options?: MaybeRefOrGetter<RovingFocusOptions>): {
4
+ handleKeyDown: (event: KeyboardEvent) => void;
5
+ handleFocusIn: (event: FocusEvent) => void;
6
+ sync: () => void;
7
+ };
@@ -0,0 +1,73 @@
1
+ import { onMounted, onUpdated, toValue } from "vue";
2
+ //#region src/vue/utilities/use-roving-focus.ts
3
+ function useRovingFocus(container, options = {}) {
4
+ let active;
5
+ function items() {
6
+ const { selector = "button, a[href], input, [tabindex]" } = toValue(options);
7
+ const list = [];
8
+ for (const element of container.value?.querySelectorAll(selector) ?? []) {
9
+ if (element.matches(":disabled, [aria-disabled='true'], [type='hidden']") || element.getAttribute("tabindex") === "-1" && element.dataset.rovingItem === void 0 || element.closest("[role='menu'], [role='dialog'], [role='listbox']") !== container.value?.closest("[role='menu'], [role='dialog'], [role='listbox']")) continue;
10
+ list.push(element);
11
+ }
12
+ return list;
13
+ }
14
+ function sync() {
15
+ const list = items();
16
+ if (!container.value?.contains(document.activeElement) || !active || !list.includes(active)) active = list.find((element) => element.matches("[aria-checked='true'], [aria-selected='true'], [aria-pressed='true']")) ?? list[0];
17
+ for (const element of list) {
18
+ element.dataset.rovingItem = "";
19
+ element.tabIndex = element === active ? 0 : -1;
20
+ }
21
+ }
22
+ function focus(element) {
23
+ active = element;
24
+ sync();
25
+ element.focus();
26
+ toValue(options).onMove?.(element);
27
+ }
28
+ function handleFocusIn(event) {
29
+ const list = items();
30
+ const target = event.target;
31
+ if (list.includes(target)) {
32
+ active = target;
33
+ sync();
34
+ }
35
+ }
36
+ function handleKeyDown(event) {
37
+ const target = event.target;
38
+ if (target.matches("input:not([type='checkbox'], [type='radio'], [type='button']), textarea, [contenteditable]")) return;
39
+ const { orientation = "horizontal", loop = true } = toValue(options);
40
+ const list = items();
41
+ const index = list.indexOf(target);
42
+ if (index === -1 || event.altKey || event.ctrlKey || event.metaKey) return;
43
+ const rtl = getComputedStyle(container.value ?? target).direction === "rtl";
44
+ let step = 0;
45
+ if (orientation !== "vertical" && event.key === "ArrowRight") step = rtl ? -1 : 1;
46
+ else if (orientation !== "vertical" && event.key === "ArrowLeft") step = rtl ? 1 : -1;
47
+ else if (orientation !== "horizontal" && event.key === "ArrowDown") step = 1;
48
+ else if (orientation !== "horizontal" && event.key === "ArrowUp") step = -1;
49
+ else if (event.key === "Home") {
50
+ event.preventDefault();
51
+ focus(list[0]);
52
+ return;
53
+ } else if (event.key === "End") {
54
+ event.preventDefault();
55
+ focus(list[list.length - 1]);
56
+ return;
57
+ } else return;
58
+ event.preventDefault();
59
+ let next = index + step;
60
+ if (loop) next = (next + list.length) % list.length;
61
+ const element = list[next];
62
+ if (element) focus(element);
63
+ }
64
+ onMounted(sync);
65
+ onUpdated(sync);
66
+ return {
67
+ handleKeyDown,
68
+ handleFocusIn,
69
+ sync
70
+ };
71
+ }
72
+ //#endregion
73
+ export { useRovingFocus };
@@ -0,0 +1,27 @@
1
+ import type { ToastOptions } from "../../components/toast";
2
+ export declare function useToast(): {
3
+ toasts: Readonly<import("vue").Ref<readonly {
4
+ readonly label: string;
5
+ readonly subtext?: string;
6
+ readonly variant?: import("../..").NotificationVariant;
7
+ readonly duration?: number;
8
+ readonly action?: {
9
+ readonly label: string;
10
+ readonly onSelect: () => void;
11
+ } | undefined;
12
+ readonly id: string;
13
+ }[], readonly {
14
+ readonly label: string;
15
+ readonly subtext?: string;
16
+ readonly variant?: import("../..").NotificationVariant;
17
+ readonly duration?: number;
18
+ readonly action?: {
19
+ readonly label: string;
20
+ readonly onSelect: () => void;
21
+ } | undefined;
22
+ readonly id: string;
23
+ }[]>>;
24
+ add: (options: ToastOptions) => string;
25
+ remove: (id: string) => void;
26
+ clear: () => void;
27
+ };
@@ -0,0 +1,33 @@
1
+ import { readonly, shallowRef } from "vue";
2
+ //#region src/vue/utilities/use-toast.ts
3
+ function useToast() {
4
+ function add(options) {
5
+ if (typeof document === "undefined") return "";
6
+ counter.value += 1;
7
+ const id = `toast-${counter.value}`;
8
+ toasts.value = [...toasts.value, {
9
+ duration: 5e3,
10
+ ...options,
11
+ id
12
+ }];
13
+ return id;
14
+ }
15
+ function remove(id) {
16
+ const list = [];
17
+ for (const toast of toasts.value) if (toast.id !== id) list.push(toast);
18
+ toasts.value = list;
19
+ }
20
+ function clear() {
21
+ toasts.value = [];
22
+ }
23
+ return {
24
+ toasts: readonly(toasts),
25
+ add,
26
+ remove,
27
+ clear
28
+ };
29
+ }
30
+ var counter = shallowRef(0);
31
+ var toasts = shallowRef([]);
32
+ //#endregion
33
+ export { useToast };
@@ -0,0 +1,5 @@
1
+ export declare function useTypeahead(timeout?: number): {
2
+ search: (key: string, labels: Array<string>, current: number) => number;
3
+ pending: () => boolean;
4
+ };
5
+ export declare function isTypeaheadKey(event: KeyboardEvent): boolean;
@@ -0,0 +1,37 @@
1
+ //#region src/vue/utilities/use-typeahead.ts
2
+ function useTypeahead(timeout = 1e3) {
3
+ let buffer = "";
4
+ let timer;
5
+ function search(key, labels, current) {
6
+ clearTimeout(timer);
7
+ buffer += key.toLowerCase();
8
+ timer = setTimeout(() => {
9
+ buffer = "";
10
+ }, timeout);
11
+ let repeated = true;
12
+ for (const character of buffer) if (character !== buffer[0]) {
13
+ repeated = false;
14
+ break;
15
+ }
16
+ const term = repeated ? buffer[0] : buffer;
17
+ let start = current;
18
+ if (repeated || current < 0) start = current + 1;
19
+ for (let offset = 0; offset < labels.length; offset++) {
20
+ const index = (start + offset) % labels.length;
21
+ if (labels[index].trim().toLowerCase().startsWith(term)) return index;
22
+ }
23
+ return -1;
24
+ }
25
+ function pending() {
26
+ return buffer !== "";
27
+ }
28
+ return {
29
+ search,
30
+ pending
31
+ };
32
+ }
33
+ function isTypeaheadKey(event) {
34
+ return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
35
+ }
36
+ //#endregion
37
+ export { isTypeaheadKey, useTypeahead };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revojs/ui",
3
- "version": "0.2.61",
3
+ "version": "0.2.62",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,2 +0,0 @@
1
- import { type MaybeRefOrGetter, type Ref } from "vue";
2
- export declare function useFocusTrap(container: Ref<HTMLElement | null>, active: MaybeRefOrGetter<boolean>): void;