@3t-transform/threeteeui 0.0.11 → 0.0.13

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 (29) hide show
  1. package/dist/cjs/loader.cjs.js +1 -1
  2. package/dist/cjs/tttx-list.cjs.entry.js +198 -0
  3. package/dist/cjs/tttx-standalone-input.cjs.entry.js +9 -7
  4. package/dist/cjs/tttx.cjs.js +1 -1
  5. package/dist/collection/collection-manifest.json +2 -1
  6. package/dist/collection/components/molecules/tttx-list/tttx-list.css +56 -0
  7. package/dist/collection/components/molecules/tttx-list/tttx-list.js +311 -0
  8. package/dist/collection/components/molecules/tttx-list/tttx-list.stories.js +37 -0
  9. package/dist/collection/components/molecules/tttx-standalone-input/tttx-standalone-input.js +37 -29
  10. package/dist/components/index.d.ts +1 -0
  11. package/dist/components/index.js +1 -0
  12. package/dist/components/tttx-list.d.ts +11 -0
  13. package/dist/components/tttx-list.js +225 -0
  14. package/dist/components/tttx-loading-spinner.js +1 -38
  15. package/dist/components/tttx-loading-spinner2.js +40 -0
  16. package/dist/components/tttx-standalone-input.js +10 -10
  17. package/dist/esm/loader.js +1 -1
  18. package/dist/esm/tttx-list.entry.js +194 -0
  19. package/dist/esm/tttx-standalone-input.entry.js +9 -7
  20. package/dist/esm/tttx.js +1 -1
  21. package/dist/tttx/p-25953f82.entry.js +1 -0
  22. package/dist/tttx/p-2b6720ac.entry.js +1 -0
  23. package/dist/tttx/tttx.esm.js +1 -1
  24. package/dist/types/components/molecules/tttx-list/tttx-list.d.ts +51 -0
  25. package/dist/types/components/molecules/tttx-list/tttx-list.stories.d.ts +13 -0
  26. package/dist/types/components/molecules/tttx-standalone-input/tttx-standalone-input.d.ts +4 -4
  27. package/dist/types/components.d.ts +26 -2
  28. package/package.json +1 -1
  29. package/dist/tttx/p-338b3976.entry.js +0 -1
