@pictogrammers/components 0.5.14 → 0.5.17

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 (153) hide show
  1. package/package.json +1 -1
  2. package/pg/autocompleteItem/__examples__/basic/basic.html +3 -0
  3. package/pg/autocompleteItem/autocompleteItem.css +55 -0
  4. package/pg/autocompleteItem/autocompleteItem.html +1 -0
  5. package/pg/autocompleteItem/autocompleteItem.ts +65 -0
  6. package/pg/button/button.css +5 -0
  7. package/pg/buttonMenu/buttonMenu.ts +1 -1
  8. package/pg/inputCombobox/__examples__/basic/basic.ts +33 -0
  9. package/pg/inputCombobox/inputCombobox.ts +158 -0
  10. package/pg/inputCombobox/inputCombobox.ts.tmp.44252.751480cb81cc +158 -0
  11. package/pg/inputCombobox/inputCombobox.ts.tmp.44252.dcea847c40a8 +156 -0
  12. package/pg/inputNumber/inputNumber.ts +31 -13
  13. package/pg/inputPill/inputPill.html +6 -0
  14. package/pg/inputPill/inputPill.html.tmp.26852.84ac63f4c46f +6 -0
  15. package/pg/inputPill/inputPill.ts +41 -0
  16. package/pg/inputPills/__examples__/basic/basic.html +9 -0
  17. package/pg/inputPills/__examples__/basic/basic.html.tmp.26852.4771e4cad5b7 +9 -0
  18. package/pg/inputPills/__examples__/basic/basic.ts.tmp.26852.d8fe41df18bd +0 -0
  19. package/pg/inputPixelEditor/README.md +104 -69
  20. package/pg/inputPixelEditor/README.md.tmp.42596.658de5f0a6f5 +0 -0
  21. package/pg/inputPixelEditor/__examples__/basic/basic.ts +6 -5
  22. package/pg/inputPixelEditor/inputPixelEditor.ts +95 -23
  23. package/pg/inputPixelEditor/inputPixelEditor.ts.tmp.38516.96529dd82670 +1703 -0
  24. package/pg/inputPixelEditor/inputPixelEditor.ts.tmp.42596.48ecc3ad9c72 +1724 -0
  25. package/pg/inputPixelEditor/utils/bitmapToMask.ts +113 -79
  26. package/pg/inputPixelEditor/utils/getLassoPixels.ts +51 -0
  27. package/pg/inputRange/inputRange.css +7 -0
  28. package/pg/inputSelect/__examples__/basic/basic.ts +2 -2
  29. package/pg/inputSelect/inputSelect.css +1 -1
  30. package/pg/inputSelect/inputSelect.ts +62 -5
  31. package/pg/inputText/README.md +6 -6
  32. package/pg/inputText/__examples__/basic/basic.ts +1 -1
  33. package/pg/inputText/inputText.css +2 -2
  34. package/pg/inputText copy/README.md +38 -0
  35. package/pg/inputText copy/inputText.css +41 -0
  36. package/pg/inputText copy/inputText.html +1 -0
  37. package/pg/inputText copy/inputText.spec.ts +59 -0
  38. package/pg/inputText copy/inputText.ts +67 -0
  39. package/pg/inputTextarea/README.md +39 -0
  40. package/pg/inputTextarea/__examples__/basic/basic.html +9 -0
  41. package/pg/inputTextarea/__examples__/basic/basic.ts +30 -0
  42. package/pg/inputTextarea/inputTextarea.ts +121 -0
  43. package/pg/inputTextarea copy/README.md +39 -0
  44. package/pg/inputTextarea copy/inputTextarea.css +41 -0
  45. package/pg/inputTextarea copy/inputTextarea.html +1 -0
  46. package/pg/inputTextarea copy/inputTextarea.spec.ts +59 -0
  47. package/pg/inputTextarea copy/inputTextarea.ts +79 -0
  48. package/pg/inputTextareaAutocomplete/README.md +70 -0
  49. package/pg/inputTextareaAutocomplete/__examples__/basic/basic.ts +67 -0
  50. package/pg/inputTextareaAutocomplete/inputTextareaAutocomplete.ts +267 -0
  51. package/pg/node/README.md +35 -0
  52. package/pg/node/__examples__/basic/basic.ts +34 -0
  53. package/pg/node/index.ts +3 -0
  54. package/pg/node/node.css +53 -0
  55. package/pg/node/node.html +5 -0
  56. package/pg/node/node.ts +270 -0
  57. package/pg/nodeEditorLink/nodeEditorLink.css +69 -0
  58. package/pg/nodeEditorLink/nodeEditorLink.css.tmp.1408.09e961fd0dcc +69 -0
  59. package/pg/nodeEditorLink/nodeEditorLink.html +9 -0
  60. package/pg/nodeEditorNumber/nodeEditorNumber.css +59 -0
  61. package/pg/nodeEditorNumber/nodeEditorNumber.ts +57 -0
  62. package/pg/nodeEditorRange/nodeEditorRange.css +23 -0
  63. package/pg/nodeEditorRange/nodeEditorRange.ts +69 -0
  64. package/pg/nodeEditorText/__examples__/basic/basic.html +9 -0
  65. package/pg/nodeEditorText/__examples__/basic/basic.ts +30 -0
  66. package/pg/nodeEditorText/nodeEditorText.css +17 -0
  67. package/pg/nodeEditorText/nodeEditorText.html +4 -0
  68. package/pg/nodeEditorText/nodeEditorText.ts +57 -0
  69. package/pg/nodeEditorText copy/README.md +35 -0
  70. package/pg/nodeEditorText copy/nodeEditorText.css +49 -0
  71. package/pg/nodeEditorText copy/nodeEditorText.html +4 -0
  72. package/pg/nodeEditorText copy/nodeEditorText.ts +50 -0
  73. package/pg/nodeEditorTextArray/nodeEditorTextArray.css +68 -0
  74. package/pg/nodeEditorTextArray/nodeEditorTextArray.html +9 -0
  75. package/pg/nodeEditorTextArray/nodeEditorTextArray.ts +162 -0
  76. package/pg/nodeEntry/nodeEntry.css +61 -0
  77. package/pg/nodeEntry/nodeEntry.ts +164 -0
  78. package/pg/nodeEntry/nodeEntry.ts.tmp.28724.dda67762bca5 +164 -0
  79. package/pg/nodeInputText/nodeInputText.css +75 -0
  80. package/pg/nodeInputText/nodeInputText.html +4 -0
  81. package/pg/nodeInputText/nodeInputText.ts +105 -0
  82. package/pg/nodeOutput/nodeOutput.css +32 -0
  83. package/pg/nodeResize/README.md +3 -0
  84. package/pg/nodeResize/THINKING.md +81 -0
  85. package/pg/nodeResize/THINKING.md.tmp.24812.99a70ba81062 +81 -0
  86. package/pg/nodeResize/basic/basic.ts +17 -0
  87. package/pg/nodeResize/dragUtil.ts +105 -0
  88. package/pg/nodeResize/dragUtil.ts.tmp.34372.787464c73309 +75 -0
  89. package/pg/nodeResize/nodeResize.css +187 -0
  90. package/pg/nodeResize/nodeResize.html +9 -0
  91. package/pg/nodeResize/nodeResize.ts +156 -0
  92. package/pg/nodes/README.md +177 -0
  93. package/pg/nodes/__examples__/basic/basic.html +33 -0
  94. package/pg/nodes/__examples__/basic/basic.ts +716 -0
  95. package/pg/nodes/__examples__/basic/exampleNodes.ts +129 -0
  96. package/pg/nodes/__examples__/basic/exampleScripts.ts +129 -0
  97. package/pg/nodes/__examples__/basic/keystore.ts +38 -0
  98. package/pg/nodes/executeInSandbox.ts +32 -0
  99. package/pg/nodes/index.ts +3 -0
  100. package/pg/nodes/nodes.css +69 -0
  101. package/pg/nodes/nodes.html +6 -0
  102. package/pg/nodes/nodes.ts +1240 -0
  103. package/pg/nodes copy/nodes.css +41 -0
  104. package/pg/nodes copy/nodes.html +1 -0
  105. package/pg/overlayContextMenu/__examples__/filter/filter.ts +87 -0
  106. package/pg/overlayContextMenu/overlayContextMenu.css +14 -0
  107. package/pg/overlayContextMenu/overlayContextMenu.html +4 -3
  108. package/pg/overlayContextMenu/overlayContextMenu.ts +6 -2
  109. package/pg/overlayContextMenu/overlayContextMenu.ts.tmp.37088.530ff1c0a321 +0 -0
  110. package/pg/overlayMenu/overlayMenu.css +3 -1
  111. package/pg/overlayMenu/overlayMenu.ts +11 -2
  112. package/pg/overlayMenu/overlayMenu.ts.tmp.44252.9bb22fd59ede +80 -0
  113. package/pg/overlaySelectMenu/overlaySelectMenu.css +2 -0
  114. package/pg/overlaySubMenu/overlaySubMenu.css +8 -6
  115. package/pg/poco/README.md +22 -0
  116. package/pg/poco/__examples__/basic/basic.html +11 -0
  117. package/pg/poco/__examples__/basic/basic.ts +95 -0
  118. package/pg/poco/__examples__/basic/examples.ts +258 -0
  119. package/pg/poco/__examples__/basic/myFont.ts +1300 -0
  120. package/pg/poco/index.ts +3 -0
  121. package/pg/poco/mockPoco.ts +640 -0
  122. package/pg/poco/poco.css +7 -0
  123. package/pg/poco/poco.html +1 -0
  124. package/pg/poco/poco.ts +75 -0
  125. package/pg/popover/README.md +47 -0
  126. package/pg/popover/__examples__/basic/basic.html +3 -0
  127. package/pg/popover/popover.html +3 -0
  128. package/pg/popover/popover.ts +44 -0
  129. package/pg/popoverAutocomplete/__examples__/basic/basic.html +4 -0
  130. package/pg/popoverAutocomplete/__examples__/basic/basic.ts +46 -0
  131. package/pg/popoverAutocomplete/popoverAutocomplete.css +25 -0
  132. package/pg/popoverAutocomplete/popoverAutocomplete.html +1 -0
  133. package/pg/popoverAutocomplete/popoverAutocomplete.ts +134 -0
  134. package/pg/popoverAutocompleteItem/popoverAutocompleteItem.css +69 -0
  135. package/pg/popoverAutocompleteItem/popoverAutocompleteItem.ts +90 -0
  136. package/pg/table/__examples__/basic/basic.ts +1 -0
  137. package/pg/tableCellFileLocal/tableCellFileLocal.html +3 -0
  138. package/pg/tableCellFileLocal/tableCellFileLocal.html.tmp.20484.68ee2e30710e +3 -0
  139. package/pg/tableCellFileLocal/tableCellFileLocal.ts +44 -0
  140. package/pg/tableCellSelect/tableCellSelect.html +3 -0
  141. package/pg/tableCellSelect/tableCellSelect.html.tmp.41808.99c1a6f14340 +3 -0
  142. package/pg/tableCellSelect/tableCellSelect.ts +51 -0
  143. package/pg/tree/__examples__/basic/basic.ts +12 -5
  144. package/pg/tree/tree.html +1 -1
  145. package/pg/tree/tree.ts +385 -227
  146. package/pg/tree/tree.ts.tmp.38376.07d376a04f41 +343 -0
  147. package/pg/tree/tree.ts.tmp.38376.2a04599ade9f +361 -0
  148. package/pg/tree/tree.ts.tmp.38376.6cb2c663d476 +0 -0
  149. package/pg/tree/tree.ts.tmp.38376.ac5af1aff2b0 +393 -0
  150. package/pg/tree/tree.ts.tmp.38376.ac9acfc4f95f +324 -0
  151. package/pg/treeItem/treeItem.ts +536 -558
  152. package/pg/treeItem/treeItem.ts.tmp.38376.145d3121d648 +530 -0
  153. package/pg/treeItem/treeItem.ts.tmp.38376.b5224d48e5c0 +0 -0
