@zywave/zui-table 4.0.2-pre.1 → 4.0.2-pre.2

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.
Files changed (48) hide show
  1. package/dist/base.d.ts +29 -0
  2. package/dist/base.js +67 -0
  3. package/dist/base.js.map +1 -0
  4. package/dist/css/zui-table.fouc.css +1 -1
  5. package/dist/custom-elements.json +192 -27
  6. package/dist/zui-table-cell-css.js +1 -1
  7. package/dist/zui-table-cell-css.js.map +1 -1
  8. package/dist/zui-table-cell.d.ts +27 -3
  9. package/dist/zui-table-cell.js +93 -6
  10. package/dist/zui-table-cell.js.map +1 -1
  11. package/dist/zui-table-css.js +1 -1
  12. package/dist/zui-table-css.js.map +1 -1
  13. package/dist/zui-table-footer.d.ts +6 -4
  14. package/dist/zui-table-footer.js +6 -4
  15. package/dist/zui-table-footer.js.map +1 -1
  16. package/dist/zui-table-row-css.js +1 -1
  17. package/dist/zui-table-row-css.js.map +1 -1
  18. package/dist/zui-table-row.d.ts +7 -4
  19. package/dist/zui-table-row.js +57 -11
  20. package/dist/zui-table-row.js.map +1 -1
  21. package/dist/zui-table-topbar-css.js +1 -1
  22. package/dist/zui-table-topbar-css.js.map +1 -1
  23. package/dist/zui-table-topbar.d.ts +12 -3
  24. package/dist/zui-table-topbar.js +105 -5
  25. package/dist/zui-table-topbar.js.map +1 -1
  26. package/dist/zui-table.d.ts +7 -5
  27. package/dist/zui-table.js +24 -10
  28. package/dist/zui-table.js.map +1 -1
  29. package/docs/customelement-manifest-element.html +28 -0
  30. package/{demo/index.html → docs/demo.html} +236 -42
  31. package/lab.html +347 -60
  32. package/package.json +9 -4
  33. package/src/base.ts +79 -0
  34. package/src/css/zui-table.fouc.scss +90 -17
  35. package/src/zui-table-cell-css.js +1 -1
  36. package/src/zui-table-cell.scss +26 -0
  37. package/src/zui-table-cell.ts +84 -6
  38. package/src/zui-table-css.js +1 -1
  39. package/src/zui-table-footer.ts +6 -4
  40. package/src/zui-table-row-css.js +1 -1
  41. package/src/zui-table-row.scss +11 -4
  42. package/src/zui-table-row.ts +51 -11
  43. package/src/zui-table-topbar-css.js +1 -1
  44. package/src/zui-table-topbar.scss +77 -9
  45. package/src/zui-table-topbar.ts +106 -5
  46. package/src/zui-table.scss +2 -2
  47. package/src/zui-table.ts +24 -9
  48. package/test/zui-table.test.ts +146 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zywave/zui-table",
3
- "version": "4.0.2-pre.1",
3
+ "version": "4.0.2-pre.2",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "license": "UNLICENSED",
@@ -9,7 +9,8 @@
9
9
  "build:clean": "node ../../../scripts/node/remove-build-files.mjs && yarn run build",
10
10
  "build:scss": "node ../../../scripts/node/sass.mjs",
11
11
  "build:ts": "tsc -p tsconfig.build.json",
12
- "launch-server:demo": "es-dev-server --root-dir ../../../ --app-index packages/components/zui-table/demo/index.html --open --node-resolve --watch",
12
+ "launch-server:cem": "es-dev-server --root-dir ../../../ --app-index packages/components/zui-table/docs/customelement-manifest-element.html --open --node-resolve --watch",
13
+ "launch-server:demo": "es-dev-server --root-dir ../../../ --app-index packages/components/zui-table/docs/demo.html --open --node-resolve --watch",
13
14
  "launch-server:lab": "es-dev-server --root-dir ../../../ --app-index packages/components/zui-table/lab.html --open --node-resolve --watch",
14
15
  "demo": "npm-run-all build --parallel watcher:start launch-server:demo",
15
16
  "watch": "npm-run-all build --parallel watcher:start launch-server:lab",
@@ -21,7 +22,11 @@
21
22
  },
22
23
  "customElements": "dist/custom-elements.json",
23
24
  "dependencies": {
24
- "@zywave/zui-base": "^4.1.20-pre.1"
25
+ "@zywave/zui-base": "^4.1.20-pre.2",
26
+ "@zywave/zui-icons": "^4.0.26-pre.2"
25
27
  },
