@revojs/ui 0.2.60 → 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 +2 -2
  82. package/dist/vue/utilities/use-focus-trap.d.ts +0 -2
  83. package/dist/vue/utilities/use-focus-trap.js +0 -34
@@ -0,0 +1,13 @@
1
+ import type { IconComponent } from "./icon";
2
+ export type AccordionValue = string | number;
3
+ export interface AccordionItem<Value extends AccordionValue = AccordionValue> {
4
+ value: Value;
5
+ label: string;
6
+ iconPrefix?: IconComponent;
7
+ disabled?: boolean;
8
+ }
9
+ export interface AccordionProps<Value extends AccordionValue = AccordionValue> {
10
+ modelValue?: Array<Value>;
11
+ items: Array<AccordionItem<Value>>;
12
+ multiple?: boolean;
13
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,6 @@
1
1
  export interface CheckboxProps {
2
2
  modelValue?: boolean;
3
+ indeterminate?: boolean;
3
4
  label?: string;
4
5
  description?: string;
5
6
  name?: string;
@@ -0,0 +1,11 @@
1
+ import type { IconComponent } from "./icon";
2
+ import type { StackProps } from "./stack";
3
+ export interface CollapsibleProps {
4
+ modelValue?: boolean;
5
+ label: string;
6
+ iconPrefix?: IconComponent;
7
+ name?: string;
8
+ disabled?: boolean;
9
+ bodyPadding?: StackProps["padding"];
10
+ bodyGap?: StackProps["gap"];
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ export type ConfirmVariant = "primary" | "destructive";
2
+ export interface ConfirmOptions {
3
+ label: string;
4
+ description?: string;
5
+ confirmLabel?: string;
6
+ cancelLabel?: string;
7
+ variant?: ConfirmVariant;
8
+ }
9
+ export interface ConfirmRequest {
10
+ options: ConfirmOptions;
11
+ resolve: (value: boolean) => void;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -4,5 +4,10 @@ export interface CounterProps {
4
4
  min?: number;
5
5
  max?: number;
6
6
  step?: number;
7
+ largeStep?: number;
8
+ name?: string;
9
+ placeholder?: string;
10
+ required?: boolean;
7
11
  disabled?: boolean;
12
+ invalid?: boolean;
8
13
  }
@@ -0,0 +1,24 @@
1
+ export interface UiLabels {
2
+ open: string;
3
+ close: string;
4
+ menu: string;
5
+ select: string;
6
+ selectRequired: string;
7
+ noResults: string;
8
+ empty: string;
9
+ previous: string;
10
+ next: string;
11
+ pagination: string;
12
+ pageNavigation: string;
13
+ fileSelected: string;
14
+ dropFile: string;
15
+ browseFiles: string;
16
+ removeFile: string;
17
+ decrease: string;
18
+ increase: string;
19
+ rating: (value: number, max: number) => string;
20
+ notifications: string;
21
+ confirm: string;
22
+ cancel: string;
23
+ remove: (label: string) => string;
24
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -2,6 +2,7 @@ import type { ButtonSize } from "./button";
2
2
  import type { IconComponent } from "./icon";
3
3
  import type { PopoverProps } from "./popover";
4
4
  export type MenuItemVariant = "default" | "destructive";
5
+ export type MenuRadioValue = string | number;
5
6
  export interface MenuProps extends Omit<PopoverProps, "contentRole" | "contentDirection" | "contentAlign" | "contentJustify" | "contentWrap"> {
6
7
  itemSize?: ButtonSize;
7
8
  }
@@ -14,3 +15,36 @@ export interface MenuItemProps {
14
15
  size?: ButtonSize;
15
16
  disabled?: boolean;
16
17
  }
18
+ export interface MenuCheckboxItemProps {
19
+ modelValue?: boolean;
20
+ label?: string;
21
+ iconPrefix?: IconComponent;
22
+ size?: ButtonSize;
23
+ disabled?: boolean;
24
+ }
25
+ export interface MenuRadioGroupProps<Value extends MenuRadioValue = MenuRadioValue> {
26
+ modelValue?: Value;
27
+ label?: string;
28
+ }
29
+ export interface MenuRadioItemProps<Value extends MenuRadioValue = MenuRadioValue> {
30
+ value: Value;
31
+ label?: string;
32
+ iconPrefix?: IconComponent;
33
+ size?: ButtonSize;
34
+ disabled?: boolean;
35
+ }
36
+ export interface MenuGroupProps {
37
+ label?: string;
38
+ size?: ButtonSize;
39
+ }
40
+ export interface MenuLayerProps {
41
+ label: string;
42
+ iconPrefix?: IconComponent;
43
+ size?: ButtonSize;
44
+ disabled?: boolean;
45
+ }
46
+ export interface ContextMenuProps {
47
+ itemSize?: ButtonSize;
48
+ contentClass?: string;
49
+ disabled?: boolean;
50
+ }
@@ -0,0 +1,17 @@
1
+ import type { RadioValue } from "./radio";
2
+ export type RadioGroupOrientation = "vertical" | "horizontal";
3
+ export interface RadioGroupOption<Value extends RadioValue = RadioValue> {
4
+ value: Value;
5
+ label: string;
6
+ description?: string;
7
+ disabled?: boolean;
8
+ }
9
+ export interface RadioGroupProps<Value extends RadioValue = RadioValue> {
10
+ modelValue?: Value;
11
+ options: Array<RadioGroupOption<Value>>;
12
+ label?: string;
13
+ name?: string;
14
+ orientation?: RadioGroupOrientation;
15
+ required?: boolean;
16
+ disabled?: boolean;
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -4,5 +4,7 @@ export interface RangeProps {
4
4
  min?: number;
5
5
  max?: number;
6
6
  step?: number;
7
+ largeStep?: number;
8
+ valueText?: (value: number) => string;
7
9
  disabled?: boolean;
8
10
  }
@@ -0,0 +1,7 @@
1
+ export type RovingFocusOrientation = "horizontal" | "vertical" | "both";
2
+ export interface RovingFocusOptions {
3
+ selector?: string;
4
+ orientation?: RovingFocusOrientation;
5
+ loop?: boolean;
6
+ onMove?: (element: HTMLElement) => void;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -8,6 +8,7 @@ export type SelectWidth = "full" | "fit";
8
8
  export interface SelectOption<Value extends SelectValue = SelectValue> {
9
9
  label: string;
10
10
  value: Value;
11
+ group?: string;
11
12
  disabled?: boolean;
12
13
  }
13
14
  export interface SelectProps<Value extends SelectValue = SelectValue> {
@@ -0,0 +1,16 @@
1
+ export type StepperValue = string | number;
2
+ export type StepperOrientation = "horizontal" | "vertical";
3
+ export type StepperState = "complete" | "current" | "upcoming";
4
+ export interface StepperItem<Value extends StepperValue = StepperValue> {
5
+ value: Value;
6
+ label: string;
7
+ description?: string;
8
+ disabled?: boolean;
9
+ }
10
+ export interface StepperProps<Value extends StepperValue = StepperValue> {
11
+ modelValue?: Value;
12
+ items: Array<StepperItem<Value>>;
13
+ label?: string;
14
+ orientation?: StepperOrientation;
15
+ linear?: boolean;
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,8 @@
1
1
  import type { IconComponent } from "./icon";
2
2
  export type TabsValue = string | number;
3
3
  export type TabsSize = "md" | "xs";
4
+ export type TabsOrientation = "horizontal" | "vertical";
5
+ export type TabsActivation = "automatic" | "manual";
4
6
  export interface TabsItem<Value extends TabsValue = TabsValue> {
5
7
  value: Value;
6
8
  label: string;
@@ -12,4 +14,6 @@ export interface TabsProps<Value extends TabsValue = TabsValue> {
12
14
  items: Array<TabsItem<Value>>;
13
15
  label?: string;
14
16
  size?: TabsSize;
17
+ orientation?: TabsOrientation;
18
+ activation?: TabsActivation;
15
19
  }
@@ -0,0 +1,10 @@
1
+ export interface TagsInputProps {
2
+ modelValue?: Array<string>;
3
+ label?: string;
4
+ placeholder?: string;
5
+ name?: string;
6
+ max?: number;
7
+ required?: boolean;
8
+ disabled?: boolean;
9
+ invalid?: boolean;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import type { NotificationVariant } from "./notification";
2
+ export interface ToastAction {
3
+ label: string;
4
+ onSelect: () => void;
5
+ }
6
+ export interface ToastOptions {
7
+ label: string;
8
+ subtext?: string;
9
+ variant?: NotificationVariant;
10
+ duration?: number;
11
+ action?: ToastAction;
12
+ }
13
+ export interface Toast extends ToastOptions {
14
+ id: string;
15
+ }
16
+ export interface ToastRegionProps {
17
+ label?: string;
18
+ hotkey?: string;
19
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  import type { IconComponent } from "./icon";
2
+ import type { RovingFocusOrientation } from "./roving-focus";
2
3
  export type ToggleGroupValue = string | number;
3
4
  export type ToggleGroupSize = "md" | "xs";
4
5
  export interface ToggleGroupItem<Value extends ToggleGroupValue = ToggleGroupValue> {
@@ -7,10 +8,12 @@ export interface ToggleGroupItem<Value extends ToggleGroupValue = ToggleGroupVal
7
8
  iconPrefix?: IconComponent;
8
9
  disabled?: boolean;
9
10
  }
10
- export interface ToggleGroupProps<Value extends ToggleGroupValue = ToggleGroupValue> {
11
- modelValue?: Value;
11
+ export interface ToggleGroupProps<Value extends ToggleGroupValue = ToggleGroupValue, Model extends Value | Array<Value> = Value> {
12
+ modelValue?: Model;
12
13
  items: Array<ToggleGroupItem<Value>>;
13
14
  label?: string;
14
15
  size?: ToggleGroupSize;
15
16
  wrap?: boolean;
17
+ multiple?: boolean;
18
+ orientation?: Exclude<RovingFocusOrientation, "both">;
16
19
  }
@@ -0,0 +1,12 @@
1
+ import type { RovingFocusOrientation } from "./roving-focus";
2
+ import type { StackProps } from "./stack";
3
+ export interface ToolbarProps {
4
+ label?: string;
5
+ orientation?: Exclude<RovingFocusOrientation, "both">;
6
+ gap?: StackProps["gap"];
7
+ padding?: StackProps["padding"];
8
+ align?: StackProps["align"];
9
+ justify?: StackProps["justify"];
10
+ wrap?: boolean;
11
+ loop?: boolean;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -4,5 +4,6 @@ export interface TooltipProps {
4
4
  label: string;
5
5
  placement?: TooltipPlacement;
6
6
  align?: TooltipAlign;
7
+ delay?: number;
7
8
  disabled?: boolean;
8
9
  }
package/dist/index.d.ts CHANGED
@@ -1,37 +1,47 @@
1
+ export type * from "./components/accordion";
1
2
  export type * from "./components/badge";
2
3
  export type * from "./components/breadcrumb";
3
4
  export type * from "./components/button";
4
5
  export type * from "./components/card";
5
6
  export type * from "./components/checkbox";
7
+ export type * from "./components/collapsible";
6
8
  export type * from "./components/collection";
9
+ export type * from "./components/confirm";
7
10
  export type * from "./components/container";
8
11
  export type * from "./components/control";
9
12
  export type * from "./components/counter";
10
13
  export type * from "./components/dialog";
14
+ export type * from "./components/entry";
11
15
  export type * from "./components/field";
12
16
  export type * from "./components/heading";
13
- export type * from "./components/shell";
14
17
  export type * from "./components/icon";
15
18
  export type * from "./components/input";
19
+ export type * from "./components/labels";
16
20
  export type * from "./components/link";
17
21
  export type * from "./components/markdown";
18
22
  export type * from "./components/menu";
19
- export type * from "./components/entry";
20
- export type * from "./components/notification";
21
23
  export type * from "./components/navigate";
22
- export type * from "./components/popover";
24
+ export type * from "./components/notification";
23
25
  export type * from "./components/pagination";
26
+ export type * from "./components/popover";
24
27
  export type * from "./components/progress";
25
28
  export type * from "./components/radio";
29
+ export type * from "./components/radio-group";
26
30
  export type * from "./components/range";
27
31
  export type * from "./components/rating";
32
+ export type * from "./components/roving-focus";
28
33
  export type * from "./components/select";
29
34
  export type * from "./components/separator";
30
35
  export type * from "./components/sheet";
36
+ export type * from "./components/shell";
31
37
  export type * from "./components/stack";
38
+ export type * from "./components/stepper";
32
39
  export type * from "./components/switch";
33
40
  export type * from "./components/tabs";
34
- export type * from "./components/upload";
41
+ export type * from "./components/tags-input";
35
42
  export type * from "./components/textarea";
43
+ export type * from "./components/toast";
36
44
  export type * from "./components/toggle-group";
45
+ export type * from "./components/toolbar";
37
46
  export type * from "./components/tooltip";
47
+ export type * from "./components/upload";
package/dist/style.css CHANGED
@@ -49,6 +49,10 @@
49
49
  }
50
50
 
51
51
  @layer base {
52
+ :root {
53
+ color-scheme: light dark;
54
+ }
55
+
52
56
  * {
53
57
  @apply border-border outline-ring;
54
58
  }
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <Stack gap="1">
3
+ <Collapsible
4
+ v-for="item in items"
5
+ :key="item.value"
6
+ :model-value="opened.includes(item.value)"
7
+ :label="item.label"
8
+ :icon-prefix="item.iconPrefix"
9
+ :disabled="item.disabled"
10
+ :name="multiple ? undefined : name"
11
+ @update:model-value="toggle(item.value, $event)"
12
+ >
13
+ <slot :item="item" />
14
+ </Collapsible>
15
+ </Stack>
16
+ </template>
17
+
18
+ <script setup lang="ts" generic="Value extends AccordionValue = AccordionValue">
19
+ import { ref, useId, watch } from "vue";
20
+ import type { AccordionProps, AccordionValue } from "../../components/accordion";
21
+ import Collapsible from "./collapsible.vue";
22
+ import Stack from "./stack.vue";
23
+
24
+ const { modelValue, items, multiple = false } = defineProps<AccordionProps<Value>>();
25
+
26
+ const emit = defineEmits<{
27
+ "update:modelValue": [value: Array<Value>];
28
+ }>();
29
+
30
+ const name = useId();
31
+
32
+ const opened = ref<Array<AccordionValue>>([...(modelValue ?? [])]);
33
+
34
+ function toggle(value: Value, open: boolean) {
35
+ const values: Array<Value> = [];
36
+
37
+ for (const entry of opened.value as Array<Value>) {
38
+ if (entry !== value && (multiple || open === false)) {
39
+ values.push(entry);
40
+ }
41
+ }
42
+
43
+ if (open) {
44
+ values.push(value);
45
+ }
46
+
47
+ opened.value = values;
48
+ emit("update:modelValue", values);
49
+ }
50
+
51
+ watch(
52
+ () => modelValue,
53
+ (value) => {
54
+ opened.value = [...(value ?? [])];
55
+ },
56
+ );
57
+ </script>
@@ -8,9 +8,11 @@
8
8
  :class="interactionVariants[interaction]"
9
9
  >
10
10
  <input
11
+ ref="input"
11
12
  class="peer sr-only"
12
13
  type="checkbox"
13
14
  :checked="modelValue"
15
+ :data-state="state"
14
16
  :name="name"
15
17
  :required="required"
16
18
  :disabled="disabled"
@@ -21,16 +23,17 @@
21
23
  aria-hidden="true"
22
24
  class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-ui border peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ring"
23
25
  :class="[
24
- modelValue ? 'border-transparent bg-primary' : 'border-input bg-background',
26
+ modelValue || indeterminate ? 'border-transparent bg-primary' : 'border-input bg-background',
25
27
  disabled
26
28
  ? ''
27
- : modelValue
29
+ : modelValue || indeterminate
28
30
  ? 'group-hover:bg-primary-hover group-active:bg-primary-active'
29
31
  : 'group-hover:bg-secondary-hover group-active:bg-secondary-active',
30
32
  ]"
31
33
  >
34
+ <span v-if="indeterminate" class="h-0.5 w-2.5 bg-primary-foreground" />
32
35
  <span
33
- v-if="modelValue"
36
+ v-else-if="modelValue"
34
37
  class="mb-px h-2.5 w-1.5 rotate-45 border-b-2 border-r-2 border-primary-foreground"
35
38
  />
36
39
  </span>
@@ -45,13 +48,14 @@
45
48
  </template>
46
49
 
47
50
  <script setup lang="ts">
48
- import { computed } from "vue";
51
+ import { computed, useTemplateRef, watchEffect } from "vue";
49
52
  import type { CheckboxProps } from "../../components/checkbox";
50
53
  import type { ControlInteraction } from "../../components/control.ts";
51
54
  import Stack from "./stack.vue";
52
55
 
53
56
  const {
54
57
  modelValue = false,
58
+ indeterminate = false,
55
59
  label,
56
60
  description,
57
61
  name,
@@ -63,6 +67,22 @@ defineEmits<{
63
67
  "update:modelValue": [value: boolean];
64
68
  }>();
65
69
 
70
+ const input = useTemplateRef("input");
71
+
72
+ const state = computed(() => {
73
+ if (indeterminate) {
74
+ return "indeterminate";
75
+ }
76
+
77
+ return modelValue ? "checked" : "unchecked";
78
+ });
79
+
80
+ watchEffect(() => {
81
+ if (input.value) {
82
+ input.value.indeterminate = indeterminate;
83
+ }
84
+ });
85
+
66
86
  const interaction = computed<ControlInteraction>(() => (disabled ? "disabled" : "enabled"));
67
87
 
68
88
  const interactionVariants: Record<ControlInteraction, string> = {
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <details
3
+ :name="name"
4
+ :open="isOpen"
5
+ class="group/collapsible w-full rounded-ui"
6
+ :data-state="isOpen ? 'open' : 'closed'"
7
+ :data-disabled="disabled ? '' : undefined"
8
+ @toggle="handleToggle"
9
+ >
10
+ <summary
11
+ class="flex cursor-pointer list-none items-center gap-1 rounded-ui p-3 text-sm font-medium outline-ring select-none hover:bg-ghost-hover active:bg-ghost-active focus-visible:outline-2 focus-visible:outline-offset-[-2px] [&::-webkit-details-marker]:hidden"
12
+ :class="disabled ? 'cursor-not-allowed opacity-50 hover:bg-transparent active:bg-transparent' : ''"
13
+ :tabindex="disabled ? -1 : undefined"
14
+ :aria-disabled="disabled || undefined"
15
+ @click="disabled && $event.preventDefault()"
16
+ >
17
+ <Icon v-if="iconPrefix" :is="iconPrefix" :size="20" />
18
+
19
+ <span class="min-w-0 flex-1 truncate px-1">{{ label }}</span>
20
+
21
+ <Icon :is="ArrowDownIcon" :size="20" class="shrink-0 group-open/collapsible:rotate-180" />
22
+ </summary>
23
+
24
+ <Stack :padding="bodyPadding">
25
+ <Stack :gap="bodyGap" class="px-1">
26
+ <slot />
27
+ </Stack>
28
+ </Stack>
29
+ </details>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import ArrowDownIcon from "~icons/hugeicons/arrow-down-01";
34
+ import { ref, watch } from "vue";
35
+ import type { CollapsibleProps } from "../../components/collapsible";
36
+ import Icon from "./icon.vue";
37
+ import Stack from "./stack.vue";
38
+
39
+ const {
40
+ modelValue = undefined,
41
+ label,
42
+ iconPrefix,
43
+ name,
44
+ disabled = false,
45
+ bodyPadding = "3",
46
+ bodyGap = "3",
47
+ } = defineProps<CollapsibleProps>();
48
+
49
+ const emit = defineEmits<{
50
+ "update:modelValue": [value: boolean];
51
+ }>();
52
+
53
+ const isOpen = ref(modelValue ?? false);
54
+
55
+ function handleToggle(event: Event) {
56
+ const value = (event.currentTarget as HTMLDetailsElement).open;
57
+
58
+ if (value !== isOpen.value) {
59
+ isOpen.value = value;
60
+ emit("update:modelValue", value);
61
+ }
62
+ }
63
+
64
+ watch(
65
+ () => modelValue,
66
+ (value) => {
67
+ if (value !== undefined) {
68
+ isOpen.value = value;
69
+ }
70
+ },
71
+ );
72
+ </script>
@@ -12,7 +12,7 @@
12
12
  <slot v-else name="empty">
13
13
  <Stack padding="1">
14
14
  <Stack padding="3" align="center" class="text-sm text-muted-foreground">
15
- <span>{{ emptyLabel }}</span>
15
+ <span>{{ emptyLabel ?? labels.empty }}</span>
16
16
  </Stack>
17
17
  </Stack>
18
18
  </slot>
@@ -25,9 +25,12 @@
25
25
  <script setup generic="T" lang="ts">
26
26
  import type { CollectionColumns, CollectionProps } from "../../components/collection";
27
27
  import type { StackGap } from "../../components/stack";
28
+ import { useUiLabels } from "../utilities/labels";
28
29
  import Stack from "./stack.vue";
29
30
 
30
- const { items, columns = 1, gap = "5", itemKey, emptyLabel = "Nothing found" } = defineProps<CollectionProps<T>>();
31
+ const { items, columns = 1, gap = "5", itemKey, emptyLabel } = defineProps<CollectionProps<T>>();
32
+
33
+ const labels = useUiLabels();
31
34
 
32
35
  defineSlots<{
33
36
  default: (() => unknown) & ((props: { item: T; index: number }) => unknown);
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <Dialog
3
+ :model-value="request !== undefined"
4
+ role="alertdialog"
5
+ :label="request?.options.label"
6
+ :aria-describedby="request?.options.description ? descriptionId : undefined"
7
+ @update:model-value="!$event && answer(false)"
8
+ >
9
+ <p v-if="request?.options.description" :id="descriptionId" class="text-sm text-muted-foreground">
10
+ {{ request.options.description }}
11
+ </p>
12
+
13
+ <template #footer>
14
+ <Stack direction="horizontal" justify="end" gap="1" wrap class="w-full">
15
+ <Button
16
+ autofocus
17
+ variant="outline"
18
+ :label="request?.options.cancelLabel ?? labels.cancel"
19
+ @click="answer(false)"
20
+ />
21
+
22
+ <Button
23
+ :variant="request?.options.variant ?? 'primary'"
24
+ :label="request?.options.confirmLabel ?? labels.confirm"
25
+ @click="answer(true)"
26
+ />
27
+ </Stack>
28
+ </template>
29
+ </Dialog>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import { useId } from "vue";
34
+ import { useUiLabels } from "../utilities/labels";
35
+ import { useConfirm } from "../utilities/use-confirm";
36
+ import Button from "./button.vue";
37
+ import Dialog from "./dialog.vue";
38
+ import Stack from "./stack.vue";
39
+
40
+ const labels = useUiLabels();
41
+
42
+ const { request, answer } = useConfirm();
43
+
44
+ const descriptionId = useId();
45
+ </script>