nuxt-ui-tools 1.4.0 → 1.6.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 (97) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +3 -1
  3. package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +8 -3
  4. package/dist/runtime/dashboard/types/filters.d.ts +7 -1
  5. package/dist/runtime/dashboard/utils/builders/dashboard-filters.js +19 -2
  6. package/dist/runtime/form/composables/use-field-options.js +19 -0
  7. package/dist/runtime/form/composables/use-remote-field-options.js +21 -2
  8. package/dist/runtime/form/types/options.d.ts +22 -2
  9. package/dist/runtime/i18n/locales/en.js +23 -2
  10. package/dist/runtime/i18n/locales/fr.js +23 -2
  11. package/dist/runtime/i18n/types.d.ts +21 -0
  12. package/dist/runtime/shared/index.d.ts +1 -0
  13. package/dist/runtime/shared/index.js +1 -0
  14. package/dist/runtime/shared/types/query.d.ts +5 -1
  15. package/dist/runtime/shared/types/remote-options.d.ts +56 -0
  16. package/dist/runtime/shared/utils/define-remote-options.d.ts +23 -0
  17. package/dist/runtime/shared/utils/define-remote-options.js +24 -0
  18. package/dist/runtime/shared/utils/remote-options-query.d.ts +6 -0
  19. package/dist/runtime/shared/utils/remote-options-query.js +18 -0
  20. package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
  21. package/dist/runtime/table/components/data-list/data-list-error-state.d.vue.ts +18 -1
  22. package/dist/runtime/table/components/data-list/data-list-error-state.vue +65 -41
  23. package/dist/runtime/table/components/data-list/data-list-error-state.vue.d.ts +18 -1
  24. package/dist/runtime/table/components/data-list/data-list-filter-tags.d.vue.ts +1 -1
  25. package/dist/runtime/table/components/data-list/data-list-filter-tags.vue +1 -1
  26. package/dist/runtime/table/components/data-list/data-list-filter-tags.vue.d.ts +1 -1
  27. package/dist/runtime/table/components/data-list/sort-menu-panel.vue +33 -25
  28. package/dist/runtime/table/components/drawers/column-panel.vue +19 -6
  29. package/dist/runtime/table/components/filters/panel/filter-panel.vue +14 -2
  30. package/dist/runtime/table/components/filters/shared/dynamic-filter-picker.vue +1 -1
  31. package/dist/runtime/table/components/filters/shared/filter-editor-header.d.vue.ts +16 -0
  32. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue +46 -0
  33. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue.d.ts +16 -0
  34. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.d.vue.ts +3 -1
  35. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue +6 -5
  36. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue.d.ts +3 -1
  37. package/dist/runtime/table/components/filters/shared/filter-option-picker-content.vue +4 -1
  38. package/dist/runtime/table/components/filters/shared/filter-option-single-list.vue +4 -4
  39. package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue +12 -8
  40. package/dist/runtime/table/components/filters/shared/filter-popover-shell.vue +10 -1
  41. package/dist/runtime/table/components/filters/shared/filter-searchable-panel.vue +14 -6
  42. package/dist/runtime/table/components/filters/shared/filter-trigger-tag.vue +12 -2
  43. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.d.vue.ts +6 -1
  44. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue +19 -5
  45. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue.d.ts +6 -1
  46. package/dist/runtime/table/components/filters/tags/date-filter-tag.d.vue.ts +6 -1
  47. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue +65 -29
  48. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue.d.ts +6 -1
  49. package/dist/runtime/table/components/filters/tags/number-filter-tag.d.vue.ts +6 -1
  50. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue +66 -18
  51. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue.d.ts +6 -1
  52. package/dist/runtime/table/components/filters/tags/option-filter-tag.d.vue.ts +2 -2
  53. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue +10 -34
  54. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue.d.ts +2 -2
  55. package/dist/runtime/table/components/filters/tags/text-filter-tag.d.vue.ts +6 -1
  56. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue +39 -14
  57. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue.d.ts +6 -1
  58. package/dist/runtime/table/components/grid/grid-renderer.d.vue.ts +8 -2
  59. package/dist/runtime/table/components/grid/grid-renderer.vue +47 -115
  60. package/dist/runtime/table/components/grid/grid-renderer.vue.d.ts +8 -2
  61. package/dist/runtime/table/components/layout/progress-line.d.vue.ts +9 -0
  62. package/dist/runtime/table/components/layout/progress-line.vue +43 -0
  63. package/dist/runtime/table/components/layout/progress-line.vue.d.ts +9 -0
  64. package/dist/runtime/table/components/table/table-empty-state.d.vue.ts +1 -1
  65. package/dist/runtime/table/components/table/table-empty-state.vue +1 -1
  66. package/dist/runtime/table/components/table/table-empty-state.vue.d.ts +1 -1
  67. package/dist/runtime/table/components/table/table-layout.d.ts +57 -0
  68. package/dist/runtime/table/components/table/table-layout.js +23 -0
  69. package/dist/runtime/table/components/table/table-renderer.d.vue.ts +8 -16
  70. package/dist/runtime/table/components/table/table-renderer.vue +180 -274
  71. package/dist/runtime/table/components/table/table-renderer.vue.d.ts +8 -16
  72. package/dist/runtime/table/components/table/table-row.d.vue.ts +19 -0
  73. package/dist/runtime/table/components/table/table-row.vue +51 -0
  74. package/dist/runtime/table/components/table/table-row.vue.d.ts +19 -0
  75. package/dist/runtime/table/components/table/table-skeleton-cell.d.ts +9 -0
  76. package/dist/runtime/table/components/table/table-skeleton-cell.js +104 -0
  77. package/dist/runtime/table/components/utils/search-query-input.vue +27 -1
  78. package/dist/runtime/table/composables/use-deferred-commit.d.ts +20 -0
  79. package/dist/runtime/table/composables/use-deferred-commit.js +31 -0
  80. package/dist/runtime/table/composables/use-table-data.d.ts +5 -1
  81. package/dist/runtime/table/composables/use-table-data.js +20 -2
  82. package/dist/runtime/table/composables/use-table-filter-selected-options.js +16 -1
  83. package/dist/runtime/table/composables/use-table-summaries.js +10 -1
  84. package/dist/runtime/table/types/columns.d.ts +15 -1
  85. package/dist/runtime/table/types/data-list.d.ts +1 -0
  86. package/dist/runtime/table/types/filters.d.ts +7 -0
  87. package/dist/runtime/table/types/remote-options.d.ts +3 -16
  88. package/dist/runtime/table/utils/columns/defs.js +1 -1
  89. package/dist/runtime/table/utils/filters/date.d.ts +6 -0
  90. package/dist/runtime/table/utils/filters/date.js +26 -2
  91. package/dist/runtime/table/utils/filters/index.d.ts +1 -0
  92. package/dist/runtime/table/utils/filters/index.js +1 -0
  93. package/dist/runtime/table/utils/filters/number.d.ts +5 -0
  94. package/dist/runtime/table/utils/filters/number.js +11 -0
  95. package/dist/runtime/table/utils/filters/ui.js +5 -5
  96. package/dist/runtime/table/utils/remote-table-options.js +37 -37
  97. package/package.json +1 -1
