@iyulab/components 0.1.1 → 0.1.3

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 (33) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/components/index.d.ts +3 -0
  3. package/dist/components/progress-bar/ProgressBar.d.ts +27 -0
  4. package/dist/components/progress-bar/ProgressBar.js +99 -0
  5. package/dist/components/progress-bar/ProgressBar.styles.d.ts +1 -0
  6. package/dist/components/progress-bar/ProgressBar.styles.js +73 -0
  7. package/dist/components/progress-bar/index.d.ts +7 -0
  8. package/dist/components/progress-ring/ProgressRing.d.ts +3 -1
  9. package/dist/components/progress-ring/ProgressRing.js +15 -14
  10. package/dist/components/tooltip/Tooltip.d.ts +1 -1
  11. package/dist/components/tooltip/Tooltip.js +2 -2
  12. package/dist/components/tree/Tree.d.ts +49 -0
  13. package/dist/components/tree/Tree.js +171 -0
  14. package/dist/components/tree/Tree.styles.d.ts +1 -0
  15. package/dist/components/tree/Tree.styles.js +26 -0
  16. package/dist/components/tree/index.d.ts +3 -0
  17. package/dist/components/tree-item/TreeItem.d.ts +52 -0
  18. package/dist/components/tree-item/TreeItem.js +163 -0
  19. package/dist/components/tree-item/TreeItem.styles.d.ts +1 -0
  20. package/dist/components/tree-item/TreeItem.styles.js +97 -0
  21. package/dist/components/tree-item/index.d.ts +3 -0
  22. package/dist/index.js +4 -1
  23. package/dist/integrations/react/UProgressBar.d.ts +8 -0
  24. package/dist/integrations/react/UProgressBar.js +10 -0
  25. package/dist/integrations/react/UTree.d.ts +8 -0
  26. package/dist/integrations/react/UTree.js +10 -0
  27. package/dist/integrations/react/UTreeItem.d.ts +8 -0
  28. package/dist/integrations/react/UTreeItem.js +10 -0
  29. package/dist/integrations/react/index.d.ts +3 -0
  30. package/dist/integrations/react/index.js +3 -0
  31. package/dist/utilities/theme.d.ts +4 -5
  32. package/dist/utilities/theme.js +10 -10
  33. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3 (2025-11-17)
4
+ - fixed a theme `useBuiltIn` Option, now it works as expected.
5
+ - added `ProgressBar` component to show loading progress at the top of the page.
6
+
7
+ ## 0.1.2 (2025-11-13)
8
+ - fixed a type issue in `theme` utility.
9
+
3
10
  ## 0.1.1 (2025-11-13)
4
11
  - added `BrowserStorage` which supports `localStorage` and `cookie` with async methods for getting, setting, and removing items.
5
12
  - changed theme `persist` option to `store` option, and fixed some issues.
@@ -8,7 +8,10 @@ export * from './icon-button';
8
8
  export * from './input';
9
9
  export * from './menu';
10
10
  export * from './panel';
11
+ export * from './progress-bar';
11
12
  export * from './progress-ring';
12
13
  export * from './spinner';
13
14
  export * from './split-panel';
14
15
  export * from './tooltip';
