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.
@@ -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 filteredRow = Object.entries(row).filter((item) => {
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
- return Object.fromEntries(filteredRow) as RowData;
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
- if (parentRowId) {
22
- row.parentRowId = parentRowId;
23
- }
24
-
25
- row.nestedLevel = nestedLevel;
27
+ const flatRow: FlatRow = {
28
+ ...row,
29
+ nestedLevel,
30
+ ...(parentRowId !== undefined ? { parentRowId } : {}),
31
+ };
26
32
 
27
- rows.push(row as FlatRow);
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
- <div
184
+ <label
189
185
  v-if="isChecked"
190
186
  v-bind="partial ? partiallyCheckedAttrs : checkedAttrs"
191
- @click="onIconClick"
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
- </div>
197
+ </label>
202
198
 
203
199
  <template #bottom>
204
200
  <!-- @slot Use it to add something below the checkbox. -->
@@ -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
- <div v-bind="numberAttrs" :data-test="getDataTest()">
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
 
@@ -31,6 +31,7 @@ export default /*tw*/ {
31
31
  align: "left",
32
32
  currencyAlign: "right",
33
33
  currencySpace: false,
34
+ raw: false,
34
35
  minFractionDigits: 0,
35
36
  maxFractionDigits: 2,
36
37
  decimalSeparator: ",",
@@ -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;
@@ -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;