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