@kubex/zinc 1.0.17 → 1.0.19

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.
@@ -41,7 +41,6 @@ on [form controls](/getting-started/form-controls) to learn more about form subm
41
41
 
42
42
  ### Distinct Selects
43
43
 
44
-
45
44
  ```html:preview
46
45
 
47
46
  <div class="form-spacing">
@@ -60,7 +59,6 @@ on [form controls](/getting-started/form-controls) to learn more about form subm
60
59
 
61
60
  ### Conditional Selects
62
61
 
63
-
64
62
  ```html:preview
65
63
 
66
64
  <div class="form-spacing">
@@ -423,6 +421,29 @@ so that we can consider whether the pattern needs to be updated.
423
421
  </zn-select>
424
422
  ```
425
423
 
424
+ ### Checkbox Prefix
425
+
426
+ You can place a checkbox in the `prefix` slot to visually join it to the select. The checkbox will match the height of
427
+ the control and can be interacted with independently (clicking or toggling it won’t open the select).
428
+
429
+ ```html:preview
430
+ <zn-select label="Notify me about" help-text="Use the checkbox to enable/disable notifications; choose a channel with the select">
431
+ <zn-checkbox slot="prefix" aria-label="Enable notifications"></zn-checkbox>
432
+ <zn-option value="email">Email</zn-option>
433
+ <zn-option value="sms">SMS</zn-option>
434
+ <zn-option value="push">Push</zn-option>
435
+ </zn-select>
436
+
437
+ <br />
438
+
439
+ <zn-select label="Large with checkbox" size="large" value="email">
440
+ <zn-checkbox slot="prefix" size="large" aria-label="Enable notifications"></zn-checkbox>
441
+ <zn-option value="email">Email</zn-option>
442
+ <zn-option value="sms">SMS</zn-option>
443
+ <zn-option value="push">Push</zn-option>
444
+ </zn-select>
445
+ ```
446
+
426
447
  ### Custom Tags
427
448
 
428
449
  When multiple options can be selected, you can provide custom tags by passing a function to the `getTag` property. Your
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "A collection of web components for building web applications based off of @shoelace-style/Shoelace",
5
5
  "keywords": [
6
6
  "web components",
@@ -0,0 +1,128 @@
1
+ import {type CSSResultGroup, html, unsafeCSS} from 'lit';
2
+ import {property, state} from "lit/decorators.js";
3
+ import ZincElement from '../../internal/zinc-element';
4
+ import type {ZnChangeEvent} from "../../events/zn-change";
5
+
6
+ import styles from './audio-select.scss';
7
+
8
+ interface AudioFile {
9
+ name: string;
10
+ url: string;
11
+ }
12
+
13
+ /**
14
+ * @summary Short summary of the component's intended use.
15
+ * @documentation https://zinc.style/components/audio-select
16
+ * @status experimental
17
+ * @since 1.0
18
+ *
19
+ * @slot - The default slot.
20
+ * @slot actions - The actions slot.
21
+ * @slot footer - The footer slot.
22
+ *
23
+ * @csspart base - The component's base wrapper.
24
+ *
25
+ * @cssproperty --example - An example CSS custom property.
26
+ */
27
+ export default class ZnAudioSelect extends ZincElement {
28
+ static styles: CSSResultGroup = unsafeCSS(styles);
29
+
30
+ private _selectedUrl: string = '';
31
+ @state() private _isPlaying: boolean = false;
32
+ private readonly _audio: HTMLAudioElement;
33
+
34
+ @property() label = '';
35
+ @property() placeholder = '';
36
+ @property({type: Object}) files: AudioFile[];
37
+
38
+ constructor() {
39
+ super();
40
+ this._audio = new Audio();
41
+
42
+ // Handle audio ending naturally
43
+ this._audio.addEventListener('ended', () => {
44
+ this._isPlaying = false;
45
+ // Reset to start so pressing play again restarts the audio
46
+ this._audio.currentTime = 0;
47
+ });
48
+ }
49
+
50
+ disconnectedCallback() {
51
+ super.disconnectedCallback();
52
+ this._stopAudio();
53
+ }
54
+
55
+ _stopAudio() {
56
+ if (this._audio) {
57
+ this._audio.pause();
58
+ this._audio.currentTime = 0;
59
+ }
60
+ this._isPlaying = false;
61
+ }
62
+
63
+ handleSelectChange(e: ZnChangeEvent) {
64
+ if (e.target !== e.currentTarget) {
65
+ return;
66
+ }
67
+
68
+ const url = (e.target as HTMLSelectElement).value;
69
+ this._selectedUrl = url;
70
+
71
+ this._stopAudio();
72
+
73
+ if (url) {
74
+ this._audio.src = url;
75
+ }
76
+ }
77
+
78
+ togglePreview(e: CustomEvent) {
79
+ e.stopPropagation();
80
+
81
+ if (!this._selectedUrl) {
82
+ e.preventDefault();
83
+ this._isPlaying = false;
84
+ this.requestUpdate();
85
+ return;
86
+ }
87
+
88
+ if (this._isPlaying) {
89
+ this._audio.pause();
90
+ this._isPlaying = false;
91
+ } else {
92
+ this._audio.play()
93
+ .then(() => {
94
+ this._isPlaying = true;
95
+ })
96
+ .catch(err => {
97
+ console.error("Audio playback failed:", err);
98
+ this._isPlaying = false;
99
+ this.requestUpdate();
100
+ });
101
+ }
102
+ }
103
+
104
+ render() {
105
+ return html`
106
+ <div class="container">
107
+ <div class="select-wrapper">
108
+ <zn-select label="${this.label}" @zn-change="${this.handleSelectChange}">
109
+ <zn-checkbox
110
+ slot="prefix"
111
+ checked-icon="pause"
112
+ unchecked-icon="play_arrow"
113
+ checked-color="primary"
114
+ unchecked-color="secondary"
115
+ ?checked="${this._isPlaying}"
116
+ @zn-change="${this.togglePreview}">
117
+ </zn-checkbox>
118
+
119
+ <zn-option value="" disabled selected>${this.placeholder || 'Select audio'}</zn-option>
120
+ ${this.files.map(file => html`
121
+ <zn-option value="${file.url}">${file.name}</zn-option>
122
+ `)}
123
+ </zn-select>
124
+ </div>
125
+ </div>
126
+ `;
127
+ }
128
+ }
@@ -0,0 +1,4 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ }
@@ -0,0 +1,10 @@
1
+ import '../../../dist/zn.min.js';
2
+ import { expect, fixture, html } from '@open-wc/testing';
3
+
4
+ describe('<zn-audio-select>', () => {
5
+ it('should render a component', async () => {
6
+ const el = await fixture(html` <zn-audio-select></zn-audio-select> `);
7
+
8
+ expect(el).to.exist;
9
+ });
10
+ });
@@ -0,0 +1,12 @@
1
+ import ZnAudioSelect from './audio-select.component';
2
+
3
+ export * from './audio-select.component';
4
+ export default ZnAudioSelect;
5
+
6
+ ZnAudioSelect.define('zn-audio-select');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-audio-select': ZnAudioSelect;
11
+ }
12
+ }
@@ -7,12 +7,14 @@ import {ifDefined} from "lit/directives/if-defined.js";
7
7
  import {live} from "lit/directives/live.js";
8
8
  import {property, query, state} from 'lit/decorators.js';
9
9
  import {watch} from '../../internal/watch';
10
- import type {ZincFormControl} from '../../internal/zinc-element';
11
10
  import ZincElement from '../../internal/zinc-element';
12
11
  import ZnIcon from "../icon";
12
+ import type {ZincFormControl} from '../../internal/zinc-element';
13
13
 
14
14
  import styles from './checkbox.scss';
15
15
 
16
+ type ColorOption = 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' | 'transparent';
17
+
16
18
  /**
17
19
  * @summary Short summary of the component's intended use.
18
20
  * @documentation https://zinc.style/components/checkbox
@@ -23,7 +25,7 @@ import styles from './checkbox.scss';
23
25
  *
24
26
  * @slot - The checkbox's label.
25
27
  * @slot description - A description of the checkbox's label. Serves as help text for a checkbox item. Alternatively, you can use the `description` attribute.
26
- * @slot selected-content - Use to nest rich content (like an input) inside a selected checkbox item. Use only with the contained style.
28
+ * @slot selected-content - Use to nest rich content (like an input) inside a selected checkbox item. Use only with the contained style.
27
29
  *
28
30
  * @event zn-blur - Emitted when the checkbox loses focus.
29
31
  * @event zn-change - Emitted when the checked state changes.
@@ -36,6 +38,7 @@ import styles from './checkbox.scss';
36
38
  * @csspart control--checked - Matches the control part when the checkbox is checked.
37
39
  * @csspart control--indeterminate - Matches the control part when the checkbox is indeterminate.
38
40
  * @csspart checked-icon - The checked icon, an `<zn-icon>` element.
41
+ * @csspart unchecked-icon - The unchecked icon, an `<zn-icon>` element.
39
42
  * @csspart indeterminate-icon - The indeterminate icon, an `<zn-icon>` element.
40
43
  * @csspart label - The container that wraps the checkbox's label.
41
44
  * @csspart description - The container that wraps the checkbox's description.
@@ -105,6 +108,21 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
105
108
 
106
109
  @property({attribute: 'label-tooltip'}) labelTooltip: string;
107
110
 
111
+ /** The icon to show when the checkbox is checked. */
112
+ @property({attribute: 'checked-icon'}) checkedIcon = '';
113
+
114
+ /** The icon to show when the checkbox is unchecked. */
115
+ @property({attribute: 'unchecked-icon'}) uncheckedIcon = '';
116
+
117
+ /** The color of the checkbox. */
118
+ @property({reflect: true}) color: ColorOption = 'default';
119
+
120
+ /** The color of the checkbox when checked. Overrides `color`. */
121
+ @property({attribute: 'checked-color'}) checkedColor: ColorOption;
122
+
123
+ /** The color of the checkbox when unchecked. Overrides `color`. */
124
+ @property({attribute: 'unchecked-color'}) uncheckedColor: ColorOption;
125
+
108
126
  /** Gets the validity state object */
109
127
  get validity() {
110
128
  return this.input?.validity;
@@ -216,6 +234,11 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
216
234
  const hasLabelTooltip = this.hasSlotController.test('label-tooltip');
217
235
  const hasLabel = this.label || hasLabelSlot;
218
236
 
237
+ // Determine the effective color
238
+ const effectiveColor = (this.checked || this.indeterminate)
239
+ ? (this.checkedColor || this.color)
240
+ : (this.uncheckedColor || this.color);
241
+
219
242
  //
220
243
  // NOTE: we use a <div> around the label slot because of this Chrome bug.
221
244
  //
@@ -262,6 +285,14 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
262
285
  'checkbox--small': this.size === 'small',
263
286
  'checkbox--medium': this.size === 'medium',
264
287
  'checkbox--large': this.size === 'large',
288
+ 'checkbox--default': effectiveColor === 'default',
289
+ 'checkbox--primary': effectiveColor === 'primary',
290
+ 'checkbox--secondary': effectiveColor === 'secondary',
291
+ 'checkbox--error': effectiveColor === 'error',
292
+ 'checkbox--info': effectiveColor === 'info',
293
+ 'checkbox--success': effectiveColor === 'success',
294
+ 'checkbox--warning': effectiveColor === 'warning',
295
+ 'checkbox--transparent': effectiveColor === 'transparent',
265
296
  'checkbox--has-description': hasDescription,
266
297
  'checkbox--has-selected-content': this.hasSlotController.test('selected-content')
267
298
  })}>
@@ -289,10 +320,28 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
289
320
  ? ' control--indeterminate'
290
321
  : ''}"
291
322
  class="checkbox__control">
292
- ${this.checked
323
+
324
+ ${this.checked && !this.indeterminate
325
+ ? this.checkedIcon
326
+ ? html`
327
+ <zn-icon part="checked-icon" class="checkbox__icon" src="${this.checkedIcon}"></zn-icon>`
328
+ : html`
329
+ <svg viewBox="0 0 24 24"
330
+ fill="none"
331
+ stroke="currentColor"
332
+ stroke-width="4"
333
+ stroke-linecap="round"
334
+ stroke-linejoin="round"
335
+ class="svg-icon">
336
+ <polyline class="checkbox__checked-icon" points="20 6 9 17 4 12"></polyline>
337
+ </svg>`
338
+ : ''}
339
+
340
+ ${!this.checked && !this.indeterminate && this.uncheckedIcon
293
341
  ? html`
