@haiyue/ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +1 -1
  3. package/README.md +226 -2
  4. package/dist/border-beam.d.ts +26 -0
  5. package/dist/border-beam.js +174 -0
  6. package/dist/button.d.ts +10 -0
  7. package/dist/button.js +85 -0
  8. package/dist/checkbox.d.ts +2 -2
  9. package/dist/checkbox.js +11 -11
  10. package/dist/context-menu.d.ts +6 -6
  11. package/dist/context-menu.js +11 -9
  12. package/dist/dialog.d.ts +5 -5
  13. package/dist/dialog.js +18 -18
  14. package/dist/drawer.d.ts +49 -0
  15. package/dist/drawer.js +377 -0
  16. package/dist/dropdown.d.ts +9 -9
  17. package/dist/dropdown.js +9 -9
  18. package/dist/expandable.d.ts +32 -0
  19. package/dist/expandable.js +136 -0
  20. package/dist/history-controls.d.ts +40 -0
  21. package/dist/history-controls.js +123 -0
  22. package/dist/index.d.ts +36 -31
  23. package/dist/index.js +35 -85
  24. package/dist/input.d.ts +5 -5
  25. package/dist/input.js +13 -13
  26. package/dist/menu-shared.d.ts +4 -4
  27. package/dist/menu-shared.js +4 -4
  28. package/dist/notification.d.ts +55 -0
  29. package/dist/notification.js +357 -0
  30. package/dist/radio.d.ts +2 -2
  31. package/dist/radio.js +12 -12
  32. package/dist/range.d.ts +74 -0
  33. package/dist/range.js +393 -0
  34. package/dist/select.d.ts +6 -6
  35. package/dist/select.js +20 -20
  36. package/dist/split.d.ts +6 -6
  37. package/dist/split.js +14 -14
  38. package/dist/tabs.d.ts +10 -6
  39. package/dist/tabs.js +94 -30
  40. package/dist/tooltip.d.ts +4 -4
  41. package/dist/tooltip.js +9 -9
  42. package/dist/tree-model.d.ts +15 -7
  43. package/dist/tree-model.js +93 -80
  44. package/dist/tree-node.d.ts +3 -3
  45. package/dist/tree-node.js +3 -3
  46. package/dist/tree-types.d.ts +14 -14
  47. package/dist/tree.d.ts +9 -7
  48. package/dist/tree.js +68 -66
  49. package/dist/virtual-list.d.ts +61 -0
  50. package/dist/virtual-list.js +324 -0
  51. package/package.json +100 -7
  52. package/themes/haiyue-dark.css +46 -0
  53. package/themes/haiyue-light.css +46 -0
  54. package/src/checkbox.ts +0 -215
  55. package/src/context-menu.ts +0 -248
  56. package/src/dialog.ts +0 -195
  57. package/src/dropdown.ts +0 -249
  58. package/src/index.ts +0 -192
  59. package/src/input.ts +0 -150
  60. package/src/menu-shared.ts +0 -65
  61. package/src/radio.ts +0 -216
  62. package/src/select.ts +0 -271
  63. package/src/split.ts +0 -306
  64. package/src/tabs.ts +0 -182
  65. package/src/tooltip.ts +0 -321
  66. package/src/tree-model.ts +0 -305
  67. package/src/tree-node.ts +0 -67
  68. package/src/tree-types.ts +0 -47
  69. package/src/tree.ts +0 -1002
