@xleddyl/443-components-vue 0.3.0 → 0.5.0

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 (109) hide show
  1. package/README.md +52 -25
  2. package/dist/components/core/FieldBoundary.vue +14 -0
  3. package/dist/components/core/FieldBoundary.vue.d.ts +15 -0
  4. package/dist/components/core/Form.vue +91 -12
  5. package/dist/components/core/Form.vue.d.ts +27 -2
  6. package/dist/components/core/FormAlerts.vue +30 -0
  7. package/dist/{internal/forms → components/core}/FormAlerts.vue.d.ts +4 -4
  8. package/dist/components/core/FormField.vue +18 -3
  9. package/dist/components/core/index.d.ts +2 -0
  10. package/dist/components/core/index.mjs +2 -0
  11. package/dist/components/data/FilterPills.vue +82 -37
  12. package/dist/components/data/FilterPills.vue.d.ts +46 -427
  13. package/dist/components/data/SelectionBar.vue +7 -4
  14. package/dist/components/data/Tree.vue +9 -4
  15. package/dist/components/data/Tree.vue.d.ts +4 -1
  16. package/dist/components/data/types.d.ts +7 -2
  17. package/dist/components/forms/CheckboxGroup.vue +2 -1
  18. package/dist/components/forms/FileInput.vue +98 -26
  19. package/dist/components/forms/FileInput.vue.d.ts +28 -10
  20. package/dist/components/forms/RadioGroup.vue +2 -1
  21. package/dist/components/forms/SegmentedControl.vue +2 -1
  22. package/dist/components/forms/types.d.ts +3 -0
  23. package/dist/components/layout/Accordion.vue.d.ts +4 -4
  24. package/dist/components/layout/AppShell.vue +1 -1
  25. package/dist/components/layout/AppShell.vue.d.ts +2 -2
  26. package/dist/components/layout/Tabs.vue +10 -1
  27. package/dist/components/layout/Tabs.vue.d.ts +4 -0
  28. package/dist/components/layout/types.d.ts +1 -0
  29. package/dist/components/overlays/CommandPalette.vue +10 -4
  30. package/dist/components/overlays/CommandPalette.vue.d.ts +273 -511
  31. package/dist/components/overlays/HoverCard.vue +1 -1
  32. package/dist/components/overlays/HoverCard.vue.d.ts +2 -2
  33. package/dist/components/overlays/Popover.vue +1 -1
  34. package/dist/components/overlays/Popover.vue.d.ts +2 -2
  35. package/dist/components/overlays/Toaster.vue +35 -4
  36. package/dist/components/overlays/Toaster.vue.d.ts +3 -0
  37. package/dist/components/overlays/types.d.ts +8 -4
  38. package/dist/components/templates/DashboardLayout.vue +21 -18
  39. package/dist/components/templates/DashboardLayout.vue.d.ts +332 -565
  40. package/dist/components/templates/ListPage.vue +1 -1
  41. package/dist/components/templates/ListPage.vue.d.ts +3 -3
  42. package/dist/components/templates/types.d.ts +4 -4
  43. package/dist/composables/useField.d.ts +6 -1
  44. package/dist/composables/useField.mjs +40 -12
  45. package/dist/composables/useFieldBoundary.d.ts +1 -0
  46. package/dist/composables/useFieldBoundary.mjs +5 -1
  47. package/dist/composables/useFileDrop.d.ts +14 -1
  48. package/dist/composables/useFileDrop.mjs +21 -20
  49. package/dist/composables/useForm.d.ts +4 -0
  50. package/dist/composables/useForm.mjs +62 -22
  51. package/dist/editor/RichTextEditor.vue +2 -1
  52. package/dist/internal/core/ButtonElement.d.ts +2 -0
  53. package/dist/internal/core/ButtonElement.mjs +8 -0
  54. package/dist/internal/forms/files.d.ts +7 -0
  55. package/dist/internal/forms/files.mjs +24 -0
  56. package/dist/internal/keys.d.ts +2 -1
  57. package/dist/internal/keys.mjs +1 -0
  58. package/dist/internal/layout/route.mjs +1 -1
  59. package/dist/locale/en.mjs +4 -1
  60. package/dist/locale/it.mjs +4 -1
  61. package/dist/locale/types.d.ts +3 -0
  62. package/dist/nuxt/module.mjs +1 -1
  63. package/dist/styles/core/button.css +1 -13
  64. package/dist/styles/core/form-field.css +2 -2
  65. package/dist/styles/data/avatar.css +1 -1
  66. package/dist/styles/data/description-list.css +5 -3
  67. package/dist/styles/data/filter-pills.css +0 -1
  68. package/dist/styles/data/pagination.css +0 -1
  69. package/dist/styles/data/selection-bar.css +4 -0
  70. package/dist/styles/data/stat.css +1 -6
  71. package/dist/styles/data/table.css +1 -2
  72. package/dist/styles/data/tag.css +1 -4
  73. package/dist/styles/data/timeline.css +1 -0
  74. package/dist/styles/data/tree.css +8 -2
  75. package/dist/styles/feedback/alert.css +1 -1
  76. package/dist/styles/feedback/empty-state.css +6 -5
  77. package/dist/styles/forms/color-input.css +1 -1
  78. package/dist/styles/forms/combobox.css +0 -1
  79. package/dist/styles/forms/date-picker.css +1 -1
  80. package/dist/styles/forms/file-input.css +9 -1
  81. package/dist/styles/forms/number-input.css +1 -1
  82. package/dist/styles/forms/password-input.css +1 -5
  83. package/dist/styles/forms/switch.css +0 -1
  84. package/dist/styles/layout/accordion.css +7 -2
  85. package/dist/styles/layout/card.css +13 -7
  86. package/dist/styles/layout/copy-button.css +2 -2
  87. package/dist/styles/layout/nav-link.css +1 -2
  88. package/dist/styles/layout/page-header.css +2 -1
  89. package/dist/styles/layout/sidebar-nav.css +1 -1
  90. package/dist/styles/layout/sidebar.css +1 -1
  91. package/dist/styles/layout/tabs.css +22 -2
  92. package/dist/styles/layout/theme-switch.css +1 -3
  93. package/dist/styles/overlays/command.css +1 -1
  94. package/dist/styles/overlays/confirm.css +0 -1
  95. package/dist/styles/overlays/drawer.css +23 -2
  96. package/dist/styles/overlays/menu.css +2 -4
  97. package/dist/styles/overlays/modal.css +4 -3
  98. package/dist/styles/overlays/toast.css +0 -1
  99. package/dist/styles/templates/auth-layout.css +4 -1
  100. package/dist/styles/templates/empty-page.css +7 -3
  101. package/dist/styles/templates/error-page.css +4 -0
  102. package/dist/styles/templates/form-page.css +1 -1
  103. package/dist/styles/templates/page.css +1 -1
  104. package/dist/styles/templates/settings-modal.css +4 -2
  105. package/dist/styles/tokens.css +1 -1
  106. package/package.json +1 -1
  107. package/dist/internal/forms/FormAlerts.vue +0 -25
  108. package/dist/internal/overlays/FieldBoundary.d.ts +0 -4
  109. package/dist/internal/overlays/FieldBoundary.mjs +0 -9
