@kubex/zinc 1.1.160 → 1.1.162
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 +1086 -892
- package/dist/vscode.html-custom-data.json +95 -87
- package/dist/web-types.json +192 -181
- package/dist/zn.d.ts +32 -9
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +367 -330
- package/docs/pages/components/data-table.md +30 -20
- package/package.json +1 -1
- package/scss/themes/_dark.scss +1 -0
- package/scss/themes/_light.scss +1 -0
- package/src/components/button/button.component.ts +4 -0
- package/src/components/button/button.scss +5 -0
- package/src/components/button/button.test.ts +10 -0
- package/src/components/checkbox/checkbox.component.ts +10 -6
- package/src/components/checkbox/checkbox.scss +4 -8
- package/src/components/content-block/content-block.component.ts +2 -3
- package/src/components/data-table/data-table.component.ts +171 -76
- package/src/components/data-table/data-table.scss +16 -60
- package/src/components/dropdown/dropdown.component.ts +6 -3
- package/src/components/flow-builder/modules/flow-node/flow-node.test.ts +2 -3
- package/src/components/flow-builder/modules/flow-step-group/flow-step-group.test.ts +14 -8
- package/src/components/form-actions/form-actions.test.ts +4 -1
- package/src/components/form-group/form-group.component.ts +45 -15
- package/src/components/navbar/navbar.test.ts +3 -2
- package/src/components/page/page.component.ts +1 -1
- package/src/components/page/page.test.ts +16 -16
- package/src/components/panel/panel.scss +23 -0
- package/src/components/popup/popup.component.ts +8 -1
- package/src/components/sp/sp.test.ts +2 -2
- package/src/components/theme-editor/theme-editor.component.ts +3 -2
- package/src/components/theme-editor/theme-editor.test.ts +30 -28
- package/src/components/tooltip/tooltip.component.ts +10 -1
- package/src/components/tooltip/tooltip.scss +5 -0
- package/src/components/translation-group/translation-group.test.ts +0 -2
|
@@ -2,7 +2,7 @@ import {classMap} from "lit/directives/class-map.js";
|
|
|
2
2
|
import {type CSSResultGroup, html, nothing, type PropertyValues, type TemplateResult, unsafeCSS} from 'lit';
|
|
3
3
|
import {HasSlotController} from "../../internal/slot";
|
|
4
4
|
import {ifDefined} from "lit/directives/if-defined.js";
|
|
5
|
-
import {property,
|
|
5
|
+
import {property, state} from 'lit/decorators.js';
|
|
6
6
|
import {ref} from "lit/directives/ref.js";
|
|
7
7
|
import {ResizeController} from '@lit-labs/observers/resize-controller.js';
|
|
8
8
|
import {Task} from "@lit/task";
|
|
@@ -91,6 +91,7 @@ interface Response {
|
|
|
91
91
|
export enum ActionSlots {
|
|
92
92
|
delete = 'delete-action',
|
|
93
93
|
modify = 'modify-action',
|
|
94
|
+
actions = 'actions',
|
|
94
95
|
create = 'create-action',
|
|
95
96
|
filter = 'filter',
|
|
96
97
|
filter_top = 'filter-top',
|
|
@@ -188,8 +189,9 @@ type AllowedInputElement =
|
|
|
188
189
|
* @slot sort - Slot for sort component, in the header's right-hand group.
|
|
189
190
|
* @slot filter - Slot for filter component, in the header's right-hand group.
|
|
190
191
|
* @slot filter-top - Slot for a top-level filter component, rendered above the table's panel.
|
|
191
|
-
* @slot delete-action - Slot for delete action button, shown
|
|
192
|
-
* @slot modify-action - Slot for modify action button, shown
|
|
192
|
+
* @slot delete-action - Slot for delete action button, shown in the header once rows are selected.
|
|
193
|
+
* @slot modify-action - Slot for modify action button, shown in the header once rows are selected.
|
|
194
|
+
* @slot actions - `zn-button` or `zn-confirm` elements listed in a header menu, enabled once rows are selected.
|
|
193
195
|
* @slot create-action - Slot for create action button, at the end of the header's right-hand group.
|
|
194
196
|
* @slot inputs - Slot for additional input controls.
|
|
195
197
|
* @slot empty-state - Slot for custom empty state.
|
|
@@ -261,9 +263,6 @@ export default class ZnDataTable extends ZincElement {
|
|
|
261
263
|
|
|
262
264
|
@property({attribute: "empty-state-icon"}) emptyStateIcon: string = "data_alert";
|
|
263
265
|
|
|
264
|
-
// Hide the checkbox column
|
|
265
|
-
@property({attribute: 'hide-checkboxes', type: Boolean}) hideCheckboxes: boolean;
|
|
266
|
-
|
|
267
266
|
@property() filters: [] = [];
|
|
268
267
|
|
|
269
268
|
@property() method: 'GET' | 'POST' = 'POST';
|
|
@@ -288,7 +287,6 @@ export default class ZnDataTable extends ZincElement {
|
|
|
288
287
|
@property() groups = '';
|
|
289
288
|
|
|
290
289
|
@property({attribute: 'per-page-size', type: Number}) itemsPerPage: number = DEFAULT_PER_PAGE;
|
|
291
|
-
@query('#select-all-rows') selectAllButton: ZnButton;
|
|
292
290
|
|
|
293
291
|
// Data Table Properties
|
|
294
292
|
private _initialLoad = true;
|
|
@@ -329,6 +327,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
329
327
|
ActionSlots.search.valueOf(),
|
|
330
328
|
ActionSlots.delete.valueOf(),
|
|
331
329
|
ActionSlots.modify.valueOf(),
|
|
330
|
+
ActionSlots.actions.valueOf(),
|
|
332
331
|
ActionSlots.create.valueOf(),
|
|
333
332
|
ActionSlots.filter.valueOf(),
|
|
334
333
|
ActionSlots.sort.valueOf(),
|
|
@@ -673,8 +672,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
673
672
|
return html`
|
|
674
673
|
<div>${this.loadingTable()}</div>`;
|
|
675
674
|
}
|
|
676
|
-
return
|
|
677
|
-
<div class="reduced-opacity">${this._lastTableContent}</div>`;
|
|
675
|
+
return this.tableContent(this._lastTableContent, true);
|
|
678
676
|
},
|
|
679
677
|
complete: (data) => {
|
|
680
678
|
this._initialLoad = false;
|
|
@@ -683,7 +681,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
683
681
|
emptyBody = !this._lastLoadHadRows;
|
|
684
682
|
this._lastTableContent = html`
|
|
685
683
|
<div>${this.renderTable(data as Response)}</div>`;
|
|
686
|
-
return this._lastTableContent;
|
|
684
|
+
return this.tableContent(this._lastTableContent, false);
|
|
687
685
|
},
|
|
688
686
|
error: (error) => {
|
|
689
687
|
if (error instanceof Error) {
|
|
@@ -719,6 +717,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
719
717
|
|
|
720
718
|
const hasActions = this.hasSlotController.test(ActionSlots.delete.valueOf())
|
|
721
719
|
|| this.hasSlotController.test(ActionSlots.modify.valueOf())
|
|
720
|
+
|| this.hasSlotController.test(ActionSlots.actions.valueOf())
|
|
722
721
|
|| this.hasSlotController.test(ActionSlots.create.valueOf())
|
|
723
722
|
|| this.hasSlotController.test(ActionSlots.sort.valueOf())
|
|
724
723
|
|| this.hasSlotController.test(ActionSlots.search.valueOf());
|
|
@@ -902,11 +901,14 @@ export default class ZnDataTable extends ZincElement {
|
|
|
902
901
|
const error = data?.error?.text ? data.error : undefined;
|
|
903
902
|
|
|
904
903
|
if (!data?.rows || data.rows.length === 0) {
|
|
904
|
+
this._rows = [];
|
|
905
|
+
this.pruneSelectedRows();
|
|
905
906
|
return html`${error ? html`
|
|
906
907
|
<div class="table__error">${this.renderErrorAlert(error)}</div>` : nothing}${this.emptyState()}`;
|
|
907
908
|
}
|
|
908
909
|
|
|
909
910
|
this._rows = this.getRows(data);
|
|
911
|
+
this.pruneSelectedRows();
|
|
910
912
|
|
|
911
913
|
if (this.groupBy) {
|
|
912
914
|
// we need to group the rows by the group column
|
|
@@ -1029,16 +1031,18 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1029
1031
|
});
|
|
1030
1032
|
|
|
1031
1033
|
const anyHidden = this.hasHiddenColumns();
|
|
1032
|
-
const
|
|
1034
|
+
const showCheckboxes = this.isSelectable();
|
|
1035
|
+
const colCount = filteredHeaders.length + (this.rowHasActions ? 1 : 0) + (anyHidden ? 1 : 0) + (showCheckboxes ? 1 : 0);
|
|
1033
1036
|
|
|
1034
1037
|
return html`
|
|
1035
1038
|
<div class="table__scroll">
|
|
1036
1039
|
<table class="${classMap({
|
|
1037
1040
|
'table': true,
|
|
1038
|
-
'with-hover': !this.unsortable &&
|
|
1041
|
+
'with-hover': !this.unsortable && this.isSelectable(),
|
|
1039
1042
|
})}">
|
|
1040
1043
|
<thead>
|
|
1041
1044
|
<tr>
|
|
1045
|
+
${showCheckboxes ? this.renderSelectAllCheckbox() : nothing}
|
|
1042
1046
|
${anyHidden ? html`
|
|
1043
1047
|
<th class="table__head table__head--expander"></th>` : nothing}
|
|
1044
1048
|
${filteredHeaders.map((header: HeaderConfig) => this.renderCellHeader(header))}
|
|
@@ -1057,10 +1061,8 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1057
1061
|
<th colspan="${colCount}" scope="colgroup">${group.label}</th>
|
|
1058
1062
|
</tr>` : nothing}
|
|
1059
1063
|
${group.rows.map((row: Row) => html`
|
|
1060
|
-
<tr class="${
|
|
1061
|
-
|
|
1062
|
-
'table__row--data': true,
|
|
1063
|
-
})}" data-row-id="${row.id}">
|
|
1064
|
+
<tr class="table__row--data" data-row-id="${row.id}">
|
|
1065
|
+
${showCheckboxes ? this.renderRowCheckbox(row) : nothing}
|
|
1064
1066
|
${anyHidden ? this.renderExpanderCell(row) : nothing}
|
|
1065
1067
|
${(visibleRowCells.get(row.id) || row.cells).map((value: Cell, index: number) => this.renderCellBody(index, value, row))}
|
|
1066
1068
|
${this.rowHasActions ? this.renderActions(row) : nothing}
|
|
@@ -1074,15 +1076,21 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1074
1076
|
`;
|
|
1075
1077
|
}
|
|
1076
1078
|
|
|
1079
|
+
// Pending and loaded states share this template so Lit keeps the table's DOM (and focus) across loads
|
|
1080
|
+
private tableContent(content: TemplateResult, pending: boolean) {
|
|
1081
|
+
return html`
|
|
1082
|
+
<div class="${classMap({'reduced-opacity': pending})}">${content}</div>`;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1077
1085
|
getTableHeader() {
|
|
1078
1086
|
return html`
|
|
1079
1087
|
<div class="table__header">
|
|
1080
1088
|
<div class="table__header__actions">
|
|
1081
1089
|
${this.caption ? html`
|
|
1082
1090
|
<h3 class="table__header__caption">${this.caption}</h3>` : nothing}
|
|
1083
|
-
${this.getActions()}
|
|
1084
1091
|
</div>
|
|
1085
1092
|
<div class="table__header__right">
|
|
1093
|
+
${this.getActions()}
|
|
1086
1094
|
${this.getHeaderControls()}
|
|
1087
1095
|
</div>
|
|
1088
1096
|
</div>
|
|
@@ -1302,37 +1310,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1302
1310
|
getActions() {
|
|
1303
1311
|
const actions = [];
|
|
1304
1312
|
|
|
1305
|
-
// Select-all only earns its place alongside the bulk actions it feeds
|
|
1306
|
-
const hasBulkActions = this.hasSlotController.test(ActionSlots.delete.valueOf())
|
|
1307
|
-
|| this.hasSlotController.test(ActionSlots.modify.valueOf());
|
|
1308
|
-
|
|
1309
|
-
if (!hasBulkActions) {
|
|
1310
|
-
return [];
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
if (!this.hideCheckboxes && this._rows.length > 0) {
|
|
1314
|
-
actions.push(html`
|
|
1315
|
-
<zn-button @click="${this.selectAll}"
|
|
1316
|
-
id="select-all-rows"
|
|
1317
|
-
color="transparent"
|
|
1318
|
-
icon="indeterminate_check_box"
|
|
1319
|
-
icon-size="22"
|
|
1320
|
-
icon-color="primary"
|
|
1321
|
-
tooltip="Select All"
|
|
1322
|
-
slot="trigger">
|
|
1323
|
-
</zn-button>`);
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
1313
|
if (this.selectedRows.length > 0) {
|
|
1327
|
-
actions.push(html`
|
|
1328
|
-
<zn-button @click="${this.clearSelectedRows}"
|
|
1329
|
-
color="transparent"
|
|
1330
|
-
icon="disabled_by_default"
|
|
1331
|
-
icon-size="22"
|
|
1332
|
-
tooltip="Clear Selection"
|
|
1333
|
-
slot="trigger">
|
|
1334
|
-
</zn-button>`);
|
|
1335
|
-
|
|
1336
1314
|
if (this.hasSlotController.test(ActionSlots.delete.valueOf())) {
|
|
1337
1315
|
actions.push(html`
|
|
1338
1316
|
<slot name="${ActionSlots.delete.valueOf()}"></slot>`);
|
|
@@ -1344,9 +1322,72 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1344
1322
|
}
|
|
1345
1323
|
}
|
|
1346
1324
|
|
|
1325
|
+
if (this.hasSlotController.test(ActionSlots.actions.valueOf())) {
|
|
1326
|
+
actions.push(this.getActionsMenu());
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1347
1329
|
return actions;
|
|
1348
1330
|
}
|
|
1349
1331
|
|
|
1332
|
+
private getActionsMenu() {
|
|
1333
|
+
const items = [...this.hasSlotController.getSlots(ActionSlots.actions.valueOf())];
|
|
1334
|
+
const disabled = this.selectedRows.length === 0;
|
|
1335
|
+
|
|
1336
|
+
return html`
|
|
1337
|
+
<zn-dropdown placement="bottom-end" ?disabled="${disabled}">
|
|
1338
|
+
<zn-button slot="trigger"
|
|
1339
|
+
icon-button="small"
|
|
1340
|
+
icon="ellipsis@lu"
|
|
1341
|
+
icon-size="18"
|
|
1342
|
+
tooltip="Actions"
|
|
1343
|
+
aria-label="Selection actions"
|
|
1344
|
+
?disabled="${disabled}">
|
|
1345
|
+
</zn-button>
|
|
1346
|
+
<zn-menu variant="shell">
|
|
1347
|
+
${items.map((item: HTMLElement) => {
|
|
1348
|
+
const trigger = this.actionTrigger(item);
|
|
1349
|
+
const icon = trigger?.getAttribute('icon') || item.getAttribute('icon');
|
|
1350
|
+
return html`
|
|
1351
|
+
<zn-menu-item color="${ifDefined(trigger?.getAttribute('color') || undefined)}"
|
|
1352
|
+
?disabled="${trigger?.hasAttribute('disabled') ?? false}"
|
|
1353
|
+
@click="${() => this.runAction(item)}">
|
|
1354
|
+
${icon ? html`
|
|
1355
|
+
<zn-icon src="${icon}" size="20" slot="prefix"></zn-icon>` : nothing}
|
|
1356
|
+
${this.actionLabel(item, trigger)}
|
|
1357
|
+
</zn-menu-item>`;
|
|
1358
|
+
})}
|
|
1359
|
+
</zn-menu>
|
|
1360
|
+
</zn-dropdown>
|
|
1361
|
+
<div class="table__actions-source">
|
|
1362
|
+
<slot name="${ActionSlots.actions.valueOf()}"></slot>
|
|
1363
|
+
</div>`;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
private actionTrigger(item: HTMLElement): HTMLElement | null {
|
|
1367
|
+
if (item instanceof ZnConfirm) {
|
|
1368
|
+
return item.querySelector<HTMLElement>(':scope > [slot="trigger"]');
|
|
1369
|
+
}
|
|
1370
|
+
return item;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
private actionLabel(item: HTMLElement, trigger: HTMLElement | null): string {
|
|
1374
|
+
return trigger?.textContent?.trim()
|
|
1375
|
+
|| trigger?.getAttribute('content')
|
|
1376
|
+
|| trigger?.getAttribute('tooltip')
|
|
1377
|
+
|| item.getAttribute('caption')
|
|
1378
|
+
|| '';
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
private runAction(item: HTMLElement) {
|
|
1382
|
+
if (item instanceof ZnConfirm) {
|
|
1383
|
+
item.show();
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
// ZnButton.click() skips the native click, so listeners and href would never fire
|
|
1387
|
+
const target = item instanceof ZnButton ? item.button : item;
|
|
1388
|
+
target?.click();
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1350
1391
|
goToPage(page: number) {
|
|
1351
1392
|
this.page = page;
|
|
1352
1393
|
this.selectedRows = [];
|
|
@@ -1380,12 +1421,12 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1380
1421
|
this._dataTask.run().then(r => r);
|
|
1381
1422
|
}
|
|
1382
1423
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
if (
|
|
1424
|
+
/** Selects every row on the page, or clears the selection when every row is already selected. */
|
|
1425
|
+
selectAll() {
|
|
1426
|
+
if (!this.isSelectable()) return;
|
|
1386
1427
|
|
|
1387
1428
|
if (this.numberOfRowsSelected === this._rows.length) {
|
|
1388
|
-
this.clearSelectedRows(
|
|
1429
|
+
this.clearSelectedRows();
|
|
1389
1430
|
return;
|
|
1390
1431
|
}
|
|
1391
1432
|
|
|
@@ -1396,7 +1437,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1396
1437
|
}
|
|
1397
1438
|
|
|
1398
1439
|
selectRow(e: Event) {
|
|
1399
|
-
if (this.
|
|
1440
|
+
if (!this.isSelectable()) {
|
|
1400
1441
|
return;
|
|
1401
1442
|
}
|
|
1402
1443
|
|
|
@@ -1420,13 +1461,14 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1420
1461
|
return;
|
|
1421
1462
|
}
|
|
1422
1463
|
|
|
1423
|
-
const
|
|
1424
|
-
const
|
|
1425
|
-
if (index === -1) return;
|
|
1426
|
-
|
|
1427
|
-
const row = this._rows[index] as Row;
|
|
1464
|
+
const rowId = (parent as HTMLElement).dataset.rowId;
|
|
1465
|
+
const row = this._rows.find((r: Row) => r.id === rowId);
|
|
1428
1466
|
if (!row) return;
|
|
1429
1467
|
|
|
1468
|
+
this.toggleRowSelection(row);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
private toggleRowSelection(row: Row) {
|
|
1430
1472
|
const alreadySelected = this.selectedRows.some((r: Row) => r.id === row.id);
|
|
1431
1473
|
if (alreadySelected) {
|
|
1432
1474
|
this.selectedRows = this.selectedRows.filter((r: Row) => r.id !== row.id);
|
|
@@ -1439,10 +1481,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1439
1481
|
this.requestUpdate();
|
|
1440
1482
|
}
|
|
1441
1483
|
|
|
1442
|
-
clearSelectedRows(
|
|
1443
|
-
const button = event.target as ZnButton;
|
|
1444
|
-
if (button.disabled) return;
|
|
1445
|
-
|
|
1484
|
+
clearSelectedRows() {
|
|
1446
1485
|
this.selectedRows = [];
|
|
1447
1486
|
this.numberOfRowsSelected = 0;
|
|
1448
1487
|
this.updateKeys();
|
|
@@ -1642,7 +1681,10 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1642
1681
|
'table__head--last': header.key === lastVisibleHeaderKey,
|
|
1643
1682
|
'table__head--hidden': Object.values(this.hiddenHeaders).includes(header.key),
|
|
1644
1683
|
})}"
|
|
1645
|
-
|
|
1684
|
+
tabindex="${ifDefined(sortable ? '0' : undefined)}"
|
|
1685
|
+
aria-sort="${ifDefined(sortable ? this.sortState(header.key) : undefined)}"
|
|
1686
|
+
@click="${sortable ? this.updateSort(header.key) : undefined}"
|
|
1687
|
+
@keydown="${sortable ? (e: KeyboardEvent) => this.handleSortKeyDown(e, header.key) : undefined}">
|
|
1646
1688
|
<div>
|
|
1647
1689
|
${header.label}
|
|
1648
1690
|
${sortable ? this.getTableSortIcon(header.key) : nothing}
|
|
@@ -1651,6 +1693,17 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1651
1693
|
`;
|
|
1652
1694
|
}
|
|
1653
1695
|
|
|
1696
|
+
private sortState(key: string): 'ascending' | 'descending' | 'none' {
|
|
1697
|
+
if (this.sortColumn !== key) return 'none';
|
|
1698
|
+
return this.sortDirection === 'asc' ? 'ascending' : 'descending';
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
private handleSortKeyDown(e: KeyboardEvent, key: string) {
|
|
1702
|
+
if (e.key !== 'Enter' && e.key !== ' ') return;
|
|
1703
|
+
e.preventDefault();
|
|
1704
|
+
this.updateSort(key)();
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1654
1707
|
private renderCellBody(index: number, value: Cell, row: Row) {
|
|
1655
1708
|
const filteredHeaders = this.visibleHeaders();
|
|
1656
1709
|
const header: HeaderConfig | undefined = filteredHeaders[index];
|
|
@@ -1658,7 +1711,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1658
1711
|
|
|
1659
1712
|
return html`
|
|
1660
1713
|
<td
|
|
1661
|
-
@click="${this.
|
|
1714
|
+
@click="${this.isSelectable() ? this.selectRow : undefined}"
|
|
1662
1715
|
class="${classMap({
|
|
1663
1716
|
'table__cell': true,
|
|
1664
1717
|
'table__cell--wide': headerKey === this.wideColumn,
|
|
@@ -1731,6 +1784,60 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1731
1784
|
return this.selectedRows.some((r: Row) => r.id === row.id);
|
|
1732
1785
|
}
|
|
1733
1786
|
|
|
1787
|
+
// Drop selections for rows no longer in the result set (filtered out, archived, paged away)
|
|
1788
|
+
// and swap survivors for the fresh row objects so selected keys reflect current cell data.
|
|
1789
|
+
private pruneSelectedRows() {
|
|
1790
|
+
if (this.selectedRows.length === 0) return;
|
|
1791
|
+
|
|
1792
|
+
const current = new Map(this._rows.map((row: Row) => [row.id, row]));
|
|
1793
|
+
const kept = this.selectedRows
|
|
1794
|
+
.map((row: Row) => current.get(row.id))
|
|
1795
|
+
.filter((row): row is Row => row !== undefined);
|
|
1796
|
+
|
|
1797
|
+
const changed = kept.length !== this.selectedRows.length;
|
|
1798
|
+
this.selectedRows = kept;
|
|
1799
|
+
this.numberOfRowsSelected = kept.length;
|
|
1800
|
+
if (changed) this.updateKeys();
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
private isSelectable(): boolean {
|
|
1804
|
+
return [ActionSlots.delete, ActionSlots.modify, ActionSlots.actions, ActionSlots.create]
|
|
1805
|
+
.some(slot => this.hasSlotController.test(slot.valueOf()));
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
private renderSelectAllCheckbox() {
|
|
1809
|
+
const allSelected = this.numberOfRowsSelected === this._rows.length;
|
|
1810
|
+
const someSelected = this.numberOfRowsSelected > 0 && !allSelected;
|
|
1811
|
+
return html`
|
|
1812
|
+
<th class="table__head table__head--checkbox">
|
|
1813
|
+
<div>
|
|
1814
|
+
<zn-checkbox aria-label="Select all rows"
|
|
1815
|
+
.checked="${allSelected}"
|
|
1816
|
+
.indeterminate="${someSelected}"
|
|
1817
|
+
@zn-change="${() => this.selectAll()}">
|
|
1818
|
+
</zn-checkbox>
|
|
1819
|
+
</div>
|
|
1820
|
+
</th>`;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
private renderRowCheckbox(row: Row) {
|
|
1824
|
+
return html`
|
|
1825
|
+
<td class="table__cell table__cell--checkbox" @click="${(e: Event) => this.handleCheckboxCellClick(e, row)}">
|
|
1826
|
+
<div>
|
|
1827
|
+
<zn-checkbox aria-label="Select row"
|
|
1828
|
+
.checked="${this.isRowSelected(row)}"
|
|
1829
|
+
@zn-change="${() => this.toggleRowSelection(row)}">
|
|
1830
|
+
</zn-checkbox>
|
|
1831
|
+
</div>
|
|
1832
|
+
</td>`;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
// A click on the checkbox itself arrives twice (label, then input), so leave it to zn-change
|
|
1836
|
+
private handleCheckboxCellClick(e: Event, row: Row) {
|
|
1837
|
+
if ((e.target as Element).closest('zn-checkbox')) return;
|
|
1838
|
+
this.toggleRowSelection(row);
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1734
1841
|
private getRows(data: Response): Row[] {
|
|
1735
1842
|
// Copy rows to avoid mutating original data
|
|
1736
1843
|
const sourceRows = Array.isArray(data.rows) ? data.rows.slice() : [];
|
|
@@ -1761,21 +1868,9 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1761
1868
|
}
|
|
1762
1869
|
|
|
1763
1870
|
private updateKeys() {
|
|
1764
|
-
this.updateSelectAll();
|
|
1765
1871
|
this.updateModifyKeys();
|
|
1766
1872
|
this.updateDeleteKeys();
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
private updateSelectAll() {
|
|
1770
|
-
if (!this.selectAllButton) {
|
|
1771
|
-
return;
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
if (this.numberOfRowsSelected === this._rows.length) {
|
|
1775
|
-
this.selectAllButton.icon = 'check_box';
|
|
1776
|
-
} else {
|
|
1777
|
-
this.selectAllButton.icon = 'indeterminate_check_box';
|
|
1778
|
-
}
|
|
1873
|
+
this.updateActionKeys(ActionSlots.actions.valueOf());
|
|
1779
1874
|
}
|
|
1780
1875
|
|
|
1781
1876
|
private updateModifyKeys() {
|
|
@@ -44,7 +44,7 @@ table {
|
|
|
44
44
|
border-bottom: 1px solid rgb(var(--zn-border-color)) !important;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
&.with-hover tbody tr.table__row--data:
|
|
47
|
+
&.with-hover tbody tr.table__row--data:hover td {
|
|
48
48
|
background-color: rgba(var(--zn-primary), 0.05) !important;
|
|
49
49
|
cursor: pointer;
|
|
50
50
|
}
|
|
@@ -414,68 +414,24 @@ zn-menu::part(base) {
|
|
|
414
414
|
background-color: rgb(var(--zn-panel, 255, 255, 255));
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
&:hover {
|
|
422
|
-
background-color: var(--zn-color-purple-200) !important;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
td {
|
|
426
|
-
border-top: 1px solid var(--zn-color-purple-600) !important;
|
|
427
|
-
border-bottom: 1px solid var(--zn-color-purple-600) !important;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
td:first-of-type {
|
|
431
|
-
border-left: 1px solid var(--zn-color-purple-600);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
td:last-of-type {
|
|
435
|
-
border-right: 1px solid var(--zn-color-purple-600);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
&:last-of-type td:first-of-type {
|
|
439
|
-
border-bottom-left-radius: var(--zn-border-radius);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
&:last-of-type td:last-of-type {
|
|
443
|
-
border-bottom-right-radius: var(--zn-border-radius);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
& + tr.table__row--selected td {
|
|
447
|
-
border-top: 0 !important;
|
|
448
|
-
|
|
449
|
-
&:first-of-type {
|
|
450
|
-
border-top-left-radius: 0;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
&:last-of-type {
|
|
454
|
-
border-top-right-radius: 0;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
&:has(+ tr.table__row--selected) td {
|
|
459
|
-
border-bottom: 0 !important;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
&:first-of-type td {
|
|
463
|
-
border-top: 0 !important;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
&:has(+ tr.table__row--selected) {
|
|
467
|
-
td:first-of-type {
|
|
468
|
-
border-bottom-left-radius: 0;
|
|
469
|
-
}
|
|
417
|
+
.table tr .table__head[tabindex]:focus-visible {
|
|
418
|
+
outline: var(--zn-focus-ring);
|
|
419
|
+
outline-offset: calc(-1 * var(--zn-focus-ring-width));
|
|
420
|
+
}
|
|
470
421
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
422
|
+
.table tr .table__head--checkbox,
|
|
423
|
+
.table tr .table__cell--checkbox {
|
|
424
|
+
width: 1px;
|
|
425
|
+
min-width: 0;
|
|
426
|
+
padding-right: 0;
|
|
475
427
|
}
|
|
476
428
|
|
|
477
|
-
|
|
478
|
-
|
|
429
|
+
// Visually hidden rather than display: none, which would stop a slotted zn-confirm's dialog rendering
|
|
430
|
+
.table__actions-source {
|
|
431
|
+
position: absolute;
|
|
432
|
+
width: 0;
|
|
433
|
+
height: 0;
|
|
434
|
+
overflow: hidden;
|
|
479
435
|
}
|
|
480
436
|
|
|
481
437
|
tbody tr.table__row--group th {
|
|
@@ -182,7 +182,7 @@ export default class ZnDropdown extends ZincElement {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
private async handleTriggerKeyDown(event: KeyboardEvent) {
|
|
185
|
-
if ([' ', 'Enter'
|
|
185
|
+
if ([' ', 'Enter'].includes(event.key)) {
|
|
186
186
|
event.preventDefault();
|
|
187
187
|
await this.handleTriggerClick();
|
|
188
188
|
return;
|
|
@@ -252,9 +252,12 @@ export default class ZnDropdown extends ZincElement {
|
|
|
252
252
|
|
|
253
253
|
if (event.key === 'Tab') {
|
|
254
254
|
if (this.open) {
|
|
255
|
-
event.preventDefault();
|
|
256
255
|
this.hide().then(r => r);
|
|
257
|
-
|
|
256
|
+
// Tabbing out of the menu resumes from the trigger; from the trigger, Tab moves on as usual
|
|
257
|
+
if (this.panel?.assignedElements({flatten: true}).some(el => el.matches(':focus-within'))) {
|
|
258
|
+
event.preventDefault();
|
|
259
|
+
this.focusOnTrigger();
|
|
260
|
+
}
|
|
258
261
|
return;
|
|
259
262
|
}
|
|
260
263
|
|
|
@@ -19,7 +19,7 @@ describe('<zn-flow-node>', () => {
|
|
|
19
19
|
expect(el).to.exist;
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
it('should render the type label and
|
|
22
|
+
it('should render the type label and a single output stem', async () => {
|
|
23
23
|
const el = await fixture<ZnFlowNode>(html`
|
|
24
24
|
<zn-flow-node></zn-flow-node>`);
|
|
25
25
|
el.node = node;
|
|
@@ -27,8 +27,7 @@ describe('<zn-flow-node>', () => {
|
|
|
27
27
|
await el.updateComplete;
|
|
28
28
|
|
|
29
29
|
expect(el.shadowRoot?.querySelector('.title')?.textContent).to.contain('Conditional Split');
|
|
30
|
-
expect(el.shadowRoot?.querySelectorAll('.port--out')?.length).to.equal(
|
|
31
|
-
expect(el.shadowRoot?.querySelector('.port-label')?.textContent).to.contain('TRUE');
|
|
30
|
+
expect(el.shadowRoot?.querySelectorAll('.port--out')?.length).to.equal(1);
|
|
32
31
|
});
|
|
33
32
|
|
|
34
33
|
it('should emit flow-node-grab when the card body is pressed', async () => {
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import '../../../../../dist/zn.min.js';
|
|
2
2
|
import {expect, fixture, html} from '@open-wc/testing';
|
|
3
|
+
import type ZnCollapsible from '../../../collapsible/collapsible.component';
|
|
3
4
|
import type ZnFlowStepGroup from './flow-step-group.component';
|
|
4
5
|
|
|
6
|
+
async function collapsibleOf(el: ZnFlowStepGroup): Promise<ZnCollapsible> {
|
|
7
|
+
const collapsible = el.shadowRoot!.querySelector<ZnCollapsible>('zn-collapsible')!;
|
|
8
|
+
await collapsible.updateComplete;
|
|
9
|
+
return collapsible;
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
describe('<zn-flow-step-group>', () => {
|
|
6
|
-
it('should render a
|
|
13
|
+
it('should render a collapsible with its caption', async () => {
|
|
7
14
|
const el = await fixture<ZnFlowStepGroup>(html`
|
|
8
15
|
<zn-flow-step-group caption="Contacts"></zn-flow-step-group>`);
|
|
9
|
-
|
|
16
|
+
const collapsible = await collapsibleOf(el);
|
|
17
|
+
expect(collapsible.caption).to.equal('Contacts');
|
|
18
|
+
expect(collapsible.expanded).to.equal(true);
|
|
10
19
|
});
|
|
11
20
|
|
|
12
|
-
it('should
|
|
21
|
+
it('should start closed when collapsed', async () => {
|
|
13
22
|
const el = await fixture<ZnFlowStepGroup>(html`
|
|
14
|
-
<zn-flow-step-group caption="Contacts"></zn-flow-step-group>`);
|
|
15
|
-
|
|
16
|
-
header.click();
|
|
17
|
-
await el.updateComplete;
|
|
18
|
-
expect(el.collapsed).to.equal(true);
|
|
23
|
+
<zn-flow-step-group caption="Contacts" collapsed></zn-flow-step-group>`);
|
|
24
|
+
expect((await collapsibleOf(el)).expanded).to.equal(false);
|
|
19
25
|
});
|
|
20
26
|
});
|
|
@@ -5,6 +5,9 @@ import { expect, fixture, html, oneEvent } from '@open-wc/testing';
|
|
|
5
5
|
const clickButton = (button: HTMLElement) =>
|
|
6
6
|
button.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true, cancelable: true }));
|
|
7
7
|
|
|
8
|
+
// The test harness loads no theme stylesheet, so a layout test stamps on the spacing token a themed page provides.
|
|
9
|
+
const spacingTokens = '--zn-spacing-small: 16px;';
|
|
10
|
+
|
|
8
11
|
describe('<zn-form-actions>', () => {
|
|
9
12
|
it('should render a component', async () => {
|
|
10
13
|
const el = await fixture(html` <zn-form-actions></zn-form-actions> `);
|
|
@@ -24,7 +27,7 @@ describe('<zn-form-actions>', () => {
|
|
|
24
27
|
|
|
25
28
|
it('holds align="start" children left and the rest right, one gap apart on each side', async () => {
|
|
26
29
|
const el = await fixture<HTMLElement>(html`
|
|
27
|
-
<zn-form-actions with-cancel style="width: 600px">
|
|
30
|
+
<zn-form-actions with-cancel style="width: 600px; ${spacingTokens}">
|
|
28
31
|
<zn-button align="start" id="translate">Translate</zn-button>
|
|
29
32
|
<zn-button align="start" id="slug">Slug</zn-button>
|
|
30
33
|
</zn-form-actions>
|