@xleddyl/443-components-vue 0.8.2 → 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.
Files changed (59) hide show
  1. package/README.md +33 -18
  2. package/dist/components/core/FormField.vue +5 -0
  3. package/dist/components/core/FormField.vue.d.ts +4 -0
  4. package/dist/components/core/Input.vue +59 -5
  5. package/dist/components/core/Input.vue.d.ts +8 -4
  6. package/dist/components/core/Textarea.vue +28 -3
  7. package/dist/components/core/Textarea.vue.d.ts +6 -4
  8. package/dist/components/data/DescriptionList.vue.d.ts +1 -1
  9. package/dist/components/data/Pagination.vue +8 -2
  10. package/dist/components/data/Pagination.vue.d.ts +2 -0
  11. package/dist/components/forms/CheckboxGroup.vue +7 -1
  12. package/dist/components/forms/CheckboxGroup.vue.d.ts +1 -0
  13. package/dist/components/forms/ColorInput.vue.d.ts +4 -4
  14. package/dist/components/forms/Combobox.vue +4 -3
  15. package/dist/components/forms/Combobox.vue.d.ts +1 -0
  16. package/dist/components/forms/DateInput.vue.d.ts +8 -4
  17. package/dist/components/forms/NumberInput.vue.d.ts +4 -4
  18. package/dist/components/forms/PasswordInput.vue.d.ts +8 -4
  19. package/dist/components/forms/PinInput.vue.d.ts +4 -4
  20. package/dist/components/forms/Select.vue +21 -2
  21. package/dist/components/forms/Select.vue.d.ts +3 -3
  22. package/dist/components/forms/Slider.vue.d.ts +4 -4
  23. package/dist/components/forms/TimeInput.vue.d.ts +8 -4
  24. package/dist/components/layout/Card.vue.d.ts +1 -1
  25. package/dist/components/overlays/ConfirmDialog.vue.d.ts +2 -2
  26. package/dist/components/overlays/ConfirmHost.vue.d.ts +2 -2
  27. package/dist/components/overlays/ContextMenu.vue.d.ts +2 -2
  28. package/dist/components/overlays/DropdownMenu.vue.d.ts +2 -2
  29. package/dist/components/overlays/types.d.ts +2 -2
  30. package/dist/components/templates/SettingsModal.vue.d.ts +2 -2
  31. package/dist/composables/useForm.d.ts +8 -1
  32. package/dist/composables/useForm.mjs +22 -4
  33. package/dist/composables/useOverlay.d.ts +1 -1
  34. package/dist/editor/RichTextEditor.vue +95 -9
  35. package/dist/editor/RichTextEditor.vue.d.ts +4 -1
  36. package/dist/editor/rich-text-editor.css +21 -0
  37. package/dist/editor/types.d.ts +1 -0
  38. package/dist/grid/DataGrid.vue +48 -18
  39. package/dist/grid/DataGrid.vue.d.ts +6 -1
  40. package/dist/grid/cells/ImageCell.vue +6 -1
  41. package/dist/grid/data-grid.css +16 -2
  42. package/dist/grid/index.d.ts +2 -1
  43. package/dist/grid/index.mjs +6 -1
  44. package/dist/grid/modules.d.ts +1 -0
  45. package/dist/grid/modules.mjs +23 -0
  46. package/dist/grid/theme.mjs +1 -1
  47. package/dist/grid/types.d.ts +3 -0
  48. package/dist/internal/forms/counter.d.ts +18 -0
  49. package/dist/internal/forms/counter.mjs +31 -0
  50. package/dist/internal/overlays/OverlayView.vue.d.ts +2 -2
  51. package/dist/locale/de.mjs +11 -1
  52. package/dist/locale/en.mjs +11 -1
  53. package/dist/locale/it.mjs +11 -1
  54. package/dist/locale/types.d.ts +10 -0
  55. package/dist/styles/core/form-field.css +6 -0
  56. package/dist/styles/core/input.css +29 -0
  57. package/dist/styles/core/textarea.css +10 -0
  58. package/dist/styles/forms/choice.css +6 -0
  59. package/package.json +1 -1
