@schukai/monster 4.128.0 → 4.128.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.
@@ -3,6 +3,8 @@ div[data-monster-role=popper] {
3
3
  box-sizing: border-box;
4
4
  display: none;
5
5
  padding: 1.1em;
6
+ max-width: var(--monster-popper-max-width, calc(100vw - 2rem));
7
+ max-height: var(--monster-popper-max-height, calc(100vh - 2rem));
6
8
  z-index: var(--monster-z-index-modal);
7
9
  background: var(--monster-bg-color-primary-1);
8
10
  color: var(--monster-color-primary-1);
@@ -21,6 +23,12 @@ div[data-monster-role=popper] {
21
23
  top: 0;
22
24
  left: 0;
23
25
 
26
+ & > [part=content] {
27
+ overflow: auto;
28
+ max-width: 100%;
29
+ max-height: var(--monster-popper-content-max-height, calc(100vh - 4.2rem));
30
+ }
31
+
24
32
  & div[data-monster-role=arrow] {
25
33
  position: absolute;
26
34
  width: calc(2 * max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance)));
@@ -25,7 +25,7 @@ try {
25
25
  FloatingUiStyleSheet.insertRule(
26
26
  `
27
27
  @layer floatingui {
28
- div[data-monster-role=popper]{align-content:center;background:var(--monster-bg-color-primary-1);border-color:var(--monster-bg-color-primary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);box-shadow:var(--monster-box-shadow-1);box-sizing:border-box;color:var(--monster-color-primary-1);display:none;justify-content:space-between;left:0;padding:1.1em;position:absolute;top:0;width:-moz-max-content;width:max-content;z-index:var(--monster-z-index-modal)}div[data-monster-role=popper] div[data-monster-role=arrow]{background:var(--monster-bg-color-primary-1);height:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);pointer-events:none;position:absolute;width:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);z-index:-1}
28
+ div[data-monster-role=popper]{align-content:center;background:var(--monster-bg-color-primary-1);border-color:var(--monster-bg-color-primary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);box-shadow:var(--monster-box-shadow-1);box-sizing:border-box;color:var(--monster-color-primary-1);display:none;justify-content:space-between;left:0;max-height:var(--monster-popper-max-height,calc(100vh - 2rem));max-width:var(--monster-popper-max-width,calc(100vw - 2rem));padding:1.1em;position:absolute;top:0;width:-moz-max-content;width:max-content;z-index:var(--monster-z-index-modal)}div[data-monster-role=popper]>[part=content]{max-height:var(--monster-popper-content-max-height,calc(100vh - 4.2rem));max-width:100%;overflow:auto}div[data-monster-role=popper] div[data-monster-role=arrow]{background:var(--monster-bg-color-primary-1);height:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);pointer-events:none;position:absolute;width:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);z-index:-1}
29
29
  }`,
30
30
  0,
31
31
  );
@@ -932,33 +932,33 @@ function attachChildMutationObserver() {
932
932
  return;
933
933
  }
934
934
 
935
- self[childMutationTimerSymbol] = setTimeout(() => {
936
- self[childMutationTimerSymbol] = null;
937
- if (!self.isConnected) {
938
- return;
939
- }
935
+ self[childMutationTimerSymbol] = setTimeout(() => {
936
+ self[childMutationTimerSymbol] = null;
937
+ if (!self.isConnected) {
938
+ return;
939
+ }
940
940
 
941
- if (!hasObjectLink(self, customElementUpdaterLinkSymbol)) {
942
- return;
943
- }
941
+ if (!hasObjectLink(self, customElementUpdaterLinkSymbol)) {
942
+ return;
943
+ }
944
+
945
+ const updaters = getLinkedObjects(self, customElementUpdaterLinkSymbol);
946
+ for (const list of updaters) {
947
+ for (const updater of list) {
948
+ if (
949
+ typeof updater?.isDisposed === "function" &&
950
+ updater.isDisposed() === true
951
+ ) {
952
+ continue;
953
+ }
954
+
955
+ if (!updater?.[internalSymbol]?.element?.isConnected) {
956
+ continue;
957
+ }
944
958
 
945
- const updaters = getLinkedObjects(self, customElementUpdaterLinkSymbol);
946
- for (const list of updaters) {
947
- for (const updater of list) {
948
- if (
949
- typeof updater?.isDisposed === "function" &&
950
- updater.isDisposed() === true
951
- ) {
952
- continue;
953
- }
954
-
955
- if (!updater?.[internalSymbol]?.element?.isConnected) {
956
- continue;
957
- }
958
-
959
- updater.run().catch((e) => {
960
- addErrorAttribute(self, e);
961
- });
959
+ updater.run().catch((e) => {
960
+ addErrorAttribute(self, e);
961
+ });
962
962
  }
963
963
  }
964
964
  }, 50);
@@ -161,15 +161,15 @@ class Updater extends Base {
161
161
  return Promise.resolve();
162
162
  }
163
163
 
164
- const real = this[internalSymbol].subject.getRealSubject();
165
- const diffResult = diff(this[internalSymbol].last, real);
166
- this[internalSymbol].last = clone(real);
167
- if (diffResult.length === 0) {
168
- return Promise.resolve();
169
- }
170
- this[pendingDiffsSymbol].push(diffResult);
171
- return this[processQueueSymbol]();
172
- });
164
+ const real = this[internalSymbol].subject.getRealSubject();
165
+ const diffResult = diff(this[internalSymbol].last, real);
166
+ this[internalSymbol].last = clone(real);
167
+ if (diffResult.length === 0) {
168
+ return Promise.resolve();
169
+ }
170
+ this[pendingDiffsSymbol].push(diffResult);
171
+ return this[processQueueSymbol]();
172
+ });
173
173
  this[internalSymbol].subject.attachObserver(this[subjectObserverSymbol]);
174
174
  }
175
175
 
@@ -464,7 +464,10 @@ function getControlEventHandler() {
464
464
  * @param {Event} event
465
465
  */
466
466
  this[symbol] = (event) => {
467
- if (this[disposedSymbol] === true || !this[internalSymbol].element?.isConnected) {
467
+ if (
468
+ this[disposedSymbol] === true ||
469
+ !this[internalSymbol].element?.isConnected
470
+ ) {
468
471
  return;
469
472
  }
470
473
 
@@ -857,16 +860,16 @@ function insertElement(change) {
857
860
  );
858
861
 
859
862
  for (const [, node] of Object.entries(nodes)) {
860
- if (
861
- !available.has(
862
- node.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE),
863
- )
864
- ) {
865
- try {
866
- teardownManagedSubtree(node);
867
- containerElement.removeChild(node);
868
- } catch (e) {
869
- addErrorAttribute(containerElement, e);
863
+ if (
864
+ !available.has(
865
+ node.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE),
866
+ )
867
+ ) {
868
+ try {
869
+ teardownManagedSubtree(node);
870
+ containerElement.removeChild(node);
871
+ } catch (e) {
872
+ addErrorAttribute(containerElement, e);
870
873
  }
871
874
  }
872
875
  }
@@ -1285,7 +1288,8 @@ function preparePatchedNode(node, value) {
1285
1288
  typeof value === "number" ||
1286
1289
  typeof value === "boolean"
1287
1290
  ) {
1288
- node.textContent = value === null || value === undefined ? "" : String(value);
1291
+ node.textContent =
1292
+ value === null || value === undefined ? "" : String(value);
1289
1293
  return node;
1290
1294
  }
1291
1295
 
@@ -0,0 +1,258 @@
1
+ import * as chai from "chai";
2
+ import { chaiDom } from "../../../util/chai-dom.mjs";
3
+ import { initJSDOM } from "../../../util/jsdom.mjs";
4
+
5
+ const expect = chai.expect;
6
+ chai.use(chaiDom);
7
+
8
+ function dispatchPointerEvent(target, type, options = {}) {
9
+ const event = new Event(type, {
10
+ bubbles: true,
11
+ cancelable: true,
12
+ composed: true,
13
+ });
14
+
15
+ const values = {
16
+ button: 0,
17
+ isPrimary: true,
18
+ pointerId: 1,
19
+ clientX: 0,
20
+ clientY: 0,
21
+ ...options,
22
+ };
23
+
24
+ for (const [key, value] of Object.entries(values)) {
25
+ Object.defineProperty(event, key, {
26
+ configurable: true,
27
+ value,
28
+ });
29
+ }
30
+
31
+ target.dispatchEvent(event);
32
+ return event;
33
+ }
34
+
35
+ describe("Datatable drag scroll", function () {
36
+ before(async function () {
37
+ await initJSDOM();
38
+ await import("element-internals-polyfill").catch(() => {});
39
+ await import("../../../../source/components/datatable/datatable.mjs");
40
+ });
41
+
42
+ beforeEach(() => {
43
+ document.getElementById("mocks").innerHTML = "";
44
+ });
45
+
46
+ it("drags horizontally on non-interactive cells and suppresses the follow-up click", async function () {
47
+ const mocks = document.getElementById("mocks");
48
+ const datatable = document.createElement("monster-datatable");
49
+ datatable.id = "drag-scroll-table";
50
+ datatable.innerHTML = `
51
+ <template id="drag-scroll-table-row">
52
+ <div data-monster-head="Name">Alpha</div>
53
+ <div data-monster-head="Value">Beta</div>
54
+ </template>
55
+ `;
56
+ datatable.setOption("data", [{}, {}]);
57
+ mocks.appendChild(datatable);
58
+
59
+ await new Promise((resolve) => setTimeout(resolve, 30));
60
+
61
+ const scroll = datatable.shadowRoot.querySelector(
62
+ "[data-monster-role=table-scroll]",
63
+ );
64
+ const cell = datatable.shadowRoot.querySelector(
65
+ "[data-monster-role=datatable] > div",
66
+ );
67
+
68
+ expect(scroll).to.exist;
69
+ expect(cell).to.exist;
70
+
71
+ Object.defineProperty(scroll, "clientWidth", {
72
+ configurable: true,
73
+ value: 200,
74
+ });
75
+ Object.defineProperty(scroll, "scrollWidth", {
76
+ configurable: true,
77
+ value: 600,
78
+ });
79
+
80
+ scroll.scrollLeft = 120;
81
+
82
+ let clickCount = 0;
83
+ cell.addEventListener("click", () => {
84
+ clickCount += 1;
85
+ });
86
+
87
+ dispatchPointerEvent(cell, "pointerdown", {
88
+ clientX: 100,
89
+ clientY: 20,
90
+ });
91
+ dispatchPointerEvent(window, "pointermove", {
92
+ clientX: 40,
93
+ clientY: 24,
94
+ });
95
+ dispatchPointerEvent(window, "pointerup", {
96
+ clientX: 40,
97
+ clientY: 24,
98
+ });
99
+
100
+ expect(scroll.scrollLeft).to.equal(180);
101
+ expect(scroll.classList.contains("is-dragging")).to.equal(false);
102
+
103
+ cell.dispatchEvent(
104
+ new window.MouseEvent("click", {
105
+ bubbles: true,
106
+ cancelable: true,
107
+ composed: true,
108
+ }),
109
+ );
110
+
111
+ expect(clickCount).to.equal(0);
112
+ });
113
+
114
+ it("does not break clicks on embedded controls", async function () {
115
+ const mocks = document.getElementById("mocks");
116
+ const datatable = document.createElement("monster-datatable");
117
+ datatable.id = "drag-scroll-button-table";
118
+ datatable.innerHTML = `
119
+ <template id="drag-scroll-button-table-row">
120
+ <div data-monster-head="Name">Alpha</div>
121
+ <div data-monster-head="Action"><button type="button">Open</button></div>
122
+ </template>
123
+ `;
124
+ datatable.setOption("data", [{}]);
125
+ mocks.appendChild(datatable);
126
+
127
+ await new Promise((resolve) => setTimeout(resolve, 30));
128
+
129
+ const scroll = datatable.shadowRoot.querySelector(
130
+ "[data-monster-role=table-scroll]",
131
+ );
132
+ const button = datatable.shadowRoot.querySelector("button");
133
+
134
+ expect(scroll).to.exist;
135
+ expect(button).to.exist;
136
+
137
+ Object.defineProperty(scroll, "clientWidth", {
138
+ configurable: true,
139
+ value: 200,
140
+ });
141
+ Object.defineProperty(scroll, "scrollWidth", {
142
+ configurable: true,
143
+ value: 600,
144
+ });
145
+
146
+ scroll.scrollLeft = 120;
147
+
148
+ let clickCount = 0;
149
+ button.addEventListener("click", () => {
150
+ clickCount += 1;
151
+ });
152
+
153
+ dispatchPointerEvent(button, "pointerdown", {
154
+ clientX: 100,
155
+ clientY: 20,
156
+ });
157
+ dispatchPointerEvent(window, "pointermove", {
158
+ clientX: 30,
159
+ clientY: 22,
160
+ });
161
+ dispatchPointerEvent(window, "pointerup", {
162
+ clientX: 30,
163
+ clientY: 22,
164
+ });
165
+
166
+ button.dispatchEvent(
167
+ new window.MouseEvent("click", {
168
+ bubbles: true,
169
+ cancelable: true,
170
+ composed: true,
171
+ }),
172
+ );
173
+
174
+ expect(scroll.scrollLeft).to.equal(120);
175
+ expect(clickCount).to.equal(1);
176
+ });
177
+
178
+ it("keeps double click copy for a single cell", async function () {
179
+ const mocks = document.getElementById("mocks");
180
+ const datatable = document.createElement("monster-datatable");
181
+ datatable.id = "copy-cell-table";
182
+ datatable.innerHTML = `
183
+ <template id="copy-cell-table-row">
184
+ <div data-monster-head="Name">Alpha</div>
185
+ <div data-monster-head="Value">Beta</div>
186
+ </template>
187
+ `;
188
+ datatable.setOption("data", [{}]);
189
+ mocks.appendChild(datatable);
190
+
191
+ await new Promise((resolve) => setTimeout(resolve, 30));
192
+
193
+ const copied = [];
194
+ Object.defineProperty(window.navigator, "clipboard", {
195
+ configurable: true,
196
+ value: {
197
+ writeText: async (text) => {
198
+ copied.push(text);
199
+ },
200
+ },
201
+ });
202
+
203
+ const cell = datatable.shadowRoot.querySelector(
204
+ "[data-monster-role=datatable] > div",
205
+ );
206
+ cell.dispatchEvent(
207
+ new window.MouseEvent("dblclick", {
208
+ bubbles: true,
209
+ cancelable: true,
210
+ composed: true,
211
+ }),
212
+ );
213
+
214
+ await new Promise((resolve) => setTimeout(resolve, 0));
215
+ expect(copied).deep.equal(["Alpha"]);
216
+ });
217
+
218
+ it("copies the whole row on shift click", async function () {
219
+ const mocks = document.getElementById("mocks");
220
+ const datatable = document.createElement("monster-datatable");
221
+ datatable.id = "copy-row-table";
222
+ datatable.innerHTML = `
223
+ <template id="copy-row-table-row">
224
+ <div data-monster-head="Name">Alpha</div>
225
+ <div data-monster-head="Value">Beta</div>
226
+ </template>
227
+ `;
228
+ datatable.setOption("data", [{}]);
229
+ mocks.appendChild(datatable);
230
+
231
+ await new Promise((resolve) => setTimeout(resolve, 30));
232
+
233
+ const copied = [];
234
+ Object.defineProperty(window.navigator, "clipboard", {
235
+ configurable: true,
236
+ value: {
237
+ writeText: async (text) => {
238
+ copied.push(text);
239
+ },
240
+ },
241
+ });
242
+
243
+ const cell = datatable.shadowRoot.querySelector(
244
+ "[data-monster-role=datatable] > div",
245
+ );
246
+ cell.dispatchEvent(
247
+ new window.MouseEvent("click", {
248
+ bubbles: true,
249
+ cancelable: true,
250
+ composed: true,
251
+ shiftKey: true,
252
+ }),
253
+ );
254
+
255
+ await new Promise((resolve) => setTimeout(resolve, 0));
256
+ expect(copied).deep.equal(['"Alpha";"Beta"']);
257
+ });
258
+ });