294
- <zn-icon part="checked-icon" class="checkbox__checked-icon" size="16" src="check"></zn-icon>`
342
+ <zn-icon part="unchecked-icon" class="checkbox__icon" src="${this.uncheckedIcon}"></zn-icon>`
295
343
  : ''}
344
+
296
345
  ${!this.checked && this.indeterminate
297
346
  ? html`
298
347
  <zn-icon
@@ -8,6 +8,7 @@
8
8
  .form-control {
9
9
  display: flex;
10
10
  flex-direction: column;
11
+ height: 100%;
11
12
  }
12
13
 
13
14
  .form-control .form-control__label {
@@ -23,6 +24,61 @@
23
24
  color: var(--zn-input-label-color);
24
25
  vertical-align: middle;
25
26
  cursor: pointer;
27
+ height: 100%;
28
+
29
+ --checkbox-color: var(--zn-color-primary-600);
30
+ --checkbox-color-hover: var(--zn-color-primary-500);
31
+ --checkbox-icon-color: var(--zn-color-neutral-0);
32
+ --checkbox-border-color: var(--zn-input-border-color);
33
+ --checkbox-bg-color: var(--zn-input-background-color);
34
+
35
+ &.checkbox--default {
36
+ --checkbox-color: var(--zn-color-primary-600);
37
+ --checkbox-color-hover: var(--zn-color-primary-500);
38
+ --checkbox-border-color: var(--zn-input-border-color);
39
+ }
40
+
41
+ &.checkbox--primary {
42
+ --checkbox-color: var(--zn-color-primary-600);
43
+ --checkbox-color-hover: var(--zn-color-primary-500);
44
+ --checkbox-border-color: var(--zn-color-primary-600);
45
+ }
46
+
47
+ &.checkbox--secondary {
48
+ --checkbox-color: var(--zn-color-neutral-600);
49
+ --checkbox-color-hover: var(--zn-color-neutral-500);
50
+ --checkbox-border-color: var(--zn-color-neutral-300);
51
+ --checkbox-bg-color: var(--zn-color-neutral-50);
52
+ }
53
+
54
+ &.checkbox--info {
55
+ --checkbox-color: var(--zn-color-info-600);
56
+ --checkbox-color-hover: var(--zn-color-info-500);
57
+ --checkbox-border-color: var(--zn-color-info-600);
58
+ }
59
+
60
+ &.checkbox--success {
61
+ --checkbox-color: var(--zn-color-success-600);
62
+ --checkbox-color-hover: var(--zn-color-success-500);
63
+ --checkbox-border-color: var(--zn-color-success-600);
64
+ }
65
+
66
+ &.checkbox--warning {
67
+ --checkbox-color: var(--zn-color-warning-600);
68
+ --checkbox-color-hover: var(--zn-color-warning-500);
69
+ --checkbox-border-color: var(--zn-color-warning-600);
70
+ }
71
+
72
+ &.checkbox--error {
73
+ --checkbox-color: var(--zn-color-danger-600);
74
+ --checkbox-color-hover: var(--zn-color-danger-500);
75
+ --checkbox-border-color: var(--zn-color-danger-600);
76
+ }
77
+
78
+ &.checkbox--transparent {
79
+ --checkbox-bg-color: transparent;
80
+ --checkbox-border-color: transparent;
81
+ }
26
82
  }
27
83
 
28
84
  .checkbox--small {
@@ -58,10 +114,10 @@
58
114
  width: var(--toggle-size);
59
115
  height: var(--toggle-size);
60
116
  box-shadow: var(--zn-shadow-x-small);
61
- border: solid var(--zn-input-border-width) var(--zn-input-border-color);
117
+ border: solid var(--zn-input-border-width) var(--checkbox-border-color);
118
+ background-color: var(--checkbox-bg-color);
62
119
  border-radius: 2px;
63
- background-color: var(--zn-input-background-color);
64
- color: var(--zn-color-neutral-0);
120
+ color: var(--checkbox-icon-color);
65
121
  transition: var(--zn-transition-fast) border-color,
66
122
  var(--zn-transition-fast) background-color,
67
123
  var(--zn-transition-fast) color,
@@ -76,25 +132,50 @@
76
132
  pointer-events: none;
77
133
  }
78
134
 
79
- .checkbox__checked-icon,
80
135
  .checkbox__indeterminate-icon {
81
136
  --icon-size: var(--toggle-size);
82
- --icon-color: var(--zn-color-neutral-0);
137
+ --icon-color: var(--checkbox-icon-color);
83
138
 
84
139
  display: inline-flex;
85
140
  width: var(--toggle-size);
86
141
  height: var(--toggle-size);
87
142
  }
88
143
 
144
+ .svg-icon {
145
+ width: max(16px, 70%);
146
+ height: max(16px, 70%);
147
+ --icon-color: var(--checkbox-icon-color);
148
+
149
+ .checkbox__checked-icon {
150
+ stroke-dasharray: 24;
151
+ stroke-dashoffset: 24;
152
+ transition: stroke-dashoffset 0.4s cubic-bezier(0.4, 0, 0.2, 1);
153
+ }
154
+ }
155
+
156
+ .checkbox--indeterminate .svg-icon {
157
+ color: transparent;
158
+ }
159
+
160
+ .checkbox--checked {
161
+ .svg-icon {
162
+ color: var(--checkbox-icon-color);
163
+ }
164
+
165
+ .checkbox__checked-icon {
166
+ stroke-dashoffset: 0;
167
+ }
168
+ }
169
+
89
170
  /* Hover */
90
171
  .checkbox:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:hover {
91
- border-color: var(--zn-input-border-color-hover);
172
+ border-color: var(--checkbox-color-hover, var(--zn-input-border-color-hover));
92
173
  background-color: var(--zn-input-background-color-hover);
93
174
  }
94
175
 
95
176
  /* Focus */
96
177
  .checkbox:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__input:focus-visible ~ .checkbox__control {
97
- border-color: var(--zn-color-primary-500);
178
+ border-color: var(--checkbox-color);
98
179
  outline: var(--zn-focus-ring);
99
180
  outline-offset: var(--zn-focus-ring-offset);
100
181
  }
@@ -102,15 +183,16 @@
102
183
  /* Checked/indeterminate */
103
184
  .checkbox--checked .checkbox__control,
104
185
  .checkbox--indeterminate .checkbox__control {
105
- border-color: var(--zn-color-primary-600);
106
- background-color: var(--zn-color-primary-600);
186
+ border-color: var(--checkbox-color);
187
+ background-color: var(--checkbox-color);
188
+ color: var(--checkbox-icon-color);
107
189
  }
108
190
 
109
191
  /* Checked/indeterminate + hover */
110
192
  .checkbox.checkbox--checked:not(.checkbox--disabled) .checkbox__control:hover,
111
193
  .checkbox.checkbox--indeterminate:not(.checkbox--disabled) .checkbox__control:hover {
112
- border-color: var(--zn-color-primary-500);
113
- background-color: var(--zn-color-primary-500);
194
+ border-color: var(--checkbox-color-hover);
195
+ background-color: var(--checkbox-color-hover);
114
196
  }
115
197
 
116
198
  /* Checked/indeterminate + focus */
@@ -200,3 +282,55 @@
200
282
  outline: 1px solid var(--zn-color-purple-600);
201
283
  transition: var(--zn-transition-medium) all;
202
284
  }
285
+
286
+ /** Select prefix slot */
287
+ :host([slot="prefix"]) {
288
+ height: 100%;
289
+ aspect-ratio: 1;
290
+
291
+ .checkbox__control {
292
+ height: 100%;
293
+ width: 100%;
294
+ border-left: unset;
295
+ border-top: unset;
296
+ border-bottom: unset;
297
+ box-shadow: unset;
298
+ background-color: var(--zn-color-purple-50);
299
+ }
300
+
301
+ .checkbox--primary .checkbox__control,
302
+ .checkbox--success .checkbox__control,
303
+ .checkbox--warning .checkbox__control,
304
+ .checkbox--error .checkbox__control,
305
+ .checkbox--info .checkbox__control {
306
+ background-color: var(--checkbox-color);
307
+ color: var(--zn-color-neutral-0);
308
+ border-color: var(--checkbox-color);
309
+ }
310
+
311
+ .checkbox--primary:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:hover,
312
+ .checkbox--success:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:hover,
313
+ .checkbox--warning:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:hover,
314
+ .checkbox--error:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:hover,
315
+ .checkbox--info:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:hover {
316
+ background-color: var(--checkbox-color-hover);
317
+ border-color: var(--checkbox-color-hover);
318
+ color: var(--zn-color-neutral-0);
319
+ }
320
+
321
+ .checkbox--secondary .checkbox__control {
322
+ background-color: var(--zn-color-neutral-200);
323
+ color: var(--zn-color-neutral-700);
324
+ }
325
+
326
+ .checkbox--secondary:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:hover {
327
+ background-color: var(--zn-color-neutral-300);
328
+ border-color: var(--zn-color-neutral-300);
329
+ color: var(--zn-color-neutral-800);
330
+ }
331
+
332
+ .checkbox--checked .checkbox__control {
333
+ background-color: var(--checkbox-color);
334
+ color: var(--zn-color-neutral-0);
335
+ }
336
+ }
@@ -417,21 +417,47 @@ export default class ZnDataTable extends ZincElement {
417
417
 
418
418
  // add groups to the current rows
419
419
  const toAdd = this.groups.split(',').map(g => g.trim()).filter(g => g.length > 0);
420
- toAdd.forEach((group) => {
421
- if (!groupedRows[this.humanize(group)]) {
422
- groupedRows[this.humanize(group)] = [];
420
+
421
+ const hasWildcard = toAdd.includes('*');
422
+ const explicitGroups = new Set(
423
+ toAdd
424
+ .filter(g => g !== '*')
425
+ .map(g => this.humanize(g))
426
+ );
427
+
428
+ // pre-create explicit groups
429
+ explicitGroups.forEach((group) => {
430
+ if (!groupedRows[group]) {
431
+ groupedRows[group] = [];
423
432
  }
424
433
  });
425
434
 
435
+ // pre-create wildcard grouping if requested
436
+ if (hasWildcard) {
437
+ groupedRows['*'] = [];
438
+ }
439
+
426
440
  this._rows.forEach((row: Row) => {
427
441
  const groupCell = row.cells.find((cell: Cell) => cell.column === this.groupBy);
428
- const groupValue = groupCell ? groupCell.text : 'Ungrouped';
429
- if (!groupedRows[this.humanize(groupValue)] && (!this.groups || groupValue === 'Ungrouped')) {
430
- groupedRows[this.humanize(groupValue)] = [];
442
+ const rawGroupValue = groupCell ? groupCell.text : 'Ungrouped';
443
+ const groupValue = this.humanize(rawGroupValue);
444
+
445
+ if (explicitGroups.has(groupValue)) {
446
+ groupedRows[groupValue].push(row);
447
+ return;
431
448
  }
432
449
 
433
- if (groupedRows[this.humanize(groupValue)]) {
434
- groupedRows[this.humanize(groupValue)].push(row);
450
+ if (hasWildcard) {
451
+ groupedRows['*'].push(row);
452
+ return;
453
+ }
454
+
455
+ // If no groups specified, or handling ungrouped fallback, create groups dynamically
456
+ if (!this.groups || groupValue === 'Ungrouped') {
457
+ if (!groupedRows[groupValue]) {
458
+ groupedRows[groupValue] = [];
459
+ }
460
+ groupedRows[groupValue].push(row);
435
461
  }
436
462
  });
437
463
 
@@ -447,7 +473,7 @@ export default class ZnDataTable extends ZincElement {
447
473
  <zn-sp flush>
448
474
  ${Object.keys(groupedRows).map((groupKey) => html`
449
475
  <div class="table-group">
450
- <h3 class="table-group__title">${groupKey === "Ungrouped" ? "" : groupKey}</h3>
476
+ <h3 class="table-group__title">${(groupKey === "Ungrouped" || groupKey === "*") ? "" : groupKey}</h3>
451
477
  ${this.renderTableData(groupedRows[groupKey])}
452
478
  </div>`
453
479
  )}