@@ -53,16 +53,42 @@
53
53
  </template>
54
54
  </div>
55
55
 
56
- <form v-if="linkOpen" class="k-editor-link" @submit.prevent="applyLink">
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="url"
62
- inputmode="url"
63
- placeholder="https://"
64
- :aria-label="text.linkUrl"
65
- @keydown.esc.stop.prevent="closeLink(true)"
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) {
@@ -391,10 +453,26 @@ function toggleLink() {
391
453
  closeLink();
392
454
  return;
393
455
  }
394
- linkUrl.value = editor.value?.getAttributes("link").href ?? "";
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
+ );
395
465
  linkOpen.value = true;
466
+ focusLinkInput();
467
+ }
468
+ function focusLinkInput() {
396
469
  nextTick(() => linkInputRef.value?.focus());
397
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
+ }
398
476
  function closeLink(toButton = false) {
399
477
  linkOpen.value = false;
400
478
  if (!toButton) {
@@ -406,6 +484,8 @@ function closeLink(toButton = false) {
406
484
  function normalizeUrl(value) {
407
485
  const trimmed = value.trim();
408
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, "")}`;
409
489
  return `https://${trimmed}`;
410
490
  }
411
491
  function applyLink() {
@@ -415,7 +495,13 @@ function applyLink() {
415
495
  removeLink();
416
496
  return;
417
497
  }
418
- current.chain().focus().extendMarkRange("link").setLink({ href: normalizeUrl(linkUrl.value) }).run();
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
+ }
419
505
  linkOpen.value = false;
420
506
  }
421
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;
@@ -433,6 +435,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
433
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;
@@ -131,6 +131,27 @@
131
131
  width: auto;
132
132
  }
