@kubex/zinc 1.1.39 → 1.1.41

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.
@@ -89,18 +89,22 @@ module.exports = function (doc, options)
89
89
  });
90
90
 
91
91
  // Wrap code preview scripts in anonymous helpers so they don't run in the global scope
92
+ const jsTypes = new Set(['', 'text/javascript', 'application/javascript', 'application/ecmascript', 'text/ecmascript']);
92
93
  doc.querySelectorAll('.code-preview__preview script').forEach(script =>
93
94
  {
94
- if(script.type === 'module')
95
+ const type = (script.getAttribute('type') || '').toLowerCase();
96
+ if(type === 'module')
95
97
  {
96
98
  // Modules are already scoped
97
99
  script.textContent = script.innerHTML;
98
100
  }
99
- else
101
+ else if(jsTypes.has(type))
100
102
  {
101
103
  // Wrap non-modules in an anonymous function so they don't run in the global scope
102
104
  script.textContent = `(() => { ${script.innerHTML} })();`;
103
105
  }
106
+ // Otherwise leave alone: data blocks (e.g. type="application/json", type="zn-templates")
107
+ // are inert by spec and components read their original textContent directly.
104
108
  });
105
109
 
106
110
  return doc;
@@ -168,7 +168,6 @@ Combine `min-date` and `max-date` to constrain the selectable date range.
168
168
  help-text="Select dates within the conference period (March 1-15, 2026)">
169
169
  </zn-datepicker>
170
170
  ```
171
-
172
171
  ### Clearable
173
172
 
174
173
  Use the `clearable` attribute to add a "Clear" button to the calendar popup, allowing users to easily remove a selected date.
@@ -417,4 +416,14 @@ Use [CSS parts](#css-parts) to customize the way form controls are drawn. This e
417
416
  </style>
418
417
  ```
419
418
 
419
+ ### Date Time
420
+
421
+ Displays time.
420
422
 
423
+ ```html:preview
424
+ <zn-datepicker
425
+ time-picker
426
+ label="Conference dates"
427
+ help-text="Select date and time">
428
+ </zn-datepicker>
429
+ ```
@@ -119,6 +119,23 @@ In ticket mode (default), Enter creates new lines. Forms must be submitted using
119
119
  </script>
120
120
  ```
121
121
 
122
+ ### Caching Unsent Content
123
+
124
+ Set a `store-key` to cache what the agent has typed, so an unsent reply survives page reloads and navigation. Content is saved as it is typed, restored when an editor with the same key next loads, and cleared when the form is submitted or the editor is emptied.
125
+
126
+ Use a key unique to the conversation — for example the ticket or chat ID. Add the `local-storage` attribute to persist across tabs and browser restarts (otherwise `sessionStorage` is used, scoped to the current tab). Cached content expires after a day by default; tune with `store-ttl` (seconds).
127
+
128
+ ```html:preview
129
+ <zn-editor
130
+ name="reply"
131
+ interaction-type="ticket"
132
+ store-key="ticket-12345"
133
+ local-storage
134
+ style="height: 250px">
135
+ </zn-editor>
136
+ <p><small>Type something, then reload the page — your draft is restored.</small></p>
137
+ ```
138
+
122
139
  ### Enabling Features
123
140
 
124
141
  The editor has many optional features that can be enabled with boolean attributes.
@@ -104,3 +104,49 @@ layout: component
104
104
  <div class="style-abg"><zn-style border a-margin=a>ALL Margin</zn-style></div>
105
105
  ```
106
106
 