@@ -4,11 +4,15 @@ import UIcon from "@nuxt/ui/components/Icon.vue";
4
4
  import { computed } from "vue";
5
5
  import { useUiToolsLocale } from "#ui-tools/i18n";
6
6
  import { useDataListUi } from "../../composables/use-data-list-ui";
7
- import { mergeDataListUiClass, resolveDataListControlGeometry } from "../../utils";
7
+ import { mergeDataListUiClass } from "../../utils";
8
8
  const props = defineProps({
9
9
  minHeight: { type: String, required: false },
10
10
  size: { type: String, required: false },
11
- ui: { type: Object, required: false }
11
+ ui: { type: Object, required: false },
12
+ title: { type: String, required: false },
13
+ description: { type: String, required: false },
14
+ reference: { type: String, required: false },
15
+ icon: { type: String, required: false }
12
16
  });
13
17
  const emit = defineEmits(["retry"]);
14
18
  const dataListUi = useDataListUi();
@@ -17,75 +21,95 @@ const rootUi = computed(() => dataListUi.ui.value.content?.ui);
17
21
  const resolvedSize = computed(
18
22
  () => props.size ?? dataListUi.ui.value.content?.size ?? dataListUi.controlSize.value
19
23
  );
20
- const geometry = computed(() => resolveDataListControlGeometry(resolvedSize.value));
24
+ const resolvedTitle = computed(() => props.title ?? t("table.states.gridError.title"));
25
+ const resolvedDescription = computed(
26
+ () => props.description ?? t("table.states.gridError.description")
27
+ );
21
28
  </script>
22
29
 
23
30
  <template>
24
31
  <div
25
32
  :class="
26
33
  mergeDataListUiClass(
27
- 'grid place-items-center px-4 py-10 text-center',
34
+ 'nut-dl-error flex w-full flex-col items-center justify-center px-6 py-12 text-center',
28
35
  rootUi?.error,
29
36
  ui?.error
30
37
  )
31
38
  "
32
39
  :style="{ minHeight }"
40
+ role="alert"
33
41
  >
34
42
  <div
35
43
  :class="
