@xleddyl/443-components-vue 0.11.0 → 0.12.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 (34) hide show
  1. package/CATALOG.md +45 -40
  2. package/README.md +62 -4
  3. package/dist/components/layout/Sidebar.vue +8 -3
  4. package/dist/components/layout/Sidebar.vue.d.ts +6 -1
  5. package/dist/components/layout/types.d.ts +1 -0
  6. package/dist/components/templates/DashboardLayout.vue +11 -1
  7. package/dist/components/templates/DashboardLayout.vue.d.ts +5 -2
  8. package/dist/components/templates/SettingsModal.vue +95 -4
  9. package/dist/components/templates/SettingsModal.vue.d.ts +6 -1
  10. package/dist/components/templates/types.d.ts +2 -0
  11. package/dist/composables/layout.d.ts +1 -0
  12. package/dist/composables/layout.mjs +1 -0
  13. package/dist/composables/useRouteState.d.ts +13 -0
  14. package/dist/composables/useRouteState.mjs +37 -0
  15. package/dist/grid/DataGrid.vue +7 -1
  16. package/dist/grid/DataGrid.vue.d.ts +8 -1
  17. package/dist/grid/GridToolbar.vue +57 -2
  18. package/dist/grid/GridToolbar.vue.d.ts +13 -2
  19. package/dist/grid/data-grid.css +33 -0
  20. package/dist/grid/types.d.ts +1 -0
  21. package/dist/icons/builtin.d.ts +2 -0
  22. package/dist/icons/builtin.mjs +2 -0
  23. package/dist/internal/layout/query.d.ts +6 -0
  24. package/dist/internal/layout/query.mjs +64 -0
  25. package/dist/internal/layout/route.d.ts +9 -2
  26. package/dist/locale/de.mjs +4 -0
  27. package/dist/locale/en.mjs +4 -0
  28. package/dist/locale/it.mjs +4 -0
  29. package/dist/locale/types.d.ts +4 -0
  30. package/dist/nuxt/runtime/layouts/dashboard.vue +1 -0
  31. package/dist/styles/layout/app-shell.css +2 -6
  32. package/dist/styles/layout/sidebar.css +11 -2
  33. package/dist/styles/templates/settings-modal.css +5 -1
  34. package/package.json +1 -1
@@ -53,7 +53,7 @@
53
53
  v-if="item.to !== void 0"
54
54
  v-bind="resolveLink({ to: item.to })?.attrs"
55
55
  class="k-settings-modal-tab"
56
- @click="close"
56
+ @click="syncing ? void 0 : close()"
57
57
  >
58
58
  <Icon v-if="item.icon" :name="item.icon" />
59
59
  <span class="k-settings-modal-tab-label">{{ item.label }}</span>
@@ -199,11 +199,13 @@
199
199
  </template>
200
200
 
201
201
  <script setup>
202
- import { computed, useId, useTemplateRef, watch } from "vue";
202
+ import { computed, nextTick, onMounted, useId, useTemplateRef, watch } from "vue";
203
203
  import { THEME_PRESETS } from "../../composables/usePreset";
204
204
  import { useLocale } from "../../composables/useLocale";
205
205
  import { useLinkResolver } from "../../internal/core/link";
206
206
  import { useScrollEdges } from "../../internal/core/scroll-edges";
207
+ import { useQueryValue, writeQueryValue } from "../../internal/layout/query";
208
+ import { useRouterLike } from "../../internal/layout/route";
207
209
  import Button from "../core/Button.vue";
208
210
  import Icon from "../core/Icon.vue";
209
211
  import Avatar from "../data/Avatar.vue";
@@ -232,7 +234,9 @@ const props = defineProps({
232
234
  presetText: { type: String, required: false, default: void 0 },
233
235
  onSignOut: { type: Function, required: false },
234
236
  hideSession: { type: Boolean, required: false },
235
- beforeClose: { type: Function, required: false }
237
+ beforeClose: { type: Function, required: false },
238
+ route: { type: [String, Boolean], required: false, default: false },
239
+ resolvePage: { type: Function, required: false }
236
240
  });
237
241
  defineSlots();
238
242
  const emit = defineEmits(["open", "close", "afterLeave"]);
