@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.
Files changed (142) hide show
  1. package/CHANGELOG.md +18 -18
  2. package/LICENSE +20 -20
  3. package/README.md +33 -33
  4. package/dist/assets/icons/arrow-clockwise.svg.js +1 -1
  5. package/dist/assets/icons/arrow-down.svg.js +1 -1
  6. package/dist/assets/icons/arrow-up.svg.js +1 -1
  7. package/dist/assets/icons/ban.svg.js +1 -1
  8. package/dist/assets/icons/bell-fill.svg.js +1 -1
  9. package/dist/assets/icons/check-circle-fill.svg.js +1 -1
  10. package/dist/assets/icons/check-lg.svg.js +1 -1
  11. package/dist/assets/icons/chevron-down.svg.js +1 -1
  12. package/dist/assets/icons/chevron-left.svg.js +1 -1
  13. package/dist/assets/icons/chevron-right.svg.js +1 -1
  14. package/dist/assets/icons/chevron-up.svg.js +1 -1
  15. package/dist/assets/icons/copy.svg.js +1 -1
  16. package/dist/assets/icons/dash-lg.svg.js +1 -1
  17. package/dist/assets/icons/exclamation-circle-fill.svg.js +1 -1
  18. package/dist/assets/icons/exclamation-triangle-fill.svg.js +1 -1
  19. package/dist/assets/icons/eye-slash.svg.js +1 -1
  20. package/dist/assets/icons/eye.svg.js +1 -1
  21. package/dist/assets/icons/info-circle-fill.svg.js +1 -1
  22. package/dist/assets/icons/layout-sidebar.svg.js +1 -1
  23. package/dist/assets/icons/lightbulb-fill.svg.js +1 -1
  24. package/dist/assets/icons/lightbulb-off.svg.js +1 -1
  25. package/dist/assets/icons/lightbulb.svg.js +1 -1
  26. package/dist/assets/icons/mic-fill.svg.js +1 -1
  27. package/dist/assets/icons/mic-mute-fill.svg.js +1 -1
  28. package/dist/assets/icons/paperclip.svg.js +1 -1
  29. package/dist/assets/icons/pencil-square.svg.js +1 -1
  30. package/dist/assets/icons/plus-lg.svg.js +1 -1
  31. package/dist/assets/icons/search.svg.js +1 -1
  32. package/dist/assets/icons/speedometer.svg.js +1 -1
  33. package/dist/assets/icons/x-lg.svg.js +1 -1
  34. package/dist/assets/styles/dark.css +129 -125
  35. package/dist/assets/styles/dark.css.js +1 -1
  36. package/dist/assets/styles/light.css +129 -126
  37. package/dist/assets/styles/light.css.js +1 -1
  38. package/dist/components/BaseElement.styles.js +2 -2
  39. package/dist/components/FloatingElement.d.ts +84 -0
  40. package/dist/components/FloatingElement.js +166 -0
  41. package/dist/components/FloatingElement.styles.js +26 -0
  42. package/dist/components/alert/Alert.d.ts +8 -4
  43. package/dist/components/alert/Alert.js +24 -15
  44. package/dist/components/alert/Alert.styles.js +32 -39
  45. package/dist/components/alert/index.js +5 -0
  46. package/dist/components/button/Button.js +4 -2
  47. package/dist/components/button/Button.styles.js +13 -20
  48. package/dist/components/button/index.js +5 -0
  49. package/dist/components/context-menu/ContextMenu.d.ts +20 -39
  50. package/dist/components/context-menu/ContextMenu.js +92 -137
  51. package/dist/components/context-menu/ContextMenu.styles.js +3 -28
  52. package/dist/components/context-menu/index.d.ts +1 -1
  53. package/dist/components/context-menu/index.js +5 -0
  54. package/dist/components/copy-button/CopyButton.d.ts +7 -3
  55. package/dist/components/copy-button/CopyButton.js +69 -0
  56. package/dist/components/copy-button/CopyButton.styles.js +17 -0
  57. package/dist/components/copy-button/index.js +5 -0
  58. package/dist/components/dialog/Dialog.d.ts +14 -7
  59. package/dist/components/dialog/Dialog.js +88 -27
  60. package/dist/components/dialog/Dialog.styles.js +60 -11
  61. package/dist/components/dialog/index.js +5 -0
  62. package/dist/components/divider/Divider.d.ts +10 -10
  63. package/dist/components/divider/Divider.js +32 -32
  64. package/dist/components/divider/Divider.styles.js +10 -10
  65. package/dist/components/divider/index.js +5 -0
  66. package/dist/components/dropdown-menu/DropdownMenu.d.ts +27 -0
  67. package/dist/components/dropdown-menu/DropdownMenu.js +135 -0
  68. package/dist/components/dropdown-menu/DropdownMenu.styles.d.ts +1 -0
  69. package/dist/components/dropdown-menu/DropdownMenu.styles.js +15 -0
  70. package/dist/components/dropdown-menu/index.d.ts +7 -0
  71. package/dist/components/dropdown-menu/index.js +5 -0
  72. package/dist/components/form/Form.d.ts +3 -1
  73. package/dist/components/form/Form.js +3 -1
  74. package/dist/components/form/Form.styles.js +1 -0
  75. package/dist/components/form/index.js +5 -0
  76. package/dist/components/icon/Icon.d.ts +6 -12
  77. package/dist/components/icon/Icon.js +15 -30
  78. package/dist/components/icon/Icon.styles.js +1 -1
  79. package/dist/components/icon/index.js +5 -0
  80. package/dist/components/icon-button/IconButton.d.ts +3 -3
  81. package/dist/components/icon-button/IconButton.js +6 -6
  82. package/dist/components/icon-button/IconButton.styles.js +14 -29
  83. package/dist/components/icon-button/index.js +5 -0
  84. package/dist/components/index.d.ts +3 -1
  85. package/dist/components/input/Input.d.ts +14 -13
  86. package/dist/components/input/Input.js +57 -56
  87. package/dist/components/input/Input.styles.js +54 -56
  88. package/dist/components/input/index.js +5 -0
  89. package/dist/components/menu/Menu.d.ts +4 -15
  90. package/dist/components/menu/Menu.js +43 -28
  91. package/dist/components/menu/Menu.styles.js +7 -19
  92. package/dist/components/menu/index.js +5 -0
  93. package/dist/components/menu-item/MenuItem.d.ts +21 -12
  94. package/dist/components/menu-item/MenuItem.js +184 -15
  95. package/dist/components/menu-item/MenuItem.styles.js +6 -0
  96. package/dist/components/menu-item/index.js +5 -0
  97. package/dist/components/progress-bar/ProgressBar.d.ts +1 -1
  98. package/dist/components/progress-bar/ProgressBar.js +2 -2
  99. package/dist/components/progress-bar/ProgressBar.styles.js +10 -10
  100. package/dist/components/progress-bar/index.js +5 -0
  101. package/dist/components/progress-ring/ProgressRing.d.ts +2 -2
  102. package/dist/components/progress-ring/ProgressRing.js +7 -6
  103. package/dist/components/progress-ring/ProgressRing.styles.js +8 -9
  104. package/dist/components/progress-ring/index.js +5 -0
  105. package/dist/components/spinner/Spinner.styles.js +5 -3
  106. package/dist/components/spinner/index.js +5 -0
  107. package/dist/components/split-panel/SplitPanel.d.ts +11 -3
  108. package/dist/components/split-panel/SplitPanel.js +44 -27
  109. package/dist/components/split-panel/SplitPanel.styles.js +1 -16
  110. package/dist/components/split-panel/index.js +5 -0
  111. package/dist/components/tooltip/Tooltip.d.ts +9 -29
  112. package/dist/components/tooltip/Tooltip.js +27 -149
  113. package/dist/components/tooltip/Tooltip.styles.js +9 -23
  114. package/dist/components/tooltip/index.js +5 -0
  115. package/dist/components/tree/index.js +5 -0
  116. package/dist/components/tree-item/index.js +5 -0
  117. package/dist/index.js +28 -5
  118. package/dist/integrations/react/UDropdownMenu.d.ts +8 -0
  119. package/dist/integrations/react/UDropdownMenu.js +10 -0
  120. package/dist/integrations/react/index.d.ts +1 -1
  121. package/dist/integrations/react/index.js +1 -1
  122. package/dist/internals/index.d.ts +0 -1
  123. package/dist/internals/node-helpers.d.ts +6 -1
  124. package/dist/internals/node-helpers.js +5 -6
  125. package/dist/utilities/decorators.js +2 -0
  126. package/dist/utilities/icons.d.ts +46 -0
  127. package/dist/utilities/icons.js +114 -0
  128. package/dist/utilities/index.d.ts +1 -0
  129. package/dist/utilities/notifier.d.ts +17 -7
  130. package/dist/utilities/notifier.js +51 -41
  131. package/dist/utilities/theme.d.ts +15 -20
  132. package/dist/utilities/theme.js +24 -20
  133. package/package.json +59 -58
  134. package/dist/components/panel/Panel.d.ts +0 -9
  135. package/dist/components/panel/Panel.js +0 -17
  136. package/dist/components/panel/Panel.styles.js +0 -12
  137. package/dist/components/panel/index.d.ts +0 -7
  138. package/dist/integrations/react/UPanel.d.ts +0 -8
  139. package/dist/integrations/react/UPanel.js +0 -10
  140. package/dist/internals/icon-helpers.d.ts +0 -32
  141. package/dist/internals/icon-helpers.js +0 -48
  142. /package/dist/components/{panel/Panel.styles.d.ts → FloatingElement.styles.d.ts} +0 -0
