@xleddyl/443-components-vue 0.8.1 → 0.8.3
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/README.md +52 -4
- package/dist/chart/BarChart.vue +33 -22
- package/dist/chart/BarChart.vue.d.ts +4 -0
- package/dist/chart/chart.css +2 -2
- package/dist/chart/useChartColors.mjs +8 -8
- package/dist/components/core/Button.vue.d.ts +1 -1
- package/dist/components/core/FormField.vue +5 -0
- package/dist/components/core/FormField.vue.d.ts +4 -0
- package/dist/components/core/Input.vue +59 -5
- package/dist/components/core/Input.vue.d.ts +9 -5
- package/dist/components/core/Textarea.vue +28 -3
- package/dist/components/core/Textarea.vue.d.ts +7 -5
- package/dist/components/core/types.d.ts +1 -1
- package/dist/components/data/Badge.vue.d.ts +1 -1
- package/dist/components/data/DescriptionList.vue.d.ts +1 -1
- package/dist/components/data/FilterPills.vue +43 -4
- package/dist/components/data/FilterPills.vue.d.ts +4 -1
- package/dist/components/data/Pagination.vue +63 -2
- package/dist/components/data/Pagination.vue.d.ts +12 -0
- package/dist/components/data/Tag.vue.d.ts +1 -1
- package/dist/components/data/Tree.vue.d.ts +1 -1
- package/dist/components/data/types.d.ts +1 -0
- package/dist/components/feedback/Meter.vue.d.ts +1 -1
- package/dist/components/feedback/Progress.vue.d.ts +1 -1
- package/dist/components/feedback/ProgressRing.vue.d.ts +1 -1
- package/dist/components/forms/Calendar.vue +31 -7
- package/dist/components/forms/Calendar.vue.d.ts +13 -2
- package/dist/components/forms/CheckboxGroup.vue +7 -1
- package/dist/components/forms/CheckboxGroup.vue.d.ts +1 -0
- package/dist/components/forms/ColorInput.vue.d.ts +5 -5
- package/dist/components/forms/Combobox.vue +4 -3
- package/dist/components/forms/Combobox.vue.d.ts +1 -0
- package/dist/components/forms/DateInput.vue.d.ts +10 -6
- package/dist/components/forms/DatePicker.vue.d.ts +1 -1
- package/dist/components/forms/FileInput.vue.d.ts +1 -1
- package/dist/components/forms/NumberInput.vue.d.ts +5 -5
- package/dist/components/forms/PasswordInput.vue.d.ts +9 -5
- package/dist/components/forms/PinInput.vue.d.ts +5 -5
- package/dist/components/forms/Select.vue +21 -2
- package/dist/components/forms/Select.vue.d.ts +4 -4
- package/dist/components/forms/Slider.vue.d.ts +5 -5
- package/dist/components/forms/Switch.vue.d.ts +1 -1
- package/dist/components/forms/TimeInput.vue.d.ts +9 -5
- package/dist/components/layout/AppShell.vue +4 -2
- package/dist/components/layout/AppShell.vue.d.ts +2 -0
- package/dist/components/layout/Card.vue.d.ts +1 -1
- package/dist/components/layout/CopyButton.vue.d.ts +1 -1
- package/dist/components/layout/PageHeader.vue +15 -3
- package/dist/components/layout/PageHeader.vue.d.ts +5 -0
- package/dist/components/layout/Separator.vue +17 -4
- package/dist/components/layout/Separator.vue.d.ts +7 -1
- package/dist/components/layout/Tabs.vue +10 -0
- package/dist/components/layout/types.d.ts +3 -0
- package/dist/components/overlays/ConfirmDialog.vue.d.ts +8 -4
- package/dist/components/overlays/ConfirmHost.vue.d.ts +8 -4
- package/dist/components/overlays/ContextMenu.vue.d.ts +4 -2
- package/dist/components/overlays/DropdownMenu.vue +5 -1
- package/dist/components/overlays/DropdownMenu.vue.d.ts +8 -2
- package/dist/components/overlays/Modal.vue +9 -2
- package/dist/components/overlays/Modal.vue.d.ts +4 -0
- package/dist/components/overlays/types.d.ts +2 -2
- package/dist/components/templates/DashboardLayout.vue +2 -0
- package/dist/components/templates/DashboardLayout.vue.d.ts +2 -0
- package/dist/components/templates/SettingsModal.vue.d.ts +8 -4
- package/dist/components/templates/types.d.ts +1 -0
- package/dist/composables/useForm.d.ts +24 -2
- package/dist/composables/useForm.mjs +51 -8
- package/dist/composables/useOverlay.d.ts +1 -1
- package/dist/editor/RichTextEditor.vue +108 -10
- package/dist/editor/RichTextEditor.vue.d.ts +5 -2
- package/dist/editor/rich-text-editor.css +31 -4
- package/dist/editor/types.d.ts +2 -1
- package/dist/grid/DataGrid.vue +106 -24
- package/dist/grid/DataGrid.vue.d.ts +14 -2
- package/dist/grid/cells/DateCell.vue +18 -3
- package/dist/grid/cells/ImageCell.vue +6 -1
- package/dist/grid/data-grid.css +17 -3
- package/dist/grid/index.d.ts +2 -1
- package/dist/grid/index.mjs +6 -1
- package/dist/grid/modules.d.ts +2 -0
- package/dist/grid/modules.mjs +32 -0
- package/dist/grid/theme.mjs +5 -5
- package/dist/grid/types.d.ts +4 -0
- package/dist/icons/builtin.d.ts +3 -0
- package/dist/icons/builtin.mjs +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -0
- package/dist/internal/core/intl.d.ts +2 -0
- package/dist/internal/core/intl.mjs +17 -0
- package/dist/internal/forms/counter.d.ts +18 -0
- package/dist/internal/forms/counter.mjs +31 -0
- package/dist/internal/keys.d.ts +2 -1
- package/dist/internal/keys.mjs +1 -0
- package/dist/internal/overlays/MenuList.vue +4 -2
- package/dist/internal/overlays/MenuList.vue.d.ts +2 -0
- package/dist/internal/overlays/OverlayView.vue.d.ts +8 -4
- package/dist/locale/de.mjs +21 -3
- package/dist/locale/en.mjs +21 -3
- package/dist/locale/it.mjs +21 -3
- package/dist/locale/types.d.ts +18 -0
- package/dist/nuxt/runtime/layouts/dashboard.vue +1 -0
- package/dist/styles/base.css +2 -1
- package/dist/styles/core/button.css +42 -16
- package/dist/styles/core/form-field.css +7 -1
- package/dist/styles/core/input.css +31 -2
- package/dist/styles/core/kbd.css +1 -1
- package/dist/styles/core/textarea.css +10 -0
- package/dist/styles/data/avatar.css +1 -1
- package/dist/styles/data/badge.css +39 -23
- package/dist/styles/data/description-list.css +2 -2
- package/dist/styles/data/filter-pills.css +24 -3
- package/dist/styles/data/pagination.css +26 -3
- package/dist/styles/data/selection-bar.css +1 -1
- package/dist/styles/data/stat.css +4 -4
- package/dist/styles/data/table.css +4 -4
- package/dist/styles/data/tag.css +1 -1
- package/dist/styles/data/timeline.css +1 -1
- package/dist/styles/data/tree.css +2 -2
- package/dist/styles/feedback/empty-state.css +1 -1
- package/dist/styles/feedback/loading-host.css +1 -1
- package/dist/styles/feedback/loading-overlay.css +1 -1
- package/dist/styles/feedback/meter.css +1 -1
- package/dist/styles/feedback/progress.css +2 -2
- package/dist/styles/forced-colors.css +6 -1
- package/dist/styles/forms/calendar.css +30 -3
- package/dist/styles/forms/checkbox.css +1 -1
- package/dist/styles/forms/choice.css +7 -1
- package/dist/styles/forms/combobox.css +2 -2
- package/dist/styles/forms/file-input.css +1 -1
- package/dist/styles/forms/password-input.css +1 -1
- package/dist/styles/forms/pin-input.css +1 -1
- package/dist/styles/forms/radio.css +1 -1
- package/dist/styles/forms/segmented.css +2 -2
- package/dist/styles/forms/select.css +2 -2
- package/dist/styles/forms/slider.css +1 -1
- package/dist/styles/forms/switch.css +4 -4
- package/dist/styles/layout/accordion.css +2 -2
- package/dist/styles/layout/app-shell.css +18 -6
- package/dist/styles/layout/breadcrumbs.css +3 -3
- package/dist/styles/layout/card.css +1 -1
- package/dist/styles/layout/nav-link.css +1 -1
- package/dist/styles/layout/page-header.css +21 -1
- package/dist/styles/layout/separator.css +21 -1
- package/dist/styles/layout/sidebar.css +1 -1
- package/dist/styles/layout/tabs.css +30 -1
- package/dist/styles/layout/theme-switch.css +1 -1
- package/dist/styles/overlays/command.css +3 -3
- package/dist/styles/overlays/drawer.css +4 -3
- package/dist/styles/overlays/floating.css +2 -2
- package/dist/styles/overlays/menu.css +5 -1
- package/dist/styles/overlays/modal.css +12 -9
- package/dist/styles/overlays/toast.css +3 -3
- package/dist/styles/overlays/tooltip.css +26 -8
- package/dist/styles/shared.css +2 -2
- package/dist/styles/templates/account-menu.css +1 -1
- package/dist/styles/templates/auth-layout.css +4 -4
- package/dist/styles/templates/brand.css +2 -2
- package/dist/styles/templates/dashboard-page.css +1 -1
- package/dist/styles/templates/empty-page.css +3 -3
- package/dist/styles/templates/error-page.css +1 -1
- package/dist/styles/templates/save-bar.css +1 -1
- package/dist/styles/templates/settings-modal.css +6 -6
- package/dist/styles/utilities.css +3 -3
- package/package.json +1 -1
|
@@ -53,16 +53,42 @@
|
|
|
53
53
|
</template>
|
|
54
54
|
</div>
|
|
55
55
|
|
|
56
|
-
<form
|
|
56
|
+
<form
|
|
57
|
+
v-if="linkOpen"
|
|
58
|
+
class="k-editor-link"
|
|
59
|
+
@submit.prevent="applyLink"
|
|
60
|
+
@keydown.esc.stop.prevent="closeLink(true)"
|
|
61
|
+
>
|
|
62
|
+
<span v-if="linkTypes.length > 1" class="k-editor-link-type">
|
|
63
|
+
<select
|
|
64
|
+
v-model="linkType"
|
|
65
|
+
class="k-field k-field-sm"
|
|
66
|
+
:aria-label="text.linkType"
|
|
67
|
+
@change="focusLinkInput"
|
|
68
|
+
>
|
|
69
|
+
<option v-for="type in linkTypes" :key="type" :value="type">
|
|
70
|
+
{{ linkTypeLabels[type] }}
|
|
71
|
+
</option>
|
|
72
|
+
</select>
|
|
73
|
+
<Icon name="chevron-up-down" class="k-editor-link-type-icon" />
|
|
74
|
+
</span>
|
|
57
75
|
<input
|
|
58
76
|
ref="linkInputRef"
|
|
59
77
|
v-model="linkUrl"
|
|
60
|
-
class="k-field k-field-sm"
|
|
61
|
-
type="
|
|
62
|
-
inputmode="
|
|
63
|
-
|
|
64
|
-
:
|
|
65
|
-
|
|
78
|
+
class="k-field k-field-sm k-editor-link-target"
|
|
79
|
+
:type="linkInput.type"
|
|
80
|
+
:inputmode="linkInput.inputmode"
|
|
81
|
+
:autocomplete="linkInput.autocomplete"
|
|
82
|
+
:placeholder="linkInput.placeholder"
|
|
83
|
+
:aria-label="linkInput.label"
|
|
84
|
+
/>
|
|
85
|
+
<input
|
|
86
|
+
v-if="linkNeedsText"
|
|
87
|
+
v-model="linkText"
|
|
88
|
+
class="k-field k-field-sm k-editor-link-text"
|
|
89
|
+
type="text"
|
|
90
|
+
:placeholder="text.linkText"
|
|
91
|
+
:aria-label="text.linkText"
|
|
66
92
|
/>
|
|
67
93
|
<Button type="submit" size="xs" :label="text.applyLink" />
|
|
68
94
|
<Button
|
|
@@ -97,6 +123,7 @@ import StarterKit from "@tiptap/starter-kit";
|
|
|
97
123
|
import { EditorContent, useEditor } from "@tiptap/vue-3";
|
|
98
124
|
import { computed, nextTick, onBeforeUnmount, ref, useId, useTemplateRef, watch } from "vue";
|
|
99
125
|
import Button from "../components/core/Button.vue";
|
|
126
|
+
import Icon from "../components/core/Icon.vue";
|
|
100
127
|
import { useFieldControl } from "../composables/useField";
|
|
101
128
|
import { useRovingTabStops } from "../internal/core/roving";
|
|
102
129
|
import LiveRegion from "../internal/core/LiveRegion.vue";
|
|
@@ -125,6 +152,7 @@ const props = defineProps({
|
|
|
125
152
|
headingLabel: { type: String, required: false, default: "level" },
|
|
126
153
|
toolbar: { type: Array, required: false },
|
|
127
154
|
extensions: { type: Array, required: false, default: () => [] },
|
|
155
|
+
linkTypes: { type: Array, required: false, default: () => ["url"] },
|
|
128
156
|
messages: { type: Object, required: false }
|
|
129
157
|
});
|
|
130
158
|
const model = defineModel({ type: [String, Object, null], ...{ default: null } });
|
|
@@ -140,6 +168,40 @@ const toolbarRef = useTemplateRef("toolbarRef");
|
|
|
140
168
|
const linkInputRef = useTemplateRef("linkInputRef");
|
|
141
169
|
const linkOpen = ref(false);
|
|
142
170
|
const linkUrl = ref("");
|
|
171
|
+
const linkText = ref("");
|
|
172
|
+
const linkType = ref("url");
|
|
173
|
+
const linkNeedsText = ref(false);
|
|
174
|
+
const linkPrefixes = { url: "", email: "mailto:", phone: "tel:" };
|
|
175
|
+
const linkTypeLabels = computed(() => ({
|
|
176
|
+
url: text.value.linkTypeUrl,
|
|
177
|
+
email: text.value.linkTypeEmail,
|
|
178
|
+
phone: text.value.linkTypePhone
|
|
179
|
+
}));
|
|
180
|
+
const linkInput = computed(() => {
|
|
181
|
+
if (linkType.value === "email")
|
|
182
|
+
return {
|
|
183
|
+
type: "email",
|
|
184
|
+
inputmode: "email",
|
|
185
|
+
autocomplete: "email",
|
|
186
|
+
placeholder: "name@example.com",
|
|
187
|
+
label: text.value.linkEmail
|
|
188
|
+
};
|
|
189
|
+
if (linkType.value === "phone")
|
|
190
|
+
return {
|
|
191
|
+
type: "tel",
|
|
192
|
+
inputmode: "tel",
|
|
193
|
+
autocomplete: "tel",
|
|
194
|
+
placeholder: "+",
|
|
195
|
+
label: text.value.linkPhone
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
type: "url",
|
|
199
|
+
inputmode: "url",
|
|
200
|
+
autocomplete: "url",
|
|
201
|
+
placeholder: "https://",
|
|
202
|
+
label: text.value.linkUrl
|
|
203
|
+
};
|
|
204
|
+
});
|
|
143
205
|
const editable = computed(() => !props.readonly && !control.disabled.value);
|
|
144
206
|
const showToolbar = computed(() => editable.value);
|
|
145
207
|
function serialize(current) {
|
|
@@ -338,6 +400,16 @@ const defaultToolbar = computed(() => [
|
|
|
338
400
|
}
|
|
339
401
|
]
|
|
340
402
|
]);
|
|
403
|
+
const optionalItems = computed(() => [
|
|
404
|
+
mark(
|
|
405
|
+
"underline",
|
|
406
|
+
text.value.underline,
|
|
407
|
+
"U",
|
|
408
|
+
"is-underline",
|
|
409
|
+
"underline",
|
|
410
|
+
() => editor.value?.chain().focus().toggleUnderline().run()
|
|
411
|
+
)
|
|
412
|
+
]);
|
|
341
413
|
function isToolbarGroups(toolbar) {
|
|
342
414
|
return toolbar.some((entry) => Array.isArray(entry));
|
|
343
415
|
}
|
|
@@ -352,7 +424,9 @@ function splitToolbar(toolbar) {
|
|
|
352
424
|
}
|
|
353
425
|
const groups = computed(() => {
|
|
354
426
|
if (!props.toolbar) return defaultToolbar.value.filter((group) => group.length);
|
|
355
|
-
const byId = new Map(
|
|
427
|
+
const byId = new Map(
|
|
428
|
+
[...defaultToolbar.value.flat(), ...optionalItems.value].map((item) => [item.id, item])
|
|
429
|
+
);
|
|
356
430
|
const headingItems = defaultToolbar.value[1] ?? [];
|
|
357
431
|
return splitToolbar(props.toolbar).map(
|
|
358
432
|
(group) => group.flatMap((entry) => {
|
|
@@ -379,10 +453,26 @@ function toggleLink() {
|
|
|
379
453
|
closeLink();
|
|
380
454
|
return;
|
|
381
455
|
}
|
|
382
|
-
|
|
456
|
+
const current = editor.value;
|
|
457
|
+
const href = current?.getAttributes("link").href ?? "";
|
|
458
|
+
const detected = detectLinkType(href);
|
|
459
|
+
linkType.value = props.linkTypes.includes(detected) ? detected : props.linkTypes[0] ?? "url";
|
|
460
|
+
linkUrl.value = href.slice(linkPrefixes[detected].length);
|
|
461
|
+
linkText.value = "";
|
|
462
|
+
linkNeedsText.value = Boolean(
|
|
463
|
+
current && current.state.selection.empty && !current.isActive("link")
|
|
464
|
+
);
|
|
383
465
|
linkOpen.value = true;
|
|
466
|
+
focusLinkInput();
|
|
467
|
+
}
|
|
468
|
+
function focusLinkInput() {
|
|
384
469
|
nextTick(() => linkInputRef.value?.focus());
|
|
385
470
|
}
|
|
471
|
+
function detectLinkType(href) {
|
|
472
|
+
if (/^mailto:/i.test(href)) return "email";
|
|
473
|
+
if (/^tel:/i.test(href)) return "phone";
|
|
474
|
+
return "url";
|
|
475
|
+
}
|
|
386
476
|
function closeLink(toButton = false) {
|
|
387
477
|
linkOpen.value = false;
|
|
388
478
|
if (!toButton) {
|
|
@@ -394,6 +484,8 @@ function closeLink(toButton = false) {
|
|
|
394
484
|
function normalizeUrl(value) {
|
|
395
485
|
const trimmed = value.trim();
|
|
396
486
|
if (/^(https?:|mailto:|tel:|\/|#)/i.test(trimmed)) return trimmed;
|
|
487
|
+
if (linkType.value === "email") return `mailto:${trimmed}`;
|
|
488
|
+
if (linkType.value === "phone") return `tel:${trimmed.replace(/[^\d+]/g, "")}`;
|
|
397
489
|
return `https://${trimmed}`;
|
|
398
490
|
}
|
|
399
491
|
function applyLink() {
|
|
@@ -403,7 +495,13 @@ function applyLink() {
|
|
|
403
495
|
removeLink();
|
|
404
496
|
return;
|
|
405
497
|
}
|
|
406
|
-
|
|
498
|
+
const href = normalizeUrl(linkUrl.value);
|
|
499
|
+
if (linkNeedsText.value) {
|
|
500
|
+
const label = linkText.value.trim() || linkUrl.value.trim();
|
|
501
|
+
current.chain().focus().insertContent({ type: "text", text: label, marks: [{ type: "link", attrs: { href } }] }).unsetMark("link").run();
|
|
502
|
+
} else {
|
|
503
|
+
current.chain().focus().extendMarkRange("link").setLink({ href }).run();
|
|
504
|
+
}
|
|
407
505
|
linkOpen.value = false;
|
|
408
506
|
}
|
|
409
507
|
function removeLink() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Editor } from '@tiptap/vue-3';
|
|
2
2
|
import type { Extensions } from '@tiptap/vue-3';
|
|
3
|
-
import type { EditorFormat, EditorHeadingLabel, EditorHeadingLevel, EditorMessages, EditorToolbar, EditorValue } from './types';
|
|
3
|
+
import type { EditorFormat, EditorHeadingLabel, EditorHeadingLevel, EditorLinkType, EditorMessages, EditorToolbar, EditorValue } from './types';
|
|
4
4
|
declare const __VLS_export: import("vue").DefineComponent<{
|
|
5
5
|
format?: EditorFormat;
|
|
6
6
|
placeholder?: string | undefined;
|
|
@@ -17,6 +17,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
17
17
|
headingLabel?: EditorHeadingLabel;
|
|
18
18
|
toolbar?: EditorToolbar;
|
|
19
19
|
extensions?: Extensions;
|
|
20
|
+
linkTypes?: EditorLinkType[];
|
|
20
21
|
messages?: Partial<EditorMessages>;
|
|
21
22
|
modelValue?: EditorValue;
|
|
22
23
|
key?: PropertyKey | undefined;
|
|
@@ -231,6 +232,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
231
232
|
headingLabel?: EditorHeadingLabel;
|
|
232
233
|
toolbar?: EditorToolbar;
|
|
233
234
|
extensions?: Extensions;
|
|
235
|
+
linkTypes?: EditorLinkType[];
|
|
234
236
|
messages?: Partial<EditorMessages>;
|
|
235
237
|
modelValue?: EditorValue;
|
|
236
238
|
key?: PropertyKey | undefined;
|
|
@@ -429,10 +431,11 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
429
431
|
}>, {
|
|
430
432
|
invalid: boolean;
|
|
431
433
|
format: EditorFormat;
|
|
432
|
-
headings: EditorHeadingLevel[];
|
|
433
434
|
minHeight: string;
|
|
435
|
+
headings: EditorHeadingLevel[];
|
|
434
436
|
headingLabel: EditorHeadingLabel;
|
|
435
437
|
extensions: Extensions;
|
|
438
|
+
linkTypes: EditorLinkType[];
|
|
436
439
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
437
440
|
declare const _default: typeof __VLS_export;
|
|
438
441
|
export default _default;
|
|
@@ -90,7 +90,13 @@
|
|
|
90
90
|
|
|
91
91
|
.k-editor-glyph-text.is-strike {
|
|
92
92
|
text-decoration: line-through;
|
|
93
|
-
font-weight: 600;
|
|
93
|
+
font-weight: var(--k-font-weight-strong, 600);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.k-editor-glyph-text.is-underline {
|
|
97
|
+
text-decoration: underline;
|
|
98
|
+
text-underline-offset: 2px;
|
|
99
|
+
font-weight: var(--k-font-weight-strong, 600);
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
.k-editor-glyph-text.is-mono {
|
|
@@ -100,7 +106,7 @@
|
|
|
100
106
|
|
|
101
107
|
.k-editor-glyph-text.is-tabular {
|
|
102
108
|
font-variant-numeric: tabular-nums;
|
|
103
|
-
font-weight: 600;
|
|
109
|
+
font-weight: var(--k-font-weight-strong, 600);
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
.k-editor-glyph-text.is-quote {
|
|
@@ -125,6 +131,27 @@
|
|
|
125
131
|
width: auto;
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
.k-editor-link-type {
|
|
135
|
+
position: relative;
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
flex: 0 0 auto;
|
|
138
|
+
align-items: center;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.k-editor-link .k-editor-link-type > .k-field {
|
|
142
|
+
flex: 0 0 auto;
|
|
143
|
+
padding-right: 1.75rem;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.k-editor-link-type-icon {
|
|
148
|
+
--k-icon-size: 0.875rem;
|
|
149
|
+
position: absolute;
|
|
150
|
+
right: 0.5rem;
|
|
151
|
+
color: var(--k-text-dimmed);
|
|
152
|
+
pointer-events: none;
|
|
153
|
+
}
|
|
154
|
+
|
|
128
155
|
.k-editor-content {
|
|
129
156
|
flex: 1;
|
|
130
157
|
min-width: 0;
|
|
@@ -161,7 +188,7 @@
|
|
|
161
188
|
.k-editor-prose h3,
|
|
162
189
|
.k-editor-prose h4 {
|
|
163
190
|
margin: 1.25em 0 0.5em;
|
|
164
|
-
font-weight: 600;
|
|
191
|
+
font-weight: var(--k-font-weight-strong, 600);
|
|
165
192
|
line-height: 1.25;
|
|
166
193
|
letter-spacing: -0.015em;
|
|
167
194
|
color: var(--k-text);
|
|
@@ -180,7 +207,7 @@
|
|
|
180
207
|
}
|
|
181
208
|
|
|
182
209
|
.k-editor-prose strong {
|
|
183
|
-
font-weight: 600;
|
|
210
|
+
font-weight: var(--k-font-weight-strong, 600);
|
|
184
211
|
color: var(--k-text);
|
|
185
212
|
}
|
|
186
213
|
|
package/dist/editor/types.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export type EditorValue = string | JSONContent | null;
|
|
|
5
5
|
export type EditorMessages = KMessages['editor'];
|
|
6
6
|
export type EditorHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
7
7
|
export type EditorHeadingLabel = 'level' | 'relative';
|
|
8
|
-
export type
|
|
8
|
+
export type EditorLinkType = 'url' | 'email' | 'phone';
|
|
9
|
+
export type EditorToolbarItem = 'undo' | 'redo' | 'headings' | `h${EditorHeadingLevel}` | 'bold' | 'italic' | 'underline' | 'strike' | 'code' | 'bulletList' | 'orderedList' | 'blockquote' | 'link' | '|';
|
|
9
10
|
export type EditorToolbar = EditorToolbarItem[] | EditorToolbarItem[][];
|
package/dist/grid/DataGrid.vue
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
class="k-data-grid"
|
|
4
|
-
:class="[
|
|
4
|
+
:class="[
|
|
5
|
+
`k-data-grid-${density}`,
|
|
6
|
+
{ 'is-loading': loading, 'is-auto-height': autoHeight, 'is-fill': fill && !autoHeight }
|
|
7
|
+
]"
|
|
5
8
|
>
|
|
6
9
|
<GridToolbar
|
|
7
10
|
v-if="toolbar"
|
|
@@ -23,7 +26,7 @@
|
|
|
23
26
|
role="region"
|
|
24
27
|
:aria-label="label ?? text.label"
|
|
25
28
|
:aria-busy="loading ? 'true' : void 0"
|
|
26
|
-
:style="autoHeight ? void 0 : { height }"
|
|
29
|
+
:style="autoHeight || fill ? void 0 : { height }"
|
|
27
30
|
>
|
|
28
31
|
<AgGridVue
|
|
29
32
|
v-if="mounted"
|
|
@@ -31,23 +34,25 @@
|
|
|
31
34
|
class="k-data-grid-ag"
|
|
32
35
|
:theme="theme"
|
|
33
36
|
:row-data="rows"
|
|
34
|
-
:column-defs="
|
|
37
|
+
:column-defs="columnDefs"
|
|
35
38
|
:default-col-def="defaultColDef"
|
|
36
|
-
:get-row-id="
|
|
39
|
+
:get-row-id="rowIdGetter"
|
|
37
40
|
:loading="loading"
|
|
38
41
|
:locale-text="gridLocaleText"
|
|
39
|
-
:context="
|
|
40
|
-
:overlay-no-rows-template="noRowsTemplate"
|
|
41
|
-
:overlay-loading-template="loadingTemplate"
|
|
42
|
+
:context="gridContext"
|
|
43
|
+
:overlay-no-rows-template="gridOptions?.overlayNoRowsTemplate ?? noRowsTemplate"
|
|
44
|
+
:overlay-loading-template="gridOptions?.overlayLoadingTemplate ?? loadingTemplate"
|
|
42
45
|
:quick-filter-text="serverPagination ? void 0 : query"
|
|
43
|
-
:row-selection="
|
|
46
|
+
:row-selection="gridRowSelection"
|
|
44
47
|
:pagination="paginated"
|
|
45
48
|
:pagination-page-size="pageSize"
|
|
46
49
|
:pagination-page-size-selector="false"
|
|
47
50
|
:suppress-pagination-panel="true"
|
|
48
|
-
:auto-size-strategy="
|
|
51
|
+
:auto-size-strategy="gridAutoSizeStrategy"
|
|
49
52
|
:dom-layout="autoHeight ? 'autoHeight' : 'normal'"
|
|
50
|
-
:
|
|
53
|
+
:row-height="rowHeight ?? gridOptions?.rowHeight"
|
|
54
|
+
:header-height="headerHeight ?? gridOptions?.headerHeight"
|
|
55
|
+
:tooltip-show-delay="gridOptions?.tooltipShowDelay ?? 600"
|
|
51
56
|
:suppress-cell-focus="false"
|
|
52
57
|
v-on="gridEvents"
|
|
53
58
|
/>
|
|
@@ -69,6 +74,9 @@
|
|
|
69
74
|
class="k-data-grid-pager"
|
|
70
75
|
size="sm"
|
|
71
76
|
compact
|
|
77
|
+
:show-edges="showEdges"
|
|
78
|
+
:show-jump="showJump"
|
|
79
|
+
:loading="loading"
|
|
72
80
|
:total="totalRows"
|
|
73
81
|
:page="page"
|
|
74
82
|
:page-size="pageSize"
|
|
@@ -91,7 +99,7 @@
|
|
|
91
99
|
|
|
92
100
|
<script setup>
|
|
93
101
|
import { AgGridVue } from "ag-grid-vue3";
|
|
94
|
-
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
|
102
|
+
import { computed, nextTick, onMounted, ref, shallowRef, watch } from "vue";
|
|
95
103
|
import Pagination from "../components/data/Pagination.vue";
|
|
96
104
|
import Spinner from "../components/core/Spinner.vue";
|
|
97
105
|
import { useCopy } from "../composables/useCopy";
|
|
@@ -102,18 +110,24 @@ import { useLiveMessage } from "../internal/core/live";
|
|
|
102
110
|
import { readStorageJson, removeStorage, writeStorageJson } from "../internal/core/storage";
|
|
103
111
|
import GridToolbar from "./GridToolbar.vue";
|
|
104
112
|
import { escapeHtml } from "../internal/core/text";
|
|
105
|
-
import {
|
|
113
|
+
import {
|
|
114
|
+
registerGridEditorModules,
|
|
115
|
+
registerGridFilterModules,
|
|
116
|
+
registerGridModules
|
|
117
|
+
} from "./modules";
|
|
106
118
|
import { getGridTheme } from "./theme";
|
|
107
119
|
registerGridModules();
|
|
108
120
|
const props = defineProps({
|
|
109
121
|
rows: { type: Array, required: false, default: () => [] },
|
|
110
122
|
columns: { type: Array, required: true },
|
|
111
123
|
rowIdKey: { type: String, required: false },
|
|
124
|
+
getRowId: { type: Function, required: false },
|
|
112
125
|
storageKey: { type: String, required: false },
|
|
113
126
|
density: { type: String, required: false, default: "normal" },
|
|
114
127
|
selectable: { type: [Boolean, String], required: false, default: false },
|
|
115
128
|
loading: { type: Boolean, required: false },
|
|
116
129
|
height: { type: String, required: false, default: "28rem" },
|
|
130
|
+
fill: { type: Boolean, required: false },
|
|
117
131
|
autoHeight: { type: Boolean, required: false },
|
|
118
132
|
label: { type: String, required: false },
|
|
119
133
|
toolbar: { type: Boolean, required: false, default: false },
|
|
@@ -124,17 +138,39 @@ const props = defineProps({
|
|
|
124
138
|
searchDebounce: { type: Number, required: false, default: 0 },
|
|
125
139
|
serverSort: { type: Boolean, required: false },
|
|
126
140
|
pagination: { type: Boolean, required: false },
|
|
141
|
+
showEdges: { type: Boolean, required: false },
|
|
142
|
+
showJump: { type: Boolean, required: false },
|
|
127
143
|
serverPagination: { type: Boolean, required: false },
|
|
128
144
|
total: { type: Number, required: false, default: 0 },
|
|
129
145
|
pageSizes: { type: Array, required: false, default: () => [10, 25, 50, 100] },
|
|
130
146
|
messages: { type: Object, required: false },
|
|
131
147
|
localeText: { type: Object, required: false },
|
|
132
|
-
gridOptions: { type: Object, required: false }
|
|
148
|
+
gridOptions: { type: Object, required: false },
|
|
149
|
+
rowHeight: { type: Number, required: false },
|
|
150
|
+
headerHeight: { type: Number, required: false },
|
|
151
|
+
headerText: { type: Function, required: false },
|
|
152
|
+
filterable: { type: Boolean, required: false },
|
|
153
|
+
editable: { type: Boolean, required: false }
|
|
133
154
|
});
|
|
134
|
-
const emit = defineEmits(["rowClick", "selectionChange", "sortChange", "copy"]);
|
|
155
|
+
const emit = defineEmits(["rowClick", "selectionChange", "sortChange", "filterChange", "copy"]);
|
|
135
156
|
const query = defineModel("query", { type: String, ...{ default: "" } });
|
|
136
157
|
const page = defineModel("page", { type: Number, ...{ default: 1 } });
|
|
137
158
|
const pageSize = defineModel("pageSize", { type: Number, ...{ default: 25 } });
|
|
159
|
+
const sort = defineModel("sort", { type: null, ...{ default: void 0 } });
|
|
160
|
+
if (props.filterable) registerGridFilterModules();
|
|
161
|
+
function hasEditableColumn() {
|
|
162
|
+
return props.editable || Boolean(props.gridOptions?.defaultColDef?.editable) || props.columns.some((column) => Boolean(column.editable));
|
|
163
|
+
}
|
|
164
|
+
if (hasEditableColumn()) registerGridEditorModules();
|
|
165
|
+
const columnDefs = computed(() => {
|
|
166
|
+
const headerText = props.headerText;
|
|
167
|
+
if (!headerText) return props.columns;
|
|
168
|
+
return props.columns.map((column) => {
|
|
169
|
+
if (column.headerName !== void 0 || !column.field) return column;
|
|
170
|
+
const headerName = headerText(String(column.field), column);
|
|
171
|
+
return headerName === void 0 ? column : { ...column, headerName };
|
|
172
|
+
});
|
|
173
|
+
});
|
|
138
174
|
const { locale, t } = useLocale();
|
|
139
175
|
const text = computed(() => ({
|
|
140
176
|
...locale.value.messages.grid,
|
|
@@ -163,8 +199,10 @@ const gridLocaleText = computed(() => ({
|
|
|
163
199
|
previousPage: text.value.previousPage,
|
|
164
200
|
noRowsToShow: text.value.noRows,
|
|
165
201
|
loadingOoo: text.value.loading,
|
|
202
|
+
...props.gridOptions?.localeText,
|
|
166
203
|
...props.localeText
|
|
167
204
|
}));
|
|
205
|
+
const gridContext = computed(() => ({ ...props.gridOptions?.context, ...cellContext.value }));
|
|
168
206
|
const live = useLiveMessage();
|
|
169
207
|
let announcedCount = null;
|
|
170
208
|
const theme = computed(() => getGridTheme(props.density));
|
|
@@ -187,8 +225,8 @@ const noRowsTemplate = computed(
|
|
|
187
225
|
const loadingTemplate = computed(
|
|
188
226
|
() => `<div class="k-data-grid-overlay"><span class="k-spinner k-spinner-md" aria-hidden="true"></span></div>`
|
|
189
227
|
);
|
|
190
|
-
const
|
|
191
|
-
if (!props.selectable) return
|
|
228
|
+
const gridRowSelection = computed(() => {
|
|
229
|
+
if (!props.selectable) return props.gridOptions?.rowSelection;
|
|
192
230
|
return {
|
|
193
231
|
mode: props.selectable === "single" ? "singleRow" : "multiRow",
|
|
194
232
|
enableClickSelection: false
|
|
@@ -197,10 +235,12 @@ const rowSelection = computed(() => {
|
|
|
197
235
|
const defaultColDef = computed(() => ({
|
|
198
236
|
sortable: true,
|
|
199
237
|
resizable: true,
|
|
238
|
+
filter: props.filterable || void 0,
|
|
200
239
|
minWidth: 80,
|
|
201
240
|
comparator: props.serverSort ? () => 0 : void 0,
|
|
202
241
|
valueFormatter: (params) => isEmpty(params.value) ? "-" : String(params.value),
|
|
203
|
-
cellRenderer: renderCell
|
|
242
|
+
cellRenderer: renderCell,
|
|
243
|
+
...props.gridOptions?.defaultColDef
|
|
204
244
|
}));
|
|
205
245
|
function isEmpty(value) {
|
|
206
246
|
return value === null || value === void 0 || value === "";
|
|
@@ -223,12 +263,20 @@ function renderCell(params) {
|
|
|
223
263
|
}
|
|
224
264
|
const storageName = computed(() => props.storageKey ? `k-grid:${props.storageKey}` : null);
|
|
225
265
|
const hasStoredState = ref(false);
|
|
226
|
-
const
|
|
227
|
-
(
|
|
228
|
-
)
|
|
229
|
-
|
|
266
|
+
const gridAutoSizeStrategy = computed(() => {
|
|
267
|
+
if (hasStoredState.value) return void 0;
|
|
268
|
+
if (props.gridOptions && "autoSizeStrategy" in props.gridOptions)
|
|
269
|
+
return props.gridOptions.autoSizeStrategy;
|
|
270
|
+
return { type: "fitGridWidth", defaultMinWidth: 100 };
|
|
271
|
+
});
|
|
272
|
+
function getRowIdByKey(params) {
|
|
230
273
|
return String(params.data[props.rowIdKey]);
|
|
231
274
|
}
|
|
275
|
+
const rowIdGetter = computed(() => {
|
|
276
|
+
if (props.getRowId) return props.getRowId;
|
|
277
|
+
if (props.rowIdKey) return getRowIdByKey;
|
|
278
|
+
return props.gridOptions?.getRowId;
|
|
279
|
+
});
|
|
232
280
|
function readStoredState() {
|
|
233
281
|
if (!storageName.value) return null;
|
|
234
282
|
const stored = readStorageJson(storageName.value);
|
|
@@ -247,8 +295,28 @@ function onGridReady(event) {
|
|
|
247
295
|
api.value = event.api;
|
|
248
296
|
const stored = readStoredState();
|
|
249
297
|
if (stored?.length) event.api.applyColumnState({ state: stored, applyOrder: true });
|
|
298
|
+
applySort();
|
|
250
299
|
syncPagination();
|
|
251
300
|
}
|
|
301
|
+
function sameSort(a, b) {
|
|
302
|
+
return (a?.column ?? null) === (b?.column ?? null) && (a?.direction ?? null) === (b?.direction ?? null);
|
|
303
|
+
}
|
|
304
|
+
function readSort(current) {
|
|
305
|
+
const sorted = current.getColumnState().filter((column) => column.sort).sort((a, b) => (a.sortIndex ?? 0) - (b.sortIndex ?? 0))[0];
|
|
306
|
+
return sorted?.sort ? { column: sorted.colId, direction: sorted.sort } : null;
|
|
307
|
+
}
|
|
308
|
+
function applySort() {
|
|
309
|
+
const current = api.value;
|
|
310
|
+
if (!current || sort.value === void 0) return;
|
|
311
|
+
if (sameSort(readSort(current), sort.value)) return;
|
|
312
|
+
const target = sort.value;
|
|
313
|
+
current.applyColumnState({
|
|
314
|
+
state: target ? [{ colId: target.column, sort: target.direction }] : [],
|
|
315
|
+
defaultState: { sort: null }
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
watch(sort, applySort);
|
|
319
|
+
watch(columnDefs, () => nextTick(applySort));
|
|
252
320
|
function syncPagination() {
|
|
253
321
|
const current = api.value;
|
|
254
322
|
if (!current || props.serverPagination) return;
|
|
@@ -281,9 +349,22 @@ function onSelectionChanged(event) {
|
|
|
281
349
|
emit("selectionChange", rows);
|
|
282
350
|
}
|
|
283
351
|
function onSortChanged(event) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
emit("sortChange",
|
|
352
|
+
const change = readSort(event.api);
|
|
353
|
+
if (!sameSort(change, sort.value)) sort.value = change;
|
|
354
|
+
if (props.serverSort) emit("sortChange", change);
|
|
355
|
+
}
|
|
356
|
+
const columnFilterModules = [
|
|
357
|
+
"TextFilterModule",
|
|
358
|
+
"NumberFilterModule",
|
|
359
|
+
"DateFilterModule",
|
|
360
|
+
"BigIntFilterModule",
|
|
361
|
+
"CustomFilterModule",
|
|
362
|
+
"SetFilterModule",
|
|
363
|
+
"MultiFilterModule"
|
|
364
|
+
];
|
|
365
|
+
function onFilterChanged(event) {
|
|
366
|
+
if (!columnFilterModules.some((name) => event.api.isModuleRegistered(name))) return;
|
|
367
|
+
emit("filterChange", event.api.getFilterModel(), event);
|
|
287
368
|
}
|
|
288
369
|
async function onCellContextMenu(event) {
|
|
289
370
|
if (!event.node || !event.column) return;
|
|
@@ -301,6 +382,7 @@ const gridEvents = {
|
|
|
301
382
|
rowClicked: (event) => emit("rowClick", event),
|
|
302
383
|
selectionChanged: onSelectionChanged,
|
|
303
384
|
sortChanged: onSortChanged,
|
|
385
|
+
filterChanged: onFilterChanged,
|
|
304
386
|
cellContextMenu: onCellContextMenu,
|
|
305
387
|
paginationChanged: syncPagination,
|
|
306
388
|
modelUpdated: onModelUpdated,
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import type { ColDef, GridApi, GridOptions, RowClickedEvent } from 'ag-grid-community';
|
|
1
|
+
import type { FilterChangedEvent, FilterModel, ColDef, GetRowIdParams, GridApi, GridOptions, RowClickedEvent } from 'ag-grid-community';
|
|
2
2
|
import type { GridDensity, GridMessages, GridSelection, GridSortChange } from './types';
|
|
3
3
|
declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
4
4
|
props: import("vue").PublicProps & __VLS_PrettifyLocal<({
|
|
5
5
|
rows?: T[];
|
|
6
6
|
columns: ColDef<T>[];
|
|
7
7
|
rowIdKey?: keyof T & string;
|
|
8
|
+
getRowId?: (params: GetRowIdParams<T>) => string;
|
|
8
9
|
storageKey?: string;
|
|
9
10
|
density?: GridDensity;
|
|
10
11
|
selectable?: GridSelection;
|
|
11
12
|
loading?: boolean;
|
|
12
13
|
height?: string;
|
|
14
|
+
fill?: boolean;
|
|
13
15
|
autoHeight?: boolean;
|
|
14
16
|
label?: string;
|
|
15
17
|
toolbar?: boolean;
|
|
@@ -20,24 +22,34 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
|
|
|
20
22
|
searchDebounce?: number;
|
|
21
23
|
serverSort?: boolean;
|
|
22
24
|
pagination?: boolean;
|
|
25
|
+
showEdges?: boolean;
|
|
26
|
+
showJump?: boolean;
|
|
23
27
|
serverPagination?: boolean;
|
|
24
28
|
total?: number;
|
|
25
29
|
pageSizes?: number[];
|
|
26
30
|
messages?: Partial<GridMessages>;
|
|
27
31
|
localeText?: Record<string, string>;
|
|
28
32
|
gridOptions?: GridOptions<T>;
|
|
33
|
+
rowHeight?: number;
|
|
34
|
+
headerHeight?: number;
|
|
35
|
+
headerText?: (field: string, column: ColDef<T>) => string | undefined;
|
|
36
|
+
filterable?: boolean;
|
|
37
|
+
editable?: boolean;
|
|
29
38
|
} & {
|
|
30
39
|
query?: string;
|
|
31
40
|
page?: number;
|
|
32
41
|
pageSize?: number;
|
|
42
|
+
sort?: GridSortChange | null | undefined;
|
|
33
43
|
}) & {
|
|
34
44
|
onCopy?: ((text: string) => any) | undefined;
|
|
35
45
|
"onUpdate:page"?: ((value: number) => any) | undefined;
|
|
36
46
|
"onUpdate:pageSize"?: ((value: number) => any) | undefined;
|
|
37
47
|
onRowClick?: ((event: RowClickedEvent<T, any>) => any) | undefined;
|
|
48
|
+
"onUpdate:sort"?: ((value: GridSortChange | null | undefined) => any) | undefined;
|
|
38
49
|
"onUpdate:query"?: ((value: string) => any) | undefined;
|
|
39
50
|
onSelectionChange?: ((rows: T[]) => any) | undefined;
|
|
40
51
|
onSortChange?: ((change: GridSortChange | null) => any) | undefined;
|
|
52
|
+
onFilterChange?: ((model: FilterModel, event: FilterChangedEvent<T, any>) => any) | undefined;
|
|
41
53
|
} & Partial<import("vue").HTMLAttributes & import("vue").ReservedProps>> & (typeof globalThis extends {
|
|
42
54
|
__VLS_PROPS_FALLBACK: infer P;
|
|
43
55
|
} ? P : {});
|
|
@@ -53,7 +65,7 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
|
|
|
53
65
|
api: GridApi<T> | null;
|
|
54
66
|
}) => any;
|
|
55
67
|
};
|
|
56
|
-
emit: (((evt: "copy", text: string) => void) & ((evt: "rowClick", event: RowClickedEvent<T, any>) => void) & ((evt: "selectionChange", rows: T[]) => void) & ((evt: "sortChange", change: GridSortChange | null) => void)) & (((event: "update:page", value: number) => void) & ((event: "update:pageSize", value: number) => void) & ((event: "update:query", value: string) => void));
|
|
68
|
+
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)) & (((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));
|
|
57
69
|
}>) => import("vue").VNode & {
|
|
58
70
|
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
59
71
|
};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<time
|
|
2
|
+
<time
|
|
3
|
+
v-if="formatted"
|
|
4
|
+
class="k-data-grid-date k-tabular"
|
|
5
|
+
:datetime="iso"
|
|
6
|
+
:title="params.relative ? absolute : void 0"
|
|
7
|
+
>{{ formatted }}</time
|
|
8
|
+
>
|
|
3
9
|
<EmptyCell v-else :context="cellContext" />
|
|
4
10
|
</template>
|
|
5
11
|
|
|
6
12
|
<script setup>
|
|
7
13
|
import { computed } from "vue";
|
|
8
14
|
import EmptyCell from "./EmptyCell.vue";
|
|
9
|
-
import { dateTimeFormat } from "../../internal/core/intl";
|
|
15
|
+
import { dateTimeFormat, formatRelativeTime } from "../../internal/core/intl";
|
|
10
16
|
const props = defineProps({
|
|
11
17
|
params: { type: Object, required: true }
|
|
12
18
|
});
|
|
@@ -19,11 +25,20 @@ const date = computed(() => {
|
|
|
19
25
|
const locale = computed(
|
|
20
26
|
() => props.params.locale ?? props.params.context?.locale
|
|
21
27
|
);
|
|
22
|
-
const
|
|
28
|
+
const absolute = computed(
|
|
23
29
|
() => date.value ? dateTimeFormat(locale.value, props.params.options ?? { dateStyle: "medium" }).format(
|
|
24
30
|
date.value
|
|
25
31
|
) : ""
|
|
26
32
|
);
|
|
33
|
+
const formatted = computed(() => {
|
|
34
|
+
const relative = props.params.relative;
|
|
35
|
+
if (!date.value || !relative) return absolute.value;
|
|
36
|
+
return formatRelativeTime(
|
|
37
|
+
date.value,
|
|
38
|
+
locale.value,
|
|
39
|
+
relative === true ? { numeric: "auto" } : relative
|
|
40
|
+
);
|
|
41
|
+
});
|
|
27
42
|
const iso = computed(() => date.value?.toISOString());
|
|
28
43
|
const cellContext = computed(() => props.params.context);
|
|
29
44
|
</script>
|