26
- "gitHead": "dd490b9f8950f89af878ff1435f6b1582491ab31"
28
+ "devDependencies": {
29
+ "@zywave/zui-button": "^4.0.26-pre.2"
30
+ },
31
+ "gitHead": "957ede57ae7125aa5f34137d1dd502260968055b"
27
32
  }
package/src/base.ts ADDED
@@ -0,0 +1,79 @@
1
+ import { ZuiBaseElement } from '@zywave/zui-base';
2
+ import type { ZuiTableElement } from './zui-table';
3
+ import type { ZuiTableCellElement } from './zui-table-cell';
4
+
5
+ class TableState {
6
+ elements: Set<ZuiTableBaseElement> = new Set();
7
+ root: ZuiTableElement;
8
+ sortedCell?: ZuiTableCellElement;
9
+ channel = new EventTarget();
10
+
11
+ constructor(root: ZuiTableElement) {
12
+ this.root = root;
13
+ }
14
+ }
15
+
16
+ export abstract class ZuiTableBaseElement extends ZuiBaseElement {
17
+ /**
18
+ * This represents a common eventing ecosystem for all ZuiTableBaseElements, regardless of association
19
+ */
20
+ protected static _globalChannel = new EventTarget();
21
+
22
+ /**
23
+ * Accessor for the associated table state
24
+ */
25
+ protected get _state() {
26
+ if (this.tagName === 'ZUI-TABLE') {
27
+ return ZuiTableBaseElement.#states.get(this as unknown as ZuiTableElement);
28
+ }
29
+ const table = ZuiTableBaseElement.#tableAssociations.get(this);
30
+ if (table) {
31
+ return ZuiTableBaseElement.#states.get(table);
32
+ }
33
+ return undefined;
34
+ }
35
+
36
+ static #states = new WeakMap<ZuiTableElement, TableState>();
37
+ static #tableAssociations = new WeakMap<ZuiTableBaseElement, ZuiTableElement>();
38
+
39
+ constructor() {
40
+ super();
41
+
42
+ if (this.tagName === 'ZUI-TABLE') {
43
+ ZuiTableBaseElement.#states.set(
44
+ this as unknown as ZuiTableElement,
45
+ new TableState(this as unknown as ZuiTableElement)
46
+ );
47
+ }
48
+ }
49
+
50
+ connectedCallback() {
51
+ super.connectedCallback();
52
+
53
+ if (this.tagName !== 'ZUI-TABLE') {
54
+ ZuiTableBaseElement._globalChannel.dispatchEvent(new CustomEvent('connected', { detail: { element: this } }));
55
+ }
56
+ }
57
+
58
+ disconnectedCallback() {
59
+ super.disconnectedCallback();
60
+
61
+ if (this.tagName === 'ZUI-TABLE') {
62
+ ZuiTableBaseElement.#states.delete(this as unknown as ZuiTableElement);
63
+ } else {
64
+ ZuiTableBaseElement.#tableAssociations.delete(this);
65
+ this._state?.elements.delete(this);
66
+ }
67
+ }
68
+
69
+ protected _associateElement(element: ZuiTableBaseElement) {
70
+ if (this.tagName !== 'ZUI-TABLE' || element.tagName === 'ZUI-TABLE') {
71
+ return;
72
+ }
73
+
74
+ ZuiTableBaseElement.#tableAssociations.set(element, this as unknown as ZuiTableElement);
75
+ this._state.elements.add(element);
76
+ }
77
+ }
78
+
79
+ export type TableElementConnectedEvent = CustomEvent<{ element: ZuiTableBaseElement }>;
@@ -5,20 +5,11 @@ zui-table {
5
5
  @include undefined-element {
6
6
  display: block;
7
7
  width: 100%;
8
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.29);
9
8
  border-collapse: collapse;
10
9
  border-spacing: 0;
11
10
 
12
- [slot='topbar'] {
13
- display: flex;
14
- min-height: rem(60);
15
- align-items: center;
16
- padding: rem(10) rem(20);
17
- background-color: var(--zui-gray-25);
18
- }
19
-
20
- [slot='footer'] {
21
- margin-top: rem(10);
11
+ @media (min-width: $bp-xs) {
12
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.29);
22
13
  }
23
14
 
24
15
  [slot='no-results-message'] {
@@ -44,6 +35,12 @@ zui-table[no-results] {
44
35
  [slot='no-results-message'] {
45
36
  display: block;
46
37
  padding: rem(12) rem(20);
38
+ background-color: #fff;
39
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.29);
40
+
41
+ @media (min-width: $bp-xs) {
42
+ box-shadow: none;
43
+ }
47
44
  }
48
45
  }
49
46
  }
