@kubex/zinc 1.0.5 → 1.0.7
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/dist/custom-elements.json +302 -16
- package/dist/vscode.html-custom-data.json +13 -2
- package/dist/web-types.json +43 -5
- package/dist/zn.d.ts +81 -40
- package/dist/zn.min.js +333 -270
- package/package.json +1 -1
- package/src/components/bulk-actions/bulk-actions.component.ts +152 -84
- package/src/components/bulk-actions/bulk-actions.scss +2 -0
- package/src/components/chart/chart.component.ts +14 -12
- package/src/components/cols/cols.component.ts +7 -3
- package/src/components/cols/cols.scss +22 -14
- package/src/components/content-block/content-block.component.ts +164 -30
- package/src/components/data-select/data-select.component.ts +51 -10
- package/src/components/data-table/data-table.component.ts +58 -38
- package/src/components/data-table/data-table.scss +74 -7
- package/src/components/data-table-filter/data-table-filter.component.ts +1 -1
- package/src/components/editor/modules/attachment/attachment.ts +53 -41
- package/src/components/editor/modules/toolbar/toolbar.ts +42 -0
- package/src/components/empty-state/empty-state.component.ts +16 -8
- package/src/components/icon/icon.component.ts +2 -0
- package/src/components/icon/icon.scss +13 -0
- package/src/components/inline-edit/inline-edit.component.ts +24 -3
- package/src/components/inline-edit/inline-edit.scss +11 -1
- package/src/components/item/item.component.ts +11 -3
- package/src/components/item/item.scss +14 -19
- package/src/components/panel/panel.component.ts +2 -0
- package/src/components/panel/panel.scss +6 -0
- package/src/components/settings-container/settings-container.scss +11 -4
- package/src/components/style/style.component.ts +7 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {classMap} from "lit/directives/class-map.js";
|
|
2
|
+
import {deepQuerySelectorAll} from "../../utilities/query";
|
|
2
3
|
import {HasSlotController} from "../../internal/slot";
|
|
3
|
-
import {html, unsafeCSS} from 'lit';
|
|
4
|
+
import {html, nothing, unsafeCSS} from 'lit';
|
|
4
5
|
import {property, queryAssignedNodes, queryAsync} from 'lit/decorators.js';
|
|
5
6
|
import {unsafeHTML} from 'lit-html/directives/unsafe-html.js';
|
|
6
7
|
import ZincElement from "../../internal/zinc-element";
|
|
7
8
|
import type {PropertyValues} from 'lit';
|
|
9
|
+
import type ZnTile from "../tile";
|
|
8
10
|
|
|
9
11
|
import styles from './content-block.scss';
|
|
10
12
|
|
|
@@ -28,6 +30,7 @@ export default class ContentBlock extends ZincElement {
|
|
|
28
30
|
|
|
29
31
|
@property({type: Boolean, reflect: true}) outbound = false;
|
|
30
32
|
@property({type: Boolean, reflect: true}) short = false;
|
|
33
|
+
@property({attribute: 'default-display', reflect: true}) defaultDisplay: 'text' | 'html' = 'text';
|
|
31
34
|
|
|
32
35
|
@queryAssignedNodes({slot: 'html', flatten: true}) htmlNodes!: Node[];
|
|
33
36
|
|
|
@@ -37,6 +40,9 @@ export default class ContentBlock extends ZincElement {
|
|
|
37
40
|
|
|
38
41
|
private _textRows: TextRow[] = [];
|
|
39
42
|
|
|
43
|
+
private _footerObserver?: MutationObserver;
|
|
44
|
+
private _replaceDebounce: number = 0;
|
|
45
|
+
|
|
40
46
|
connectedCallback() {
|
|
41
47
|
super.connectedCallback();
|
|
42
48
|
this.iframe.then((iframe) => {
|
|
@@ -49,37 +55,34 @@ export default class ContentBlock extends ZincElement {
|
|
|
49
55
|
convertedHtml = convertedHtml.replace(/>/g, '>');
|
|
50
56
|
convertedHtml = convertedHtml.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
51
57
|
|
|
52
|
-
const baseStyles = "<style>body,html{
|
|
58
|
+
const baseStyles = "<style>body,html{background-color: #ffffff}img{max-width:100%; max-height: 500px;}</style>";
|
|
53
59
|
iframe.srcdoc = baseStyles + convertedHtml;
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
iframe.addEventListener("load", () => {
|
|
58
|
-
|
|
59
|
-
const originalDisplay = iframe.style.display;
|
|
60
|
-
const originalVisibility = iframe.style.visibility;
|
|
61
|
-
const originalPosition = iframe.style.position;
|
|
62
|
-
|
|
63
|
-
// Make iframe render but stay hidden
|
|
64
|
-
iframe.style.display = 'block';
|
|
65
|
-
iframe.style.visibility = 'hidden';
|
|
66
|
-
iframe.style.position = 'absolute';
|
|
67
|
-
|
|
68
|
-
setTimeout(() => {
|
|
69
|
-
const iframeBody = iframe.contentDocument?.body;
|
|
70
|
-
if (iframeBody) {
|
|
71
|
-
const height = iframeBody.scrollHeight;
|
|
72
|
-
iframe.style.height = `${height + 30}px`;
|
|
73
|
-
}
|
|
74
|
-
// Restore original styles
|
|
75
|
-
iframe.style.display = originalDisplay;
|
|
76
|
-
iframe.style.visibility = originalVisibility;
|
|
77
|
-
iframe.style.position = originalPosition;
|
|
78
|
-
}, 50);
|
|
64
|
+
setTimeout(() => this._resizeIframe(iframe), 50);
|
|
79
65
|
});
|
|
80
66
|
});
|
|
81
67
|
}
|
|
82
68
|
|
|
69
|
+
disconnectedCallback() {
|
|
70
|
+
try {
|
|
71
|
+
if (this._footerObserver) {
|
|
72
|
+
this._footerObserver.disconnect();
|
|
73
|
+
this._footerObserver = undefined;
|
|
74
|
+
}
|
|
75
|
+
if (this._replaceDebounce) {
|
|
76
|
+
clearTimeout(this._replaceDebounce);
|
|
77
|
+
this._replaceDebounce = 0;
|
|
78
|
+
}
|
|
79
|
+
} finally {
|
|
80
|
+
// Ensure base class cleanup
|
|
81
|
+
// @ts-ignore - base may or may not implement disconnectedCallback
|
|
82
|
+
super.disconnectedCallback?.();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
83
86
|
private _collapseContent(e: Event) {
|
|
84
87
|
const target = e.target as HTMLElement;
|
|
85
88
|
const headerClick = target.classList.contains('content-block-header') ||
|
|
@@ -88,6 +91,15 @@ export default class ContentBlock extends ZincElement {
|
|
|
88
91
|
if (headerClick && target.slot !== 'nav') {
|
|
89
92
|
this.short = !this.short;
|
|
90
93
|
this.requestUpdate();
|
|
94
|
+
this.updateComplete.then(() => {
|
|
95
|
+
if (!this.short) {
|
|
96
|
+
this.iframe.then((iframe) => {
|
|
97
|
+
if (iframe && !iframe.classList.contains('hidden')) {
|
|
98
|
+
this._resizeIframe(iframe);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
105
|
|
|
@@ -107,10 +119,46 @@ export default class ContentBlock extends ZincElement {
|
|
|
107
119
|
if (textContent) {
|
|
108
120
|
iframe.classList.remove('hidden');
|
|
109
121
|
textContent.classList.add('hidden');
|
|
122
|
+
setTimeout(() => this._resizeIframe(iframe), 0);
|
|
110
123
|
}
|
|
111
124
|
});
|
|
112
125
|
}
|
|
113
126
|
|
|
127
|
+
private _resizeIframe(iframe?: HTMLIFrameElement) {
|
|
128
|
+
const resize = (frame: HTMLIFrameElement) => {
|
|
129
|
+
const doc = frame.contentDocument;
|
|
130
|
+
if (!doc) return;
|
|
131
|
+
|
|
132
|
+
// Allow the iframe to shrink as well as grow
|
|
133
|
+
frame.style.height = 'auto';
|
|
134
|
+
|
|
135
|
+
const body = doc.body;
|
|
136
|
+
const element = doc.documentElement;
|
|
137
|
+
|
|
138
|
+
const heights = [
|
|
139
|
+
body?.scrollHeight ?? 0,
|
|
140
|
+
element?.scrollHeight ?? 0,
|
|
141
|
+
body?.offsetHeight ?? 0,
|
|
142
|
+
element?.offsetHeight ?? 0
|
|
143
|
+
];
|
|
144
|
+
const height = Math.max(...heights);
|
|
145
|
+
|
|
146
|
+
if (height > 0) {
|
|
147
|
+
frame.style.height = `${height}px`;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
if (iframe) {
|
|
152
|
+
resize(iframe);
|
|
153
|
+
} else {
|
|
154
|
+
this.iframe.then((frame) => {
|
|
155
|
+
if (frame?.contentDocument) {
|
|
156
|
+
requestAnimationFrame(() => resize(frame));
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
114
162
|
protected firstUpdated(_changedProperties: PropertyValues) {
|
|
115
163
|
super.firstUpdated(_changedProperties);
|
|
116
164
|
|
|
@@ -130,12 +178,16 @@ export default class ContentBlock extends ZincElement {
|
|
|
130
178
|
const hasTextSlot = this.hasSlotController.test('text');
|
|
131
179
|
const hasHtmlSlot = this.hasSlotController.test('html');
|
|
132
180
|
const showActions = hasTextSlot && hasHtmlSlot;
|
|
181
|
+
const initialShowHtml = hasHtmlSlot && (!hasTextSlot || this.defaultDisplay === 'html');
|
|
133
182
|
|
|
134
183
|
return html`
|
|
135
|
-
<zn-panel flush
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
184
|
+
<zn-panel flush
|
|
185
|
+
tabbed
|
|
186
|
+
flush-footer="${hasFooter || nothing}"
|
|
187
|
+
class="${classMap({
|
|
188
|
+
'content-block--outbound': this.outbound,
|
|
189
|
+
'content-block--short': this.short
|
|
190
|
+
})}">
|
|
139
191
|
|
|
140
192
|
<zn-header class="content-block-header"
|
|
141
193
|
caption="${this.sender}"
|
|
@@ -167,10 +219,10 @@ export default class ContentBlock extends ZincElement {
|
|
|
167
219
|
</zn-header>
|
|
168
220
|
|
|
169
221
|
<zn-sp no-gap>
|
|
170
|
-
<iframe class="hidden" title="Content block HTML preview"></iframe>
|
|
222
|
+
<iframe class="${classMap({'hidden': !initialShowHtml})}" title="Content block HTML preview"></iframe>
|
|
171
223
|
<slot class="html-content" name="html" style="display: none;"></slot>
|
|
172
224
|
<slot name="text" style="display: none;"></slot>
|
|
173
|
-
<div class="text-content">
|
|
225
|
+
<div class="${classMap({'text-content': true, 'hidden': initialShowHtml})}">
|
|
174
226
|
${text.map((section) => html`
|
|
175
227
|
${section.type === 'reply' ? html`
|
|
176
228
|
<div class="toggle-reply" @click="${this.showReply}">...</div>` : ''}
|
|
@@ -187,7 +239,7 @@ export default class ContentBlock extends ZincElement {
|
|
|
187
239
|
</zn-sp>
|
|
188
240
|
|
|
189
241
|
${hasFooter ? html`
|
|
190
|
-
<slot slot="footer" name="footer"></slot>` : ''}
|
|
242
|
+
<slot slot="footer" name="footer" @slotchange="${this._handleSlotChange}"></slot>` : ''}
|
|
191
243
|
</zn-panel>
|
|
192
244
|
`;
|
|
193
245
|
}
|
|
@@ -199,6 +251,88 @@ export default class ContentBlock extends ZincElement {
|
|
|
199
251
|
return trimmed.length > 32 ? trimmed.substring(0, 32) + '...' : trimmed;
|
|
200
252
|
}
|
|
201
253
|
|
|
254
|
+
private _handleSlotChange(e: Event) {
|
|
255
|
+
const slot = e.target as HTMLSlotElement;
|
|
256
|
+
const assignedEls: Element[] = slot.assignedElements({flatten: true});
|
|
257
|
+
if (!assignedEls || assignedEls.length === 0) return;
|
|
258
|
+
|
|
259
|
+
// Reset and attach a new observer to watch for dynamic content inside the footer subtree
|
|
260
|
+
if (this._footerObserver) {
|
|
261
|
+
this._footerObserver.disconnect();
|
|
262
|
+
this._footerObserver = undefined;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const obs = new MutationObserver(() => {
|
|
266
|
+
this._debouncedReplace();
|
|
267
|
+
});
|
|
268
|
+
this._footerObserver = obs;
|
|
269
|
+
|
|
270
|
+
assignedEls.forEach(el => {
|
|
271
|
+
obs.observe(el, {
|
|
272
|
+
subtree: true,
|
|
273
|
+
childList: true,
|
|
274
|
+
attributes: true,
|
|
275
|
+
attributeFilter: ['href', 'download', 'caption']
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Run now and again shortly after to catch async rendering
|
|
280
|
+
this._replaceImagePlaceholders();
|
|
281
|
+
setTimeout(() => this._replaceImagePlaceholders(), 50);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private _debouncedReplace() {
|
|
285
|
+
if (this._replaceDebounce) {
|
|
286
|
+
clearTimeout(this._replaceDebounce);
|
|
287
|
+
}
|
|
288
|
+
this._replaceDebounce = window.setTimeout(() => this._replaceImagePlaceholders(), 50);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private _replaceImagePlaceholders() {
|
|
292
|
+
const textContainer = this.shadowRoot?.querySelector('.text-content') as HTMLDivElement | null;
|
|
293
|
+
if (!textContainer) {
|
|
294
|
+
// If the shadow content hasn't been rendered yet, try after the next update
|
|
295
|
+
this.updateComplete.then(() => this._replaceImagePlaceholders());
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const footer = this.querySelector(':scope > [slot="footer"]') as HTMLElement | null;
|
|
300
|
+
if (!footer) return;
|
|
301
|
+
|
|
302
|
+
const anchors = deepQuerySelectorAll('a[href]', footer, '') as HTMLAnchorElement[];
|
|
303
|
+
if (anchors.length === 0) return;
|
|
304
|
+
|
|
305
|
+
let textContent = textContainer.innerHTML;
|
|
306
|
+
const extensions = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'svg', 'heic', 'tiff', 'tif', 'avif']);
|
|
307
|
+
anchors.forEach((a) => {
|
|
308
|
+
const candidates = new Set<string>();
|
|
309
|
+
const tile = a.querySelector('zn-tile') as ZnTile | null;
|
|
310
|
+
|
|
311
|
+
const caption = (tile?.caption ?? tile?.getAttribute?.('caption') ?? '').trim();
|
|
312
|
+
if (caption) candidates.add(caption);
|
|
313
|
+
|
|
314
|
+
const downloadAttr = (a.getAttribute('download') || '').trim();
|
|
315
|
+
if (downloadAttr) candidates.add(downloadAttr);
|
|
316
|
+
|
|
317
|
+
const url = a.getAttribute('href') || '';
|
|
318
|
+
candidates.forEach((rawName) => {
|
|
319
|
+
const name = (rawName || '').trim();
|
|
320
|
+
if (!name) return;
|
|
321
|
+
|
|
322
|
+
const base = name.includes('/') ? name.split('/').pop()! : name;
|
|
323
|
+
const lowerBase = base.toLowerCase();
|
|
324
|
+
const ext = lowerBase.includes('.') ? lowerBase.split('.').pop()! : '';
|
|
325
|
+
if (ext && !extensions.has(ext)) return;
|
|
326
|
+
|
|
327
|
+
const alt = base.replace(/"/g, '"');
|
|
328
|
+
textContent = textContent.replace(`[image: ${base}]`, () => `<img src="${url}" alt="${alt}" style="max-height: 500px; max-width: 100%">`);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Update the generated content directly without triggering a re-render
|
|
333
|
+
textContainer.innerHTML = textContent;
|
|
334
|
+
}
|
|
335
|
+
|
|
202
336
|
protected getTextSections(): TextRow[] {
|
|
203
337
|
const textContent = this.querySelector('[slot="text"]') as HTMLDivElement | null;
|
|
204
338
|
const textRows: TextRow[] = [];
|
|
@@ -7,13 +7,12 @@ import {
|
|
|
7
7
|
emptyDataProvider,
|
|
8
8
|
type LocalDataProvider,
|
|
9
9
|
} from "./providers/provider";
|
|
10
|
+
import {type CSSResultGroup, html, nothing, unsafeCSS, PropertyValues} from 'lit';
|
|
10
11
|
import {FormControlController} from "../../internal/form";
|
|
11
|
-
import {html, nothing, unsafeCSS} from 'lit';
|
|
12
12
|
import {property, query} from 'lit/decorators.js';
|
|
13
13
|
import {watch} from "../../internal/watch";
|
|
14
|
-
import ZincElement from '../../internal/zinc-element';
|
|
15
|
-
import type {CSSResultGroup} from 'lit';
|
|
16
14
|
import type {ZincFormControl} from '../../internal/zinc-element';
|
|
15
|
+
import ZincElement from '../../internal/zinc-element';
|
|
17
16
|
import type ZnSelect from "../select";
|
|
18
17
|
|
|
19
18
|
import styles from './data-select.scss';
|
|
@@ -37,15 +36,22 @@ import styles from './data-select.scss';
|
|
|
37
36
|
*/
|
|
38
37
|
export default class ZnDataSelect extends ZincElement implements ZincFormControl {
|
|
39
38
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
39
|
+
|
|
40
40
|
@query('#select') select: ZnSelect;
|
|
41
41
|
@query('#select__prefix') selectPrefix: HTMLElement;
|
|
42
|
+
|
|
42
43
|
/** The name of the select. Used for form submission. */
|
|
43
44
|
@property() name: string;
|
|
45
|
+
|
|
44
46
|
/** The value of the select. Used for form submission. */
|
|
45
47
|
@property() value: string;
|
|
48
|
+
|
|
46
49
|
/** The provider of the select. */
|
|
47
50
|
@property() provider: 'color' | 'currency' | 'country' | 'phone';
|
|
51
|
+
|
|
52
|
+
/** The position of the icon. */
|
|
48
53
|
@property({attribute: 'icon-position'}) iconPosition: 'start' | 'end' | 'none' = 'none';
|
|
54
|
+
|
|
49
55
|
/** An array of keys to use for filtering the options in the selected provider. */
|
|
50
56
|
@property({
|
|
51
57
|
attribute: 'filter',
|
|
@@ -54,27 +60,39 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
54
60
|
toAttribute: (value: string[]) => value.join(',')
|
|
55
61
|
}
|
|
56
62
|
}) filter: string[];
|
|
63
|
+
|
|
57
64
|
/** The selects size. */
|
|
58
65
|
@property({reflect: true}) size: 'small' | 'medium' | 'large' = 'medium';
|
|
66
|
+
|
|
59
67
|
/** Should we show the clear button */
|
|
60
68
|
@property({type: Boolean}) clearable: boolean;
|
|
69
|
+
|
|
61
70
|
/** Include an "All" option at the top. */
|
|
62
71
|
@property({type: Boolean, attribute: 'allow-all'}) allowAll = false;
|
|
72
|
+
|
|
63
73
|
/** The selects label. If you need to display HTML, use the `label` slot instead. */
|
|
64
74
|
@property() label = '';
|
|
75
|
+
|
|
65
76
|
/** Text that appears in a tooltip next to the label. If you need to display HTML in the tooltip, use the `label-tooltip` slot instead. */
|
|
66
77
|
@property({attribute: 'label-tooltip'}) labelTooltip = '';
|
|
78
|
+
|
|
67
79
|
/** Text that appears above the input, on the right, to add additional context. If you need to display HTML in this text, use the `context-note` slot instead. */
|
|
68
80
|
@property({attribute: 'context-note'}) contextNote = '';
|
|
81
|
+
|
|
69
82
|
/**
|
|
70
83
|
* The preferred placement of the selects menu. Note that the actual placement may vary as needed to keep the listbox
|
|
71
84
|
* inside the viewport.
|
|
72
85
|
*/
|
|
73
86
|
@property({reflect: true}) placement: 'top' | 'bottom' = 'bottom';
|
|
87
|
+
|
|
74
88
|
/** The selects help text. If you need to display HTML, use the `help-text` slot instead. */
|
|
75
89
|
@property({attribute: 'help-text'}) helpText = '';
|
|
90
|
+
|
|
76
91
|
/** The selects required attribute. */
|
|
77
92
|
@property({type: Boolean, reflect: true}) required = false;
|
|
93
|
+
|
|
94
|
+
@property({attribute: "icon-only", type: Boolean, reflect: true}) iconOnly = false;
|
|
95
|
+
|
|
78
96
|
protected readonly formControlController = new FormControlController(this);
|
|
79
97
|
|
|
80
98
|
get validationMessage() {
|
|
@@ -85,6 +103,13 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
85
103
|
return this.select.validity;
|
|
86
104
|
}
|
|
87
105
|
|
|
106
|
+
constructor() {
|
|
107
|
+
super();
|
|
108
|
+
if (this.iconOnly && this.iconPosition === 'none') {
|
|
109
|
+
this.iconPosition = 'start';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
88
113
|
connectedCallback() {
|
|
89
114
|
super.connectedCallback();
|
|
90
115
|
window.addEventListener('keydown', this.closeOnTab);
|
|
@@ -95,6 +120,11 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
95
120
|
window.removeEventListener('keydown', this.closeOnTab);
|
|
96
121
|
}
|
|
97
122
|
|
|
123
|
+
protected firstUpdated(_changedProperties: PropertyValues) {
|
|
124
|
+
super.firstUpdated(_changedProperties);
|
|
125
|
+
this._updatePrefix();
|
|
126
|
+
}
|
|
127
|
+
|
|
98
128
|
checkValidity(): boolean {
|
|
99
129
|
return this.select.checkValidity();
|
|
100
130
|
}
|
|
@@ -185,15 +215,15 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
185
215
|
@zn-clear="${this.handleClear}"
|
|
186
216
|
@blur=${this.blur}
|
|
187
217
|
value="${this.value}"
|
|
188
|
-
placeholder="
|
|
218
|
+
placeholder="${this.getPlaceholder(localProvider)}"
|
|
189
219
|
exportparts="combobox,expand-icon,form-control-help-text,form-control-input,display-input">
|
|
190
|
-
${this.iconPosition !== 'none' ? html`
|
|
220
|
+
${(this.iconPosition !== 'none' || this.iconOnly) ? html`
|
|
191
221
|
<div id="select__prefix" slot="prefix" class="select__prefix"></div>` : ''}
|
|
192
222
|
${data.map((item: DataProviderOption) => html`
|
|
193
223
|
<zn-option class="select__option" value="${item.key}">
|
|
194
|
-
${this.iconPosition !== 'none' ? html`<span
|
|
224
|
+
${(this.iconPosition !== 'none' || this.iconOnly) ? html`<span
|
|
195
225
|
slot="${this.iconPosition === 'end' ? 'suffix' : 'prefix'}">${item.prefix}</span>` : ''}
|
|
196
|
-
${item.value}
|
|
226
|
+
${this.iconOnly ? undefined : item.value}
|
|
197
227
|
</zn-option>`)}
|
|
198
228
|
</zn-select>`;
|
|
199
229
|
}
|
|
@@ -201,19 +231,30 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
201
231
|
private _updatePrefix() {
|
|
202
232
|
// Set the prefix of the select to the selected values prefix
|
|
203
233
|
const selectedOption = this.select.selectedOptions[0];
|
|
204
|
-
|
|
234
|
+
console.log('selectedOption', selectedOption);
|
|
235
|
+
if (selectedOption && (this.iconPosition !== 'none' || this.iconOnly) && this.selectPrefix) {
|
|
236
|
+
if (this.iconPosition === 'none' && this.iconOnly) {
|
|
237
|
+
this.iconPosition = 'start';
|
|
238
|
+
}
|
|
205
239
|
const slot = this.iconPosition === 'start'
|
|
206
240
|
? selectedOption.querySelector('[slot="prefix"]')
|
|
207
241
|
: selectedOption.querySelector('[slot="suffix"]');
|
|
208
|
-
|
|
209
242
|
if (slot) {
|
|
210
243
|
this.selectPrefix.innerHTML = '';
|
|
211
244
|
this.selectPrefix.appendChild(slot.cloneNode(true));
|
|
212
245
|
} else {
|
|
213
246
|
this.selectPrefix.innerHTML = '';
|
|
214
247
|
}
|
|
215
|
-
} else {
|
|
248
|
+
} else if (this.selectPrefix) {
|
|
216
249
|
this.selectPrefix.innerHTML = '';
|
|
217
250
|
}
|
|
218
251
|
}
|
|
252
|
+
|
|
253
|
+
private getPlaceholder(localProvider: LocalDataProvider<any>) {
|
|
254
|
+
if (this.value) {
|
|
255
|
+
return '';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return `Choose a ${localProvider.getName}`
|
|
259
|
+
}
|
|
219
260
|
}
|
|
@@ -19,7 +19,7 @@ import ZnHoverContainer from "../hover-container";
|
|
|
19
19
|
import ZnMenu from "../menu";
|
|
20
20
|
import ZnMenuItem from "../menu-item";
|
|
21
21
|
import ZnSkeleton from "../skeleton";
|
|
22
|
-
|
|
22
|
+
import ZnStyle from "../style";
|
|
23
23
|
import type ZnDataSelect from "../data-select";
|
|
24
24
|
import type ZnInput from "../input";
|
|
25
25
|
import type ZnQueryBuilder from "../query-builder";
|
|
@@ -148,6 +148,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
148
148
|
'zn-button-group': ZnButtonGroup,
|
|
149
149
|
'zn-confirm': ZnConfirm,
|
|
150
150
|
'zn-skeleton': ZnSkeleton,
|
|
151
|
+
'zn-style': ZnStyle,
|
|
151
152
|
};
|
|
152
153
|
|
|
153
154
|
@property({attribute: 'data-uri'}) dataUri: string;
|
|
@@ -183,7 +184,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
183
184
|
@property({attribute: "empty-state-icon"}) emptyStateIcon: string = "data_alert";
|
|
184
185
|
|
|
185
186
|
// Hide the checkbox column
|
|
186
|
-
@property({attribute: 'hide-checkboxes', type: Boolean}) hideCheckboxes: boolean;
|
|
187
|
+
@property({attribute: 'hide-checkboxes', type: Boolean}) hideCheckboxes: boolean = true;
|
|
187
188
|
|
|
188
189
|
@property() filters: [] = [];
|
|
189
190
|
|
|
@@ -277,8 +278,6 @@ export default class ZnDataTable extends ZincElement {
|
|
|
277
278
|
|
|
278
279
|
requestParams: Record<string, any> = {};
|
|
279
280
|
|
|
280
|
-
builtinRequestParams: Record<string, any> = {};
|
|
281
|
-
|
|
282
281
|
refresh() {
|
|
283
282
|
// Allow manual refresh to trigger the first data load when no-initial-load is set
|
|
284
283
|
this._initialLoad = false;
|
|
@@ -330,12 +329,15 @@ export default class ZnDataTable extends ZincElement {
|
|
|
330
329
|
|| this.hasSlotController.test(ActionSlots.create.valueOf())
|
|
331
330
|
|| this.hasSlotController.test(ActionSlots.sort.valueOf())
|
|
332
331
|
|| this.hasSlotController.test(ActionSlots.filter.valueOf())
|
|
333
|
-
|| this.hasSlotController.test(ActionSlots.filter_top.valueOf())
|
|
334
|
-
|
|
332
|
+
|| this.hasSlotController.test(ActionSlots.filter_top.valueOf());
|
|
333
|
+
|
|
334
|
+
const hasInputs = this.hasSlotController.test(ActionSlots.inputs.valueOf());
|
|
335
335
|
|
|
336
336
|
// Headers do not need to be re-rendered with new data
|
|
337
337
|
return html`
|
|
338
338
|
<div class="table-container" ${ref((el) => (this.tableContainer = el))}>
|
|
339
|
+
${hasInputs ? html`
|
|
340
|
+
<slot name="${ActionSlots.inputs.valueOf()}" style="display: none"></slot>` : null}
|
|
339
341
|
${hasActions ? this.getTableHeader() : html``}
|
|
340
342
|
${tableBody}
|
|
341
343
|
</div>
|
|
@@ -420,7 +422,6 @@ export default class ZnDataTable extends ZincElement {
|
|
|
420
422
|
'table': true,
|
|
421
423
|
'table--standalone': this.standalone,
|
|
422
424
|
'with-hover': !this.unsortable,
|
|
423
|
-
'table-with--checkboxes': !this.hideCheckboxes,
|
|
424
425
|
})}">
|
|
425
426
|
<thead>
|
|
426
427
|
<tr>
|
|
@@ -435,11 +436,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
435
436
|
</thead>
|
|
436
437
|
<tbody>
|
|
437
438
|
${this._rows.map((row: Row) => html`
|
|
438
|
-
<tr>
|
|
439
|
-
${this.hideCheckboxes ? html`` : html`
|
|
440
|
-
<td class="${classMap({'hidden': !hasSelectedRows})}">
|
|
441
|
-
<div><input type="checkbox" @change="${this.selectRow}"></div>
|
|
442
|
-
</td>`}
|
|
439
|
+
<tr class="${classMap({'table__row--selected': this.isRowSelected(row)})}">
|
|
443
440
|
${row.cells.map((value: Cell, index: number) => this.renderCellBody(index, value))}
|
|
444
441
|
${this.rowHasActions ? this.renderActions(row) : null}
|
|
445
442
|
</tr>`)}
|
|
@@ -454,7 +451,6 @@ export default class ZnDataTable extends ZincElement {
|
|
|
454
451
|
getTableHeader() {
|
|
455
452
|
return html`
|
|
456
453
|
<slot name="${ActionSlots.filter_top.valueOf()}"></slot>
|
|
457
|
-
<slot name="${ActionSlots.inputs.valueOf()}" style="display: none"></slot>
|
|
458
454
|
<div class="table__header">
|
|
459
455
|
<div class="table__header__actions">
|
|
460
456
|
${this.getActions()}
|
|
@@ -487,7 +483,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
487
483
|
}
|
|
488
484
|
|
|
489
485
|
getRowsSelected() {
|
|
490
|
-
if (this.
|
|
486
|
+
if (this.selectedRows.length <= 0) return null;
|
|
491
487
|
|
|
492
488
|
return html`
|
|
493
489
|
<p>${this.numberOfRowsSelected} of ${this._rows.length} rows selected</p>`
|
|
@@ -621,7 +617,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
621
617
|
const checked = checkbox.checked;
|
|
622
618
|
|
|
623
619
|
// go through all the checkboxes and check them
|
|
624
|
-
for (const row of this.renderRoot.querySelectorAll('tbody
|
|
620
|
+
for (const row of this.renderRoot.querySelectorAll('tbody zn-checkbox')) {
|
|
625
621
|
(row as HTMLInputElement).checked = checked;
|
|
626
622
|
}
|
|
627
623
|
|
|
@@ -631,34 +627,40 @@ export default class ZnDataTable extends ZincElement {
|
|
|
631
627
|
this.requestUpdate();
|
|
632
628
|
}
|
|
633
629
|
|
|
634
|
-
selectRow(e:
|
|
630
|
+
selectRow(e: Event) {
|
|
635
631
|
if (!(e.target && (e.target instanceof Element))) {
|
|
636
632
|
return;
|
|
637
633
|
}
|
|
638
634
|
|
|
639
|
-
|
|
635
|
+
const target: Element | null = e.target;
|
|
640
636
|
// if is a button or a link continue
|
|
641
|
-
if (target
|
|
642
|
-
return
|
|
637
|
+
if (target?.tagName === 'ZN-BUTTON' || target?.tagName === 'A' || target?.tagName === 'BUTTON') {
|
|
638
|
+
return;
|
|
643
639
|
}
|
|
644
640
|
|
|
645
|
-
//
|
|
646
|
-
|
|
647
|
-
|
|
641
|
+
// Find the parent row
|
|
642
|
+
let parent: Element | null = target.closest ? target.closest('tr') : target.parentElement;
|
|
643
|
+
while (parent && parent.tagName !== 'TR') {
|
|
644
|
+
parent = parent.parentElement;
|
|
648
645
|
}
|
|
649
646
|
|
|
650
|
-
if (
|
|
647
|
+
if (parent === null) {
|
|
651
648
|
return;
|
|
652
649
|
}
|
|
653
650
|
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
}
|
|
651
|
+
const rows = Array.from(this.renderRoot.querySelectorAll('tbody tr'));
|
|
652
|
+
const index = rows.indexOf(parent as HTMLTableRowElement);
|
|
653
|
+
if (index === -1) return;
|
|
658
654
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
655
|
+
const row = this._rows[index] as Row;
|
|
656
|
+
if (!row) return;
|
|
657
|
+
|
|
658
|
+
const alreadySelected = this.selectedRows.some((r: Row) => r.id === row.id);
|
|
659
|
+
if (alreadySelected) {
|
|
660
|
+
this.selectedRows = this.selectedRows.filter((r: Row) => r.id !== row.id);
|
|
661
|
+
} else {
|
|
662
|
+
this.selectedRows = [...(this.selectedRows as Row[]), row];
|
|
663
|
+
}
|
|
662
664
|
|
|
663
665
|
this.numberOfRowsSelected = this.selectedRows.length;
|
|
664
666
|
this.updateKeys();
|
|
@@ -669,13 +671,9 @@ export default class ZnDataTable extends ZincElement {
|
|
|
669
671
|
const button = event.target as ZnButton;
|
|
670
672
|
if (button.disabled) return;
|
|
671
673
|
|
|
672
|
-
(this.renderRoot.querySelectorAll('thead input[type="checkbox"]')[0] as HTMLInputElement).checked = false;
|
|
673
|
-
for (const row of this.renderRoot.querySelectorAll('tbody input[type="checkbox"]')) {
|
|
674
|
-
(row as HTMLInputElement).checked = false;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
674
|
this.selectedRows = [];
|
|
678
675
|
this.numberOfRowsSelected = 0;
|
|
676
|
+
this.updateKeys();
|
|
679
677
|
this.requestUpdate();
|
|
680
678
|
}
|
|
681
679
|
|
|
@@ -690,12 +688,30 @@ export default class ZnDataTable extends ZincElement {
|
|
|
690
688
|
|
|
691
689
|
renderCell(data: Cell) {
|
|
692
690
|
if (data && typeof data === 'object') {
|
|
693
|
-
let content: TemplateResult = html`${data.text}`;
|
|
691
|
+
let content: TemplateResult | ZincElement = html`${data.text}`;
|
|
694
692
|
|
|
695
693
|
if (data.style || data.color) {
|
|
694
|
+
const styleStr = typeof data.style === 'string' ? data.style : '';
|
|
695
|
+
const tokens = new Set(styleStr.split(',').filter(Boolean));
|
|
696
|
+
|
|
697
|
+
const isMono = tokens.has('mono') || tokens.has('code');
|
|
698
|
+
const isBorder = tokens.has('border');
|
|
699
|
+
const isCenter = tokens.has('center');
|
|
700
|
+
|
|
701
|
+
if (tokens.has('bold') || tokens.has('strong')) {
|
|
702
|
+
content = html`<strong>${content}</strong>`;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (tokens.has('italic')) {
|
|
706
|
+
content = html`<em>${content}</em>`;
|
|
707
|
+
}
|
|
708
|
+
|
|
696
709
|
content = html`
|
|
697
|
-
<zn-style
|
|
698
|
-
|
|
710
|
+
<zn-style
|
|
711
|
+
font="${isMono ? 'mono' : nothing}"
|
|
712
|
+
border="${isBorder || nothing}"
|
|
713
|
+
center="${isCenter || nothing}"
|
|
714
|
+
color="${ifDefined(data.color)}">${content}
|
|
699
715
|
</zn-style>`;
|
|
700
716
|
}
|
|
701
717
|
|
|
@@ -827,6 +843,10 @@ export default class ZnDataTable extends ZincElement {
|
|
|
827
843
|
</td>`;
|
|
828
844
|
}
|
|
829
845
|
|
|
846
|
+
private isRowSelected(row: Row): boolean {
|
|
847
|
+
return this.selectedRows.some((r: Row) => r.id === row.id);
|
|
848
|
+
}
|
|
849
|
+
|
|
830
850
|
private getRows(data: Response): Row[] {
|
|
831
851
|
const sourceRows = data.rows;
|
|
832
852
|
|