@iyulab/components 0.1.4 → 0.1.6
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 +18 -18
- package/LICENSE +20 -20
- package/README.md +33 -33
- package/dist/assets/icons/arrow-clockwise.svg.js +1 -1
- package/dist/assets/icons/arrow-down.svg.js +1 -1
- package/dist/assets/icons/arrow-up.svg.js +1 -1
- package/dist/assets/icons/ban.svg.js +1 -1
- package/dist/assets/icons/bell-fill.svg.js +1 -1
- package/dist/assets/icons/check-circle-fill.svg.js +1 -1
- package/dist/assets/icons/check-lg.svg.js +1 -1
- package/dist/assets/icons/chevron-down.svg.js +1 -1
- package/dist/assets/icons/chevron-left.svg.js +1 -1
- package/dist/assets/icons/chevron-right.svg.js +1 -1
- package/dist/assets/icons/chevron-up.svg.js +1 -1
- package/dist/assets/icons/copy.svg.js +1 -1
- package/dist/assets/icons/dash-lg.svg.js +1 -1
- package/dist/assets/icons/exclamation-circle-fill.svg.js +1 -1
- package/dist/assets/icons/exclamation-triangle-fill.svg.js +1 -1
- package/dist/assets/icons/eye-slash.svg.js +1 -1
- package/dist/assets/icons/eye.svg.js +1 -1
- package/dist/assets/icons/info-circle-fill.svg.js +1 -1
- package/dist/assets/icons/layout-sidebar.svg.js +1 -1
- package/dist/assets/icons/lightbulb-fill.svg.js +1 -1
- package/dist/assets/icons/lightbulb-off.svg.js +1 -1
- package/dist/assets/icons/lightbulb.svg.js +1 -1
- package/dist/assets/icons/mic-fill.svg.js +1 -1
- package/dist/assets/icons/mic-mute-fill.svg.js +1 -1
- package/dist/assets/icons/paperclip.svg.js +1 -1
- package/dist/assets/icons/pencil-square.svg.js +1 -1
- package/dist/assets/icons/plus-lg.svg.js +1 -1
- package/dist/assets/icons/search.svg.js +1 -1
- package/dist/assets/icons/speedometer.svg.js +1 -1
- package/dist/assets/icons/x-lg.svg.js +1 -1
- package/dist/assets/styles/dark.css +129 -125
- package/dist/assets/styles/dark.css.js +1 -1
- package/dist/assets/styles/light.css +129 -126
- package/dist/assets/styles/light.css.js +1 -1
- package/dist/components/BaseElement.styles.js +2 -2
- package/dist/components/FloatingElement.d.ts +84 -0
- package/dist/components/FloatingElement.js +166 -0
- package/dist/components/FloatingElement.styles.js +26 -0
- package/dist/components/alert/Alert.d.ts +8 -4
- package/dist/components/alert/Alert.js +24 -15
- package/dist/components/alert/Alert.styles.js +32 -39
- package/dist/components/alert/index.js +5 -0
- package/dist/components/button/Button.js +4 -2
- package/dist/components/button/Button.styles.js +13 -20
- package/dist/components/button/index.js +5 -0
- package/dist/components/context-menu/ContextMenu.d.ts +20 -39
- package/dist/components/context-menu/ContextMenu.js +92 -137
- package/dist/components/context-menu/ContextMenu.styles.js +3 -28
- package/dist/components/context-menu/index.d.ts +1 -1
- package/dist/components/context-menu/index.js +5 -0
- package/dist/components/copy-button/CopyButton.d.ts +7 -3
- package/dist/components/copy-button/CopyButton.js +69 -0
- package/dist/components/copy-button/CopyButton.styles.js +17 -0
- package/dist/components/copy-button/index.js +5 -0
- package/dist/components/dialog/Dialog.d.ts +14 -7
- package/dist/components/dialog/Dialog.js +88 -27
- package/dist/components/dialog/Dialog.styles.js +60 -11
- package/dist/components/dialog/index.js +5 -0
- package/dist/components/divider/Divider.d.ts +10 -10
- package/dist/components/divider/Divider.js +32 -32
- package/dist/components/divider/Divider.styles.js +10 -10
- package/dist/components/divider/index.js +5 -0
- package/dist/components/dropdown-menu/DropdownMenu.d.ts +27 -0
- package/dist/components/dropdown-menu/DropdownMenu.js +135 -0
- package/dist/components/dropdown-menu/DropdownMenu.styles.d.ts +1 -0
- package/dist/components/dropdown-menu/DropdownMenu.styles.js +15 -0
- package/dist/components/dropdown-menu/index.d.ts +7 -0
- package/dist/components/dropdown-menu/index.js +5 -0
- package/dist/components/form/Form.d.ts +3 -1
- package/dist/components/form/Form.js +3 -1
- package/dist/components/form/Form.styles.js +1 -0
- package/dist/components/form/index.js +5 -0
- package/dist/components/icon/Icon.d.ts +6 -12
- package/dist/components/icon/Icon.js +15 -30
- package/dist/components/icon/Icon.styles.js +1 -1
- package/dist/components/icon/index.js +5 -0
- package/dist/components/icon-button/IconButton.d.ts +3 -3
- package/dist/components/icon-button/IconButton.js +6 -6
- package/dist/components/icon-button/IconButton.styles.js +14 -29
- package/dist/components/icon-button/index.js +5 -0
- package/dist/components/index.d.ts +3 -1
- package/dist/components/input/Input.d.ts +14 -13
- package/dist/components/input/Input.js +57 -56
- package/dist/components/input/Input.styles.js +54 -56
- package/dist/components/input/index.js +5 -0
- package/dist/components/menu/Menu.d.ts +4 -15
- package/dist/components/menu/Menu.js +43 -28
- package/dist/components/menu/Menu.styles.js +7 -19
- package/dist/components/menu/index.js +5 -0
- package/dist/components/menu-item/MenuItem.d.ts +21 -12
- package/dist/components/menu-item/MenuItem.js +184 -15
- package/dist/components/menu-item/MenuItem.styles.js +6 -0
- package/dist/components/menu-item/index.js +5 -0
- package/dist/components/progress-bar/ProgressBar.d.ts +1 -1
- package/dist/components/progress-bar/ProgressBar.js +2 -2
- package/dist/components/progress-bar/ProgressBar.styles.js +10 -10
- package/dist/components/progress-bar/index.js +5 -0
- package/dist/components/progress-ring/ProgressRing.d.ts +2 -2
- package/dist/components/progress-ring/ProgressRing.js +7 -6
- package/dist/components/progress-ring/ProgressRing.styles.js +8 -9
- package/dist/components/progress-ring/index.js +5 -0
- package/dist/components/spinner/Spinner.styles.js +5 -3
- package/dist/components/spinner/index.js +5 -0
- package/dist/components/split-panel/SplitPanel.d.ts +11 -3
- package/dist/components/split-panel/SplitPanel.js +44 -27
- package/dist/components/split-panel/SplitPanel.styles.js +1 -16
- package/dist/components/split-panel/index.js +5 -0
- package/dist/components/tooltip/Tooltip.d.ts +9 -29
- package/dist/components/tooltip/Tooltip.js +27 -149
- package/dist/components/tooltip/Tooltip.styles.js +9 -23
- package/dist/components/tooltip/index.js +5 -0
- package/dist/components/tree/index.js +5 -0
- package/dist/components/tree-item/index.js +5 -0
- package/dist/index.js +28 -5
- package/dist/integrations/react/UDropdownMenu.d.ts +8 -0
- package/dist/integrations/react/UDropdownMenu.js +10 -0
- package/dist/integrations/react/index.d.ts +1 -1
- package/dist/integrations/react/index.js +1 -1
- package/dist/internals/index.d.ts +0 -1
- package/dist/internals/node-helpers.d.ts +6 -1
- package/dist/internals/node-helpers.js +5 -6
- package/dist/utilities/decorators.js +2 -0
- package/dist/utilities/icons.d.ts +46 -0
- package/dist/utilities/icons.js +114 -0
- package/dist/utilities/index.d.ts +1 -0
- package/dist/utilities/notifier.d.ts +17 -7
- package/dist/utilities/notifier.js +51 -41
- package/dist/utilities/theme.d.ts +15 -20
- package/dist/utilities/theme.js +24 -20
- package/package.json +59 -58
- package/dist/components/panel/Panel.d.ts +0 -9
- package/dist/components/panel/Panel.js +0 -17
- package/dist/components/panel/Panel.styles.js +0 -12
- package/dist/components/panel/index.d.ts +0 -7
- package/dist/integrations/react/UPanel.d.ts +0 -8
- package/dist/integrations/react/UPanel.js +0 -10
- package/dist/internals/icon-helpers.d.ts +0 -32
- package/dist/internals/icon-helpers.js +0 -48
- /package/dist/components/{panel/Panel.styles.d.ts → FloatingElement.styles.d.ts} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { property } from 'lit/decorators.js';
|
|
1
|
+
import { nothing, html } from 'lit';
|
|
2
|
+
import { property, state, query, queryAssignedElements } from 'lit/decorators.js';
|
|
3
|
+
import { autoUpdate, computePosition, offset, flip, shift } from '@floating-ui/dom';
|
|
3
4
|
import { BaseElement } from '../BaseElement.js';
|
|
4
5
|
import { styles } from './MenuItem.styles.js';
|
|
5
6
|
|
|
@@ -20,13 +21,125 @@ class MenuItem extends BaseElement {
|
|
|
20
21
|
this.checked = false;
|
|
21
22
|
this.checkable = false;
|
|
22
23
|
this.value = "";
|
|
23
|
-
|
|
24
|
-
this.
|
|
24
|
+
this.submenuOpen = false;
|
|
25
|
+
this.submenuCleanup = null;
|
|
26
|
+
this.closeTimer = null;
|
|
27
|
+
/** 외부에서 호출 가능하게(부모 menu가 형제 submenu 닫을 때 사용) */
|
|
28
|
+
this.closeSubmenu = async () => {
|
|
29
|
+
const sm = this.submenu;
|
|
30
|
+
if (!sm) return;
|
|
31
|
+
this.stopSubmenuAutoUpdate();
|
|
32
|
+
if (this.closeTimer) window.clearTimeout(this.closeTimer);
|
|
33
|
+
this.submenuOpen = false;
|
|
34
|
+
this.setAttribute("aria-expanded", "false");
|
|
35
|
+
await sm.hide();
|
|
36
|
+
};
|
|
37
|
+
this.openSubmenu = async (focusFirst = false) => {
|
|
38
|
+
const sm = this.submenu;
|
|
39
|
+
if (!sm || this.disabled) return;
|
|
40
|
+
if (this.closeTimer) {
|
|
41
|
+
window.clearTimeout(this.closeTimer);
|
|
42
|
+
this.closeTimer = null;
|
|
43
|
+
}
|
|
44
|
+
this.submenuOpen = true;
|
|
45
|
+
this.setAttribute("aria-expanded", "true");
|
|
46
|
+
this.emit("u-submenu-open", { item: this });
|
|
47
|
+
await sm.show();
|
|
48
|
+
await this.positionSubmenu();
|
|
49
|
+
this.stopSubmenuAutoUpdate();
|
|
50
|
+
this.submenuCleanup = autoUpdate(this.containerEl, sm, () => {
|
|
51
|
+
this.positionSubmenu();
|
|
52
|
+
});
|
|
53
|
+
sm.addEventListener("u-select", this.onSubmenuSelect, { once: true });
|
|
54
|
+
if (focusFirst) {
|
|
55
|
+
await sm.focusFirstItem?.();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
this.onSubmenuSelect = () => {
|
|
59
|
+
this.closeSubmenu();
|
|
60
|
+
};
|
|
61
|
+
this.scheduleCloseSubmenu = () => {
|
|
62
|
+
if (!this.submenu) return;
|
|
63
|
+
if (this.closeTimer) window.clearTimeout(this.closeTimer);
|
|
64
|
+
this.closeTimer = window.setTimeout(() => this.closeSubmenu(), 150);
|
|
65
|
+
};
|
|
66
|
+
this.positionSubmenu = async () => {
|
|
67
|
+
const sm = this.submenu;
|
|
68
|
+
if (!sm) return;
|
|
69
|
+
const pos = await computePosition(this.containerEl, sm, {
|
|
70
|
+
strategy: "fixed",
|
|
71
|
+
placement: "right-start",
|
|
72
|
+
middleware: [
|
|
73
|
+
offset(6),
|
|
74
|
+
flip(),
|
|
75
|
+
shift({ padding: 8 })
|
|
76
|
+
]
|
|
77
|
+
});
|
|
78
|
+
Object.assign(sm.style, {
|
|
79
|
+
position: "fixed",
|
|
80
|
+
left: `${pos.x}px`,
|
|
81
|
+
top: `${pos.y}px`,
|
|
82
|
+
zIndex: "1001",
|
|
83
|
+
transformOrigin: (() => {
|
|
84
|
+
switch (pos.placement) {
|
|
85
|
+
case "left":
|
|
86
|
+
case "left-start":
|
|
87
|
+
case "left-end":
|
|
88
|
+
return "right center";
|
|
89
|
+
case "right":
|
|
90
|
+
case "right-start":
|
|
91
|
+
case "right-end":
|
|
92
|
+
return "left center";
|
|
93
|
+
case "top":
|
|
94
|
+
case "top-start":
|
|
95
|
+
case "top-end":
|
|
96
|
+
return "center bottom";
|
|
97
|
+
default:
|
|
98
|
+
return "center top";
|
|
99
|
+
}
|
|
100
|
+
})()
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
this.onPointerEnter = () => {
|
|
104
|
+
if (this.submenu) this.openSubmenu(false);
|
|
105
|
+
};
|
|
106
|
+
this.onPointerLeave = (e) => {
|
|
107
|
+
const sm = this.submenu;
|
|
108
|
+
if (!sm) return;
|
|
109
|
+
const next = e.relatedTarget;
|
|
110
|
+
if (next && (this.contains(next) || sm.contains(next))) return;
|
|
111
|
+
this.scheduleCloseSubmenu();
|
|
112
|
+
};
|
|
113
|
+
this.onFocusIn = () => {
|
|
114
|
+
if (this.submenu) this.openSubmenu(false);
|
|
115
|
+
};
|
|
116
|
+
this.onFocusOut = (e) => {
|
|
117
|
+
const sm = this.submenu;
|
|
118
|
+
if (!sm) return;
|
|
119
|
+
const next = e.relatedTarget;
|
|
120
|
+
if (next && (this.contains(next) || sm.contains(next))) return;
|
|
121
|
+
this.scheduleCloseSubmenu();
|
|
122
|
+
};
|
|
123
|
+
this.handleKeydown = async (e) => {
|
|
124
|
+
if (!this.submenu) return;
|
|
125
|
+
if (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
await this.openSubmenu(true);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
this.handleClick = async (e) => {
|
|
25
131
|
if (this.disabled) {
|
|
26
132
|
e.preventDefault();
|
|
27
133
|
e.stopPropagation();
|
|
28
134
|
return;
|
|
29
135
|
}
|
|
136
|
+
if (this.submenu) {
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
e.stopPropagation();
|
|
139
|
+
if (this.submenuOpen) await this.closeSubmenu();
|
|
140
|
+
else await this.openSubmenu(true);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
30
143
|
if (this.checkable) {
|
|
31
144
|
this.checked = !this.checked;
|
|
32
145
|
this.emit("u-check", { checked: this.checked });
|
|
@@ -41,41 +154,88 @@ class MenuItem extends BaseElement {
|
|
|
41
154
|
static {
|
|
42
155
|
this.dependencies = {};
|
|
43
156
|
}
|
|
157
|
+
get submenu() {
|
|
158
|
+
return this.submenuList?.[0];
|
|
159
|
+
}
|
|
44
160
|
connectedCallback() {
|
|
45
161
|
super.connectedCallback();
|
|
46
|
-
this.setAttribute("role", "menuitem");
|
|
47
162
|
this.setAttribute("tabindex", this.disabled ? "-1" : "0");
|
|
163
|
+
this.updateRole();
|
|
164
|
+
this.addEventListener("keydown", this.handleKeydown);
|
|
165
|
+
this.addEventListener("pointerenter", this.onPointerEnter);
|
|
166
|
+
this.addEventListener("pointerleave", this.onPointerLeave);
|
|
167
|
+
this.addEventListener("focusin", this.onFocusIn);
|
|
168
|
+
this.addEventListener("focusout", this.onFocusOut);
|
|
48
169
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
170
|
+
disconnectedCallback() {
|
|
171
|
+
this.removeEventListener("keydown", this.handleKeydown);
|
|
172
|
+
this.removeEventListener("pointerenter", this.onPointerEnter);
|
|
173
|
+
this.removeEventListener("pointerleave", this.onPointerLeave);
|
|
174
|
+
this.removeEventListener("focusin", this.onFocusIn);
|
|
175
|
+
this.removeEventListener("focusout", this.onFocusOut);
|
|
176
|
+
this.stopSubmenuAutoUpdate();
|
|
177
|
+
if (this.closeTimer) window.clearTimeout(this.closeTimer);
|
|
178
|
+
super.disconnectedCallback();
|
|
179
|
+
}
|
|
180
|
+
updated(changed) {
|
|
181
|
+
super.updated(changed);
|
|
182
|
+
if (changed.has("disabled")) {
|
|
52
183
|
this.setAttribute("tabindex", this.disabled ? "-1" : "0");
|
|
53
184
|
this.setAttribute("aria-disabled", this.disabled ? "true" : "false");
|
|
185
|
+
if (this.disabled) this.closeSubmenu();
|
|
54
186
|
}
|
|
55
|
-
if (
|
|
187
|
+
if (changed.has("checked")) {
|
|
56
188
|
this.setAttribute("aria-checked", this.checked ? "true" : "false");
|
|
57
189
|
}
|
|
190
|
+
if (changed.has("checkable")) {
|
|
191
|
+
this.updateRole();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
updateRole() {
|
|
195
|
+
const hasSubmenu = !!this.submenu;
|
|
196
|
+
if (this.checkable) {
|
|
197
|
+
this.setAttribute("role", "menuitemcheckbox");
|
|
198
|
+
this.setAttribute("aria-checked", this.checked ? "true" : "false");
|
|
199
|
+
} else {
|
|
200
|
+
this.setAttribute("role", "menuitem");
|
|
201
|
+
}
|
|
202
|
+
if (hasSubmenu) {
|
|
203
|
+
this.setAttribute("aria-haspopup", "menu");
|
|
204
|
+
this.setAttribute("aria-expanded", this.submenuOpen ? "true" : "false");
|
|
205
|
+
} else {
|
|
206
|
+
this.removeAttribute("aria-haspopup");
|
|
207
|
+
this.removeAttribute("aria-expanded");
|
|
208
|
+
}
|
|
58
209
|
}
|
|
59
210
|
render() {
|
|
211
|
+
const hasSubmenu = !!this.submenu;
|
|
60
212
|
return html`
|
|
61
213
|
<div class="container" @click=${this.handleClick}>
|
|
62
214
|
${this.renderPrefixItem()}
|
|
63
215
|
<slot name="prefix"></slot>
|
|
216
|
+
|
|
64
217
|
<span class="content">
|
|
65
218
|
<slot></slot>
|
|
66
219
|
</span>
|
|
220
|
+
|
|
67
221
|
<slot name="suffix"></slot>
|
|
222
|
+
${hasSubmenu ? html`<span class="submenu-indicator">›</span>` : nothing}
|
|
68
223
|
</div>
|
|
224
|
+
|
|
225
|
+
<!-- Shoelace 방식 그대로 -->
|
|
226
|
+
<slot name="submenu"></slot>
|
|
69
227
|
`;
|
|
70
228
|
}
|
|
71
|
-
/** 메뉴 항목의 앞부분을 렌더링합니다. */
|
|
72
229
|
renderPrefixItem() {
|
|
73
230
|
if (this.checkable) {
|
|
74
|
-
return html
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
231
|
+
return html`<span class="checker" ?checked=${this.checked}>✓</span>`;
|
|
232
|
+
}
|
|
233
|
+
return nothing;
|
|
234
|
+
}
|
|
235
|
+
stopSubmenuAutoUpdate() {
|
|
236
|
+
if (this.submenuCleanup) {
|
|
237
|
+
this.submenuCleanup();
|
|
238
|
+
this.submenuCleanup = null;
|
|
79
239
|
}
|
|
80
240
|
}
|
|
81
241
|
}
|
|
@@ -94,5 +254,14 @@ __decorateClass([
|
|
|
94
254
|
__decorateClass([
|
|
95
255
|
property({ type: String })
|
|
96
256
|
], MenuItem.prototype, "value");
|
|
257
|
+
__decorateClass([
|
|
258
|
+
state()
|
|
259
|
+
], MenuItem.prototype, "submenuOpen");
|
|
260
|
+
__decorateClass([
|
|
261
|
+
query(".container")
|
|
262
|
+
], MenuItem.prototype, "containerEl");
|
|
263
|
+
__decorateClass([
|
|
264
|
+
queryAssignedElements({ slot: "submenu", selector: "u-menu" })
|
|
265
|
+
], MenuItem.prototype, "submenuList");
|
|
97
266
|
|
|
98
267
|
export { MenuItem };
|
|
@@ -34,7 +34,7 @@ class ProgressBar extends BaseElement {
|
|
|
34
34
|
this.progressState = this.indeterminate ? "indeterminate" : "turned-on";
|
|
35
35
|
}
|
|
36
36
|
if (changedProperties.has("minValue") || changedProperties.has("maxValue") || changedProperties.has("value")) {
|
|
37
|
-
this.
|
|
37
|
+
this.updateProgressState();
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
render() {
|
|
@@ -48,7 +48,7 @@ class ProgressBar extends BaseElement {
|
|
|
48
48
|
`;
|
|
49
49
|
}
|
|
50
50
|
/** 내부 진행 상태 업데이트 */
|
|
51
|
-
async
|
|
51
|
+
async updateProgressState() {
|
|
52
52
|
await this.updateComplete;
|
|
53
53
|
if (this.indeterminate) return;
|
|
54
54
|
const range = this.maxValue - this.minValue;
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
2
|
|
|
3
3
|
const styles = css`
|
|
4
|
+
:host {
|
|
5
|
+
--progress-value: 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
:host {
|
|
5
9
|
position: relative;
|
|
6
10
|
display: block;
|
|
7
11
|
width: 100%;
|
|
8
|
-
height:
|
|
12
|
+
height: 1em;
|
|
9
13
|
border-radius: 9999px;
|
|
10
14
|
background-color: var(--u-neutral-200);
|
|
11
15
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
12
16
|
overflow: hidden;
|
|
13
|
-
|
|
14
|
-
--progress-value: 0;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
.indicator {
|
|
18
20
|
position: absolute;
|
|
19
21
|
inset: 0 0 0 0;
|
|
20
22
|
display: block;
|
|
21
|
-
width: 100%;
|
|
22
|
-
height: 100%;
|
|
23
23
|
border-radius: inherit;
|
|
24
24
|
background-color: var(--u-blue-600);
|
|
25
25
|
transform-origin: left center;
|
|
@@ -47,14 +47,14 @@ const styles = css`
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
.content {
|
|
50
|
-
position:
|
|
51
|
-
z-index:
|
|
50
|
+
position: absolute;
|
|
51
|
+
z-index: 100;
|
|
52
52
|
display: flex;
|
|
53
|
+
inset: 0 0 0 0;
|
|
53
54
|
align-items: center;
|
|
54
55
|
justify-content: center;
|
|
55
|
-
font-size:
|
|
56
|
-
line-height:
|
|
57
|
-
color: var(--u-neutral-800);
|
|
56
|
+
font-size: 0.5em;
|
|
57
|
+
line-height: 1em;
|
|
58
58
|
white-space: nowrap;
|
|
59
59
|
user-select: none;
|
|
60
60
|
pointer-events: none;
|
|
@@ -9,8 +9,8 @@ export type ProgressRingVariant = 'smooth' | 'blocks' | 'ticks';
|
|
|
9
9
|
export declare class ProgressRing extends BaseElement {
|
|
10
10
|
static styles: import('lit').CSSResultGroup[];
|
|
11
11
|
static dependencies: Record<string, typeof BaseElement>;
|
|
12
|
-
progress: number;
|
|
13
12
|
dasharray: string;
|
|
13
|
+
progress: number;
|
|
14
14
|
/** 진행 표시기의 스타일 변형을 설정합니다. */
|
|
15
15
|
variant: ProgressRingVariant;
|
|
16
16
|
/** 진행 표시기의 최소값을 설정합니다. */
|
|
@@ -24,7 +24,7 @@ export declare class ProgressRing extends BaseElement {
|
|
|
24
24
|
/**
|
|
25
25
|
* 진행 상태를 0과 1 사이의 값으로 반환합니다.
|
|
26
26
|
*/
|
|
27
|
-
private
|
|
27
|
+
private updateProgressState;
|
|
28
28
|
/**
|
|
29
29
|
* variant에 따라 dasharray 값을 반환합니다.
|
|
30
30
|
* [dash-length] [gap-length] or [dash-length] 형식
|
|
@@ -16,8 +16,8 @@ const PATH_LENGTH = 100;
|
|
|
16
16
|
class ProgressRing extends BaseElement {
|
|
17
17
|
constructor() {
|
|
18
18
|
super(...arguments);
|
|
19
|
-
this.progress = 0;
|
|
20
19
|
this.dasharray = PATH_LENGTH.toString();
|
|
20
|
+
this.progress = 0;
|
|
21
21
|
this.variant = "smooth";
|
|
22
22
|
this.minValue = 0;
|
|
23
23
|
this.maxValue = 100;
|
|
@@ -35,7 +35,7 @@ class ProgressRing extends BaseElement {
|
|
|
35
35
|
this.updateDashArray();
|
|
36
36
|
}
|
|
37
37
|
if (changedProperties.has("minValue") || changedProperties.has("maxValue") || changedProperties.has("value")) {
|
|
38
|
-
this.
|
|
38
|
+
this.updateProgressState();
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
render() {
|
|
@@ -48,6 +48,7 @@ class ProgressRing extends BaseElement {
|
|
|
48
48
|
stroke="white"
|
|
49
49
|
stroke-dasharray="${PATH_LENGTH}"
|
|
50
50
|
stroke-dashoffset="${PATH_LENGTH * (1 - this.progress)}"
|
|
51
|
+
stroke-linecap=${this.variant === "smooth" ? "round" : "butt"}
|
|
51
52
|
></circle>
|
|
52
53
|
</mask>
|
|
53
54
|
</defs>
|
|
@@ -65,14 +66,14 @@ class ProgressRing extends BaseElement {
|
|
|
65
66
|
></circle>
|
|
66
67
|
</svg>
|
|
67
68
|
<div class="label" part="label">
|
|
68
|
-
${Math.round(this.progress * 100)}%
|
|
69
|
+
${Math.round(this.progress * 100)} %
|
|
69
70
|
</div>
|
|
70
71
|
`;
|
|
71
72
|
}
|
|
72
73
|
/**
|
|
73
74
|
* 진행 상태를 0과 1 사이의 값으로 반환합니다.
|
|
74
75
|
*/
|
|
75
|
-
|
|
76
|
+
updateProgressState() {
|
|
76
77
|
const range = this.maxValue - this.minValue;
|
|
77
78
|
const clampedValue = Math.max(this.minValue, Math.min(this.maxValue, this.value));
|
|
78
79
|
this.progress = range === 0 ? 0 : (clampedValue - this.minValue) / range;
|
|
@@ -93,10 +94,10 @@ class ProgressRing extends BaseElement {
|
|
|
93
94
|
}
|
|
94
95
|
__decorateClass([
|
|
95
96
|
state()
|
|
96
|
-
], ProgressRing.prototype, "
|
|
97
|
+
], ProgressRing.prototype, "dasharray");
|
|
97
98
|
__decorateClass([
|
|
98
99
|
state()
|
|
99
|
-
], ProgressRing.prototype, "
|
|
100
|
+
], ProgressRing.prototype, "progress");
|
|
100
101
|
__decorateClass([
|
|
101
102
|
property({ type: String, reflect: true })
|
|
102
103
|
], ProgressRing.prototype, "variant");
|
|
@@ -2,20 +2,19 @@ import { css } from 'lit';
|
|
|
2
2
|
|
|
3
3
|
const styles = css`
|
|
4
4
|
:host {
|
|
5
|
-
position: relative;
|
|
6
|
-
display: inline-flex;
|
|
7
|
-
font-size: inherit;
|
|
8
|
-
|
|
9
5
|
--track-width: 0.25em;
|
|
10
6
|
--track-color: var(--u-neutral-200, #e5e7eb);
|
|
11
7
|
--indicator-color: var(--u-blue-600, #3b82f6);
|
|
12
8
|
}
|
|
13
|
-
|
|
14
|
-
:host
|
|
15
|
-
|
|
9
|
+
|
|
10
|
+
:host {
|
|
11
|
+
position: relative;
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
font-size: inherit;
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
.progress-ring {
|
|
17
|
+
position: relative;
|
|
19
18
|
width: 6em;
|
|
20
19
|
height: 6em;
|
|
21
20
|
transform: rotate(-90deg); /* 0%가 위에서 시작하도록 회전 */
|
|
@@ -39,12 +38,12 @@ const styles = css`
|
|
|
39
38
|
/* 라벨 중앙 숫자 */
|
|
40
39
|
.label {
|
|
41
40
|
position: absolute;
|
|
42
|
-
inset: 0;
|
|
41
|
+
inset: 0 0 0 0;
|
|
43
42
|
display: flex;
|
|
44
43
|
align-items: center;
|
|
45
44
|
justify-content: center;
|
|
46
45
|
font-weight: 600;
|
|
47
|
-
font-size:
|
|
46
|
+
font-size: 1em;
|
|
48
47
|
color: var(--indicator-color);
|
|
49
48
|
pointer-events: none;
|
|
50
49
|
}
|
|
@@ -2,14 +2,16 @@ import { css } from 'lit';
|
|
|
2
2
|
|
|
3
3
|
const styles = css`
|
|
4
4
|
:host {
|
|
5
|
-
display: inline-flex;
|
|
6
|
-
font-size: inherit;
|
|
7
|
-
|
|
8
5
|
--track-width: 0.125em;
|
|
9
6
|
--track-color: var(--u-neutral-200, #e5e7eb);
|
|
10
7
|
--indicator-color: var(--u-neutral-800, #1f2937);
|
|
11
8
|
}
|
|
12
9
|
|
|
10
|
+
:host {
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
font-size: inherit;
|
|
13
|
+
}
|
|
14
|
+
|
|
13
15
|
.spinner {
|
|
14
16
|
flex: 1 1 auto;
|
|
15
17
|
width: 1em;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PropertyValues } from 'lit';
|
|
1
2
|
import { BaseElement } from '../BaseElement.js';
|
|
2
3
|
/**
|
|
3
4
|
* SplitPanel 컴포넌트는 화면을 여러 개의 패널로 분할하여 표시합니다.
|
|
@@ -9,14 +10,21 @@ export declare class SplitPanel extends BaseElement {
|
|
|
9
10
|
private dividers;
|
|
10
11
|
private panelSizes;
|
|
11
12
|
private panelAdjustSizes;
|
|
13
|
+
childElements: HTMLElement[];
|
|
12
14
|
/** 분할 방향을 설정합니다. 'horizontal'은 좌우 분할, 'vertical'은 상하 분할입니다. */
|
|
13
15
|
orientation: 'horizontal' | 'vertical';
|
|
14
16
|
/** 초기 패널 크기 비율을 설정합니다. (예: [30, 70]은 첫 번째 패널이 30%, 두 번째 패널이 70%를 차지) */
|
|
15
|
-
|
|
17
|
+
initRatio: number[];
|
|
18
|
+
/** 각 패널의 최소 크기를 설정합니다. (픽셀 단위) */
|
|
19
|
+
minSizes: number[];
|
|
20
|
+
/** 각 패널의 최대 크기를 설정합니다. (픽셀 단위) */
|
|
21
|
+
maxSizes: number[];
|
|
16
22
|
disconnectedCallback(): void;
|
|
23
|
+
protected willUpdate(changedProperties: PropertyValues): void;
|
|
17
24
|
render(): import('lit-html').TemplateResult<1>;
|
|
18
|
-
private
|
|
25
|
+
private initialize;
|
|
19
26
|
private handleSlotChange;
|
|
20
27
|
/** 디바이더 드래그 이벤트 핸들러 */
|
|
21
|
-
private
|
|
28
|
+
private handleDividerMove;
|
|
29
|
+
private getDividerSize;
|
|
22
30
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
|
-
import { property } from 'lit/decorators.js';
|
|
2
|
+
import { queryAssignedElements, property } from 'lit/decorators.js';
|
|
3
3
|
import { arrayAttributeConverter } from '../../internals/attribute-converters.js';
|
|
4
4
|
import { BaseElement } from '../BaseElement.js';
|
|
5
5
|
import { Divider } from '../divider/Divider.js';
|
|
6
|
-
import { Panel } from '../panel/Panel.js';
|
|
7
6
|
import { styles } from './SplitPanel.styles.js';
|
|
8
7
|
|
|
9
8
|
var __defProp = Object.defineProperty;
|
|
@@ -23,44 +22,48 @@ class SplitPanel extends BaseElement {
|
|
|
23
22
|
this.panelSizes = [];
|
|
24
23
|
this.panelAdjustSizes = [];
|
|
25
24
|
this.orientation = "horizontal";
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
this.panels = panels;
|
|
25
|
+
this.initRatio = [];
|
|
26
|
+
this.minSizes = [];
|
|
27
|
+
this.maxSizes = [];
|
|
28
|
+
// 초기화 메서드
|
|
29
|
+
this.initialize = async () => {
|
|
30
|
+
this.panels = this.childElements.filter((el) => el instanceof Divider === false);
|
|
35
31
|
this.dividers.forEach((divider) => divider.remove());
|
|
36
32
|
this.dividers = [];
|
|
37
|
-
if (this.
|
|
38
|
-
const totalRatio = this.
|
|
39
|
-
this.panelSizes = this.
|
|
33
|
+
if (this.initRatio.length === this.panels.length) {
|
|
34
|
+
const totalRatio = this.initRatio.reduce((sum, r) => sum + r, 0);
|
|
35
|
+
this.panelSizes = this.initRatio.map((r) => r / totalRatio * 100);
|
|
40
36
|
} else {
|
|
41
|
-
const equalRatio = 100 / panels.length;
|
|
42
|
-
this.panelSizes = panels.map(() => equalRatio);
|
|
37
|
+
const equalRatio = 100 / this.panels.length;
|
|
38
|
+
this.panelSizes = this.panels.map(() => equalRatio);
|
|
43
39
|
}
|
|
44
|
-
this.panelAdjustSizes = panels.map(() => 0);
|
|
40
|
+
this.panelAdjustSizes = this.panels.map(() => 0);
|
|
45
41
|
const property2 = this.orientation === "horizontal" ? "width" : "height";
|
|
46
42
|
const dividerSize = this.getDividerSize();
|
|
47
|
-
panels.forEach((panel, index) => {
|
|
43
|
+
this.panels.forEach((panel, index) => {
|
|
48
44
|
const ratio = this.panelSizes[index];
|
|
49
45
|
panel.style.flex = "none";
|
|
50
|
-
panel.style[property2] = index === 0 || index === panels.length - 1 ? `calc(${ratio}% - ${dividerSize / 2}px)` : `calc(${ratio}% - ${dividerSize}px)`;
|
|
51
|
-
if (index === panels.length - 1) return;
|
|
46
|
+
panel.style[property2] = index === 0 || index === this.panels.length - 1 ? `calc(${ratio}% - ${dividerSize / 2}px)` : `calc(${ratio}% - ${dividerSize}px)`;
|
|
47
|
+
if (index === this.panels.length - 1) return;
|
|
52
48
|
const divider = new Divider();
|
|
53
49
|
divider.orientation = this.orientation;
|
|
54
|
-
divider.
|
|
55
|
-
divider.addEventListener("u-
|
|
50
|
+
divider.movable = true;
|
|
51
|
+
divider.addEventListener("u-move", this.handleDividerMove);
|
|
56
52
|
this.dividers.push(divider);
|
|
57
53
|
panel.after(divider);
|
|
58
54
|
});
|
|
59
55
|
this.requestUpdate();
|
|
60
56
|
await this.updateComplete;
|
|
61
57
|
};
|
|
58
|
+
this.handleSlotChange = async () => {
|
|
59
|
+
const panels = this.childElements.filter((el) => el instanceof Divider === false);
|
|
60
|
+
if (panels.every((p, i) => p === this.panels[i])) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
await this.initialize();
|
|
64
|
+
};
|
|
62
65
|
/** 디바이더 드래그 이벤트 핸들러 */
|
|
63
|
-
this.
|
|
66
|
+
this.handleDividerMove = (event) => {
|
|
64
67
|
const divider = event.target;
|
|
65
68
|
const delta = event.detail.delta;
|
|
66
69
|
const index = this.dividers.indexOf(divider);
|
|
@@ -93,14 +96,19 @@ class SplitPanel extends BaseElement {
|
|
|
93
96
|
}
|
|
94
97
|
static {
|
|
95
98
|
this.dependencies = {
|
|
96
|
-
"u-divider": Divider
|
|
97
|
-
"u-panel": Panel
|
|
99
|
+
"u-divider": Divider
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
102
|
disconnectedCallback() {
|
|
101
103
|
this.dividers.forEach((divider) => divider.remove());
|
|
102
104
|
super.disconnectedCallback();
|
|
103
105
|
}
|
|
106
|
+
willUpdate(changedProperties) {
|
|
107
|
+
super.willUpdate(changedProperties);
|
|
108
|
+
if (changedProperties.has("orientation")) {
|
|
109
|
+
this.initialize();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
104
112
|
render() {
|
|
105
113
|
return html`
|
|
106
114
|
<slot @slotchange=${this.handleSlotChange}></slot>
|
|
@@ -112,11 +120,20 @@ class SplitPanel extends BaseElement {
|
|
|
112
120
|
return parseFloat(sizeStr) || 2;
|
|
113
121
|
}
|
|
114
122
|
}
|
|
123
|
+
__decorateClass([
|
|
124
|
+
queryAssignedElements({ flatten: true })
|
|
125
|
+
], SplitPanel.prototype, "childElements");
|
|
115
126
|
__decorateClass([
|
|
116
127
|
property({ type: String, reflect: true })
|
|
117
128
|
], SplitPanel.prototype, "orientation");
|
|
118
129
|
__decorateClass([
|
|
119
|
-
property({ type: Array, attribute: "
|
|
120
|
-
], SplitPanel.prototype, "
|
|
130
|
+
property({ type: Array, attribute: "init-ratio", converter: arrayAttributeConverter(parseFloat) })
|
|
131
|
+
], SplitPanel.prototype, "initRatio");
|
|
132
|
+
__decorateClass([
|
|
133
|
+
property({ type: Array, attribute: "min-sizes", converter: arrayAttributeConverter(parseFloat) })
|
|
134
|
+
], SplitPanel.prototype, "minSizes");
|
|
135
|
+
__decorateClass([
|
|
136
|
+
property({ type: Array, attribute: "max-sizes", converter: arrayAttributeConverter(parseFloat) })
|
|
137
|
+
], SplitPanel.prototype, "maxSizes");
|
|
121
138
|
|
|
122
139
|
export { SplitPanel };
|