@@ -53,24 +50,98 @@ zui-table-topbar {
53
50
  display: flex;
54
51
  width: 100%;
55
52
  min-height: rem(60);
53
+ flex-wrap: wrap;
56
54
  align-items: center;
57
- padding: rem(10) rem(20);
58
- background-color: var(--zui-gray-25);
55
+ // padding-bottom: rem(10);
56
+
57
+ @media (min-width: $bp-xs) {
58
+ flex-wrap: nowrap;
59
+ padding: rem(10) rem(20);
60
+ background-color: var(--zui-gray-25);
61
+ }
62
+
63
+ *:not([slot='action']):not([slot='counter']) {
64
+ margin-bottom: rem(20);
65
+
66
+ @media (min-width: $bp-xs) {
67
+ margin-bottom: 0;
68
+ }
69
+ }
70
+
71
+ zui-search {
72
+ --zui-search-border-color: var(--zui-gray-50);
73
+ flex: 1;
74
+ flex-basis: 100%;
75
+ order: 1;
76
+
77
+ @media (min-width: $bp-xxs) {
78
+ flex-basis: auto;
79
+ order: 0;
80
+ }
81
+
82
+ @media (min-width: $bp-xs) {
83
+ flex: 0;
84
+ margin-right: auto;
85
+ }
86
+ }
59
87
 
60
88
  [slot='counter'] {
89
+ display: flex;
90
+ width: 100%;
61
91
  flex-shrink: 0;
62
- margin-left: auto;
92
+ justify-content: flex-end;
93
+ order: 2;
94
+ margin-bottom: rem(10);
63
95
  padding-left: rem(10);
64
96
  font-size: rem(12);
65
97
  color: var(--zui-gray-400);
98
+
99
+ @media (min-width: $bp-xs) {
100
+ width: auto;
101
+ margin-bottom: 0;
102
+ }
66
103
  }
67
104
 
68
105
  [slot='action'] {
69
- margin-left: rem(10);
106
+ width: 100%;
107
+ margin-bottom: rem(20);
70
108
 
71
- zui-button:not(:first-of-type) {
109
+ @media (min-width: $bp-xxs) {
110
+ width: auto;
72
111
  margin-left: rem(10);
73
112
  }
113
+
114
+ @media (min-width: $bp-xs) {
115
+ margin-bottom: 0;
116
+ }
117
+
118
+ &:first-of-type {
119
+ margin-bottom: 0;
120
+
121
+ @media (min-width: $bp-xxs) {
122
+ margin-bottom: rem(20);
123
+ margin-left: rem(20);
124
+ }
125
+
126
+ @media (min-width: $bp-xs) {
127
+ margin-bottom: 0;
128
+ margin-left: rem(10);
129
+ }
130
+ }
131
+
132
+ &:last-of-type {
133
+ margin-bottom: rem(20);
134
+
135
+ @media (min-width: $bp-xs) {
136
+ margin-bottom: 0;
137
+ }
138
+ }
139
+
140
+ + [slot='action'] {
141
+ @media (max-width: $bp-xxs) {
142
+ margin-top: rem(10);
143
+ }
144
+ }
74
145
  }
75
146
  }
76
147
  }
@@ -81,10 +152,12 @@ zui-table-row {
81
152
  grid-template-columns: auto;
82
153
  padding: rem(10) 0;
83
154
  background-color: #fff;
155
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.29);
84
156
 
85
157
  @media (min-width: $bp-xs) {
86
158
  display: flex;
87
159
  padding: 0;
160
+ box-shadow: none;
88
161
  }
89
162
  }
90
163
  }