@@ -17,7 +17,7 @@
17
17
 
18
18
  <script setup>
19
19
  import { useTemplateRef } from "vue";
20
- import FieldBoundary from "../../internal/overlays/FieldBoundary";
20
+ import FieldBoundary from "../core/FieldBoundary.vue";
21
21
  import { useHoverTrigger } from "../../internal/overlays/hover";
22
22
  import { ARROW_OFFSET, syncOpenModel, useFloatingLayer } from "../../internal/floating/layer";
23
23
  import { useSlotTrigger } from "../../internal/floating/trigger";
@@ -206,7 +206,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
206
206
  is?: string | undefined | undefined;
207
207
  exportparts?: string | undefined;
208
208
  part?: string | undefined;
209
- popover?: (import("../..").PopoverMode | "" | boolean) | undefined;
209
+ popover?: (import("../../index.ts").PopoverMode | "" | boolean) | undefined;
210
210
  }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
211
211
  "update:open": (value: boolean) => any;
212
212
  }, string, import("vue").PublicProps, Readonly<{
@@ -408,7 +408,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
408
408
  is?: string | undefined | undefined;
409
409
  exportparts?: string | undefined;
410
410
  part?: string | undefined;
411
- popover?: (import("../..").PopoverMode | "" | boolean) | undefined;
411
+ popover?: (import("../../index.ts").PopoverMode | "" | boolean) | undefined;
412
412
  }> & Readonly<{
413
413
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
414
414
  }>, {
@@ -21,7 +21,7 @@
21
21
 
22
22
  <script setup>
23
23
  import { useId, useTemplateRef, watch } from "vue";
24
- import FieldBoundary from "../../internal/overlays/FieldBoundary";
24
+ import FieldBoundary from "../core/FieldBoundary.vue";
25
25
  import { focusFirst } from "../../internal/overlays/focus";
26
26
  import { ARROW_OFFSET, syncOpenModel, useFloatingLayer } from "../../internal/floating/layer";
27
27
  import { setAttributes, useInvoker, useSlotTrigger } from "../../internal/floating/trigger";
@@ -209,7 +209,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
209
209
  is?: string | undefined | undefined;
210
210
  exportparts?: string | undefined;
211
211
  part?: string | undefined;
212
- popover?: (import("../..").PopoverMode | "" | boolean) | undefined;
212
+ popover?: (import("../../index.ts").PopoverMode | "" | boolean) | undefined;
213
213
  }, {
214
214
  show: () => void;
215
215
  hide: () => void;
@@ -415,7 +415,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
415
415
  is?: string | undefined | undefined;
416
416
  exportparts?: string | undefined;
417
417
  part?: string | undefined;
418
- popover?: (import("../..").PopoverMode | "" | boolean) | undefined;
418
+ popover?: (import("../../index.ts").PopoverMode | "" | boolean) | undefined;
419
419
  }> & Readonly<{
420
420
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
421
421
  }>, {
@@ -79,7 +79,7 @@
79
79
  variant="ghost"
80
80
  tone="neutral"
81
81
  :aria-label="dismissText"
82
- @click="dismiss(entry.id)"
82
+ @click="dismissFromToast(entry.id)"
83
83
  />
84
84
  </div>
85
85
  <span
@@ -122,6 +122,7 @@ const props = defineProps({
122
122
  position: { type: String, required: false, default: "bottom-right" },
123
123
  max: { type: Number, required: false, default: 4 },
124
124
  duration: { type: Number, required: false, default: 4e3 },
125
+ actionDuration: { type: Number, required: false, default: 1e4 },
125
126
  gap: { type: Number, required: false, default: 8 },
126
127
  expand: { type: Boolean, required: false },
127
128
  progress: { type: Boolean, required: false },
@@ -274,7 +275,8 @@ function syncEntries() {
274
275
  }
275
276
  const timers = /* @__PURE__ */ new Map();
276
277
  function durationOf(item) {
277
- return item.duration ?? props.duration;
278
+ if (item.duration !== void 0) return item.duration;
279
+ return item.action ? Math.max(props.duration, props.actionDuration) : props.duration;
278
280
  }
279
281
  function hasTimer(item) {
280
282
  const duration = durationOf(item);
@@ -343,8 +345,15 @@ function resumeTimers() {
343
345
  for (const [id, timer] of timers) if (timer.handle === void 0) run(id, timer);
344
346
  }
345
347
  watch(paused, (value) => value ? pauseTimers() : resumeTimers());
348
+ function actionHint(item) {
349
+ if (!item.action || !props.hotkey) return "";
350
+ return t("toaster.actionHint", {
351
+ hotkey: ariaKeyShortcuts(props.hotkey, false),
352
+ action: item.action.label
353
+ });
354
+ }
346
355
  function announce(item) {
347
- const text = [item.title, ...descriptionLines(item)].filter(Boolean).join(". ");
356
+ const text = [item.title, ...descriptionLines(item), actionHint(item)].filter(Boolean).join(". ");
348
357
  const target = item.tone === "danger" ? assertiveMessage : politeMessage;
349
358
  target.value = "";
350
359
  nextTick(() => target.value = text);
@@ -408,9 +417,29 @@ watch(active, async (value) => {
408
417
  function dismiss(id) {
409
418
  toast.dismiss(id);
410
419
  }
420
+ let returnFocus = null;
421
+ function focusInToaster() {
422
+ const active2 = document.activeElement;
423
+ return Boolean(active2 && toasterRef.value?.contains(active2));
424
+ }
425
+ function restoreFocus() {
426
+ const target = returnFocus;
427
+ returnFocus = null;
428
+ const next = toasterRef.value?.querySelector(
429
+ '.k-toast[data-visible="true"]:not([data-removed="true"]) button'
430
+ );
431
+ if (target?.isConnected) target.focus({ preventScroll: true });
432
+ else if (next) next.focus({ preventScroll: true });
433
+ if (!focusInToaster()) focusInside.value = false;
434
+ }
435
+ function dismissFromToast(id) {
436
+ const hadFocus = focusInToaster();
437
+ dismiss(id);
438
+ if (hadFocus) nextTick(restoreFocus);
439
+ }
411
440
  function runAction(item, event) {
412
441
  item.action?.onClick(event);
413
- if (!item.action?.keepOpen) dismiss(item.id);
442
+ if (!item.action?.keepOpen) dismissFromToast(item.id);
414
443
  }
415
444
  function onPointerEnter(event) {
416
445
  if (event.pointerType === "mouse") hovered.value = true;
@@ -451,6 +480,8 @@ function onVisibilityChange() {
451
480
  useHotkey(
452
481
  () => props.hotkey ? [props.hotkey] : [],
453
482
  () => {
483
+ if (!focusInToaster())
484
+ returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
454
485
  const first = toasterRef.value?.querySelector(
455
486
  '.k-toast[data-visible="true"] button'
456
487
  );
@@ -3,6 +3,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
3
3
  position?: ToasterPosition;
4
4
  max?: number;
5
5
  duration?: number;
6
+ actionDuration?: number;
6
7
  gap?: number;
7
8
  expand?: boolean;
8
9
  progress?: boolean;
@@ -214,6 +215,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
214
215
  position?: ToasterPosition;
215
216
  max?: number;
216
217
  duration?: number;
218
+ actionDuration?: number;
217
219
  gap?: number;
218
220
  expand?: boolean;
219
221
  progress?: boolean;
@@ -426,6 +428,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
426
428
  duration: number;
427
429
  position: ToasterPosition;
428
430
  gap: number;
431
+ actionDuration: number;
429
432
  hotkey: string | false;
430
433
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
431
434
  declare const _default: typeof __VLS_export;
@@ -10,6 +10,7 @@ export interface ToasterOptions {
10
10
  position?: ToasterPosition;
11
11
  max?: number;
12
12
  duration?: number;
13
+ actionDuration?: number;
13
14
  gap?: number;
14
15
  expand?: boolean;
15
16
  progress?: boolean;
@@ -60,7 +61,7 @@ export interface MenuSeparator {
60
61
  export type MenuItem = MenuActionItem | MenuCheckboxItem | MenuRadioItem;
61
62
  export type MenuEntry = MenuItem | MenuLabel | MenuSeparator;
62
63
  export type MenuItems = MenuEntry[] | MenuEntry[][];
63
- export interface CommandItem {
64
+ export interface CommandItem<T = unknown> {
64
65
  id: string;
65
66
  label: string;
66
67
  description?: string;
@@ -70,11 +71,14 @@ export interface CommandItem {
70
71
  to?: LinkTarget;
71
72
  href?: string;
72
73
  disabled?: boolean;
73
- onSelect?: (item: CommandItem) => void;
74
+ data?: T;
75
+ onSelect?: (item: CommandItem<T>) => void;
74
76
  }
75
- export interface CommandGroup {
77
+ export interface CommandGroup<T = unknown> {
76
78
  id: string;
77
79
  label?: string;
78
80
  description?: string;
79
- items: CommandItem[];
81
+ items: CommandItem<T>[];
80
82
  }
83
+ export type CommandSearch<T = unknown> = (query: string, signal: AbortSignal) => CommandGroup<T>[] | Promise<CommandGroup<T>[]>;
84
+ export type CommandEmptyText = string | ((query: string) => string);
@@ -45,12 +45,17 @@
45
45
  :search="searchFunction"
46
46
  :placeholder="searchProps?.placeholder ?? searchPlaceholderText"
47
47
  >
48
- <template
49
- v-for="entry in searchSlots"
50
- :key="entry.source"
51
- #[entry.target]="scope"
52
- >
53
- <slot :name="entry.source" v-bind="scope" />
48
+ <template v-if="$slots['search-item']" #item="scope">
49
+ <slot name="search-item" v-bind="scope" />
50
+ </template>
51
+ <template v-if="$slots['search-empty']" #empty="scope">
52
+ <slot name="search-empty" v-bind="scope" />
53
+ </template>
54
+ <template v-if="$slots['search-group-label']" #group-label="scope">
55
+ <slot name="search-group-label" v-bind="scope" />
56
+ </template>
57
+ <template v-if="$slots['search-after-results']" #after-results="scope">
58
+ <slot name="search-after-results" v-bind="scope" />
54
59
  </template>
55
60
  </CommandPalette>
56
61
  </slot>
@@ -138,6 +143,9 @@
138
143
  :title="settingsTitleText"
139
144
  :on-sign-out="onSignOut"
140
145
  >
146
+ <template v-if="$slots['settings-account-fields']" #account-fields="scope">
147
+ <slot name="settings-account-fields" v-bind="scope" />
148
+ </template>
141
149
  <template v-for="entry in settingsSlots" :key="entry.source" #[entry.target]="scope">
142
150
  <slot :name="entry.source" v-bind="scope" />
143
151
  </template>
@@ -147,7 +155,7 @@
147
155
  </template>
148
156
 
149
157
  <script setup>
150
- import { computed, ref, useSlots, useTemplateRef } from "vue";
158
+ import { computed, ref, useTemplateRef } from "vue";
151
159
  import { useLocale } from "../../composables/useLocale";
152
160
  import { navCommands, toNavSections } from "../../internal/layout/navigation";
153
161
  import { matchesSearch } from "../../internal/core/text";
@@ -200,6 +208,7 @@ const props = defineProps({
200
208
  themeLabel: { type: String, required: false }
201
209
  });
202
210
  const emit = defineEmits(["accountSelect"]);
211
+ const slots = defineSlots();
203
212
  const collapsed = defineModel("collapsed", { type: Boolean, ...{ default: false } });
204
213
  const open = defineModel("open", { type: Boolean, ...{ default: false } });
205
214
  const settingsOpen = defineModel("settingsOpen", { type: Boolean, ...{ default: false } });
@@ -212,7 +221,6 @@ const searchPlaceholderText = computed(
212
221
  const navigationGroupLabel = computed(
213
222
  () => props.searchNavigationLabel ?? t("dashboardLayout.pages")
214
223
  );
215
- const slots = useSlots();
216
224
  const shell = useTemplateRef("shell");
217
225
  const searchOpen = ref(false);
218
226
  const hasDesktopTopbar = computed(
@@ -228,21 +236,16 @@ const hasSidebarFooter = computed(
228
236
  const hasSettings = computed(
229
237
  () => props.accountAction === "settings" && Boolean(props.account || props.settingsSections)
230
238
  );
231
- const SETTINGS_SLOT_PREFIX = "settings-";
239
+ const SETTINGS_SLOT_PATTERN = /^settings-(section|actions)-./;
232
240
  const settingsSlots = computed(
233
- () => Object.keys(slots).filter((name) => name.startsWith(SETTINGS_SLOT_PREFIX)).map((name) => ({
241
+ () => Object.keys(slots).filter((name) => SETTINGS_SLOT_PATTERN.test(name)).map((name) => ({
234
242
  source: name,
235
- target: name.slice(SETTINGS_SLOT_PREFIX.length)
243
+ target: name.slice("settings-".length)
236
244
  }))
237
245
  );
238
- const SEARCH_SLOT_PREFIX = "search-";
239
- const searchSlots = computed(
240
- () => Object.keys(slots).filter((name) => name.startsWith(SEARCH_SLOT_PREFIX)).map((name) => ({
241
- source: name,
242
- target: name.slice(SEARCH_SLOT_PREFIX.length)
243
- }))
246
+ const navigationCommands = computed(
247
+ () => navCommands(toNavSections(props.navigation))
244
248
  );
245
- const navigationCommands = computed(() => navCommands(toNavSections(props.navigation)));
246
249
  const searchFunction = computed(() => {
247
250
  const search = props.search;
248
251
  if (typeof search !== "function") return void 0;