@nanoporetech-digital/components 4.9.4 → 4.10.0

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 (147) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/drag-777bd8dd.js +74 -0
  3. package/dist/cjs/drag-777bd8dd.js.map +1 -0
  4. package/dist/cjs/index-71f899a7.js +4 -0
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/nano-components.cjs.js +1 -1
  7. package/dist/cjs/nano-global-nav-user-profile_3.cjs.entry.js +4 -4
  8. package/dist/cjs/nano-global-nav-user-profile_3.cjs.entry.js.map +1 -1
  9. package/dist/cjs/nano-icon-button_2.cjs.entry.js +40 -3
  10. package/dist/cjs/nano-icon-button_2.cjs.entry.js.map +1 -1
  11. package/dist/cjs/nano-sortable.cjs.entry.js +653 -0
  12. package/dist/cjs/nano-sortable.cjs.entry.js.map +1 -0
  13. package/dist/cjs/nano-split-pane.cjs.entry.js +30 -45
  14. package/dist/cjs/nano-split-pane.cjs.entry.js.map +1 -1
  15. package/dist/cjs/nano-tab-group.cjs.entry.js +39 -43
  16. package/dist/cjs/nano-tab-group.cjs.entry.js.map +1 -1
  17. package/dist/cjs/nano-tab.cjs.entry.js +3 -3
  18. package/dist/cjs/nano-tab.cjs.entry.js.map +1 -1
  19. package/dist/cjs/{nano-table-54a4ba34.js → nano-table-ff33dc43.js} +20 -147
  20. package/dist/cjs/nano-table-ff33dc43.js.map +1 -0
  21. package/dist/cjs/nano-table.cjs.entry.js +1 -1
  22. package/dist/cjs/{table.worker-20ed37a5.js → table.worker-0a6bc962.js} +3 -3
  23. package/dist/cjs/table.worker-0a6bc962.js.map +1 -0
  24. package/dist/cjs/{table.worker-f820b411.js → table.worker-bd51e29f.js} +1 -1
  25. package/dist/collection/collection-manifest.json +1 -0
  26. package/dist/collection/components/icon-button/icon-button.css +6 -4
  27. package/dist/collection/components/icon-button/icon-button.js +83 -4
  28. package/dist/collection/components/icon-button/icon-button.js.map +1 -1
  29. package/dist/collection/components/nav-item/nav-item.js +4 -4
  30. package/dist/collection/components/nav-item/nav-item.js.map +1 -1
  31. package/dist/collection/components/sortable/sortable.css +28 -0
  32. package/dist/collection/components/sortable/sortable.js +1180 -0
  33. package/dist/collection/components/sortable/sortable.js.map +1 -0
  34. package/dist/collection/components/split-pane/split-pane.js +29 -27
  35. package/dist/collection/components/split-pane/split-pane.js.map +1 -1
  36. package/dist/collection/components/table/table-interface.js.map +1 -1
  37. package/dist/collection/components/table/table.css +12 -38
  38. package/dist/collection/components/table/table.header.js +3 -86
  39. package/dist/collection/components/table/table.header.js.map +1 -1
  40. package/dist/collection/components/table/table.js +4 -92
  41. package/dist/collection/components/table/table.js.map +1 -1
  42. package/dist/collection/components/table/table.store.js +1 -1
  43. package/dist/collection/components/table/table.store.js.map +1 -1
  44. package/dist/collection/components/table/table.worker.js +3 -3
  45. package/dist/collection/components/table/table.worker.js.map +1 -1
  46. package/dist/collection/components/tabs/tab-group.css +9 -13
  47. package/dist/collection/components/tabs/tab-group.js +39 -43
  48. package/dist/collection/components/tabs/tab-group.js.map +1 -1
  49. package/dist/collection/components/tabs/tab.css +53 -14
  50. package/dist/collection/components/tabs/tab.js +8 -2
  51. package/dist/collection/components/tabs/tab.js.map +1 -1
  52. package/dist/collection/utils/drag.js +52 -4
  53. package/dist/collection/utils/drag.js.map +1 -1
  54. package/dist/components/drag.js +72 -0
  55. package/dist/components/drag.js.map +1 -0
  56. package/dist/components/icon-button.js +45 -5
  57. package/dist/components/icon-button.js.map +1 -1
  58. package/dist/components/index.d.ts +1 -0
  59. package/dist/components/index.js +1 -0
  60. package/dist/components/index.js.map +1 -1
  61. package/dist/components/nano-sortable.d.ts +11 -0
  62. package/dist/components/nano-sortable.js +691 -0
  63. package/dist/components/nano-sortable.js.map +1 -0
  64. package/dist/components/nano-split-pane.js +30 -45
  65. package/dist/components/nano-split-pane.js.map +1 -1
  66. package/dist/components/nano-tab-group.js +40 -44
  67. package/dist/components/nano-tab-group.js.map +1 -1
  68. package/dist/components/nano-tab.js +3 -3
  69. package/dist/components/nano-tab.js.map +1 -1
  70. package/dist/components/nav-item.js +4 -4
  71. package/dist/components/nav-item.js.map +1 -1
  72. package/dist/components/table.js +19 -147
  73. package/dist/components/table.js.map +1 -1
  74. package/dist/components/table.worker.js +1 -1
  75. package/dist/esm/drag-1723a4cc.js +72 -0
  76. package/dist/esm/drag-1723a4cc.js.map +1 -0
  77. package/dist/esm/index-dad5627b.js +4 -0
  78. package/dist/esm/loader.js +1 -1
  79. package/dist/esm/nano-components.js +1 -1
  80. package/dist/esm/nano-global-nav-user-profile_3.entry.js +4 -4
  81. package/dist/esm/nano-global-nav-user-profile_3.entry.js.map +1 -1
  82. package/dist/esm/nano-icon-button_2.entry.js +41 -4
  83. package/dist/esm/nano-icon-button_2.entry.js.map +1 -1
  84. package/dist/esm/nano-sortable.entry.js +649 -0
  85. package/dist/esm/nano-sortable.entry.js.map +1 -0
  86. package/dist/esm/nano-split-pane.entry.js +30 -45
  87. package/dist/esm/nano-split-pane.entry.js.map +1 -1
  88. package/dist/esm/nano-tab-group.entry.js +39 -43
  89. package/dist/esm/nano-tab-group.entry.js.map +1 -1
  90. package/dist/esm/nano-tab.entry.js +3 -3
  91. package/dist/esm/nano-tab.entry.js.map +1 -1
  92. package/dist/esm/{nano-table-929ac4d9.js → nano-table-ec980076.js} +21 -148
  93. package/dist/esm/nano-table-ec980076.js.map +1 -0
  94. package/dist/esm/nano-table.entry.js +1 -1
  95. package/dist/esm/{table.worker-2425382a.js → table.worker-b53db58e.js} +3 -3
  96. package/dist/esm/table.worker-b53db58e.js.map +1 -0
  97. package/dist/esm/{table.worker-f820b411.js → table.worker-bd51e29f.js} +1 -1
  98. package/dist/nano-components/nano-components.css +1 -1
  99. package/dist/nano-components/nano-components.esm.js +1 -1
  100. package/dist/nano-components/nano-components.esm.js.map +1 -1
  101. package/dist/nano-components/p-064af7d0.js +5 -0
  102. package/dist/nano-components/p-064af7d0.js.map +1 -0
  103. package/dist/nano-components/p-241baff8.entry.js +5 -0
  104. package/dist/nano-components/p-241baff8.entry.js.map +1 -0
  105. package/dist/nano-components/p-58b53239.entry.js.map +1 -1
  106. package/dist/nano-components/{p-5381c118.js → p-806bcd46.js} +2 -2
  107. package/dist/nano-components/p-842cf127.js +5 -0
  108. package/dist/nano-components/p-842cf127.js.map +1 -0
  109. package/dist/nano-components/p-b8e76fdf.entry.js +5 -0
  110. package/dist/nano-components/p-b8e76fdf.entry.js.map +1 -0
  111. package/dist/nano-components/{p-f820b411.js → p-bd51e29f.js} +1 -1
  112. package/dist/nano-components/p-d3de231c.entry.js +5 -0
  113. package/dist/nano-components/p-d3de231c.entry.js.map +1 -0
  114. package/dist/nano-components/{p-906de5a2.entry.js → p-f591400b.entry.js} +2 -2
  115. package/dist/nano-components/p-f60fe933.entry.js +5 -0
  116. package/dist/nano-components/p-f60fe933.entry.js.map +1 -0
  117. package/dist/nano-components/p-f7535f45.entry.js +5 -0
  118. package/dist/nano-components/p-f7535f45.entry.js.map +1 -0
  119. package/dist/types/components/icon-button/icon-button.d.ts +14 -0
  120. package/dist/types/components/sortable/sortable.d.ts +204 -0
  121. package/dist/types/components/table/table-interface.d.ts +2 -4
  122. package/dist/types/components/table/table.d.ts +0 -30
  123. package/dist/types/components/table/table.header.d.ts +0 -3
  124. package/dist/types/components/tabs/tab-group.d.ts +0 -1
  125. package/dist/types/components/tabs/tab.d.ts +6 -0
  126. package/dist/types/components.d.ts +248 -28
  127. package/dist/types/utils/drag.d.ts +21 -1
  128. package/docs-json.json +562 -46
  129. package/docs-vscode.json +74 -5
  130. package/hydrate/index.js +875 -251
  131. package/package.json +2 -2
  132. package/dist/cjs/nano-table-54a4ba34.js.map +0 -1
  133. package/dist/cjs/table.worker-20ed37a5.js.map +0 -1
  134. package/dist/esm/nano-table-929ac4d9.js.map +0 -1
  135. package/dist/esm/table.worker-2425382a.js.map +0 -1
  136. package/dist/nano-components/p-068bdd89.entry.js +0 -5
  137. package/dist/nano-components/p-068bdd89.entry.js.map +0 -1
  138. package/dist/nano-components/p-4f260028.js +0 -5
  139. package/dist/nano-components/p-4f260028.js.map +0 -1
  140. package/dist/nano-components/p-64b56ee6.entry.js +0 -5
  141. package/dist/nano-components/p-64b56ee6.entry.js.map +0 -1
  142. package/dist/nano-components/p-a5a560e7.entry.js +0 -5
  143. package/dist/nano-components/p-a5a560e7.entry.js.map +0 -1
  144. package/dist/nano-components/p-a761ac89.entry.js +0 -5
  145. package/dist/nano-components/p-a761ac89.entry.js.map +0 -1
  146. /package/dist/nano-components/{p-5381c118.js.map → p-806bcd46.js.map} +0 -0
  147. /package/dist/nano-components/{p-906de5a2.entry.js.map → p-f591400b.entry.js.map} +0 -0