@@ -108,7 +181,7 @@ zui-table-row[header] {
108
181
 
109
182
  zui-table-row:not([header]) {
110
183
  @include undefined-element {
111
- border-bottom: 3px solid var(--zui-gray-100);
184
+ border-bottom: 2px solid var(--zui-gray-100);
112
185
 
113
186
  @media (min-width: $bp-xs) {
114
187
  border-bottom-width: 1px;
@@ -1,3 +1,3 @@
1
1
  import { css } from 'lit';
2
2
 
3
- export const style = css`:host{contain:none}:host([action]) div{display:flex}@media(min-width: 45em){:host([action]) div{--zui-table-cell-padding: 0.375rem 1.25rem;align-items:center}}:host([action]) ::slotted(zui-button:not(:first-of-type)){margin-left:.625rem}div{padding:var(--zui-table-cell-padding, 0.3125rem 0.9375rem)}@media(min-width: 45em){div{padding:var(--zui-table-cell-padding, 0.8125rem 1.25rem)}}div.header{float:left;width:33.333%;font-weight:600}div.header+div{padding:var(--zui-table-cell-padding, 0.3125rem 0.9375rem 0.3125rem 0)}`;
3
+ export const style = css`:host{--zui-table-cell-sort-active-color: var(--zui-blue);--zui-table-cell-sort-color: var(--zui-gray-300);contain:none;overflow-wrap:break-word}:host([action]) div{display:flex}@media(min-width: 45em){:host([action]) div{--zui-table-cell-padding: 0.375rem 1.25rem;align-items:center}}:host([action]) ::slotted(zui-button:not(:first-of-type)){margin-left:.625rem}:host([sort=ascending]) zui-icon{--zui-icon-sort-ascending-color: var(--zui-table-cell-sort-active-color)}:host([sort=descending]) zui-icon{--zui-icon-sort-descending-color: var(--zui-table-cell-sort-active-color)}div{padding:var(--zui-table-cell-padding, 0.3125rem 0.9375rem)}@media(min-width: 45em){div{padding:var(--zui-table-cell-padding, 0.8125rem 1.25rem)}}div.header{float:left;width:33.333%;font-weight:600}div.header+div{padding:var(--zui-table-cell-padding, 0.3125rem 0.9375rem 0.3125rem 0)}zui-icon{--zui-icon-size: 1.125rem;vertical-align:middle;margin-left:.625rem;fill:var(--zui-table-cell-sort-color)}.is-selectable{cursor:pointer}`;
@@ -2,7 +2,10 @@
2
2
  @use '@zywave/zui-base-styles/src/variables' as *;
3
3
 
4
4
  :host {
5
+ --zui-table-cell-sort-active-color: var(--zui-blue);
6
+ --zui-table-cell-sort-color: var(--zui-gray-300);
5
7
  contain: none;
8
+ overflow-wrap: break-word;
6
9
  }
7
10
 
8
11
  :host([action]) {
@@ -19,6 +22,18 @@
19
22
  }
20
23
  }
21
24
 
25
+ :host([sort='ascending']) {
26
+ zui-icon {
27
+ --zui-icon-sort-ascending-color: var(--zui-table-cell-sort-active-color);
28
+ }
29
+ }
30
+
31
+ :host([sort='descending']) {
32
+ zui-icon {
33
+ --zui-icon-sort-descending-color: var(--zui-table-cell-sort-active-color);
34
+ }
35
+ }
36
+
22
37
  div {
23
38
  padding: var(--zui-table-cell-padding, #{rem(5)} #{rem(15)});
24
39
 
@@ -36,3 +51,14 @@ div.header {
36
51
  padding: var(--zui-table-cell-padding, #{rem(5)} #{rem(15)} #{rem(5)} 0);
37
52
  }
38
53
  }
54
+
55
+ zui-icon {
56
+ --zui-icon-size: #{rem(18)};
57
+ vertical-align: middle;
58
+ margin-left: rem(10);
59
+ fill: var(--zui-table-cell-sort-color);
60
+ }
61
+
62
+ .is-selectable {
63
+ cursor: pointer;
64
+ }
@@ -1,33 +1,111 @@
1
- import { ZuiBaseElement } from '@zywave/zui-base';
2
- import { html } from 'lit';
3
- import { property } from 'lit/decorators.js';
1
+ import { ZuiTableBaseElement } from './base.js';
2
+ import { html, nothing } from 'lit';
3
+ import { property, state } from 'lit/decorators.js';
4
+ import { classMap } from 'lit/directives/class-map.js';
4
5
  import { style } from './zui-table-cell-css.js';
5
6
 
6
7
  /**
8
+ * An individual table cell to be passed into a `<zui-table-row>`.
9
+ *
7
10
  * @element zui-table-cell
8
11
  *
9
- * @slot - Default slot; table cell content goes here
12
+ * @slot - Unnamed slot, table cell content goes here
10
13
  *
11
14
  * @cssprop [--zui-table-cell-padding=13px 20px] - Override cell padding
15
+ * @cssprop [--zui-table-cell-sort-color=var(--zui-gray-300)] - ([sortable]): Default color of chevron when `direction` is `null`
16
+ * @cssprop [--zui-table-cell-sort-active-color=var(--zui-blue)] - Highlight color used to indicate the active sort direction
17
+ *
18
+ * @event sort - Event fires when a `sortable` cell is clicked
12
19
  */
13
- export class ZuiTableCellElement extends ZuiBaseElement {
20
+ export class ZuiTableCellElement extends ZuiTableBaseElement {
14
21
  /**
15
22
  * Set to decrease table cell padding to accommodate action button(s)
16
23
  */
17
24
  @property({ type: Boolean, reflect: true })
18
25
  action = false;
19
26
 
27
+ /**
28
+ * Current sort direction of the sortable cell
29
+ * @type {null | "ascending" | "descending"}
30
+ */
31
+ @property({ type: String, reflect: true })
32
+ get sort(): ZuiTableSortDirection {
33
+ return this.#sort;
34
+ }
35
+
36
+ set sort(val: ZuiTableSortDirection) {
37
+ const acceptableVals = ['ascending', 'descending', null];
38
+ if (acceptableVals.includes(val)) {
39
+ const oldVal = this.#sort;
40
+ this.#sort = val;
41
+ this.requestUpdate('sort', oldVal);
42
+ this._state?.channel.dispatchEvent(new CustomEvent('sort', { detail: { sort: val, cell: this } }));
43
+ }
44
+ }
45
+
46
+ /**
47
+ * (`zui-table-row[header]`): Whether or not cell header is sortable; another requirement is the parent element, `<zui-table-cell header>` must have `header` attribute or property set
48
+ */
49
+ @property({ type: Boolean, reflect: true })
50
+ get sortable() {
51
+ return this._isAllowedSort && this.#sortable;
52
+ }
53
+
54
+ set sortable(val: boolean) {
55
+ const oldVal = this.#sortable;
56
+ this.#sortable = val;
57
+ this.requestUpdate('sortable', oldVal);
58
+ }
59
+
60
+ /**
61
+ * This private field is needed to persist state between the cell and the header row. DO NOT USE EXTERNALLY
62
+ */
63
+ @state()
64
+ private _isAllowedSort = false;
65
+
66
+ #sort: ZuiTableSortDirection = null;
67
+ #sortable = false;
68
+
20
69
  static get styles() {
21
70
  return [super.styles, style];
22
71
  }
23
72
 
24
73
  render() {
25
- return html`<div><slot></slot></div>`;
74
+ const styles = { 'is-selectable': this.sortable };
75
+ return html`<div @click="${this.#onSortableClick}" class="${classMap(styles)}">
76
+ <slot></slot>
77
+ ${this.sortable ? html`<zui-icon icon="zui-sort"></zui-icon>` : nothing}
78
+ </div>`;
79
+ }
80
+
81
+ click() {
82
+ super.click();
83
+
84
+ this.#onSortableClick();
85
+ }
86
+
87
+ #onSortableClick() {
88
+ if (this.sortable) {
89
+ switch (this.sort) {
90
+ case 'ascending':
91
+ this.sort = 'descending';
92
+ break;
93
+ case 'descending':
94
+ this.sort = null;
95
+ break;
96
+ case null:
97
+ default:
98
+ this.sort = 'ascending';
99
+ break;
100
+ }
101
+ }
26
102
  }
27
103
  }
28
104
 
29
105
  window.customElements.define('zui-table-cell', ZuiTableCellElement);
30
106
 
107
+ export type ZuiTableSortDirection = null | 'ascending' | 'descending';
108
+
31
109
  declare global {
32
110
  interface HTMLElementTagNameMap {
33
111
  'zui-table-cell': ZuiTableCellElement;
@@ -1,3 +1,3 @@
1
1
  import { css } from 'lit';
2
2
 
3
- export const style = css`:host{contain:none;display:block}:host([banded]) ::slotted(zui-table-row:not([header]):nth-child(even)){background-color:var(--zui-gray-25)}:host([banded]) ::slotted(zui-table-row:not([header])){border:0}:host([no-results]) .no-results{padding:.75rem 1.25rem;background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.29)}@media(min-width: 45em){:host([no-results]) .no-results{background-color:transparent;box-shadow:none}}::slotted(zui-table-row:not([header])){border-bottom:3px solid var(--zui-gray-100)}@media(min-width: 45em){::slotted(zui-table-row:not([header])){border-bottom-width:1px}}::slotted(zui-table-row:not([header]):last-of-type){border-bottom:0}.table{display:flex;width:100%;flex-direction:column;box-shadow:0 1px 3px rgba(0,0,0,.29);border-collapse:collapse;border-spacing:0}@media(min-width: 45em){.table{background-color:#fff}}`;
3
+ export const style = css`:host{contain:none;display:block}:host([banded]) ::slotted(zui-table-row:not([header]):nth-child(even)){background-color:var(--zui-gray-25)}:host([banded]) ::slotted(zui-table-row:not([header])){border:0}:host([no-results]) .no-results{padding:.75rem 1.25rem;background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.29)}@media(min-width: 45em){:host([no-results]) .no-results{background-color:transparent;box-shadow:none}}::slotted(zui-table-row:not([header])){border-bottom:2px solid var(--zui-gray-100)}@media(min-width: 45em){::slotted(zui-table-row:not([header])){border-bottom-width:1px}}::slotted(zui-table-row:not([header]):last-of-type){border-bottom:0}.table{display:flex;width:100%;flex-direction:column;border-collapse:collapse;border-spacing:0}@media(min-width: 45em){.table{background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.29)}}`;
@@ -1,15 +1,17 @@
1
- import { ZuiBaseElement } from '@zywave/zui-base';
1
+ import { ZuiTableBaseElement } from './base.js';
2
2
  import { html } from 'lit';
3
3
  import { style } from './zui-table-footer-css.js';
4
4
 
5
5
  /**
6
+ * Footer table element, should come as the last item passed into a `<zui-table>`.
7
+ *
6
8
  * @element zui-table-footer
7
9
  *
8
- * @slot - Default slot; table footer content goes here
10
+ * @slot - Unnamed slot, table footer content goes here
9
11
  *
10
- * @cssprop [--zui-table-footer-margin=10px] - Override the margin between the table and footer of the table
12
+ * @cssprop [--zui-table-footer-margin=0.625rem (10px)] - Override the margin between the table and footer of the table
11
13
  */
12
- export class ZuiTableFooterElement extends ZuiBaseElement {
14
+ export class ZuiTableFooterElement extends ZuiTableBaseElement {
13
15
  static get styles() {
14
16
  return [super.styles, style];
15
17
  }
@@ -1,3 +1,3 @@
1
1
  import { css } from 'lit';
2
2
 
3
- export const style = css`:host{contain:none}:host(:last-of-type:not([summary])) div,:host([summary]) div{margin-bottom:0}:host([header]){display:none;background-color:#fff;border-bottom:1px solid var(--zui-gray-200)}:host([header]) ::slotted(zui-table-cell){--zui-table-cell-padding: 0.53125rem 1.25rem;font-weight:600}@media(min-width: 45em){:host([header]){display:block}}div{display:grid;grid-template-columns:auto;margin-bottom:0;padding:.625rem 0;background-color:#fff}@media(min-width: 45em){div{grid-template-columns:var(--zui-table-columns-template, repeat(auto-fit, minmax(0, 1fr)));padding:0;background-color:transparent}}:host([summary]){background-color:var(--zui-table-summary-background-color, var(--zui-gray-600)) !important}:host([summary]) div{margin-bottom:0;background-color:transparent}:host([summary]) ::slotted(zui-table-cell){font-weight:600;color:var(--zui-table-summary-text-color, #fff)}`;
3
+ export const style = css`:host{contain:none}:host(:last-of-type:not([summary])) div,:host([summary]) div{margin-bottom:0}:host([header]){display:none;background-color:#fff;border-bottom:1px solid var(--zui-gray-200)}@media(min-width: 45em){:host([header]){display:block}}:host([header]) div{box-shadow:none}:host([header]) ::slotted(zui-table-cell){--zui-table-cell-padding: 0.53125rem 1.25rem;font-weight:600;user-select:none}div{display:grid;grid-template-columns:auto;margin-bottom:0;padding:.625rem 0;background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.29)}@media(min-width: 45em){div{grid-template-columns:var(--zui-table-columns-template, repeat(auto-fit, minmax(0, 1fr)));padding:0;background-color:transparent;box-shadow:none}}:host([summary]){background-color:var(--zui-table-summary-background-color, var(--zui-gray-600)) !important}:host([summary]) div{margin-bottom:0;background-color:transparent}:host([summary]) ::slotted(zui-table-cell){font-weight:600;color:var(--zui-table-summary-text-color, #fff)}`;
@@ -17,13 +17,18 @@
17
17
  background-color: #fff;
18
18
  border-bottom: 1px solid var(--zui-gray-200);
19
19
 
20
+ @media (min-width: $bp-xs) {
21
+ display: block;
22
+ }
23
+
24
+ div {
25
+ box-shadow: none;
26
+ }
27
+
20
28
  ::slotted(zui-table-cell) {
21
29
  --zui-table-cell-padding: #{rem(8.5)} #{rem(20)};
22
30
  font-weight: 600;
23
- }
24
-
25
- @media (min-width: $bp-xs) {
26
- display: block;
31
+ user-select: none;
27
32
  }
28
33
  }
29
34
 
@@ -33,11 +38,13 @@ div {
33
38
  margin-bottom: 0;
34
39
  padding: rem(10) 0;
35
40
  background-color: #fff;
41
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.29);
36
42
 
37
43
  @media (min-width: $bp-xs) {
38
44
  grid-template-columns: var(--zui-table-columns-template, repeat(auto-fit, minmax(0, 1fr)));
39
45
  padding: 0;
40
46
  background-color: transparent;
47
+ box-shadow: none;
41
48
  }
42
49
  }
43
50
 
@@ -1,19 +1,21 @@
1
- import { ZuiBaseElement } from '@zywave/zui-base';
1
+ import { ZuiTableBaseElement } from './base.js';
2
2
  import { html } from 'lit';
3
3
  import { property, queryAssignedElements } from 'lit/decorators.js';
4
4
  import { style } from './zui-table-row-css.js';
5
5
  import type { ZuiTableCellElement } from './zui-table-cell.js';
6
6
 
7
7
  /**
8
+ * Pass into `<zui-table>`, `<zui-table-row>` holds a row of `<zui-table-cell>`'s.
9
+ *
8
10
  * @element zui-table-row
9
11
  *
10
- * @slot - Default slot; `<zui-table-cell>`s are declared here
12
+ * @slot - Unnamed slot, `<zui-table-cell>`s are declared here
11
13
  *
12
- * @cssprop [--zui-table-columns-template=repeat(auto-fit, minmax(0, 1fr))] - Override the table columns template. See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns} for more information on valid values.
14
+ * @cssprop [--zui-table-columns-template=repeat(auto-fit, minmax(0, 1fr))] - Override the table columns template. See https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns for more information on valid values.
13
15
  * @cssprop [--zui-table-summary-background-color=var(--zui-gray-600)] - Override the table summary background color
14
16
  * @cssprop [--zui-table-summary-text-color=#fff] - Override the table summary text color
15
17
  */
16
- export class ZuiTableRowElement extends ZuiBaseElement {
18
+ export class ZuiTableRowElement extends ZuiTableBaseElement {
17
19
  /**
18
20
  * Declare a table header; typically the first row in `<zui-table>`
19
21
  */
@@ -29,6 +31,8 @@ export class ZuiTableRowElement extends ZuiBaseElement {
29
31
  @queryAssignedElements({ selector: 'zui-table-cell' })
30
32
  private _slottedCells: Array<ZuiTableCellElement>;
31
33
 
34
+ #sortLock?: boolean;
35
+
32
36
  static get styles() {
33
37
  return [super.styles, style];
34
38
  }
@@ -39,27 +43,63 @@ export class ZuiTableRowElement extends ZuiBaseElement {
39
43
  }
40
44
 
41
45
  firstUpdated() {
42
- this.#setCellsRole();
46
+ if (this.header) {
47
+ const firstCellWithDirection = this._slottedCells?.find((cell) => cell?.hasAttribute('sort'));
48
+ this._slottedCells.forEach((cell) => {
49
+ if (cell.sort && cell !== firstCellWithDirection) {
50
+ cell.sort = null;
51
+ }
52
+ });
53
+ }
54
+ }
55
+
56
+ updated(changedProperties: Map<string | number | symbol, unknown>) {
57
+ super.updated(changedProperties);
58
+ if (changedProperties.has('header')) {
59
+ this.#ensureCellState();
60
+ if (this.header) {
61
+ this._state?.channel.addEventListener('sort', (e: CustomEvent<{ cell: HTMLElement }>) => this.#onTableSort(e));
62
+ }
63
+ }
43
64
  }
44
65
 
45
66
  render() {
46
67
  return html`<div>
47
- <slot @slotchange="${this.#setCellsRole()}"></slot>
68
+ <slot @slotchange="${this.#ensureCellState()}"></slot>
48
69
  </div>`;
49
70
  }
50
71
 
51
- #setCellsRole() {
72
+ #onTableSort(event: CustomEvent<{ cell: HTMLElement }>) {
73
+ if (this.#sortLock) {
74
+ return;
75
+ }
76
+ this.#sortLock = true;
77
+ this._slottedCells?.forEach((cell) => {
78
+ if (cell !== event.detail.cell) {
79
+ cell.sort = null;
80
+ }
81
+ });
82
+ setTimeout(() => {
83
+ this.#sortLock = false;
84
+ }, 0);
85
+ }
86
+
87
+ #ensureCellState() {
52
88
  if (!this._slottedCells) {
53
89
  return;
54
90
  }
55
91
 
56
92
  if (this.header) {
57
- this._slottedCells.map((cell) => cell.setAttribute('role', 'columnheader'));
93
+ this._slottedCells.forEach((cell) => {
94
+ cell.setAttribute('role', 'columnheader');
95
+ (cell as any)._isAllowedSort = true;
96
+ });
58
97
  } else {
59
- this._slottedCells.map((cell) => cell.setAttribute('role', 'cell'));
98
+ this._slottedCells.forEach((cell) => {
99
+ cell.setAttribute('role', 'cell');
100
+ (cell as any)._isAllowedSort = false;
101
+ });
60
102
  }
61
-
62
- this.requestUpdate();
63
103
  }
64
104
  }
65
105
 
@@ -1,3 +1,3 @@
1
1
  import { css } from 'lit';
2
2
 
3
- export const style = css`:host{contain:none}.topbar{display:flex;width:100%;min-height:3.75rem;align-items:center;padding:.625rem 1.25rem;background-color:var(--zui-gray-25);box-shadow:0 1px 3px rgba(0,0,0,.29)}@media(min-width: 45em){.topbar{box-shadow:none}}.content{display:flex;flex:1;align-items:center}.content ::slotted(zui-search){--zui-search-border-color: var(--zui-gray-50)}.counter{flex-shrink:0;margin-left:auto;padding-left:.625rem;font-size:.75rem;color:var(--zui-gray-400)}.action{margin-left:.625rem}.action ::slotted(zui-button:not(:first-of-type)){margin-left:.625rem}slot[name=action]{display:flex}`;
3
+ export const style = css`:host{contain:none}.topbar{display:flex;width:100%;min-height:3.75rem;flex-wrap:wrap;justify-content:space-between;align-items:center}@media(min-width: 45em){.topbar{flex-wrap:nowrap;padding:.625rem 1.25rem;background-color:var(--zui-gray-25);box-shadow:none}}.content{display:flex;flex:1;align-items:center;order:1;margin-bottom:1.25rem}@media(min-width: 30em){.content{flex:auto;order:0}}@media(min-width: 45em){.content{margin-bottom:0}}.content ::slotted(zui-search){--zui-search-border-color: var(--zui-gray-50);width:100%}@media(min-width: 45em){.content ::slotted(zui-search){width:auto}}.counter{display:flex;width:100%;flex-shrink:0;justify-content:flex-end;order:2;margin-bottom:.625rem;margin-left:auto;padding-left:.625rem;font-size:.75rem;color:var(--zui-gray-400)}@media(min-width: 45em){.counter{width:auto;margin-bottom:0}}.action{display:flex;width:100%;justify-content:flex-end;margin-bottom:1.25rem}@media(min-width: 30em){.action{width:auto}}@media(min-width: 45em){.action{margin-bottom:0}}.action slot[name=action]{display:flex;width:100%;flex-direction:column}@media(min-width: 30em){.action slot[name=action]{flex-direction:row}}.action ::slotted(zui-button){width:100%}@media(min-width: 30em){.action ::slotted(zui-button){width:auto}}@media(min-width: 30em){.action ::slotted(zui-button:first-of-type){margin-left:1.25rem}}@media(min-width: 45em){.action ::slotted(zui-button:first-of-type){margin-left:.625rem}}.action ::slotted(zui-button:not(:first-of-type)){margin-top:.625rem}@media(min-width: 30em){.action ::slotted(zui-button:not(:first-of-type)){margin-top:0;margin-left:.625rem}}`;