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
package/ui.data-table/types.ts
CHANGED
|
@@ -16,6 +16,12 @@ export type RowId = string | number;
|
|
|
16
16
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
17
|
export type Cell = CellObject & any;
|
|
18
18
|
|
|
19
|
+
export interface SearchMatch {
|
|
20
|
+
rowId: RowId;
|
|
21
|
+
columnKey: string;
|
|
22
|
+
indices: number[];
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
export interface RowData {
|
|
20
26
|
[key: string]: Cell;
|
|
21
27
|
}
|
|
@@ -131,6 +137,21 @@ export interface Props {
|
|
|
131
137
|
*/
|
|
132
138
|
bufferSize?: number;
|
|
133
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Search string to highlight in table cells.
|
|
142
|
+
*/
|
|
143
|
+
search?: string;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Index of the current search match to highlight (0-based).
|
|
147
|
+
*/
|
|
148
|
+
searchMatch?: number;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Enable text ellipsis for table cells (renders div wrapper for overflow handling).
|
|
152
|
+
*/
|
|
153
|
+
textEllipsis?: boolean;
|
|
154
|
+
|
|
134
155
|
/**
|
|
135
156
|
* Component config object.
|
|
136
157
|
*/
|
|
@@ -155,11 +176,20 @@ export interface UTableRowAttrs {
|
|
|
155
176
|
bodyRowAttrs: Ref<UnknownObject>;
|
|
156
177
|
bodyCellStickyLeftAttrs: Ref<UnknownObject>;
|
|
157
178
|
bodyCellStickyRightAttrs: Ref<UnknownObject>;
|
|
179
|
+
bodyCellSearchMatchAttrs: Ref<UnknownObject>;
|
|
180
|
+
bodyCellSearchMatchTextAttrs: Ref<UnknownObject>;
|
|
181
|
+
bodyCellSearchMatchActiveAttrs: Ref<UnknownObject>;
|
|
182
|
+
bodyCellSearchMatchTextActiveAttrs: Ref<UnknownObject>;
|
|
158
183
|
}
|
|
159
184
|
|
|
160
185
|
export interface UTableRowProps {
|
|
161
186
|
row: FlatRow;
|
|
162
187
|
columns: ColumnObject[];
|
|
188
|
+
/**
|
|
189
|
+
* Row index in the parent table (used for slot params).
|
|
190
|
+
* Optional to keep UTableRow mountable standalone in tests/internal usage.
|
|
191
|
+
*/
|
|
192
|
+
rowIndex?: number;
|
|
163
193
|
emptyCellLabel?: string;
|
|
164
194
|
selectable: boolean;
|
|
165
195
|
nestedLevel: number;
|
|
@@ -169,5 +199,12 @@ export interface UTableRowProps {
|
|
|
169
199
|
config: Config;
|
|
170
200
|
isChecked: boolean;
|
|
171
201
|
isExpanded: boolean;
|
|
202
|
+
isVisible: boolean;
|
|
172
203
|
columnPositions: Map<string, number>;
|
|
204
|
+
search?: string;
|
|
205
|
+
searchMatchColumns?: Set<string>;
|
|
206
|
+
activeSearchMatchColumn?: string;
|
|
207
|
+
textEllipsis?: boolean;
|
|
208
|
+
onToggleExpand?: (row: Row) => void;
|
|
209
|
+
onToggleCheckbox?: (row: Row) => void;
|
|
173
210
|
}
|
|
@@ -7,24 +7,30 @@ export function normalizeColumns(columns: Column[]): ColumnObject[] {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function mapRowColumns(row: Row, columns: ColumnObject[]): RowData {
|
|
10
|
-
const
|
|
11
|
-
return columns.some((column) => column.key === item[0] && column.isShown !== false);
|
|
12
|
-
});
|
|
10
|
+
const visibleKeys = new Set(columns.filter((col) => col.isShown !== false).map((col) => col.key));
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
const result: RowData = {};
|
|
13
|
+
|
|
14
|
+
for (const key of Object.keys(row)) {
|
|
15
|
+
if (visibleKeys.has(key)) {
|
|
16
|
+
result[key] = row[key];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return result;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
export function getFlatRows(tableRows: Row[]) {
|
|
18
24
|
const rows: FlatRow[] = [];
|
|
19
25
|
|
|
20
26
|
function addRow(row: Row, nestedLevel: number, parentRowId?: string | number) {
|
|
21
|
-
|
|
22
|
-
row
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
const flatRow: FlatRow = {
|
|
28
|
+
...row,
|
|
29
|
+
nestedLevel,
|
|
30
|
+
...(parentRowId !== undefined ? { parentRowId } : {}),
|
|
31
|
+
};
|
|
26
32
|
|
|
27
|
-
rows.push(
|
|
33
|
+
rows.push(flatRow);
|
|
28
34
|
|
|
29
35
|
if (row.row && !Array.isArray(row.row)) {
|
|
30
36
|
addRow(row.row, nestedLevel + 1, row.id);
|
|
@@ -121,10 +121,6 @@ function onChange() {
|
|
|
121
121
|
emit("input", newModelValue);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
function onIconClick() {
|
|
125
|
-
document.getElementById(elementId)?.click();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
124
|
/**
|
|
129
125
|
* Get element / nested component attributes for each config token ✨
|
|
130
126
|
* Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
|
|
@@ -185,10 +181,10 @@ const {
|
|
|
185
181
|
@change="onChange"
|
|
186
182
|
/>
|
|
187
183
|
|
|
188
|
-
<
|
|
184
|
+
<label
|
|
189
185
|
v-if="isChecked"
|
|
190
186
|
v-bind="partial ? partiallyCheckedAttrs : checkedAttrs"
|
|
191
|
-
|
|
187
|
+
:for="elementId"
|
|
192
188
|
>
|
|
193
189
|
<UIcon
|
|
194
190
|
v-if="partial"
|
|
@@ -198,7 +194,7 @@ const {
|
|
|
198
194
|
/>
|
|
199
195
|
|
|
200
196
|
<UIcon v-else :name="config.defaults.checkedIcon" color="inherit" v-bind="checkedIconAttrs" />
|
|
201
|
-
</
|
|
197
|
+
</label>
|
|
202
198
|
|
|
203
199
|
<template #bottom>
|
|
204
200
|
<!-- @slot Use it to add something below the checkbox. -->
|
package/ui.text-notify/config.ts
CHANGED
|
@@ -15,7 +15,7 @@ export default /*tw*/ {
|
|
|
15
15
|
bodyWarning: "{>body} bg-radial-[circle_at_0%_50%] from-warning/25 from-2.17% to-transparent",
|
|
16
16
|
bodyError: "{>body} bg-radial-[circle_at_0%_50%] from-error/25 from-2.17% to-transparent",
|
|
17
17
|
bodyInfo: "{>body} bg-radial-[circle_at_0%_50%] from-info/25 from-2.17% to-transparent",
|
|
18
|
-
content: "w-full flex flex-col max-w-full text-medium text-inverted",
|
|
18
|
+
content: "w-full flex flex-col max-w-full text-medium text-inverted break-all text-wrap",
|
|
19
19
|
label: "mb-0.5 font-medium",
|
|
20
20
|
description: "wrap-break-word font-normal",
|
|
21
21
|
statusIcon: "{UIcon} brightness-125 dark:brightness-75",
|
|
@@ -48,6 +48,38 @@ const preparedNumber = computed(() => {
|
|
|
48
48
|
);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
const formattedNumber = computed(() => {
|
|
52
|
+
let result = "";
|
|
53
|
+
|
|
54
|
+
if (props.currencyAlign === "left" && props.currency) {
|
|
55
|
+
result += props.currency;
|
|
56
|
+
|
|
57
|
+
if (props.currencySpace) {
|
|
58
|
+
result += " ";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (props.value) {
|
|
63
|
+
result += mathSign.value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
result += preparedNumber.value.integer;
|
|
67
|
+
|
|
68
|
+
if (props.maxFractionDigits > 0) {
|
|
69
|
+
result += preparedNumber.value.decimalSeparator + preparedNumber.value.fraction;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (props.currencyAlign === "right" && props.currency) {
|
|
73
|
+
if (props.currencySpace) {
|
|
74
|
+
result += " ";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
result += props.currency;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
});
|
|
82
|
+
|
|
51
83
|
defineExpose({
|
|
52
84
|
/**
|
|
53
85
|
* A reference to the component's wrapper element for direct DOM manipulation.
|
|
@@ -76,10 +108,12 @@ const {
|
|
|
76
108
|
<!-- @slot Use it to add something before the number. -->
|
|
77
109
|
<slot name="left" />
|
|
78
110
|
|
|
79
|
-
<
|
|
111
|
+
<template v-if="raw">{{ formattedNumber }}</template>
|
|
112
|
+
|
|
113
|
+
<div v-else v-bind="numberAttrs" :data-test="getDataTest()">
|
|
80
114
|
<span v-if="currencyAlign === 'left' && currency" v-bind="currencyAttrs" v-text="currency" />
|
|
81
115
|
|
|
82
|
-
<span v-if="value" v-bind="mathSignAttrs" v-text="mathSign" />
|
|
116
|
+
<span v-if="value && mathSign" v-bind="mathSignAttrs" v-text="mathSign" />
|
|
83
117
|
|
|
84
118
|
<span v-bind="integerAttrs" v-text="preparedNumber.integer" />
|
|
85
119
|
|
package/ui.text-number/config.ts
CHANGED
|
@@ -92,6 +92,17 @@ Sizes.args = { enum: "size" };
|
|
|
92
92
|
export const CurrencyAlign = EnumTemplate.bind({});
|
|
93
93
|
CurrencyAlign.args = { enum: "currencyAlign", currency: "USD", currencySpace: true };
|
|
94
94
|
|
|
95
|
+
export const Raw = DefaultTemplate.bind({});
|
|
96
|
+
Raw.args = { raw: true, currency: "USD", currencySpace: true };
|
|
97
|
+
Raw.parameters = {
|
|
98
|
+
docs: {
|
|
99
|
+
description: {
|
|
100
|
+
story:
|
|
101
|
+
"When `raw` is enabled, the number is displayed without formatting and html tags, showing only the raw value.",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
95
106
|
export const LimitFractionDigits: StoryFn<UNumberArgs> = (args: UNumberArgs) => ({
|
|
96
107
|
components: { UNumber, UCol },
|
|
97
108
|
setup: () => ({ args }),
|
|
@@ -192,6 +192,96 @@ describe("UNumber.vue", () => {
|
|
|
192
192
|
});
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
+
it("Raw – renders formatted number as plain text without HTML elements when raw is true", () => {
|
|
196
|
+
const component = mount(UNumber, {
|
|
197
|
+
props: {
|
|
198
|
+
value,
|
|
199
|
+
raw: true,
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Should render plain text without the number div structure
|
|
204
|
+
expect(component.find("[vl-key='number']").exists()).toBe(false);
|
|
205
|
+
expect(component.text()).toContain("1 234,56");
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("Raw – renders with currency when raw is true and currency is set", () => {
|
|
209
|
+
const currency = "$";
|
|
210
|
+
|
|
211
|
+
const component = mount(UNumber, {
|
|
212
|
+
props: {
|
|
213
|
+
value,
|
|
214
|
+
currency,
|
|
215
|
+
currencyAlign: "left",
|
|
216
|
+
raw: true,
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(component.text()).toBe("$1 234,56");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("Raw – renders with currency and space when raw is true, currency is set, and currencySpace is true", () => {
|
|
224
|
+
const currency = "$";
|
|
225
|
+
|
|
226
|
+
const component = mount(UNumber, {
|
|
227
|
+
props: {
|
|
228
|
+
value,
|
|
229
|
+
currency,
|
|
230
|
+
currencyAlign: "left",
|
|
231
|
+
currencySpace: true,
|
|
232
|
+
raw: true,
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
expect(component.text()).toBe("$ 1 234,56");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("Raw – renders with currency on right when raw is true and currencyAlign is right", () => {
|
|
240
|
+
const currency = "$";
|
|
241
|
+
|
|
242
|
+
const component = mount(UNumber, {
|
|
243
|
+
props: {
|
|
244
|
+
value,
|
|
245
|
+
currency,
|
|
246
|
+
currencyAlign: "right",
|
|
247
|
+
raw: true,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(component.text()).toBe("1 234,56$");
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// eslint-disable-next-line vue/max-len
|
|
255
|
+
it("Raw – renders with currency on right and space when raw is true, currencyAlign is right, and currencySpace is true", () => {
|
|
256
|
+
const currency = "$";
|
|
257
|
+
|
|
258
|
+
const component = mount(UNumber, {
|
|
259
|
+
props: {
|
|
260
|
+
value,
|
|
261
|
+
currency,
|
|
262
|
+
currencyAlign: "right",
|
|
263
|
+
currencySpace: true,
|
|
264
|
+
raw: true,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
expect(component.text()).toBe("1 234,56 $");
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("Raw – renders with sign when raw is true and sign is set", () => {
|
|
272
|
+
const testNegativeValue = -123;
|
|
273
|
+
|
|
274
|
+
const component = mount(UNumber, {
|
|
275
|
+
props: {
|
|
276
|
+
value: testNegativeValue,
|
|
277
|
+
sign: MATH_SIGN_TYPE.auto as Props["sign"],
|
|
278
|
+
raw: true,
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
expect(component.text()).toContain(MATH_SIGN.MINUS);
|
|
283
|
+
});
|
|
284
|
+
|
|
195
285
|
it("MinFractionDigits – adds zeros to meet the minimum fraction digits requirement", () => {
|
|
196
286
|
const value = 123;
|
|
197
287
|
const minFractionDigits = 2;
|
package/ui.text-number/types.ts
CHANGED
|
@@ -48,6 +48,11 @@ export interface Props {
|
|
|
48
48
|
*/
|
|
49
49
|
currencySpace?: boolean;
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Show formatted number as plain text without HTML elements.
|
|
53
|
+
*/
|
|
54
|
+
raw?: boolean;
|
|
55
|
+
|
|
51
56
|
/**
|
|
52
57
|
* Minimal number of signs after the decimal separator (fractional part of a number).
|
|
53
58
|
*/
|
|
@@ -4,10 +4,10 @@ export function createMergeConfigs(cx: any): ({ defaultConfig, globalConfig, pro
|
|
|
4
4
|
propsConfig: any;
|
|
5
5
|
config?: {} | undefined;
|
|
6
6
|
isVariants?: boolean | undefined;
|
|
7
|
-
}) =>
|
|
7
|
+
}) => any;
|
|
8
8
|
export function createGetMergedConfig(cx: any): ({ defaultConfig, globalConfig, propsConfig, unstyled }: {
|
|
9
9
|
defaultConfig: any;
|
|
10
10
|
globalConfig: any;
|
|
11
11
|
propsConfig: any;
|
|
12
12
|
unstyled: any;
|
|
13
|
-
}) =>
|
|
13
|
+
}) => any;
|