@pictogrammers/components 0.5.15 → 0.5.18

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 +96 -24
  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
package/pg/tree/tree.ts CHANGED
@@ -1,227 +1,385 @@
1
- import { Component, Prop, Part, forEach, getProxyValue } from '@pictogrammers/element';
2
-
3
- import PgTreeItem from '../treeItem/treeItem';
4
-
5
- import template from './tree.html';
6
- import style from './tree.css';
7
-
8
- export type SelectedTreeItem = {
9
- indexes: number[];
10
- remove: () => void;
11
- getData: () => any;
12
- getParentData: () => any;
13
- }
14
-
15
- function move(arr, fromIndex, toIndex) {
16
- const [element] = arr.splice(fromIndex, 1);
17
- arr.splice(toIndex, 0, element);
18
- }
19
-
20
- @Component({
21
- selector: 'pg-tree',
22
- style,
23
- template
24
- })
25
- export default class PgTree extends HTMLElement {
26
- @Prop() items: any[] = [];
27
-
28
- @Part() $items: HTMLDivElement;
29
-
30
- #selectedIndexes = new Map();
31
-
32
- connectedCallback() {
33
- forEach({
34
- container: this.$items,
35
- items: this.items,
36
- type: (item) => {
37
- return PgTreeItem;
38
- }
39
- });
40
- this.$items.addEventListener('action', (e: any) => {
41
- e.stopPropagation();
42
- this.dispatchEvent(new CustomEvent('action', {
43
- detail: {
44
- actionIndex: e.detail.actionIndex,
45
- item: this.#wrap(e.detail.indexes),
46
- }
47
- }));
48
- });
49
- this.$items.addEventListener('move', (e: any) => {
50
- const { indexes, position } = e.detail;
51
- e.stopPropagation();
52
- this.dispatchEvent(new CustomEvent('move', {
53
- detail: {
54
- item: this.#wrap(e.detail.indexes),
55
- position,
56
- }
57
- }));
58
- });
59
- this.$items.addEventListener('rename', (e: any) => {
60
- const { indexes, label } = e.detail;
61
- e.stopPropagation();
62
- this.dispatchEvent(new CustomEvent('rename', {
63
- detail: {
64
- item: this.#wrap(indexes),
65
- label,
66
- }
67
- }));
68
- });
69
- this.$items.addEventListener('toggle', (e: any) => {
70
- const { indexes } = e.detail;
71
- let item = this.#getItem(indexes);
72
- item.expanded = !item.expanded;
73
- });
74
- this.$items.addEventListener('select', (e: any) => {
75
- e.stopPropagation();
76
- const { indexes, type, ctrlKey, shiftKey } = e.detail;
77
- const item = this.#getItem(indexes);
78
- const selectedCount = this.#selectedIndexes.size;
79
- const unproxyItem = getProxyValue(item);
80
- if (!ctrlKey && selectedCount && !this.#selectedIndexes.has(unproxyItem)) {
81
- this.#selectedIndexes.forEach((x: any) => this.#getItem(x).selected = false);
82
- this.#selectedIndexes.clear();
83
- }
84
- item.selected = type === 'rename' ? true : !item.selected;
85
- if (item.selected) {
86
- this.#selectedIndexes.set(unproxyItem, indexes);
87
- } else {
88
- this.#selectedIndexes.delete(unproxyItem);
89
- }
90
- // Dispatch Event
91
- this.dispatchEvent(new CustomEvent('select', {
92
- detail: {
93
- items: [...this.#selectedIndexes.values()].map((indexes: any) => {
94
- return this.#wrap(indexes);
95
- }),
96
- }
97
- }));
98
- });
99
- this.$items.addEventListener('keydown', (e: any) => {
100
- if (e.key === 'Delete') {
101
- this.#selectedIndexes.forEach((indexes: number[]) => {
102
- this.#removeItem(indexes);
103
- });
104
- this.#selectedIndexes.clear();
105
- }
106
- });
107
- this.$items.addEventListener('itemdragstart', (e: any) => {
108
- const { indexes, callback, ctrlKey, shiftKey } = e.detail;
109
- console.log('drag valid', indexes);
110
- const item = this.#getItem(indexes);
111
- const unproxyItem = getProxyValue(item);
112
- if (!ctrlKey && this.#selectedIndexes.size) {
113
- this.#selectedIndexes.forEach((x: any) => this.#getItem(x).selected = false);
114
- this.#selectedIndexes.clear();
115
- }
116
- item.selected = true;
117
- this.#selectedIndexes.set(unproxyItem, indexes);
118
- let count = 0;
119
- this.#selectedIndexes.forEach((indexes) => {
120
- const item = this.#getItem(indexes);
121
- count = this.#getDragCount(item, count);
122
- });
123
- callback(count);
124
- this.$items.classList.toggle('dragging', true);
125
- });
126
- this.$items.addEventListener('itemdragend', (e: any) => {
127
- this.$items.classList.toggle('dragging', false);
128
- });
129
- this.$items.addEventListener('itemdropenter', this.#handleDropEnter.bind(this));
130
- }
131
-
132
- #getDragCount(item, count) {
133
- count += 1
134
- if (item.items && item.items.length > 0) {
135
- item.items.forEach((itm) => {
136
- count = this.#getDragCount(itm, count);
137
- });
138
- }
139
- return count;
140
- }
141
-
142
- #handleDropEnter(e: any) {
143
- const { indexes, callback } = e.detail;
144
- const excludes = this.#calculateDragExcludes();
145
- console.log('valid???', indexes, excludes);
146
- const joined = indexes.join(',') as string;
147
- const isInvalid = excludes.some(exclude => joined.startsWith(exclude));
148
- callback(!isInvalid, (dropEffect) => {
149
- e.dataTransfer.effectAllowed = dropEffect;
150
- });
151
- }
152
-
153
- #removeItem(indexes: number[]) {
154
- const deleteIndex = indexes[indexes.length - 1];
155
- const tempIndexes = indexes.slice(0, indexes.length - 1);
156
- const item = tempIndexes.reduce((item: any, index) => {
157
- return item.items[index];
158
- }, this);
159
- item.items.splice(deleteIndex, 1);
160
- }
161
-
162
- #getItem(indexes: number[]) {
163
- return indexes.reduce((item: any, index) => {
164
- return item.items[index];
165
- }, this);
166
- }
167
-
168
- /**
169
- * Appends helper methods for selected
170
- *
171
- * @param indexes indexes
172
- */
173
- #wrap(indexes: number[]) {
174
- return {
175
- indexes,
176
- remove: () => {
177
- this.#removeItem(indexes);
178
- this.#selectedIndexes.clear();
179
- },
180
- getData: () => {
181
- return this.#getItem(indexes);
182
- },
183
- getParentData: () => {
184
- const parent = indexes.slice(0, indexes.length - 1);
185
- if (parent.length === 0) {
186
- return this;
187
- }
188
- return this.#getItem(parent);
189
- },
190
- move: (item, position) => {
191
- console.log(item, position);
192
- const index = item.indexes[item.indexes.length - 1];
193
- const parent = item.indexes.slice(0, item.indexes.length - 1);
194
- const cache = this.#getItem(indexes);
195
- this.#removeItem(indexes);
196
- if (position === 'on') {
197
- this.#getItem(item.indexes).items.push(cache);
198
- this.#getItem(item.indexes).expanded = true;
199
- } else if (parent.length === 0) {
200
- this.items.splice(position === 'after' ? index + 1 : index, 0, cache);
201
- } else {
202
- this.#getItem(parent).items.splice(position === 'after' ? index + 1 : index, 0, cache);
203
- }
204
- this.#selectedIndexes.clear();
205
- this.$items.classList.toggle('dragging', false);
206
- }
207
- }
208
- }
209
-
210
- render(changes) {
211
- if (changes.items) {
212
- console.log('yay', this.items.map(x => x));
213
- }
214
- }
215
-
216
- unselect(indexes: number[]) {
217
-
218
- }
219
-
220
- #calculateDragExcludes() {
221
- const exclude: string[] = [];
222
- this.#selectedIndexes.forEach((indexes) => {
223
- exclude.push(indexes.join(','));
224
- });
225
- return exclude;
226
- }
227
- }
1
+ import { Component, Prop, Part, forEach, getProxyValue } from '@pictogrammers/element';
2
+
3
+ import PgTreeItem from '../treeItem/treeItem';
4
+
5
+ import template from './tree.html';
6
+ import style from './tree.css';
7
+
8
+ export type SelectedTreeItem = {
9
+ indexes: number[];
10
+ remove: () => void;
11
+ getData: () => any;
12
+ getParentData: () => any;
13
+ move: (item: SelectedTreeItem, position: string) => void;
14
+ }
15
+
16
+ @Component({
17
+ selector: 'pg-tree',
18
+ style,
19
+ template
20
+ })
21
+ export default class PgTree extends HTMLElement {
22
+ @Prop() items: any[] = [];
23
+
24
+ @Part() $items: HTMLDivElement;
25
+
26
+ #selectedIndexes = new Map();
27
+ #lastSelectedIndexes: number[] | null = null;
28
+ #draggingElements: Set<HTMLElement> = new Set();
29
+
30
+ connectedCallback() {
31
+ forEach({
32
+ container: this.$items,
33
+ items: this.items,
34
+ type: () => PgTreeItem
35
+ });
36
+ this.$items.addEventListener('action', (e: any) => {
37
+ e.stopPropagation();
38
+ this.dispatchEvent(new CustomEvent('action', {
39
+ detail: {
40
+ actionIndex: e.detail.actionIndex,
41
+ item: this.#wrap(e.detail.indexes),
42
+ }
43
+ }));
44
+ });
45
+ this.$items.addEventListener('move', (e: any) => {
46
+ e.stopPropagation();
47
+ const selectedIdxsList = [...this.#selectedIndexes.values()] as number[][];
48
+ this.#batchMove(selectedIdxsList, e.detail.indexes, e.detail.position);
49
+ this.dispatchEvent(new CustomEvent('move', {
50
+ detail: { position: e.detail.position }
51
+ }));
52
+ this.dispatchEvent(new CustomEvent('select', {
53
+ detail: {
54
+ items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
55
+ }
56
+ }));
57
+ });
58
+ this.$items.addEventListener('rename', (e: any) => {
59
+ e.stopPropagation();
60
+ this.dispatchEvent(new CustomEvent('rename', {
61
+ detail: {
62
+ item: this.#wrap(e.detail.indexes),
63
+ label: e.detail.label,
64
+ }
65
+ }));
66
+ });
67
+ this.$items.addEventListener('toggle', (e: any) => {
68
+ const item = this.#getItem(e.detail.indexes);
69
+ item.expanded = !item.expanded;
70
+ });
71
+ this.$items.addEventListener('expand', (e: any) => {
72
+ this.#getItem(e.detail.indexes).expanded = true;
73
+ });
74
+ this.$items.addEventListener('menu', (e: any) => {
75
+ e.stopPropagation();
76
+ this.dispatchEvent(new CustomEvent('menu', {
77
+ detail: {
78
+ item: this.#wrap(e.detail.indexes),
79
+ x: e.detail.x,
80
+ y: e.detail.y,
81
+ }
82
+ }));
83
+ });
84
+ this.$items.addEventListener('select', (e: any) => {
85
+ e.stopPropagation();
86
+ const { indexes, type, ctrlKey, shiftKey } = e.detail;
87
+ const item = this.#getItem(indexes);
88
+ const unproxyItem = getProxyValue(item);
89
+
90
+ if (shiftKey && this.#lastSelectedIndexes) {
91
+ const visible = this.#getVisibleIndexes();
92
+ const lastPos = visible.findIndex(v => v.join(',') === this.#lastSelectedIndexes!.join(','));
93
+ const currPos = visible.findIndex(v => v.join(',') === indexes.join(','));
94
+ this.#selectedIndexes.forEach((x: any) => this.#getItem(x).selected = false);
95
+ this.#selectedIndexes.clear();
96
+ const start = Math.min(lastPos, currPos);
97
+ const end = Math.max(lastPos, currPos);
98
+ for (let i = start; i <= end; i++) {
99
+ const rangeItem = this.#getItem(visible[i]);
100
+ rangeItem.selected = true;
101
+ this.#selectedIndexes.set(getProxyValue(rangeItem), visible[i]);
102
+ }
103
+ } else {
104
+ if (!ctrlKey && this.#selectedIndexes.size && !this.#selectedIndexes.has(unproxyItem)) {
105
+ this.#selectedIndexes.forEach((x: any) => this.#getItem(x).selected = false);
106
+ this.#selectedIndexes.clear();
107
+ }
108
+ item.selected = type === 'rename' ? true : !item.selected;
109
+ if (item.selected) {
110
+ this.#selectedIndexes.set(unproxyItem, indexes);
111
+ this.#lastSelectedIndexes = indexes;
112
+ } else {
113
+ this.#selectedIndexes.delete(unproxyItem);
114
+ }
115
+ }
116
+
117
+ this.dispatchEvent(new CustomEvent('select', {
118
+ detail: {
119
+ items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
120
+ }
121
+ }));
122
+ });
123
+ this.$items.addEventListener('up', (e: any) => {
124
+ e.stopPropagation();
125
+ const visible = this.#getVisibleIndexes();
126
+ const idx = visible.findIndex(v => v.join(',') === e.detail.indexes.join(','));
127
+ if (idx > 0) {
128
+ this.#focusItem(visible[idx - 1]);
129
+ }
130
+ });
131
+ this.$items.addEventListener('down', (e: any) => {
132
+ e.stopPropagation();
133
+ const visible = this.#getVisibleIndexes();
134
+ const idx = visible.findIndex(v => v.join(',') === e.detail.indexes.join(','));
135
+ if (idx < visible.length - 1) {
136
+ this.#focusItem(visible[idx + 1]);
137
+ }
138
+ });
139
+ this.$items.addEventListener('keydown', (e: any) => {
140
+ if (e.key === 'Escape') {
141
+ this.#selectedIndexes.forEach((idxs: any) => this.#getItem(idxs).selected = false);
142
+ this.#selectedIndexes.clear();
143
+ this.#lastSelectedIndexes = null;
144
+ this.dispatchEvent(new CustomEvent('select', {
145
+ detail: { items: [] }
146
+ }));
147
+ } else if (e.key === 'Delete') {
148
+ const toRemove = [...this.#selectedIndexes.values()] as number[][];
149
+ // Remove higher sibling indexes first to avoid index shifts
150
+ toRemove.sort((a, b) => {
151
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
152
+ const ai = i < a.length ? a[i] : -1;
153
+ const bi = i < b.length ? b[i] : -1;
154
+ if (ai !== bi) return bi - ai;
155
+ }
156
+ return 0;
157
+ });
158
+ toRemove.forEach((indexes: number[]) => this.#removeItem(indexes));
159
+ this.#selectedIndexes.clear();
160
+ this.#lastSelectedIndexes = null;
161
+ }
162
+ });
163
+ this.$items.addEventListener('itemdragstart', (e: any) => {
164
+ const { indexes, callback, ctrlKey } = e.detail;
165
+ const item = this.#getItem(indexes);
166
+ const unproxyItem = getProxyValue(item);
167
+ if (!ctrlKey && this.#selectedIndexes.size && !this.#selectedIndexes.has(unproxyItem)) {
168
+ this.#selectedIndexes.forEach((x: any) => this.#getItem(x).selected = false);
169
+ this.#selectedIndexes.clear();
170
+ }
171
+ item.selected = true;
172
+ this.#selectedIndexes.set(unproxyItem, indexes);
173
+ let count = 0;
174
+ this.#selectedIndexes.forEach((idxs: any) => {
175
+ count = this.#getDragCount(this.#getItem(idxs), count);
176
+ });
177
+ callback(count);
178
+ this.#draggingElements.clear();
179
+ this.#selectedIndexes.forEach((idxs: any) => {
180
+ const el = this.#getItemElement(idxs) as any;
181
+ if (el) {
182
+ el.$item.classList.add('dragging');
183
+ el.$items.classList.add('dragging');
184
+ this.#draggingElements.add(el.$item);
185
+ this.#draggingElements.add(el.$items);
186
+ }
187
+ });
188
+ this.$items.classList.toggle('dragging', true);
189
+ // Sync selection to consumers so #selectedItems reflects the dragged item
190
+ this.dispatchEvent(new CustomEvent('select', {
191
+ detail: {
192
+ items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
193
+ }
194
+ }));
195
+ });
196
+ this.$items.addEventListener('itemdragend', () => {
197
+ this.#draggingElements.forEach(el => el.classList.remove('dragging'));
198
+ this.#draggingElements.clear();
199
+ this.$items.classList.toggle('dragging', false);
200
+ this.#clearDropHighlights(this.$items);
201
+ });
202
+ this.$items.addEventListener('itemdropenter', this.#handleDropEnter.bind(this));
203
+ }
204
+
205
+ #getDragCount(item: any, count: number): number {
206
+ count += 1;
207
+ if (item.items?.length) {
208
+ item.items.forEach((itm: any) => {
209
+ count = this.#getDragCount(itm, count);
210
+ });
211
+ }
212
+ return count;
213
+ }
214
+
215
+ #handleDropEnter(e: any) {
216
+ const { indexes, callback } = e.detail;
217
+ const excludes = this.#calculateDragExcludes();
218
+ const joined = indexes.join(',') as string;
219
+ // Use comma-terminated prefix to prevent "0,1" from matching "0,10"
220
+ const isInvalid = excludes.some(ex => joined === ex || joined.startsWith(ex + ','));
221
+ callback(!isInvalid);
222
+ }
223
+
224
+ #removeItem(indexes: number[]) {
225
+ const deleteIndex = indexes[indexes.length - 1];
226
+ const parentIndexes = indexes.slice(0, indexes.length - 1);
227
+ const parent = parentIndexes.reduce((acc: any, i) => acc.items[i], this);
228
+ parent.items.splice(deleteIndex, 1);
229
+ }
230
+
231
+ #getItem(indexes: number[]) {
232
+ return indexes.reduce((acc: any, i) => acc.items[i], this);
233
+ }
234
+
235
+ #getItemElement(indexes: number[]): PgTreeItem | null {
236
+ let container: HTMLElement = this.$items;
237
+ let element: PgTreeItem | null = null;
238
+ for (const i of indexes) {
239
+ element = container.children[i] as PgTreeItem;
240
+ if (!element) return null;
241
+ container = (element as any).$items;
242
+ if (!container) return null;
243
+ }
244
+ return element;
245
+ }
246
+
247
+ #focusItem(indexes: number[]) {
248
+ const element = this.#getItemElement(indexes);
249
+ if (element) {
250
+ (element as any).$labelButton.focus();
251
+ }
252
+ }
253
+
254
+ #getVisibleIndexes(): number[][] {
255
+ const result: number[][] = [];
256
+ const traverse = (items: any[], prefix: number[]) => {
257
+ items.forEach((item: any, i: number) => {
258
+ const indexes = [...prefix, i];
259
+ result.push(indexes);
260
+ if (item.expanded && item.items?.length) {
261
+ traverse(item.items, indexes);
262
+ }
263
+ });
264
+ };
265
+ traverse(this.items, []);
266
+ return result;
267
+ }
268
+
269
+ #clearDropHighlights(container: HTMLElement) {
270
+ Array.from(container.children).forEach((child: any) => {
271
+ if (child.$dropabove) {
272
+ child.$dropabove.classList.remove('drop');
273
+ child.$dropon.classList.remove('drop');
274
+ child.$dropbelow.classList.remove('drop');
275
+ }
276
+ if (child.$items) {
277
+ this.#clearDropHighlights(child.$items);
278
+ }
279
+ });
280
+ }
281
+
282
+ #batchMove(selectedIdxsList: number[][], targetIdxs: number[], position: string) {
283
+ if (selectedIdxsList.length === 0) return;
284
+
285
+ // Ascending order preserves the original relative order of dragged items on insertion
286
+ const ascending = [...selectedIdxsList].sort((a, b) => {
287
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
288
+ const av = i < a.length ? a[i] : -1;
289
+ const bv = i < b.length ? b[i] : -1;
290
+ if (av !== bv) return av - bv;
291
+ }
292
+ return 0;
293
+ });
294
+
295
+ // Capture data references before any structural changes
296
+ const caches = ascending.map(idxs => this.#getItem(idxs));
297
+
298
+ // Count how many selected siblings precede the target — each one shifts its index down after removal
299
+ const targetParentStr = targetIdxs.slice(0, -1).join(',');
300
+ let targetShift = 0;
301
+ for (const sidxs of ascending) {
302
+ if (sidxs.slice(0, -1).join(',') === targetParentStr &&
303
+ sidxs[sidxs.length - 1] < targetIdxs[targetIdxs.length - 1]) {
304
+ targetShift++;
305
+ }
306
+ }
307
+ const adjustedTargetIdxs = [
308
+ ...targetIdxs.slice(0, -1),
309
+ targetIdxs[targetIdxs.length - 1] - targetShift
310
+ ];
311
+
312
+ // Remove descending so higher sibling indexes don't shift lower ones mid-loop
313
+ [...ascending].reverse().forEach(idxs => this.#removeItem(idxs));
314
+
315
+ // Rebuild selection map with post-move positions so items stay selected
316
+ this.#selectedIndexes.clear();
317
+ this.#lastSelectedIndexes = null;
318
+
319
+ if (position === 'on') {
320
+ const targetData = this.#getItem(adjustedTargetIdxs);
321
+ const prevLen = Array.isArray(targetData.items) ? targetData.items.length : 0;
322
+ if (!Array.isArray(targetData.items)) targetData.items = [];
323
+ caches.forEach((cache, i) => {
324
+ targetData.items.push(cache);
325
+ const newIdxs = [...adjustedTargetIdxs, prevLen + i];
326
+ this.#selectedIndexes.set(getProxyValue(this.#getItem(newIdxs)), newIdxs);
327
+ });
328
+ targetData.expanded = true;
329
+ } else {
330
+ const adjustedParent = adjustedTargetIdxs.slice(0, -1);
331
+ const adjustedIdx = adjustedTargetIdxs[adjustedTargetIdxs.length - 1];
332
+ const insertAt = position === 'after' ? adjustedIdx + 1 : adjustedIdx;
333
+ if (adjustedParent.length === 0) {
334
+ this.items.splice(insertAt, 0, ...caches);
335
+ } else {
336
+ this.#getItem(adjustedParent).items.splice(insertAt, 0, ...caches);
337
+ }
338
+ caches.forEach((_, i) => {
339
+ const newIdxs = [...adjustedParent, insertAt + i];
340
+ this.#selectedIndexes.set(getProxyValue(this.#getItem(newIdxs)), newIdxs);
341
+ });
342
+ }
343
+
344
+ this.$items.classList.toggle('dragging', false);
345
+ }
346
+
347
+ #wrap(indexes: number[]): SelectedTreeItem {
348
+ return {
349
+ indexes,
350
+ remove: () => {
351
+ this.#removeItem(indexes);
352
+ this.#selectedIndexes.clear();
353
+ this.#lastSelectedIndexes = null;
354
+ },
355
+ getData: () => this.#getItem(indexes),
356
+ getParentData: () => {
357
+ const parent = indexes.slice(0, indexes.length - 1);
358
+ return parent.length === 0 ? this : this.#getItem(parent);
359
+ },
360
+ move: (item: SelectedTreeItem, position: string) => {
361
+ this.#batchMove([indexes], item.indexes, position);
362
+ }
363
+ };
364
+ }
365
+
366
+ unselect(indexes: number[]) {
367
+ const item = this.#getItem(indexes);
368
+ const unproxyItem = getProxyValue(item);
369
+ item.selected = false;
370
+ this.#selectedIndexes.delete(unproxyItem);
371
+ }
372
+
373
+ unselectAll() {
374
+ this.#selectedIndexes.forEach((idxs: any) => this.#getItem(idxs).selected = false);
375
+ this.#selectedIndexes.clear();
376
+ }
377
+
378
+ #calculateDragExcludes(): string[] {
379
+ const exclude: string[] = [];
380
+ this.#selectedIndexes.forEach((indexes: number[]) => {
381
+ exclude.push(indexes.join(','));
382
+ });
383
+ return exclude;
384
+ }
385
+ }