@@ -14,7 +14,7 @@ const patchEsm = () => {
14
14
  const defineCustomElements = (win, options) => {
15
15
  if (typeof window === 'undefined') return Promise.resolve();
16
16
  return patchEsm().then(() => {
17
- return index.bootstrapLazy([["tttx-standalone-input.cjs",[[2,"tttx-standalone-input",{"label":[1],"valid":[4],"showerrormsg":[4],"errormsg":[1],"iconleft":[1],"iconright":[1],"autocapitalize":[1],"autocomplete":[1],"autofocus":[4],"checked":[4],"disabled":[4],"max":[8],"maxlength":[8],"min":[8],"minlength":[8],"name":[1],"pattern":[1],"placeholder":[1],"readonly":[8],"required":[4],"step":[8],"type":[1],"value":[1032],"isfocused":[32]}]]],["tttx-button.cjs",[[1,"tttx-button",{"fontColor":[1,"font-color"],"buttonStyle":[1,"button-style"]},[[0,"clickEvent","onClicked"]]]]],["tttx-checkbox.cjs",[[1,"tttx-checkbox",{"value":[4],"label":[1],"required":[4]}]]],["tttx-form.cjs",[[1,"tttx-form",{"formschema":[8],"submitValue":[8,"submit-value"]}]]],["tttx-loading-spinner.cjs",[[1,"tttx-loading-spinner",{"loadingMessage":[1028,"loading-message"],"size":[1025]}]]],["tttx-popover-content.cjs",[[1,"tttx-popover-content",{"header":[1],"body":[1],"linkcontext":[1],"linktext":[1]}]]],["tttx-icon.cjs",[[1,"tttx-icon",{"icon":[1],"colour":[1]},[[4,"click","handleDocumentClick"]]]]]], options);
17
+ return index.bootstrapLazy([["tttx-list.cjs",[[1,"tttx-list",{"name":[1],"selectable":[4],"items":[32],"selectedIds":[32],"loading":[32],"lastPage":[32]},[[4,"listPageLoad","listLoadHandler"],[4,"listClearDataCache","listClearDataCacheHandler"],[4,"listActionSelectedEvent","listActionSelectedEventHandler"]]]]],["tttx-standalone-input.cjs",[[2,"tttx-standalone-input",{"label":[1],"showerrormsg":[4],"errormsg":[1],"iconleft":[1],"iconright":[1],"autocapitalize":[1],"autocomplete":[1],"autofocus":[4],"checked":[4],"disabled":[4],"max":[8],"maxlength":[8],"min":[8],"minlength":[8],"name":[1],"pattern":[1],"placeholder":[1],"readonly":[8],"required":[4],"step":[8],"type":[1],"value":[1032]}]]],["tttx-button.cjs",[[1,"tttx-button",{"fontColor":[1,"font-color"],"buttonStyle":[1,"button-style"]},[[0,"clickEvent","onClicked"]]]]],["tttx-checkbox.cjs",[[1,"tttx-checkbox",{"value":[4],"label":[1],"required":[4]}]]],["tttx-form.cjs",[[1,"tttx-form",{"formschema":[8],"submitValue":[8,"submit-value"]}]]],["tttx-popover-content.cjs",[[1,"tttx-popover-content",{"header":[1],"body":[1],"linkcontext":[1],"linktext":[1]}]]],["tttx-icon.cjs",[[1,"tttx-icon",{"icon":[1],"colour":[1]},[[4,"click","handleDocumentClick"]]]]],["tttx-loading-spinner.cjs",[[1,"tttx-loading-spinner",{"loadingMessage":[1028,"loading-message"],"size":[1025]}]]]], options);
18
18
  });
19
19
  };
20
20
 
@@ -0,0 +1,198 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const index = require('./index-bf39be87.js');
6
+
7
+ const tttxListCss = ".material-symbols-rounded{font-variation-settings:\"FILL\" 1, \"wght\" 400, \"GRAD\" 0, \"opsz\" 24}.material-symbols-rounded{font-family:\"Material Symbols Rounded\", sans-serif;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;color:#9e9e9e}:host{display:flex;flex-direction:column}.tttx-list__row{min-height:52px;line-height:36px;padding:8px;display:flex;flex-direction:row;align-items:center;cursor:pointer;border-bottom:1px solid #d5d5d5}.tttx-list__row .generic-template__content{width:100%;display:flex;align-items:center;gap:8px}.tttx-list__row:first-of-type{border-top:1px solid #d5d5d5}.tttx-list__row:focus,.tttx-list__row:active{background-color:#e6e6e6}.tttx-list__row.selected{background-color:#e7f1f9}.load-indicator{display:flex;justify-content:center}";
8
+
9
+ const TttxList = class {
10
+ constructor(hostRef) {
11
+ index.registerInstance(this, hostRef);
12
+ this.listPaginate = index.createEvent(this, "listPaginate", 7);
13
+ this.listSelectedEvent = index.createEvent(this, "listSelectedEvent", 7);
14
+ this.listItemClick = index.createEvent(this, "listItemClick", 7);
15
+ this.rowCount = 0;
16
+ this.name = undefined;
17
+ this.selectable = undefined;
18
+ this.items = [];
19
+ this.selectedIds = [];
20
+ this.loading = true;
21
+ this.lastPage = false;
22
+ }
23
+ listLoadHandler(event) {
24
+ if (event.detail.name !== this.name)
25
+ return;
26
+ this.items = [...this.items, ...event.detail.items];
27
+ this.loading = false;
28
+ this.lastPage = event.detail.lastPage;
29
+ this.renderRows(event.detail.items);
30
+ if (this.scrollableParent.clientHeight === this.scrollableParent.scrollHeight)
31
+ this.listPaginateHandler();
32
+ }
33
+ listPaginateHandler() {
34
+ // We don't want to emit an event to load the next page if this is the last page, or if we're already loading
35
+ if (this.lastPage || this.loading)
36
+ return;
37
+ this.loading = true;
38
+ this.listPaginate.emit({ name: this.name });
39
+ }
40
+ listClearDataCacheHandler(event) {
41
+ if (event.detail.name !== this.name)
42
+ return;
43
+ this.items = [];
44
+ this.selectedIds = [];
45
+ this.listItemContainer()
46
+ .querySelectorAll(".tttx-list__row")
47
+ .forEach((node) => {
48
+ node.remove();
49
+ });
50
+ this.rowCount = 0;
51
+ this.lastPage = false;
52
+ this.loading = false;
53
+ }
54
+ listActionSelectedEventHandler(event) {
55
+ if (event.detail.name !== this.name || !this.selectable || this.selectedIds.length === 0)
56
+ return;
57
+ const selectedRows = this.selectedIds.map(id => {
58
+ return this.items[id];
59
+ });
60
+ if (event.detail.removeRows)
61
+ this.removeSelectedRows();
62
+ this.listSelectedEvent.emit({
63
+ name: this.name,
64
+ eventName: event.detail.eventName,
65
+ selectedRows,
66
+ });
67
+ }
68
+ removeSelectedRows() {
69
+ const orderedSelectedIds = [...this.selectedIds].sort((a, b) => {
70
+ return b - a;
71
+ });
72
+ orderedSelectedIds.forEach(itemIndex => {
73
+ this.items.splice(itemIndex, 1);
74
+ this.listItemContainer().removeChild(this.listItemContainer().querySelector(`[data-row-id="${itemIndex}"]`));
75
+ // If this removes the scrollbar and we're not on the last page, load in a new page
76
+ if (this.scrollableParent.clientHeight === this.scrollableParent.scrollHeight)
77
+ this.listPaginateHandler();
78
+ });
79
+ // Empty selected ids with mutation rather than assignment to avoid unecessary component rerender
80
+ this.selectedIds.splice(0, this.selectedIds.length);
81
+ }
82
+ listItemClickHandler(itemData) {
83
+ if (this.selectedIds.length !== 0)
84
+ return;
85
+ this.listItemClick.emit({ itemData, name: this.name });
86
+ }
87
+ componentWillLoad() {
88
+ this.template = this.host.querySelector("template");
89
+ this.scrollableParent = this.getScrollableParent(this.host);
90
+ const scrollableContext = this.scrollableParent === document.scrollingElement ? document : this.scrollableParent;
91
+ scrollableContext.addEventListener("scroll", this.scrollHandler.bind(this));
92
+ window.addEventListener("resize", this.scrollHandler.bind(this));
93
+ }
94
+ componentDidLoad() {
95
+ // Emit event to load first page
96
+ this.listPaginate.emit({ name: this.name });
97
+ }
98
+ listItemContainer() {
99
+ return this.host.shadowRoot.querySelector(".list-item-container");
100
+ }
101
+ async scrollHandler() {
102
+ const { clientHeight, scrollTop, scrollHeight } = this.scrollableParent;
103
+ if (Math.abs(scrollHeight - clientHeight - scrollTop) < 26)
104
+ this.listPaginateHandler();
105
+ }
106
+ isScrollable(node) {
107
+ const hasScrollbar = ["scroll", "auto"].includes(node.style.overflowY);
108
+ return hasScrollbar;
109
+ }
110
+ getScrollableParent(node) {
111
+ if (!node || node === document.body) {
112
+ return document.scrollingElement;
113
+ }
114
+ else {
115
+ return this.isScrollable(node) ? node : this.getScrollableParent(node.parentElement);
116
+ }
117
+ }
118
+ appendRowCheckbox(rowContainer) {
119
+ if (!this.selectable)
120
+ return;
121
+ const rowId = this.rowCount;
122
+ const checkbox = document.createElement("input");
123
+ checkbox.setAttribute("type", "checkbox");
124
+ checkbox.addEventListener("click", e => {
125
+ e.stopPropagation();
126
+ if (e.target.checked) {
127
+ this.selectedIds = [...this.selectedIds, rowId];
128
+ rowContainer.classList.add("selected");
129
+ }
130
+ else {
131
+ this.selectedIds = this.selectedIds.filter(id => {
132
+ return id !== rowId;
133
+ });
134
+ rowContainer.classList.remove("selected");
135
+ }
136
+ });
137
+ rowContainer.setAttribute("data-row-id", `${rowId}`);
138
+ this.rowCount++;
139
+ rowContainer.appendChild(checkbox);
140
+ }
141
+ appendSeededTemplate(rowData, rowContainer) {
142
+ this.template.childNodes.forEach(child => {
143
+ const clone = child.cloneNode(false);
144
+ if (clone.getAttribute("data-fields")) {
145
+ const fields = clone.getAttribute("data-fields").replace(/\s/g, "").split(",");
146
+ const filteredRowData = Object.fromEntries(fields.map((field) => {
147
+ return [field, rowData[field]];
148
+ }));
149
+ clone.setAttribute("row-data", JSON.stringify(filteredRowData));
150
+ }
151
+ else {
152
+ clone.setAttribute("row-data", JSON.stringify(rowData));
153
+ }
154
+ rowContainer.appendChild(clone);
155
+ });
156
+ }
157
+ appendGenericTemplate(rowData, rowContainer) {
158
+ const row = document.createElement("div");
159
+ row.classList.add("generic-template__content");
160
+ row.textContent = rowData.text;
161
+ rowContainer.appendChild(row);
162
+ if (!rowData.icon)
163
+ return;
164
+ const icon = document.createElement("span");
165
+ icon.classList.add("material-symbols-rounded");
166
+ icon.textContent = rowData.icon;
167
+ if (rowData.iconColor)
168
+ icon.style["color"] = rowData.iconColor;
169
+ row.prepend(icon);
170
+ }
171
+ row(rowData) {
172
+ const rowContainer = document.createElement("div");
173
+ rowContainer.classList.add("tttx-list__row");
174
+ rowContainer.addEventListener("click", () => {
175
+ this.listItemClickHandler(rowData);
176
+ });
177
+ this.appendRowCheckbox(rowContainer);
178
+ this.template ? this.appendSeededTemplate(rowData, rowContainer) : this.appendGenericTemplate(rowData, rowContainer);
179
+ return rowContainer;
180
+ }
181
+ renderRows(batchData) {
182
+ batchData.forEach(rowData => {
183
+ this.listItemContainer().appendChild(this.row(rowData));
184
+ });
185
+ }
186
+ loadIndicator() {
187
+ if (!this.loading)
188
+ return;
189
+ return index.h("div", { class: "load-indicator" }, index.h("tttx-loading-spinner", { size: 'large' }));
190
+ }
191
+ render() {
192
+ return (index.h(index.Host, null, index.h("div", { class: "list-item-container", tabindex: "0" }, !this.loading && this.items.length === 0 ? "No data to display" : undefined), this.loadIndicator()));
193
+ }
194
+ get host() { return index.getElement(this); }
195
+ };
196
+ TttxList.style = tttxListCss;
197
+
198
+ exports.tttx_list = TttxList;
@@ -10,9 +10,9 @@ const TttxInput = class {
10
10
  constructor(hostRef) {
11
11
  index.registerInstance(this, hostRef);
12
12
  this.valueChanged = index.createEvent(this, "valueChanged", 7);
13
- this.isfocused = false;
13
+ this.focusChanged = index.createEvent(this, "focusChanged", 7);
14
+ this.blurChanged = index.createEvent(this, "blurChanged", 7);
14
15
  this.label = undefined;
15
- this.valid = undefined;
16
16
  this.showerrormsg = undefined;
17
17
  this.errormsg = undefined;
18
18
  this.iconleft = undefined;
@@ -40,11 +40,13 @@ const TttxInput = class {
40
40
  this.value = target.value;
41
41
  this.valueChanged.emit(target.value);
42
42
  }
43
- handleFocus() {
44
- this.isfocused = true;
43
+ handleFocus(event) {
44
+ const target = event.target;
45
+ this.valueChanged.emit(target.value);
45
46
  }
46
- handleBlur() {
47
- this.isfocused = false;
47
+ handleBlur(event) {
48
+ const target = event.target;
49
+ this.valueChanged.emit(target.value);
48
50
  }
49
51
  render() {
50
52
  const classNames = ['standalone', this.showerrormsg ? 'invalid' : ''].join(' ');
@@ -74,7 +76,7 @@ const TttxInput = class {
74
76
  this.type === 'number' ||
75
77
  this.type === 'range'
76
78
  ? this.min
77
- : null, minlength: this.maxlength, name: this.name, pattern: this.type === 'text' ||
79
+ : null, minlength: this.minlength, name: this.name, pattern: this.type === 'text' ||
78
80
  this.type === 'date' ||
79
81
  this.type === 'search' ||
80
82
  this.type === 'url' ||
@@ -17,7 +17,7 @@ const patchBrowser = () => {
17
17
  };
18
18
 
19
19
  patchBrowser().then(options => {
20
- return index.bootstrapLazy([["tttx-standalone-input.cjs",[[2,"tttx-standalone-input",{"label":[1],"valid":[4],"showerrormsg":[4],"errormsg":[1],"iconleft":[1],"iconright":[1],"autocapitalize":[1],"autocomplete":[1],"autofocus":[4],"checked":[4],"disabled":[4],"max":[8],"maxlength":[8],"min":[8],"minlength":[8],"name":[1],"pattern":[1],"placeholder":[1],"readonly":[8],"required":[4],"step":[8],"type":[1],"value":[1032],"isfocused":[32]}]]],["tttx-button.cjs",[[1,"tttx-button",{"fontColor":[1,"font-color"],"buttonStyle":[1,"button-style"]},[[0,"clickEvent","onClicked"]]]]],["tttx-checkbox.cjs",[[1,"tttx-checkbox",{"value":[4],"label":[1],"required":[4]}]]],["tttx-form.cjs",[[1,"tttx-form",{"formschema":[8],"submitValue":[8,"submit-value"]}]]],["tttx-loading-spinner.cjs",[[1,"tttx-loading-spinner",{"loadingMessage":[1028,"loading-message"],"size":[1025]}]]],["tttx-popover-content.cjs",[[1,"tttx-popover-content",{"header":[1],"body":[1],"linkcontext":[1],"linktext":[1]}]]],["tttx-icon.cjs",[[1,"tttx-icon",{"icon":[1],"colour":[1]},[[4,"click","handleDocumentClick"]]]]]], options);
20
+ return index.bootstrapLazy([["tttx-list.cjs",[[1,"tttx-list",{"name":[1],"selectable":[4],"items":[32],"selectedIds":[32],"loading":[32],"lastPage":[32]},[[4,"listPageLoad","listLoadHandler"],[4,"listClearDataCache","listClearDataCacheHandler"],[4,"listActionSelectedEvent","listActionSelectedEventHandler"]]]]],["tttx-standalone-input.cjs",[[2,"tttx-standalone-input",{"label":[1],"showerrormsg":[4],"errormsg":[1],"iconleft":[1],"iconright":[1],"autocapitalize":[1],"autocomplete":[1],"autofocus":[4],"checked":[4],"disabled":[4],"max":[8],"maxlength":[8],"min":[8],"minlength":[8],"name":[1],"pattern":[1],"placeholder":[1],"readonly":[8],"required":[4],"step":[8],"type":[1],"value":[1032]}]]],["tttx-button.cjs",[[1,"tttx-button",{"fontColor":[1,"font-color"],"buttonStyle":[1,"button-style"]},[[0,"clickEvent","onClicked"]]]]],["tttx-checkbox.cjs",[[1,"tttx-checkbox",{"value":[4],"label":[1],"required":[4]}]]],["tttx-form.cjs",[[1,"tttx-form",{"formschema":[8],"submitValue":[8,"submit-value"]}]]],["tttx-popover-content.cjs",[[1,"tttx-popover-content",{"header":[1],"body":[1],"linkcontext":[1],"linktext":[1]}]]],["tttx-icon.cjs",[[1,"tttx-icon",{"icon":[1],"colour":[1]},[[4,"click","handleDocumentClick"]]]]],["tttx-loading-spinner.cjs",[[1,"tttx-loading-spinner",{"loadingMessage":[1028,"loading-message"],"size":[1025]}]]]], options);
21
21
  });
22
22
 
23
23
  exports.setNonce = index.setNonce;
@@ -6,7 +6,8 @@
6
6
  "./components/atoms/tttx-loading-spinner/tttx-loading-spinner.js",
7
7
  "./components/atoms/tttx-popover-content/tttx-popover-content.js",
8
8
  "./components/atoms/ttx-checkbox/tttx-checkbox.js",
9
- "./components/molecules/tttx-form/tttx-form.js"
9
+ "./components/molecules/tttx-form/tttx-form.js",
10
+ "./components/molecules/tttx-list/tttx-list.js"
10
11
  ],
11
12
  "compiler": {
12
13
  "name": "@stencil/core",
@@ -0,0 +1,56 @@
1
+ .material-symbols-rounded {
2
+ font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 24;
3
+ }
4
+
5
+ .material-symbols-rounded {
6
+ font-family: "Material Symbols Rounded", sans-serif;
7
+ font-weight: 400;
8
+ font-style: normal;
9
+ font-size: 24px;
10
+ line-height: 1;
11
+ letter-spacing: normal;
12
+ text-transform: none;
13
+ display: inline-block;
14
+ white-space: nowrap;
15
+ word-wrap: normal;
16
+ direction: ltr;
17
+ text-rendering: optimizeLegibility;
18
+ -webkit-font-smoothing: antialiased;
19
+ color: #9e9e9e;
20
+ }
21
+
22
+ :host {
23
+ display: flex;
24
+ flex-direction: column;
25
+ }
26
+
27
+ .tttx-list__row {
28
+ min-height: 52px;
29
+ line-height: 36px;
30
+ padding: 8px;
31
+ display: flex;
32
+ flex-direction: row;
33
+ align-items: center;
34
+ cursor: pointer;
35
+ border-bottom: 1px solid #d5d5d5;
36
+ }
37
+ .tttx-list__row .generic-template__content {
38
+ width: 100%;
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 8px;
42
+ }
43
+ .tttx-list__row:first-of-type {
44
+ border-top: 1px solid #d5d5d5;
45
+ }
46
+ .tttx-list__row:focus, .tttx-list__row:active {
47
+ background-color: #e6e6e6;
48
+ }
49
+ .tttx-list__row.selected {
50
+ background-color: #e7f1f9;
51
+ }
52
+
53
+ .load-indicator {
54
+ display: flex;
55
+ justify-content: center;
56
+ }
@@ -0,0 +1,311 @@
1
+ import { h, Host } from "@stencil/core";
2
+ export class TttxList {
3
+ constructor() {
4
+ this.rowCount = 0;
5
+ this.name = undefined;
6
+ this.selectable = undefined;
7
+ this.items = [];
8
+ this.selectedIds = [];
9
+ this.loading = true;
10
+ this.lastPage = false;
11
+ }
12
+ listLoadHandler(event) {
13
+ if (event.detail.name !== this.name)
14
+ return;
15
+ this.items = [...this.items, ...event.detail.items];
16
+ this.loading = false;
17
+ this.lastPage = event.detail.lastPage;
18
+ this.renderRows(event.detail.items);
19
+ if (this.scrollableParent.clientHeight === this.scrollableParent.scrollHeight)
20
+ this.listPaginateHandler();
21
+ }
22
+ listPaginateHandler() {
23
+ // We don't want to emit an event to load the next page if this is the last page, or if we're already loading
24
+ if (this.lastPage || this.loading)
25
+ return;
26
+ this.loading = true;
27
+ this.listPaginate.emit({ name: this.name });
28
+ }
29
+ listClearDataCacheHandler(event) {
30
+ if (event.detail.name !== this.name)
31
+ return;
32
+ this.items = [];
33
+ this.selectedIds = [];
34
+ this.listItemContainer()
35
+ .querySelectorAll(".tttx-list__row")
36
+ .forEach((node) => {
37
+ node.remove();
38
+ });
39
+ this.rowCount = 0;
40
+ this.lastPage = false;
41
+ this.loading = false;
42
+ }
43
+ listActionSelectedEventHandler(event) {
44
+ if (event.detail.name !== this.name || !this.selectable || this.selectedIds.length === 0)
45
+ return;
46
+ const selectedRows = this.selectedIds.map(id => {
47
+ return this.items[id];
48
+ });
49
+ if (event.detail.removeRows)
50
+ this.removeSelectedRows();
51
+ this.listSelectedEvent.emit({
52
+ name: this.name,
53
+ eventName: event.detail.eventName,
54
+ selectedRows,
55
+ });
56
+ }
57
+ removeSelectedRows() {
58
+ const orderedSelectedIds = [...this.selectedIds].sort((a, b) => {
59
+ return b - a;
60
+ });
61
+ orderedSelectedIds.forEach(itemIndex => {
62
+ this.items.splice(itemIndex, 1);
63
+ this.listItemContainer().removeChild(this.listItemContainer().querySelector(`[data-row-id="${itemIndex}"]`));
64
+ // If this removes the scrollbar and we're not on the last page, load in a new page
65
+ if (this.scrollableParent.clientHeight === this.scrollableParent.scrollHeight)
66
+ this.listPaginateHandler();
67
+ });
68
+ // Empty selected ids with mutation rather than assignment to avoid unecessary component rerender
69
+ this.selectedIds.splice(0, this.selectedIds.length);
70
+ }
71
+ listItemClickHandler(itemData) {
72
+ if (this.selectedIds.length !== 0)
73
+ return;
74
+ this.listItemClick.emit({ itemData, name: this.name });
75
+ }
76
+ componentWillLoad() {
77
+ this.template = this.host.querySelector("template");
78
+ this.scrollableParent = this.getScrollableParent(this.host);
79
+ const scrollableContext = this.scrollableParent === document.scrollingElement ? document : this.scrollableParent;
80
+ scrollableContext.addEventListener("scroll", this.scrollHandler.bind(this));
81
+ window.addEventListener("resize", this.scrollHandler.bind(this));
82
+ }
83
+ componentDidLoad() {
84
+ // Emit event to load first page
85
+ this.listPaginate.emit({ name: this.name });
86
+ }
87
+ listItemContainer() {
88
+ return this.host.shadowRoot.querySelector(".list-item-container");
89
+ }
90
+ async scrollHandler() {
91
+ const { clientHeight, scrollTop, scrollHeight } = this.scrollableParent;
92
+ if (Math.abs(scrollHeight - clientHeight - scrollTop) < 26)
93
+ this.listPaginateHandler();
94
+ }
95
+ isScrollable(node) {
96
+ const hasScrollbar = ["scroll", "auto"].includes(node.style.overflowY);
97
+ return hasScrollbar;
98
+ }
99
+ getScrollableParent(node) {
100
+ if (!node || node === document.body) {
101
+ return document.scrollingElement;
102
+ }
103
+ else {
104
+ return this.isScrollable(node) ? node : this.getScrollableParent(node.parentElement);
105
+ }
106
+ }
107
+ appendRowCheckbox(rowContainer) {
108
+ if (!this.selectable)
109
+ return;
110
+ const rowId = this.rowCount;
111
+ const checkbox = document.createElement("input");
112
+ checkbox.setAttribute("type", "checkbox");
113
+ checkbox.addEventListener("click", e => {
114
+ e.stopPropagation();
115
+ if (e.target.checked) {
116
+ this.selectedIds = [...this.selectedIds, rowId];
117
+ rowContainer.classList.add("selected");
118
+ }
119
+ else {
120
+ this.selectedIds = this.selectedIds.filter(id => {
121
+ return id !== rowId;
122
+ });
123
+ rowContainer.classList.remove("selected");
124
+ }
125
+ });
126
+ rowContainer.setAttribute("data-row-id", `${rowId}`);
127
+ this.rowCount++;
128
+ rowContainer.appendChild(checkbox);
129
+ }
130
+ appendSeededTemplate(rowData, rowContainer) {
131
+ this.template.childNodes.forEach(child => {
132
+ const clone = child.cloneNode(false);
133
+ if (clone.getAttribute("data-fields")) {
134
+ const fields = clone.getAttribute("data-fields").replace(/\s/g, "").split(",");
135
+ const filteredRowData = Object.fromEntries(fields.map((field) => {
136
+ return [field, rowData[field]];
137
+ }));
138
+ clone.setAttribute("row-data", JSON.stringify(filteredRowData));
139
+ }
140
+ else {
141
+ clone.setAttribute("row-data", JSON.stringify(rowData));
142
+ }
143
+ rowContainer.appendChild(clone);
144
+ });
145
+ }
146
+ appendGenericTemplate(rowData, rowContainer) {
147
+ const row = document.createElement("div");
148
+ row.classList.add("generic-template__content");
149
+ row.textContent = rowData.text;
150
+ rowContainer.appendChild(row);
151
+ if (!rowData.icon)
152
+ return;
153
+ const icon = document.createElement("span");
154
+ icon.classList.add("material-symbols-rounded");
155
+ icon.textContent = rowData.icon;
156
+ if (rowData.iconColor)
157
+ icon.style["color"] = rowData.iconColor;
158
+ row.prepend(icon);
159
+ }
160
+ row(rowData) {
161
+ const rowContainer = document.createElement("div");
162
+ rowContainer.classList.add("tttx-list__row");
163
+ rowContainer.addEventListener("click", () => {
164
+ this.listItemClickHandler(rowData);
165
+ });
166
+ this.appendRowCheckbox(rowContainer);
167
+ this.template ? this.appendSeededTemplate(rowData, rowContainer) : this.appendGenericTemplate(rowData, rowContainer);
168
+ return rowContainer;
169
+ }
170
+ renderRows(batchData) {
171
+ batchData.forEach(rowData => {
172
+ this.listItemContainer().appendChild(this.row(rowData));
173
+ });
174
+ }
175
+ loadIndicator() {
176
+ if (!this.loading)
177
+ return;
178
+ return h("div", { class: "load-indicator" }, h("tttx-loading-spinner", { size: 'large' }));
179
+ }
180
+ render() {
181
+ return (h(Host, null, h("div", { class: "list-item-container", tabindex: "0" }, !this.loading && this.items.length === 0 ? "No data to display" : undefined), this.loadIndicator()));
182
+ }
183
+ static get is() { return "tttx-list"; }
184
+ static get encapsulation() { return "shadow"; }
185
+ static get originalStyleUrls() {
186
+ return {
187
+ "$": ["tttx-list.scss"]
188
+ };
189
+ }
190
+ static get styleUrls() {
191
+ return {
192
+ "$": ["tttx-list.css"]
193
+ };
194
+ }
195
+ static get properties() {
196
+ return {
197
+ "name": {
198
+ "type": "string",
199
+ "mutable": false,
200
+ "complexType": {
201
+ "original": "string",
202
+ "resolved": "string",
203
+ "references": {}
204
+ },
205
+ "required": false,
206
+ "optional": false,
207
+ "docs": {
208
+ "tags": [],
209
+ "text": ""
210
+ },
211
+ "attribute": "name",
212
+ "reflect": false
213
+ },
214
+ "selectable": {
215
+ "type": "boolean",
216
+ "mutable": false,
217
+ "complexType": {
218
+ "original": "boolean",
219
+ "resolved": "boolean",
220
+ "references": {}
221
+ },
222
+ "required": false,
223
+ "optional": false,
224
+ "docs": {
225
+ "tags": [],
226
+ "text": ""
227
+ },
228
+ "attribute": "selectable",
229
+ "reflect": false
230
+ }
231
+ };
232
+ }
233
+ static get states() {
234
+ return {
235
+ "items": {},
236
+ "selectedIds": {},
237
+ "loading": {},
238
+ "lastPage": {}
239
+ };
240
+ }
241
+ static get events() {
242
+ return [{
243
+ "method": "listPaginate",
244
+ "name": "listPaginate",
245
+ "bubbles": true,
246
+ "cancelable": true,
247
+ "composed": true,
248
+ "docs": {
249
+ "tags": [],
250
+ "text": ""
251
+ },
252
+ "complexType": {
253
+ "original": "any",
254
+ "resolved": "any",
255
+ "references": {}
256
+ }
257
+ }, {
258
+ "method": "listSelectedEvent",
259
+ "name": "listSelectedEvent",
260
+ "bubbles": true,
261
+ "cancelable": true,
262
+ "composed": true,
263
+ "docs": {
264
+ "tags": [],
265
+ "text": ""
266
+ },
267
+ "complexType": {
268
+ "original": "any",
269
+ "resolved": "any",
270
+ "references": {}
271
+ }
272
+ }, {
273
+ "method": "listItemClick",
274
+ "name": "listItemClick",
275
+ "bubbles": true,
276
+ "cancelable": true,
277
+ "composed": true,
278
+ "docs": {
279
+ "tags": [],
280
+ "text": ""
281
+ },
282
+ "complexType": {
283
+ "original": "any",
284
+ "resolved": "any",
285
+ "references": {}
286
+ }
287
+ }];
288
+ }
289
+ static get elementRef() { return "host"; }
290
+ static get listeners() {
291
+ return [{
292
+ "name": "listPageLoad",
293
+ "method": "listLoadHandler",
294
+ "target": "document",
295
+ "capture": false,
296
+ "passive": false
297
+ }, {
298
+ "name": "listClearDataCache",
299
+ "method": "listClearDataCacheHandler",
300
+ "target": "document",
301
+ "capture": false,
302
+ "passive": false
303
+ }, {
304
+ "name": "listActionSelectedEvent",
305
+ "method": "listActionSelectedEventHandler",
306
+ "target": "document",
307
+ "capture": false,
308
+ "passive": false
309
+ }];
310
+ }
311
+ }
@@ -0,0 +1,37 @@
1
+ export default {
2
+ title: "molecules/List",
3
+ component: "tttx-list",
4
+ argTypes: {
5
+ selectable: {
6
+ control: {
7
+ type: "boolean"
8
+ }
9
+ }
10
+ }
11
+ };
12
+ export const ListWithGenericTemplate = (args) => {
13
+ return `
14
+ <script>
15
+ function loadPage (event) {
16
+ const page = new CustomEvent('listPageLoad', {
17
+ detail: {
18
+ name: 'listWithGenericTemplate',
19
+ lastPage: true,
20
+ items: [
21
+ { text: 'Row with only text' },
22
+ { text: 'Row with text and an icon', icon: 'sentiment_calm' },
23
+ { text: 'Row with text and a coloured icon', icon: 'check_circle', iconColor: 'blue' },
24
+ ]
25
+ }
26
+ });
27
+ document.dispatchEvent(page);
28
+
29
+ // Stop this event handler from being duplicated every time the storybook iframe is reloaded
30
+ document.removeEventListener("listPaginate", loadPage);
31
+ }
32
+ document.addEventListener("listPaginate", loadPage);
33
+ </script>
34
+
35
+ <tttx-list ${args.selectable ? "selectable" : ""} name='listWithGenericTemplate' />
36
+ `;
37
+ };