@@ -1,5 +1,6 @@
1
- import { html, nothing } from 'lit';
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.handleClick = (e) => {
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
- updated(changedProperties) {
50
- super.updated(changedProperties);
51
- if (changedProperties.has("disabled")) {
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 (changedProperties.has("checked")) {
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
- <span class="checker" ?checked=${this.checked}>✓</span>
76
- `;
77
- } else {
78
- return nothing;
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 };
@@ -59,6 +59,12 @@ const styles = css`
59
59
  text-overflow: ellipsis;
60
60
  white-space: nowrap;
61
61
  }
62
+
63
+ .submenu-indicator {
64
+ margin-left: 8px;
65
+ opacity: 0.7;
66
+ user-select: none;
67
+ }
62
68
  `;
63
69
 
64
70
  export { styles };
@@ -0,0 +1,5 @@
1
+ import { MenuItem } from './MenuItem.js';
2
+
3
+ MenuItem.define("u-menu-item");
4
+
5
+ export { MenuItem };
@@ -23,5 +23,5 @@ export declare class ProgressBar extends BaseElement {
23
23
  protected willUpdate(changedProperties: PropertyValues): void;
24
24
  render(): import('lit-html').TemplateResult<1>;
25
25
  /** 내부 진행 상태 업데이트 */
26
- private updateProgress;
26
+ private updateProgressState;
27
27
  }
@@ -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.updateProgress();
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 updateProgress() {
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: 12px;
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: relative;
51
- z-index: 1;
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: 8px;
56
- line-height: 12px;
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;
@@ -0,0 +1,5 @@
1
+ import { ProgressBar } from './ProgressBar.js';
2
+
3
+ ProgressBar.define("u-progress-bar");
4
+
5
+ export { ProgressBar };
@@ -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 updateProgress;
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.updateProgress();
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
- updateProgress() {
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, "progress");
97
+ ], ProgressRing.prototype, "dasharray");
97
98
  __decorateClass([
98
99
  state()
99
- ], ProgressRing.prototype, "dasharray");
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
- /* smooth variant - 매끄러운 원형 */
14
- :host([variant="smooth"]) .mask {
15
- stroke-linecap: round;
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: 1.1em;
46
+ font-size: 1em;
48
47
  color: var(--indicator-color);
49
48
  pointer-events: none;
50
49
  }
@@ -0,0 +1,5 @@
1
+ import { ProgressRing } from './ProgressRing.js';
2
+
3
+ ProgressRing.define("u-progress-ring");
4
+
5
+ export { ProgressRing };
@@ -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;
@@ -0,0 +1,5 @@
1
+ import { Spinner } from './Spinner.js';
2
+
3
+ Spinner.define("u-spinner");
4
+
5
+ export { Spinner };
@@ -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
- initialRatio: number[];
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 getDividerSize;
25
+ private initialize;
19
26
  private handleSlotChange;
20
27
  /** 디바이더 드래그 이벤트 핸들러 */
21
- private handleDividerDrag;
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.initialRatio = [];
27
- this.handleSlotChange = async (e) => {
28
- const slot = e.target;
29
- const childrens = slot.assignedElements({ flatten: true });
30
- const panels = childrens.filter((el) => el instanceof Panel);
31
- if (this.panels.length === panels.length && this.panels.every((p, i) => p === panels[i])) {
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.initialRatio.length === panels.length) {
38
- const totalRatio = this.initialRatio.reduce((sum, r) => sum + r, 0);
39
- this.panelSizes = this.initialRatio.map((r) => r / totalRatio * 100);
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.draggable = true;
55
- divider.addEventListener("u-drag", this.handleDividerDrag);
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.handleDividerDrag = (event) => {
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: "initial-ratio", converter: arrayAttributeConverter(parseFloat) })
120
- ], SplitPanel.prototype, "initialRatio");
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 };