package/dist/tabs.js CHANGED
@@ -1,10 +1,12 @@
1
- export class GETabs extends HTMLElement {
1
+ export class HYTabs extends HTMLElement {
2
2
  _style = document.createElement('style');
3
3
  _tabs = document.createElement('div');
4
4
  _panels = document.createElement('div');
5
+ _instanceId = `hy-tabs-${globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2)}`;
5
6
  _options = [];
7
+ _pendingFocusValue = null;
6
8
  static get observedAttributes() {
7
- return ['options', 'value'];
9
+ return ['options', 'value', 'aria-label'];
8
10
  }
9
11
  constructor() {
10
12
  super();
@@ -18,8 +20,8 @@ export class GETabs extends HTMLElement {
18
20
  height: 100%;
19
21
  min-width: 0;
20
22
  min-height: 0;
21
- color: var(--ge-text-color, #d8e2f2);
22
- background: var(--ge-panel-bg-color, #171d28);
23
+ color: var(--hy-text-color, #d8e2f2);
24
+ background: var(--hy-panel-bg-color, #171d28);
23
25
  font: 12px system-ui, sans-serif;
24
26
  }
25
27
  .tabs {
@@ -27,30 +29,35 @@ export class GETabs extends HTMLElement {
27
29
  align-items: end;
28
30
  gap: 2px;
29
31
  min-width: 0;
32
+ overflow-x: auto;
33
+ overflow-y: hidden;
34
+ scrollbar-width: none;
30
35
  padding: 4px 6px 0;
31
- border-bottom: 1px solid var(--ge-border-color, #303746);
32
- background: var(--ge-tabs-bar-bg-color, var(--ge-menu-bg-color, var(--ge-surface-elevated-color, #1b2230)));
36
+ border-bottom: 1px solid var(--hy-border-color, #303746);
37
+ background: var(--hy-tabs-bar-bg-color, var(--hy-menu-bg-color, var(--hy-surface-elevated-color, #1b2230)));
33
38
  }
34
39
  button {
35
40
  height: 28px;
36
41
  min-width: 0;
42
+ flex-shrink: 0;
43
+ white-space: nowrap;
37
44
  padding: 0 10px;
38
45
  border: 1px solid transparent;
39
46
  border-bottom: 0;
40
47
  border-radius: 4px 4px 0 0;
41
- color: var(--ge-secondary-text-color, #8fa7c8);
48
+ color: var(--hy-secondary-text-color, #8fa7c8);
42
49
  background: transparent;
43
50
  font: inherit;
44
51
  cursor: pointer;
45
52
  }
46
53
  button:hover {
47
- color: var(--ge-text-color, #d8e2f2);
48
- background: var(--ge-hover-bg-color, #222b3a);
54
+ color: var(--hy-text-color, #d8e2f2);
55
+ background: var(--hy-hover-bg-color, #222b3a);
49
56
  }
50
57
  button.active {
51
- color: var(--ge-active-text-color, #eef3ff);
52
- border-color: var(--ge-border-color, #303746);
53
- background: var(--ge-panel-bg-color, #171d28);
58
+ color: var(--hy-active-text-color, #eef3ff);
59
+ border-color: var(--hy-border-color, #303746);
60
+ background: var(--hy-panel-bg-color, #171d28);
54
61
  }
55
62
  button:disabled {
56
63
  opacity: 0.45;
@@ -66,6 +73,7 @@ export class GETabs extends HTMLElement {
66
73
  }
67
74
  `;
68
75
  this._tabs.className = 'tabs';
76
+ this._tabs.setAttribute('role', 'tablist');
69
77
  this._panels.className = 'panels';
70
78
  root.append(this._style, this._tabs, this._panels);
71
79
  }
@@ -74,11 +82,16 @@ export class GETabs extends HTMLElement {
74
82
  this._render();
75
83
  }
76
84
  attributeChangedCallback(name, _oldValue, newValue) {
85
+ if (_oldValue === newValue)
86
+ return;
77
87
  if (name === 'options')
78
88
  this._readOptionsAttribute();
79
- if (name === 'value' && newValue === null && this._options[0]) {
80
- this.setAttribute('value', this._options[0].value);
81
- return;
89
+ if (name === 'value' && newValue === null) {
90
+ const value = this._resolveValue();
91
+ if (value) {
92
+ this.setAttribute('value', value);
93
+ return;
94
+ }
82
95
  }
83
96
  this._render();
84
97
  }
@@ -87,9 +100,9 @@ export class GETabs extends HTMLElement {
87
100
  }
88
101
  set options(value) {
89
102
  this._options = Array.isArray(value) ? value : [];
90
- if (!this._options.some(item => item.value === this.value)) {
91
- this.value = this._options[0]?.value ?? '';
92
- }
103
+ const resolved = this._resolveValue();
104
+ if (resolved !== this.value)
105
+ this.value = resolved;
93
106
  this._render();
94
107
  }
95
108
  get value() {
@@ -97,12 +110,13 @@ export class GETabs extends HTMLElement {
97
110
  }
98
111
  set value(value) {
99
112
  this.setAttribute('value', value);
100
- this._render();
101
113
  }
102
114
  _syncFromAttributes() {
103
- this._readOptionsAttribute();
104
- if (!this.value && this._options[0])
105
- this.setAttribute('value', this._options[0].value);
115
+ if (this.hasAttribute('options'))
116
+ this._readOptionsAttribute();
117
+ const value = this._resolveValue();
118
+ if (value !== this.value)
119
+ this.setAttribute('value', value);
106
120
  }
107
121
  _readOptionsAttribute() {
108
122
  const raw = this.getAttribute('options');
@@ -121,29 +135,79 @@ export class GETabs extends HTMLElement {
121
135
  : [];
122
136
  }
123
137
  catch (error) {
124
- console.warn('Invalid ge-tabs options attribute.', error);
138
+ console.warn('Invalid hy-tabs options attribute.', error);
125
139
  }
126
140
  }
127
141
  _render() {
142
+ const focusedValue = this._pendingFocusValue
143
+ ?? [...this._tabs.querySelectorAll('button')].find(button => button === this.shadowRoot?.activeElement)?.dataset.value
144
+ ?? null;
145
+ this._pendingFocusValue = null;
128
146
  this._tabs.replaceChildren();
129
147
  this._panels.replaceChildren();
130
- const value = this.value || this._options[0]?.value || '';
131
- for (const option of this._options) {
148
+ this._tabs.setAttribute('aria-label', this.getAttribute('aria-label') ?? 'Tabs');
149
+ const enabled = this._options.filter(option => !option.disabled);
150
+ const value = this._resolveValue(enabled);
151
+ if (value !== this.value) {
152
+ this.setAttribute('value', value);
153
+ return;
154
+ }
155
+ const focusValue = enabled.some(option => option.value === value) ? value : enabled[0]?.value;
156
+ this._options.forEach((option, index) => {
132
157
  const button = document.createElement('button');
158
+ const panel = document.createElement('div');
133
159
  button.type = 'button';
160
+ button.id = `${this._instanceId}-tab-${index}`;
161
+ button.dataset.value = option.value;
162
+ button.setAttribute('role', 'tab');
163
+ button.setAttribute('aria-selected', String(option.value === value));
164
+ panel.id = `${this._instanceId}-panel-${index}`;
165
+ panel.hidden = option.value !== value;
166
+ panel.setAttribute('role', 'tabpanel');
167
+ panel.setAttribute('aria-labelledby', button.id);
168
+ button.setAttribute('aria-controls', panel.id);
169
+ button.tabIndex = option.value === focusValue && !option.disabled ? 0 : -1;
134
170
  button.textContent = option.label;
135
171
  button.disabled = Boolean(option.disabled);
136
172
  button.classList.toggle('active', option.value === value);
137
173
  button.addEventListener('click', () => this._select(option.value));
174
+ button.addEventListener('keydown', event => {
175
+ if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key) || !enabled.length)
176
+ return;
177
+ event.preventDefault();
178
+ const position = enabled.findIndex(item => item.value === option.value);
179
+ const next = event.key === 'Home' ? 0 : event.key === 'End' ? enabled.length - 1
180
+ : (position + (event.key === 'ArrowLeft' ? -1 : 1) + enabled.length) % enabled.length;
181
+ this._pendingFocusValue = enabled[next].value;
182
+ this._select(enabled[next].value);
183
+ });
184
+ const slot = document.createElement('slot');
185
+ slot.name = option.value;
186
+ panel.append(slot);
138
187
  this._tabs.append(button);
188
+ this._panels.append(panel);
189
+ });
190
+ if (focusedValue) {
191
+ const restoreValue = enabled.some(option => option.value === focusedValue) ? focusedValue : focusValue;
192
+ if (restoreValue)
193
+ this._focusTab(restoreValue);
139
194
  }
140
- const slot = document.createElement('slot');
141
- slot.name = value;
142
- this._panels.append(slot);
195
+ }
196
+ _focusTab(value) {
197
+ [...this._tabs.querySelectorAll('button')].find(button => button.dataset.value === value)?.focus();
198
+ }
199
+ _resolveValue(enabled = this._options.filter(option => !option.disabled)) {
200
+ if (enabled.some(option => option.value === this.value))
201
+ return this.value;
202
+ if (enabled[0])
203
+ return enabled[0].value;
204
+ return '';
143
205
  }
144
206
  _select(value) {
145
207
  if (value === this.value)
146
208
  return;
209
+ if (!this._options.some(option => option.value === value && !option.disabled))
210
+ return;
147
211
  this.setAttribute('value', value);
148
212
  const option = this._options.find(item => item.value === value) ?? null;
149
213
  this.dispatchEvent(new CustomEvent('tab-change', {
@@ -154,8 +218,8 @@ export class GETabs extends HTMLElement {
154
218
  }
155
219
  }
156
220
  export function defineTabsComponents() {
157
- if (!customElements.get('ge-tabs')) {
158
- customElements.define('ge-tabs', GETabs);
221
+ if (!customElements.get('hy-tabs')) {
222
+ customElements.define('hy-tabs', HYTabs);
159
223
  }
160
224
  }
161
225
  //# sourceMappingURL=tabs.js.map
package/dist/tooltip.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type GETooltipPlacement = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom';
2
- export declare class GETooltip extends HTMLElement {
1
+ export type HYTooltipPlacement = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom';
2
+ export declare class HYTooltip extends HTMLElement {
3
3
  private readonly _root;
4
4
  private readonly _style;
5
5
  private readonly _slot;
@@ -15,8 +15,8 @@ export declare class GETooltip extends HTMLElement {
15
15
  attributeChangedCallback(name: string): void;
16
16
  get label(): string;
17
17
  set label(value: string);
18
- get placement(): GETooltipPlacement;
19
- set placement(value: GETooltipPlacement);
18
+ get placement(): HYTooltipPlacement;
19
+ set placement(value: HYTooltipPlacement);
20
20
  get delay(): number;
21
21
  set delay(value: number);
22
22
  get arrow(): boolean;
package/dist/tooltip.js CHANGED
@@ -17,7 +17,7 @@ const TOOLTIP_PLACEMENTS = new Set([
17
17
  'rightTop',
18
18
  'rightBottom',
19
19
  ]);
20
- export class GETooltip extends HTMLElement {
20
+ export class HYTooltip extends HTMLElement {
21
21
  _root;
22
22
  _style = document.createElement('style');
23
23
  _slot = document.createElement('slot');
@@ -45,10 +45,10 @@ export class GETooltip extends HTMLElement {
45
45
  max-width: min(280px, calc(100vw - 16px));
46
46
  padding: 5px 8px;
47
47
  border-radius: 4px;
48
- color: var(--ge-tooltip-text-color, #f4f7fb);
49
- background: var(--ge-tooltip-bg-color, #0f141c);
50
- border: 1px solid var(--ge-tooltip-border-color, rgba(255, 255, 255, 0.08));
51
- box-shadow: 0 8px 24px var(--ge-tooltip-shadow-color, rgba(0, 0, 0, 0.35));
48
+ color: var(--hy-tooltip-text-color, #f4f7fb);
49
+ background: var(--hy-tooltip-bg-color, #0f141c);
50
+ border: 1px solid var(--hy-tooltip-border-color, rgba(255, 255, 255, 0.08));
51
+ box-shadow: 0 8px 24px var(--hy-tooltip-shadow-color, rgba(0, 0, 0, 0.35));
52
52
  font: 12px system-ui, sans-serif;
53
53
  line-height: 1.35;
54
54
  width: max-content;
@@ -63,8 +63,8 @@ export class GETooltip extends HTMLElement {
63
63
  position: absolute;
64
64
  width: ${ARROW_SIZE}px;
65
65
  height: ${ARROW_SIZE}px;
66
- background: var(--ge-tooltip-bg-color, #0f141c);
67
- border: solid var(--ge-tooltip-border-color, rgba(255, 255, 255, 0.08));
66
+ background: var(--hy-tooltip-bg-color, #0f141c);
67
+ border: solid var(--hy-tooltip-border-color, rgba(255, 255, 255, 0.08));
68
68
  border-width: 0 1px 1px 0;
69
69
  transform: rotate(45deg);
70
70
  }
@@ -279,8 +279,8 @@ function getAlignedLeft(hostRect, tooltipRect, placement) {
279
279
  return hostRect.left + (hostRect.width - tooltipRect.width) / 2;
280
280
  }
281
281
  export function defineTooltipComponents() {
282
- if (!customElements.get('ge-tooltip')) {
283
- customElements.define('ge-tooltip', GETooltip);
282
+ if (!customElements.get('hy-tooltip')) {
283
+ customElements.define('hy-tooltip', HYTooltip);
284
284
  }
285
285
  }
286
286
  //# sourceMappingURL=tooltip.js.map
@@ -1,6 +1,6 @@
1
- import type { FlatTreeNode, GETreeDropPosition, GETreeNodeData, IndexedTreeNode } from './tree-types.js';
2
- /** Mutable hierarchy model owned by ge-tree. It has no DOM dependency and is safe to unit test. */
3
- export declare class GETreeModel {
1
+ import type { FlatTreeNode, HYTreeDropPosition, HYTreeNodeData, IndexedTreeNode } from './tree-types.js';
2
+ /** Mutable hierarchy model owned by hy-tree. It has no DOM dependency and is safe to unit test. */
3
+ export declare class HYTreeModel {
4
4
  private _data;
5
5
  private readonly _expandedIds;
6
6
  private readonly _nodeIndex;
@@ -13,21 +13,29 @@ export declare class GETreeModel {
13
13
  get index(): ReadonlyMap<string, IndexedTreeNode>;
14
14
  get visibleIndex(): ReadonlyMap<string, number>;
15
15
  get isVisibilityDirty(): boolean;
16
- setData(data: GETreeNodeData[]): void;
16
+ setData(data: HYTreeNodeData[]): void;
17
17
  invalidateVisibility(): void;
18
18
  getVisibleNodes(): readonly FlatTreeNode[];
19
19
  getVisibleIds(): readonly string[];
20
20
  getIndexedNode(id: string): IndexedTreeNode | null;
21
21
  findNode(id: string): {
22
- node: GETreeNodeData;
23
- parent: GETreeNodeData | null;
22
+ node: HYTreeNodeData;
23
+ parent: HYTreeNodeData | null;
24
24
  } | null;
25
25
  topLevelSelection(ids: Iterable<string>): string[];
26
26
  removeNode(id: string): boolean;
27
- moveNode(sourceId: string, targetId: string, position: GETreeDropPosition): boolean;
27
+ moveNode(sourceId: string, targetId: string, position: HYTreeDropPosition): boolean;
28
+ private _createMovePlan;
29
+ private _visibleRange;
30
+ private _resolveMoveDestination;
31
+ private _refreshMovedSubtree;
28
32
  private _refreshSiblingIndexes;
29
33
  private _refreshSubtreeIndex;
30
34
  private _moveVisibleSubtree;
35
+ private _removeVisibleRange;
36
+ private _insertExpandedChildren;
37
+ private _resolveVisibleInsertAt;
38
+ private _insertVisibleSubtrees;
31
39
  private _collectVisibleSubtree;
32
40
  private _visibleSubtreeEnd;
33
41
  private _refreshVisibleIndexes;
@@ -1,5 +1,5 @@
1
- /** Mutable hierarchy model owned by ge-tree. It has no DOM dependency and is safe to unit test. */
2
- export class GETreeModel {
1
+ /** Mutable hierarchy model owned by hy-tree. It has no DOM dependency and is safe to unit test. */
2
+ export class HYTreeModel {
3
3
  _data = [];
4
4
  _expandedIds;
5
5
  _nodeIndex = new Map();
@@ -87,58 +87,74 @@ export class GETreeModel {
87
87
  moveNode(sourceId, targetId, position) {
88
88
  this._ensureIndex();
89
89
  this.getVisibleNodes();
90
+ const plan = this._createMovePlan(sourceId, targetId);
91
+ if (!plan)
92
+ return false;
93
+ const destination = this._resolveMoveDestination(plan, position);
94
+ if (!destination)
95
+ return false;
96
+ plan.sourceList.splice(plan.sourceIndex, 1);
97
+ destination.targetList.splice(destination.insertIndex, 0, plan.source.node);
98
+ this._refreshSiblingIndexes(plan.sourceList);
99
+ if (destination.targetList !== plan.sourceList)
100
+ this._refreshSiblingIndexes(destination.targetList);
101
+ this._refreshMovedSubtree(plan.source.node, destination.nextParent);
102
+ this._moveVisibleSubtree(plan.source.node, plan.start, plan.count, destination.nextParent, destination.expandedNextParent);
103
+ return true;
104
+ }
105
+ _createMovePlan(sourceId, targetId) {
90
106
  const source = this._nodeIndex.get(sourceId);
91
107
  const target = this._nodeIndex.get(targetId);
92
108
  if (!source || !target || sourceId === targetId)
93
- return false;
109
+ return null;
94
110
  if (target.ancestorIds.includes(sourceId))
95
- return false;
111
+ return null;
96
112
  const sourceList = source.parent?.children ?? this._data;
97
113
  const sourceIndex = source.index;
98
114
  if (sourceIndex < 0)
99
- return false;
100
- const previousDepth = source.depth;
101
- const visibleStart = this._visibleIndex.get(sourceId) ?? -1;
102
- let visibleCount = 0;
103
- if (visibleStart >= 0) {
104
- visibleCount = 1;
105
- while (visibleStart + visibleCount < this._visibleNodes.length
106
- && (this._visibleNodes[visibleStart + visibleCount]?.depth ?? -1) > previousDepth) {
107
- visibleCount++;
108
- }
115
+ return null;
116
+ return { source, target, sourceList, sourceIndex, ...this._visibleRange(source) };
117
+ }
118
+ _visibleRange(source) {
119
+ const start = this._visibleIndex.get(source.node.id) ?? -1;
120
+ if (start < 0)
121
+ return { start, count: 0 };
122
+ let count = 1;
123
+ while (start + count < this._visibleNodes.length
124
+ && (this._visibleNodes[start + count]?.depth ?? -1) > source.depth) {
125
+ count++;
109
126
  }
110
- sourceList.splice(sourceIndex, 1);
111
- let nextParent;
112
- let targetList;
113
- let insertIndex;
114
- let expandedNextParent = false;
127
+ return { start, count };
128
+ }
129
+ _resolveMoveDestination(plan, position) {
115
130
  if (position === 'inside') {
116
- target.node.children ??= [];
117
- nextParent = target.node;
118
- targetList = target.node.children;
119
- insertIndex = targetList.length;
120
- expandedNextParent = !this._expandedIds.has(target.node.id);
121
- this._expandedIds.add(target.node.id);
131
+ plan.target.node.children ??= [];
132
+ const expandedNextParent = !this._expandedIds.has(plan.target.node.id);
133
+ this._expandedIds.add(plan.target.node.id);
134
+ return {
135
+ nextParent: plan.target.node,
136
+ targetList: plan.target.node.children,
137
+ insertIndex: plan.target.node.children.length,
138
+ expandedNextParent,
139
+ };
122
140
  }
123
- else {
124
- nextParent = target.parent;
125
- targetList = target.parent?.children ?? this._data;
126
- let targetIndex = target.index;
127
- if (sourceList === targetList && sourceIndex < targetIndex)
128
- targetIndex--;
129
- if (targetIndex < 0)
130
- return false;
131
- insertIndex = position === 'before' ? targetIndex : targetIndex + 1;
132
- }
133
- targetList.splice(insertIndex, 0, source.node);
134
- this._refreshSiblingIndexes(sourceList);
135
- if (targetList !== sourceList)
136
- this._refreshSiblingIndexes(targetList);
141
+ const targetList = plan.target.parent?.children ?? this._data;
142
+ let targetIndex = plan.target.index;
143
+ if (plan.sourceList === targetList && plan.sourceIndex < targetIndex)
144
+ targetIndex--;
145
+ if (targetIndex < 0)
146
+ return null;
147
+ return {
148
+ nextParent: plan.target.parent,
149
+ targetList,
150
+ insertIndex: position === 'before' ? targetIndex : targetIndex + 1,
151
+ expandedNextParent: false,
152
+ };
153
+ }
154
+ _refreshMovedSubtree(source, nextParent) {
137
155
  const parentIndex = nextParent ? this._nodeIndex.get(nextParent.id) : null;
138
- const ancestorIds = parentIndex ? [...parentIndex.ancestorIds, nextParent.id] : [];
139
- this._refreshSubtreeIndex(source.node, nextParent, parentIndex ? parentIndex.depth + 1 : 0, ancestorIds);
140
- this._moveVisibleSubtree(source.node, visibleStart, visibleCount, nextParent, expandedNextParent);
141
- return true;
156
+ const ancestorIds = parentIndex && nextParent ? [...parentIndex.ancestorIds, nextParent.id] : [];
157
+ this._refreshSubtreeIndex(source, nextParent, parentIndex ? parentIndex.depth + 1 : 0, ancestorIds);
142
158
  }
143
159
  _refreshSiblingIndexes(nodes) {
144
160
  for (let index = 0; index < nodes.length; index++) {
@@ -167,49 +183,46 @@ export class GETreeModel {
167
183
  }
168
184
  }
169
185
  _moveVisibleSubtree(source, previousStart, previousCount, nextParent, expandedNextParent) {
170
- if (previousStart >= 0 && previousCount > 0) {
171
- this._visibleNodes.splice(previousStart, previousCount);
172
- this._visibleIds.splice(previousStart, previousCount);
173
- this._refreshVisibleIndexes(previousStart);
174
- }
186
+ this._removeVisibleRange(previousStart, previousCount);
175
187
  if (expandedNextParent && nextParent) {
176
- const parentVisible = this._visibleIndex.get(nextParent.id);
177
- if (parentVisible === undefined)
178
- return;
179
- const expandedChildren = [];
180
- for (const child of nextParent.children ?? []) {
181
- this._collectVisibleSubtree(child, expandedChildren);
182
- }
183
- if (expandedChildren.length === 0)
184
- return;
185
- const insertAt = parentVisible + 1;
186
- this._visibleNodes.splice(insertAt, 0, ...expandedChildren);
187
- this._visibleIds.splice(insertAt, 0, ...expandedChildren.map(item => item.node.id));
188
- this._refreshVisibleIndexes(insertAt);
188
+ this._insertExpandedChildren(nextParent);
189
189
  return;
190
190
  }
191
- let insertAt = -1;
191
+ const insertAt = this._resolveVisibleInsertAt(source, nextParent);
192
+ if (insertAt >= 0)
193
+ this._insertVisibleSubtrees(insertAt, [source]);
194
+ }
195
+ _removeVisibleRange(start, count) {
196
+ if (start < 0 || count <= 0)
197
+ return;
198
+ this._visibleNodes.splice(start, count);
199
+ this._visibleIds.splice(start, count);
200
+ this._refreshVisibleIndexes(start);
201
+ }
202
+ _insertExpandedChildren(parent) {
203
+ const parentVisible = this._visibleIndex.get(parent.id);
204
+ if (parentVisible === undefined)
205
+ return;
206
+ this._insertVisibleSubtrees(parentVisible + 1, parent.children ?? []);
207
+ }
208
+ _resolveVisibleInsertAt(source, nextParent) {
192
209
  const sourceIndex = this._nodeIndex.get(source.id)?.index ?? -1;
210
+ if (sourceIndex < 0)
211
+ return -1;
193
212
  const siblings = nextParent?.children ?? this._data;
194
- if (sourceIndex >= 0) {
195
- if (nextParent) {
196
- const parentVisible = this._visibleIndex.get(nextParent.id);
197
- if (parentVisible !== undefined && this._expandedIds.has(nextParent.id)) {
198
- insertAt = sourceIndex === 0
199
- ? parentVisible + 1
200
- : this._visibleSubtreeEnd(siblings[sourceIndex - 1]?.id);
201
- }
202
- }
203
- else {
204
- insertAt = sourceIndex === 0
205
- ? 0
206
- : this._visibleSubtreeEnd(siblings[sourceIndex - 1]?.id);
207
- }
208
- }
209
- if (insertAt < 0)
210
- return;
213
+ if (!nextParent)
214
+ return sourceIndex === 0 ? 0 : this._visibleSubtreeEnd(siblings[sourceIndex - 1]?.id);
215
+ const parentVisible = this._visibleIndex.get(nextParent.id);
216
+ if (parentVisible === undefined || !this._expandedIds.has(nextParent.id))
217
+ return -1;
218
+ return sourceIndex === 0
219
+ ? parentVisible + 1
220
+ : this._visibleSubtreeEnd(siblings[sourceIndex - 1]?.id);
221
+ }
222
+ _insertVisibleSubtrees(insertAt, roots) {
211
223
  const moved = [];
212
- this._collectVisibleSubtree(source, moved);
224
+ for (const root of roots)
225
+ this._collectVisibleSubtree(root, moved);
213
226
  if (moved.length === 0)
214
227
  return;
215
228
  this._visibleNodes.splice(insertAt, 0, ...moved);
@@ -1,11 +1,11 @@
1
- import type { GETreeNodeData } from './tree-types.js';
2
- export declare class GETreeNode extends HTMLElement {
1
+ import type { HYTreeNodeData } from './tree-types.js';
2
+ export declare class HYTreeNode extends HTMLElement {
3
3
  private readonly _icon;
4
4
  private readonly _label;
5
5
  static get observedAttributes(): string[];
6
6
  constructor();
7
7
  connectedCallback(): void;
8
8
  attributeChangedCallback(): void;
9
- set node(value: GETreeNodeData);
9
+ set node(value: HYTreeNodeData);
10
10
  private _sync;
11
11
  }
package/dist/tree-node.js CHANGED
@@ -1,4 +1,4 @@
1
- export class GETreeNode extends HTMLElement {
1
+ export class HYTreeNode extends HTMLElement {
2
2
  _icon = document.createElement('span');
3
3
  _label = document.createElement('span');
4
4
  static get observedAttributes() {
@@ -22,7 +22,7 @@ export class GETreeNode extends HTMLElement {
22
22
  flex: 0 0 16px;
23
23
  display: inline-grid;
24
24
  place-items: center;
25
- color: var(--ge-tree-icon-color, var(--ge-secondary-text-color, #8fa7c8));
25
+ color: var(--hy-tree-icon-color, var(--hy-secondary-text-color, #8fa7c8));
26
26
  font-size: 12px;
27
27
  }
28
28
  .label {
@@ -32,7 +32,7 @@ export class GETreeNode extends HTMLElement {
32
32
  white-space: nowrap;
33
33
  }
34
34
  :host([selected]) .label {
35
- color: var(--ge-selected-text-color, #ffffff);
35
+ color: var(--hy-selected-text-color, #ffffff);
36
36
  }
37
37
  `;
38
38
  this._icon.className = 'icon';
@@ -1,38 +1,38 @@
1
- export interface GETreeNodeData {
1
+ export interface HYTreeNodeData {
2
2
  id: string;
3
3
  label?: string;
4
4
  icon?: string;
5
5
  expanded?: boolean;
6
6
  renderer?: string;
7
- children?: GETreeNodeData[];
7
+ children?: HYTreeNodeData[];
8
8
  [key: string]: unknown;
9
9
  }
10
- export type GETreeDropPosition = 'before' | 'after' | 'inside';
11
- export interface GETreeSelectionChangeDetail {
10
+ export type HYTreeDropPosition = 'before' | 'after' | 'inside';
11
+ export interface HYTreeSelectionChangeDetail {
12
12
  selectedId: string | null;
13
- node: GETreeNodeData | null;
13
+ node: HYTreeNodeData | null;
14
14
  selectedIds: string[];
15
- nodes: GETreeNodeData[];
15
+ nodes: HYTreeNodeData[];
16
16
  }
17
- export interface GETreeDataChangeDetail {
18
- data: GETreeNodeData[];
17
+ export interface HYTreeDataChangeDetail {
18
+ data: HYTreeNodeData[];
19
19
  action?: 'copy' | 'paste' | 'delete' | 'drop';
20
20
  sourceId?: string;
21
21
  targetId?: string | null;
22
- dropPosition?: GETreeDropPosition;
23
- pastedNodes?: GETreeNodeData[];
22
+ dropPosition?: HYTreeDropPosition;
23
+ pastedNodes?: HYTreeNodeData[];
24
24
  deletedIds?: string[];
25
25
  }
26
- export interface GETreeNodeContextMenuDetail {
26
+ export interface HYTreeNodeContextMenuDetail {
27
27
  id: string;
28
- node: GETreeNodeData;
28
+ node: HYTreeNodeData;
29
29
  selectedIds: string[];
30
30
  clientX: number;
31
31
  clientY: number;
32
32
  }
33
33
  export interface FlatTreeNode {
34
- node: GETreeNodeData;
35
- parent: GETreeNodeData | null;
34
+ node: HYTreeNodeData;
35
+ parent: HYTreeNodeData | null;
36
36
  depth: number;
37
37
  }
38
38
  export interface IndexedTreeNode extends FlatTreeNode {