133
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
+
134
155
  .k-editor-content {
135
156
  flex: 1;
136
157
  min-width: 0;
@@ -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 EditorLinkType = 'url' | 'email' | 'phone';
8
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[][];
@@ -1,7 +1,10 @@
1
1
  <template>
2
2
  <div
3
3
  class="k-data-grid"
4
- :class="[`k-data-grid-${density}`, { 'is-loading': loading, 'is-auto-height': autoHeight }]"
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"
@@ -33,23 +36,23 @@
33
36
  :row-data="rows"
34
37
  :column-defs="columnDefs"
35
38
  :default-col-def="defaultColDef"
36
- :get-row-id="props.rowIdKey ? getRowId : void 0"
39
+ :get-row-id="rowIdGetter"
37
40
  :loading="loading"
38
41
  :locale-text="gridLocaleText"
39
- :context="cellContext"
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="rowSelection"
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="autoSizeStrategy"
51
+ :auto-size-strategy="gridAutoSizeStrategy"
49
52
  :dom-layout="autoHeight ? 'autoHeight' : 'normal'"
50
53
  :row-height="rowHeight ?? gridOptions?.rowHeight"
51
54
  :header-height="headerHeight ?? gridOptions?.headerHeight"
52
- :tooltip-show-delay="600"
55
+ :tooltip-show-delay="gridOptions?.tooltipShowDelay ?? 600"
53
56
  :suppress-cell-focus="false"
54
57
  v-on="gridEvents"
55
58
  />
@@ -71,6 +74,9 @@
71
74
  class="k-data-grid-pager"
72
75
  size="sm"
73
76
  compact
77
+ :show-edges="showEdges"
78
+ :show-jump="showJump"
79
+ :loading="loading"
74
80
  :total="totalRows"
75
81
  :page="page"
76
82
  :page-size="pageSize"
@@ -104,18 +110,24 @@ import { useLiveMessage } from "../internal/core/live";
104
110
  import { readStorageJson, removeStorage, writeStorageJson } from "../internal/core/storage";
105
111
  import GridToolbar from "./GridToolbar.vue";
106
112
  import { escapeHtml } from "../internal/core/text";
107
- import { registerGridFilterModules, registerGridModules } from "./modules";
113
+ import {
114
+ registerGridEditorModules,
115
+ registerGridFilterModules,
116
+ registerGridModules
117
+ } from "./modules";
108
118
  import { getGridTheme } from "./theme";
109
119
  registerGridModules();
110
120
  const props = defineProps({
111
121
  rows: { type: Array, required: false, default: () => [] },
112
122
  columns: { type: Array, required: true },
113
123
  rowIdKey: { type: String, required: false },
124
+ getRowId: { type: Function, required: false },
114
125
  storageKey: { type: String, required: false },
115
126
  density: { type: String, required: false, default: "normal" },
116
127
  selectable: { type: [Boolean, String], required: false, default: false },
117
128
  loading: { type: Boolean, required: false },
118
129
  height: { type: String, required: false, default: "28rem" },
130
+ fill: { type: Boolean, required: false },
119
131
  autoHeight: { type: Boolean, required: false },
120
132
  label: { type: String, required: false },
121
133
  toolbar: { type: Boolean, required: false, default: false },
@@ -126,6 +138,8 @@ const props = defineProps({
126
138
  searchDebounce: { type: Number, required: false, default: 0 },
127
139
  serverSort: { type: Boolean, required: false },
128
140
  pagination: { type: Boolean, required: false },
141
+ showEdges: { type: Boolean, required: false },
142
+ showJump: { type: Boolean, required: false },
129
143
  serverPagination: { type: Boolean, required: false },
130
144
  total: { type: Number, required: false, default: 0 },
131
145
  pageSizes: { type: Array, required: false, default: () => [10, 25, 50, 100] },
@@ -135,7 +149,8 @@ const props = defineProps({
135
149
  rowHeight: { type: Number, required: false },
136
150
  headerHeight: { type: Number, required: false },
137
151
  headerText: { type: Function, required: false },
138
- filterable: { type: Boolean, required: false }
152
+ filterable: { type: Boolean, required: false },
153
+ editable: { type: Boolean, required: false }
139
154
  });
140
155
  const emit = defineEmits(["rowClick", "selectionChange", "sortChange", "filterChange", "copy"]);
141
156
  const query = defineModel("query", { type: String, ...{ default: "" } });
@@ -143,6 +158,10 @@ const page = defineModel("page", { type: Number, ...{ default: 1 } });
143
158
  const pageSize = defineModel("pageSize", { type: Number, ...{ default: 25 } });
144
159
  const sort = defineModel("sort", { type: null, ...{ default: void 0 } });
145
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();
146
165
  const columnDefs = computed(() => {
147
166
  const headerText = props.headerText;
148
167
  if (!headerText) return props.columns;
@@ -180,8 +199,10 @@ const gridLocaleText = computed(() => ({
180
199
  previousPage: text.value.previousPage,
181
200
  noRowsToShow: text.value.noRows,
182
201
  loadingOoo: text.value.loading,
202
+ ...props.gridOptions?.localeText,
183
203
  ...props.localeText
184
204
  }));
205
+ const gridContext = computed(() => ({ ...props.gridOptions?.context, ...cellContext.value }));
185
206
  const live = useLiveMessage();
186
207
  let announcedCount = null;
187
208
  const theme = computed(() => getGridTheme(props.density));
@@ -204,8 +225,8 @@ const noRowsTemplate = computed(
204
225
  const loadingTemplate = computed(
205
226
  () => `<div class="k-data-grid-overlay"><span class="k-spinner k-spinner-md" aria-hidden="true"></span></div>`
206
227
  );
207
- const rowSelection = computed(() => {
208
- if (!props.selectable) return void 0;
228
+ const gridRowSelection = computed(() => {
229
+ if (!props.selectable) return props.gridOptions?.rowSelection;
209
230
  return {
210
231
  mode: props.selectable === "single" ? "singleRow" : "multiRow",
211
232
  enableClickSelection: false
@@ -218,7 +239,8 @@ const defaultColDef = computed(() => ({
218
239
  minWidth: 80,
219
240
  comparator: props.serverSort ? () => 0 : void 0,
220
241
  valueFormatter: (params) => isEmpty(params.value) ? "-" : String(params.value),
221
- cellRenderer: renderCell
242
+ cellRenderer: renderCell,
243
+ ...props.gridOptions?.defaultColDef
222
244
  }));
223
245
  function isEmpty(value) {
224
246
  return value === null || value === void 0 || value === "";
@@ -241,12 +263,20 @@ function renderCell(params) {
241
263
  }
242
264
  const storageName = computed(() => props.storageKey ? `k-grid:${props.storageKey}` : null);
243
265
  const hasStoredState = ref(false);
244
- const autoSizeStrategy = computed(
245
- () => hasStoredState.value ? void 0 : { type: "fitGridWidth", defaultMinWidth: 100 }
246
- );
247
- function getRowId(params) {
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) {
248
273
  return String(params.data[props.rowIdKey]);
249
274
  }
275
+ const rowIdGetter = computed(() => {
276
+ if (props.getRowId) return props.getRowId;
277
+ if (props.rowIdKey) return getRowIdByKey;
278
+ return props.gridOptions?.getRowId;
279
+ });
250
280
  function readStoredState() {
251
281
  if (!storageName.value) return null;
252
282
  const stored = readStorageJson(storageName.value);
@@ -1,15 +1,17 @@
1
- import type { FilterChangedEvent, FilterModel, 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,6 +22,8 @@ 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[];
@@ -30,6 +34,7 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
30
34
  headerHeight?: number;
31
35
  headerText?: (field: string, column: ColDef<T>) => string | undefined;
32
36
  filterable?: boolean;
37
+ editable?: boolean;
33
38
  } & {
34
39
  query?: string;
35
40
  page?: number;
@@ -2,7 +2,8 @@
2
2
  <img
3
3
  v-if="src"
4
4
  class="k-data-grid-image"
5
- :class="{ 'is-rounded': params.rounded }"
5
+ :class="{ 'is-rounded': params.rounded, 'is-contain': params.fit === 'contain' }"
6
+ :style="sizeStyle"
6
7
  :src="src"
7
8
  :alt="alt"
8
9
  loading="lazy"
@@ -12,6 +13,7 @@
12
13
  v-else
13
14
  class="k-data-grid-image is-empty"
14
15
  :class="{ 'is-rounded': params.rounded }"
16
+ :style="sizeStyle"
15
17
  aria-hidden="true"
16
18
  >
17
19
  <Icon name="photo" />
@@ -28,5 +30,8 @@ const src = computed(() => {
28
30
  const raw = props.params.value;
29
31
  return typeof raw === "string" && raw ? raw : "";
30
32
  });
33
+ const sizeStyle = computed(
34
+ () => props.params.size ? { "--k-grid-image-size": props.params.size } : void 0
35
+ );
31
36
  const alt = computed(() => props.params.alt?.(props.params.data) ?? "");
32
37
  </script>
@@ -33,6 +33,16 @@
33
33
  min-height: 8rem;
34
34
  }
35
35
 
36
+ .k-data-grid.is-fill {
37
+ flex: 1 1 0%;
38
+ height: 100%;
39
+ min-height: 0;
40
+ }
41
+
42
+ .k-data-grid.is-fill > .k-data-grid-body {
43
+ flex: 1 1 0%;
44
+ }
45
+
36
46
  .k-data-grid-ag {
37
47
  height: 100%;
38
48
  }
@@ -174,8 +184,8 @@
174
184
 
175
185
  .k-data-grid-image {
176
186
  display: block;
177
- width: 1.75rem;
178
- height: 1.75rem;
187
+ width: var(--k-grid-image-size, 1.75rem);
188
+ height: var(--k-grid-image-size, 1.75rem);
179
189
  object-fit: cover;
180
190
  background: var(--k-fill);
181
191
  border-radius: var(--k-radius-sm);
@@ -188,6 +198,10 @@
188
198
  --k-icon-size: 1rem;
189
199
  }
190
200
 
201
+ .k-data-grid-image.is-contain {
202
+ object-fit: contain;
203
+ }
204
+
191
205
  .k-data-grid-image.is-rounded {
192
206
  border-radius: var(--k-radius-full);
193
207
  }
@@ -2,9 +2,10 @@ export { default as DataGrid } from './DataGrid.vue';
2
2
  export { default as GridToolbar } from './GridToolbar.vue';
3
3
  export { default as BooleanCell } from './cells/BooleanCell.vue';
4
4
  export { default as DateCell } from './cells/DateCell.vue';
5
+ export { default as EmptyCell } from './cells/EmptyCell.vue';
5
6
  export { default as BadgeCell } from './cells/BadgeCell.vue';
6
7
  export { default as ImageCell } from './cells/ImageCell.vue';
7
8
  export { default as LinkCell } from './cells/LinkCell.vue';
8
- export { registerGridFilterModules, registerGridModules } from './modules';
9
+ export { registerGridEditorModules, registerGridFilterModules, registerGridModules, } from './modules';
9
10
  export { createGridTheme } from './theme';
10
11
  export type * from './types';
@@ -2,8 +2,13 @@ export { default as DataGrid } from "./DataGrid.vue";
2
2
  export { default as GridToolbar } from "./GridToolbar.vue";
3
3
  export { default as BooleanCell } from "./cells/BooleanCell.vue";
4
4
  export { default as DateCell } from "./cells/DateCell.vue";
5
+ export { default as EmptyCell } from "./cells/EmptyCell.vue";
5
6
  export { default as BadgeCell } from "./cells/BadgeCell.vue";
6
7
  export { default as ImageCell } from "./cells/ImageCell.vue";
7
8
  export { default as LinkCell } from "./cells/LinkCell.vue";
8
- export { registerGridFilterModules, registerGridModules } from "./modules.mjs";
9
+ export {
10
+ registerGridEditorModules,
11
+ registerGridFilterModules,
12
+ registerGridModules
13
+ } from "./modules.mjs";
9
14
  export { createGridTheme } from "./theme.mjs";
@@ -1,2 +1,3 @@
1
1
  export declare function registerGridModules(): void;
2
2
  export declare function registerGridFilterModules(): void;
3
+ export declare function registerGridEditorModules(): void;
@@ -1,5 +1,13 @@
1
1
  import {
2
2
  CellApiModule,
3
+ CheckboxEditorModule,
4
+ CustomEditorModule,
5
+ DateEditorModule,
6
+ LargeTextEditorModule,
7
+ NumberEditorModule,
8
+ SelectEditorModule,
9
+ TextEditorModule,
10
+ UndoRedoEditModule,
3
11
  CellStyleModule,
4
12
  ClientSideRowModelApiModule,
5
13
  ClientSideRowModelModule,
@@ -51,3 +59,18 @@ export function registerGridFilterModules() {
51
59
  filtersRegistered = true;
52
60
  ModuleRegistry.registerModules([TextFilterModule, NumberFilterModule, DateFilterModule]);
53
61
  }
62
+ let editorsRegistered = false;
63
+ export function registerGridEditorModules() {
64
+ if (editorsRegistered) return;
65
+ editorsRegistered = true;
66
+ ModuleRegistry.registerModules([
67
+ TextEditorModule,
68
+ LargeTextEditorModule,
69
+ NumberEditorModule,
70
+ DateEditorModule,
71
+ CheckboxEditorModule,
72
+ SelectEditorModule,
73
+ CustomEditorModule,
74
+ UndoRedoEditModule
75
+ ]);
76
+ }
@@ -17,7 +17,7 @@ export function createGridTheme(density) {
17
17
  headerBackgroundColor: "var(--k-grid-header-background, var(--k-subtle))",
18
18
  headerTextColor: "var(--k-grid-header-text, var(--k-text-muted))",
19
19
  headerFontFamily: "var(--k-grid-header-font, var(--k-font-sans))",
20
- headerFontSize: 12,
20
+ headerFontSize: "var(--k-grid-header-font-size, 12px)",
21
21
  headerFontWeight: "var(--k-grid-header-font-weight, var(--k-font-weight-medium, 500))",
22
22
  headerRowBorder: { color: "var(--k-line)" },
23
23
  headerColumnBorder: false,
@@ -45,9 +45,12 @@ export interface BadgeCellParams {
45
45
  labels?: Record<string, string>;
46
46
  defaultTone?: Tone;
47
47
  }
48
+ export type ImageCellFit = 'cover' | 'contain';
48
49
  export interface ImageCellParams<T = unknown> {
49
50
  alt?: (data: T | undefined) => string;
50
51
  rounded?: boolean;
52
+ fit?: ImageCellFit;
53
+ size?: string;
51
54
  }
52
55
  export interface LinkCellParams<T = unknown> {
53
56
  href?: (value: unknown, data: T | undefined) => string;
@@ -0,0 +1,18 @@
1
+ interface CharacterCounterOptions {
2
+ enabled: () => boolean | undefined;
3
+ value: () => unknown;
4
+ attrs: Record<string, unknown>;
5
+ }
6
+ export interface CharacterCounterState {
7
+ id: string;
8
+ visible: boolean;
9
+ length: number;
10
+ max: number;
11
+ atLimit: boolean;
12
+ text: string;
13
+ label: string;
14
+ limitMessage: string;
15
+ width: number;
16
+ }
17
+ export declare function useCharacterCounter(options: CharacterCounterOptions): () => CharacterCounterState;
18
+ export {};
@@ -0,0 +1,31 @@
1
+ import { useId } from "vue";
2
+ import { useLocale } from "../../composables/useLocale.mjs";
3
+ function readLimit(attrs) {
4
+ const raw = attrs.maxlength ?? attrs.maxLength;
5
+ if (raw === void 0 || raw === null || raw === "" || raw === false) return void 0;
6
+ const limit = Number(raw);
7
+ return Number.isFinite(limit) && limit >= 0 ? Math.trunc(limit) : void 0;
8
+ }
9
+ export function useCharacterCounter(options) {
10
+ const { t } = useLocale();
11
+ const id = useId();
12
+ return () => {
13
+ const max = readLimit(options.attrs);
14
+ const visible = Boolean(options.enabled()) && max !== void 0;
15
+ const value = options.value();
16
+ const length = value === null || value === void 0 ? 0 : String(value).length;
17
+ const limit = max ?? 0;
18
+ const atLimit = visible && length >= limit;
19
+ return {
20
+ id,
21
+ visible,
22
+ length,
23
+ max: limit,
24
+ atLimit,
25
+ text: `${length} / ${limit}`,
26
+ label: visible ? t("form.characterCount", { count: length, max: limit }) : "",
27
+ limitMessage: atLimit ? t("form.characterLimitReached") : "",
28
+ width: String(limit).length * 2 + 3
29
+ };
30
+ };
31
+ }
@@ -208,9 +208,9 @@ declare const __VLS_export: import("vue").DefineComponent<{
208
208
  onVnodeBeforeUnmount?: (((vnode: import("vue").VNode) => void) | ((vnode: import("vue").VNode) => void)[]) | undefined;
209
209
  onVnodeUnmounted?: (((vnode: import("vue").VNode) => void) | ((vnode: import("vue").VNode) => void)[]) | undefined;
210
210
  flush?: boolean;
211
+ sheet?: boolean;
211
212
  onCancel?: ((payload: Event) => void) | undefined;
212
213
  side?: import("../../index.ts").DrawerSide;
213
- sheet?: boolean;
214
214
  onOpen?: ((() => any) & (() => any)) | undefined;
215
215
  closeLabel?: string;
216
216
  rootClass?: unknown;
@@ -431,9 +431,9 @@ declare const __VLS_export: import("vue").DefineComponent<{
431
431
  onVnodeBeforeUnmount?: (((vnode: import("vue").VNode) => void) | ((vnode: import("vue").VNode) => void)[]) | undefined;
432
432
  onVnodeUnmounted?: (((vnode: import("vue").VNode) => void) | ((vnode: import("vue").VNode) => void)[]) | undefined;
433
433
  flush?: boolean;
434
+ sheet?: boolean;
434
435
  onCancel?: ((payload: Event) => void) | undefined;
435
436
  side?: import("../../index.ts").DrawerSide;
436
- sheet?: boolean;
437
437
  onOpen?: ((() => any) & (() => any)) | undefined;
438
438
  closeLabel?: string;
439
439
  rootClass?: unknown;