@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.
- package/CATALOG.md +45 -40
- package/README.md +62 -4
- package/dist/components/layout/Sidebar.vue +8 -3
- package/dist/components/layout/Sidebar.vue.d.ts +6 -1
- package/dist/components/layout/types.d.ts +1 -0
- package/dist/components/templates/DashboardLayout.vue +11 -1
- package/dist/components/templates/DashboardLayout.vue.d.ts +5 -2
- package/dist/components/templates/SettingsModal.vue +95 -4
- package/dist/components/templates/SettingsModal.vue.d.ts +6 -1
- package/dist/components/templates/types.d.ts +2 -0
- package/dist/composables/layout.d.ts +1 -0
- package/dist/composables/layout.mjs +1 -0
- package/dist/composables/useRouteState.d.ts +13 -0
- package/dist/composables/useRouteState.mjs +37 -0
- package/dist/grid/DataGrid.vue +7 -1
- package/dist/grid/DataGrid.vue.d.ts +8 -1
- package/dist/grid/GridToolbar.vue +57 -2
- package/dist/grid/GridToolbar.vue.d.ts +13 -2
- package/dist/grid/data-grid.css +33 -0
- package/dist/grid/types.d.ts +1 -0
- package/dist/icons/builtin.d.ts +2 -0
- package/dist/icons/builtin.mjs +2 -0
- package/dist/internal/layout/query.d.ts +6 -0
- package/dist/internal/layout/query.mjs +64 -0
- package/dist/internal/layout/route.d.ts +9 -2
- package/dist/locale/de.mjs +4 -0
- package/dist/locale/en.mjs +4 -0
- package/dist/locale/it.mjs +4 -0
- package/dist/locale/types.d.ts +4 -0
- package/dist/nuxt/runtime/layouts/dashboard.vue +1 -0
- package/dist/styles/layout/app-shell.css +2 -6
- package/dist/styles/layout/sidebar.css +11 -2
- package/dist/styles/templates/settings-modal.css +5 -1
- 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
|
-
|
|
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;
|
|
@@ -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
|
+
}
|
package/dist/grid/DataGrid.vue
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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;
|
package/dist/grid/data-grid.css
CHANGED
|
@@ -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;
|
package/dist/grid/types.d.ts
CHANGED
package/dist/icons/builtin.d.ts
CHANGED
|
@@ -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"];
|
package/dist/icons/builtin.mjs
CHANGED
|
@@ -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 {};
|
package/dist/locale/de.mjs
CHANGED
|
@@ -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",
|
package/dist/locale/en.mjs
CHANGED
|
@@ -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",
|
package/dist/locale/it.mjs
CHANGED
|
@@ -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",
|