36
44
  mergeDataListUiClass(
37
- `flex max-w-md items-start text-left ${geometry.toolbarGap}`,
38
- rootUi?.errorBody,
39
- ui?.errorBody
45
+ 'nut-dl-error__art relative mb-4 flex size-14 items-center justify-center',
46
+ rootUi?.errorIcon,
47
+ ui?.errorIcon
40
48
  )
41
49
  "
42
50
  >
43
51
  <span
52
+ class="nut-dl-error__ring absolute inset-0 rounded-2xl bg-error/10 ring ring-inset ring-error/15"
53
+ />
54
+ <span
55
+ class="nut-dl-error__ring nut-dl-error__ring--back absolute inset-1.5 -z-10 -rotate-6 rounded-xl bg-error/5"
56
+ />
57
+ <UIcon :name="icon ?? 'i-lucide-cloud-alert'" class="relative size-5 text-error" />
58
+ </div>
59
+ <div :class="mergeDataListUiClass('min-w-0 max-w-md', rootUi?.errorCopy, ui?.errorCopy)">
60
+ <div
44
61
  :class="
45
62
  mergeDataListUiClass(
46
- 'grid size-9 shrink-0 place-items-center rounded-md bg-error/10 text-error',
47
- rootUi?.errorIcon,
48
- ui?.errorIcon
63
+ 'nut-dl-error__title text-[14px] font-medium text-highlighted',
64
+ rootUi?.errorTitle,
65
+ ui?.errorTitle
49
66
  )
50
67
  "
51
68
  >
52
- <UIcon name="i-lucide-cloud-alert" class="size-4" />
53
- </span>
54
- <div
69
+ {{ resolvedTitle }}
70
+ </div>
71
+ <p
55
72
  :class="
56
- mergeDataListUiClass(`min-w-0 flex-1 ${geometry.text}`, rootUi?.errorCopy, ui?.errorCopy)
57
- "
58
- >
59
- <div
60
- :class="
61
- mergeDataListUiClass('font-medium text-highlighted', rootUi?.errorTitle, ui?.errorTitle)
62
- "
63
- >
64
- {{ t("table.states.gridError.title") }}
65
- </div>
66
- <p
67
- :class="
68
73
  mergeDataListUiClass(
69
- 'mt-0.5 leading-5 text-muted',
74
+ 'nut-dl-error__description mx-auto mt-1 max-w-[40ch] text-[12.5px] leading-relaxed text-muted',
70
75
  rootUi?.errorDescription,
71
76
  ui?.errorDescription
72
77
  )
73
78
  "
74
- >
75
- {{ t("table.states.gridError.description") }}
76
- </p>
77
- <UButton
78
- color="neutral"
79
- variant="soft"
80
- :size="resolvedSize"
81
- icon="i-lucide-refresh-cw"
82
- class="mt-3"
83
- :ui="{ base: mergeDataListUiClass(rootUi?.retry, ui?.retry) }"
84
- @click="emit('retry')"
85
- >
86
- {{ t("table.states.gridError.action") }}
87
- </UButton>
88
- </div>
79
+ >
80
+ {{ resolvedDescription }}
81
+ </p>
82
+ <code
83
+ v-if="reference"
84
+ class="nut-dl-error__reference mt-2.5 inline-block rounded-md bg-elevated px-2 py-1 font-mono text-[11.5px] text-muted select-all"
85
+ >
86
+ {{ reference }}
87
+ </code>
88
+ </div>
89
+ <div
90
+ :class="
91
+ mergeDataListUiClass(
92
+ 'nut-dl-error__actions mt-4 flex items-center gap-2',
93
+ rootUi?.errorBody,
94
+ ui?.errorBody
95
+ )
96
+ "
97
+ >
98
+ <UButton
99
+ color="neutral"
100
+ variant="outline"
101
+ :size="resolvedSize"
102
+ icon="i-lucide-refresh-cw"
103
+ :ui="{ base: mergeDataListUiClass(rootUi?.retry, ui?.retry) }"
104
+ @click="emit('retry')"
105
+ >
106
+ {{ t("table.states.gridError.action") }}
107
+ </UButton>
108
+ <slot name="actions" />
89
109
  </div>
90
110
  </div>
91
111
  </template>
112
+
113
+ <style>
114
+ .nut-dl-error{animation:nut-dl-error-in .24s cubic-bezier(.2,.9,.3,1) both}@keyframes nut-dl-error-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}@media (prefers-reduced-motion:reduce){.nut-dl-error{animation:none}}
115
+ </style>
@@ -3,11 +3,28 @@ type __VLS_Props = {
3
3
  minHeight?: string;
4
4
  size?: DataListControlSize;
5
5
  ui?: DataListContentUi;
6
+ /** Replaces the localized default title. */
7
+ title?: string;
8
+ /** Replaces the localized default description. */
9
+ description?: string;
10
+ /** Support reference shown under the description, such as a request identifier. */
11
+ reference?: string;
12
+ icon?: string;
6
13
  };