@@ -0,0 +1,653 @@
1
+ /*!
2
+ * Web Components for Nanopore digital Web Apps
3
+ */
4
+ 'use strict';
5
+
6
+ Object.defineProperty(exports, '__esModule', { value: true });
7
+
8
+ const index = require('./index-71f899a7.js');
9
+ const drag = require('./drag-777bd8dd.js');
10
+ const throttle = require('./throttle-f55496c8.js');
11
+
12
+ const sortableCss = ":host{box-sizing:border-box}*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}:host{position:relative;display:block}.sortable__live-region{clip:rect(1px, 1px, 1px, 1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);block-size:1px;inline-size:1px;margin:-1px;overflow:hidden;padding:0;position:absolute}";
13
+
14
+ // Orientation map to limit dragging to horizontal or vertical.
15
+ const orientationMap = {
16
+ horizontal: { x: 1, y: 0 },
17
+ vertical: { x: 0, y: 1 },
18
+ };
19
+ let sortableIds = 0;
20
+ const Sortable = class {
21
+ handleItemSelectorChange() {
22
+ this.refreshKeyboardHandles();
23
+ }
24
+ handleHandleSelectorChange() {
25
+ this.refreshKeyboardHandles();
26
+ this.attachMutationObserver();
27
+ }
28
+ handleCreateKeyboardHandleChange() {
29
+ this.refreshKeyboardHandles();
30
+ }
31
+ handleSortableHostElement(_newVal, oldVal) {
32
+ if (oldVal)
33
+ this.removeEventHandlers(oldVal);
34
+ this.addEventHandlers();
35
+ this.refreshKeyboardHandles();
36
+ this.attachMutationObserver();
37
+ if (this.sortableHostElement) {
38
+ // sortable host must have position relative
39
+ this.sortableHostElement.style.position = 'relative';
40
+ }
41
+ }
42
+ /** If sortable elements change dynamically, use this method to add handle controls to new elements */
43
+ async refreshKeyboardHandles() {
44
+ var _a, _b, _c;
45
+ if (this.handleSelector) {
46
+ if ((_a = this.keyboardHandleMap) === null || _a === void 0 ? void 0 : _a.size) {
47
+ this.keyboardHandleMap.clear();
48
+ }
49
+ this.sortableHost
50
+ .querySelectorAll(this.handleSelector)
51
+ .forEach((handle) => {
52
+ if (!handle.getAttribute('aria-describedby'))
53
+ handle.setAttribute('aria-describedby', this.sortableId);
54
+ const sortEle = handle.closest(this.itemSelector);
55
+ if (sortEle)
56
+ this.keyboardHandleMap.set(handle, sortEle);
57
+ });
58
+ return;
59
+ }
60
+ if ((_b = this.keyboardHandleMap) === null || _b === void 0 ? void 0 : _b.size) {
61
+ (_c = this.keyboardHandleMap) === null || _c === void 0 ? void 0 : _c.forEach((_ele, handle) => handle.remove());
62
+ this.keyboardHandleMap.clear();
63
+ }
64
+ this.sortableHost
65
+ .querySelectorAll(this.itemSelector)
66
+ .forEach((ele, i) => {
67
+ const handle = this.createKeyboardHandle(i, ele);
68
+ if (!handle) {
69
+ console.error('`createKeyboardHandle` *must* return the handle element it creates');
70
+ return;
71
+ }
72
+ this.keyboardHandleMap.set(handle, ele);
73
+ handle.setAttribute('aria-describedby', this.sortableId);
74
+ });
75
+ }
76
+ /**
77
+ * Get instance of sortable host.
78
+ * By default it is `nano-sortable` which can be overridden by property `sortableHostElement`
79
+ */
80
+ get sortableHost() {
81
+ if (this.sortableHostElement)
82
+ return this.sortableHostElement;
83
+ return this.host;
84
+ }
85
+ /**
86
+ * Queues aria messages which are then displayed in a 'live' region.
87
+ * Messages are cleared after 10s
88
+ * @param msg aria message to announce
89
+ */
90
+ addAriaMsg(msg) {
91
+ this.ariaTextList = [...this.ariaTextList, msg];
92
+ setTimeout(() => {
93
+ const mIdx = this.ariaTextList.indexOf(msg);
94
+ this.ariaTextList.splice(mIdx, 1);
95
+ this.ariaTextList = [...this.ariaTextList];
96
+ }, 10000);
97
+ }
98
+ /**
99
+ * Try to stop text highlight whilst dragging
100
+ * @param userSelect
101
+ */
102
+ updateUserSelectStyle(userSelect) {
103
+ this.host.style.userSelect = userSelect;
104
+ // @ts-ignore
105
+ this.host.style.MozUserSelect = userSelect;
106
+ // @ts-ignore
107
+ this.host.style.msUserSelect = userSelect;
108
+ this.host.style.webkitUserSelect = userSelect;
109
+ }
110
+ /**
111
+ * Fast and simple hit test to check whether the center of a node intersects with the rectangle of any of the
112
+ * given targets. Returns an array of matches.
113
+ * @param node
114
+ * @param targets
115
+ * @returns all the items that currently intersect with the target node
116
+ */
117
+ hitTest(node, targets) {
118
+ const { left: l, top: t, width: w, height: h, } = node.getBoundingClientRect();
119
+ const x = l + w / 2;
120
+ const y = t + h / 2;
121
+ return targets.filter((item) => {
122
+ const { left, right, top, bottom } = item.getBoundingClientRect();
123
+ return !(x < left || x > right || y < top || y > bottom);
124
+ });
125
+ }
126
+ /**
127
+ * Returns a boolean indicating whether the node is currently in an animation.
128
+ * @param node
129
+ * @returns whether a node is currently animating or not
130
+ */
131
+ isAnimating(node) {
132
+ return this.animatedElements.indexOf(node) !== -1;
133
+ }
134
+ /**
135
+ * Triggers a CSS animation on a node with the given dx and dy. Used following dom updates to make it appear as
136
+ * if nodes animate from their old to their new position in the dom. */
137
+ animateNode(node, dx = 0, dy = 0) {
138
+ if (!node.animate) {
139
+ return;
140
+ }
141
+ // keep a stack of currently animating nodes to exclude as drag & drop targets.
142
+ this.animatedElements.push(node);
143
+ node.style.willChange = 'transform';
144
+ // animate from dx/dy (old node position) to none (new node position)
145
+ index.writeTask(() => {
146
+ this.animationPromise = new Promise((resolve) => {
147
+ node
148
+ .animate([
149
+ { transform: `translate3d(${dx}px, ${dy}px, 0)` },
150
+ { transform: 'none' },
151
+ ], this.animationTiming)
152
+ .addEventListener('finish', () => {
153
+ const index = this.animatedElements.indexOf(node);
154
+ node.style.willChange = '';
155
+ if (index !== -1) {
156
+ // splice out when done to unlock as a valid target
157
+ this.animatedElements.splice(index, 1);
158
+ }
159
+ resolve();
160
+ delete this.animationPromise;
161
+ }, { once: true });
162
+ });
163
+ });
164
+ }
165
+ /**
166
+ * Inserts node at target to update sibling sorting. If the node precedes the target, it is inserted after it;
167
+ * If it follows the target, it is inserted before it. This ensures any node can be dragged from the very
168
+ * beginning to the very end and vice versa. The animateNode function is called for all nodes that moved because
169
+ * of this dom update */
170
+ insertAtTarget(node, target) {
171
+ if (!node || !target)
172
+ return;
173
+ let offsets = [];
174
+ if (this.animationEnabled) {
175
+ offsets = this.sortableNodes.map((item) => ({
176
+ x: item.offsetLeft,
177
+ y: item.offsetTop,
178
+ }));
179
+ }
180
+ if (!node.isConnected || !target.isConnected)
181
+ return;
182
+ if (this.dropzoneNodes.indexOf(target) > -1) {
183
+ target.append(node);
184
+ }
185
+ else {
186
+ const nodeComparison = node.compareDocumentPosition(target);
187
+ let position;
188
+ if (nodeComparison & this.host.DOCUMENT_POSITION_FOLLOWING) {
189
+ position =
190
+ target.parentNode === node.parentNode ? 'afterend' : 'beforebegin';
191
+ }
192
+ if (nodeComparison & this.host.DOCUMENT_POSITION_PRECEDING) {
193
+ position =
194
+ target.parentNode === node.parentNode ? 'beforebegin' : 'afterend';
195
+ }
196
+ if (position)
197
+ target.insertAdjacentElement(position, node);
198
+ }
199
+ if (this.animationEnabled) {
200
+ this.sortableNodes.forEach((sortableNode, i) => {
201
+ const { x, y } = offsets[i];
202
+ const dx = x - sortableNode.offsetLeft;
203
+ const dy = y - sortableNode.offsetTop;
204
+ if (dx !== 0 || dy !== 0) {
205
+ this.animateNode(sortableNode, dx, dy);
206
+ }
207
+ });
208
+ }
209
+ }
210
+ reset() {
211
+ if (this.draggedElementClone !== undefined &&
212
+ this.draggedElementClone.parentNode !== null) {
213
+ this.draggedElementClone.parentNode.removeChild(this.draggedElementClone);
214
+ }
215
+ if (this.draggedElement &&
216
+ this.draggedElement.parentNode &&
217
+ this.draggedElementOrigin) {
218
+ this.draggedElement.classList.remove(this.placeholderClass);
219
+ }
220
+ if (this.dropzoneActiveClass && this.dropzoneNodes.length) {
221
+ this.dropzoneNodes.forEach((dze) => dze.classList.remove(this.dropzoneActiveClass));
222
+ }
223
+ delete this.draggedElementClone;
224
+ delete this.draggedElement;
225
+ this.dropzoneNodes = [];
226
+ this.sortableNodes = [];
227
+ this.animatedElements = [];
228
+ this.dragRequestPending = false;
229
+ this.updateUserSelectStyle('');
230
+ }
231
+ /**
232
+ * Clones a given node to visually drag around. The original node is left in the same flow as its siblings.
233
+ * Clone styles are added onto the style object directly, since the ::slotted()
234
+ * selector can't universally target nodes that may be nested an unknown amount of shadow dom levels deep
235
+ * @param node html node to clone
236
+ * @returns the cloned html node
237
+ */
238
+ createClone(node) {
239
+ const clone = node.cloneNode(true);
240
+ if (node.id)
241
+ clone.id = 'clone__' + clone.id;
242
+ /**
243
+ * Bugfix for table row sorting.
244
+ * During dragging table rows shrink, so we manually set them width of original node.
245
+ */
246
+ Array.from(clone.children).forEach((nodeChild, index) => {
247
+ const clonedNodeChild = nodeChild;
248
+ const originalNodeChild = node.children.item(index);
249
+ if (originalNodeChild) {
250
+ clonedNodeChild.style.width = `${originalNodeChild.offsetWidth}px`;
251
+ }
252
+ });
253
+ const { offsetLeft: x, offsetTop: y, offsetWidth: w, offsetHeight: h, } = node;
254
+ Object.assign(clone.style, {
255
+ position: 'absolute',
256
+ left: `calc(${x}px - var(--grab-offset-x, 0px))`,
257
+ top: `calc(${y}px - var(--grab-offset-y, 0px))`,
258
+ height: this.dragResize ? `${h}px` : undefined,
259
+ width: this.dragResize ? `${w}px` : undefined,
260
+ willChange: 'transform,opacity',
261
+ });
262
+ clone.classList.add(this.draggedClass);
263
+ return node.parentNode.appendChild(clone);
264
+ }
265
+ /// Event handlers ///
266
+ removeEventHandlers(ele) {
267
+ ele = ele || this.sortableHost;
268
+ ele.removeEventListener('mousedown', this.handleTrack);
269
+ ele.removeEventListener('touchstart', this.handleTrack);
270
+ ele.removeEventListener('keydown', this.handleKeydown);
271
+ }
272
+ addEventHandlers(ele) {
273
+ ele = ele || this.sortableHost;
274
+ ele.addEventListener('mousedown', this.handleTrack);
275
+ ele.addEventListener('touchstart', this.handleTrack);
276
+ ele.addEventListener('keydown', this.handleKeydown);
277
+ }
278
+ /**
279
+ * Watch for any changes in grab-able handle elements.
280
+ */
281
+ attachMutationObserver() {
282
+ if (this.mutationObserver) {
283
+ this.mutationObserver.disconnect();
284
+ this.mutationObserver = undefined;
285
+ }
286
+ this.mutationObserver = new MutationObserver(() => {
287
+ const currHandles = Array.from(this.keyboardHandleMap.values());
288
+ const newHandles = Array.from(this.sortableHost.querySelectorAll(this.itemSelector));
289
+ // simple test for equality
290
+ if (currHandles.length !== newHandles.length ||
291
+ !!newHandles.find((h) => !currHandles.includes(h))) {
292
+ this.refreshKeyboardHandles();
293
+ }
294
+ });
295
+ this.mutationObserver.observe(this.sortableHost, {
296
+ subtree: true,
297
+ childList: true,
298
+ });
299
+ }
300
+ handleKeydown(e) {
301
+ const targetElement = e.target;
302
+ let foundHandle;
303
+ let sortEle;
304
+ if (this.handleSelector) {
305
+ foundHandle = targetElement.closest(this.handleSelector);
306
+ sortEle = targetElement.closest(this.itemSelector);
307
+ }
308
+ else {
309
+ sortEle = this.keyboardHandleMap.get(targetElement);
310
+ foundHandle = targetElement;
311
+ }
312
+ // have we found a handle and matching sort element from the keydown element
313
+ if (!foundHandle || !sortEle)
314
+ return;
315
+ const activateSort = (isActive) => {
316
+ this.keyboardSortActive = isActive;
317
+ this.draggedElement = isActive ? sortEle : undefined;
318
+ sortEle.classList.toggle(this.draggedClass, isActive);
319
+ foundHandle.classList.toggle(this.handleDraggedClass, isActive);
320
+ if (isActive) {
321
+ this.addAriaMsg(this.grabbedHelperText(sortEle));
322
+ document.addEventListener('mousedown', () => activateSort(false), {
323
+ once: true,
324
+ });
325
+ }
326
+ else {
327
+ this.addAriaMsg(this.droppedHelperText(sortEle));
328
+ }
329
+ };
330
+ // start editing this element's order?
331
+ if ([' ', 'Space', 'Enter'].includes(e.key)) {
332
+ e.preventDefault();
333
+ if (!this.keyboardSortActive) {
334
+ // grabbed the element
335
+ activateSort(true);
336
+ this.sortableNodes =
337
+ Array.from(this.sortableHost.querySelectorAll(this.itemSelector)) ||
338
+ [];
339
+ const nanoGrabbedEv = this.nanoGrabbed.emit({
340
+ element: sortEle,
341
+ index: this.sortableNodes.indexOf(sortEle),
342
+ });
343
+ if (nanoGrabbedEv.defaultPrevented) {
344
+ activateSort(false);
345
+ return;
346
+ }
347
+ }
348
+ else {
349
+ // dropped the element
350
+ activateSort(false);
351
+ this.nanoDropped.emit({ element: sortEle });
352
+ }
353
+ return;
354
+ }
355
+ if (!this.keyboardSortActive)
356
+ return;
357
+ // Tabbing away from this handle - deactivate
358
+ if (['Escape', 'Tab'].includes(e.key))
359
+ activateSort(false);
360
+ let moveKeys = ['Home', 'End'];
361
+ if (!this.orientation || this.orientation === 'horizontal')
362
+ moveKeys = [...moveKeys, 'ArrowRight', 'ArrowLeft'];
363
+ if (!this.orientation || this.orientation === 'vertical')
364
+ moveKeys = [...moveKeys, 'ArrowUp', 'ArrowDown'];
365
+ if (!moveKeys.includes(e.key))
366
+ return;
367
+ // move the element with the keyboard
368
+ e.preventDefault();
369
+ /** Collect all elements that have drag positions.
370
+ * Both sortable elements and 'dropzones' (placeholders where items can always be placed) */
371
+ this.sortableNodes =
372
+ Array.from(this.sortableHost.querySelectorAll(this.itemSelector)) || [];
373
+ this.dropzoneNodes =
374
+ Array.from(this.sortableHost.querySelectorAll(this.dropzoneSelector)) ||
375
+ [];
376
+ const currIdx = this.sortableNodes.indexOf(this.draggedElement);
377
+ let curDzIdx = -1;
378
+ if (this.dropzoneNodes.length) {
379
+ const curDropzone = this.draggedElement.closest(this.dropzoneSelector);
380
+ curDzIdx = this.dropzoneNodes.indexOf(curDropzone);
381
+ curDzIdx = curDzIdx > -1 ? curDzIdx : -1;
382
+ }
383
+ /** If we don't have a next / prev sortable element in our list, test to see if there's a dropzone instead */
384
+ const prevEle = currIdx - 1 < 0 && curDzIdx > -1
385
+ ? this.dropzoneNodes[curDzIdx - 1]
386
+ : this.sortableNodes[currIdx - 1];
387
+ const nextEle = currIdx + 1 === this.sortableNodes.length && curDzIdx > -1
388
+ ? this.dropzoneNodes[curDzIdx + 1]
389
+ : this.sortableNodes[currIdx + 1];
390
+ if (e.key === 'Home') {
391
+ this.insertAtTarget(this.draggedElement, this.sortableNodes[0]);
392
+ }
393
+ if (e.key === 'End') {
394
+ this.insertAtTarget(this.draggedElement, this.sortableNodes[this.sortableNodes.length - 1]);
395
+ }
396
+ if (['ArrowRight', 'ArrowDown'].includes(e.key)) {
397
+ this.insertAtTarget(this.draggedElement, nextEle);
398
+ }
399
+ if (['ArrowLeft', 'ArrowUp'].includes(e.key)) {
400
+ this.insertAtTarget(this.draggedElement, prevEle);
401
+ }
402
+ this.finishOrder();
403
+ this.draggedElement = sortEle;
404
+ const focus = () => {
405
+ requestAnimationFrame(() => {
406
+ typeof foundHandle['setFocus'] === 'function'
407
+ ? foundHandle.setFocus()
408
+ : foundHandle.focus();
409
+ });
410
+ };
411
+ // replace focus to handle so we don't 'drop' the element
412
+ if (this.animationPromise)
413
+ this.animationPromise.then(() => focus());
414
+ else
415
+ focus();
416
+ }
417
+ /**
418
+ * Tracks a pointer from touchstart/mousedown to touchend/mouseup. Note that the start state is fired following
419
+ * the first actual move event following a touchstart/mousedown */
420
+ handleTrack(e) {
421
+ if (this.dragRequestPending || (e.button && e.button !== 1))
422
+ return;
423
+ clearTimeout(this.mouseDownTimer);
424
+ this.mouseDownTimer = window === null || window === void 0 ? void 0 : window.setTimeout(() => {
425
+ if (!this.trackStart(e))
426
+ return;
427
+ this.addAriaMsg(this.grabbedHelperText(this.draggedElement));
428
+ drag.drag(this.sortableHost, {
429
+ initialEvent: e,
430
+ relative: true,
431
+ onMove: (x, y) => {
432
+ this.trackMove(x, y);
433
+ },
434
+ onStop: () => {
435
+ this.nanoDropped.emit({ element: this.draggedElement });
436
+ const didStop = () => {
437
+ this.addAriaMsg(this.droppedHelperText(this.draggedElement));
438
+ requestAnimationFrame(() => this.finishOrder());
439
+ };
440
+ if (this.animationPromise) {
441
+ this.animationPromise.then(() => didStop());
442
+ }
443
+ else
444
+ didStop();
445
+ },
446
+ });
447
+ }, 100);
448
+ document.addEventListener('mouseup', () => clearTimeout(this.mouseDownTimer), { once: true });
449
+ }
450
+ /**
451
+ * Initialized a drag and drop sequence if a child node was clicked that matches the itemSelector property.
452
+ * OR If a handleSelector is defined, a node matching this selector must be clicked instead
453
+ * @returns boolean - whether tracking for this event should continue or not
454
+ */
455
+ trackStart(e) {
456
+ const targetElement = e.target;
457
+ let targetHandle;
458
+ // If we have a handle set, return now if not being pressed
459
+ if (this.handleSelector) {
460
+ targetHandle = targetElement.closest(this.handleSelector);
461
+ if (!targetHandle)
462
+ return;
463
+ targetHandle.classList.add(this.handleDraggedClass);
464
+ }
465
+ // Check that we've found the target element via itemSelector
466
+ const node = targetElement.closest(this.itemSelector);
467
+ if (!node)
468
+ return false;
469
+ this.sortableNodes =
470
+ Array.from(this.sortableHost.querySelectorAll(this.itemSelector)) || [];
471
+ const nanoGrabbedEv = this.nanoGrabbed.emit({
472
+ element: node,
473
+ index: this.sortableNodes.indexOf(node),
474
+ });
475
+ if (nanoGrabbedEv.defaultPrevented)
476
+ return false;
477
+ // Element found... start everything
478
+ e.preventDefault();
479
+ this.updateUserSelectStyle('none');
480
+ this.dragRequestPending = true;
481
+ this.draggedElement = node;
482
+ this.dropzoneNodes =
483
+ Array.from(this.sortableHost.querySelectorAll(this.dropzoneSelector)) ||
484
+ [];
485
+ this.draggedElementClone = this.createClone(node);
486
+ this.draggedElementOrigin = node.nextSibling;
487
+ this.animatedElements = [];
488
+ this.draggedElement.classList.add(this.placeholderClass);
489
+ return true;
490
+ }
491
+ /** Ends re-ordering */
492
+ finishOrder() {
493
+ if (!this.draggedElement)
494
+ return;
495
+ const updated = Array.from(this.sortableHost.querySelectorAll(this.itemSelector)).filter((ele) => ele !== this.draggedElementClone);
496
+ const originalIndex = this.sortableNodes.indexOf(this.draggedElement);
497
+ const targetIndex = updated.indexOf(this.draggedElement);
498
+ if (this.handleSelector) {
499
+ const targetHandle = this.draggedElement.querySelector(this.handleSelector);
500
+ targetHandle.classList.remove(this.handleDraggedClass);
501
+ }
502
+ if (originalIndex !== targetIndex) {
503
+ const orderChangeEv = this.nanoOrderChange.emit({
504
+ element: this.draggedElement,
505
+ originalIndex,
506
+ targetIndex,
507
+ });
508
+ if (orderChangeEv.defaultPrevented) {
509
+ /** Event was prevented, wait a moment to send element back to whence it came - gives a nicer visual queue */
510
+ this.animationPromise = new Promise((resolve) => {
511
+ setTimeout(() => {
512
+ this.insertAtTarget(this.draggedElement, updated[originalIndex]);
513
+ this.reset();
514
+ this.dragRequestPending = false;
515
+ resolve();
516
+ }, 200);
517
+ });
518
+ return;
519
+ }
520
+ this.addAriaMsg(this.reorderHelperText(this.draggedElement, updated, targetIndex + 1));
521
+ }
522
+ this.reset();
523
+ this.dragRequestPending = false;
524
+ }
525
+ /// Component lifecycle ///
526
+ constructor(hostRef) {
527
+ index.registerInstance(this, hostRef);
528
+ this.nanoOrderChange = index.createEvent(this, "nanoOrderChange", 7);
529
+ this.nanoGrabbed = index.createEvent(this, "nanoGrabbed", 7);
530
+ this.nanoDropped = index.createEvent(this, "nanoDropped", 7);
531
+ this.dragRequestPending = false;
532
+ this.sortableNodes = [];
533
+ this.dropzoneNodes = [];
534
+ this.animatedElements = [];
535
+ this.keyboardHandleMap = new Map();
536
+ this.sortableId = `nano-sortable-${sortableIds++}`;
537
+ /**
538
+ * Moves the active node's clone to follow the pointer. The node that the clone intersects with (via hitTest) is
539
+ * the insert point for updated sorting */
540
+ this.trackMove = (x, y) => {
541
+ if (!this.draggedElementClone) {
542
+ return;
543
+ }
544
+ if (this.orientation) {
545
+ x = x * orientationMap[this.orientation].x;
546
+ y = y * orientationMap[this.orientation].y;
547
+ }
548
+ index.writeTask(() => {
549
+ Object.assign(this.draggedElementClone.style, {
550
+ transform: `translate3d(${x}px, ${y}px, 0)`,
551
+ });
552
+ });
553
+ let target = this.hitTest(this.draggedElementClone, this.sortableNodes)[0];
554
+ let activeDropzone;
555
+ if (this.dropzoneSelector && this.dropzoneActiveClass) {
556
+ index.readTask(() => {
557
+ activeDropzone = this.draggedElement.closest(this.dropzoneSelector);
558
+ index.writeTask(() => {
559
+ this.dropzoneNodes
560
+ .filter((dze) => dze !== activeDropzone)
561
+ .forEach((dze) => dze.classList.remove(this.dropzoneActiveClass));
562
+ activeDropzone.classList.add(this.dropzoneActiveClass);
563
+ });
564
+ });
565
+ }
566
+ // didn't find a target - let's test to see if we can use a drop-zone instead
567
+ if (!target && this.dropzoneNodes.length) {
568
+ target = this.hitTest(this.draggedElementClone, this.dropzoneNodes)[0];
569
+ if (this.draggedElement.closest(this.dropzoneSelector) === target)
570
+ return;
571
+ }
572
+ if (
573
+ // if clone intersects with a valid target,
574
+ target &&
575
+ // other than its own origin,
576
+ target !== this.draggedElement &&
577
+ // and the target isn't currently animating, which causes false hit tests,
578
+ !this.isAnimating(target)) {
579
+ this.insertAtTarget(this.draggedElement, target);
580
+ }
581
+ };
582
+ this.itemSelector = 'li';
583
+ this.handleSelector = undefined;
584
+ this.dropzoneSelector = undefined;
585
+ this.helperText = 'Press "Space" or "Enter" to enable element reordering and use the arrow keys to reorder items.' +
586
+ 'Press "Escape" to cancel reordering. Alternatively, use your mouse to drag / reorder.';
587
+ this.itemDescriptor = (el) => `"${el.textContent.trim()}"`;
588
+ this.grabbedHelperText = (el) => `${this.itemDescriptor(el)} grabbed`;
589
+ this.droppedHelperText = (el) => `${this.itemDescriptor(el)} dropped`;
590
+ this.reorderHelperText = (el, elements, position) => `The list has been reordered, ${this.itemDescriptor(el)} is now item ${position} of ${elements.length}`;
591
+ this.createKeyboardHandle = (_number, _element) => {
592
+ const handleTpl = /* html */ `
593
+ <nano-icon-button
594
+ slot="end"
595
+ icon-name="light/arrows"
596
+ class="nano-sortable__keyboard-handle visually-hidden"
597
+ ></nano-icon-button>`;
598
+ const div = globalThis.document.createElement('div');
599
+ div.innerHTML = handleTpl;
600
+ const handle = div.children[0];
601
+ _element.append(handle);
602
+ return handle;
603
+ };
604
+ this.sortableHostElement = undefined;
605
+ this.animationEnabled = true;
606
+ this.draggedClass = 'nano-sortable__dragged';
607
+ this.handleDraggedClass = 'nano-sortable__handle-dragged';
608
+ this.placeholderClass = 'nano-sortable__placeholder';
609
+ this.dropzoneActiveClass = '';
610
+ this.animationTiming = { duration: 200, easing: 'ease-out' };
611
+ this.orientation = undefined;
612
+ this.dragResize = false;
613
+ this.keyboardSortActive = false;
614
+ this.ariaTextList = [];
615
+ this.handleTrack = this.handleTrack.bind(this);
616
+ this.handleKeydown = this.handleKeydown.bind(this);
617
+ this.refreshKeyboardHandles = this.refreshKeyboardHandles.bind(this);
618
+ this.refreshKeyboardHandles = throttle.debounce(this.refreshKeyboardHandles, 500);
619
+ }
620
+ connectedCallback() {
621
+ this.addEventHandlers();
622
+ this.refreshKeyboardHandles();
623
+ this.attachMutationObserver();
624
+ if (!this.host.querySelector(`#${this.sortableId}`)) {
625
+ // inject a light-dom / a11y description that we can connect sortable items to
626
+ this.host.insertAdjacentHTML('beforeend', `<div class="visually-hidden" id="${this.sortableId}">${this.helperText}</div>`);
627
+ }
628
+ }
629
+ disconnectedCallback() {
630
+ var _a;
631
+ this.removeEventHandlers();
632
+ (_a = this.host.querySelector(`#${this.sortableId}`)) === null || _a === void 0 ? void 0 : _a.remove();
633
+ if (this.mutationObserver) {
634
+ this.mutationObserver.disconnect();
635
+ this.mutationObserver = undefined;
636
+ }
637
+ }
638
+ render() {
639
+ return (index.h(index.Host, null, index.h("div", { class: "sortable__live-region", "aria-live": "polite", "aria-relevant": "additions", "aria-atomic": "true", role: "log", part: "announcements" }, this.ariaTextList.map((str) => (index.h("div", null, str)))), index.h("slot", null)));
640
+ }
641
+ get host() { return index.getElement(this); }
642
+ static get watchers() { return {
643
+ "itemSelector": ["handleItemSelectorChange"],
644
+ "handleSelector": ["handleHandleSelectorChange"],
645
+ "createKeyboardHandle": ["handleCreateKeyboardHandleChange"],
646
+ "sortableHostElement": ["handleSortableHostElement"]
647
+ }; }
648
+ };
649
+ Sortable.style = sortableCss;
650
+
651
+ exports.nano_sortable = Sortable;
652
+
653
+ //# sourceMappingURL=nano-sortable.cjs.entry.js.map