@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.149 → 2.0.0-next.150
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/bundle/openbridge-webcomponents.bundle.js +4443 -3841
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +381 -2
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.css.js +94 -0
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.css.js.map +1 -0
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.d.ts +72 -0
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.d.ts.map +1 -0
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.js +396 -0
- package/dist/components/alert-list-details-experimental/alert-list-details-experimental.js.map +1 -0
- package/dist/components/table/table.css.js +43 -0
- package/dist/components/table/table.css.js.map +1 -1
- package/dist/components/table/table.d.ts +31 -1
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +124 -11
- package/dist/components/table/table.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/components/alert-list-details-experimental/alert-list-details-experimental.css +70 -0
- package/src/components/alert-list-details-experimental/alert-list-details-experimental.stories.ts +510 -0
- package/src/components/alert-list-details-experimental/alert-list-details-experimental.ts +457 -0
- package/src/components/table/table.css +42 -0
- package/src/components/table/table.stories.ts +279 -0
- package/src/components/table/table.ts +177 -13
- package/src/types.ts +6 -4
|
@@ -2,6 +2,7 @@ import type {Meta, StoryObj} from '@storybook/web-components-vite';
|
|
|
2
2
|
import {
|
|
3
3
|
ObcTable,
|
|
4
4
|
ObcTableCellData,
|
|
5
|
+
ObcTableExpandToggleEvent,
|
|
5
6
|
ObcTableRow,
|
|
6
7
|
ObcTableCellType,
|
|
7
8
|
} from './table.js';
|
|
@@ -689,3 +690,281 @@ export const Interactive: Story = {
|
|
|
689
690
|
`;
|
|
690
691
|
},
|
|
691
692
|
};
|
|
693
|
+
|
|
694
|
+
interface HierarchyNode {
|
|
695
|
+
id: string;
|
|
696
|
+
system: string;
|
|
697
|
+
location: string;
|
|
698
|
+
state: string;
|
|
699
|
+
members?: HierarchyNode[];
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
const systemTree: HierarchyNode[] = [
|
|
703
|
+
{
|
|
704
|
+
id: 'propulsion',
|
|
705
|
+
system: 'Propulsion',
|
|
706
|
+
location: 'Engine room',
|
|
707
|
+
state: 'Running',
|
|
708
|
+
members: [
|
|
709
|
+
{
|
|
710
|
+
id: 'main-engine',
|
|
711
|
+
system: 'Main engine',
|
|
712
|
+
location: 'Engine room',
|
|
713
|
+
state: 'Running',
|
|
714
|
+
members: [
|
|
715
|
+
{
|
|
716
|
+
id: 'lube-oil',
|
|
717
|
+
system: 'Lube oil',
|
|
718
|
+
location: 'Engine room',
|
|
719
|
+
state: 'Nominal',
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
id: 'cooling-water',
|
|
723
|
+
system: 'Cooling water',
|
|
724
|
+
location: 'Engine room',
|
|
725
|
+
state: 'Nominal',
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
id: 'shaft-generator',
|
|
731
|
+
system: 'Shaft generator',
|
|
732
|
+
location: 'Engine room',
|
|
733
|
+
state: 'Standby',
|
|
734
|
+
},
|
|
735
|
+
],
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
id: 'power',
|
|
739
|
+
system: 'Power',
|
|
740
|
+
location: 'Switchboard',
|
|
741
|
+
state: 'Running',
|
|
742
|
+
members: [
|
|
743
|
+
{
|
|
744
|
+
id: 'aux-engine-1',
|
|
745
|
+
system: 'Aux engine 1',
|
|
746
|
+
location: 'Switchboard',
|
|
747
|
+
state: 'Running',
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
id: 'aux-engine-2',
|
|
751
|
+
system: 'Aux engine 2',
|
|
752
|
+
location: 'Switchboard',
|
|
753
|
+
state: 'Stopped',
|
|
754
|
+
},
|
|
755
|
+
],
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
id: 'ballast',
|
|
759
|
+
system: 'Ballast',
|
|
760
|
+
location: 'Pump room',
|
|
761
|
+
state: 'Idle',
|
|
762
|
+
},
|
|
763
|
+
];
|
|
764
|
+
|
|
765
|
+
const collapsedRowIds = new Set<string>(['power']);
|
|
766
|
+
|
|
767
|
+
const hierarchyRows = (
|
|
768
|
+
nodes: HierarchyNode[],
|
|
769
|
+
level = 0,
|
|
770
|
+
parentId?: string
|
|
771
|
+
): ObcTableRow[] => {
|
|
772
|
+
const rows: ObcTableRow[] = [];
|
|
773
|
+
for (const node of nodes) {
|
|
774
|
+
const expanded = !collapsedRowIds.has(node.id);
|
|
775
|
+
rows.push({
|
|
776
|
+
id: node.id,
|
|
777
|
+
parentId,
|
|
778
|
+
level,
|
|
779
|
+
expandable: (node.members?.length ?? 0) > 0,
|
|
780
|
+
expanded,
|
|
781
|
+
system: {type: ObcTableCellType.Regular, text: node.system},
|
|
782
|
+
location: {type: ObcTableCellType.Regular, text: node.location},
|
|
783
|
+
state: {type: ObcTableCellType.Regular, text: node.state, neutral: true},
|
|
784
|
+
});
|
|
785
|
+
if (expanded && node.members) {
|
|
786
|
+
rows.push(...hierarchyRows(node.members, level + 1, node.id));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return rows;
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
const compareCellText = (a: ObcTableCellData, b: ObcTableCellData) => {
|
|
793
|
+
const text = (value: ObcTableCellData) =>
|
|
794
|
+
value?.type === ObcTableCellType.Regular ? String(value.text ?? '') : '';
|
|
795
|
+
return text(a).localeCompare(text(b));
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
export const Hierarchy: Story = {
|
|
799
|
+
args: {
|
|
800
|
+
width: 700,
|
|
801
|
+
columns: [
|
|
802
|
+
{
|
|
803
|
+
label: 'System',
|
|
804
|
+
key: 'system',
|
|
805
|
+
sortable: true,
|
|
806
|
+
compareFunction: compareCellText,
|
|
807
|
+
},
|
|
808
|
+
{label: 'Location', key: 'location'},
|
|
809
|
+
{label: 'State', key: 'state'},
|
|
810
|
+
],
|
|
811
|
+
},
|
|
812
|
+
parameters: {
|
|
813
|
+
docs: {
|
|
814
|
+
description: {
|
|
815
|
+
story:
|
|
816
|
+
'Rows form a tree through `parentId` and `level`. The table draws the indent and the chevron and reports `expand-toggle`; the story owns which rows are collapsed and passes only the visible ones. Sorting the System column reorders each sibling set on its own, so a child never leaves its parent.',
|
|
817
|
+
},
|
|
818
|
+
},
|
|
819
|
+
},
|
|
820
|
+
render: (args) => {
|
|
821
|
+
return html`<obc-table
|
|
822
|
+
.data=${hierarchyRows(systemTree)}
|
|
823
|
+
.columns=${args.columns}
|
|
824
|
+
.striped=${true}
|
|
825
|
+
@expand-toggle=${(e: ObcTableExpandToggleEvent) => {
|
|
826
|
+
if (e.detail.expanded) {
|
|
827
|
+
collapsedRowIds.delete(e.detail.rowId);
|
|
828
|
+
} else {
|
|
829
|
+
collapsedRowIds.add(e.detail.rowId);
|
|
830
|
+
}
|
|
831
|
+
(e.target as ObcTable).data = hierarchyRows(systemTree);
|
|
832
|
+
}}
|
|
833
|
+
></obc-table>`;
|
|
834
|
+
},
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
const cyclicRows = (): ObcTableRow[] => [
|
|
838
|
+
{
|
|
839
|
+
id: 'standalone',
|
|
840
|
+
level: 0,
|
|
841
|
+
system: {type: ObcTableCellType.Regular, text: 'Ballast'},
|
|
842
|
+
parent: {type: ObcTableCellType.Regular, text: '—', neutral: true},
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
id: 'pump-a',
|
|
846
|
+
parentId: 'pump-b',
|
|
847
|
+
level: 1,
|
|
848
|
+
system: {type: ObcTableCellType.Regular, text: 'Pump A'},
|
|
849
|
+
parent: {type: ObcTableCellType.Regular, text: 'pump-b', neutral: true},
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
id: 'pump-b',
|
|
853
|
+
parentId: 'pump-a',
|
|
854
|
+
level: 1,
|
|
855
|
+
system: {type: ObcTableCellType.Regular, text: 'Pump B'},
|
|
856
|
+
parent: {type: ObcTableCellType.Regular, text: 'pump-a', neutral: true},
|
|
857
|
+
},
|
|
858
|
+
];
|
|
859
|
+
|
|
860
|
+
export const CyclicHierarchy: Story = {
|
|
861
|
+
args: {width: 700},
|
|
862
|
+
parameters: {
|
|
863
|
+
docs: {
|
|
864
|
+
description: {
|
|
865
|
+
story:
|
|
866
|
+
'Regression guard. Both tables get the same three rows, two of which name each other as `parentId`. `sortWithinSiblings()` walks down from rows whose `parentId` is absent from the data, so the cycle yields no root; the rows it cannot reach are re-entered as a top-level sibling set instead of being dropped. Both tables list all three rows, in the sort order each is given. Make all three rows cyclic and all three still render.',
|
|
867
|
+
},
|
|
868
|
+
},
|
|
869
|
+
},
|
|
870
|
+
render: (args) => {
|
|
871
|
+
const columns = (sorted: boolean) => [
|
|
872
|
+
{
|
|
873
|
+
label: 'System',
|
|
874
|
+
key: 'system',
|
|
875
|
+
...(sorted
|
|
876
|
+
? {
|
|
877
|
+
sortable: true as const,
|
|
878
|
+
sortDirection: 'asc' as const,
|
|
879
|
+
compareFunction: compareCellText,
|
|
880
|
+
}
|
|
881
|
+
: {}),
|
|
882
|
+
},
|
|
883
|
+
{label: 'Declared parentId', key: 'parent'},
|
|
884
|
+
];
|
|
885
|
+
return html`<div style="display: grid; gap: 24px; width: ${args.width}px;">
|
|
886
|
+
<div>
|
|
887
|
+
<p>Unsorted</p>
|
|
888
|
+
<obc-table .data=${cyclicRows()} .columns=${columns(false)}></obc-table>
|
|
889
|
+
</div>
|
|
890
|
+
<div>
|
|
891
|
+
<p>Sorted on System — the cyclic rows are recovered</p>
|
|
892
|
+
<obc-table .data=${cyclicRows()} .columns=${columns(true)}></obc-table>
|
|
893
|
+
</div>
|
|
894
|
+
</div>`;
|
|
895
|
+
},
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
const cycleWithDescendantRows = (): ObcTableRow[] => [
|
|
899
|
+
{
|
|
900
|
+
id: 'standalone',
|
|
901
|
+
level: 0,
|
|
902
|
+
system: {type: ObcTableCellType.Regular, text: 'Ballast'},
|
|
903
|
+
parent: {type: ObcTableCellType.Regular, text: '—', neutral: true},
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
id: 'pump-a',
|
|
907
|
+
parentId: 'pump-b',
|
|
908
|
+
level: 1,
|
|
909
|
+
system: {type: ObcTableCellType.Regular, text: 'Pump A'},
|
|
910
|
+
parent: {type: ObcTableCellType.Regular, text: 'pump-b', neutral: true},
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
id: 'pump-b',
|
|
914
|
+
parentId: 'pump-a',
|
|
915
|
+
level: 1,
|
|
916
|
+
system: {type: ObcTableCellType.Regular, text: 'Pump B'},
|
|
917
|
+
parent: {type: ObcTableCellType.Regular, text: 'pump-a', neutral: true},
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
id: 'pump-sensor',
|
|
921
|
+
parentId: 'pump-a',
|
|
922
|
+
level: 2,
|
|
923
|
+
system: {type: ObcTableCellType.Regular, text: 'Pump sensor'},
|
|
924
|
+
parent: {type: ObcTableCellType.Regular, text: 'pump-a', neutral: true},
|
|
925
|
+
},
|
|
926
|
+
];
|
|
927
|
+
|
|
928
|
+
export const CycleWithDescendants: Story = {
|
|
929
|
+
args: {width: 700},
|
|
930
|
+
parameters: {
|
|
931
|
+
docs: {
|
|
932
|
+
description: {
|
|
933
|
+
story:
|
|
934
|
+
'Regression guard for the blast radius of a cycle. Pump sensor has one ordinary parent and is in no cycle, but its parent sits in one, so recovering only the cycle members would still lose it. All four rows render in both tables; Pump sensor stays under Pump A, because the recovered rows are walked, not flattened.',
|
|
935
|
+
},
|
|
936
|
+
},
|
|
937
|
+
},
|
|
938
|
+
render: (args) => {
|
|
939
|
+
const columns = (sorted: boolean) => [
|
|
940
|
+
{
|
|
941
|
+
label: 'System',
|
|
942
|
+
key: 'system',
|
|
943
|
+
...(sorted
|
|
944
|
+
? {
|
|
945
|
+
sortable: true as const,
|
|
946
|
+
sortDirection: 'asc' as const,
|
|
947
|
+
compareFunction: compareCellText,
|
|
948
|
+
}
|
|
949
|
+
: {}),
|
|
950
|
+
},
|
|
951
|
+
{label: 'Declared parentId', key: 'parent'},
|
|
952
|
+
];
|
|
953
|
+
return html`<div style="display: grid; gap: 24px; width: ${args.width}px;">
|
|
954
|
+
<div>
|
|
955
|
+
<p>Unsorted</p>
|
|
956
|
+
<obc-table
|
|
957
|
+
.data=${cycleWithDescendantRows()}
|
|
958
|
+
.columns=${columns(false)}
|
|
959
|
+
></obc-table>
|
|
960
|
+
</div>
|
|
961
|
+
<div>
|
|
962
|
+
<p>Sorted on System — the cycle and its descendant are recovered</p>
|
|
963
|
+
<obc-table
|
|
964
|
+
.data=${cycleWithDescendantRows()}
|
|
965
|
+
.columns=${columns(true)}
|
|
966
|
+
></obc-table>
|
|
967
|
+
</div>
|
|
968
|
+
</div>`;
|
|
969
|
+
},
|
|
970
|
+
};
|
|
@@ -13,6 +13,7 @@ import '../table-header-item/table-header-item.js';
|
|
|
13
13
|
import {ObcTableHeaderItemType} from '../table-header-item/table-header-item.js';
|
|
14
14
|
import {classMap} from 'lit/directives/class-map.js';
|
|
15
15
|
import '../button/button.js';
|
|
16
|
+
import '../../icons/icon-chevron-right-google.js';
|
|
16
17
|
import {CheckboxStatus, ObcCheckboxChangeEvent} from '../checkbox/checkbox.js';
|
|
17
18
|
import {TagColor} from '../tag/tag.js';
|
|
18
19
|
import '../../building-blocks/bar-horizontal/bar-horizontal.js';
|
|
@@ -130,7 +131,11 @@ export type ObcTableCellData =
|
|
|
130
131
|
export interface ObcTableRow {
|
|
131
132
|
selected?: boolean;
|
|
132
133
|
id: string;
|
|
133
|
-
|
|
134
|
+
parentId?: string;
|
|
135
|
+
level?: number;
|
|
136
|
+
expandable?: boolean;
|
|
137
|
+
expanded?: boolean;
|
|
138
|
+
[key: string]: ObcTableCellData | boolean | undefined | string | number;
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
export interface ObcTableColumnUnsortable<
|
|
@@ -187,6 +192,11 @@ export type ObcTableRowClickEvent = CustomEvent<{
|
|
|
187
192
|
row: ObcTableRow;
|
|
188
193
|
}>;
|
|
189
194
|
|
|
195
|
+
export type ObcTableExpandToggleEvent = CustomEvent<{
|
|
196
|
+
rowId: string;
|
|
197
|
+
expanded: boolean;
|
|
198
|
+
}>;
|
|
199
|
+
|
|
190
200
|
function cssPart(value: ObcTableCellData, subpart: string): string | undefined {
|
|
191
201
|
if (value.cssPart) {
|
|
192
202
|
return `${value.cssPart} ${subpart}`;
|
|
@@ -206,11 +216,17 @@ function cssPart(value: ObcTableCellData, subpart: string): string | undefined {
|
|
|
206
216
|
* - Cell rendering variants: `checkbox`, `tag`, `horizontal-bar`, and `button` cell types.
|
|
207
217
|
* - Visual variants: `rowDivider`, `striped`, `narrowHeader`, and `showHeader`.
|
|
208
218
|
* - Sorting support for sortable columns via column definitions.
|
|
219
|
+
* - Hierarchical rows via `parentId`, `level`, `expandable` and `expanded`.
|
|
209
220
|
*
|
|
210
221
|
* Usage Guidelines
|
|
211
222
|
* - Controlled selection: pass `selectedRowIds` and update it on `selection-change`.
|
|
212
223
|
* - Uncontrolled selection: pass `defaultSelectedRowIds` and listen to `selection-change`.
|
|
213
224
|
* - Use `selectAllAriaLabel` to customize the header checkbox accessibility label.
|
|
225
|
+
* - Hierarchy is controlled, and flat: pass only the rows that are currently
|
|
226
|
+
* visible, each with the `parentId` of its parent row, and drop a collapsed
|
|
227
|
+
* group's descendants from `data` on `expand-toggle`. The table draws the
|
|
228
|
+
* indent and the chevron, keeps each sibling set sorted within its parent,
|
|
229
|
+
* and owns no expansion state of its own.
|
|
214
230
|
*
|
|
215
231
|
* Slots/Content
|
|
216
232
|
* - This component does not expose public slots. Provide content via `data` and `columns`.
|
|
@@ -221,6 +237,15 @@ function cssPart(value: ObcTableCellData, subpart: string): string | undefined {
|
|
|
221
237
|
* - `cell-checkbox-change` fires when a checkbox cell changes.
|
|
222
238
|
* - `cell-tag-click` fires when a tag inside a cell is clicked.
|
|
223
239
|
* - `selection-change` fires when row selection changes (source: `row` or `header`).
|
|
240
|
+
* - `expand-toggle` fires when a group row's chevron or expand key is used.
|
|
241
|
+
*
|
|
242
|
+
* Accessibility
|
|
243
|
+
* A table with hierarchical rows renders as a
|
|
244
|
+
* [treegrid](https://www.w3.org/WAI/ARIA/apg/patterns/treegrid/): rows carry
|
|
245
|
+
* `aria-level`, and group rows `aria-expanded`. Right expands a collapsed
|
|
246
|
+
* group, Left collapses an expanded one or moves to the parent row. Cell-level
|
|
247
|
+
* arrow navigation, which the full pattern also specifies, is out of scope —
|
|
248
|
+
* arrow keys move between rows, matching the flat table.
|
|
224
249
|
*
|
|
225
250
|
* Best Practices
|
|
226
251
|
* - Keep column `key` values stable across renders to avoid selection or sorting resets.
|
|
@@ -247,6 +272,7 @@ function cssPart(value: ObcTableCellData, subpart: string): string | undefined {
|
|
|
247
272
|
* @fires {ObcTableCellCheckboxChangeEvent} cell-checkbox-change - Fired when a cell checkbox is changed.
|
|
248
273
|
* @fires {ObcTableCellTagClickEvent} cell-tag-click - Fired when a tag inside a cell is clicked.
|
|
249
274
|
* @fires {ObcTableSelectionChangeEvent} selection-change - Fired when row selection changes.
|
|
275
|
+
* @fires {ObcTableExpandToggleEvent} expand-toggle - Fired when a group row is expanded or collapsed.
|
|
250
276
|
* @beta
|
|
251
277
|
*/
|
|
252
278
|
@customElement('obc-table')
|
|
@@ -273,6 +299,50 @@ export class ObcTable extends LitElement {
|
|
|
273
299
|
|
|
274
300
|
private _previousPositions: {top: number; index: string}[] = [];
|
|
275
301
|
|
|
302
|
+
private get hasHierarchy() {
|
|
303
|
+
return this.data.some(
|
|
304
|
+
(row) =>
|
|
305
|
+
row.parentId !== undefined ||
|
|
306
|
+
(row.level ?? 0) > 0 ||
|
|
307
|
+
row.expandable === true
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private sortWithinSiblings(
|
|
312
|
+
rows: ObcTableRow[],
|
|
313
|
+
compare: (a: ObcTableRow, b: ObcTableRow) => number
|
|
314
|
+
) {
|
|
315
|
+
const ids = new Set(rows.map((row) => row.id));
|
|
316
|
+
const childrenByParent = new Map<string, ObcTableRow[]>();
|
|
317
|
+
const roots: ObcTableRow[] = [];
|
|
318
|
+
for (const row of rows) {
|
|
319
|
+
const parentId = row.parentId;
|
|
320
|
+
if (parentId !== undefined && parentId !== row.id && ids.has(parentId)) {
|
|
321
|
+
const siblings = childrenByParent.get(parentId) ?? [];
|
|
322
|
+
siblings.push(row);
|
|
323
|
+
childrenByParent.set(parentId, siblings);
|
|
324
|
+
} else {
|
|
325
|
+
roots.push(row);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const sorted: ObcTableRow[] = [];
|
|
330
|
+
const visited = new Set<string>();
|
|
331
|
+
const visit = (siblings: ObcTableRow[]) => {
|
|
332
|
+
for (const row of [...siblings].sort(compare)) {
|
|
333
|
+
if (visited.has(row.id)) {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
visited.add(row.id);
|
|
337
|
+
sorted.push(row);
|
|
338
|
+
visit(childrenByParent.get(row.id) ?? []);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
visit(roots);
|
|
342
|
+
visit(rows.filter((row) => !visited.has(row.id)));
|
|
343
|
+
return sorted;
|
|
344
|
+
}
|
|
345
|
+
|
|
276
346
|
get sortedData() {
|
|
277
347
|
if (this._sortByColumnIdx === undefined) {
|
|
278
348
|
return this.data;
|
|
@@ -285,8 +355,7 @@ export class ObcTable extends LitElement {
|
|
|
285
355
|
return this.data;
|
|
286
356
|
}
|
|
287
357
|
const sortDirection = this._sortDirection;
|
|
288
|
-
const
|
|
289
|
-
sortedData.sort((a, b) => {
|
|
358
|
+
const compare = (a: ObcTableRow, b: ObcTableRow) => {
|
|
290
359
|
const aValue = a[sortByColumn.key];
|
|
291
360
|
const bValue = b[sortByColumn.key];
|
|
292
361
|
if (sortDirection === 'asc') {
|
|
@@ -304,7 +373,12 @@ export class ObcTable extends LitElement {
|
|
|
304
373
|
a
|
|
305
374
|
);
|
|
306
375
|
}
|
|
307
|
-
}
|
|
376
|
+
};
|
|
377
|
+
if (this.hasHierarchy) {
|
|
378
|
+
return this.sortWithinSiblings(this.data, compare);
|
|
379
|
+
}
|
|
380
|
+
const sortedData = [...this.data];
|
|
381
|
+
sortedData.sort(compare);
|
|
308
382
|
return sortedData;
|
|
309
383
|
}
|
|
310
384
|
|
|
@@ -329,6 +403,42 @@ export class ObcTable extends LitElement {
|
|
|
329
403
|
);
|
|
330
404
|
}
|
|
331
405
|
|
|
406
|
+
private _handleExpandToggle(row: ObcTableRow, expanded: boolean) {
|
|
407
|
+
if (!row.expandable) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
this.dispatchEvent(
|
|
411
|
+
new CustomEvent('expand-toggle', {
|
|
412
|
+
detail: {rowId: row.id, expanded},
|
|
413
|
+
}) as ObcTableExpandToggleEvent
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private _renderRowExpander(row: ObcTableRow) {
|
|
418
|
+
if (!this.hasHierarchy) {
|
|
419
|
+
return nothing;
|
|
420
|
+
}
|
|
421
|
+
const level = row.level ?? 0;
|
|
422
|
+
return html`<span
|
|
423
|
+
class="row-expander"
|
|
424
|
+
style="--row-level: ${level}"
|
|
425
|
+
aria-hidden="true"
|
|
426
|
+
>
|
|
427
|
+
${row.expandable
|
|
428
|
+
? html`<span
|
|
429
|
+
class=${classMap({chevron: true, expanded: row.expanded ?? false})}
|
|
430
|
+
@click=${(event: MouseEvent) => {
|
|
431
|
+
event.preventDefault();
|
|
432
|
+
event.stopPropagation();
|
|
433
|
+
this._handleExpandToggle(row, !(row.expanded ?? false));
|
|
434
|
+
}}
|
|
435
|
+
>
|
|
436
|
+
<obi-chevron-right-google></obi-chevron-right-google>
|
|
437
|
+
</span>`
|
|
438
|
+
: nothing}
|
|
439
|
+
</span>`;
|
|
440
|
+
}
|
|
441
|
+
|
|
332
442
|
private _focusFirstRow() {
|
|
333
443
|
const firstRow = this.renderRoot.querySelector<HTMLButtonElement>(
|
|
334
444
|
'button[role="row"].grid-row'
|
|
@@ -393,6 +503,13 @@ export class ObcTable extends LitElement {
|
|
|
393
503
|
|
|
394
504
|
private _handleRowKeyDown(event: KeyboardEvent) {
|
|
395
505
|
const key = event.key;
|
|
506
|
+
if (
|
|
507
|
+
this.hasHierarchy &&
|
|
508
|
+
(key === 'ArrowRight' || key === 'ArrowLeft') &&
|
|
509
|
+
this._handleRowExpandKey(event, key)
|
|
510
|
+
) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
396
513
|
if (
|
|
397
514
|
key !== 'ArrowDown' &&
|
|
398
515
|
key !== 'ArrowUp' &&
|
|
@@ -447,6 +564,37 @@ export class ObcTable extends LitElement {
|
|
|
447
564
|
}
|
|
448
565
|
}
|
|
449
566
|
|
|
567
|
+
private _handleRowExpandKey(
|
|
568
|
+
event: KeyboardEvent,
|
|
569
|
+
key: 'ArrowRight' | 'ArrowLeft'
|
|
570
|
+
) {
|
|
571
|
+
const target = event.currentTarget as HTMLButtonElement | null;
|
|
572
|
+
const rowId = target?.dataset.rowId;
|
|
573
|
+
const row = this.sortedData.find((candidate) => candidate.id === rowId);
|
|
574
|
+
if (!row) return false;
|
|
575
|
+
|
|
576
|
+
const expanded = row.expanded ?? false;
|
|
577
|
+
if (key === 'ArrowRight' && row.expandable && !expanded) {
|
|
578
|
+
this._handleExpandToggle(row, true);
|
|
579
|
+
} else if (key === 'ArrowLeft' && row.expandable && expanded) {
|
|
580
|
+
this._handleExpandToggle(row, false);
|
|
581
|
+
} else if (key === 'ArrowLeft') {
|
|
582
|
+
const parent = this.renderRoot.querySelector<HTMLButtonElement>(
|
|
583
|
+
`button[role="row"].grid-row[data-row-id="${CSS.escape(
|
|
584
|
+
row.parentId ?? ''
|
|
585
|
+
)}"]`
|
|
586
|
+
);
|
|
587
|
+
if (!parent) return false;
|
|
588
|
+
parent.focus();
|
|
589
|
+
} else {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
event.preventDefault();
|
|
594
|
+
event.stopPropagation();
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
|
|
450
598
|
private _getAllPositions(): {
|
|
451
599
|
top: number;
|
|
452
600
|
height: number;
|
|
@@ -459,7 +607,6 @@ export class ObcTable extends LitElement {
|
|
|
459
607
|
)
|
|
460
608
|
);
|
|
461
609
|
|
|
462
|
-
// Calculate the zoom factor for the first row
|
|
463
610
|
const firstRow = rows[0];
|
|
464
611
|
if (!firstRow) {
|
|
465
612
|
return [];
|
|
@@ -609,7 +756,7 @@ export class ObcTable extends LitElement {
|
|
|
609
756
|
--grid-columns-rest: ${selectionColumnCount};
|
|
610
757
|
--selection-column-width: var(--menu-navigation-components-table-item-touch-target-size);
|
|
611
758
|
"
|
|
612
|
-
role
|
|
759
|
+
role=${this.hasHierarchy ? 'treegrid' : 'table'}
|
|
613
760
|
>
|
|
614
761
|
${this.showHeader
|
|
615
762
|
? html`
|
|
@@ -741,8 +888,18 @@ export class ObcTable extends LitElement {
|
|
|
741
888
|
data-row-id=${row.id}
|
|
742
889
|
style="grid-row: ${rowIndex + 1}"
|
|
743
890
|
part="row"
|
|
891
|
+
aria-level=${ifDefined(
|
|
892
|
+
this.hasHierarchy ? (row.level ?? 0) + 1 : undefined
|
|
893
|
+
)}
|
|
894
|
+
aria-expanded=${ifDefined(
|
|
895
|
+
row.expandable ? (row.expanded ?? false) : undefined
|
|
896
|
+
)}
|
|
744
897
|
>
|
|
745
|
-
${map(effectiveColumns, (col) => {
|
|
898
|
+
${map(effectiveColumns, (col, colIndex) => {
|
|
899
|
+
const expander =
|
|
900
|
+
colIndex === (this.selectable ? 1 : 0)
|
|
901
|
+
? this._renderRowExpander(row)
|
|
902
|
+
: nothing;
|
|
746
903
|
if (this.selectable && col.key === '__selection__') {
|
|
747
904
|
const checked = this._selectedRowIds.has(row.id);
|
|
748
905
|
return html`<div
|
|
@@ -765,7 +922,9 @@ export class ObcTable extends LitElement {
|
|
|
765
922
|
}
|
|
766
923
|
const value = row[col.key];
|
|
767
924
|
if (value === undefined) {
|
|
768
|
-
return html`<div class="grid-cell" role="cell"
|
|
925
|
+
return html`<div class="grid-cell" role="cell">
|
|
926
|
+
${expander}
|
|
927
|
+
</div>`;
|
|
769
928
|
}
|
|
770
929
|
if (col.renderCell) {
|
|
771
930
|
return html`<div
|
|
@@ -775,7 +934,7 @@ export class ObcTable extends LitElement {
|
|
|
775
934
|
role="cell"
|
|
776
935
|
part=${ifDefined((value as ObcTableCellData).cssPart)}
|
|
777
936
|
>
|
|
778
|
-
${col.renderCell(
|
|
937
|
+
${expander}${col.renderCell(
|
|
779
938
|
value as ObcTableCellData,
|
|
780
939
|
row,
|
|
781
940
|
row.id
|
|
@@ -785,7 +944,8 @@ export class ObcTable extends LitElement {
|
|
|
785
944
|
return this._renderCell(
|
|
786
945
|
value as ObcTableCellData,
|
|
787
946
|
row,
|
|
788
|
-
col
|
|
947
|
+
col,
|
|
948
|
+
expander
|
|
789
949
|
);
|
|
790
950
|
}
|
|
791
951
|
})}
|
|
@@ -951,7 +1111,8 @@ export class ObcTable extends LitElement {
|
|
|
951
1111
|
private _renderCell(
|
|
952
1112
|
value: ObcTableCellData,
|
|
953
1113
|
row: ObcTableRow,
|
|
954
|
-
column: ObcTableColumn<ObcTableCellData, ObcTableRow
|
|
1114
|
+
column: ObcTableColumn<ObcTableCellData, ObcTableRow>,
|
|
1115
|
+
expander: HTMLTemplateResult | typeof nothing = nothing
|
|
955
1116
|
) {
|
|
956
1117
|
if (value.type === ObcTableCellType.Regular) {
|
|
957
1118
|
return html`<div
|
|
@@ -968,7 +1129,7 @@ export class ObcTable extends LitElement {
|
|
|
968
1129
|
role="cell"
|
|
969
1130
|
part=${ifDefined(cssPart(value, 'cell'))}
|
|
970
1131
|
>
|
|
971
|
-
${value.icon3
|
|
1132
|
+
${expander}${value.icon3
|
|
972
1133
|
? html`<span class="icon" part=${ifDefined(cssPart(value, 'icon3'))}
|
|
973
1134
|
>${value.icon3}</span
|
|
974
1135
|
>`
|
|
@@ -999,6 +1160,7 @@ export class ObcTable extends LitElement {
|
|
|
999
1160
|
class="grid-cell button ${column.dividerRight ? 'divider-right' : ''}"
|
|
1000
1161
|
role="cell"
|
|
1001
1162
|
>
|
|
1163
|
+
${expander}
|
|
1002
1164
|
<obc-button
|
|
1003
1165
|
variant="normal"
|
|
1004
1166
|
fullWidth
|
|
@@ -1036,6 +1198,7 @@ export class ObcTable extends LitElement {
|
|
|
1036
1198
|
role="cell"
|
|
1037
1199
|
part=${ifDefined(cssPart(value, 'cell'))}
|
|
1038
1200
|
>
|
|
1201
|
+
${expander}
|
|
1039
1202
|
<obc-checkbox
|
|
1040
1203
|
.status=${value.status ?? CheckboxStatus.unchecked}
|
|
1041
1204
|
.disabled=${value.disabled ?? false}
|
|
@@ -1083,7 +1246,7 @@ export class ObcTable extends LitElement {
|
|
|
1083
1246
|
role="cell"
|
|
1084
1247
|
part=${ifDefined(cssPart(value, 'cell'))}
|
|
1085
1248
|
>
|
|
1086
|
-
${visibleTags.map((tag) => {
|
|
1249
|
+
${expander}${visibleTags.map((tag) => {
|
|
1087
1250
|
const hasIcon = tag.hasIcon ?? tag.icon !== undefined;
|
|
1088
1251
|
return html`<obc-tag
|
|
1089
1252
|
.label=${tag.label}
|
|
@@ -1122,6 +1285,7 @@ export class ObcTable extends LitElement {
|
|
|
1122
1285
|
role="cell"
|
|
1123
1286
|
part=${ifDefined(cssPart(value, 'cell'))}
|
|
1124
1287
|
>
|
|
1288
|
+
${expander}
|
|
1125
1289
|
<obc-bar-horizontal
|
|
1126
1290
|
.minValue=${value.minValue ?? 0}
|
|
1127
1291
|
.maxValue=${value.maxValue ?? 100}
|
package/src/types.ts
CHANGED
|
@@ -34,11 +34,11 @@ export enum AlertCategory {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export interface Alert {
|
|
37
|
-
id: string;
|
|
38
|
-
tagId: string;
|
|
37
|
+
id: string;
|
|
38
|
+
tagId: string;
|
|
39
39
|
source: string;
|
|
40
40
|
text: string;
|
|
41
|
-
note?: string;
|
|
41
|
+
note?: string;
|
|
42
42
|
acknowledged:
|
|
43
43
|
| false
|
|
44
44
|
| {
|
|
@@ -67,8 +67,10 @@ export interface Alert {
|
|
|
67
67
|
blockedEndTime?: Date;
|
|
68
68
|
blockedBy?: string;
|
|
69
69
|
};
|
|
70
|
-
noAck?: boolean;
|
|
70
|
+
noAck?: boolean;
|
|
71
|
+
noSilence?: boolean;
|
|
71
72
|
category?: AlertCategory;
|
|
73
|
+
memberOf?: string[];
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export function isActive(alert: Alert) {
|