7
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ declare var __VLS_14: {};
15
+ type __VLS_Slots = {} & {
16
+ actions?: (props: typeof __VLS_14) => any;
17
+ };
18
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
8
19
  retry: () => any;
9
20
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
10
21
  onRetry?: (() => any) | undefined;
11
22
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
12
24
  declare const _default: typeof __VLS_export;
13
25
  export default _default;
26
+ type __VLS_WithSlots<T, S> = T & {
27
+ new (): {
28
+ $slots: S;
29
+ };
30
+ };
@@ -2,7 +2,7 @@ import type { DataListControlSize, DataListFilterTagsUi } from '../../types/inde
2
2
  type __VLS_Props = {
3
3
  showAdd?: boolean;
4
4
  showClear?: boolean;
5
- /** `sheet` collapses the tags into a bottom sheet on mobile; `tags` keeps inline tags everywhere. */
5
+ /** `tags` keeps inline tags on mobile (default); `sheet` moves all filters into a bottom sheet. */
6
6
  mobile?: 'sheet' | 'tags';
7
7
  size?: DataListControlSize;
8
8
  ui?: DataListFilterTagsUi;
@@ -9,7 +9,7 @@ import FilterTagsBar from "../filters/tags/filter-tags-bar.vue";
9
9
  const props = defineProps({
10
10
  showAdd: { type: Boolean, required: false, default: false },
11
11
  showClear: { type: Boolean, required: false, default: false },
12
- mobile: { type: String, required: false, default: "sheet" },
12
+ mobile: { type: String, required: false, default: "tags" },
13
13
  size: { type: String, required: false },
14
14
  ui: { type: Object, required: false }
15
15
  });
@@ -2,7 +2,7 @@ import type { DataListControlSize, DataListFilterTagsUi } from '../../types/inde
2
2
  type __VLS_Props = {
3
3
  showAdd?: boolean;
4
4
  showClear?: boolean;
5
- /** `sheet` collapses the tags into a bottom sheet on mobile; `tags` keeps inline tags everywhere. */
5
+ /** `tags` keeps inline tags on mobile (default); `sheet` moves all filters into a bottom sheet. */
6
6
  mobile?: 'sheet' | 'tags';
7
7
  size?: DataListControlSize;
8
8
  ui?: DataListFilterTagsUi;
@@ -31,7 +31,7 @@ function humanize(value) {
31
31
  v-for="key in keys"
32
32
  :key="key"
33
33
  type="button"
34
- class="nut-dl-sort__row flex w-full items-center gap-[10px] rounded-lg text-left transition-colors hover:bg-elevated active:bg-elevated"
34
+ class="nut-dl-sort__row flex w-full items-center gap-[10px] rounded-lg text-left outline-none transition-colors hover:bg-elevated focus-visible:ring-2 focus-visible:ring-primary/40 active:bg-elevated"
35
35
  :class="[
36
36
  compact ? 'h-8 px-2.5 text-[13px]' : 'h-[46px] px-[10px] text-[15px]',
37
37
  activeKey === key ? 'nut-dl-sort__row--active font-semibold text-highlighted' : 'text-default'
@@ -52,35 +52,43 @@ function humanize(value) {
52
52
  />
53
53
  </button>
54
54
  </div>
55
- <div
56
- class="nut-dl-sort__caption border-t border-default font-semibold tracking-[0.06em] text-dimmed uppercase"
57
- :class="
58
- compact ? 'mx-1 mt-1.5 px-1.5 pt-2 pb-1 text-[10.5px]' : 'mt-2 px-5 pt-4 pb-2 text-[11px]'
59
- "
60
- >
61
- {{ t("table.filters.sheet.order") }}
55
+ <div class="w-full min-w-0" :class="compact ? 'mt-1.5 px-1' : 'mt-2'">
56
+ <div
57
+ class="nut-dl-sort__caption border-t border-default font-semibold tracking-[0.06em] text-dimmed uppercase"
58
+ :class="compact ? 'px-1.5 pt-2 pb-1 text-[10.5px]' : 'px-5 pt-4 pb-2 text-[11px]'"
59
+ >
60
+ {{ t("table.filters.sheet.order") }}
61
+ </div>
62
62
  </div>
63
63
  <div
64
- class="nut-dl-sort__dir grid grid-cols-[repeat(2,minmax(0,1fr))] gap-0.5 overflow-hidden rounded-md bg-elevated p-0.5"
65
- :class="compact ? 'mx-2.5 mb-2.5' : 'mx-4 mb-5'"
64
+ class="nut-dl-sort__dir-wrap w-full min-w-0"
65
+ :class="compact ? 'px-2.5 pb-2.5' : 'px-4 pb-5'"
66
66
  >
67
- <button
68
- v-for="dir in ['asc', 'desc']"
69
- :key="dir"
70
- type="button"
71
- class="flex min-w-0 items-center justify-center gap-1.5 rounded-[5px] px-1 font-medium whitespace-nowrap"
72
- :class="[
67
+ <div
68
+ class="nut-dl-sort__dir grid w-full min-w-0 grid-cols-[repeat(2,minmax(0,1fr))] gap-0.5 overflow-hidden rounded-md bg-elevated p-0.5"
69
+ role="radiogroup"
70
+ :aria-label="t('table.filters.sheet.order')"
71
+ >
72
+ <button
73
+ v-for="dir in ['asc', 'desc']"
74
+ :key="dir"
75
+ type="button"
76
+ role="radio"
77
+ :aria-checked="activeDirection === dir"
78
+ class="flex min-w-0 items-center justify-center gap-1.5 overflow-hidden rounded-[5px] px-1 font-medium whitespace-nowrap outline-none transition-colors focus-visible:ring-2 focus-visible:ring-primary/40"
79
+ :class="[
73
80
  compact ? 'h-7 text-[12px]' : 'h-8 text-[12.5px]',
74
- activeDirection === dir ? 'nut-dl-sort__dir--active bg-default text-highlighted shadow-[0_0_0_1px_var(--ui-border)]' : 'text-muted'
81
+ activeDirection === dir ? 'nut-dl-sort__dir--active bg-default text-highlighted shadow-[0_0_0_1px_var(--ui-border)]' : 'text-muted hover:text-default'
75
82
  ]"
76
- @click="emit('selectDirection', dir)"
77
- >
78
- <UIcon
79
- :name="dir === 'asc' ? 'i-lucide-arrow-up' : 'i-lucide-arrow-down'"
80
- class="size-3.5"
81
- />
82
- {{ dir === "asc" ? "A \u2192 Z" : "Z \u2192 A" }}
83
- </button>
83
+ @click="emit('selectDirection', dir)"
84
+ >
85
+ <UIcon
86
+ :name="dir === 'asc' ? 'i-lucide-arrow-up' : 'i-lucide-arrow-down'"
87
+ class="size-3.5 shrink-0"
88
+ />
89
+ <span class="truncate">{{ dir === "asc" ? "A \u2192 Z" : "Z \u2192 A" }}</span>
90
+ </button>
91
+ </div>
84
92
  </div>
85
93
  </div>
86
94
  </template>
@@ -124,6 +124,15 @@ function toggleColumn(columnId) {
124
124
  const isVisible2 = internals.tableColumns.tableState.value.columnVisibility?.[columnId] !== false;
125
125
  internals.tableColumns.setVisibility({ columnId, visible: !isVisible2 });
126
126
  }
127
+ function toggleRow(event, column) {
128
+ if (event.target instanceof Element && event.target.closest('button, input, [role="checkbox"]')) {
129
+ return;
130
+ }
131
+ if (isRequired(column)) {
132
+ return;
133
+ }
134
+ toggleColumn(column.id);
135
+ }
127
136
  function open() {
128
137
  internals.controls.columnsPanelOpen.value = true;
129
138
  }
@@ -226,7 +235,7 @@ function toggle() {
226
235
  <div
227
236
  :class="
228
237
  mergeDataListUiClass(
229
- 'nut-dl-colpanel__list grid max-h-80 gap-px overflow-y-auto px-2 py-1',
238
+ 'nut-dl-colpanel__list grid max-h-80 grid-cols-1 gap-px overflow-y-auto px-2 py-1',
230
239
  void 0,
231
240
  ui.list
232
241
  )
@@ -238,11 +247,12 @@ function toggle() {
238
247
  :key="column.id"
239
248
  :class="
240
249
  mergeDataListUiClass(
241
- 'nut-dl-colpanel__row group/col flex h-8 items-center gap-2.5 rounded-md px-2 hover:bg-elevated',
250
+ 'nut-dl-colpanel__row group/col flex h-8 cursor-pointer items-center gap-2.5 rounded-md px-2 hover:bg-elevated',
242
251
  void 0,
243
252
  ui.row
244
253
  )
245
254
  "
255
+ @click="toggleRow($event, column)"
246
256
  >
247
257
  <UCheckbox
248
258
  v-bind="checkboxProps"
@@ -278,18 +288,19 @@ function toggle() {
278
288
  ghost-class="column-panel-row-ghost"
279
289
  chosen-class="column-panel-row-chosen"
280
290
  drag-class="column-panel-row-dragging"
281
- :class="mergeDataListUiClass('grid gap-px', void 0, ui.section)"
291
+ :class="mergeDataListUiClass('grid grid-cols-1 gap-px', void 0, ui.section)"
282
292
  >
283
293
  <div
284
294
  v-for="column in draggableColumns"
285
295
  :key="column.id"
286
296
  :class="
287
297
  mergeDataListUiClass(
288
- 'nut-dl-colpanel__row group/col flex h-8 items-center gap-2.5 rounded-md px-2 hover:bg-elevated',
298
+ 'nut-dl-colpanel__row group/col flex h-8 cursor-pointer items-center gap-2.5 rounded-md px-2 hover:bg-elevated',
289
299
  void 0,
290
300
  ui.row
291
301
  )
292
302
  "
303
+ @click="toggleRow($event, column)"
293
304
  >
294
305
  <UCheckbox
295
306
  v-bind="checkboxProps"
@@ -328,11 +339,12 @@ function toggle() {
328
339
  :key="column.id"
329
340
  :class="
330
341
  mergeDataListUiClass(
331
- 'nut-dl-colpanel__row group/col flex h-8 items-center gap-2.5 rounded-md px-2 hover:bg-elevated',
342
+ 'nut-dl-colpanel__row group/col flex h-8 cursor-pointer items-center gap-2.5 rounded-md px-2 hover:bg-elevated',
332
343
  void 0,
333
344
  ui.row
334
345
  )
335
346
  "
347
+ @click="toggleRow($event, column)"
336
348
  >
337
349
  <UCheckbox
338
350
  v-bind="checkboxProps"
@@ -365,11 +377,12 @@ function toggle() {
365
377
  :key="column.id"
366
378
  :class="
367
379
  mergeDataListUiClass(
368
- 'nut-dl-colpanel__row flex h-8 items-center gap-2.5 rounded-md px-2 hover:bg-elevated',
380
+ 'nut-dl-colpanel__row flex h-8 cursor-pointer items-center gap-2.5 rounded-md px-2 hover:bg-elevated',
369
381
  void 0,
370
382
  ui.row
371
383
  )
372
384
  "
385
+ @click="toggleRow($event, column)"
373
386
  >
374
387
  <UCheckbox
375
388
  v-bind="checkboxProps"
@@ -5,6 +5,7 @@ import USlideover from "@nuxt/ui/components/Slideover.vue";
5
5
  import { computed, onMounted, watch } from "vue";
6
6
  import { useUiToolsLocale } from "#ui-tools/i18n";
7
7
  import { isNullish } from "../../../../shared/utils/predicate";
8
+ import { useDataListBreakpoint } from "../../../composables/use-data-list-breakpoint";
8
9
  import { useDataListUi } from "../../../composables/use-data-list-ui";
9
10
  import { useTableInternals } from "../../../composables/use-table-internals";
10
11
  import { mergeDataListProps, mergeDataListUiClass } from "../../../utils";
@@ -19,6 +20,7 @@ const props = defineProps({
19
20
  });
20
21
  const internals = useTableInternals();
21
22
  const dataListUi = useDataListUi();
23
+ const { isMobile } = useDataListBreakpoint();
22
24
  const { locale, t } = useUiToolsLocale();
23
25
  const config = computed(() => dataListUi.ui.value.filterPanel);
24
26
  const resolvedUi = computed(() => ({ ...config.value?.ui, ...props.ui }));
@@ -39,6 +41,7 @@ const triggerBind = computed(() => {
39
41
  return rest;
40
42
  });
41
43
  const triggerLabel = computed(() => triggerProps.value.label ?? t("table.filters.panel.trigger"));
44
+ const hasTriggerIcon = computed(() => Boolean(triggerProps.value.icon));
42
45
  const countProps = computed(
43
46
  () => controlProps.value.count === false ? null : mergeDataListProps(
44
47
  { color: "neutral", size: "xs", variant: "solid" },
@@ -212,6 +215,10 @@ onMounted(() => {
212
215
  <UButton
213
216
  v-bind="triggerBind"
214
217
  :aria-expanded="open"
218
+ :aria-label="triggerLabel"
219
+ :square="
220
+ triggerProps.square ?? (isMobile && hasTriggerIcon && (!countProps || activeCount === 0))
221
+ "
215
222
  :ui="{
216
223
  base: mergeDataListUiClass(
217
224
  'nut-dl-fpanel-trigger shrink-0',
@@ -221,11 +228,16 @@ onMounted(() => {
221
228
  }"
222
229
  >
223
230
  <span
231
+ v-if="!isMobile || !hasTriggerIcon || countProps && activeCount > 0"
224
232
  :class="
225
- mergeDataListUiClass('flex items-center gap-2', void 0, resolvedUi.triggerContent)
233
+ mergeDataListUiClass(
234
+ 'nut-dl-fpanel-trigger__content flex items-center gap-2',
235
+ void 0,
236
+ resolvedUi.triggerContent
237
+ )
226
238
  "
227
239
  >
228
- <span>{{ triggerLabel }}</span>
240
+ <span v-if="!isMobile || !hasTriggerIcon">{{ triggerLabel }}</span>
229
241
  <UBadge
230
242
  v-if="countProps && activeCount > 0"
231
243
  v-bind="countProps"
@@ -203,7 +203,7 @@ function toggle() {
203
203
  "
204
204
  :ui="{
205
205
  base: mergeDataListUiClass(
206
- `nut-dl-tag nut-dl-tag--add shrink-0 ${selectedDefinition ? '' : 'border border-dashed border-[var(--ui-border-accented)] text-muted hover:text-default hover:border-[var(--ui-text-dimmed)]'}`,
206
+ 'nut-dl-tag nut-dl-tag--add shrink-0 border border-dashed border-[var(--ui-border-accented)] text-muted hover:text-default hover:border-[var(--ui-text-dimmed)]',
207
207
  void 0,
208
208
  ui?.trigger
209
209
  ),
@@ -0,0 +1,16 @@
1
+ import type { DataListFilterEditorUi } from '../../../types/index.js';
2
+ type __VLS_Props = {
3
+ label: string;
4
+ active?: boolean;
5
+ embedded?: boolean;
6
+ ui?: DataListFilterEditorUi;
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ clear: () => any;
10
+ back: () => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ onClear?: (() => any) | undefined;
13
+ onBack?: (() => any) | undefined;
14
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
@@ -0,0 +1,46 @@
1
+ <script setup>
2
+ import UIcon from "@nuxt/ui/components/Icon.vue";
3
+ import { useUiToolsLocale } from "#ui-tools/i18n";
4
+ import { mergeDataListUiClass } from "../../../utils";
5
+ defineProps({
6
+ label: { type: String, required: true },
7
+ active: { type: Boolean, required: false },
8
+ embedded: { type: Boolean, required: false },
9
+ ui: { type: Object, required: false }
10
+ });
11
+ const emit = defineEmits(["back", "clear"]);
12
+ const { t } = useUiToolsLocale();
13
+ </script>
14
+
15
+ <template>
16
+ <div
17
+ :class="
18
+ mergeDataListUiClass(
19
+ 'nut-dl-editor__head flex items-center gap-2 border-b border-default py-2 pr-3',
20
+ embedded ? 'pl-1.5' : 'pl-3',
21
+ ui?.editorHeader
22
+ )
23
+ "
24
+ >
25
+ <button
26
+ v-if="embedded"
27
+ type="button"
28
+ class="nut-dl-editor__back flex size-6 items-center justify-center rounded-md text-muted outline-none hover:bg-elevated hover:text-default focus-visible:ring-2 focus-visible:ring-primary/40"
29
+ :aria-label="t('table.filters.sheet.back')"
30
+ @click="emit('back')"
31
+ >
32
+ <UIcon name="i-lucide-arrow-left" class="size-4" />
33
+ </button>
34
+ <span class="min-w-0 flex-1 truncate text-[12.5px] font-semibold text-highlighted">
35
+ {{ label }}
36
+ </span>
37
+ <button
38
+ type="button"
39
+ class="nut-dl-editor__clear rounded text-[12.5px] font-semibold text-primary outline-none focus-visible:ring-2 focus-visible:ring-primary/40 disabled:opacity-40"
40
+ :disabled="!active"
41
+ @click="emit('clear')"
42
+ >
43
+ {{ t("table.filters.editor.clear") }}
44
+ </button>
45
+ </div>
46
+ </template>
@@ -0,0 +1,16 @@
1
+ import type { DataListFilterEditorUi } from '../../../types/index.js';
2
+ type __VLS_Props = {
3
+ label: string;
4
+ active?: boolean;
5
+ embedded?: boolean;
6
+ ui?: DataListFilterEditorUi;
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ clear: () => any;
10
+ back: () => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ onClear?: (() => any) | undefined;
13
+ onBack?: (() => any) | undefined;
14
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
@@ -36,6 +36,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
36
36
  index: number;
37
37
  sectionKey: string;
38
38
  }) => any) | undefined;
39
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
+ }>, {
40
+ truncate: boolean;
41
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
42
  declare const _default: typeof __VLS_export;
41
43
  export default _default;
@@ -15,7 +15,7 @@ const props = defineProps({
15
15
  showCounts: { type: Boolean, required: true },
16
16
  countLoading: { type: Boolean, required: true },
17
17
  selectedIcon: { type: String, required: false },
18
- truncate: { type: Boolean, required: false },
18
+ truncate: { type: Boolean, required: false, default: true },
19
19
  size: { type: String, required: false },
20
20
  ui: { type: Object, required: false }
21
21
  });
@@ -36,7 +36,7 @@ const geometry = computed(() => resolveDataListControlGeometry(size.value));
36
36
  </script>
37
37
 
38
38
  <template>
39
- <div :class="mergeDataListUiClass('grid gap-0.5', void 0, ui?.list)">
39
+ <div :class="mergeDataListUiClass('grid grid-cols-1 gap-0.5', void 0, ui?.list)">
40
40
  <template v-for="section in props.sections" :key="section.key">
41
41
  <div
42
42
  v-if="section.dividerBefore && section.entries.length"
@@ -48,11 +48,12 @@ const geometry = computed(() => resolveDataListControlGeometry(size.value));
48
48
  :key="entry.value == null ? entry.label : String(entry.value)"
49
49
  :class="
50
50
  mergeDataListUiClass(
51
- `nut-dl-option flex items-center rounded-md text-left outline-none transition-colors hover:bg-elevated ${sizeClasses.option} ${entry.selected ? 'text-highlighted' : 'text-default'}`,
51
+ `nut-dl-option flex min-w-0 cursor-pointer items-center rounded-md text-left outline-none transition-colors hover:bg-elevated ${sizeClasses.option} ${entry.selected ? 'text-highlighted' : 'text-default'}`,
52
52
  void 0,
53
53
  ui?.option
54
54
  )
55
55
  "
56
+ @click="emit('select', { event: $event, entry, index, sectionKey: section.key })"
56
57
  >
57
58
  <UCheckbox
58
59
  :model-value="entry.selected ?? false"
@@ -67,7 +68,6 @@ const geometry = computed(() => resolveDataListControlGeometry(size.value));
67
68
  <button
68
69
  type="button"
69
70
  :class="['flex min-w-0 flex-1 items-center text-left', geometry.toolbarGap]"
70
- @click="emit('select', { event: $event, entry, index, sectionKey: section.key })"
71
71
  >
72
72
  <span
73
73
  v-if="entry.color"
@@ -89,11 +89,12 @@ const geometry = computed(() => resolveDataListControlGeometry(size.value));
89
89
  <span
90
90
  :class="
91
91
  mergeDataListUiClass(
92
- `min-w-0 flex-1 ${sizeClasses.optionLabel} ${props.truncate ?? true ? 'truncate' : ''}`,
92
+ `min-w-0 flex-1 ${sizeClasses.optionLabel} ${props.truncate ? 'truncate' : ''}`,
93
93
  void 0,
94
94
  ui?.optionLabel
95
95
  )
96
96
  "
97
+ :title="entry.label"
97
98
  >
98
99
  {{ entry.label }}
99
100
  </span>
@@ -36,6 +36,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
36
36
  index: number;
37
37
  sectionKey: string;
38
38
  }) => any) | undefined;
39
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
+ }>, {
40
+ truncate: boolean;
41
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
42
  declare const _default: typeof __VLS_export;
41
43
  export default _default;
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import { useMediaQuery } from "@vueuse/core";
2
3
  import { isNullish } from "../../../../shared/utils/predicate";
3
4
  import FilterOptionListEnd from "./filter-option-list-end.vue";
4
5
  import FilterOptionLoadingList from "./filter-option-loading-list.vue";
@@ -13,6 +14,7 @@ const props = defineProps({
13
14
  ui: { type: Object, required: false }
14
15
  });
15
16
  const searchQuery = defineModel("searchQuery", { type: String, ...{ default: "" } });
17
+ const finePointer = useMediaQuery("(pointer: fine)");
16
18
  const flatRadioValue = defineModel("flatRadioValue", { type: null, ...{ default: void 0 } });
17
19
  const treeRadioValue = defineModel("treeRadioValue", { type: null, ...{ default: void 0 } });
18
20
  const emit = defineEmits(["selectEntry", "toggleTreeEntry", "toggleExpanded"]);
@@ -33,10 +35,11 @@ function handleSelect(options) {
33
35
  <FilterSearchablePanel
34
36
  v-model:search-query="searchQuery"
35
37
  :searchable="props.state.filterUi.value.searchable"
38
+ :autofocus="props.state.filterUi.value.searchable && finePointer"
36
39
  :search-placeholder="props.state.filterUi.value.labels.searchPlaceholder"
37
40
  :search-loading="props.state.optionSource.isStaleLoading.value"
38
41
  :show-empty="
39
- !props.state.optionSource.isLoading.value && !props.state.displayEntries.value.length && !props.state.visibleTreeEntries.value.length
42
+ !props.state.optionSource.isLoading.value && (props.state.filterUi.value.presentation === 'tree' ? !props.state.visibleTreeEntries.value.length : !props.state.displayEntries.value.length)
40
43
  "
41
44
  :empty-label="props.state.filterUi.value.labels.empty"
42
45
  :size="props.size"