@kubex/zinc 1.0.13 → 1.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
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",
@@ -73,52 +73,59 @@ zn-panel:not(.content-block--short) zn-header::part(header-description) {
73
73
  display: none;
74
74
  }
75
75
 
76
- li[data-list="ordered"] {
77
- counter-increment: list-0 1;
76
+ ol {
77
+ counter-reset: list-0 0;
78
78
  }
79
79
 
80
80
  @supports (counter-set:none) {
81
- li[data-list] {
81
+ ol li, ul li {
82
82
  counter-set: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0;
83
83
  }
84
84
  }
85
85
 
86
- li[data-list=bullet] > .ql-ui:before {
87
- content: "•";
86
+ ol, ul {
87
+ padding: 0;
88
+ margin: 0;
88
89
  }
89
90
 
90
- li[data-list=checked] > .ql-ui:before {
91
- content: "☑";
91
+ li[data-list=checked] > .ql-ui, li[data-list=unchecked] > .ql-ui {
92
+ color: #777;
92
93
  }
93
94
 
94
- li[data-list=unchecked] > .ql-ui:before {
95
- content: "☐";
95
+ li {
96
+ list-style-type: none;
97
+ padding-left: 1em;
98
+ position: relative;
96
99
  }
97
100
 
98
- li[data-list="ordered"] > .ql-ui::before {
99
- content: counter(list-0) ". ";
101
+ ul > li::before {
102
+ content: "";
100
103
  }
101
104
 
102
- ol {
103
- padding: 0;
104
- margin: 0;
105
+ ol > li {
106
+ counter-increment: list-0;
105
107
  }
106
108
 
107
- li[data-list=checked] > .ql-ui, li[data-list=unchecked] > .ql-ui {
108
- color: #777;
109
+ ol > li::before {
110
+ content: counter(list-0) ". ";
109
111
  }
110
112
 
111
- .ql-ui {
112
- position: absolute;
113
+ /* Checkbox list markers */
114
+ li[data-list="checked"]::before {
115
+ content: "☑";
113
116
  }
114
117
 
115
- li {
116
- list-style-type: none;
117
- padding-left: 1em;
118
- position: relative;
118
+ li[data-list="unchecked"]::before {
119
+ content: "☐";
120
+ }
121
+
122
+ li[data-list="checked"]::before,
123
+ li[data-list="unchecked"]::before {
124
+ color: #777;
119
125
  }
120
126
 
121
- li > .ql-ui::before {
127
+ ol li::before,
128
+ ul li::before {
122
129
  display: inline-block;
123
130
  margin-left: -1.5em;
124
131
  margin-right: 0.3em;
@@ -126,3 +133,8 @@ li > .ql-ui::before {
126
133
  white-space: nowrap;
127
134
  width: 1.2em;
128
135
  }
136
+
137
+ li:has(> .ql-ui)::before {
138
+ content: none;
139
+ display: none;
140
+ }
@@ -92,6 +92,7 @@ interface HeaderConfig {
92
92
  filterable?: boolean;
93
93
  hideHeader?: boolean;
94
94
  hideColumn?: boolean;
95
+ secondary?: boolean;
95
96
  }
96
97
 
97
98
  interface DataRequest {
@@ -252,7 +253,7 @@ export default class ZnDataTable extends ZincElement {
252
253
  const params: Record<string, any> = {};
253
254
  if (inputs) {
254
255
  inputs.forEach((input) => {
255
- const allowedInputs = ['zn-input', 'zn-select', 'zn-query-builder', 'zn-multiselect', 'zn-params-select', 'input', 'select', 'textarea'];
256
+ const allowedInputs = ['zn-input', 'zn-select', 'zn-query-builder', 'zn-multiselect', 'zn-params-select', 'zn-datepicker', 'input', 'select', 'textarea'];
256
257
  if (allowedInputs.includes(input.tagName.toLowerCase())) {
257
258
  const value = (input as AllowedInputElement).value as string || input.getAttribute('value');
258
259
  const name = (input as AllowedInputElement).name || input.getAttribute('name');
@@ -289,6 +290,10 @@ export default class ZnDataTable extends ZincElement {
289
290
 
290
291
  private rowHasActions: boolean = false;
291
292
 
293
+ private _expandedRows: Set<string> = new Set();
294
+ private _hiddenCells: Map<string, Cell[]> = new Map();
295
+ private _secondaryHeaders: HeaderConfig[];
296
+
292
297
  requestParams: Record<string, any> = {};
293
298
 
294
299
  refresh() {
@@ -458,22 +463,40 @@ export default class ZnDataTable extends ZincElement {
458
463
  }
459
464
 
460
465
  public renderTableData(data: any) {
466
+ // Primary (visible) headers exclude those explicitly hidden and those marked as secondary
461
467
  const filteredHeaders = Object.values(this.headers).filter((header: HeaderConfig) => {
462
- if (header.hideHeader || header.hideColumn) {
463
- return false;
464
- }
465
- return !Object.values(this.hiddenColumns).includes(header.key);
468
+ if (header.hideHeader || header.hideColumn) return false;
469
+
470
+ if (Object.values(this.hiddenColumns).includes(header.key)) return false;
471
+
472
+ return !header.secondary;
473
+ });
474
+
475
+ // Secondary headers (shown in expandable details)
476
+ this._secondaryHeaders = Object.values(this.headers).filter((header: HeaderConfig) => {
477
+ if (header.hideColumn) return false;
478
+
479
+ if (Object.values(this.hiddenColumns).includes(header.key)) return false;
480
+
481
+ return header.secondary === true;
466
482
  });
467
483
 
468
484
  this.rowHasActions = this._rows.some((row: Row) => row.actions && row.actions.length > 0);
469
485
 
470
- // To make sure the cells are in the same order as the headers, we need to map them cell.col to heading.key
486
+ // Compute and store hidden cells per row (secondary items only), and reorder visible cells to match header order
487
+ const secondaryKeys = new Set(this._secondaryHeaders.map(h => h.key));
488
+ this._hiddenCells.clear();
489
+ const visibleRowCells: Map<string, Cell[]> = new Map();
490
+
471
491
  this._rows.forEach((row: Row) => {
492
+ const originalCells = Array.isArray(row.cells) ? row.cells : [];
472
493
  const orderedCells: Cell[] = [];
473
- // const unorderedCells = row.cells.filter((c: Cell) => !filteredHeaders.some((h: HeaderConfig) => h.key === c.column));
494
+
495
+ const hiddenCells = originalCells.filter((c: Cell) => secondaryKeys.has(c.column));
496
+ this._hiddenCells.set(row.id, hiddenCells);
474
497
 
475
498
  filteredHeaders.forEach((header: HeaderConfig) => {
476
- const cell = row.cells.find((c: Cell) => c.column === header.key);
499
+ const cell = originalCells.find((c: Cell) => c.column === header.key);
477
500
  if (cell) {
478
501
  orderedCells.push(cell);
479
502
  } else {
@@ -481,9 +504,12 @@ export default class ZnDataTable extends ZincElement {
481
504
  }
482
505
  });
483
506
 
484
- row.cells = orderedCells;//.concat(unorderedCells);
507
+ visibleRowCells.set(row.id, orderedCells);
485
508
  });
486
509
 
510
+ const anyHidden = this.hasHiddenColumns();
511
+ const colCount = filteredHeaders.length + (this.rowHasActions ? 1 : 0) + (anyHidden ? 1 : 0);
512
+
487
513
  return html`
488
514
  <div style="overflow-x: auto">
489
515
  <table class="${classMap({
@@ -493,17 +519,27 @@ export default class ZnDataTable extends ZincElement {
493
519
  })}">
494
520
  <thead>
495
521
  <tr>
522
+ ${anyHidden ? html`
523
+ <th class="table__head table__head--expander"></th>` : nothing}
496
524
  ${filteredHeaders.map((header: HeaderConfig) => this.renderCellHeader(header))}
497
525
  ${this.rowHasActions ? html`
498
526
  <th></th>` : html``}
499
527
  </tr>
500
528
  </thead>
501
529
  <tbody>
502
- ${data.map((row: Row) => html`
503
- <tr class="${classMap({'table__row--selected': this.isRowSelected(row)})}">
504
- ${row.cells.map((value: Cell, index: number) => this.renderCellBody(index, value))}
505
- ${this.rowHasActions ? this.renderActions(row) : null}
506
- </tr>`)}
530
+ ${(data as Row[]).map((row: Row, rowIndex: number) => html`
531
+ <tr class="${classMap({
532
+ 'table__row--selected': this.isRowSelected(row),
533
+ 'table__row--data': true,
534
+ 'table__row--even': (rowIndex % 2) === 0,
535
+ 'table__row--odd': (rowIndex % 2) === 1,
536
+ })}" data-row-id="${row.id}">
537
+ ${anyHidden ? this.renderExpanderCell(row) : nothing}
538
+ ${(visibleRowCells.get(row.id) || row.cells).map((value: Cell, index: number) => this.renderCellBody(index, value))}
539
+ ${this.rowHasActions ? this.renderActions(row) : nothing}
540
+ </tr>
541
+ ${this._expandedRows.has(row.id) ? this.renderDetailsRow(row, colCount) : nothing}
542
+ `)}
507
543
  </tbody>
508
544
  </table>
509
545
  </div>
@@ -743,7 +779,7 @@ export default class ZnDataTable extends ZincElement {
743
779
  return;
744
780
  }
745
781
 
746
- const rows = Array.from(this.renderRoot.querySelectorAll('tbody tr'));
782
+ const rows = Array.from(this.renderRoot.querySelectorAll('tbody tr:not([data-details="true"])'));
747
783
  const index = rows.indexOf(parent as HTMLTableRowElement);
748
784
  if (index === -1) return;
749
785
 
@@ -900,15 +936,24 @@ export default class ZnDataTable extends ZincElement {
900
936
 
901
937
  private renderCellHeader(header: HeaderConfig) {
902
938
  const sortable = !Object.values(this.unsortableHeaders).includes(header.key) && !Object.values(this.hiddenHeaders).includes(header.key) && !this.unsortable;
903
- const lastHeader = Object.keys(this.headers).filter((key) => !Object.values(this.hiddenColumns).includes(key)).slice(-1)[0];
904
- const lastHeaderKey = lastHeader ? this.headers[lastHeader].key : '';
939
+
940
+ // Determine the last visible header considering secondary flags and hidden columns
941
+ const lastVisibleHeaderKey = Object.values(this.headers)
942
+ .filter((h: HeaderConfig) => {
943
+ if (h.hideHeader || h.hideColumn) return false;
944
+
945
+ if (Object.values(this.hiddenColumns).includes(h.key)) return false;
946
+
947
+ return !h.secondary;
948
+ })
949
+ .slice(-1)[0]?.key;
905
950
 
906
951
  return html`
907
952
  <th
908
953
  class="${classMap({
909
954
  'table__head': true,
910
955
  'table__head--wide': header.key === this.wideColumn,
911
- 'table__head--last': header.key === lastHeaderKey,
956
+ 'table__head--last': header.key === lastVisibleHeaderKey,
912
957
  'table__head--hidden': Object.values(this.hiddenHeaders).includes(header.key),
913
958
  })}"
914
959
  @click="${sortable ? this.updateSort(header.key) : undefined}">
@@ -922,16 +967,13 @@ export default class ZnDataTable extends ZincElement {
922
967
 
923
968
  private renderCellBody(index: number, value: Cell) {
924
969
  const filteredHeaders = Object.values(this.headers).filter((header: HeaderConfig) => {
925
- if (header.hideHeader || header.hideColumn) {
926
- return false;
927
- }
928
- return !Object.values(this.hiddenColumns).includes(header.key);
929
- });
970
+ if (header.hideHeader || header.hideColumn) return false;
971
+
972
+ if (Object.values(this.hiddenColumns).includes(header.key)) return false;
930
973
 
931
- const headerKeys: string[] = Object.keys(this.headers).filter(
932
- (key) => !Object.values(this.hiddenColumns).includes(key)
933
- );
934
- const headerKey: string = headerKeys[index];
974
+ return !header.secondary;
975
+ });
976
+ const headerKey: string = filteredHeaders[index]?.key;
935
977
 
936
978
  return html`
937
979
  <td
@@ -945,6 +987,65 @@ export default class ZnDataTable extends ZincElement {
945
987
  </td>`;
946
988
  }
947
989
 
990
+ private hasHiddenColumns(): boolean {
991
+ return this._secondaryHeaders.length > 0;
992
+ }
993
+
994
+ private renderExpanderCell(row: Row) {
995
+ const hasHidden = (this._hiddenCells.get(row.id) || []).length > 0;
996
+ if (!hasHidden) return html`
997
+ <td class="table__cell table__cell--expander"></td>`;
998
+
999
+ const expanded = this._expandedRows.has(row.id);
1000
+ const icon = expanded ? 'expand_more' : 'chevron_right';
1001
+
1002
+ return html`
1003
+ <td class="table__cell table__cell--expander">
1004
+ <zn-button
1005
+ color="transparent"
1006
+ size="x-small"
1007
+ icon="${icon}"
1008
+ icon-size="18"
1009
+ aria-label="${expanded ? 'Collapse row' : 'Expand row'}"
1010
+ @click="${(e: Event) => this.toggleRowExpansion(e, row)}">
1011
+ </zn-button>
1012
+ </td>`;
1013
+ }
1014
+
1015
+ private renderDetailsRow(row: Row, colSpan: number) {
1016
+ const hiddenCells = this._hiddenCells.get(row.id) || [];
1017
+ if (hiddenCells.length === 0) return html``;
1018
+
1019
+ return html`
1020
+ <tr class="table__row--details" data-details="true">
1021
+ <td colspan="${colSpan}">
1022
+ <div class="table__details">
1023
+ ${hiddenCells.map((cell: Cell) => {
1024
+ const header = Object.values(this.headers).find((h: HeaderConfig) => h.key === cell.column);
1025
+ const label = header?.label || this.humanize(cell.column);
1026
+ return html`
1027
+ <div class="table__details__item">
1028
+ <div class="table__details__label">${label}</div>
1029
+ <div class="table__details__value">${this.renderCell(cell)}</div>
1030
+ </div>`;
1031
+ })}
1032
+ </div>
1033
+ </td>
1034
+ </tr>`;
1035
+ }
1036
+
1037
+ private toggleRowExpansion(e: Event, row: Row) {
1038
+ e.stopPropagation();
1039
+
1040
+ if (this._expandedRows.has(row.id)) {
1041
+ this._expandedRows.delete(row.id);
1042
+ } else {
1043
+ this._expandedRows.add(row.id);
1044
+ }
1045
+
1046
+ this.requestUpdate();
1047
+ }
1048
+
948
1049
  private isRowSelected(row: Row): boolean {
949
1050
  return this.selectedRows.some((r: Row) => r.id === row.id);
950
1051
  }
@@ -18,6 +18,18 @@ table {
18
18
  table-layout: auto;
19
19
  text-align: left;
20
20
 
21
+ tbody tr:nth-of-type(even) {
22
+ background-color: unset !important;
23
+ }
24
+
25
+ tbody tr.table__row--data.table__row--odd:not(.table__row--selected) td {
26
+ background-color: rgba(var(--zn-body));
27
+ }
28
+
29
+ tbody tr.table__row--details td {
30
+ background-color: rgb(var(--zn-shadow));
31
+ }
32
+
21
33
  &.table--standalone {
22
34
  background-color: rgb(var(--zn-panel));
23
35
  border-radius: var(--zn-border-radius-large);
@@ -34,11 +46,16 @@ table {
34
46
  border-bottom: 1px solid rgb(var(--zn-border-color)) !important;
35
47
  }
36
48
 
37
- &.with-hover tbody tr:hover {
49
+ &.with-hover tbody tr.table__row--data:not(.table__row--selected):hover td {
38
50
  background-color: rgba(var(--zn-primary), 0.05) !important;
39
51
  cursor: pointer;
40
52
  }
41
53
 
54
+ &.with-hover tbody tr.table__row--details:hover {
55
+ background-color: inherit;
56
+ cursor: default;
57
+ }
58
+
42
59
  tbody tr td {
43
60
  border-top: 1px solid rgb(var(--zn-border-color)) !important;
44
61
  }
@@ -47,10 +64,6 @@ table {
47
64
  border-top: 0 !important;
48
65
  }
49
66
 
50
- tbody tr:nth-of-type(even) {
51
- background-color: rgba(var(--zn-body));
52
- }
53
-
54
67
  thead tr th:first-of-type,
55
68
  tbody tr td:first-of-type {
56
69
  padding-left: 14px;
@@ -169,6 +182,12 @@ table {
169
182
  visibility: hidden;
170
183
  }
171
184
  }
185
+
186
+ &--expander {
187
+ width: 28px;
188
+ min-width: 28px;
189
+ padding-right: 0;
190
+ }
172
191
  }
173
192
 
174
193
  &__cell {
@@ -232,6 +251,11 @@ table {
232
251
  gap: var(--zn-spacing-small);
233
252
  }
234
253
  }
254
+
255
+ &--expander {
256
+ width: 28px;
257
+ min-width: 28px;
258
+ }
235
259
  }
236
260
 
237
261
  &__header {
@@ -306,6 +330,28 @@ table {
306
330
  }
307
331
  }
308
332
  }
333
+
334
+ &__details {
335
+ display: grid;
336
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
337
+ gap: var(--zn-spacing-x-large) !important;
338
+
339
+ &__item {
340
+ display: flex;
341
+ flex-direction: column;
342
+ gap: var(--zn-spacing-2x-small, 4px);
343
+ }
344
+
345
+ &__label {
346
+ font-size: 12px;
347
+ color: rgb(var(--zn-text-subtle, var(--zn-text)));
348
+ opacity: 0.8;
349
+ }
350
+
351
+ &__value {
352
+ font-size: 14px;
353
+ }
354
+ }
309
355
  }
310
356
 
311
357
  .reduced-opacity .table tbody {
@@ -335,10 +381,6 @@ table {
335
381
  table tbody tr.table__row--selected {
336
382
  background-color: var(--zn-color-purple-100);
337
383
 
338
- &:nth-of-type(even) {
339
- background-color: hsla(from var(--zn-color-purple-200) h s l / 0.8);
340
- }
341
-
342
384
  &:hover {
343
385
  background-color: var(--zn-color-purple-200) !important;
344
386
  }
@@ -393,13 +435,16 @@ table tbody tr.table__row--selected {
393
435
  border-bottom-right-radius: 0;
394
436
  }
395
437
  }
438
+
439
+ &.table__row--odd {
440
+ background-color: hsla(from var(--zn-color-purple-200) h s l / 0.8);
441
+ }
396
442
  }
397
443
 
398
444
  table:has(tbody tr:first-of-type.table__row--selected) thead tr th {
399
445
  border-bottom-color: var(--zn-color-purple-600) !important;
400
446
  }
401
447
 
402
-
403
448
  .table-group__title {
404
449
  font-weight: var(--zn-font-weight-semibold);
405
450
  font-size: var(--zn-font-size-large);
@@ -411,3 +456,4 @@ table:has(tbody tr:first-of-type.table__row--selected) thead tr th {
411
456
  white-space: nowrap;
412
457
  text-overflow: ellipsis;
413
458
  }
459
+
@@ -374,7 +374,7 @@ export default class ZnEditor extends ZincElement implements ZincFormControl {
374
374
  }
375
375
 
376
376
  private _handleTextChange() {
377
- this.value = this.quillElement.root.innerHTML;
377
+ this.value = this.quillElement.getSemanticHTML();
378
378
  this.editorHtml.value = this.value;
379
379
  this.emit('zn-change');
380
380
  }
@@ -47,7 +47,7 @@ export default class ZnFilterWrapper extends ZincElement {
47
47
  event.preventDefault();
48
48
  const dataTable: ZnDataTable | null = this.closest('zn-data-table');
49
49
  if (dataTable) {
50
- const allowedInputs = ['zn-input', 'zn-select', 'zn-query-builder', 'zn-data-select', 'input', 'select', 'textarea'];
50
+ const allowedInputs = ['zn-input', 'zn-select', 'zn-query-builder', 'zn-data-select', 'zn-datepicker', 'input', 'select', 'textarea'];
51
51
  const inputs = Array.from(this.querySelectorAll(allowedInputs.join(','))) as AllowedInputElement[];
52
52
  const params: Record<string, any> = {};
53
53
  inputs.forEach(input => {
@@ -35,7 +35,6 @@ export default class ZnFormGroup extends ZincElement {
35
35
  /** The form groups help text. If you need to display HTML, use the `help-text` slot instead. */
36
36
  @property({attribute: 'help-text'}) helpText = '';
37
37
 
38
-
39
38
  render() {
40
39
  const hasLabelSlot = this.hasSlotController.test('label');
41
40
  const hasLabelTooltipSlot = this.hasSlotController.test('label-tooltip');
@@ -43,7 +43,6 @@
43
43
  display: grid;
44
44
  grid-template-columns: 1fr;
45
45
  gap: var(--zn-spacing-medium) var(--zn-spacing-small);
46
- flex: 1 1 auto;
47
46
 
48
47
  @include wc.media-query(md) {
49
48
  grid-template-columns: repeat(6, 1fr);
@@ -90,7 +90,9 @@ export default class ZnTile extends ZincElement {
90
90
  <div class="tile__left">
91
91
  <slot name="image" part="image" class="tile__image"></slot>
92
92
  <div class="tile__content">
93
- <p part="caption" class="tile__caption">${this.caption}</p>
93
+ <p part="caption" class="tile__caption">
94
+ <slot name="caption">${this.caption}</slot>
95
+ </p>
94
96
  <p part="description" class="tile__description">${this.description}</p>
95
97
  </div>
96
98
  </div>