@kubex/zinc 1.1.66 → 1.1.68
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 +410 -280
- package/dist/vscode.html-custom-data.json +65 -31
- package/dist/web-types.json +129 -69
- package/dist/zn.d.ts +44 -6
- package/dist/zn.min.js +439 -433
- package/package.json +1 -1
- package/src/components/data-table/data-table.component.ts +68 -45
- package/src/components/data-table/data-table.scss +4 -0
- package/src/components/data-table/data-table.test.ts +88 -0
- package/src/components/defined-label/defined-label.component.ts +118 -95
- package/src/components/defined-label/defined-label.scss +40 -28
- package/src/components/file/file.component.ts +70 -0
- package/src/components/page-builder/page-builder.scss +1 -1
package/package.json
CHANGED
|
@@ -36,6 +36,7 @@ const DEFAULT_PER_PAGE = 10;
|
|
|
36
36
|
|
|
37
37
|
interface Cell {
|
|
38
38
|
text: string;
|
|
39
|
+
subText?: string;
|
|
39
40
|
column: string;
|
|
40
41
|
color?: string;
|
|
41
42
|
style?: string;
|
|
@@ -51,6 +52,7 @@ interface Cell {
|
|
|
51
52
|
uri?: string;
|
|
52
53
|
target?: string;
|
|
53
54
|
copyable?: boolean;
|
|
55
|
+
title?: string;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
interface Row {
|
|
@@ -123,7 +125,7 @@ const defaultTemplates = {
|
|
|
123
125
|
dateTime: ((cell) => {
|
|
124
126
|
const t = cell.text || '';
|
|
125
127
|
return html`
|
|
126
|
-
<div class="table__collum--datetime">
|
|
128
|
+
<div class="table__collum--datetime" title="${ifDefined(cell.title)}">
|
|
127
129
|
<span><strong>${t.slice(0, 10)}</strong></span>
|
|
128
130
|
<zn-style size="s" muted>${t.slice(11)}</zn-style>
|
|
129
131
|
</div>`
|
|
@@ -178,6 +180,7 @@ type AllowedInputElement =
|
|
|
178
180
|
* @slot create-action - Slot for create action button.
|
|
179
181
|
* @slot inputs - Slot for additional input controls.
|
|
180
182
|
* @slot empty-state - Slot for custom empty state.
|
|
183
|
+
* @slot no-results - Slot for a custom no-results state, shown when a search on a no-initial-load table returns no rows.
|
|
181
184
|
*
|
|
182
185
|
* @csspart base - The component's base wrapper.
|
|
183
186
|
*
|
|
@@ -288,7 +291,8 @@ export default class ZnDataTable extends ZincElement {
|
|
|
288
291
|
ActionSlots.filter.valueOf(),
|
|
289
292
|
ActionSlots.sort.valueOf(),
|
|
290
293
|
ActionSlots.inputs.valueOf(),
|
|
291
|
-
'empty-state'
|
|
294
|
+
'empty-state',
|
|
295
|
+
'no-results'
|
|
292
296
|
);
|
|
293
297
|
|
|
294
298
|
private _dataTask = new Task(this, {
|
|
@@ -501,6 +505,22 @@ export default class ZnDataTable extends ZincElement {
|
|
|
501
505
|
}
|
|
502
506
|
|
|
503
507
|
emptyState() {
|
|
508
|
+
if (this.noInitialLoad && !this._initialLoad) {
|
|
509
|
+
if (this.hasSlotController.test('no-results')) {
|
|
510
|
+
return html`
|
|
511
|
+
<div class="table--empty">
|
|
512
|
+
<slot name="no-results"></slot>
|
|
513
|
+
</div>`;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return html`
|
|
517
|
+
<div class="table--empty">
|
|
518
|
+
<zn-empty-state caption="No Results Found" icon="search">
|
|
519
|
+
<p>Nothing matched your search. Try adjusting your filters.</p>
|
|
520
|
+
</zn-empty-state>
|
|
521
|
+
</div>`;
|
|
522
|
+
}
|
|
523
|
+
|
|
504
524
|
if (this.hasSlotController.test('empty-state')) {
|
|
505
525
|
return html`
|
|
506
526
|
<div class="table--empty">
|
|
@@ -1019,7 +1039,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1019
1039
|
if (header?.cellTemplate !== undefined) {
|
|
1020
1040
|
data = {...header.cellTemplate, ...data}
|
|
1021
1041
|
}
|
|
1022
|
-
|
|
1042
|
+
|
|
1023
1043
|
let content: TemplateResult | ZincElement = html`${data.text}`;
|
|
1024
1044
|
|
|
1025
1045
|
if (data.style || data.color) {
|
|
@@ -1045,55 +1065,25 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1045
1065
|
border="${isBorder || nothing}"
|
|
1046
1066
|
center="${isCenter || nothing}"
|
|
1047
1067
|
muted="${isMuted || nothing}"
|
|
1048
|
-
color="${ifDefined(data.color)}"
|
|
1068
|
+
color="${ifDefined(data.color)}"
|
|
1069
|
+
title="${ifDefined(data.title)}"
|
|
1070
|
+
>${content}
|
|
1049
1071
|
</zn-style>`;
|
|
1050
1072
|
}
|
|
1051
1073
|
|
|
1052
1074
|
if (data.uri) {
|
|
1053
1075
|
content = html`
|
|
1054
1076
|
<a href="${data.uri}"
|
|
1077
|
+
title="${ifDefined(data.title)}"
|
|
1055
1078
|
data-target="${ifDefined(data.target || nothing)}"
|
|
1056
1079
|
gaid="${ifDefined(data.gaid || nothing)}">${content}</a>`;
|
|
1057
1080
|
}
|
|
1058
1081
|
|
|
1059
1082
|
if (data.chipColor) {
|
|
1060
|
-
|
|
1061
|
-
<zn-chip type="${data.chipColor}">${content}</zn-chip>`;
|
|
1083
|
+
content = html`
|
|
1084
|
+
<zn-chip type="${data.chipColor}" title="${ifDefined(data.title)}">${content}</zn-chip>`;
|
|
1062
1085
|
}
|
|
1063
1086
|
|
|
1064
|
-
if (data.hoverContent) {
|
|
1065
|
-
const placement = data.hoverPlacement ?? 'top';
|
|
1066
|
-
|
|
1067
|
-
if (data.iconSrc) {
|
|
1068
|
-
const src = data.iconSrc;
|
|
1069
|
-
const color = data.iconColor;
|
|
1070
|
-
const size = data.iconSize;
|
|
1071
|
-
const tokens = (data.iconStyle ?? '').split(',').map(s => s.trim()).filter(Boolean);
|
|
1072
|
-
|
|
1073
|
-
return html`
|
|
1074
|
-
${content}
|
|
1075
|
-
<zn-hover-container placement="${placement}" flip>
|
|
1076
|
-
<zn-icon
|
|
1077
|
-
src="${src}"
|
|
1078
|
-
size="${ifDefined(size)}"
|
|
1079
|
-
color="${ifDefined(color)}"
|
|
1080
|
-
${ref(el => {
|
|
1081
|
-
if (!el) return;
|
|
1082
|
-
tokens.forEach(t => el.setAttribute(t, ''));
|
|
1083
|
-
})}
|
|
1084
|
-
></zn-icon>
|
|
1085
|
-
<div slot="content">
|
|
1086
|
-
${unsafeHTML(data.hoverContent)}
|
|
1087
|
-
</div>
|
|
1088
|
-
</zn-hover-container>`;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
return html`
|
|
1092
|
-
<zn-hover-container placement="${placement}" flip>
|
|
1093
|
-
<div slot="anchor">${content}</div>
|
|
1094
|
-
${unsafeHTML(data.hoverContent)}
|
|
1095
|
-
</zn-hover-container>`;
|
|
1096
|
-
}
|
|
1097
1087
|
|
|
1098
1088
|
if (data.iconSrc) {
|
|
1099
1089
|
const src = data.iconSrc;
|
|
@@ -1101,24 +1091,57 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1101
1091
|
const size = data.iconSize;
|
|
1102
1092
|
const tokens = (data.iconStyle ?? '').split(',').map(s => s.trim()).filter(Boolean);
|
|
1103
1093
|
|
|
1104
|
-
|
|
1094
|
+
content = html`
|
|
1105
1095
|
<zn-icon
|
|
1106
1096
|
src="${src}"
|
|
1107
1097
|
size="${ifDefined(size)}"
|
|
1108
1098
|
color="${ifDefined(color)}"
|
|
1109
1099
|
${ref(el => {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1100
|
+
if (!el) return;
|
|
1101
|
+
tokens.forEach(t => el.setAttribute(t, ''));
|
|
1102
|
+
})}
|
|
1103
|
+
title="${ifDefined(data.title)}"
|
|
1113
1104
|
></zn-icon> ${content}`;
|
|
1114
1105
|
}
|
|
1115
1106
|
|
|
1107
|
+
if (data.hoverContent) {
|
|
1108
|
+
const placement = data.hoverPlacement ?? 'top';
|
|
1109
|
+
if (data.iconSrc) {
|
|
1110
|
+
content = html`
|
|
1111
|
+
<zn-hover-container placement="${placement}" flip>
|
|
1112
|
+
${content}
|
|
1113
|
+
<div slot="content">
|
|
1114
|
+
${unsafeHTML(data.hoverContent)}
|
|
1115
|
+
</div>
|
|
1116
|
+
</zn-hover-container>`;
|
|
1117
|
+
} else {
|
|
1118
|
+
content = html`
|
|
1119
|
+
<zn-hover-container placement="${placement}" flip>
|
|
1120
|
+
${content}
|
|
1121
|
+
<div slot="content">
|
|
1122
|
+
${unsafeHTML(data.hoverContent)}
|
|
1123
|
+
</div>
|
|
1124
|
+
</zn-hover-container>`;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1116
1128
|
if (data.copyable) {
|
|
1117
|
-
|
|
1129
|
+
content = html`
|
|
1118
1130
|
${content}
|
|
1119
1131
|
<zn-copy-button value="${data.text}"></zn-copy-button>`;
|
|
1120
1132
|
}
|
|
1121
1133
|
|
|
1134
|
+
|
|
1135
|
+
if (data.subText) {
|
|
1136
|
+
content = html`
|
|
1137
|
+
<div>
|
|
1138
|
+
<div>${content}</div>
|
|
1139
|
+
<span class="table__cell--subtext">${data.subText}</span>
|
|
1140
|
+
</div>
|
|
1141
|
+
`
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
|
|
1122
1145
|
return content;
|
|
1123
1146
|
|
|
1124
1147
|
}
|
|
@@ -1199,7 +1222,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1199
1222
|
return !header.secondary;
|
|
1200
1223
|
});
|
|
1201
1224
|
const header: HeaderConfig | undefined = filteredHeaders[index];
|
|
1202
|
-
const headerKey: string = header?.key;
|
|
1225
|
+
const headerKey: string | undefined = header?.key;
|
|
1203
1226
|
|
|
1204
1227
|
return html`
|
|
1205
1228
|
<td
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
2
|
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
|
|
3
|
+
import { render } from 'lit';
|
|
3
4
|
import type ZnDataTable from './data-table.component';
|
|
4
5
|
|
|
5
6
|
describe('<zn-data-table>', () => {
|
|
@@ -73,9 +74,96 @@ describe('<zn-data-table>', () => {
|
|
|
73
74
|
}
|
|
74
75
|
});
|
|
75
76
|
|
|
77
|
+
it('shows the empty-state slot before any load on a no-initial-load table, then a no-results state after an empty search', async () => {
|
|
78
|
+
const originalFetch = window.fetch;
|
|
79
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({rows: [], page: 1, perPage: 10, total: 0}),
|
|
80
|
+
{status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const el = await fixture<ZnDataTable>(html`
|
|
84
|
+
<zn-data-table data-uri="/test-data" no-initial-load headers='{"name": {"key": "name", "label": "Name"}}'>
|
|
85
|
+
<div slot="empty-state" id="filters-prompt">No Filters Selected</div>
|
|
86
|
+
</zn-data-table>`);
|
|
87
|
+
|
|
88
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.exist;
|
|
89
|
+
|
|
90
|
+
el.refresh();
|
|
91
|
+
await waitUntil(() => el.shadowRoot?.querySelector('zn-empty-state'));
|
|
92
|
+
|
|
93
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.not.exist;
|
|
94
|
+
const emptyState = el.shadowRoot!.querySelector('zn-empty-state')!;
|
|
95
|
+
expect(emptyState.getAttribute('caption')).to.equal('No Results Found');
|
|
96
|
+
} finally {
|
|
97
|
+
window.fetch = originalFetch;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('prefers the no-results slot after an empty search on a no-initial-load table', async () => {
|
|
102
|
+
const originalFetch = window.fetch;
|
|
103
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({rows: [], page: 1, perPage: 10, total: 0}),
|
|
104
|
+
{status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const el = await fixture<ZnDataTable>(html`
|
|
108
|
+
<zn-data-table data-uri="/test-data" no-initial-load headers='{"name": {"key": "name", "label": "Name"}}'>
|
|
109
|
+
<div slot="empty-state">No Filters Selected</div>
|
|
110
|
+
<div slot="no-results">No Customers Found</div>
|
|
111
|
+
</zn-data-table>`);
|
|
112
|
+
|
|
113
|
+
el.refresh();
|
|
114
|
+
await waitUntil(() => el.shadowRoot?.querySelector('slot[name="no-results"]'));
|
|
115
|
+
|
|
116
|
+
expect(el.shadowRoot!.querySelector('slot[name="no-results"]')).to.exist;
|
|
117
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.not.exist;
|
|
118
|
+
} finally {
|
|
119
|
+
window.fetch = originalFetch;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
76
123
|
it('exposes displayTemplates as an object property', async () => {
|
|
77
124
|
const el = await fixture<ZnDataTable>(html` <zn-data-table></zn-data-table> `);
|
|
78
125
|
const templates = (el as unknown as {displayTemplates: Record<string, unknown>}).displayTemplates;
|
|
79
126
|
expect(templates).to.be.an('object');
|
|
80
127
|
});
|
|
128
|
+
|
|
129
|
+
it('renders subText as muted secondary text', async () => {
|
|
130
|
+
const el = await fixture<ZnDataTable>(html` <zn-data-table></zn-data-table> `);
|
|
131
|
+
const container = document.createElement('div');
|
|
132
|
+
render(el.renderCell({text: 'Main', column: 'name', subText: 'Secondary'}), container);
|
|
133
|
+
|
|
134
|
+
const subtext = container.querySelector('.table__cell--subtext');
|
|
135
|
+
expect(subtext).to.exist;
|
|
136
|
+
expect(subtext?.textContent).to.contain('Secondary');
|
|
137
|
+
expect(container.textContent).to.contain('Main');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('applies title as a tooltip on link cells', async () => {
|
|
141
|
+
const el = await fixture<ZnDataTable>(html` <zn-data-table></zn-data-table> `);
|
|
142
|
+
const container = document.createElement('div');
|
|
143
|
+
render(el.renderCell({text: 'Go', column: 'name', uri: 'https://example.com', title: 'Tip'}), container);
|
|
144
|
+
|
|
145
|
+
const link = container.querySelector('a');
|
|
146
|
+
expect(link).to.exist;
|
|
147
|
+
expect(link?.getAttribute('title')).to.equal('Tip');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('applies title as a tooltip on styled cells', async () => {
|
|
151
|
+
const el = await fixture<ZnDataTable>(html` <zn-data-table></zn-data-table> `);
|
|
152
|
+
const container = document.createElement('div');
|
|
153
|
+
render(el.renderCell({text: 'Val', column: 'name', style: 'bold', title: 'Info'}), container);
|
|
154
|
+
|
|
155
|
+
const styled = container.querySelector('zn-style');
|
|
156
|
+
expect(styled).to.exist;
|
|
157
|
+
expect(styled?.getAttribute('title')).to.equal('Info');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('omits the title attribute when no title is provided', async () => {
|
|
161
|
+
const el = await fixture<ZnDataTable>(html` <zn-data-table></zn-data-table> `);
|
|
162
|
+
const container = document.createElement('div');
|
|
163
|
+
render(el.renderCell({text: 'Plain', column: 'name', uri: 'https://example.com'}), container);
|
|
164
|
+
|
|
165
|
+
const link = container.querySelector('a');
|
|
166
|
+
expect(link).to.exist;
|
|
167
|
+
expect(link?.hasAttribute('title')).to.be.false;
|
|
168
|
+
});
|
|
81
169
|
});
|
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
import {type CSSResultGroup, html, type PropertyValues, type TemplateResult, unsafeCSS} from 'lit';
|
|
1
|
+
import {type CSSResultGroup, html, nothing, type PropertyValues, type TemplateResult, unsafeCSS} from 'lit';
|
|
2
2
|
import {FormControlController} from '../../internal/form';
|
|
3
3
|
import {ifDefined} from 'lit/directives/if-defined.js';
|
|
4
4
|
import {property, query} from 'lit/decorators.js';
|
|
5
5
|
import {watch} from '../../internal/watch';
|
|
6
6
|
import ZincElement from '../../internal/zinc-element';
|
|
7
|
+
import ZnButton from '../button';
|
|
8
|
+
import ZnDropdown from '../dropdown';
|
|
9
|
+
import ZnInput from '../input';
|
|
10
|
+
import ZnOption from '../option';
|
|
11
|
+
import ZnSelect from '../select';
|
|
7
12
|
import type {ZincFormControl} from '../../internal/zinc-element';
|
|
8
|
-
import type ZnDropdown from '../dropdown';
|
|
9
|
-
import type ZnInput from '../input';
|
|
10
13
|
|
|
11
14
|
import styles from './defined-label.scss';
|
|
12
15
|
|
|
16
|
+
interface PredefinedLabel {
|
|
17
|
+
name: string;
|
|
18
|
+
options?: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
/**
|
|
14
22
|
* @summary This component provides a labeled input with support for predefined and custom labels,
|
|
15
23
|
* allowing users to select or enter label-value pairs within a dropdown interface.
|
|
@@ -21,31 +29,51 @@ import styles from './defined-label.scss';
|
|
|
21
29
|
* @dependency zn-dropdown
|
|
22
30
|
* @dependency zn-input
|
|
23
31
|
* @dependency zn-option
|
|
24
|
-
* @dependency zn-panel
|
|
25
32
|
* @dependency zn-select
|
|
26
|
-
* @dependency zn-sp
|
|
27
33
|
*
|
|
28
34
|
* @csspart input - The component's main input.
|
|
29
35
|
* @csspart input-value - The label's value inputs.
|
|
30
36
|
*/
|
|
31
37
|
export default class ZnDefinedLabel extends ZincElement implements ZincFormControl {
|
|
32
38
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
39
|
+
static dependencies = {
|
|
40
|
+
'zn-button': ZnButton,
|
|
41
|
+
'zn-dropdown': ZnDropdown,
|
|
42
|
+
'zn-input': ZnInput,
|
|
43
|
+
'zn-option': ZnOption,
|
|
44
|
+
'zn-select': ZnSelect
|
|
45
|
+
};
|
|
33
46
|
|
|
34
47
|
private readonly formControlController = new FormControlController(this, {
|
|
35
48
|
value: (control: this) => control.value + (control.inputValue ? `:${control.inputValue}` : ''),
|
|
36
49
|
});
|
|
37
50
|
|
|
38
|
-
@query('.
|
|
51
|
+
@query('.defined-label__input') input: ZnInput;
|
|
39
52
|
@query('.defined-label__dropdown') dropdown: ZnDropdown;
|
|
40
53
|
|
|
54
|
+
/** The selected label key. Also acts as the filter while typing. */
|
|
41
55
|
@property() value: string = '';
|
|
56
|
+
|
|
57
|
+
/** The value entered for the selected label. Submitted as `label:value` when present. */
|
|
42
58
|
@property() inputValue: string = '';
|
|
59
|
+
|
|
60
|
+
/** The size of the main input. */
|
|
43
61
|
@property({attribute: 'input-size', reflect: true}) inputSize: 'x-small' | 'small' | 'medium' | 'large' = 'medium';
|
|
62
|
+
|
|
63
|
+
/** The name of the control. Used for form submission. */
|
|
44
64
|
@property() name: string = 'label';
|
|
65
|
+
|
|
66
|
+
/** The title of the main input. */
|
|
45
67
|
@property() title: string;
|
|
68
|
+
|
|
69
|
+
/** Disables the control. */
|
|
46
70
|
@property({type: Boolean}) disabled: boolean = false;
|
|
71
|
+
|
|
72
|
+
/** Allows labels that aren't in the predefined list. */
|
|
47
73
|
@property({attribute: 'allow-custom', type: Boolean}) allowCustom: boolean = false;
|
|
48
|
-
|
|
74
|
+
|
|
75
|
+
/** The predefined labels. Entries are either a string or `{name, options}`. */
|
|
76
|
+
@property({type: Array, attribute: 'predefined-labels'}) predefinedLabels: (PredefinedLabel | string)[] = [];
|
|
49
77
|
|
|
50
78
|
get validationMessage() {
|
|
51
79
|
return this.input.validationMessage;
|
|
@@ -83,6 +111,14 @@ export default class ZnDefinedLabel extends ZincElement implements ZincFormContr
|
|
|
83
111
|
this.formControlController.updateValidity();
|
|
84
112
|
}
|
|
85
113
|
|
|
114
|
+
private getFilteredLabels(): PredefinedLabel[] {
|
|
115
|
+
const filter = this.value.toLowerCase();
|
|
116
|
+
return this.predefinedLabels
|
|
117
|
+
.map(label => typeof label === 'string' ? {name: label} : label)
|
|
118
|
+
.filter((label): label is PredefinedLabel => Boolean(label?.name))
|
|
119
|
+
.filter(label => !filter || label.name.toLowerCase().includes(filter));
|
|
120
|
+
}
|
|
121
|
+
|
|
86
122
|
private handleChange() {
|
|
87
123
|
if (!this.dropdown.open) {
|
|
88
124
|
this.dropdown.show().then(r => r);
|
|
@@ -117,14 +153,18 @@ export default class ZnDefinedLabel extends ZincElement implements ZincFormContr
|
|
|
117
153
|
if (target.hasAttribute('data-label')) this.value = target.getAttribute('data-label') ?? "";
|
|
118
154
|
}
|
|
119
155
|
|
|
120
|
-
private
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
156
|
+
private handleFormSubmit(e: Event, label?: string) {
|
|
157
|
+
// Sync from the submitted row so a partially typed filter never wins over
|
|
158
|
+
// the row's actual label key, and stale values from other rows are discarded
|
|
159
|
+
const row = (e.currentTarget as HTMLElement).closest('.defined-label__row');
|
|
160
|
+
const control = row?.querySelector<ZnInput | ZnSelect>('.defined-label__value');
|
|
161
|
+
if (label) {
|
|
162
|
+
this.value = label;
|
|
163
|
+
}
|
|
164
|
+
if (control) {
|
|
165
|
+
this.inputValue = String(control.value ?? '').toLowerCase();
|
|
166
|
+
}
|
|
126
167
|
|
|
127
|
-
private handleFormSubmit() {
|
|
128
168
|
const form = this.formControlController.getForm();
|
|
129
169
|
|
|
130
170
|
if (form && form.reportValidity()) {
|
|
@@ -135,106 +175,89 @@ export default class ZnDefinedLabel extends ZincElement implements ZincFormContr
|
|
|
135
175
|
}
|
|
136
176
|
}
|
|
137
177
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
let selector: TemplateResult<1>;
|
|
155
|
-
if (options && options.length > 0) {
|
|
156
|
-
selector = html`
|
|
157
|
-
<zn-select
|
|
158
|
-
part="input-value"
|
|
159
|
-
id="input-value input-value-${label}"
|
|
160
|
-
class="input__control-value input__control-value--${label}"
|
|
161
|
-
data-label="${label}"
|
|
162
|
-
@zn-change="${this.handleInputValueChange}"
|
|
163
|
-
@zn-input="${this.handleInputValueInput}"
|
|
164
|
-
size="small">
|
|
165
|
-
<zn-option value="">Select ${label}</zn-option>
|
|
166
|
-
${options.map((option: string) => html`
|
|
167
|
-
<zn-option value="${option}">${option}</zn-option>
|
|
168
|
-
`)}
|
|
169
|
-
</zn-select>`;
|
|
170
|
-
} else {
|
|
171
|
-
selector = html`
|
|
172
|
-
<zn-input
|
|
173
|
-
part="input-value"
|
|
174
|
-
id="input-value input-value-${label}"
|
|
175
|
-
class="input__control-value input__control-value--${label}"
|
|
176
|
-
type="text"
|
|
177
|
-
data-label="${label}"
|
|
178
|
-
@zn-change="${this.handleInputValueChange}"
|
|
179
|
-
@zn-input="${this.handleInputValueInput}"
|
|
180
|
-
size="small"></zn-input>`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
predefinedLabels = html`
|
|
184
|
-
<div class="defined-label__input">
|
|
185
|
-
<small>${label}</small>
|
|
186
|
-
<div class="defined-label__input-wrap">
|
|
187
|
-
${selector}
|
|
188
|
-
<zn-button type="submit" icon="add" @click="${this.handleFormSubmit}"></zn-button>
|
|
189
|
-
</div>
|
|
190
|
-
</div>`;
|
|
191
|
-
});
|
|
178
|
+
private renderValueControl(label: PredefinedLabel): TemplateResult {
|
|
179
|
+
if (label.options && label.options.length > 0) {
|
|
180
|
+
return html`
|
|
181
|
+
<zn-select
|
|
182
|
+
part="input-value"
|
|
183
|
+
class="defined-label__value"
|
|
184
|
+
data-label="${label.name}"
|
|
185
|
+
size="small"
|
|
186
|
+
@zn-change="${this.handleInputValueChange}"
|
|
187
|
+
@zn-input="${this.handleInputValueChange}">
|
|
188
|
+
<zn-option value="">Select ${label.name}</zn-option>
|
|
189
|
+
${label.options.map(option => html`
|
|
190
|
+
<zn-option value="${option}">${option}</zn-option>`)}
|
|
191
|
+
</zn-select>`;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
return html`
|
|
195
|
-
<zn-
|
|
195
|
+
<zn-input
|
|
196
|
+
part="input-value"
|
|
197
|
+
class="defined-label__value"
|
|
198
|
+
type="text"
|
|
199
|
+
placeholder="Label Value"
|
|
200
|
+
data-label="${label.name}"
|
|
201
|
+
size="small"
|
|
202
|
+
@zn-change="${this.handleInputValueChange}"
|
|
203
|
+
@zn-input="${this.handleInputValueChange}"></zn-input>`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private renderRow(name: string, control: TemplateResult, label?: string): TemplateResult {
|
|
207
|
+
return html`
|
|
208
|
+
<div class="defined-label__row">
|
|
209
|
+
<small class="defined-label__row-label">${name}</small>
|
|
210
|
+
<div class="defined-label__row-controls">
|
|
211
|
+
${control}
|
|
212
|
+
<zn-button
|
|
213
|
+
type="submit"
|
|
214
|
+
icon="add"
|
|
215
|
+
@click="${(e: Event) => this.handleFormSubmit(e, label)}"></zn-button>
|
|
216
|
+
</div>
|
|
217
|
+
</div>`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
render() {
|
|
221
|
+
const labels = this.getFilteredLabels();
|
|
222
|
+
|
|
223
|
+
return html`
|
|
224
|
+
<zn-dropdown class="defined-label__dropdown" sync="width">
|
|
196
225
|
<zn-input
|
|
197
226
|
part="input"
|
|
198
227
|
id="input"
|
|
199
|
-
class="
|
|
228
|
+
class="defined-label__input"
|
|
200
229
|
type="text"
|
|
201
|
-
title="${this.title}"
|
|
230
|
+
title="${ifDefined(this.title)}"
|
|
202
231
|
value="${this.value}"
|
|
203
232
|
name="${ifDefined(this.name)}"
|
|
204
233
|
placeholder="Add a Label"
|
|
205
234
|
maxlength="60"
|
|
206
235
|
autocomplete="off"
|
|
207
236
|
size="${this.inputSize}"
|
|
237
|
+
?disabled="${this.disabled}"
|
|
208
238
|
@zn-change="${this.handleChange}"
|
|
209
239
|
@zn-input="${this.handleInput}"
|
|
210
240
|
@click="${this.handleClick}"
|
|
211
241
|
slot="trigger"
|
|
212
242
|
></zn-input>
|
|
213
243
|
|
|
214
|
-
<
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
@zn-input="${this.handleInputValueInput}"></zn-input>
|
|
232
|
-
<zn-button type="submit" icon="add" @click="${this.handleFormSubmit}"></zn-button>
|
|
233
|
-
</div>
|
|
234
|
-
</div>` : ''}
|
|
235
|
-
|
|
236
|
-
</zn-sp>
|
|
237
|
-
</zn-panel>
|
|
244
|
+
<div class="defined-label__panel">
|
|
245
|
+
${labels.length > 0
|
|
246
|
+
? labels.map(label => this.renderRow(label.name, this.renderValueControl(label), label.name))
|
|
247
|
+
: html`
|
|
248
|
+
<div class="defined-label__empty">Cannot find any predefined labels</div>`}
|
|
249
|
+
|
|
250
|
+
${this.allowCustom && this.value !== '' ? this.renderRow(this.value, html`
|
|
251
|
+
<zn-input
|
|
252
|
+
part="input-value"
|
|
253
|
+
class="defined-label__value"
|
|
254
|
+
placeholder="Label Value"
|
|
255
|
+
type="text"
|
|
256
|
+
size="small"
|
|
257
|
+
maxlength="60"
|
|
258
|
+
@zn-change="${this.handleInputValueChange}"
|
|
259
|
+
@zn-input="${this.handleInputValueChange}"></zn-input>`) : nothing}
|
|
260
|
+
</div>
|
|
238
261
|
</zn-dropdown>
|
|
239
262
|
`;
|
|
240
263
|
}
|