16
+ export * from './tree';
17
+ export * from './tree-item';
@@ -0,0 +1,27 @@
1
+ import { PropertyValues } from 'lit';
2
+ import { BaseElement } from '@iyulab/components/dist/components/BaseElement.js';
3
+ /**
4
+ * ProgressBar 컴포넌트는 진행 상태를 시각적으로 표시합니다.
5
+ * 로딩 상태나 작업 진행률을 표시하는데 사용됩니다.
6
+ */
7
+ export declare class ProgressBar extends BaseElement {
8
+ static styles: import('lit').CSSResultGroup[];
9
+ static dependencies: Record<string, typeof BaseElement>;
10
+ indicatorEl: HTMLElement;
11
+ /** 내부 진행 상태 */
12
+ progressState: 'turned-on' | 'turned-off' | 'determinate' | 'indeterminate';
13
+ /** 내부 진행 상태 (0과 1 사이 값) */
14
+ progress: number;
15
+ /** 불확정 상태 (로딩 애니메이션 표시) */
16
+ indeterminate: boolean;
17
+ /** 최소값 (기본값: 0) */
18
+ minValue: number;
19
+ /** 최대값 (기본값: 100) */
20
+ maxValue: number;
21
+ /** 현재값 */
22
+ value: number;
23
+ protected willUpdate(changedProperties: PropertyValues): void;
24
+ render(): import('lit-html').TemplateResult<1>;
25
+ /** 내부 진행 상태 업데이트 */
26
+ private updateProgress;
27
+ }
@@ -0,0 +1,99 @@
1
+ import { html } from 'lit';
2
+ import { query, state, property } from 'lit/decorators.js';
3
+ import { BaseElement } from '../BaseElement.js';
4
+ import { styles } from './ProgressBar.styles.js';
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __decorateClass = (decorators, target, key, kind) => {
8
+ var result = void 0 ;
9
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
10
+ if (decorator = decorators[i])
11
+ result = (decorator(target, key, result) ) || result;
12
+ if (result) __defProp(target, key, result);
13
+ return result;
14
+ };
15
+ class ProgressBar extends BaseElement {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.progressState = "turned-on";
19
+ this.progress = 0;
20
+ this.indeterminate = false;
21
+ this.minValue = 0;
22
+ this.maxValue = 100;
23
+ this.value = 0;
24
+ }
25
+ static {
26
+ this.styles = [super.styles, styles];
27
+ }
28
+ static {
29
+ this.dependencies = {};
30
+ }
31
+ willUpdate(changedProperties) {
32
+ super.willUpdate(changedProperties);
33
+ if (changedProperties.has("indeterminate")) {
34
+ this.progressState = this.indeterminate ? "indeterminate" : "turned-on";
35
+ }
36
+ if (changedProperties.has("minValue") || changedProperties.has("maxValue") || changedProperties.has("value")) {
37
+ this.updateProgress();
38
+ }
39
+ }
40
+ render() {
41
+ return html`
42
+ <div class="indicator" part="indicator"
43
+ state=${this.progressState}
44
+ ></div>
45
+ <div class="content" part="content">
46
+ <slot></slot>
47
+ </div>
48
+ `;
49
+ }
50
+ /** 내부 진행 상태 업데이트 */
51
+ async updateProgress() {
52
+ await this.updateComplete;
53
+ if (this.indeterminate) return;
54
+ const range = this.maxValue - this.minValue;
55
+ const clampedValue = Math.max(this.minValue, Math.min(this.maxValue, this.value));
56
+ const progress = range === 0 ? 0 : (clampedValue - this.minValue) / range;
57
+ if (isNaN(progress)) return;
58
+ if (this.progress <= 0 || progress < this.progress) {
59
+ this.progressState = "turned-on";
60
+ await this.updateComplete;
61
+ this.progressState = "determinate";
62
+ }
63
+ if (this.progressState === "determinate") {
64
+ this.indicatorEl.style.setProperty("--progress-value", progress.toString());
65
+ }
66
+ if (progress >= 1) {
67
+ setTimeout(() => {
68
+ this.progressState = "turned-off";
69
+ setTimeout(() => {
70
+ this.progressState = "turned-on";
71
+ }, 500);
72
+ }, 300);
73
+ }
74
+ this.progress = progress;
75
+ }
76
+ }
77
+ __decorateClass([
78
+ query(".indicator")
79
+ ], ProgressBar.prototype, "indicatorEl");
80
+ __decorateClass([
81
+ state()
82
+ ], ProgressBar.prototype, "progressState");
83
+ __decorateClass([
84
+ state()
85
+ ], ProgressBar.prototype, "progress");
86
+ __decorateClass([
87
+ property({ type: Boolean, reflect: true })
88
+ ], ProgressBar.prototype, "indeterminate");
89
+ __decorateClass([
90
+ property({ type: Number })
91
+ ], ProgressBar.prototype, "minValue");
92
+ __decorateClass([
93
+ property({ type: Number })
94
+ ], ProgressBar.prototype, "maxValue");
95
+ __decorateClass([
96
+ property({ type: Number })
97
+ ], ProgressBar.prototype, "value");
98
+
99
+ export { ProgressBar };
@@ -0,0 +1 @@
1
+ export declare const styles: import('lit').CSSResult;
@@ -0,0 +1,73 @@
1
+ import { css } from 'lit';
2
+
3
+ const styles = css`
4
+ :host {
5
+ position: relative;
6
+ display: block;
7
+ width: 100%;
8
+ height: 12px;
9
+ border-radius: 9999px;
10
+ background-color: var(--u-neutral-200);
11
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
12
+ overflow: hidden;
13
+
14
+ --progress-value: 0;
15
+ }
16
+
17
+ .indicator {
18
+ position: absolute;
19
+ inset: 0 0 0 0;
20
+ display: block;
21
+ width: 100%;
22
+ height: 100%;
23
+ border-radius: inherit;
24
+ background-color: var(--u-blue-600);
25
+ transform-origin: left center;
26
+ will-change: transform, opacity;
27
+ }
28
+ .indicator[state="turned-on"] {
29
+ opacity: 0;
30
+ transform: scaleX(0);
31
+ transition: none;
32
+ }
33
+ .indicator[state="turned-off"] {
34
+ opacity: 0;
35
+ transform: scaleX(1);
36
+ transition: transform 0.3s ease, opacity 0.5s ease;
37
+ }
38
+ .indicator[state="determinate"] {
39
+ opacity: 1;
40
+ transform: scaleX(var(--progress-value));
41
+ transition: transform 0.3s ease;
42
+ }
43
+ .indicator[state="indeterminate"] {
44
+ opacity: 1;
45
+ transform: scaleX(1);
46
+ animation: indeterminate 1.5s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
47
+ }
48
+
49
+ .content {
50
+ position: relative;
51
+ z-index: 1;
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ font-size: 8px;
56
+ line-height: 12px;
57
+ color: var(--u-neutral-800);
58
+ white-space: nowrap;
59
+ user-select: none;
60
+ pointer-events: none;
61
+ }
62
+
63
+ @keyframes indeterminate {
64
+ 0% {
65
+ transform: translateX(-100%);
66
+ }
67
+ 100% {
68
+ transform: translateX(100%);
69
+ }
70
+ }
71
+ `;
72
+
73
+ export { styles };
@@ -0,0 +1,7 @@
1
+ import { ProgressBar } from './ProgressBar';
2
+ declare global {
3
+ interface HTMLElementTagNameMap {
4
+ 'u-progress-bar': ProgressBar;
5
+ }
6
+ }
7
+ export { ProgressBar };
@@ -21,7 +21,9 @@ export declare class ProgressRing extends BaseElement {
21
21
  value: number;
22
22
  protected willUpdate(changedProperties: PropertyValues): void;
23
23
  render(): import('lit-html').TemplateResult<1>;
24
- /** 진행 상태를 0과 1 사이의 값으로 반환합니다. */
24
+ /**
25
+ * 진행 상태를 0과 1 사이의 값으로 반환합니다.
26
+ */
25
27
  private updateProgress;
26
28
  /**
27
29
  * variant에 따라 dasharray 값을 반환합니다.
@@ -32,10 +32,10 @@ class ProgressRing extends BaseElement {
32
32
  willUpdate(changedProperties) {
33
33
  super.willUpdate(changedProperties);
34
34
  if (changedProperties.has("variant")) {
35
- this.dasharray = this.updateDashArray(this.variant);
35
+ this.updateDashArray();
36
36
  }
37
37
  if (changedProperties.has("minValue") || changedProperties.has("maxValue") || changedProperties.has("value")) {
38
- this.progress = this.updateProgress(this.minValue, this.maxValue, this.value);
38
+ this.updateProgress();
39
39
  }
40
40
  }
41
41
  render() {
@@ -67,24 +67,25 @@ class ProgressRing extends BaseElement {
67
67
  <div class="label" part="label">${Math.round(this.progress * 100)}%</div>
68
68
  `;
69
69
  }
70
- /** 진행 상태를 0과 1 사이의 값으로 반환합니다. */
71
- updateProgress(min, max, val) {
72
- min = Number(min ?? 0);
73
- max = Number(max ?? 100);
74
- val = Math.min(Math.max(Number(val ?? 0), min), max);
75
- return max === min ? 0 : (val - min) / (max - min);
70
+ /**
71
+ * 진행 상태를 0과 1 사이의 값으로 반환합니다.
72
+ */
73
+ updateProgress() {
74
+ const range = this.maxValue - this.minValue;
75
+ const clampedValue = Math.max(this.minValue, Math.min(this.maxValue, this.value));
76
+ this.progress = range === 0 ? 0 : (clampedValue - this.minValue) / range;
76
77
  }
77
78
  /**
78
79
  * variant에 따라 dasharray 값을 반환합니다.
79
80
  * [dash-length] [gap-length] or [dash-length] 형식
80
81
  */
81
- updateDashArray(variant) {
82
- if (variant === "ticks") {
83
- return "1 1";
84
- } else if (variant === "blocks") {
85
- return "8 2";
82
+ updateDashArray() {
83
+ if (this.variant === "ticks") {
84
+ this.dasharray = "1 1";
85
+ } else if (this.variant === "blocks") {
86
+ this.dasharray = "8 2";
86
87
  } else {
87
- return PATH_LENGTH.toString();
88
+ this.dasharray = PATH_LENGTH.toString();
88
89
  }
89
90
  }
90
91
  }
@@ -23,7 +23,7 @@ export declare class Tooltip extends BaseElement {
23
23
  distance: number;
24
24
  connectedCallback(): void;
25
25
  disconnectedCallback(): void;
26
- protected updated(changedProperties: PropertyValues): void;
26
+ protected willUpdate(changedProperties: PropertyValues): void;
27
27
  render(): import('lit-html').TemplateResult<1>;
28
28
  /** 툴팁을 표시합니다. */
29
29
  show: (e?: Event) => Promise<void>;
@@ -87,8 +87,8 @@ class Tooltip extends BaseElement {
87
87
  this.detachTriggers(this.triggers);
88
88
  super.disconnectedCallback();
89
89
  }
90
- updated(changedProperties) {
91
- super.updated(changedProperties);
90
+ willUpdate(changedProperties) {
91
+ super.willUpdate(changedProperties);
92
92
  if (changedProperties.has("triggers") && this.triggers) {
93
93
  const oldTriggers = changedProperties.get("triggers");
94
94
  this.detachTriggers(oldTriggers);
@@ -0,0 +1,49 @@
1
+ import { BaseElement } from '../BaseElement.js';
2
+ import { TreeItem } from '../tree-item/TreeItem.js';
3
+ /**
4
+ * Tree 컴포넌트는 계층적 데이터 구조를 표시하는 트리 뷰를 제공합니다.
5
+ * u-tree-item 컴포넌트를 자식으로 사용하여 중첩된 구조를 구성할 수 있습니다.
6
+ */
7
+ export declare class Tree extends BaseElement {
8
+ static styles: import('lit').CSSResultGroup[];
9
+ static dependencies: Record<string, typeof BaseElement>;
10
+ /** 트리가 비활성화 상태인지 여부입니다. */
11
+ disabled: boolean;
12
+ /** 다중 선택을 허용할지 여부입니다. */
13
+ multiple: boolean;
14
+ /** 선택 가능한 트리인지 여부입니다. */
15
+ selectable: boolean;
16
+ private treeItems;
17
+ private selectedItems;
18
+ connectedCallback(): void;
19
+ disconnectedCallback(): void;
20
+ render(): import('lit-html').TemplateResult<1>;
21
+ /** 슬롯 변경 이벤트를 처리합니다. */
22
+ private handleSlotChange;
23
+ /** 트리 항목의 레벨을 업데이트합니다. */
24
+ private updateTreeItemsLevel;
25
+ /** 트리 항목 선택 이벤트를 처리합니다. */
26
+ private handleItemSelect;
27
+ /** 트리 항목 확장/축소 이벤트를 처리합니다. */
28
+ private handleItemToggle;
29
+ /**
30
+ * 모든 트리 항목을 확장합니다.
31
+ */
32
+ expandAll(): void;
33
+ /**
34
+ * 모든 트리 항목을 축소합니다.
35
+ */
36
+ collapseAll(): void;
37
+ /**
38
+ * 선택된 모든 항목을 반환합니다.
39
+ */
40
+ getSelectedItems(): TreeItem[];
41
+ /**
42
+ * 모든 선택을 해제합니다.
43
+ */
44
+ clearSelection(): void;
45
+ /**
46
+ * 재귀적으로 모든 TreeItem을 가져옵니다.
47
+ */
48
+ private getAllTreeItems;
49
+ }
@@ -0,0 +1,171 @@
1
+ import { html } from 'lit';
2
+ import { property, queryAssignedElements } from 'lit/decorators.js';
3
+ import { BaseElement } from '../BaseElement.js';
4
+ import { TreeItem } from '../tree-item/TreeItem.js';
5
+ import { styles } from './Tree.styles.js';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __decorateClass = (decorators, target, key, kind) => {
9
+ var result = void 0 ;
10
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
11
+ if (decorator = decorators[i])
12
+ result = (decorator(target, key, result) ) || result;
13
+ if (result) __defProp(target, key, result);
14
+ return result;
15
+ };
16
+ class Tree extends BaseElement {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.disabled = false;
20
+ this.multiple = false;
21
+ this.selectable = true;
22
+ this.selectedItems = /* @__PURE__ */ new Set();
23
+ /** 슬롯 변경 이벤트를 처리합니다. */
24
+ this.handleSlotChange = () => {
25
+ this.updateTreeItemsLevel();
26
+ };
27
+ /** 트리 항목 선택 이벤트를 처리합니다. */
28
+ this.handleItemSelect = (e) => {
29
+ if (!this.selectable || this.disabled) {
30
+ e.stopPropagation();
31
+ return;
32
+ }
33
+ const item = e.detail.item;
34
+ if (!this.multiple) {
35
+ this.selectedItems.forEach((selectedItem) => {
36
+ if (selectedItem !== item) {
37
+ selectedItem.selected = false;
38
+ }
39
+ });
40
+ this.selectedItems.clear();
41
+ }
42
+ if (item.selected) {
43
+ this.selectedItems.add(item);
44
+ } else {
45
+ this.selectedItems.delete(item);
46
+ }
47
+ this.emit("u-tree-select", {
48
+ value: item.value,
49
+ item,
50
+ selectedItems: Array.from(this.selectedItems)
51
+ });
52
+ };
53
+ /** 트리 항목 확장/축소 이벤트를 처리합니다. */
54
+ this.handleItemToggle = (e) => {
55
+ if (this.disabled) {
56
+ e.stopPropagation();
57
+ return;
58
+ }
59
+ const item = e.detail.item;
60
+ this.emit("u-tree-toggle", {
61
+ expanded: item.expanded,
62
+ item
63
+ });
64
+ };
65
+ }
66
+ static {
67
+ this.styles = [super.styles, styles];
68
+ }
69
+ static {
70
+ this.dependencies = {
71
+ "u-tree-item": TreeItem
72
+ };
73
+ }
74
+ connectedCallback() {
75
+ super.connectedCallback();
76
+ this.setAttribute("role", "tree");
77
+ this.addEventListener("u-select", this.handleItemSelect);
78
+ this.addEventListener("u-toggle", this.handleItemToggle);
79
+ }
80
+ disconnectedCallback() {
81
+ super.disconnectedCallback();
82
+ this.removeEventListener("u-select", this.handleItemSelect);
83
+ this.removeEventListener("u-toggle", this.handleItemToggle);
84
+ }
85
+ render() {
86
+ return html`
87
+ <div class="tree-container">
88
+ <slot @slotchange=${this.handleSlotChange}></slot>
89
+ </div>
90
+ `;
91
+ }
92
+ /** 트리 항목의 레벨을 업데이트합니다. */
93
+ updateTreeItemsLevel(items = this.treeItems, level = 0) {
94
+ items.forEach((item) => {
95
+ if (item instanceof TreeItem) {
96
+ item.level = level;
97
+ const childrenSlot = item.shadowRoot?.querySelector('slot[name="children"]');
98
+ if (childrenSlot) {
99
+ const children = childrenSlot.assignedElements();
100
+ if (children.length > 0) {
101
+ this.updateTreeItemsLevel(children, level + 1);
102
+ }
103
+ }
104
+ }
105
+ });
106
+ }
107
+ /**
108
+ * 모든 트리 항목을 확장합니다.
109
+ */
110
+ expandAll() {
111
+ this.getAllTreeItems().forEach((item) => {
112
+ item.expand();
113
+ });
114
+ }
115
+ /**
116
+ * 모든 트리 항목을 축소합니다.
117
+ */
118
+ collapseAll() {
119
+ this.getAllTreeItems().forEach((item) => {
120
+ item.collapse();
121
+ });
122
+ }
123
+ /**
124
+ * 선택된 모든 항목을 반환합니다.
125
+ */
126
+ getSelectedItems() {
127
+ return Array.from(this.selectedItems);
128
+ }
129
+ /**
130
+ * 모든 선택을 해제합니다.
131
+ */
132
+ clearSelection() {
133
+ this.selectedItems.forEach((item) => {
134
+ item.selected = false;
135
+ });
136
+ this.selectedItems.clear();
137
+ }
138
+ /**
139
+ * 재귀적으로 모든 TreeItem을 가져옵니다.
140
+ */
141
+ getAllTreeItems(items = this.treeItems) {
142
+ let allItems = [];
143
+ items.forEach((item) => {
144
+ if (item instanceof TreeItem) {
145
+ allItems.push(item);
146
+ const childrenSlot = item.shadowRoot?.querySelector('slot[name="children"]');
147
+ if (childrenSlot) {
148
+ const children = childrenSlot.assignedElements();
149
+ if (children.length > 0) {
150
+ allItems = allItems.concat(this.getAllTreeItems(children));
151
+ }
152
+ }
153
+ }
154
+ });
155
+ return allItems;
156
+ }
157
+ }
158
+ __decorateClass([
159
+ property({ type: Boolean, reflect: true })
160
+ ], Tree.prototype, "disabled");
161
+ __decorateClass([
162
+ property({ type: Boolean })
163
+ ], Tree.prototype, "multiple");
164
+ __decorateClass([
165
+ property({ type: Boolean })
166
+ ], Tree.prototype, "selectable");
167
+ __decorateClass([
168
+ queryAssignedElements({ selector: "u-tree-item" })
169
+ ], Tree.prototype, "treeItems");
170
+
171
+ export { Tree };
@@ -0,0 +1 @@
1
+ export declare const styles: import('lit').CSSResult;
@@ -0,0 +1,26 @@
1
+ import { css } from 'lit';
2
+
3
+ const styles = css`
4
+ :host {
5
+ display: block;
6
+ width: 100%;
7
+ min-width: 0;
8
+ }
9
+
10
+ :host([disabled]) {
11
+ opacity: 0.5;
12
+ pointer-events: none;
13
+ cursor: not-allowed;
14
+ }
15
+
16
+ .tree-container {
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: 2px;
20
+ padding: 4px;
21
+ background-color: transparent;
22
+ border-radius: 4px;
23
+ }
24
+ `;
25
+
26
+ export { styles };
@@ -0,0 +1,3 @@
1
+ import { Tree } from './Tree.js';
2
+ export { Tree };
3
+ export default Tree;
@@ -0,0 +1,52 @@
1
+ import { PropertyValues } from 'lit';
2
+ import { BaseElement } from '../BaseElement.js';
3
+ /**
4
+ * TreeItem 컴포넌트는 트리의 개별 노드를 나타냅니다.
5
+ * u-tree 컴포넌트와 함께 사용되며, 중첩된 구조를 지원합니다.
6
+ */
7
+ export declare class TreeItem extends BaseElement {
8
+ static styles: import('lit').CSSResultGroup[];
9
+ static dependencies: Record<string, typeof BaseElement>;
10
+ /** 트리 항목이 비활성화 상태인지 여부입니다. */
11
+ disabled: boolean;
12
+ /** 트리 항목이 선택된 상태인지 여부입니다. */
13
+ selected: boolean;
14
+ /** 트리 항목이 확장된 상태인지 여부입니다. */
15
+ expanded: boolean;
16
+ /** 트리 항목이 리프 노드인지 여부입니다. */
17
+ leaf: boolean;
18
+ /** 트리 항목의 들여쓰기 레벨입니다. */
19
+ level: number;
20
+ /** 트리 항목의 값입니다. */
21
+ value: string;
22
+ /** 트리 항목의 아이콘입니다. */
23
+ icon: string;
24
+ private hasChildren;
25
+ connectedCallback(): void;
26
+ protected updated(changedProperties: PropertyValues): void;
27
+ render(): import('lit-html').TemplateResult<1>;
28
+ /** 확장/축소 아이콘을 렌더링합니다. */
29
+ private renderExpandIcon;
30
+ /** 커스텀 아이콘을 렌더링합니다. */
31
+ private renderIcon;
32
+ /** 헤더 클릭 이벤트를 처리합니다. */
33
+ private handleHeaderClick;
34
+ /** 확장/축소 아이콘 클릭 이벤트를 처리합니다. */
35
+ private handleExpandClick;
36
+ /** 슬롯 변경 시 자식 노드 확인 */
37
+ private handleSlotChange;
38
+ /** 자식 노드가 있는지 확인합니다. */
39
+ private checkForChildren;
40
+ /**
41
+ * 트리 항목을 확장합니다.
42
+ */
43
+ expand(): void;
44
+ /**
45
+ * 트리 항목을 축소합니다.
46
+ */
47
+ collapse(): void;
48
+ /**
49
+ * 트리 항목의 확장 상태를 토글합니다.
50
+ */
51
+ toggle(): void;
52
+ }
@@ -0,0 +1,163 @@
1
+ import { html, nothing } from 'lit';
2
+ import { property, state } from 'lit/decorators.js';
3
+ import { BaseElement } from '../BaseElement.js';
4
+ import { styles } from './TreeItem.styles.js';
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __decorateClass = (decorators, target, key, kind) => {
8
+ var result = void 0 ;
9
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
10
+ if (decorator = decorators[i])
11
+ result = (decorator(target, key, result) ) || result;
12
+ if (result) __defProp(target, key, result);
13
+ return result;
14
+ };
15
+ class TreeItem extends BaseElement {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.disabled = false;
19
+ this.selected = false;
20
+ this.expanded = false;
21
+ this.leaf = false;
22
+ this.level = 0;
23
+ this.value = "";
24
+ this.icon = "";
25
+ this.hasChildren = false;
26
+ /** 헤더 클릭 이벤트를 처리합니다. */
27
+ this.handleHeaderClick = (e) => {
28
+ if (this.disabled) {
29
+ e.preventDefault();
30
+ e.stopPropagation();
31
+ return;
32
+ }
33
+ this.selected = true;
34
+ this.emit("u-select", { value: this.value, item: this });
35
+ };
36
+ /** 확장/축소 아이콘 클릭 이벤트를 처리합니다. */
37
+ this.handleExpandClick = (e) => {
38
+ e.stopPropagation();
39
+ if (this.disabled || this.leaf || !this.hasChildren) {
40
+ return;
41
+ }
42
+ this.expanded = !this.expanded;
43
+ this.emit("u-toggle", { expanded: this.expanded, item: this });
44
+ };
45
+ /** 슬롯 변경 시 자식 노드 확인 */
46
+ this.handleSlotChange = () => {
47
+ this.checkForChildren();
48
+ };
49
+ }
50
+ static {
51
+ this.styles = [super.styles, styles];
52
+ }
53
+ static {
54
+ this.dependencies = {};
55
+ }
56
+ connectedCallback() {
57
+ super.connectedCallback();
58
+ this.setAttribute("role", "treeitem");
59
+ this.setAttribute("tabindex", this.disabled ? "-1" : "0");
60
+ this.checkForChildren();
61
+ }
62
+ updated(changedProperties) {
63
+ super.updated(changedProperties);
64
+ if (changedProperties.has("disabled")) {
65
+ this.setAttribute("tabindex", this.disabled ? "-1" : "0");
66
+ this.setAttribute("aria-disabled", this.disabled ? "true" : "false");
67
+ }
68
+ if (changedProperties.has("expanded")) {
69
+ this.setAttribute("aria-expanded", this.hasChildren ? String(this.expanded) : "false");
70
+ }
71
+ if (changedProperties.has("selected")) {
72
+ this.setAttribute("aria-selected", this.selected ? "true" : "false");
73
+ }
74
+ }
75
+ render() {
76
+ return html`
77
+ <div class="header" @click=${this.handleHeaderClick} style="padding-left: calc(${this.level} * 20px)">
78
+ ${this.renderExpandIcon()}
79
+ ${this.renderIcon()}
80
+ <span class="label">
81
+ <slot name="label"></slot>
82
+ <slot></slot>
83
+ </span>
84
+ </div>
85
+ <div class="children" ?hidden=${!this.expanded}>
86
+ <slot name="children" @slotchange=${this.handleSlotChange}></slot>
87
+ </div>
88
+ `;
89
+ }
90
+ /** 확장/축소 아이콘을 렌더링합니다. */
91
+ renderExpandIcon() {
92
+ if (this.leaf || !this.hasChildren) {
93
+ return html`<span class="expand-icon placeholder"></span>`;
94
+ }
95
+ return html`
96
+ <span class="expand-icon" @click=${this.handleExpandClick}>
97
+ ${this.expanded ? "▼" : "▶"}
98
+ </span>
99
+ `;
100
+ }
101
+ /** 커스텀 아이콘을 렌더링합니다. */
102
+ renderIcon() {
103
+ if (!this.icon) {
104
+ return nothing;
105
+ }
106
+ return html`<span class="icon">${this.icon}</span>`;
107
+ }
108
+ /** 자식 노드가 있는지 확인합니다. */
109
+ checkForChildren() {
110
+ const childrenSlot = this.shadowRoot?.querySelector('slot[name="children"]');
111
+ const children = childrenSlot?.assignedElements() || [];
112
+ this.hasChildren = children.length > 0;
113
+ this.setAttribute("aria-expanded", this.hasChildren ? String(this.expanded) : "false");
114
+ }
115
+ /**
116
+ * 트리 항목을 확장합니다.
117
+ */
118
+ expand() {
119
+ if (!this.leaf && this.hasChildren) {
120
+ this.expanded = true;
121
+ }
122
+ }
123
+ /**
124
+ * 트리 항목을 축소합니다.
125
+ */
126
+ collapse() {
127
+ this.expanded = false;
128
+ }
129
+ /**
130
+ * 트리 항목의 확장 상태를 토글합니다.
131
+ */
132
+ toggle() {
133
+ if (!this.leaf && this.hasChildren) {
134
+ this.expanded = !this.expanded;
135
+ }
136
+ }
137
+ }
138
+ __decorateClass([
139
+ property({ type: Boolean, reflect: true })
140
+ ], TreeItem.prototype, "disabled");
141
+ __decorateClass([
142
+ property({ type: Boolean, reflect: true })
143
+ ], TreeItem.prototype, "selected");
144
+ __decorateClass([
145
+ property({ type: Boolean, reflect: true })
146
+ ], TreeItem.prototype, "expanded");
147
+ __decorateClass([
148
+ property({ type: Boolean, reflect: true })
149
+ ], TreeItem.prototype, "leaf");
150
+ __decorateClass([
151
+ property({ type: Number, reflect: true })
152
+ ], TreeItem.prototype, "level");
153
+ __decorateClass([
154
+ property({ type: String })
155
+ ], TreeItem.prototype, "value");
156
+ __decorateClass([
157
+ property({ type: String })
158
+ ], TreeItem.prototype, "icon");
159
+ __decorateClass([
160
+ state()
161
+ ], TreeItem.prototype, "hasChildren");
162
+
163
+ export { TreeItem };
@@ -0,0 +1 @@
1
+ export declare const styles: import('lit').CSSResult;
@@ -0,0 +1,97 @@
1
+ import { css } from 'lit';
2
+
3
+ const styles = css`
4
+ :host {
5
+ display: block;
6
+ --tree-indent: 20px;
7
+ }
8
+
9
+ :host([disabled]) {
10
+ opacity: 0.5;
11
+ pointer-events: none;
12
+ cursor: not-allowed;
13
+ }
14
+
15
+ :host([disabled]) .header {
16
+ cursor: not-allowed;
17
+ pointer-events: none;
18
+ }
19
+
20
+ .header {
21
+ display: flex;
22
+ flex-direction: row;
23
+ align-items: center;
24
+ gap: 6px;
25
+ padding: 6px 8px;
26
+ line-height: 1.5;
27
+ border-radius: 4px;
28
+ background-color: transparent;
29
+ transition: background-color 0.2s ease;
30
+ user-select: none;
31
+ cursor: pointer;
32
+ }
33
+
34
+ :host(:not([disabled])) .header:hover {
35
+ background-color: var(--u-bg-color-hover, #f5f5f5);
36
+ }
37
+
38
+ :host([selected]) .header {
39
+ color: var(--u-blue-600, #0066cc);
40
+ background-color: var(--u-blue-0, #e8f4f8);
41
+ }
42
+
43
+ .expand-icon {
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ width: 16px;
48
+ height: 16px;
49
+ font-size: 10px;
50
+ color: var(--u-icon-color, #757575);
51
+ transition: transform 0.2s ease, color 0.2s ease;
52
+ cursor: pointer;
53
+ }
54
+
55
+ .expand-icon:hover {
56
+ color: var(--u-icon-color-hover, #0066cc);
57
+ }
58
+
59
+ .expand-icon.placeholder {
60
+ cursor: default;
61
+ opacity: 0;
62
+ pointer-events: none;
63
+ }
64
+
65
+ .icon {
66
+ display: flex;
67
+ align-items: center;
68
+ justify-content: center;
69
+ width: 18px;
70
+ height: 18px;
71
+ font-size: 14px;
72
+ color: var(--u-icon-color, #757575);
73
+ }
74
+
75
+ .label {
76
+ flex: 1;
77
+ overflow: hidden;
78
+ text-overflow: ellipsis;
79
+ white-space: nowrap;
80
+ font-size: 14px;
81
+ color: var(--u-text-color, #212121);
82
+ }
83
+
84
+ :host([selected]) .label {
85
+ color: var(--u-blue-700, #1976d2);
86
+ }
87
+
88
+ .children {
89
+ display: block;
90
+ }
91
+
92
+ .children[hidden] {
93
+ display: none;
94
+ }
95
+ `;
96
+
97
+ export { styles };
@@ -0,0 +1,3 @@
1
+ import { TreeItem } from './TreeItem.js';
2
+ export { TreeItem };
3
+ export default TreeItem;
package/dist/index.js CHANGED
@@ -8,11 +8,14 @@ export { IconButton } from './components/icon-button/IconButton.js';
8
8
  export { Input } from './components/input/Input.js';
9
9
  export { Menu } from './components/menu/Menu.js';
10
10
  export { Panel } from './components/panel/Panel.js';
11
+ export { ProgressBar } from './components/progress-bar/ProgressBar.js';
11
12
  export { ProgressRing } from './components/progress-ring/ProgressRing.js';
12
13
  export { Spinner } from './components/spinner/Spinner.js';
13
14
  export { SplitPanel } from './components/split-panel/SplitPanel.js';
14
15
  export { Tooltip } from './components/tooltip/Tooltip.js';
16
+ export { Tree } from './components/tree/Tree.js';
17
+ export { TreeItem } from './components/tree-item/TreeItem.js';
15
18
  export { BrowserStorage } from './utilities/BrowserStorage.js';
16
19
  export { getPropertyMeta, propertyMeta, setPropertyMeta } from './utilities/decorators.js';
17
20
  export { notifier } from './utilities/notifier.js';
18
- export { theme } from './utilities/theme.js';
21
+ export { Theme, theme } from './utilities/theme.js';
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { ProgressBar } from '../../components/progress-bar/index';
3
+
4
+ export declare const UProgressBar: React.ForwardRefExoticComponent<
5
+ Partial<ProgressBar> & React.HTMLAttributes<ProgressBar>
6
+ >;
7
+
8
+ export type UProgressBarProps = React.ComponentProps<typeof UProgressBar>;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { createComponent } from '@lit/react';
3
+ import { ProgressBar } from '../../components/progress-bar/index';
4
+
5
+ export const UProgressBar = createComponent({
6
+ react: React,
7
+ tagName: 'u-progress-bar',
8
+ elementClass: ProgressBar,
9
+ events: {}
10
+ });
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { Tree } from '../../components/tree/index';
3
+
4
+ export declare const UTree: React.ForwardRefExoticComponent<
5
+ Partial<Tree> & React.HTMLAttributes<Tree>
6
+ >;
7
+
8
+ export type UTreeProps = React.ComponentProps<typeof UTree>;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { createComponent } from '@lit/react';
3
+ import { Tree } from '../../components/tree/index';
4
+
5
+ export const UTree = createComponent({
6
+ react: React,
7
+ tagName: 'u-tree',
8
+ elementClass: Tree,
9
+ events: {}
10
+ });
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { TreeItem } from '../../components/tree-item/index';
3
+
4
+ export declare const UTreeItem: React.ForwardRefExoticComponent<
5
+ Partial<TreeItem> & React.HTMLAttributes<TreeItem>
6
+ >;
7
+
8
+ export type UTreeItemProps = React.ComponentProps<typeof UTreeItem>;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { createComponent } from '@lit/react';
3
+ import { TreeItem } from '../../components/tree-item/index';
4
+
5
+ export const UTreeItem = createComponent({
6
+ react: React,
7
+ tagName: 'u-tree-item',
8
+ elementClass: TreeItem,
9
+ events: {}
10
+ });
@@ -17,7 +17,10 @@ export { UInput, UInputProps } from './UInput';
17
17
  export { UMenu, UMenuProps } from './UMenu';
18
18
  export { UMenuItem, UMenuItemProps } from './UMenuItem';
19
19
  export { UPanel, UPanelProps } from './UPanel';
20
+ export { UProgressBar, UProgressBarProps } from './UProgressBar';
20
21
  export { UProgressRing, UProgressRingProps } from './UProgressRing';
21
22
  export { USpinner, USpinnerProps } from './USpinner';
22
23
  export { USplitPanel, USplitPanelProps } from './USplitPanel';
23
24
  export { UTooltip, UTooltipProps } from './UTooltip';
25
+ export { UTree, UTreeProps } from './UTree';
26
+ export { UTreeItem, UTreeItemProps } from './UTreeItem';
@@ -10,7 +10,10 @@ export { UInput } from './UInput.js';
10
10
  export { UMenu } from './UMenu.js';
11
11
  export { UMenuItem } from './UMenuItem.js';
12
12
  export { UPanel } from './UPanel.js';
13
+ export { UProgressBar } from './UProgressBar.js';
13
14
  export { UProgressRing } from './UProgressRing.js';
14
15
  export { USpinner } from './USpinner.js';
15
16
  export { USplitPanel } from './USplitPanel.js';
16
17
  export { UTooltip } from './UTooltip.js';
18
+ export { UTree } from './UTree.js';
19
+ export { UTreeItem } from './UTreeItem.js';
@@ -27,18 +27,18 @@ export interface ThemeInitOptions {
27
27
  /**
28
28
  * Theme 클래스 — 싱글톤 패턴으로 사용합니다.
29
29
  */
30
- declare class Theme {
30
+ export declare class Theme {
31
31
  private static _instance;
32
32
  private readonly STORAGE_KEY_DEFAULT;
33
33
  private storage;
34
- private isInitialized;
35
- private isDebugMode;
34
+ private _isInitialized;
35
+ private _isDebugMode;
36
36
  /** private 생성자 — 외부에서 new Theme()를 할 수 없게 함 */
37
37
  private constructor();
38
38
  /** 싱글톤 인스턴스 접근자 */
39
39
  static get instance(): Theme;
40
40
  /** 외부에서 현재 초기화 상태를 확인할 수 있게 getter 제공 */
41
- get initialized(): boolean;
41
+ get isInitialized(): boolean;
42
42
  /**
43
43
  * 테마 유틸리티 초기화
44
44
  */
@@ -64,4 +64,3 @@ declare class Theme {
64
64
  * 테마 유틸리티 싱글톤 인스턴스입니다.
65
65
  */
66
66
  export declare const theme: Theme;
67
- export {};
@@ -7,8 +7,8 @@ class Theme {
7
7
  constructor() {
8
8
  this.STORAGE_KEY_DEFAULT = "theme";
9
9
  this.storage = null;
10
- this.isInitialized = false;
11
- this.isDebugMode = false;
10
+ this._isInitialized = false;
11
+ this._isDebugMode = false;
12
12
  /**
13
13
  * 시스템 테마 변경을 처리하는 메서드
14
14
  */
@@ -27,21 +27,21 @@ class Theme {
27
27
  return this._instance;
28
28
  }
29
29
  /** 외부에서 현재 초기화 상태를 확인할 수 있게 getter 제공 */
30
- get initialized() {
31
- return this.isInitialized;
30
+ get isInitialized() {
31
+ return this._isInitialized;
32
32
  }
33
33
  /**
34
34
  * 테마 유틸리티 초기화
35
35
  */
36
36
  async init(options) {
37
- this.isDebugMode = options?.debug || false;
37
+ this._isDebugMode = options?.debug || false;
38
38
  this.log("init called", { options });
39
39
  if (options?.store) {
40
40
  this.log("store option provided, initializing BrowserStorage");
41
41
  this.storage = new BrowserStorage(options.store);
42
42
  }
43
- options?.useBuiltIn || true;
44
- {
43
+ const useBuiltIn = options?.useBuiltIn ?? true;
44
+ if (useBuiltIn) {
45
45
  this.log("Import enabled: loading styles via internal assets");
46
46
  const assets = Object.entries(/* #__PURE__ */ Object.assign({"../assets/styles/dark.css": dark,"../assets/styles/light.css": light
47
47
 
@@ -70,7 +70,7 @@ class Theme {
70
70
  }
71
71
  }
72
72
  this.set(theme2);
73
- this.isInitialized = true;
73
+ this._isInitialized = true;
74
74
  this.log("theme initialized");
75
75
  }
76
76
  /**
@@ -125,9 +125,9 @@ class Theme {
125
125
  * 디버그 모드시 로그 출력 함수 (인스턴스 스코프)
126
126
  */
127
127
  log(...args) {
128
- if (this.isDebugMode) console.log("[theme]", ...args);
128
+ if (this._isDebugMode) console.log("[theme]", ...args);
129
129
  }
130
130
  }
131
131
  const theme = Theme.instance;
132
132
 
133
- export { theme };
133
+ export { Theme, theme };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/components",
3
3
  "description": "web-components library by iyulab based on lit-element",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-components",