@@ -0,0 +1,530 @@
1
+ import { Component, Prop, Part, forEach } from '@pictogrammers/element';
2
+
3
+ import PgIcon from '../icon/icon';
4
+ import PgTreeButtonIcon from '../treeButtonIcon/treeButtonIcon';
5
+
6
+ import template from './treeItem.html';
7
+ import style from './treeItem.css';
8
+
9
+ const noIcon = 'M0 0h24v24H0V0zm2 2v20h20V2H2z';
10
+
11
+ @Component({
12
+ selector: 'pg-tree-item',
13
+ style,
14
+ template
15
+ })
16
+ export default class PgTreeItem extends HTMLElement {
17
+
18
+ @Prop() index: number;
19
+ @Prop() label: string = '';
20
+ @Prop() selected: boolean = false;
21
+ @Prop() expanded: boolean = false;
22
+ @Prop() isFolder: boolean = false;
23
+ @Prop() icon: { path: string } = { path: noIcon };
24
+ @Prop() actions: any[] = [];
25
+ @Prop() items: any[] = [];
26
+
27
+ @Part() $toggle: HTMLButtonElement;
28
+ @Part() $item: HTMLDivElement;
29
+ @Part() $input: HTMLInputElement;
30
+ @Part() $iconButton: HTMLButtonElement;
31
+ @Part() $icon: PgIcon;
32
+ @Part() $label: HTMLDivElement;
33
+ @Part() $labelButton: HTMLButtonElement;
34
+ @Part() $actions: HTMLDivElement;
35
+ @Part() $items: HTMLDivElement;
36
+ @Part() $dropabove: HTMLDivElement;
37
+ @Part() $dropon: HTMLDivElement;
38
+ @Part() $dropbelow: HTMLDivElement;
39
+
40
+ connectedCallback() {
41
+ this.$toggle.addEventListener('click', this.#handleToggleClick.bind(this));
42
+ this.$item.addEventListener('action', this.#handleItemAction.bind(this));
43
+ this.$item.addEventListener('pointerenter', this.#handlePointerEnter.bind(this));
44
+ this.$item.addEventListener('pointerleave', this.#handlePointerLeave.bind(this));
45
+ this.$item.addEventListener('dragstart', this.#handleDragStart.bind(this));
46
+ this.$item.addEventListener('dragend', this.#handleDragEnd.bind(this));
47
+ this.$labelButton.addEventListener('dblclick', this.#handleDoubleClick.bind(this));
48
+ this.$labelButton.addEventListener('click', this.#handleClick.bind(this));
49
+ this.$labelButton.addEventListener('keydown', this.#handleKeyDownLabel.bind(this));
50
+ this.$iconButton.addEventListener('dblclick', this.#handleIconDoubleClick.bind(this));
51
+ this.$iconButton.addEventListener('click', this.#handleIconClick.bind(this));
52
+ this.$iconButton.addEventListener('keydown', this.#handleIconKeyDown.bind(this));
53
+ this.$item.addEventListener('contextmenu', this.#handleContextMenu.bind(this));
54
+ this.$input.addEventListener('blur', this.#handleBlur.bind(this));
55
+ this.$input.addEventListener('keydown', this.#handleInputKeyDown.bind(this));
56
+ // Bubble indexes up through nested items containers
57
+ this.$items.addEventListener('action', this.#handleAction.bind(this));
58
+ this.$items.addEventListener('move', this.#handleMove.bind(this));
59
+ this.$items.addEventListener('toggle', this.#handleToggle.bind(this));
60
+ this.$items.addEventListener('select', this.#handleSelect.bind(this));
61
+ this.$items.addEventListener('rename', this.#handleRename.bind(this));
62
+ this.$items.addEventListener('menu', this.#handleMenu.bind(this));
63
+ this.$items.addEventListener('up', this.#handleUp.bind(this));
64
+ this.$items.addEventListener('down', this.#handleDown.bind(this));
65
+ this.$items.addEventListener('itemdragstart', this.#handleItemDragStart.bind(this));
66
+ this.$items.addEventListener('itemdragend', this.#handleItemDragEnd.bind(this));
67
+ this.$items.addEventListener('itemdropenter', this.#handleItemDropEnter.bind(this));
68
+ this.$items.addEventListener('expand', this.#handleItemExpand.bind(this));
69
+ // Drop zones
70
+ this.$dropabove.addEventListener('dragenter', this.#handleDragAboveEnter.bind(this));
71
+ this.$dropabove.addEventListener('dragleave', this.#handleDragAboveLeave.bind(this));
72
+ this.$dropabove.addEventListener('dragover', this.#handleDragOver.bind(this));
73
+ this.$dropabove.addEventListener('drop', this.#handleDragAbove.bind(this));
74
+ this.$dropon.addEventListener('dragenter', this.#handleDragOnEnter.bind(this));
75
+ this.$dropon.addEventListener('dragleave', this.#handleDragOnLeave.bind(this));
76
+ this.$dropon.addEventListener('dragover', this.#handleDragOver.bind(this));
77
+ this.$dropon.addEventListener('drop', this.#handleDropOn.bind(this));
78
+ this.$dropbelow.addEventListener('dragenter', this.#handleDragBelowEnter.bind(this));
79
+ this.$dropbelow.addEventListener('dragleave', this.#handleDragBelowLeave.bind(this));
80
+ this.$dropbelow.addEventListener('dragover', this.#handleDragOver.bind(this));
81
+ this.$dropbelow.addEventListener('drop', this.#handleDropBelow.bind(this));
82
+
83
+ forEach({
84
+ container: this.$actions,
85
+ items: this.actions,
86
+ type: (item) => item.type || PgTreeButtonIcon
87
+ });
88
+ if (this.expanded) {
89
+ this.#initItems();
90
+ }
91
+ }
92
+
93
+ disconnectedCallback() {
94
+ window.clearTimeout(this.#dragOnTimer);
95
+ this.#canvas?.remove();
96
+ this.#canvas = null;
97
+ }
98
+
99
+ #initItemsOnce = true;
100
+ #initItems() {
101
+ if (this.#initItemsOnce) {
102
+ forEach({
103
+ container: this.$items,
104
+ items: this.items,
105
+ type: () => PgTreeItem
106
+ });
107
+ this.#initItemsOnce = false;
108
+ }
109
+ }
110
+
111
+ render(changes) {
112
+ if (changes.label) {
113
+ this.$label.textContent = this.label;
114
+ }
115
+ if (changes.icon && this.icon) {
116
+ this.$icon.path = this.icon.path;
117
+ }
118
+ if (changes.selected) {
119
+ this.$item.classList.toggle('selected', this.selected);
120
+ }
121
+ if (changes.items || changes.isFolder) {
122
+ this.$item.classList.toggle('items', this.isFolder || this.items.length !== 0);
123
+ }
124
+ if (changes.expanded) {
125
+ if (this.expanded) {
126
+ this.#initItems();
127
+ }
128
+ this.$item.classList.toggle('expanded', this.expanded);
129
+ this.$items.classList.toggle('expanded', this.expanded);
130
+ }
131
+ }
132
+
133
+ #handleToggleClick() {
134
+ this.dispatchEvent(new CustomEvent('toggle', {
135
+ bubbles: true,
136
+ composed: true,
137
+ detail: { indexes: [this.index] }
138
+ }));
139
+ }
140
+
141
+ #handleIconDoubleClick(e: MouseEvent) {
142
+ if (e.ctrlKey || e.shiftKey) return;
143
+ this.dispatchEvent(new CustomEvent('select', {
144
+ bubbles: true,
145
+ composed: true,
146
+ detail: {
147
+ type: 'icondoubleclick',
148
+ indexes: [this.index]
149
+ }
150
+ }));
151
+ }
152
+
153
+ #handleIconClick() {
154
+ this.dispatchEvent(new CustomEvent('select', {
155
+ bubbles: true,
156
+ composed: true,
157
+ detail: {
158
+ type: 'icon',
159
+ indexes: [this.index]
160
+ }
161
+ }));
162
+ }
163
+
164
+ #handleClick(e: MouseEvent) {
165
+ if (this.#ignoreNextClick) {
166
+ this.#ignoreNextClick = false;
167
+ return;
168
+ }
169
+ this.dispatchEvent(new CustomEvent('select', {
170
+ bubbles: true,
171
+ composed: true,
172
+ detail: {
173
+ type: 'label',
174
+ indexes: [this.index],
175
+ ctrlKey: e.ctrlKey,
176
+ shiftKey: e.shiftKey
177
+ }
178
+ }));
179
+ }
180
+
181
+ #handleKeyDownLabel(e: KeyboardEvent) {
182
+ if (e.key === 'Enter') {
183
+ this.#enableRename();
184
+ e.preventDefault();
185
+ } else if (e.key === 'ArrowUp') {
186
+ this.dispatchEvent(new CustomEvent('up', {
187
+ bubbles: true,
188
+ composed: true,
189
+ detail: { indexes: [this.index] }
190
+ }));
191
+ e.preventDefault();
192
+ } else if (e.key === 'ArrowDown') {
193
+ this.dispatchEvent(new CustomEvent('down', {
194
+ bubbles: true,
195
+ composed: true,
196
+ detail: { indexes: [this.index] }
197
+ }));
198
+ e.preventDefault();
199
+ }
200
+ }
201
+
202
+ #handlePointerEnter() {
203
+ this.dispatchEvent(new CustomEvent('enter', {
204
+ bubbles: true,
205
+ composed: true,
206
+ detail: { indexes: [this.index] }
207
+ }));
208
+ }
209
+
210
+ #handlePointerLeave() {
211
+ this.dispatchEvent(new CustomEvent('leave', {
212
+ bubbles: true,
213
+ composed: true,
214
+ detail: { indexes: [this.index] }
215
+ }));
216
+ }
217
+
218
+ #handleItemAction(e: any) {
219
+ e.stopPropagation();
220
+ this.dispatchEvent(new CustomEvent('action', {
221
+ bubbles: true,
222
+ composed: true,
223
+ detail: {
224
+ indexes: [this.index],
225
+ actionIndex: e.detail.index
226
+ }
227
+ }));
228
+ }
229
+
230
+ #handleAction(e: any) { e.detail.indexes.unshift(this.index); }
231
+ #handleMove(e: any) { e.detail.indexes.unshift(this.index); }
232
+ #handleToggle(e: any) { e.detail.indexes.unshift(this.index); }
233
+ #handleRename(e: any) { e.detail.indexes.unshift(this.index); }
234
+ #handleMenu(e: any) { e.detail.indexes.unshift(this.index); }
235
+ #handleUp(e: any) { e.detail.indexes.unshift(this.index); }
236
+ #handleDown(e: any) { e.detail.indexes.unshift(this.index); }
237
+ #handleSelect(e: any) { e.detail.indexes.unshift(this.index); }
238
+ #handleItemDropEnter(e: any) { e.detail.indexes.unshift(this.index); }
239
+
240
+ #handleContextMenu(e: MouseEvent) {
241
+ e.preventDefault();
242
+ this.dispatchEvent(new CustomEvent('menu', {
243
+ bubbles: true,
244
+ composed: true,
245
+ detail: {
246
+ indexes: [this.index],
247
+ x: e.clientX,
248
+ y: e.clientY
249
+ }
250
+ }));
251
+ }
252
+
253
+ #enableRename() {
254
+ this.$labelButton.classList.add('hide');
255
+ this.$actions.classList.add('hide');
256
+ this.$input.classList.remove('hide');
257
+ this.$input.value = this.label;
258
+ this.$input.select();
259
+ this.#ignoreNextClick = true;
260
+ this.dispatchEvent(new CustomEvent('select', {
261
+ bubbles: true,
262
+ composed: true,
263
+ detail: {
264
+ type: 'rename',
265
+ indexes: [this.index]
266
+ }
267
+ }));
268
+ }
269
+
270
+ #ignoreNextClick = false;
271
+ #handleDoubleClick(e: MouseEvent) {
272
+ if (e.ctrlKey || e.shiftKey) return;
273
+ this.#enableRename();
274
+ e.preventDefault();
275
+ }
276
+
277
+ #handleBlur() {
278
+ this.$labelButton.classList.remove('hide');
279
+ this.$actions.classList.remove('hide');
280
+ this.$input.classList.add('hide');
281
+ this.$labelButton.click();
282
+ this.dispatchEvent(new CustomEvent('rename', {
283
+ bubbles: true,
284
+ composed: true,
285
+ detail: {
286
+ indexes: [this.index],
287
+ label: this.$input.value
288
+ }
289
+ }));
290
+ }
291
+
292
+ #handleIconKeyDown(e: KeyboardEvent) {
293
+ switch (e.key) {
294
+ case 'ArrowUp':
295
+ this.dispatchEvent(new CustomEvent('up', {
296
+ bubbles: true,
297
+ composed: true,
298
+ detail: { indexes: [this.index] }
299
+ }));
300
+ e.preventDefault();
301
+ break;
302
+ case 'ArrowDown':
303
+ this.dispatchEvent(new CustomEvent('down', {
304
+ bubbles: true,
305
+ composed: true,
306
+ detail: { indexes: [this.index] }
307
+ }));
308
+ e.preventDefault();
309
+ break;
310
+ }
311
+ }
312
+
313
+ #handleInputKeyDown(e: KeyboardEvent) {
314
+ switch (e.key) {
315
+ case 'Enter':
316
+ this.#handleBlur();
317
+ break;
318
+ case 'Escape':
319
+ this.$labelButton.classList.remove('hide');
320
+ this.$actions.classList.remove('hide');
321
+ this.$input.classList.add('hide');
322
+ this.$input.value = this.label;
323
+ this.$labelButton.click();
324
+ break;
325
+ case 'ArrowUp':
326
+ this.dispatchEvent(new CustomEvent('up', {
327
+ bubbles: true,
328
+ composed: true,
329
+ detail: { indexes: [this.index] }
330
+ }));
331
+ this.$labelButton.classList.remove('hide');
332
+ this.$actions.classList.remove('hide');
333
+ this.$input.classList.add('hide');
334
+ e.preventDefault();
335
+ break;
336
+ case 'ArrowDown':
337
+ this.dispatchEvent(new CustomEvent('down', {
338
+ bubbles: true,
339
+ composed: true,
340
+ detail: { indexes: [this.index] }
341
+ }));
342
+ this.$labelButton.classList.remove('hide');
343
+ this.$actions.classList.remove('hide');
344
+ this.$input.classList.add('hide');
345
+ e.preventDefault();
346
+ break;
347
+ }
348
+ }
349
+
350
+ #canvas: HTMLCanvasElement | null = null;
351
+ #handleDragStart(event: DragEvent) {
352
+ let dragCount = 0;
353
+ this.dispatchEvent(new CustomEvent('itemdragstart', {
354
+ bubbles: true,
355
+ composed: true,
356
+ detail: {
357
+ ctrlKey: event.ctrlKey,
358
+ shiftKey: event.shiftKey,
359
+ indexes: [this.index],
360
+ callback: (count: number) => {
361
+ dragCount = count;
362
+ }
363
+ }
364
+ }));
365
+ this.$item.classList.toggle('dragging', true);
366
+ this.$items.classList.toggle('dragging', true);
367
+ // Generate drag image showing selected item count
368
+ const dpr = window.devicePixelRatio;
369
+ const canvas = document.createElement('canvas');
370
+ // Position off-screen so it doesn't flash before becoming the drag image
371
+ canvas.style.position = 'absolute';
372
+ canvas.style.top = '-200px';
373
+ canvas.style.left = '-200px';
374
+ document.body.append(canvas);
375
+ const offsetInline = 20;
376
+ const fontSize = 16;
377
+ const paddingBlock = 6;
378
+ const paddingInline = 6;
379
+ canvas.width = 100 * dpr;
380
+ canvas.height = 40 * dpr;
381
+ canvas.style.width = `${canvas.width / dpr}px`;
382
+ const ctx = canvas.getContext('2d');
383
+ if (ctx) {
384
+ const text = `${dragCount}`;
385
+ ctx.font = `bold ${fontSize * dpr}px Segoe UI`;
386
+ const textSize = ctx.measureText(text);
387
+ ctx.fillStyle = '#453C4F';
388
+ ctx.beginPath();
389
+ ctx.roundRect(
390
+ offsetInline * dpr,
391
+ 0,
392
+ (textSize.width + paddingInline * 2) * dpr,
393
+ (fontSize + paddingBlock * 2) * dpr,
394
+ 8 + dpr * 2
395
+ );
396
+ ctx.fill();
397
+ ctx.fillStyle = '#FFF';
398
+ ctx.beginPath();
399
+ ctx.roundRect(
400
+ (offsetInline + 2) * dpr,
401
+ 2 * dpr,
402
+ (textSize.width + paddingInline * 2 - 4) * dpr,
403
+ (fontSize + paddingBlock * 2 - 4) * dpr,
404
+ 8
405
+ );
406
+ ctx.fill();
407
+ ctx.fillStyle = '#453C4F';
408
+ ctx.fillText(
409
+ text,
410
+ (offsetInline + paddingInline + 4) * dpr,
411
+ (fontSize + paddingBlock - 2) * dpr
412
+ );
413
+ }
414
+ event.dataTransfer!.setDragImage(canvas, 0, 0);
415
+ this.#canvas = canvas;
416
+ }
417
+
418
+ #handleDragEnd(_event: DragEvent) {
419
+ this.dispatchEvent(new CustomEvent('itemdragend', {
420
+ bubbles: true,
421
+ composed: true,
422
+ detail: { indexes: [this.index] }
423
+ }));
424
+ this.$item.classList.toggle('dragging', false);
425
+ this.$items.classList.toggle('dragging', false);
426
+ this.#canvas?.remove();
427
+ this.#canvas = null;
428
+ }
429
+
430
+ #handleItemDragStart(e: any) { e.detail.indexes.unshift(this.index); }
431
+ #handleItemDragEnd(e: any) { e.detail.indexes.unshift(this.index); }
432
+
433
+ #handleDragAboveEnter(e: DragEvent) {
434
+ this.dispatchEvent(new CustomEvent('itemdropenter', {
435
+ bubbles: true,
436
+ composed: true,
437
+ detail: {
438
+ indexes: [this.index],
439
+ callback: (isValid: boolean) => {
440
+ if (isValid) {
441
+ e.dataTransfer!.dropEffect = 'move';
442
+ }
443
+ }
444
+ }
445
+ }));
446
+ (e.target as HTMLElement).classList.toggle('drop', true);
447
+ }
448
+
449
+ #handleDragAboveLeave(e: DragEvent) {
450
+ (e.target as HTMLElement).classList.toggle('drop', false);
451
+ }
452
+
453
+ #dragOnTimer: number | undefined;
454
+ #handleDragOnEnter(e: DragEvent) {
455
+ this.dispatchEvent(new CustomEvent('itemdropenter', {
456
+ bubbles: true,
457
+ composed: true,
458
+ detail: {
459
+ indexes: [this.index],
460
+ callback: (isValid: boolean) => {
461
+ if (isValid) {
462
+ e.dataTransfer!.dropEffect = 'move';
463
+ }
464
+ }
465
+ }
466
+ }));
467
+ (e.target as HTMLElement).classList.toggle('drop', true);
468
+ this.#dragOnTimer = window.setTimeout(() => {
469
+ this.#handleToggleClick();
470
+ }, 1500);
471
+ }
472
+
473
+ #handleDragOnLeave(e: DragEvent) {
474
+ window.clearTimeout(this.#dragOnTimer);
475
+ (e.target as HTMLElement).classList.toggle('drop', false);
476
+ }
477
+
478
+ #handleDragBelowEnter(e: DragEvent) {
479
+ this.dispatchEvent(new CustomEvent('itemdropenter', {
480
+ bubbles: true,
481
+ composed: true,
482
+ detail: {
483
+ indexes: [this.index],
484
+ callback: (isValid: boolean) => {
485
+ if (isValid) {
486
+ e.dataTransfer!.dropEffect = 'move';
487
+ }
488
+ }
489
+ }
490
+ }));
491
+ (e.target as HTMLElement).classList.toggle('drop', true);
492
+ }
493
+
494
+ #handleDragBelowLeave(e: DragEvent) {
495
+ (e.target as HTMLElement).classList.toggle('drop', false);
496
+ }
497
+
498
+ #handleDragOver(e: DragEvent) {
499
+ e.preventDefault();
500
+ e.dataTransfer!.dropEffect = 'move';
501
+ }
502
+
503
+ #handleDragAbove(e: DragEvent) {
504
+ (e.target as HTMLElement).classList.toggle('drop', false);
505
+ this.dispatchEvent(new CustomEvent('move', {
506
+ bubbles: true,
507
+ composed: true,
508
+ detail: { indexes: [this.index], position: 'before' }
509
+ }));
510
+ }
511
+
512
+ #handleDropOn(e: DragEvent) {
513
+ (e.target as HTMLElement).classList.toggle('drop', false);
514
+ this.dispatchEvent(new CustomEvent('move', {
515
+ bubbles: true,
516
+ composed: true,
517
+ detail: { indexes: [this.index], position: 'on' }
518
+ }));
519
+ }
520
+
521
+ #handleDropBelow(e: DragEvent) {
522
+ (e.target as HTMLElement).classList.toggle('drop', false);
523
+ this.dispatchEvent(new CustomEvent('move', {
524
+ bubbles: true,
525
+ composed: true,
526
+ detail: { indexes: [this.index], position: 'after' }
527
+ }));
528
+ }
529
+
530
+ }
File without changes