vueless 1.3.9-beta.9 → 1.4.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/composables/useUI.ts +187 -155
- package/package.json +2 -2
- package/types.ts +11 -0
- package/ui.button-link/ULink.vue +18 -2
- package/ui.data-table/UTable.vue +568 -198
- package/ui.data-table/UTableRow.vue +372 -168
- package/ui.data-table/config.ts +8 -1
- package/ui.data-table/storybook/stories.ts +90 -0
- package/ui.data-table/tests/UTable.test.ts +317 -20
- package/ui.data-table/tests/UTableRow.test.ts +35 -104
- package/ui.data-table/types.ts +37 -0
- package/ui.data-table/utilTable.ts +16 -10
- package/ui.form-checkbox/UCheckbox.vue +3 -7
- package/ui.text-notify/config.ts +1 -1
- package/ui.text-number/UNumber.vue +36 -2
- package/ui.text-number/config.ts +1 -0
- package/ui.text-number/storybook/stories.ts +11 -0
- package/ui.text-number/tests/UNumber.test.ts +90 -0
- package/ui.text-number/types.ts +5 -0
- package/utils/node/mergeConfigs.d.ts +2 -2
- package/utils/node/mergeConfigs.js +240 -121
- package/utils/node/vuelessConfig.d.ts +1 -1
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Comment,
|
|
4
|
+
Fragment,
|
|
5
|
+
Text,
|
|
6
|
+
computed,
|
|
7
|
+
h,
|
|
8
|
+
onMounted,
|
|
9
|
+
useAttrs,
|
|
10
|
+
useSlots,
|
|
11
|
+
useTemplateRef,
|
|
12
|
+
} from "vue";
|
|
13
|
+
|
|
14
|
+
import { useUI } from "../composables/useUI";
|
|
15
|
+
import { useMutationObserver } from "../composables/useMutationObserver";
|
|
16
|
+
|
|
17
|
+
import { isEmptyValue } from "../utils/helper";
|
|
3
18
|
import { cx } from "../utils/ui";
|
|
4
|
-
import { hasSlotContent, isEmptyValue } from "../utils/helper";
|
|
5
19
|
|
|
6
20
|
import { PX_IN_REM } from "../constants";
|
|
7
|
-
import { mapRowColumns } from "./utilTable";
|
|
8
21
|
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
22
|
+
import defaultConfig from "./config";
|
|
23
|
+
import { mapRowColumns } from "./utilTable";
|
|
24
|
+
import { StickySide } from "./types";
|
|
11
25
|
|
|
12
|
-
import UIcon from "../ui.image-icon/UIcon.vue";
|
|
13
26
|
import UCheckbox from "../ui.form-checkbox/UCheckbox.vue";
|
|
27
|
+
import UIcon from "../ui.image-icon/UIcon.vue";
|
|
14
28
|
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
import { StickySide } from "./types";
|
|
29
|
+
import type { Slot, VNode } from "vue";
|
|
18
30
|
import type { Cell, CellObject, Row, UTableRowProps, Config, ColumnObject } from "./types";
|
|
19
31
|
|
|
20
32
|
const NESTED_ROW_SHIFT_REM = 1.5;
|
|
@@ -24,17 +36,19 @@ defineOptions({ internal: true });
|
|
|
24
36
|
|
|
25
37
|
const props = defineProps<UTableRowProps>();
|
|
26
38
|
|
|
27
|
-
const
|
|
39
|
+
const slots = useSlots();
|
|
40
|
+
const attrs = useAttrs();
|
|
28
41
|
|
|
29
42
|
const cellRef = useTemplateRef<HTMLDivElement[]>("cell");
|
|
30
|
-
const toggleWrapperRef = useTemplateRef<HTMLDivElement[]>("toggle-wrapper");
|
|
31
43
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
if (props.textEllipsis) {
|
|
45
|
+
useMutationObserver(cellRef, setCellTitle, {
|
|
46
|
+
subtree: true,
|
|
47
|
+
childList: true,
|
|
48
|
+
characterData: true,
|
|
49
|
+
attributes: false,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
38
52
|
|
|
39
53
|
const toggleIconConfig = computed(() => {
|
|
40
54
|
const nestedRow = props.row?.row;
|
|
@@ -63,17 +77,6 @@ function getToggleIconName() {
|
|
|
63
77
|
: props.config?.defaults?.expandIcon;
|
|
64
78
|
}
|
|
65
79
|
|
|
66
|
-
function getIconWidth() {
|
|
67
|
-
const icon = document.querySelector(`[data-row-toggle-icon='${props.row.id}']`);
|
|
68
|
-
const currentWrapperWidth = toggleWrapperRef.value?.at(0)?.getBoundingClientRect()?.width || 0;
|
|
69
|
-
|
|
70
|
-
if (icon) {
|
|
71
|
-
return `${icon.getBoundingClientRect().width / PX_IN_REM}rem`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return `${currentWrapperWidth / PX_IN_REM || 1}rem`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
80
|
function getCellClasses(row: Row, key: string) {
|
|
78
81
|
const isCellData = typeof row[key] === "object" && row[key] !== null && "class" in row[key];
|
|
79
82
|
const cell = row[key] as CellObject;
|
|
@@ -107,20 +110,6 @@ function getNestedCheckboxShift() {
|
|
|
107
110
|
return { transform: `translateX(${props.nestedLevel * LAST_NESTED_ROW_SHIFT_REM}rem)` };
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
function onClick(row: Row) {
|
|
111
|
-
emit("click", row);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function onDoubleClick(row: Row) {
|
|
115
|
-
const selection = window.getSelection();
|
|
116
|
-
|
|
117
|
-
if (selection) {
|
|
118
|
-
selection.removeAllRanges();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
emit("dblclick", row);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
113
|
function setCellTitle(mutations: MutationRecord[]) {
|
|
125
114
|
mutations.forEach((mutation) => {
|
|
126
115
|
const { target } = mutation;
|
|
@@ -145,10 +134,6 @@ function setElementTitle(element: HTMLElement) {
|
|
|
145
134
|
}
|
|
146
135
|
}
|
|
147
136
|
|
|
148
|
-
function onClickCell(cell: unknown | string | number, row: Row, key: string | number) {
|
|
149
|
-
emit("clickCell", cell, row, key);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
137
|
function getRowClasses(row: Row) {
|
|
153
138
|
const rowClasses = row?.class || "";
|
|
154
139
|
|
|
@@ -159,14 +144,6 @@ function getRowAttrs() {
|
|
|
159
144
|
return props.isChecked ? props.attrs.bodyRowCheckedAttrs.value : props.attrs.bodyRowAttrs.value;
|
|
160
145
|
}
|
|
161
146
|
|
|
162
|
-
function onToggleExpand(row: Row) {
|
|
163
|
-
emit("toggleExpand", row);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function onInputCheckbox(row: Row) {
|
|
167
|
-
emit("toggleCheckbox", row);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
147
|
function getStickyColumnStyle(column: ColumnObject) {
|
|
171
148
|
const position = props.columnPositions.get(column.key);
|
|
172
149
|
|
|
@@ -195,122 +172,349 @@ function getStickyColumnClass(column: ColumnObject) {
|
|
|
195
172
|
return "";
|
|
196
173
|
}
|
|
197
174
|
|
|
175
|
+
function isCellSearchMatch(key: string): boolean {
|
|
176
|
+
return Boolean(props.searchMatchColumns?.has(key));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isCellActiveSearchMatch(key: string): boolean {
|
|
180
|
+
return props.activeSearchMatchColumn === key;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getSearchMatchCellClass(key: string): string {
|
|
184
|
+
if (!isCellSearchMatch(key)) return "";
|
|
185
|
+
|
|
186
|
+
return isCellActiveSearchMatch(key)
|
|
187
|
+
? (props.attrs.bodyCellSearchMatchActiveAttrs.value.class as string)
|
|
188
|
+
: (props.attrs.bodyCellSearchMatchAttrs.value.class as string);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function getHighlightedHtml(value: Cell, key: string): string {
|
|
192
|
+
const text = String(formatCellValue(value));
|
|
193
|
+
const query = props.search;
|
|
194
|
+
|
|
195
|
+
if (!query || !isCellSearchMatch(key)) return escapeHtml(text);
|
|
196
|
+
|
|
197
|
+
const isActive = isCellActiveSearchMatch(key);
|
|
198
|
+
const matchClass = isActive
|
|
199
|
+
? (props.attrs.bodyCellSearchMatchTextActiveAttrs.value.class as string)
|
|
200
|
+
: (props.attrs.bodyCellSearchMatchTextAttrs.value.class as string);
|
|
201
|
+
|
|
202
|
+
const lowerText = text.toLowerCase();
|
|
203
|
+
const lowerQuery = query.toLowerCase();
|
|
204
|
+
const parts: string[] = [];
|
|
205
|
+
let lastIndex = 0;
|
|
206
|
+
|
|
207
|
+
let pos = lowerText.indexOf(lowerQuery);
|
|
208
|
+
|
|
209
|
+
while (~pos) {
|
|
210
|
+
if (pos > lastIndex) {
|
|
211
|
+
parts.push(escapeHtml(text.slice(lastIndex, pos)));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
parts.push(
|
|
215
|
+
`<mark class="${matchClass}">${escapeHtml(text.slice(pos, pos + query.length))}</mark>`,
|
|
216
|
+
);
|
|
217
|
+
lastIndex = pos + query.length;
|
|
218
|
+
pos = lowerText.indexOf(lowerQuery, lastIndex);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (lastIndex < text.length) {
|
|
222
|
+
parts.push(escapeHtml(text.slice(lastIndex)));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return parts.join("");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function escapeHtml(text: string): string {
|
|
229
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function isNestedFirstCell(index: number): boolean {
|
|
233
|
+
return (Boolean(props.row.row) || Boolean(props.nestedLevel)) && index === 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function getRowIndex(): number {
|
|
237
|
+
return props.rowIndex ?? 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function isSlotContentEmpty(content: unknown): boolean {
|
|
241
|
+
const toArray = (arg: unknown) => {
|
|
242
|
+
return Array.isArray(arg) ? arg : [arg];
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
if (content === null || content === undefined) return true;
|
|
246
|
+
|
|
247
|
+
if (typeof content === "boolean" || typeof content === "number") {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (typeof content === "string") {
|
|
252
|
+
return content.length === 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return toArray(content).every((node) => {
|
|
256
|
+
if (node === null || node === undefined) return true;
|
|
257
|
+
|
|
258
|
+
if (typeof node === "boolean" || typeof node === "number") {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (typeof node === "string") {
|
|
263
|
+
return node.length === 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const vnode = node as VNode;
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
vnode.type === Comment ||
|
|
270
|
+
(vnode.type === Text && !vnode.children?.length) ||
|
|
271
|
+
(vnode.type === Fragment && !vnode.children?.length)
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function resolveSlotContent(slot: Slot | undefined, slotParams: Record<string, unknown>) {
|
|
277
|
+
if (!slot) return null;
|
|
278
|
+
|
|
279
|
+
const content = slot(slotParams);
|
|
280
|
+
|
|
281
|
+
return isSlotContentEmpty(content) ? null : content;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function shouldRenderCellWrapper(row: Row, key: string): boolean {
|
|
285
|
+
return Boolean(
|
|
286
|
+
props.textEllipsis ||
|
|
287
|
+
(props.search && isCellSearchMatch(key)) ||
|
|
288
|
+
getCellContentClass(row, String(key)),
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function renderCellContent(value: Cell, key: string, cellIndex: number): VNode | VNode[] | string {
|
|
293
|
+
const keyStr = String(key);
|
|
294
|
+
|
|
295
|
+
const slotContent = resolveSlotContent(slots[`cell-${key}`], {
|
|
296
|
+
value,
|
|
297
|
+
row: props.row,
|
|
298
|
+
index: getRowIndex(),
|
|
299
|
+
cellIndex,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Check if slot exists
|
|
303
|
+
if (slotContent) {
|
|
304
|
+
return slotContent as VNode | VNode[] | string;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Render cell wrapper with highlighted HTML
|
|
308
|
+
if (shouldRenderCellWrapper(props.row, keyStr)) {
|
|
309
|
+
return h("div", {
|
|
310
|
+
ref: cellRef,
|
|
311
|
+
...props.attrs.bodyCellContentAttrs.value,
|
|
312
|
+
class: cx([
|
|
313
|
+
props.attrs.bodyCellContentAttrs.value.class,
|
|
314
|
+
getCellContentClass(props.row, keyStr),
|
|
315
|
+
]),
|
|
316
|
+
innerHTML: getHighlightedHtml(value, keyStr),
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Render plain text
|
|
321
|
+
return formatCellValue(value);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function renderNestedFirstCell(value: Cell, key: string, cellIndex: number): VNode {
|
|
325
|
+
const keyStr = String(key);
|
|
326
|
+
|
|
327
|
+
const expandSlotContent = resolveSlotContent(slots.expand, {
|
|
328
|
+
index: getRowIndex(),
|
|
329
|
+
row: props.row,
|
|
330
|
+
expanded: props.isExpanded,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const toggleIconNode = h(UIcon, {
|
|
334
|
+
size: "xs",
|
|
335
|
+
interactive: true,
|
|
336
|
+
name: getToggleIconName(),
|
|
337
|
+
color: "primary",
|
|
338
|
+
...toggleIconConfig.value,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const toggleWrapperNode = h(
|
|
342
|
+
"div",
|
|
343
|
+
{
|
|
344
|
+
...props.attrs.bodyCellNestedIconWrapperAttrs.value,
|
|
345
|
+
},
|
|
346
|
+
[toggleIconNode],
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const toggleIconWrapperNode = h(
|
|
350
|
+
"div",
|
|
351
|
+
{
|
|
352
|
+
"data-row-toggle-icon": props.row.id,
|
|
353
|
+
"data-expand-icon": props.row.id,
|
|
354
|
+
onDblclick: (e: Event) => e.stopPropagation(),
|
|
355
|
+
},
|
|
356
|
+
expandSlotContent || [toggleWrapperNode],
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
const cellSlotContent = resolveSlotContent(slots[`cell-${key}`], {
|
|
360
|
+
value,
|
|
361
|
+
row: props.row,
|
|
362
|
+
index: getRowIndex(),
|
|
363
|
+
cellIndex,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return h(
|
|
367
|
+
"div",
|
|
368
|
+
{
|
|
369
|
+
style: getNestedShift(),
|
|
370
|
+
...props.attrs.bodyCellNestedAttrs.value,
|
|
371
|
+
},
|
|
372
|
+
[
|
|
373
|
+
props.row.row ? toggleIconWrapperNode : null,
|
|
374
|
+
// Cell content
|
|
375
|
+
...(() => {
|
|
376
|
+
if (cellSlotContent) {
|
|
377
|
+
return Array.isArray(cellSlotContent) ? cellSlotContent : [cellSlotContent];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (shouldRenderCellWrapper(props.row, keyStr)) {
|
|
381
|
+
return [
|
|
382
|
+
h("div", {
|
|
383
|
+
ref: cellRef,
|
|
384
|
+
...props.attrs.bodyCellContentAttrs.value,
|
|
385
|
+
class: cx([
|
|
386
|
+
props.attrs.bodyCellContentAttrs.value.class,
|
|
387
|
+
getCellContentClass(props.row, keyStr),
|
|
388
|
+
]),
|
|
389
|
+
"data-test": getDataTest(`${key}-cell`),
|
|
390
|
+
innerHTML: getHighlightedHtml(value, keyStr),
|
|
391
|
+
}),
|
|
392
|
+
];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return [formatCellValue(value)];
|
|
396
|
+
})(),
|
|
397
|
+
].filter(Boolean),
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function renderTableCell(value: Cell, key: string, index: number): VNode {
|
|
402
|
+
const keyStr = String(key);
|
|
403
|
+
|
|
404
|
+
const nestedCellNode = isNestedFirstCell(index)
|
|
405
|
+
? renderNestedFirstCell(value, key, index)
|
|
406
|
+
: renderCellContent(value, key, index);
|
|
407
|
+
|
|
408
|
+
const cellChildren = Array.isArray(nestedCellNode) ? nestedCellNode : [nestedCellNode];
|
|
409
|
+
|
|
410
|
+
return h(
|
|
411
|
+
"td",
|
|
412
|
+
{
|
|
413
|
+
key: index,
|
|
414
|
+
...props.attrs.bodyCellBaseAttrs.value,
|
|
415
|
+
class: cx([
|
|
416
|
+
props.attrs.bodyCellBaseAttrs.value.class,
|
|
417
|
+
props.columns[index].tdClass,
|
|
418
|
+
getCellClasses(props.row, keyStr),
|
|
419
|
+
getStickyColumnClass(props.columns[index]),
|
|
420
|
+
getSearchMatchCellClass(keyStr),
|
|
421
|
+
]),
|
|
422
|
+
style: getStickyColumnStyle(props.columns[index]),
|
|
423
|
+
"data-cell-key": key,
|
|
424
|
+
"data-test": getDataTest(`${key}-cell`),
|
|
425
|
+
},
|
|
426
|
+
cellChildren,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function renderCheckboxCell(): VNode | null {
|
|
431
|
+
if (!props.selectable) return null;
|
|
432
|
+
|
|
433
|
+
const checkboxNode = h(UCheckbox, {
|
|
434
|
+
modelValue: props.isChecked,
|
|
435
|
+
size: "md",
|
|
436
|
+
...props.attrs.bodyCheckboxAttrs.value,
|
|
437
|
+
"data-id": props.row.id,
|
|
438
|
+
"data-checkbox-id": props.row.id,
|
|
439
|
+
"data-test": getDataTest("body-checkbox"),
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
return h(
|
|
443
|
+
"td",
|
|
444
|
+
{
|
|
445
|
+
...props.attrs.bodyCellCheckboxAttrs.value,
|
|
446
|
+
"data-checkbox-id": props.row.id,
|
|
447
|
+
class: cx([
|
|
448
|
+
props.attrs.bodyCellCheckboxAttrs.value.class,
|
|
449
|
+
props.columns[0]?.sticky === StickySide.Left
|
|
450
|
+
? props.attrs.bodyCellStickyLeftAttrs.value.class
|
|
451
|
+
: "",
|
|
452
|
+
]),
|
|
453
|
+
style: {
|
|
454
|
+
...getNestedCheckboxShift(),
|
|
455
|
+
...(props.columns[0]?.sticky === StickySide.Left ? { left: "0" } : {}),
|
|
456
|
+
},
|
|
457
|
+
onDblclick: (e: Event) => e.stopPropagation(),
|
|
458
|
+
},
|
|
459
|
+
[checkboxNode],
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function renderMainRow(): VNode {
|
|
464
|
+
const cells = Object.entries(mapRowColumns(props.row, props.columns)).map(
|
|
465
|
+
([key, value], index) => {
|
|
466
|
+
return renderTableCell(value, key, index);
|
|
467
|
+
},
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
return h(
|
|
471
|
+
"tr",
|
|
472
|
+
{
|
|
473
|
+
...attrs,
|
|
474
|
+
...getRowAttrs(),
|
|
475
|
+
class: cx([getRowAttrs().class, getRowClasses(props.row)]),
|
|
476
|
+
style: { display: props.isVisible ? "" : "none" },
|
|
477
|
+
},
|
|
478
|
+
[renderCheckboxCell(), ...cells].filter(Boolean),
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function renderNestedRow(nestedRowSlotContent: VNode[]): VNode {
|
|
483
|
+
const tdNode = h(
|
|
484
|
+
"td",
|
|
485
|
+
{ colspan: props.columns.length + Number(props.selectable) },
|
|
486
|
+
nestedRowSlotContent,
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
return h(
|
|
490
|
+
"tr",
|
|
491
|
+
{
|
|
492
|
+
class: props.row.class,
|
|
493
|
+
style: { display: props.isVisible ? "" : "none" },
|
|
494
|
+
},
|
|
495
|
+
[tdNode],
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function renderRows(): VNode[] {
|
|
500
|
+
if (props.row.parentRowId) {
|
|
501
|
+
const nestedRowSlotContent = resolveSlotContent(slots["nested-row"], {
|
|
502
|
+
index: getRowIndex(),
|
|
503
|
+
row: props.row,
|
|
504
|
+
nestedLevel: props.nestedLevel,
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
if (nestedRowSlotContent) {
|
|
508
|
+
return [renderNestedRow(nestedRowSlotContent)];
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return [renderMainRow()];
|
|
513
|
+
}
|
|
514
|
+
|
|
198
515
|
const { getDataTest } = useUI<Config>(defaultConfig);
|
|
199
516
|
</script>
|
|
200
517
|
|
|
201
518
|
<template>
|
|
202
|
-
<
|
|
203
|
-
v-if="!row.parentRowId || !hasSlotContent($slots['nested-row'])"
|
|
204
|
-
v-bind="{ ...$attrs, ...getRowAttrs() }"
|
|
205
|
-
:class="cx([getRowAttrs().class, getRowClasses(row)])"
|
|
206
|
-
@click="onClick(props.row)"
|
|
207
|
-
@dblclick="onDoubleClick(props.row)"
|
|
208
|
-
>
|
|
209
|
-
<td
|
|
210
|
-
v-if="selectable"
|
|
211
|
-
:style="{
|
|
212
|
-
...getNestedCheckboxShift(),
|
|
213
|
-
...(columns[0]?.sticky === StickySide.Left ? { left: '0' } : {}),
|
|
214
|
-
}"
|
|
215
|
-
v-bind="attrs.bodyCellCheckboxAttrs.value"
|
|
216
|
-
:class="
|
|
217
|
-
cx([
|
|
218
|
-
attrs.bodyCellCheckboxAttrs.value.class,
|
|
219
|
-
columns[0]?.sticky === StickySide.Left ? attrs.bodyCellStickyLeftAttrs.value.class : '',
|
|
220
|
-
])
|
|
221
|
-
"
|
|
222
|
-
@click.stop
|
|
223
|
-
@dblclick.stop
|
|
224
|
-
>
|
|
225
|
-
<UCheckbox
|
|
226
|
-
:model-value="isChecked"
|
|
227
|
-
size="md"
|
|
228
|
-
v-bind="attrs.bodyCheckboxAttrs.value"
|
|
229
|
-
:data-id="row.id"
|
|
230
|
-
:data-test="getDataTest('body-checkbox')"
|
|
231
|
-
@input="onInputCheckbox(row)"
|
|
232
|
-
/>
|
|
233
|
-
</td>
|
|
234
|
-
|
|
235
|
-
<td
|
|
236
|
-
v-for="(value, key, index) in mapRowColumns(row, columns)"
|
|
237
|
-
:key="index"
|
|
238
|
-
v-bind="attrs.bodyCellBaseAttrs.value"
|
|
239
|
-
:class="
|
|
240
|
-
cx([
|
|
241
|
-
columns[index].tdClass,
|
|
242
|
-
getCellClasses(row, String(key)),
|
|
243
|
-
getStickyColumnClass(columns[index]),
|
|
244
|
-
])
|
|
245
|
-
"
|
|
246
|
-
:style="getStickyColumnStyle(columns[index])"
|
|
247
|
-
@click="onClickCell(value, row, key)"
|
|
248
|
-
>
|
|
249
|
-
<div
|
|
250
|
-
v-if="(row.row || nestedLevel) && index === 0"
|
|
251
|
-
:style="getNestedShift()"
|
|
252
|
-
v-bind="attrs.bodyCellNestedAttrs.value"
|
|
253
|
-
>
|
|
254
|
-
<div
|
|
255
|
-
v-if="row.row"
|
|
256
|
-
:data-row-toggle-icon="row.id"
|
|
257
|
-
@dblclick.stop
|
|
258
|
-
@click.stop="onToggleExpand(row)"
|
|
259
|
-
>
|
|
260
|
-
<slot name="expand" :row="row" :expanded="isExpanded">
|
|
261
|
-
<div
|
|
262
|
-
ref="toggle-wrapper"
|
|
263
|
-
v-bind="attrs.bodyCellNestedIconWrapperAttrs.value"
|
|
264
|
-
:style="{ width: getIconWidth() }"
|
|
265
|
-
>
|
|
266
|
-
<UIcon
|
|
267
|
-
size="xs"
|
|
268
|
-
interactive
|
|
269
|
-
:name="getToggleIconName()"
|
|
270
|
-
color="primary"
|
|
271
|
-
v-bind="toggleIconConfig"
|
|
272
|
-
/>
|
|
273
|
-
</div>
|
|
274
|
-
</slot>
|
|
275
|
-
</div>
|
|
276
|
-
<slot :name="`cell-${key}`" :value="value" :row="row" :index="index">
|
|
277
|
-
<div
|
|
278
|
-
v-if="value"
|
|
279
|
-
ref="cell"
|
|
280
|
-
v-bind="attrs.bodyCellContentAttrs.value"
|
|
281
|
-
:class="
|
|
282
|
-
cx([attrs.bodyCellContentAttrs.value.class, getCellContentClass(row, String(key))])
|
|
283
|
-
"
|
|
284
|
-
:data-test="getDataTest(`${key}-cell`)"
|
|
285
|
-
>
|
|
286
|
-
{{ formatCellValue(value) }}
|
|
287
|
-
</div>
|
|
288
|
-
</slot>
|
|
289
|
-
</div>
|
|
290
|
-
|
|
291
|
-
<template v-else>
|
|
292
|
-
<slot :name="`cell-${key}`" :value="value" :row="row" :index="index">
|
|
293
|
-
<div
|
|
294
|
-
v-bind="attrs.bodyCellContentAttrs.value"
|
|
295
|
-
ref="cell"
|
|
296
|
-
:class="
|
|
297
|
-
cx([attrs.bodyCellContentAttrs.value.class, getCellContentClass(row, String(key))])
|
|
298
|
-
"
|
|
299
|
-
:data-test="getDataTest(`${key}-cell`)"
|
|
300
|
-
>
|
|
301
|
-
{{ formatCellValue(value) }}
|
|
302
|
-
</div>
|
|
303
|
-
</slot>
|
|
304
|
-
</template>
|
|
305
|
-
</td>
|
|
306
|
-
</tr>
|
|
307
|
-
|
|
308
|
-
<tr
|
|
309
|
-
v-if="row.parentRowId && hasSlotContent($slots['nested-row'], { row, nestedLevel })"
|
|
310
|
-
:class="row.class"
|
|
311
|
-
>
|
|
312
|
-
<td :colspan="columns.length + Number(selectable)">
|
|
313
|
-
<slot name="nested-row" :row="row" :nested-level="nestedLevel" />
|
|
314
|
-
</td>
|
|
315
|
-
</tr>
|
|
519
|
+
<component :is="renderRows" />
|
|
316
520
|
</template>
|
package/ui.data-table/config.ts
CHANGED
|
@@ -33,7 +33,7 @@ export default /*tw*/ {
|
|
|
33
33
|
base: "border border-solid border-muted rounded-medium bg-default overflow-x-auto ",
|
|
34
34
|
variants: {
|
|
35
35
|
virtualScroll: {
|
|
36
|
-
true: "h-150 overflow-y-auto
|
|
36
|
+
true: "h-150 overflow-y-auto",
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
},
|
|
@@ -97,6 +97,10 @@ export default /*tw*/ {
|
|
|
97
97
|
bodyCellNestedExpandIcon: "{UIcon}",
|
|
98
98
|
bodyCellNestedCollapseIcon: "{UIcon}",
|
|
99
99
|
bodyCheckbox: "{UCheckbox}",
|
|
100
|
+
bodyCellSearchMatch: "bg-warning/10",
|
|
101
|
+
bodyCellSearchMatchActive: "bg-warning/15",
|
|
102
|
+
bodyCellSearchMatchText: "bg-warning/50 rounded-xs",
|
|
103
|
+
bodyCellSearchMatchTextActive: "bg-warning rounded-xs",
|
|
100
104
|
bodyDateDivider: {
|
|
101
105
|
base: "{UDivider} py-2",
|
|
102
106
|
label: "py-0 leading-none",
|
|
@@ -143,6 +147,9 @@ export default /*tw*/ {
|
|
|
143
147
|
stickyHeader: false,
|
|
144
148
|
stickyFooter: false,
|
|
145
149
|
loading: false,
|
|
150
|
+
textEllipsis: false,
|
|
151
|
+
search: "",
|
|
152
|
+
searchMatch: -1,
|
|
146
153
|
/* icons */
|
|
147
154
|
expandIcon: "add",
|
|
148
155
|
collapseIcon: "remove",
|