@@ -307,7 +311,94 @@ const trail = computed(() => [
307
311
  function slotProps(item, close) {
308
312
  return { section: item, close, page: currentPage.value, pages: pages.value, openPage, back };
309
313
  }
310
- watch(activeId, () => goTo(0));
314
+ const router = useRouterLike();
315
+ const routeKey = computed(() => props.route === true ? "settings" : props.route || void 0);
316
+ const routeParam = useQueryValue(router, routeKey);
317
+ const syncing = computed(() => Boolean(router && routeKey.value));
318
+ let applying = false;
319
+ let resolution = 0;
320
+ const routeValue = computed(() => {
321
+ if (!open.value || !activeId.value) return void 0;
322
+ return [activeId.value, ...pages.value.map((page) => page.id)].map(encodeURIComponent).join("/");
323
+ });
324
+ function decodeSegment(segment) {
325
+ try {
326
+ return decodeURIComponent(segment);
327
+ } catch {
328
+ return segment;
329
+ }
330
+ }
331
+ function writeRoute(value, history) {
332
+ const key = routeKey.value;
333
+ if (!router || !key || value === routeParam.value) return;
334
+ void writeQueryValue(router, key, value, history);
335
+ }
336
+ async function settle() {
337
+ await nextTick();
338
+ applying = false;
339
+ writeRoute(routeValue.value, "replace");
340
+ }
341
+ function resolvedPage(token, sectionId, index, id, label) {
342
+ if (token !== resolution || activeId.value !== sectionId || pages.value[index]?.id !== id) return;
343
+ if (label === void 0) {
344
+ applying = true;
345
+ pages.value = pages.value.slice(0, index);
346
+ void settle();
347
+ return;
348
+ }
349
+ pages.value = pages.value.map((page, position) => position === index ? { id, label } : page);
350
+ }
351
+ function applyRoute(value) {
352
+ if (value === routeValue.value) return;
353
+ const token = ++resolution;
354
+ applying = true;
355
+ const [sectionId, ...pageIds] = (value ?? "").split("/").filter(Boolean).map(decodeSegment);
356
+ if (!sectionId) {
357
+ open.value = false;
358
+ void settle();
359
+ return;
360
+ }
361
+ const reuse = activeId.value === sectionId;
362
+ open.value = true;
363
+ section.value = sectionId;
364
+ const next = [];
365
+ if (selectable.value.some((item) => item.id === sectionId)) {
366
+ for (const [index, id] of pageIds.entries()) {
367
+ const known = pages.value[index];
368
+ if (reuse && known?.id === id && next.every((page, i) => pages.value[i] === page)) {
369
+ next.push(known);
370
+ continue;
371
+ }
372
+ const result = props.resolvePage ? props.resolvePage(sectionId, id, next.slice()) : id;
373
+ if (result === void 0) break;
374
+ if (typeof result === "string") {
375
+ next.push({ id, label: result });
376
+ continue;
377
+ }
378
+ next.push({ id, label: "" });
379
+ void Promise.resolve(result).then(
380
+ (label) => resolvedPage(token, sectionId, index, id, label),
381
+ () => resolvedPage(token, sectionId, index, id, void 0)
382
+ );
383
+ }
384
+ }
385
+ pages.value = next;
386
+ void settle();
387
+ }
388
+ watch(activeId, () => {
389
+ if (!applying) goTo(0);
390
+ });
391
+ watch(routeValue, (value) => {
392
+ if (!applying) writeRoute(value, "push");
393
+ });
394
+ watch(routeParam, (value) => {
395
+ if (syncing.value) applyRoute(value);
396
+ });
397
+ onMounted(() => {
398
+ if (!syncing.value) return;
399
+ if (routeParam.value !== void 0) applyRoute(routeParam.value);
400
+ else writeRoute(routeValue.value, "replace");
401
+ });
311
402
  watch(
312
403
  () => pages.value.length,
313
404
  () => contentBody.value?.scrollTo({ top: 0 })
@@ -1,6 +1,6 @@
1
1
  import type { ThemePreset } from '../../composables/usePreset';
2
2
  import type { DialogBeforeClose } from '../overlays/types';
3
- import type { SettingsModalAccount, SettingsModalPage, SettingsModalSection, SettingsSectionSlotProps } from './types';
3
+ import type { SettingsModalAccount, SettingsModalPage, SettingsModalSection, SettingsPageResolver, SettingsSectionSlotProps } from './types';
4
4
  type __VLS_Slots = {
5
5
  'account-fields'?: (props: {
6
6
  account: SettingsModalAccount;
@@ -32,6 +32,8 @@ declare const __VLS_base: import("vue").DefineComponent<{
32
32
  onSignOut?: () => void;
33
33
  hideSession?: boolean;
34
34
  beforeClose?: DialogBeforeClose;
35
+ route?: string | boolean;
36
+ resolvePage?: SettingsPageResolver;
35
37
  open?: boolean | undefined;
36
38
  section?: string;
37
39
  pages?: SettingsModalPage[];
@@ -274,6 +276,8 @@ declare const __VLS_base: import("vue").DefineComponent<{
274
276
  onSignOut?: () => void;
275
277
  hideSession?: boolean;
276
278
  beforeClose?: DialogBeforeClose;
279
+ route?: string | boolean;
280
+ resolvePage?: SettingsPageResolver;
277
281
  open?: boolean | undefined;
278
282
  section?: string;
279
283
  pages?: SettingsModalPage[];
@@ -501,6 +505,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
501
505
  themeText: string;
502
506
  presetLabel: string;
503
507
  presetText: string;
508
+ route: string | boolean;
504
509
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
505
510
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
506
511
  declare const _default: typeof __VLS_export;
@@ -34,6 +34,7 @@ export interface SettingsModalPage {
34
34
  id: string;
35
35
  label: string;
36
36
  }
37
+ export type SettingsPageResolver = (section: string, id: string, parents: SettingsModalPage[]) => string | undefined | Promise<string | undefined>;
37
38
  export interface SettingsSectionSlotProps {
38
39
  section: SettingsModalSection;
39
40
  close: () => void;
@@ -129,6 +130,7 @@ export interface KAppConfig {
129
130
  sections?: SettingsModalSection[];
130
131
  presets?: ThemePreset[] | false;
131
132
  signOutTo?: LinkTarget | false;
133
+ route?: string | boolean;
132
134
  };
133
135
  dashboard?: {
134
136
  variant?: DashboardVariant;
@@ -1 +1,2 @@
1
1
  export * from './useAppShell';
2
+ export * from './useRouteState';
@@ -1 +1,2 @@
1
1
  export * from "./useAppShell.mjs";
2
+ export * from "./useRouteState.mjs";
@@ -0,0 +1,13 @@
1
+ import type { Ref } from 'vue';
2
+ import type { RouteHistory } from '../internal/layout/query';
3
+ export type { RouteHistory };
4
+ export interface RouteStateOptions<T> {
5
+ parse?: (value: string) => T | undefined;
6
+ serialize?: (value: T) => string | undefined;
7
+ history?: RouteHistory;
8
+ default?: T;
9
+ }
10
+ export declare function useRouteState<T = string>(key: string, options: RouteStateOptions<T> & {
11
+ default: T;
12
+ }): Ref<T>;
13
+ export declare function useRouteState<T = string>(key: string, options?: RouteStateOptions<T>): Ref<T | undefined>;
@@ -0,0 +1,37 @@
1
+ import { computed, shallowRef, watch } from "vue";
2
+ import { useQueryValue, writeQueryValue } from "../internal/layout/query.mjs";
3
+ import { useRouterLike } from "../internal/layout/route.mjs";
4
+ export function useRouteState(key, options = {}) {
5
+ const router = useRouterLike();
6
+ const raw = useQueryValue(router, key);
7
+ const fallback = options.default;
8
+ const parse = options.parse ?? ((value) => value);
9
+ const serialize = options.serialize ?? ((value) => String(value));
10
+ function fromRaw(value) {
11
+ if (value === void 0 || value === "") return fallback;
12
+ return parse(value) ?? fallback;
13
+ }
14
+ function toRaw(value) {
15
+ if (value === void 0 || value === null) return void 0;
16
+ const text = serialize(value);
17
+ if (text === void 0 || text === "") return void 0;
18
+ if (fallback !== void 0 && text === serialize(fallback)) return void 0;
19
+ return text;
20
+ }
21
+ const state = shallowRef(fromRaw(raw.value));
22
+ let version = 0;
23
+ watch(raw, (value) => {
24
+ state.value = fromRaw(value);
25
+ });
26
+ return computed({
27
+ get: () => state.value,
28
+ set(value) {
29
+ state.value = value;
30
+ if (!router) return;
31
+ const current = ++version;
32
+ void writeQueryValue(router, key, toRaw(value), options.history ?? "push").then((ok) => {
33
+ if (!ok && current === version) state.value = fromRaw(raw.value);
34
+ });
35
+ }
36
+ });
37
+ }
@@ -22,9 +22,14 @@
22
22
  :exportable="exportable"
23
23
  :export-file-name="exportFileName"
24
24
  :debounce="searchDebounce"
25
+ :filter-count="filterCount"
25
26
  @reset-columns="clearStoredState"
27
+ @filters-reset="emit('filtersReset')"
26
28
  >
27
29
  <slot name="actions" :api="api" v-bind="sheetSlot" />
30
+ <template v-if="$slots.filters" #filters="scope">
31
+ <slot name="filters" v-bind="scope" />
32
+ </template>
28
33
  </GridToolbar>
29
34
 
30
35
  <div
@@ -152,6 +157,7 @@ const props = defineProps({
152
157
  exportable: { type: Boolean, required: false, default: true },
153
158
  exportFileName: { type: String, required: false, default: "export" },
154
159
  searchDebounce: { type: Number, required: false, default: 0 },
160
+ filterCount: { type: Number, required: false, default: 0 },
155
161
  serverSort: { type: Boolean, required: false },
156
162
  pagination: { type: Boolean, required: false },
157
163
  showEdges: { type: Boolean, required: false },
@@ -169,7 +175,7 @@ const props = defineProps({
169
175
  editable: { type: Boolean, required: false },
170
176
  spreadsheet: { type: Boolean, required: false }
171
177
  });
172
- const emit = defineEmits(["rowClick", "selectionChange", "sortChange", "filterChange", "copy", "cellsChange", "cellsInvalid", "dirtyChange", "rangeChange"]);
178
+ const emit = defineEmits(["rowClick", "selectionChange", "sortChange", "filterChange", "copy", "cellsChange", "cellsInvalid", "dirtyChange", "rangeChange", "filtersReset"]);
173
179
  const query = defineModel("query", { type: String, ...{ default: "" } });
174
180
  const page = defineModel("page", { type: Number, ...{ default: 1 } });
175
181
  const pageSize = defineModel("pageSize", { type: Number, ...{ default: 25 } });
@@ -20,6 +20,7 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
20
20
  exportable?: boolean;
21
21
  exportFileName?: string;
22
22
  searchDebounce?: number;
23
+ filterCount?: number;
23
24
  serverSort?: boolean;
24
25
  pagination?: boolean;
25
26
  showEdges?: boolean;
@@ -48,6 +49,7 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
48
49
  onRowClick?: ((event: RowClickedEvent<T, any>) => any) | undefined;
49
50
  "onUpdate:sort"?: ((value: GridSortChange | null | undefined) => any) | undefined;
50
51
  "onUpdate:query"?: ((value: string) => any) | undefined;
52
+ onFiltersReset?: (() => any) | undefined;
51
53
  onSelectionChange?: ((rows: T[]) => any) | undefined;
52
54
  onSortChange?: ((change: GridSortChange | null) => any) | undefined;
53
55
  onFilterChange?: ((model: FilterModel, event: FilterChangedEvent<T, any>) => any) | undefined;
@@ -106,8 +108,13 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
106
108
  clearDirty: (rowIds?: string[]) => void;
107
109
  api: GridApi<T> | null;
108
110
  }) => any;
111
+ } & {
112
+ filters?: (props: {
113
+ close: () => void;
114
+ count: number;
115
+ }) => any;
109
116
  };
110
- emit: (((evt: "copy", text: string) => void) & ((evt: "rowClick", event: RowClickedEvent<T, any>) => void) & ((evt: "selectionChange", rows: T[]) => void) & ((evt: "sortChange", change: GridSortChange | null) => void) & ((evt: "filterChange", model: FilterModel, event: FilterChangedEvent<T, any>) => void) & ((evt: "cellsChange", event: GridCellsChange) => void) & ((evt: "cellsInvalid", event: GridCellsInvalid) => void) & ((evt: "dirtyChange", count: number) => void) & ((evt: "rangeChange", range: GridCellRange | null) => void)) & (((event: "update:page", value: number) => void) & ((event: "update:pageSize", value: number) => void) & ((event: "update:sort", value: GridSortChange | null | undefined) => void) & ((event: "update:query", value: string) => void));
117
+ emit: (((evt: "copy", text: string) => void) & ((evt: "rowClick", event: RowClickedEvent<T, any>) => void) & ((evt: "filtersReset") => void) & ((evt: "selectionChange", rows: T[]) => void) & ((evt: "sortChange", change: GridSortChange | null) => void) & ((evt: "filterChange", model: FilterModel, event: FilterChangedEvent<T, any>) => void) & ((evt: "cellsChange", event: GridCellsChange) => void) & ((evt: "cellsInvalid", event: GridCellsInvalid) => void) & ((evt: "dirtyChange", count: number) => void) & ((evt: "rangeChange", range: GridCellRange | null) => void)) & (((event: "update:page", value: number) => void) & ((event: "update:pageSize", value: number) => void) & ((event: "update:sort", value: GridSortChange | null | undefined) => void) & ((event: "update:query", value: string) => void));
111
118
  }>) => import("vue").VNode & {
112
119
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
113
120
  };
@@ -12,6 +12,46 @@
12
12
  :aria-label="text.search"
13
13
  />
14
14
  </div>
15
+ <span v-if="$slots.filters" class="k-data-grid-filters">
16
+ <Button
17
+ variant="outline"
18
+ tone="neutral"
19
+ size="sm"
20
+ icon="funnel"
21
+ square
22
+ :aria-label="filtersLabel"
23
+ aria-haspopup="dialog"
24
+ :aria-expanded="filtersOpen ? 'true' : 'false'"
25
+ :class="{ 'is-active': activeFilters > 0 }"
26
+ @click="filtersOpen = true"
27
+ />
28
+ <Badge
29
+ v-if="activeFilters > 0"
30
+ class="k-data-grid-filters-count"
31
+ tone="accent"
32
+ variant="solid"
33
+ size="sm"
34
+ :label="activeFilters"
35
+ aria-hidden="true"
36
+ />
37
+ </span>
38
+ <Modal v-if="$slots.filters" v-model:open="filtersOpen" :title="text.filters" size="sm">
39
+ <div class="k-data-grid-filters-body">
40
+ <slot name="filters" :close="closeFilters" :count="activeFilters" />
41
+ </div>
42
+ <template #footer="{ close }">
43
+ <Button
44
+ class="k-data-grid-filters-reset"
45
+ variant="ghost"
46
+ tone="neutral"
47
+ icon="arrow-path"
48
+ :label="text.resetFilters"
49
+ :aria-disabled="activeFilters === 0 ? 'true' : void 0"
50
+ @click="resetFilters"
51
+ />
52
+ <Button :label="text.showResults" @click="close" />
53
+ </template>
54
+ </Modal>
15
55
  <div class="k-data-grid-toolbar-actions">
16
56
  <slot />
17
57
  <DropdownMenu
@@ -47,8 +87,11 @@
47
87
  import { computed, onBeforeUnmount, ref, watch } from "vue";
48
88
  import Button from "../components/core/Button.vue";
49
89
  import Input from "../components/core/Input.vue";
90
+ import Badge from "../components/data/Badge.vue";
50
91
  import DropdownMenu from "../components/overlays/DropdownMenu.vue";
92
+ import Modal from "../components/overlays/Modal.vue";
51
93
  import { useLocale } from "../composables/useLocale";
94
+ import { formatLocaleMessage } from "../locale";
52
95
  const props = defineProps({
53
96
  api: { type: [Object, null], required: false },
54
97
  messages: { type: Object, required: false },
@@ -56,15 +99,21 @@ const props = defineProps({
56
99
  columnMenu: { type: Boolean, required: false, default: true },
57
100
  exportable: { type: Boolean, required: false, default: true },
58
101
  exportFileName: { type: String, required: false, default: "export" },
59
- debounce: { type: Number, required: false, default: 0 }
102
+ debounce: { type: Number, required: false, default: 0 },
103
+ filterCount: { type: Number, required: false, default: 0 }
60
104
  });
61
- const emit = defineEmits(["resetColumns"]);
105
+ const emit = defineEmits(["resetColumns", "filtersReset"]);
62
106
  const query = defineModel({ type: String, ...{ default: "" } });
63
107
  const { locale } = useLocale();
108
+ const filtersOpen = ref(false);
109
+ const activeFilters = computed(() => Math.max(0, Math.floor(props.filterCount)));
64
110
  const text = computed(() => ({
65
111
  ...locale.value.messages.grid,
66
112
  ...props.messages
67
113
  }));
114
+ const filtersLabel = computed(
115
+ () => formatLocaleMessage(locale.value.code, text.value.filtersActive, { count: activeFilters.value })
116
+ );
68
117
  const draft = ref(query.value);
69
118
  const columns = ref([]);
70
119
  const visibleCount = computed(() => columns.value.filter((column) => column.visible).length);
@@ -130,6 +179,12 @@ function resetColumns() {
130
179
  props.api?.resetColumnState();
131
180
  emit("resetColumns");
132
181
  }
182
+ function resetFilters() {
183
+ if (activeFilters.value > 0) emit("filtersReset");
184
+ }
185
+ function closeFilters() {
186
+ filtersOpen.value = false;
187
+ }
133
188
  function exportCsv() {
134
189
  props.api?.exportDataAsCsv({ fileName: props.exportFileName });
135
190
  }
@@ -1,7 +1,13 @@
1
1
  import type { GridMessages } from './types';
2
- declare var __VLS_8: {};
2
+ declare function closeFilters(): void;
3
+ declare var __VLS_28: {
4
+ close: typeof closeFilters;
5
+ count: number;
6
+ }, __VLS_45: {};
3
7
  type __VLS_Slots = {} & {
4
- default?: (props: typeof __VLS_8) => any;
8
+ filters?: (props: typeof __VLS_28) => any;
9
+ } & {
10
+ default?: (props: typeof __VLS_45) => any;
5
11
  };
6
12
  declare const __VLS_base: import("vue").DefineComponent<{
7
13
  api?: import("ag-grid-community").GridApi | null;
@@ -11,6 +17,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
11
17
  exportable?: boolean;
12
18
  exportFileName?: string;
13
19
  debounce?: number;
20
+ filterCount?: number;
14
21
  modelValue?: string;
15
22
  key?: PropertyKey | undefined;
16
23
  prefix?: string | undefined | undefined;
@@ -207,6 +214,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
207
214
  }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
208
215
  resetColumns: () => any;
209
216
  "update:modelValue": (value: string) => any;
217
+ filtersReset: () => any;
210
218
  }, string, import("vue").PublicProps, Readonly<{
211
219
  api?: import("ag-grid-community").GridApi | null;
212
220
  messages?: Partial<GridMessages>;
@@ -215,6 +223,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
215
223
  exportable?: boolean;
216
224
  exportFileName?: string;
217
225
  debounce?: number;
226
+ filterCount?: number;
218
227
  modelValue?: string;
219
228
  key?: PropertyKey | undefined;
220
229
  prefix?: string | undefined | undefined;
@@ -411,12 +420,14 @@ declare const __VLS_base: import("vue").DefineComponent<{
411
420
  }> & Readonly<{
412
421
  onResetColumns?: (() => any) | undefined;
413
422
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
423
+ onFiltersReset?: (() => any) | undefined;
414
424
  }>, {
415
425
  debounce: number;
416
426
  searchable: boolean;
417
427
  columnMenu: boolean;
418
428
  exportable: boolean;
419
429
  exportFileName: string;
430
+ filterCount: number;
420
431
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
421
432
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
422
433
  declare const _default: typeof __VLS_export;
@@ -20,6 +20,39 @@
20
20
  max-width: 22rem;
21
21
  }
22
22
 
23
+ .k-data-grid-filters {
24
+ position: relative;
25
+ display: inline-flex;
26
+ flex: none;
27
+ }
28
+
29
+ .k-data-grid-filters > .k-btn.is-active {
30
+ color: var(--k-accent-text);
31
+ }
32
+
33
+ .k-data-grid-filters > .k-data-grid-filters-count {
34
+ position: absolute;
35
+ inset-block-start: -0.4375rem;
36
+ inset-inline-end: -0.4375rem;
37
+ min-width: 1.125rem;
38
+ height: 1.125rem;
39
+ padding-inline: 0.25rem;
40
+ justify-content: center;
41
+ font-size: 0.75rem;
42
+ line-height: 1;
43
+ pointer-events: none;
44
+ }
45
+
46
+ .k-data-grid-filters-body {
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: 0.875rem;
50
+ }
51
+
52
+ .k-data-grid-filters-reset {
53
+ margin-inline-end: auto;
54
+ }
55
+
23
56
  .k-data-grid-toolbar-actions {
24
57
  display: flex;
25
58
  flex-wrap: wrap;
@@ -31,6 +31,7 @@ export interface GridToolbarProps {
31
31
  exportable?: boolean;
32
32
  exportFileName?: string;
33
33
  debounce?: number;
34
+ filterCount?: number;
34
35
  }
35
36
  export interface BooleanCellParams {
36
37
  trueLabel?: string;
@@ -7,6 +7,8 @@ export declare const builtinIcons: {
7
7
  readonly 'chevron-up': readonly ["m4.5 15.75 7.5-7.5 7.5 7.5"];
8
8
  readonly 'chevron-left': readonly ["M15.75 19.5 8.25 12l7.5-7.5"];
9
9
  readonly 'chevron-right': readonly ["m8.25 4.5 7.5 7.5-7.5 7.5"];
10
+ readonly 'sidebar-collapse': readonly ["M15.75 19.5 8.25 12l7.5-7.5"];
11
+ readonly 'sidebar-expand': readonly ["m8.25 4.5 7.5 7.5-7.5 7.5"];
10
12
  readonly 'chevron-up-down': readonly ["M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"];
11
13
  readonly 'chevron-double-left': readonly ["m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5"];
12
14
  readonly 'chevron-double-right': readonly ["m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5"];
@@ -7,6 +7,8 @@ export const builtinIcons = {
7
7
  "chevron-up": ["m4.5 15.75 7.5-7.5 7.5 7.5"],
8
8
  "chevron-left": ["M15.75 19.5 8.25 12l7.5-7.5"],
9
9
  "chevron-right": ["m8.25 4.5 7.5 7.5-7.5 7.5"],
10
+ "sidebar-collapse": ["M15.75 19.5 8.25 12l7.5-7.5"],
11
+ "sidebar-expand": ["m8.25 4.5 7.5 7.5-7.5 7.5"],
10
12
  "chevron-up-down": ["M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"],
11
13
  "chevron-double-left": ["m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5"],
12
14
  "chevron-double-right": ["m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5"],
@@ -0,0 +1,6 @@
1
+ import type { MaybeRefOrGetter } from 'vue';
2
+ import type { RouterLike } from './route';
3
+ export type RouteHistory = 'push' | 'replace';
4
+ export declare function firstQueryValue(value: unknown): string | undefined;
5
+ export declare function useQueryValue(router: RouterLike | undefined, key: MaybeRefOrGetter<string | undefined>): import("vue").ComputedRef<string | undefined>;
6
+ export declare function writeQueryValue(router: RouterLike, key: string, value: string | undefined, history: RouteHistory): Promise<boolean>;
@@ -0,0 +1,64 @@
1
+ import { computed, toValue } from "vue";
2
+ import { isClient } from "../core/env.mjs";
3
+ const batches = /* @__PURE__ */ new WeakMap();
4
+ export function firstQueryValue(value) {
5
+ if (Array.isArray(value)) return firstQueryValue(value[0]);
6
+ return typeof value === "string" ? value : void 0;
7
+ }
8
+ export function useQueryValue(router, key) {
9
+ return computed(() => {
10
+ const name = toValue(key);
11
+ if (!router || !name) return void 0;
12
+ return firstQueryValue(router.currentRoute?.value?.query?.[name]);
13
+ });
14
+ }
15
+ function isPreviousEntry(router, location) {
16
+ if (!router.back || !router.resolve) return false;
17
+ const back = window.history.state?.back;
18
+ if (typeof back !== "string") return false;
19
+ return router.resolve(location).fullPath === back;
20
+ }
21
+ async function flush(router, batch) {
22
+ batches.delete(router);
23
+ const route = router.currentRoute?.value;
24
+ const query = { ...route?.query };
25
+ let changed = false;
26
+ for (const [key, value] of batch.changes) {
27
+ if (value === void 0) {
28
+ if (!(key in query)) continue;
29
+ delete query[key];
30
+ changed = true;
31
+ } else if (query[key] !== value) {
32
+ query[key] = value;
33
+ changed = true;
34
+ }
35
+ }
36
+ if (!changed) return true;
37
+ const location = { path: route?.path, query, hash: route?.hash };
38
+ try {
39
+ if (batch.history === "push" && isPreviousEntry(router, location)) {
40
+ router.back?.();
41
+ return true;
42
+ }
43
+ const failure = batch.history === "replace" && router.replace ? await router.replace(location) : await router.push(location);
44
+ return !failure;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+ export function writeQueryValue(router, key, value, history) {
50
+ if (!isClient()) return Promise.resolve(true);
51
+ let batch = batches.get(router);
52
+ if (!batch) {
53
+ const created = {
54
+ changes: /* @__PURE__ */ new Map(),
55
+ history,
56
+ promise: Promise.resolve().then(() => flush(router, created))
57
+ };
58
+ batch = created;
59
+ batches.set(router, batch);
60
+ }
61
+ batch.changes.set(key, value === "" ? void 0 : value);
62
+ if (history === "push") batch.history = "push";
63
+ return batch.promise;
64
+ }
@@ -1,9 +1,17 @@
1
1
  import type { MaybeRefOrGetter } from 'vue';
2
- interface RouteLike {
2
+ export interface RouteLike {
3
3
  path?: string;
4
+ fullPath?: string;
5
+ hash?: string;
6
+ query?: Record<string, unknown>;
4
7
  }
5
8
  export interface RouterLike {
6
9
  push: (to: unknown) => unknown;
10
+ replace?: (to: unknown) => unknown;
11
+ back?: () => unknown;
12
+ resolve?: (to: unknown) => {
13
+ fullPath?: string;
14
+ };
7
15
  beforeEach: (guard: (to: RouteLike, from: RouteLike) => boolean | void | Promise<boolean | void>) => () => void;
8
16
  currentRoute?: {
9
17
  value?: RouteLike;
@@ -13,4 +21,3 @@ export declare function useRouterLike(): RouterLike | undefined;
13
21
  export declare function useCurrentPath(override?: MaybeRefOrGetter<string | undefined>): import("vue").ComputedRef<string | undefined>;
14
22
  export declare function targetPath(target: unknown): string | undefined;
15
23
  export declare function matchesPath(current: string, target: string, exact: boolean): boolean;
16
- export {};
@@ -82,6 +82,10 @@ export const de = {
82
82
  columns: "Spalten",
83
83
  resetColumns: "Spalten zur\xFCcksetzen",
84
84
  exportCsv: "CSV exportieren",
85
+ filters: "Filter",
86
+ filtersActive: "Filter | Filter, {count} aktiv | Filter, {count} aktiv",
87
+ resetFilters: "Filter zur\xFCcksetzen",
88
+ showResults: "Ergebnisse anzeigen",
85
89
  rowsPerPage: "Zeilen pro Seite",
86
90
  previousPage: "Vorherige Seite",
87
91
  nextPage: "N\xE4chste Seite",
@@ -82,6 +82,10 @@ export const en = {
82
82
  columns: "Columns",
83
83
  resetColumns: "Reset columns",
84
84
  exportCsv: "Export CSV",
85
+ filters: "Filters",
86
+ filtersActive: "Filters | Filters, {count} active | Filters, {count} active",
87
+ resetFilters: "Reset filters",
88
+ showResults: "Show results",
85
89
  rowsPerPage: "Rows per page",
86
90
  previousPage: "Previous page",
87
91
  nextPage: "Next page",
@@ -82,6 +82,10 @@ export const it = {
82
82
  columns: "Colonne",
83
83
  resetColumns: "Ripristina colonne",
84
84
  exportCsv: "Esporta CSV",
85
+ filters: "Filtri",
86
+ filtersActive: "Filtri | Filtri, {count} attivo | Filtri, {count} attivi",
87
+ resetFilters: "Azzera filtri",
88
+ showResults: "Mostra risultati",
85
89
  rowsPerPage: "Righe per pagina",
86
90
  previousPage: "Pagina precedente",
87
91
  nextPage: "Pagina successiva",