@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.
- package/CHANGELOG.md +29 -0
- package/LICENSE +1 -1
- package/README.md +226 -2
- package/dist/border-beam.d.ts +26 -0
- package/dist/border-beam.js +174 -0
- package/dist/button.d.ts +10 -0
- package/dist/button.js +85 -0
- package/dist/checkbox.d.ts +2 -2
- package/dist/checkbox.js +11 -11
- package/dist/context-menu.d.ts +6 -6
- package/dist/context-menu.js +11 -9
- package/dist/dialog.d.ts +5 -5
- package/dist/dialog.js +18 -18
- package/dist/drawer.d.ts +49 -0
- package/dist/drawer.js +377 -0
- package/dist/dropdown.d.ts +9 -9
- package/dist/dropdown.js +9 -9
- package/dist/expandable.d.ts +32 -0
- package/dist/expandable.js +136 -0
- package/dist/history-controls.d.ts +40 -0
- package/dist/history-controls.js +123 -0
- package/dist/index.d.ts +36 -31
- package/dist/index.js +35 -85
- package/dist/input.d.ts +5 -5
- package/dist/input.js +13 -13
- package/dist/menu-shared.d.ts +4 -4
- package/dist/menu-shared.js +4 -4
- package/dist/notification.d.ts +55 -0
- package/dist/notification.js +357 -0
- package/dist/radio.d.ts +2 -2
- package/dist/radio.js +12 -12
- package/dist/range.d.ts +74 -0
- package/dist/range.js +393 -0
- package/dist/select.d.ts +6 -6
- package/dist/select.js +20 -20
- package/dist/split.d.ts +6 -6
- package/dist/split.js +14 -14
- package/dist/tabs.d.ts +10 -6
- package/dist/tabs.js +94 -30
- package/dist/tooltip.d.ts +4 -4
- package/dist/tooltip.js +9 -9
- package/dist/tree-model.d.ts +15 -7
- package/dist/tree-model.js +93 -80
- package/dist/tree-node.d.ts +3 -3
- package/dist/tree-node.js +3 -3
- package/dist/tree-types.d.ts +14 -14
- package/dist/tree.d.ts +9 -7
- package/dist/tree.js +68 -66
- package/dist/virtual-list.d.ts +61 -0
- package/dist/virtual-list.js +324 -0
- package/package.json +100 -7
- package/themes/haiyue-dark.css +46 -0
- package/themes/haiyue-light.css +46 -0
- package/src/checkbox.ts +0 -215
- package/src/context-menu.ts +0 -248
- package/src/dialog.ts +0 -195
- package/src/dropdown.ts +0 -249
- package/src/index.ts +0 -192
- package/src/input.ts +0 -150
- package/src/menu-shared.ts +0 -65
- package/src/radio.ts +0 -216
- package/src/select.ts +0 -271
- package/src/split.ts +0 -306
- package/src/tabs.ts +0 -182
- package/src/tooltip.ts +0 -321
- package/src/tree-model.ts +0 -305
- package/src/tree-node.ts +0 -67
- package/src/tree-types.ts +0 -47
- package/src/tree.ts +0 -1002
package/dist/tabs.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
export class
|
|
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(--
|
|
22
|
-
background: var(--
|
|
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(--
|
|
32
|
-
background: var(--
|
|
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(--
|
|
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(--
|
|
48
|
-
background: var(--
|
|
54
|
+
color: var(--hy-text-color, #d8e2f2);
|
|
55
|
+
background: var(--hy-hover-bg-color, #222b3a);
|
|
49
56
|
}
|
|
50
57
|
button.active {
|
|
51
|
-
color: var(--
|
|
52
|
-
border-color: var(--
|
|
53
|
-
background: var(--
|
|
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
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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.
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
this.
|
|
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('
|
|
158
|
-
customElements.define('
|
|
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
|
|
2
|
-
export declare class
|
|
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():
|
|
19
|
-
set placement(value:
|
|
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
|
|
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(--
|
|
49
|
-
background: var(--
|
|
50
|
-
border: 1px solid var(--
|
|
51
|
-
box-shadow: 0 8px 24px var(--
|
|
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(--
|
|
67
|
-
border: solid var(--
|
|
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('
|
|
283
|
-
customElements.define('
|
|
282
|
+
if (!customElements.get('hy-tooltip')) {
|
|
283
|
+
customElements.define('hy-tooltip', HYTooltip);
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
286
|
//# sourceMappingURL=tooltip.js.map
|
package/dist/tree-model.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { FlatTreeNode,
|
|
2
|
-
/** Mutable hierarchy model owned by
|
|
3
|
-
export declare class
|
|
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:
|
|
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:
|
|
23
|
-
parent:
|
|
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:
|
|
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;
|
package/dist/tree-model.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** Mutable hierarchy model owned by
|
|
2
|
-
export class
|
|
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
|
|
109
|
+
return null;
|
|
94
110
|
if (target.ancestorIds.includes(sourceId))
|
|
95
|
-
return
|
|
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
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
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);
|
package/dist/tree-node.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare class
|
|
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:
|
|
9
|
+
set node(value: HYTreeNodeData);
|
|
10
10
|
private _sync;
|
|
11
11
|
}
|
package/dist/tree-node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export class
|
|
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(--
|
|
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(--
|
|
35
|
+
color: var(--hy-selected-text-color, #ffffff);
|
|
36
36
|
}
|
|
37
37
|
`;
|
|
38
38
|
this._icon.className = 'icon';
|
package/dist/tree-types.d.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
export interface
|
|
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?:
|
|
7
|
+
children?: HYTreeNodeData[];
|
|
8
8
|
[key: string]: unknown;
|
|
9
9
|
}
|
|
10
|
-
export type
|
|
11
|
-
export interface
|
|
10
|
+
export type HYTreeDropPosition = 'before' | 'after' | 'inside';
|
|
11
|
+
export interface HYTreeSelectionChangeDetail {
|
|
12
12
|
selectedId: string | null;
|
|
13
|
-
node:
|
|
13
|
+
node: HYTreeNodeData | null;
|
|
14
14
|
selectedIds: string[];
|
|
15
|
-
nodes:
|
|
15
|
+
nodes: HYTreeNodeData[];
|
|
16
16
|
}
|
|
17
|
-
export interface
|
|
18
|
-
data:
|
|
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?:
|
|
23
|
-
pastedNodes?:
|
|
22
|
+
dropPosition?: HYTreeDropPosition;
|
|
23
|
+
pastedNodes?: HYTreeNodeData[];
|
|
24
24
|
deletedIds?: string[];
|
|
25
25
|
}
|
|
26
|
-
export interface
|
|
26
|
+
export interface HYTreeNodeContextMenuDetail {
|
|
27
27
|
id: string;
|
|
28
|
-
node:
|
|
28
|
+
node: HYTreeNodeData;
|
|
29
29
|
selectedIds: string[];
|
|
30
30
|
clientX: number;
|
|
31
31
|
clientY: number;
|
|
32
32
|
}
|
|
33
33
|
export interface FlatTreeNode {
|
|
34
|
-
node:
|
|
35
|
-
parent:
|
|
34
|
+
node: HYTreeNodeData;
|
|
35
|
+
parent: HYTreeNodeData | null;
|
|
36
36
|
depth: number;
|
|
37
37
|
}
|
|
38
38
|
export interface IndexedTreeNode extends FlatTreeNode {
|