107
+ ### Border
108
+
109
+ Accepts any combination of `t`, `b`, `l`, `r` to render specific sides. Use `a` (or the equivalent `tblr`) as a shortcut for all four sides. The bare `border` attribute (no value) is equivalent to `border="a"` and is kept for backward compatibility.
110
+
111
+ ```html:preview
112
+ <zn-style border="t" pad=a>Top only</zn-style>
113
+ <zn-style border="b" pad=a>Bottom only</zn-style>
114
+ <zn-style border="l" pad=a>Left only</zn-style>
115
+ <zn-style border="r" pad=a>Right only</zn-style>
116
+ <zn-style border="tb" pad=a>Top + Bottom</zn-style>
117
+ <zn-style border="lr" pad=a>Left + Right</zn-style>
118
+ <zn-style border="tl" pad=a>Top + Left</zn-style>
119
+ <zn-style border="a" pad=a>All sides (a)</zn-style>
120
+ <zn-style border pad=a>All sides (bare attribute)</zn-style>
121
+ ```
122
+
123
+ ### Size
124
+
125
+ Adjusts font size in five steps mapped to the zinc font-size tokens. `m` is the default and applies no class.
126
+
127
+ | value | token |
128
+ | ----- | --------------------------- |
129
+ | `xs` | `--zn-font-size-x-small` |
130
+ | `s` | `--zn-font-size-small` |
131
+ | `m` | inherits (no class applied) |
132
+ | `l` | `--zn-font-size-large` |
133
+ | `xl` | `--zn-font-size-x-large` |
134
+
135
+ ```html:preview
136
+ <zn-style size="xs">Extra small</zn-style>
137
+ <zn-style size="s">Small</zn-style>
138
+ <zn-style size="m">Medium (default)</zn-style>
139
+ <zn-style size="l">Large</zn-style>
140
+ <zn-style size="xl">Extra large</zn-style>
141
+ ```
142
+
143
+ ### Muted
144
+
145
+ Dims the content with reduced opacity. Useful for placeholder-like values, em-dashes for empty cells, or any text you want visually de-emphasized without changing its colour.
146
+
147
+ ```html:preview
148
+ <zn-style>Regular text</zn-style>
149
+ <zn-style muted>Muted text</zn-style>
150
+ <zn-style muted>&mdash;</zn-style>
151
+ ```
152
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.1.39",
3
+ "version": "1.1.41",
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",
package/scss/_root.scss CHANGED
@@ -108,7 +108,27 @@
108
108
  }
109
109
 
110
110
  .zn-mono {
111
- font-family: var(--zn-font-family-mono, "monospace");
111
+ font-family: var(--zn-font-family-mono), monospace;
112
+ }
113
+
114
+ .zn-muted {
115
+ color: var(--zn-color-muted-text);
116
+ }
117
+
118
+ .zn-size-xs {
119
+ font-size: var(--zn-font-size-x-small);
120
+ }
121
+
122
+ .zn-size-s {
123
+ font-size: var(--zn-font-size-small);
124
+ }
125
+
126
+ .zn-size-l {
127
+ font-size: var(--zn-font-size-large);
128
+ }
129
+
130
+ .zn-size-xl {
131
+ font-size: var(--zn-font-size-x-large);
112
132
  }
113
133
 
