@iyulab/components 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/components/UOverlayElement.js +2 -1
- package/dist/components/input/UInput.d.ts +6 -2
- package/dist/components/input/UInput.js +4 -1
- package/dist/components/tree/UTree.js +2 -1
- package/dist/utilities/Dialog.js +34 -27
- package/dist/utilities/Theme.js +1 -1
- package/dist/utilities/icons.js +1 -1
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.7.2] - 2026-07-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **`Dialog.show()`가 영구 대기(hang)에 빠지던 결함 수정** — 프로미스 executor 가 `async` 였던 탓에 `await dialog.updateComplete` 가 reject 되면 예외가 삼켜지고 `hide` 리스너가 등록조차 되지 않아, `await Dialog.show(...)` 호출자가 **영원히 매달렸다**. 이제 리스너를 `await` **이전에** 등록하고(대기 중 발생한 `hide` 를 놓치던 경합도 함께 해소), 업데이트 실패 시 `console.error` 후 문서화된 "닫힘 = null" 규약대로 `null` 로 종료하며 고아 엘리먼트를 DOM 에서 제거한다. 회귀 테스트 3건 추가(정상 resolve / 닫힘 null / updateComplete reject).
|
|
7
|
+
- `UOverlayElement`: 삼항 연산자를 문(statement)으로 사용하던 `open ? setup() : cleanup()` 을 `if/else` 로 교정.
|
|
8
|
+
- `URating`/`USelect`/`UTree`: `switch` case 블록 안의 `const` 선언이 블록 스코프를 벗어나 다른 case 로 누출될 수 있던 형태를 중괄호 블록으로 격리(`no-case-declarations`).
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **이 패키지의 eslint 가 실제로 동작하기 시작했다.** `eslint.config.js` 의 두 결함 — (1) `files: ["src/**/*"]` 가 ESLint 9 에서 universal 패턴으로 취급돼 `.ts` 를 린팅 대상으로 opt-in 하지 못함, (2) 배열 프리셋(`tseslint.configs.recommended`)을 객체 스프레드해 프리셋이 통째로 무력화됨 — 을 수정했다. `build` 스크립트에 `eslint &&` 게이트가 있었으나 매칭 파일이 0개라 **항상 통과**하고 있었다. 위 결함들은 모두 이 복구로 처음 드러난 것이다.
|
|
12
|
+
- `npm run lint` / `npm run lint:fix` 스크립트 추가(flex-table·u-widgets 와 통일).
|
|
13
|
+
- 내부 타입 정밀화: `Dialog`/`Theme`/`UTooltip`/`UInput`/`UTextarea` 의 `any` 캐스팅을 실제 타입(`CloseOnPolicy[]`, `UInput`, `InputType`, `VirtualElement`, `unknown[]`)으로 교체. 공개 API 시그니처 변경 없음.
|
|
14
|
+
|
|
15
|
+
## [1.7.1] - 2026-07-19
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- `UInput.type` 을 host 요소로 **reflect** 하도록 수정 — 미반영 시 `u-input[type="number"]::part(input)` 같은 속성 셀렉터가 HTML 속성으로 준 경우에만 매칭되고, React/Lit 의 property 바인딩(`.type=`, `el.type=`)에서는 host 에 속성이 나타나지 않아 무효였다. 형제 컴포넌트 `URadio.type` 은 이미 reflect 하고 있어 리포 내 비일관이기도 했다.
|
|
19
|
+
|
|
20
|
+
### Documentation
|
|
21
|
+
- `docs/theming.md` 에 `::part()` 커스터마이즈 섹션 신설 — 텍스트 정렬 레시피(`text-align` + `font-variant-numeric: tabular-nums`), 숫자 입력 우측정렬을 기본값으로 두지 않는 근거, 비반영 속성용 클래스 셀렉터 대안, 숫자 포맷팅 책임 범위.
|
|
22
|
+
- `docs/architecture.md` CSS Parts 절에서 `theming.md` 로 상호 링크. `UInput` 의 `@csspart input` JSDoc 보강.
|
|
23
|
+
|
|
3
24
|
## [1.7.0] - 2026-07-17
|
|
4
25
|
|
|
5
26
|
### Fixed
|
|
@@ -48,7 +48,8 @@ var UOverlayElement = class extends UElement {
|
|
|
48
48
|
}
|
|
49
49
|
updated(changedProperties) {
|
|
50
50
|
super.updated(changedProperties);
|
|
51
|
-
if (changedProperties.has("open")) this.open
|
|
51
|
+
if (changedProperties.has("open")) if (this.open) this.setup();
|
|
52
|
+
else this.cleanup();
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
55
|
* 오버레이를 표시합니다. u-show 이벤트가 취소되면 열리지 않습니다.
|
|
@@ -16,7 +16,11 @@ export type InputVariant = 'outlined' | 'filled' | 'underlined' | 'borderless';
|
|
|
16
16
|
*
|
|
17
17
|
* @csspart field - u-field 요소
|
|
18
18
|
* @csspart container - input과 prefix/suffix를 감싸는 컨테이너
|
|
19
|
-
* @csspart input - 네이티브 input
|
|
19
|
+
* @csspart input - 네이티브 input 요소.
|
|
20
|
+
* text-align 등 표현 관련 속성을 지정하지 않으므로 소비앱이 ::part(input)으로 재정의한다.
|
|
21
|
+
* 예: `u-input[type="number"]::part(input) { text-align: right; font-variant-numeric: tabular-nums; }`
|
|
22
|
+
* (숫자 우측정렬은 값이 열로 쌓여 자릿수를 비교할 때 유효하므로 라이브러리 기본값으로 두지 않는다.
|
|
23
|
+
* docs/theming.md 참고)
|
|
20
24
|
* @csspart popover - 드롭다운 팝오버 요소
|
|
21
25
|
*
|
|
22
26
|
* @cssprop --input-popover-width - 드롭다운 팝오버의 너비 (기본값: 앵커(트리거) 너비)
|
|
@@ -32,7 +36,7 @@ export declare class UInput extends UFormControlElement<string> {
|
|
|
32
36
|
variant: InputVariant;
|
|
33
37
|
/** 전체 지우기 버튼 표시 여부 */
|
|
34
38
|
clearable: boolean;
|
|
35
|
-
/** input 요소의 type
|
|
39
|
+
/** input 요소의 type 속성. 외부 `u-input[type="..."]` 스타일 훅을 위해 reflect 한다 (URadio.type 과 동일). */
|
|
36
40
|
type: InputType;
|
|
37
41
|
/** 최소 글자 수 */
|
|
38
42
|
minlength?: number;
|
|
@@ -276,7 +276,10 @@ __decorate([property({
|
|
|
276
276
|
type: Boolean,
|
|
277
277
|
reflect: true
|
|
278
278
|
}), __decorateMetadata("design:type", Boolean)], UInput.prototype, "clearable", void 0);
|
|
279
|
-
__decorate([property({
|
|
279
|
+
__decorate([property({
|
|
280
|
+
type: String,
|
|
281
|
+
reflect: true
|
|
282
|
+
}), __decorateMetadata("design:type", Object)], UInput.prototype, "type", void 0);
|
|
280
283
|
__decorate([property({ type: Number }), __decorateMetadata("design:type", Number)], UInput.prototype, "minlength", void 0);
|
|
281
284
|
__decorate([property({ type: Number }), __decorateMetadata("design:type", Number)], UInput.prototype, "maxlength", void 0);
|
|
282
285
|
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UInput.prototype, "min", void 0);
|
|
@@ -119,11 +119,12 @@ var UTree = class UTree extends UElement {
|
|
|
119
119
|
items[items.length - 1]?.focus();
|
|
120
120
|
break;
|
|
121
121
|
case "Enter":
|
|
122
|
-
case " ":
|
|
122
|
+
case " ": {
|
|
123
123
|
e.preventDefault();
|
|
124
124
|
const header = focused.shadowRoot?.querySelector(".header");
|
|
125
125
|
if (header) header.click();
|
|
126
126
|
break;
|
|
127
|
+
}
|
|
127
128
|
}
|
|
128
129
|
};
|
|
129
130
|
}
|
package/dist/utilities/Dialog.js
CHANGED
|
@@ -81,39 +81,46 @@ var Dialog = class {
|
|
|
81
81
|
* 커스텀 다이얼로그를 표시합니다.
|
|
82
82
|
* @returns 클릭된 액션의 value 또는 닫힌 경우 null
|
|
83
83
|
*/
|
|
84
|
-
static show(options) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
await dialog.updateComplete;
|
|
110
|
-
dialog.show();
|
|
84
|
+
static async show(options) {
|
|
85
|
+
let closeValue = null;
|
|
86
|
+
const dialog = this.createDialog(options);
|
|
87
|
+
const content = typeof options.content === "string" ? unsafeHTML(options.content) : options.content;
|
|
88
|
+
const actions = options.actions && options.actions.length > 0 ? options.actions : null;
|
|
89
|
+
render(html`
|
|
90
|
+
<div style="min-width: 320px; font-size: 14px; line-height: 1.6;">
|
|
91
|
+
${content}
|
|
92
|
+
${actions ? html`
|
|
93
|
+
<div style="display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px;">
|
|
94
|
+
${actions.map((action) => html`
|
|
95
|
+
<u-button
|
|
96
|
+
variant=${action.variant || "solid"}
|
|
97
|
+
@click=${() => {
|
|
98
|
+
closeValue = action.value;
|
|
99
|
+
dialog.hide();
|
|
100
|
+
}}
|
|
101
|
+
>${action.label}</u-button>
|
|
102
|
+
`)}
|
|
103
|
+
</div>
|
|
104
|
+
` : nothing}
|
|
105
|
+
</div>
|
|
106
|
+
`, dialog);
|
|
107
|
+
(options.target || document.body).appendChild(dialog);
|
|
108
|
+
const closed = new Promise((resolve) => {
|
|
111
109
|
dialog.addEventListener("hide", (e) => {
|
|
112
110
|
if (e.target !== dialog) return;
|
|
113
111
|
setTimeout(() => dialog.remove(), 300);
|
|
114
112
|
resolve(closeValue);
|
|
115
113
|
});
|
|
116
114
|
});
|
|
115
|
+
try {
|
|
116
|
+
await dialog.updateComplete;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error("[Dialog] 다이얼로그를 표시하지 못했습니다.", error);
|
|
119
|
+
dialog.remove();
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
dialog.show();
|
|
123
|
+
return closed;
|
|
117
124
|
}
|
|
118
125
|
/** 다이얼로그 엘리먼트를 생성하고 옵션을 적용합니다. */
|
|
119
126
|
static createDialog(options) {
|
package/dist/utilities/Theme.js
CHANGED
|
@@ -51,7 +51,7 @@ var Theme = class {
|
|
|
51
51
|
}
|
|
52
52
|
if (options?.useBuiltIn ?? true) {
|
|
53
53
|
this.log("Import enabled: loading styles via internal assets");
|
|
54
|
-
for (
|
|
54
|
+
for (const [name, module] of internalStyleBundle) {
|
|
55
55
|
const style = document.createElement("style");
|
|
56
56
|
style.setAttribute("data-name", name);
|
|
57
57
|
style.textContent = module;
|
package/dist/utilities/icons.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/components",
|
|
3
3
|
"description": "web-components library based on lit-element made by iyulab",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iyulab",
|
|
7
7
|
"components",
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
"start": "vite --force",
|
|
43
43
|
"test": "vitest run",
|
|
44
44
|
"test:browser": "vitest run --project=browser",
|
|
45
|
+
"lint": "eslint src/",
|
|
46
|
+
"lint:fix": "eslint src/ --fix",
|
|
45
47
|
"build:plugins": "tsc -p plugins/tsconfig.json",
|
|
46
48
|
"build": "eslint && vite build && npm run build:plugins"
|
|
47
49
|
},
|