114
134
  :root, [t="light"], .theme-light, [t="dark"], .theme-dark {
@@ -0,0 +1,50 @@
1
+
2
+ // External components
3
+ @use "../node_modules/air-datepicker/air-datepicker.css";
4
+
5
+ .air-datepicker {
6
+ --adp-color: rgba(var(--zn-text), var(--zn-text-opacity));
7
+ --adp-nav-color-secondary: rgb(var(--zn-purple-text));
8
+
9
+ --adp-day-name-color: var(--zn-color-primary-600);
10
+ --adp-day-name-color-hover: var(--zn-color-primary-700);
11
+ --adp-cell-background-color-selected: var(--zn-color-primary-600);
12
+ --adp-cell-background-color-selected-hover: var(--zn-color-primary-700);
13
+ --adp-color-current-date: var(--zn-color-primary-600);
14
+
15
+ --adp-background-color: var(--zn-input-background-color);
16
+ --adp-background-color-hover: var(--zn-input-background-color-hover);
17
+ --adp-background-color-active: var(--zn-input-background-color-hover);
18
+ --adp-background-color-selected-other-month: var(--zn-color-primary-300);
19
+ --adp-background-color-in-range: var(--zn-color-primary-100);
20
+
21
+ --adp-border-color: rgb(var(--zn-border-color));
22
+ --adp-border-color-inline: rgb(var(--zn-border-color));
23
+ --adp-border-color-inner: rgb(var(--zn-border-color));
24
+
25
+ --adp-cell-background-color-in-range: var(--zn-color-primary-100);
26
+ --adp-cell-background-color-in-range-hover: var(--zn-color-primary-200);
27
+ }
28
+
29
+ .air-datepicker--pointer:after {
30
+ background-color: var(--zn-input-background-color);
31
+ }
32
+
33
+ .air-datepicker-global-container {
34
+ z-index: 1000000;
35
+ }
36
+
37
+ .air-datepicker-button {
38
+ border: none;
39
+ padding: var(--zn-spacing-2x-small) var(--zn-spacing-x-small);
40
+ border-radius: var(--adp-border-radius);
41
+ font-family: var(--adp-font-family), sans-serif;
42
+ font-size: var(--zn-font-size-medium);
43
+ font-weight: var(--zn-font-weight-normal);
44
+ cursor: pointer;
45
+ color: rgba(var(--zn-text), var(--zn-text-opacity));
46
+
47
+ &:hover {
48
+ background-color: var(--zn-input-background-color-hover) !important;
49
+ }
50
+ }
package/scss/boot.scss CHANGED
@@ -9,55 +9,8 @@
9
9
  // Needed components for positioning
10
10
  @use "./shared";
11
11
 
12
- // External components
13
- @use "../node_modules/air-datepicker/air-datepicker.css";
12
+ @use "./air-datapicker";
14
13
 
15
- .air-datepicker {
16
- --adp-color: rgba(var(--zn-text), var(--zn-text-opacity));
17
- --adp-nav-color-secondary: rgb(var(--zn-purple-text));
18
-
19
- --adp-day-name-color: var(--zn-color-primary-600);
20
- --adp-day-name-color-hover: var(--zn-color-primary-700);
21
- --adp-cell-background-color-selected: var(--zn-color-primary-600);
22
- --adp-cell-background-color-selected-hover: var(--zn-color-primary-700);
23
- --adp-color-current-date: var(--zn-color-primary-600);
24
-
25
- --adp-background-color: var(--zn-input-background-color);
26
- --adp-background-color-hover: var(--zn-input-background-color-hover);
27
- --adp-background-color-active: var(--zn-input-background-color-hover);
28
- --adp-background-color-selected-other-month: var(--zn-color-primary-300);
29
- --adp-background-color-in-range: var(--zn-color-primary-100);
30
-
31
- --adp-border-color: rgb(var(--zn-border-color));
32
- --adp-border-color-inline: rgb(var(--zn-border-color));
33
- --adp-border-color-inner: rgb(var(--zn-border-color));
34
-
35
- --adp-cell-background-color-in-range: var(--zn-color-primary-100);
36
- --adp-cell-background-color-in-range-hover: var(--zn-color-primary-200);
37
- }
38
-
39
- .air-datepicker--pointer:after {
40
- background-color: var(--zn-input-background-color);
41
- }
42
-
43
- .air-datepicker-global-container {
44
- z-index: 1000000;
45
- }
46
-
47
- .air-datepicker-button {
48
- border: none;
49
- padding: var(--zn-spacing-2x-small) var(--zn-spacing-x-small);
50
- border-radius: var(--adp-border-radius);
51
- font-family: var(--adp-font-family), sans-serif;
52
- font-size: var(--zn-font-size-medium);
53
- font-weight: var(--zn-font-weight-normal);
54
- cursor: pointer;
55
- color: rgba(var(--zn-text), var(--zn-text-opacity));
56
-
57
- &:hover {
58
- background-color: var(--zn-input-background-color-hover) !important;
59
- }
60
- }
61
14
 
62
15
  // Utility to minimize flashing of undefined web components
63
16
  .zn-cloak:has(:not(:defined)) {
@@ -3,8 +3,8 @@ import {type CSSResultGroup, html, nothing, type TemplateResult, unsafeCSS} from
3
3
  import {HasSlotController} from "../../internal/slot";
4
4
  import {ifDefined} from "lit/directives/if-defined.js";
5
5
  import {property, query} from 'lit/decorators.js';
6
- import {ResizeController} from '@lit-labs/observers/resize-controller.js';
7
6
  import {ref} from "lit/directives/ref.js";
7
+ import {ResizeController} from '@lit-labs/observers/resize-controller.js';
8
8
  import {Task} from "@lit/task";
9
9
  import {unsafeHTML} from "lit/directives/unsafe-html.js";
10
10
  import {type ZnFilterChangeEvent} from "../../events/zn-filter-change";
@@ -40,6 +40,8 @@ interface Cell {
40
40
  style?: string;
41
41
  iconSrc?: string;
42
42
  iconColor?: string;
43
+ iconSize?: number;
44
+ iconStyle?: string;
43
45
  hoverContent?: string;
44
46
  hoverPlacement?: string;
45
47
  chipColor?: string;
@@ -100,8 +102,27 @@ interface HeaderConfig {
100
102
  hideHeader?: boolean;
101
103
  hideColumn?: boolean;
102
104
  secondary?: boolean;
105
+ type?: string;
106
+ cellTemplate ?: Cell;
107
+ ifEmpty ?:Cell;
103
108
  }
104
109
 
110
+ type DisplayTemplate = (cell: Cell, row: Row, header: HeaderConfig) =>
111
+ TemplateResult | string;
112
+
113
+
114
+ const defaultTemplates = {
115
+ dateTime: ((cell) => {
116
+ const t = cell.text || '';
117
+ return html`
118
+ <div class="table__collum--datetime">
119
+ <span><strong>${t.slice(0, 10)}</strong></span>
120
+ <zn-style size="s" muted>${t.slice(11)}</zn-style>
121
+ </div>`
122
+ }) satisfies DisplayTemplate
123
+ };
124
+
125
+
105
126
  interface DataRequest {
106
127
  page: number;
107
128
  perPage: number;
@@ -171,7 +192,7 @@ export default class ZnDataTable extends ZincElement {
171
192
  };
172
193
 
173
194
  @property({attribute: 'data-uri'}) dataUri: string;
174
- @property({attribute: 'data', type: Object, reflect: true}) data: any;
195
+ @property({attribute: 'data', type: Object}) data: Row[] | Row = [];
175
196
  @property({attribute: 'sort-column'}) sortColumn: string;
176
197
  @property({attribute: 'sort-direction'}) sortDirection: string = "asc";
177
198
  @property({attribute: 'local-sort', type: Boolean}) localSort: boolean = false;
@@ -181,6 +202,8 @@ export default class ZnDataTable extends ZincElement {
181
202
  @property({attribute: 'key'}) key: string = 'id';
182
203
  @property({attribute: 'headers', type: Object}) headers: Record<string, HeaderConfig> = {};
183
204
 
205
+ @property({attribute: false}) displayTemplates: Record<string, DisplayTemplate> = {};
206
+
184
207
  // Hide header text keeping the content - e.g. Action buttons without a header
185
208
  @property({attribute: 'hide-headers', type: Object}) hiddenHeaders = '{}';
186
209
 
@@ -217,6 +240,7 @@ export default class ZnDataTable extends ZincElement {
217
240
 
218
241
  @property() groups = '';
219
242
 
243
+ @property({attribute: 'per-page-size', type:Number}) itemsPerPage:number = DEFAULT_PER_PAGE;
220
244
  @query('#select-all-rows') selectAllButton: ZnButton;
221
245
 
222
246
  // Data Table Properties
@@ -232,7 +256,7 @@ export default class ZnDataTable extends ZincElement {
232
256
  },
233
257
  });
234
258
 
235
- private itemsPerPage: number = DEFAULT_PER_PAGE;
259
+ // private itemsPerPage: number = DEFAULT_PER_PAGE;
236
260
  private page: number = DEFAULT_PAGE;
237
261
  private totalPages: number;
238
262
 
@@ -292,7 +316,6 @@ export default class ZnDataTable extends ZincElement {
292
316
  Object.assign(requestData, params);
293
317
  }
294
318
 
295
-
296
319
  // Add any extra request params
297
320
  if (requestParams && typeof requestParams === 'object') {
298
321
  Object.assign(requestData, requestParams);
@@ -320,6 +343,7 @@ export default class ZnDataTable extends ZincElement {
320
343
  private _expandedRows: Set<string> = new Set();
321
344
  private _hiddenCells: Map<string, Cell[]> = new Map();
322
345
  private _secondaryHeaders: HeaderConfig[];
346
+ private _formatTemplates: Record<string, DisplayTemplate> = defaultTemplates;
323
347
 
324
348
  requestParams: Record<string, any> = {};
325
349
 
@@ -330,12 +354,13 @@ export default class ZnDataTable extends ZincElement {
330
354
  }
331
355
 
332
356
  render() {
357
+
333
358
  // If no-initial-load is set, do not invoke the Task on the first render
334
- let tableBody: TemplateResult;
359
+ let tableBody: TemplateResult = html``;
335
360
  if (this.noInitialLoad && this._initialLoad) {
336
361
  tableBody = html`
337
362
  <slot name="empty-state"></slot>`;
338
- } else {
363
+ } else if (this.dataUri) {
339
364
  tableBody = this._dataTask.render({
340
365
  pending: () => {
341
366
  if (this._initialLoad) {
@@ -367,6 +392,18 @@ export default class ZnDataTable extends ZincElement {
367
392
  <div>${error}</div>`
368
393
  }
369
394
  }) as TemplateResult;
395
+ } else if (Array.isArray(this.data) ? this.data.length > 0 : !!this.data) {
396
+ const rows: Row[] = Array.isArray(this.data) ? this.data : [this.data];
397
+ const response: Response = {
398
+ rows,
399
+ perPage: rows.length,
400
+ total: rows.length,
401
+ page: 1,
402
+ };
403
+ this._initialLoad = false;
404
+ this._lastTableContent = html`
405
+ <div>${this.renderTable(response)}</div>`;
406
+ tableBody = this._lastTableContent;
370
407
  }
371
408
 
372
409
  const hasActions = this.hasSlotController.test(ActionSlots.delete.valueOf())
@@ -396,6 +433,10 @@ export default class ZnDataTable extends ZincElement {
396
433
  this.addEventListener('zn-search-change', this.searchChangeListener);
397
434
  }
398
435
 
436
+ private getTemplate(name: string): DisplayTemplate | undefined {
437
+ return this.displayTemplates[name] ?? this._formatTemplates[name];
438
+ }
439
+
399
440
  disconnectedCallback() {
400
441
  super.disconnectedCallback();
401
442
  this.removeEventListener('zn-filter-change', this.filterChangeListener);
@@ -616,7 +657,7 @@ export default class ZnDataTable extends ZincElement {
616
657
  'table__row--data': true,
617
658
  })}" data-row-id="${row.id}">
618
659
  ${anyHidden ? this.renderExpanderCell(row) : nothing}
619
- ${(visibleRowCells.get(row.id) || row.cells).map((value: Cell, index: number) => this.renderCellBody(index, value))}
660
+ ${(visibleRowCells.get(row.id) || row.cells).map((value: Cell, index: number) => this.renderCellBody(index, value, row))}
620
661
  ${this.rowHasActions ? this.renderActions(row) : nothing}
621
662
  </tr>
622
663
  ${this._expandedRows.has(row.id) ? this.renderDetailsRow(row, colCount) : nothing}
@@ -922,91 +963,128 @@ export default class ZnDataTable extends ZincElement {
922
963
  };
923
964
  }
924
965
 
925
- renderCell(data: Cell) {
926
- if (data && typeof data === 'object') {
927
- let content: TemplateResult | ZincElement = html`${data.text}`;
966
+ renderCell(data: Cell, row?: Row, header?: HeaderConfig): TemplateResult | ZincElement {
967
+ const fn = (header?.type ? this.getTemplate(header.type) : undefined);
928
968
 
929
- if (data.style || data.color) {
930
- const styleStr = typeof data.style === 'string' ? data.style : '';
931
- const tokens = new Set(styleStr.split(',').filter(Boolean));
969
+ if((!data.text && !data.iconSrc) && header?.ifEmpty !== undefined) {
970
+ const header2 = {...header, type: undefined, render: undefined, renderTemplate: undefined};
971
+ return this.renderCell({...data, ...header.ifEmpty}, row, header2)
972
+ }
932
973
 
933
- const isMono = tokens.has('mono') || tokens.has('code');
934
- const isBorder = tokens.has('border');
935
- const isCenter = tokens.has('center');
974
+ if (fn && row && header) {
975
+ const out = fn(data, row, header);
976
+ return typeof out === 'string' ? html`${unsafeHTML(out)}` : out;
977
+ }
936
978
 
937
- if (tokens.has('bold') || tokens.has('strong')) {
938
- content = html`<strong>${content}</strong>`;
939
- }
979
+ if(!data || typeof data !== 'object') {
980
+ return data;
981
+ }
940
982
 
941
- if (tokens.has('italic')) {
942
- content = html`<em>${content}</em>`;
943
- }
983
+ if(header?.cellTemplate !== undefined) {
984
+ data = {...header.cellTemplate, ...data}
985
+ }
944
986
 
945
- content = html`
946
- <zn-style
947
- font="${isMono ? 'mono' : nothing}"
948
- border="${isBorder || nothing}"
949
- center="${isCenter || nothing}"
950
- color="${ifDefined(data.color)}">${content}
951
- </zn-style>`;
952
- }
987
+ let content: TemplateResult | ZincElement = html`${data.text}`;
988
+
989
+ if (data.style || data.color) {
990
+ const styleStr = typeof data.style === 'string' ? data.style : '';
991
+ const tokens = new Set(styleStr.split(',').filter(Boolean));
992
+
993
+ const isMono = tokens.has('mono') || tokens.has('code');
994
+ const isBorder = tokens.has('border');
995
+ const isCenter = tokens.has('center');
996
+ const isMuted = tokens.has('muted');
953
997
 
954
- if (data.uri) {
955
- content = html`
956
- <a href="${data.uri}"
957
- data-target="${ifDefined(data.target || nothing)}"
958
- gaid="${ifDefined(data.gaid || nothing)}">${content}</a>`;
998
+ if (tokens.has('bold') || tokens.has('strong')) {
999
+ content = html`<strong>${content}</strong>`;
959
1000
  }
960
1001
 
961
- if (data.chipColor) {
962
- return html`
963
- <zn-chip type="${data.chipColor}">${content}</zn-chip>`;
1002
+ if (tokens.has('italic')) {
1003
+ content = html`<em>${content}</em>`;
964
1004
  }
965
1005
 
966
- if (data.hoverContent) {
967
- const placement = data.hoverPlacement ?? 'top';
1006
+ content = html`
1007
+ <zn-style
1008
+ font="${isMono ? 'mono' : nothing}"
1009
+ border="${isBorder || nothing}"
1010
+ center="${isCenter || nothing}"
1011
+ muted="${isMuted || nothing}"
1012
+ color="${ifDefined(data.color)}">${content}
1013
+ </zn-style>`;
1014
+ }
968
1015
 
969
- if (data.iconSrc) {
970
- const src = data.iconSrc;
971
- const color = data.iconColor ?? '';
1016
+ if (data.uri) {
1017
+ content = html`
1018
+ <a href="${data.uri}"
1019
+ data-target="${ifDefined(data.target || nothing)}"
1020
+ gaid="${ifDefined(data.gaid || nothing)}">${content}</a>`;
1021
+ }
972
1022
 
973
- return html`
974
- ${content}
975
- <zn-hover-container placement="${placement}"
976
- flip>
977
- <zn-icon src="${src}" color="${color}"></zn-icon>
978
- <div slot="content">
979
- ${unsafeHTML(data.hoverContent)}
980
- </div>
981
- </zn-hover-container>`;
982
- }
1023
+ if (data.chipColor) {
1024
+ return html`
1025
+ <zn-chip type="${data.chipColor}">${content}</zn-chip>`;
1026
+ }
983
1027
 
984
- return html`
985
- <zn-hover-container placement="${placement}"
986
- flip>
987
- <div slot="anchor">${content}</div>
988
- ${unsafeHTML(data.hoverContent)}
989
- </zn-hover-container>`;
990
- }
1028
+ if (data.hoverContent) {
1029
+ const placement = data.hoverPlacement ?? 'top';
991
1030
 
992
1031
  if (data.iconSrc) {
993
1032
  const src = data.iconSrc;
994
- const color = data.iconColor ?? '';
1033
+ const color = data.iconColor;
1034
+ const size = data.iconSize;
1035
+ const tokens = (data.iconStyle ?? '').split(',').map(s => s.trim()).filter(Boolean);
995
1036
 
996
- return html`
997
- <zn-icon src="${src}" color="${color}"></zn-icon> ${content}`;
998
- }
999
-
1000
- if (data.copyable) {
1001
1037
  return html`
1002
1038
  ${content}
1003
- <zn-copy-button value="${data.text}"></zn-copy-button>`;
1039
+ <zn-hover-container placement="${placement}" flip>
1040
+ <zn-icon
1041
+ src="${src}"
1042
+ size="${ifDefined(size)}"
1043
+ color="${ifDefined(color)}"
1044
+ ${ref(el => {
1045
+ if (!el) return;
1046
+ tokens.forEach(t => el.setAttribute(t, ''));
1047
+ })}
1048
+ ></zn-icon>
1049
+ <div slot="content">
1050
+ ${unsafeHTML(data.hoverContent)}
1051
+ </div>
1052
+ </zn-hover-container>`;
1004
1053
  }
1005
1054
 
1006
- return content;
1055
+ return html`
1056
+ <zn-hover-container placement="${placement}" flip>
1057
+ <div slot="anchor">${content}</div>
1058
+ ${unsafeHTML(data.hoverContent)}
1059
+ </zn-hover-container>`;
1007
1060
  }
1008
1061
 
1009
- return data;
1062
+ if (data.iconSrc) {
1063
+ const src = data.iconSrc;
1064
+ const color = data.iconColor;
1065
+ const size = data.iconSize;
1066
+ const tokens = (data.iconStyle ?? '').split(',').map(s => s.trim()).filter(Boolean);
1067
+
1068
+ return html`
1069
+ <zn-icon
1070
+ src="${src}"
1071
+ size="${ifDefined(size)}"
1072
+ color="${ifDefined(color)}"
1073
+ ${ref(el => {
1074
+ if (!el) return;
1075
+ tokens.forEach(t => el.setAttribute(t, ''));
1076
+ })}
1077
+ ></zn-icon> ${content}`;
1078
+ }
1079
+
1080
+ if (data.copyable) {
1081
+ return html`
1082
+ ${content}
1083
+ <zn-copy-button value="${data.text}"></zn-copy-button>`;
1084
+ }
1085
+
1086
+ return content;
1087
+
1010
1088
  }
1011
1089
 
1012
1090
  private updateActionKeys(slotName: string) {
@@ -1076,7 +1154,7 @@ export default class ZnDataTable extends ZincElement {
1076
1154
  `;
1077
1155
  }
1078
1156
 
1079
- private renderCellBody(index: number, value: Cell) {
1157
+ private renderCellBody(index: number, value: Cell, row: Row) {
1080
1158
  const filteredHeaders = Object.values(this.headers).filter((header: HeaderConfig) => {
1081
1159
  if (header.hideHeader || header.hideColumn) return false;
1082
1160
 
@@ -1084,7 +1162,8 @@ export default class ZnDataTable extends ZincElement {
1084
1162
 
1085
1163
  return !header.secondary;
1086
1164
  });
1087
- const headerKey: string = filteredHeaders[index]?.key;
1165
+ const header: HeaderConfig | undefined = filteredHeaders[index];
1166
+ const headerKey: string = header?.key;
1088
1167
 
1089
1168
  return html`
1090
1169
  <td
@@ -1094,7 +1173,7 @@ export default class ZnDataTable extends ZincElement {
1094
1173
  'table__cell--wide': headerKey === this.wideColumn,
1095
1174
  'table__cell--last': index === filteredHeaders.length - 1,
1096
1175
  })}">
1097
- <div>${this.renderCell(value)}</div>
1176
+ <div>${this.renderCell(value, row, header)}</div>
1098
1177
  </td>`;
1099
1178
  }
1100
1179
 
@@ -1136,7 +1215,7 @@ export default class ZnDataTable extends ZincElement {
1136
1215
  return html`
1137
1216
  <div class="table__details__item">
1138
1217
  <div class="table__details__label">${label}</div>
1139
- <div class="table__details__value">${this.renderCell(cell)}</div>
1218
+ <div class="table__details__value">${this.renderCell(cell, row, header)}</div>
1140
1219
  </div>`;
1141
1220
  })}
1142
1221
  </div>
@@ -422,3 +422,9 @@ table:has(tbody tr:first-of-type.table__row--selected) thead tr th {
422
422
  text-overflow: ellipsis;
423
423
  }
424
424
 
425
+ .table__collum--datetime{
426
+ display:flex;
427
+ flex-direction:column;
428
+ align-items: flex-end;
429
+ gap:2px;
430
+ }