@iyulab/components 1.22.0 → 1.23.0
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 +180 -0
- package/README.md +32 -0
- package/dist/components/UOverlayElement.d.ts +18 -0
- package/dist/components/UOverlayElement.js +24 -0
- package/dist/components/badge/UBadge.d.ts +5 -0
- package/dist/components/badge/UBadge.js +9 -0
- package/dist/components/badge/UBadge.styles.js +6 -0
- package/dist/components/dialog/UDialog.styles.js +1 -1
- package/dist/components/drawer/UDrawer.styles.js +1 -1
- package/dist/components/expander/UExpander.d.ts +38 -0
- package/dist/components/expander/UExpander.js +78 -0
- package/dist/components/expander/UExpander.styles.d.ts +1 -0
- package/dist/components/expander/UExpander.styles.js +95 -0
- package/dist/components/panel/UPanel.d.ts +0 -2
- package/dist/components/panel/UPanel.js +0 -5
- package/dist/components/skeleton/USkeleton.d.ts +7 -0
- package/dist/components/skeleton/USkeleton.js +7 -1
- package/dist/components/skeleton/USkeleton.styles.js +64 -0
- package/dist/components/tab-panel/UTabPanel.d.ts +4 -2
- package/dist/components/tab-panel/UTabPanel.js +0 -5
- package/dist/components/tag/UTag.d.ts +7 -0
- package/dist/components/tag/UTag.js +10 -1
- package/dist/components/tag/UTag.styles.js +4 -0
- package/dist/components/tree/UTree.d.ts +4 -2
- package/dist/components/tree/UTree.js +0 -5
- package/dist/components/tree-item/UTreeItem.d.ts +4 -1
- package/dist/components/tree-item/UTreeItem.js +0 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/react/UExpander.d.ts +16 -0
- package/dist/react/UExpander.js +13 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/utilities/Dialog.js +1 -1
- package/dist/utilities/Locale.d.ts +34 -0
- package/dist/utilities/Locale.js +43 -6
- package/dist/utilities/Theme.d.ts +24 -0
- package/dist/utilities/Theme.js +52 -0
- package/dist/utilities/accent.d.ts +52 -0
- package/dist/utilities/accent.js +127 -0
- package/dist/utilities/icons.js +1 -1
- package/dist/utilities/statusIcon.d.ts +31 -0
- package/dist/utilities/statusIcon.js +34 -0
- package/package.json +3 -2
- package/skills/iyulab-components/SKILL.md +2 -1
- package/skills/iyulab-components/references/components/badge.md +2 -1
- package/skills/iyulab-components/references/components/button.md +3 -1
- package/skills/iyulab-components/references/components/drawer.md +30 -0
- package/skills/iyulab-components/references/components/expander.md +70 -0
- package/skills/iyulab-components/references/components/input.md +6 -0
- package/skills/iyulab-components/references/components/panel.md +6 -4
- package/skills/iyulab-components/references/components/skeleton.md +12 -0
- package/skills/iyulab-components/references/components/tab-panel.md +4 -5
- package/skills/iyulab-components/references/components/tag.md +18 -1
- package/skills/iyulab-components/references/components/textarea.md +5 -0
- package/skills/iyulab-components/references/components/tree.md +2 -4
- package/skills/iyulab-components/references/usage.md +22 -2
- package/skills/iyulab-components/references/utilities/theme.md +23 -4
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//#region src/utilities/accent.ts
|
|
2
|
+
/** 대비 계약의 문턱값 — `tests/build/token-contrast.test.ts` 와 같은 값이다. */
|
|
3
|
+
var AA_TEXT = 4.5;
|
|
4
|
+
/** `-strong` 이 `-color` 와 갈려 보이기 위한 최소 대비(2026-08-04 채택). */
|
|
5
|
+
var MIN_STEP_SEPARATION = 1.2;
|
|
6
|
+
var clamp = (v) => Math.min(255, Math.max(0, v));
|
|
7
|
+
var channelLum = (c) => c <= .03928 ? c / 12.92 : Math.pow((c + .055) / 1.055, 2.4);
|
|
8
|
+
/** `#rgb`·`#rrggbb`·`rgb(r g b)` 를 [r,g,b] 로. 해석할 수 없으면 `null`. */
|
|
9
|
+
function parseColor(value) {
|
|
10
|
+
const v = value.trim();
|
|
11
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(v);
|
|
12
|
+
if (hex) {
|
|
13
|
+
const h = hex[1].length === 3 ? hex[1].replace(/./g, (c) => c + c) : hex[1];
|
|
14
|
+
const n = parseInt(h, 16);
|
|
15
|
+
return [
|
|
16
|
+
n >> 16 & 255,
|
|
17
|
+
n >> 8 & 255,
|
|
18
|
+
n & 255
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
const rgb = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(v);
|
|
22
|
+
if (rgb) return [
|
|
23
|
+
Number(rgb[1]),
|
|
24
|
+
Number(rgb[2]),
|
|
25
|
+
Number(rgb[3])
|
|
26
|
+
];
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function toHex([r, g, b]) {
|
|
30
|
+
return "#" + [
|
|
31
|
+
r,
|
|
32
|
+
g,
|
|
33
|
+
b
|
|
34
|
+
].map((v) => Math.round(clamp(v)).toString(16).padStart(2, "0")).join("");
|
|
35
|
+
}
|
|
36
|
+
function luminance(color) {
|
|
37
|
+
const rgb = parseColor(color);
|
|
38
|
+
if (!rgb) return 0;
|
|
39
|
+
const [r, g, b] = rgb.map((v) => channelLum(v / 255));
|
|
40
|
+
return .2126 * r + .7152 * g + .0722 * b;
|
|
41
|
+
}
|
|
42
|
+
/** WCAG 2.1 명암비. */
|
|
43
|
+
function contrast(a, b) {
|
|
44
|
+
const [x, y] = [luminance(a), luminance(b)];
|
|
45
|
+
return (Math.max(x, y) + .05) / (Math.min(x, y) + .05);
|
|
46
|
+
}
|
|
47
|
+
/** `color-mix(in srgb, a p%, b)` 와 같은 계산. */
|
|
48
|
+
function mix(a, b, p) {
|
|
49
|
+
const [x, y] = [parseColor(a), parseColor(b)];
|
|
50
|
+
if (!x || !y) return a;
|
|
51
|
+
return toHex([
|
|
52
|
+
0,
|
|
53
|
+
1,
|
|
54
|
+
2
|
|
55
|
+
].map((i) => x[i] * p + y[i] * (1 - p)));
|
|
56
|
+
}
|
|
57
|
+
/** 면 위의 글자를 고른다 — 검정/흰색 중 대비가 큰 쪽. */
|
|
58
|
+
function pickOnColor(surface) {
|
|
59
|
+
return contrast(surface, "#000000") >= contrast(surface, "#ffffff") ? "#000000" : "#ffffff";
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 시드에서 `--u-primary-*` 램프를 만든다.
|
|
63
|
+
*
|
|
64
|
+
* @param seed 브랜드 색
|
|
65
|
+
* @param bg 그 램프가 놓일 바탕(`--u-bg-color`) — 라이트/다크가 이 값 하나로 갈린다
|
|
66
|
+
*/
|
|
67
|
+
var STEP = .02;
|
|
68
|
+
/**
|
|
69
|
+
* `from` 에서 `target` 쪽으로 조금씩 옮기며 **조건을 만족하는 첫 색**을 돌려준다.
|
|
70
|
+
* 못 찾으면 `null` — 호출부가 반대 방향을 시도한다.
|
|
71
|
+
*/
|
|
72
|
+
function towards(from, target, ok) {
|
|
73
|
+
for (let p = 1; p >= 0; p -= STEP) {
|
|
74
|
+
const c = mix(from, target, p);
|
|
75
|
+
if (ok(c)) return c;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function deriveAccentRamp(seed, bg) {
|
|
80
|
+
const light = luminance(bg) > .5;
|
|
81
|
+
const away = light ? "#000000" : "#ffffff";
|
|
82
|
+
const near = light ? "#ffffff" : "#000000";
|
|
83
|
+
/**
|
|
84
|
+
* 🔴**방향은 항상 «바탕에서 멀어지는 쪽»이 아니다 — 실측이 이 폴백을 만들게 했다.**
|
|
85
|
+
*
|
|
86
|
+
* `#F5F5F5` 시드 + 다크 바탕 → `-strong` 을 흰색 쪽으로 밀어도 **구분 1.09** 가 한계다
|
|
87
|
+
* (시드가 이미 거의 흰색이다) ⇒ 반대로 «어둡게» 가야 갈린다
|
|
88
|
+
* `#6A1B9A` 시드 + 다크 바탕 → `-weak` 이 바탕 쪽으로는 그래픽 3.0 을 못 낸다
|
|
89
|
+
*
|
|
90
|
+
* ⇒ 한 방향을 시도하고 실패하면 **반대 방향**을 본다. 두 방향 다 실패하는 것만 극단값으로
|
|
91
|
+
* 떨어뜨린다.
|
|
92
|
+
*/
|
|
93
|
+
const either = (from, ok, first, second) => towards(from, first, ok) ?? towards(from, second, ok);
|
|
94
|
+
const color = either(seed, (c) => contrast(c, pickOnColor(c)) >= 4.5, away, near) ?? seed;
|
|
95
|
+
const strongOk = (c) => contrast(c, bg) >= 4.5 && contrast(c, color) >= 1.2;
|
|
96
|
+
const strong = either(color, strongOk, away, near) ?? away;
|
|
97
|
+
let weak;
|
|
98
|
+
if (contrast(color, bg) >= 3) {
|
|
99
|
+
weak = color;
|
|
100
|
+
for (let p = 1; p >= 0; p -= STEP) {
|
|
101
|
+
const candidate = mix(color, bg, p);
|
|
102
|
+
if (contrast(candidate, bg) < 3) break;
|
|
103
|
+
weak = candidate;
|
|
104
|
+
}
|
|
105
|
+
} else weak = towards(color, away, (c) => contrast(c, bg) >= 3) ?? away;
|
|
106
|
+
return {
|
|
107
|
+
weakest: mix(weak, bg, .25),
|
|
108
|
+
weaker: mix(weak, bg, .5),
|
|
109
|
+
weak,
|
|
110
|
+
color,
|
|
111
|
+
strong,
|
|
112
|
+
txt: pickOnColor(color)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** 램프를 커스텀 프로퍼티 이름 → 값 맵으로. */
|
|
116
|
+
function accentCustomProperties(ramp, role = "primary") {
|
|
117
|
+
return {
|
|
118
|
+
[`--u-${role}-color-weakest`]: ramp.weakest,
|
|
119
|
+
[`--u-${role}-color-weaker`]: ramp.weaker,
|
|
120
|
+
[`--u-${role}-color-weak`]: ramp.weak,
|
|
121
|
+
[`--u-${role}-color`]: ramp.color,
|
|
122
|
+
[`--u-${role}-color-strong`]: ramp.strong,
|
|
123
|
+
[`--u-${role}-txt-color`]: ramp.txt
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { AA_TEXT, MIN_STEP_SEPARATION, accentCustomProperties, contrast, deriveAccentRamp, luminance, mix, parseColor, pickOnColor, toHex };
|
package/dist/utilities/icons.js
CHANGED
|
@@ -187,7 +187,7 @@ var IconRegistry = class {
|
|
|
187
187
|
IconCache.set(lib, name, svg);
|
|
188
188
|
return svg;
|
|
189
189
|
} catch (error) {
|
|
190
|
-
console.error(`[IconRegistry] '${key}'
|
|
190
|
+
console.error(`[IconRegistry] Failed to resolve icon '${key}' (will retry):`, error);
|
|
191
191
|
return;
|
|
192
192
|
}
|
|
193
193
|
})();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 역할 축(의미)의 상태 → 내장 아이콘 이름.
|
|
3
|
+
*
|
|
4
|
+
* ## 왜 있나
|
|
5
|
+
*
|
|
6
|
+
* `u-tag`·`u-badge` 의 상태는 **색으로만** 전달돼 왔다. 색각 이상(남성 약 8%)이나 흑백
|
|
7
|
+
* 인쇄에서는 *"성공"* 과 *"실패"* 가 같은 회색 알약이 된다. 대비 계약(1.20.0)은
|
|
8
|
+
* *읽히는가* 를 지키지 *구별되는가* 를 지키지 않는다 — 역할 색 충돌 검사(ΔE)가
|
|
9
|
+
* **색 공간에서** 세운 그 구분을, 이 표는 **모양 공간에서** 세운다.
|
|
10
|
+
*
|
|
11
|
+
* ## 매핑하지 않는 것
|
|
12
|
+
*
|
|
13
|
+
* - **장식 축**(`blue`·`purple` …)은 *색 자체*를 뜻하지 의미를 뜻하지 않는다.
|
|
14
|
+
* - **`neutral`·`primary`** 는 상태가 아니다(`primary` 는 브랜드 강조이지 «좋음»이 아니다).
|
|
15
|
+
*
|
|
16
|
+
* ⇒ 그 자리에 `icon` 을 줘도 **아무것도 그리지 않는다.** 없는 의미를 아이콘으로 지어내면
|
|
17
|
+
* 그 아이콘이 잘못된 정보를 나른다.
|
|
18
|
+
*
|
|
19
|
+
* ⚠**`u-alert` 의 `status` 축(`error`·`notice` …)은 다른 어휘다** — 이름이 겹치지 않으므로
|
|
20
|
+
* 한 표로 합치지 않았다. 합치려면 두 축의 이름을 먼저 통일해야 하고, 그것은 파괴적 변경이다.
|
|
21
|
+
*/
|
|
22
|
+
export declare const STATUS_ICON: {
|
|
23
|
+
readonly info: "info-circle-fill";
|
|
24
|
+
readonly success: "circle-check-fill";
|
|
25
|
+
readonly warning: "alert-triangle-fill";
|
|
26
|
+
readonly danger: "alert-circle-fill";
|
|
27
|
+
};
|
|
28
|
+
/** 의미를 갖는 역할 값. */
|
|
29
|
+
export type StatusRole = keyof typeof STATUS_ICON;
|
|
30
|
+
/** 역할 값이면 아이콘 이름을, 아니면 `undefined` 를 돌려준다. */
|
|
31
|
+
export declare function statusIcon(color?: string): string | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/utilities/statusIcon.ts
|
|
2
|
+
/**
|
|
3
|
+
* 역할 축(의미)의 상태 → 내장 아이콘 이름.
|
|
4
|
+
*
|
|
5
|
+
* ## 왜 있나
|
|
6
|
+
*
|
|
7
|
+
* `u-tag`·`u-badge` 의 상태는 **색으로만** 전달돼 왔다. 색각 이상(남성 약 8%)이나 흑백
|
|
8
|
+
* 인쇄에서는 *"성공"* 과 *"실패"* 가 같은 회색 알약이 된다. 대비 계약(1.20.0)은
|
|
9
|
+
* *읽히는가* 를 지키지 *구별되는가* 를 지키지 않는다 — 역할 색 충돌 검사(ΔE)가
|
|
10
|
+
* **색 공간에서** 세운 그 구분을, 이 표는 **모양 공간에서** 세운다.
|
|
11
|
+
*
|
|
12
|
+
* ## 매핑하지 않는 것
|
|
13
|
+
*
|
|
14
|
+
* - **장식 축**(`blue`·`purple` …)은 *색 자체*를 뜻하지 의미를 뜻하지 않는다.
|
|
15
|
+
* - **`neutral`·`primary`** 는 상태가 아니다(`primary` 는 브랜드 강조이지 «좋음»이 아니다).
|
|
16
|
+
*
|
|
17
|
+
* ⇒ 그 자리에 `icon` 을 줘도 **아무것도 그리지 않는다.** 없는 의미를 아이콘으로 지어내면
|
|
18
|
+
* 그 아이콘이 잘못된 정보를 나른다.
|
|
19
|
+
*
|
|
20
|
+
* ⚠**`u-alert` 의 `status` 축(`error`·`notice` …)은 다른 어휘다** — 이름이 겹치지 않으므로
|
|
21
|
+
* 한 표로 합치지 않았다. 합치려면 두 축의 이름을 먼저 통일해야 하고, 그것은 파괴적 변경이다.
|
|
22
|
+
*/
|
|
23
|
+
var STATUS_ICON = {
|
|
24
|
+
info: "info-circle-fill",
|
|
25
|
+
success: "circle-check-fill",
|
|
26
|
+
warning: "alert-triangle-fill",
|
|
27
|
+
danger: "alert-circle-fill"
|
|
28
|
+
};
|
|
29
|
+
/** 역할 값이면 아이콘 이름을, 아니면 `undefined` 를 돌려준다. */
|
|
30
|
+
function statusIcon(color) {
|
|
31
|
+
return color && color in STATUS_ICON ? STATUS_ICON[color] : void 0;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { STATUS_ICON, statusIcon };
|
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.
|
|
4
|
+
"version": "1.23.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iyulab",
|
|
7
7
|
"components",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"docs:cssprops": "node scripts/cssprops-doc.mjs --write",
|
|
49
49
|
"docs:react-events": "node scripts/react-events-doc.mjs --write",
|
|
50
50
|
"docs:tokens": "node scripts/design-tokens-doc.mjs --write",
|
|
51
|
-
"build:plugins": "tsc -p plugins/tsconfig.build.json"
|
|
51
|
+
"build:plugins": "tsc -p plugins/tsconfig.build.json",
|
|
52
|
+
"probe:seed-ramp": "node scripts/seed-ramp.mjs"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
55
|
"@floating-ui/dom": "^1.8.0",
|
|
@@ -98,7 +98,8 @@ import { UButton, UInput } from '@iyulab/components/react';
|
|
|
98
98
|
- [`u-card`](./references/components/card.md) — Content card with media, header, and footer slots
|
|
99
99
|
- [`u-carousel`](./references/components/carousel.md) — Slide carousel with autoplay, navigation, and pagination
|
|
100
100
|
- [`u-split-panel`](./references/components/split-panel.md) — Resizable two-panel layout
|
|
101
|
-
- [`u-panel`](./references/components/panel.md) — General-purpose content panel
|
|
101
|
+
- [`u-panel`](./references/components/panel.md) — General-purpose content panel
|
|
102
|
+
- [`u-expander`](./references/components/expander.md) — Disclosure section; header toggles the body open/closed
|
|
102
103
|
- [`u-divider`](./references/components/divider.md) — Horizontal or vertical separator line
|
|
103
104
|
|
|
104
105
|
### Data Display
|
|
@@ -34,5 +34,6 @@ Status badge for numbers, labels, or dot indicators. Use `anchor` to position it
|
|
|
34
34
|
| Property | Type | Default | Reflect | Description |
|
|
35
35
|
|----------|------|---------|---------|-------------|
|
|
36
36
|
| `variant` | `'pill'\|'dot'\|'square'` | `'pill'` | ✓ | Shape variant; `dot` renders no content |
|
|
37
|
-
| `color` | `'neutral'\|'blue'\|'green'\|'yellow'\|'red'\|'orange'\|'teal'\|'cyan'\|'purple'\|'pink'` | `'
|
|
37
|
+
| `color` | `'neutral'\|'primary'\|'info'\|'success'\|'warning'\|'danger'\|'blue'\|'green'\|'yellow'\|'red'\|'orange'\|'teal'\|'cyan'\|'purple'\|'pink'` | `'blue'` | ✓ | Badge color (role axis = semantics, decorative axis = the color itself) |
|
|
38
38
|
| `anchor` | `'top-right'\|'top-left'\|'bottom-right'\|'bottom-left'` | — | ✓ | Absolute anchor position relative to parent |
|
|
39
|
+
| `icon` | `boolean` | `false` | ✓ | Adds a status icon so the meaning survives without color (`info`/`success`/`warning`/`danger`; not drawn for `variant="dot"`) |
|
|
@@ -45,12 +45,14 @@ Versatile button with multiple visual variants. Renders as an `<a>` element when
|
|
|
45
45
|
| Property | Type | Default | Reflect | Description |
|
|
46
46
|
|----------|------|---------|---------|-------------|
|
|
47
47
|
| `variant` | `'solid'\|'surface'\|'filled'\|'outlined'\|'ghost'\|'link'` | `'solid'` | ✓ | Visual style |
|
|
48
|
-
| `color` | `'neutral'\|'blue'\|'green'\|'red'\|'orange'\|'teal'\|'cyan'\|'purple'\|'pink'` | `'neutral'` | ✓ | Semantic color, independent of `variant`. `ghost` is unaffected (see notes below). |
|
|
48
|
+
| `color` | `'neutral'\|'primary'\|'info'\|'success'\|'warning'\|'danger'\|'blue'\|'green'\|'red'\|'orange'\|'teal'\|'cyan'\|'purple'\|'pink'` | `'neutral'` | ✓ | Semantic color, independent of `variant`. `ghost` is unaffected (see notes below). |
|
|
49
49
|
| `size` | `'sm'\|'md'\|'lg'` | `'md'` | ✓ | Button size (12px/14px/16px font-size; padding, spinner, and icon slots scale proportionally). |
|
|
50
50
|
| `rounded` | `boolean` | `false` | ✓ | Pill-shaped border radius |
|
|
51
51
|
| `disabled` | `boolean` | `false` | ✓ | Disable the button |
|
|
52
52
|
| `loading` | `boolean` | `false` | ✓ | Show loading spinner; disables interaction |
|
|
53
53
|
| `type` | `'button'\|'submit'\|'reset'` | `'button'` | — | Button `type` attribute |
|
|
54
|
+
| `name` | `string` | — | ✓ | Form field name — submitted with the form |
|
|
55
|
+
| `value` | `string` | — | ✓ | Form field value — submitted with the form |
|
|
54
56
|
| `href` | `string` | — | — | Link URL (renders as `<a>`) |
|
|
55
57
|
| `target` | `string` | — | — | Link `target` |
|
|
56
58
|
| `rel` | `string` | — | — | Link `rel` |
|
|
@@ -61,3 +61,33 @@ Side panel that slides in from any screen edge. Extends `UOverlayElement` (focus
|
|
|
61
61
|
| `header` | Header area |
|
|
62
62
|
| `body` | Body area |
|
|
63
63
|
| `close-btn` | Close button |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Edit-panel pattern
|
|
68
|
+
|
|
69
|
+
A side panel for editing a record needs no extra component — `u-drawer` already provides the
|
|
70
|
+
whole contract. Measured in a real browser
|
|
71
|
+
(`tests/browser/drawer-edit-panel-pattern.browser.test.ts`):
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<u-drawer id="edit" placement="right" closable>
|
|
75
|
+
<span slot="header">Edit order</span>
|
|
76
|
+
|
|
77
|
+
<u-input label="Quantity" autofocus></u-input>
|
|
78
|
+
<u-textarea label="Note"></u-textarea>
|
|
79
|
+
|
|
80
|
+
<div slot="footer">
|
|
81
|
+
<u-button variant="ghost" @click=${() => edit.hide()}>Cancel</u-button>
|
|
82
|
+
<u-button color="primary" @click=${save}>Save</u-button>
|
|
83
|
+
</div>
|
|
84
|
+
</u-drawer>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
| Requirement | How it is met |
|
|
88
|
+
|---|---|
|
|
89
|
+
| Focus the first input on open, restore the trigger on close | `[autofocus]` → first input control → first tabbable; focus is returned by the trap |
|
|
90
|
+
| Body scrolls, footer stays visible | `part="body"` is `flex: 1; overflow: auto`; the `footer` slot is `flex-shrink: 0` |
|
|
91
|
+
| Focus cannot leave the panel | `mode="modal"` (default) activates the focus trap |
|
|
92
|
+
| `Esc` closes, background scroll is locked | `closeOn` defaults to `['escape','backdrop','button']` |
|
|
93
|
+
| Nothing pops open by itself | Selects/comboboxes open only on user interaction |
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# u-expander
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
import '@iyulab/components/dist/components/expander/UExpander.js';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
**Tag:** `u-expander`
|
|
8
|
+
|
|
9
|
+
Disclosure section — the header toggles the body open and closed. The header is a native
|
|
10
|
+
`<button>`, so keyboard operation (Enter / Space) and focus order come for free, and the
|
|
11
|
+
collapsed body is removed from both the accessibility tree and the tab order.
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<u-expander label="Shipping details" open>
|
|
15
|
+
<p>Body content.</p>
|
|
16
|
+
</u-expander>
|
|
17
|
+
|
|
18
|
+
<!-- Custom header + trailing slot -->
|
|
19
|
+
<u-expander>
|
|
20
|
+
<span slot="header">Order 1042</span>
|
|
21
|
+
<u-tag slot="suffix" color="success">Paid</u-tag>
|
|
22
|
+
<p>Body content.</p>
|
|
23
|
+
</u-expander>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The open/close transition runs on the motion tokens (`--u-duration-normal` /
|
|
27
|
+
`--u-ease-standard`) and is suppressed under `prefers-reduced-motion: reduce` — the
|
|
28
|
+
transition is decoration here, since the collapsed/expanded result reads the same when static.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Slots
|
|
33
|
+
|
|
34
|
+
| Name | Description |
|
|
35
|
+
|------|-------------|
|
|
36
|
+
| `header` | Replaces the header text (wins over `label`) |
|
|
37
|
+
| *(default)* | Body content, shown while open |
|
|
38
|
+
| `suffix` | Content placed at the end of the header row |
|
|
39
|
+
|
|
40
|
+
## Properties
|
|
41
|
+
|
|
42
|
+
| Property | Type | Default | Reflect | Description |
|
|
43
|
+
|----------|------|---------|---------|-------------|
|
|
44
|
+
| `open` | `boolean` | `false` | ✓ | Expanded state |
|
|
45
|
+
| `disabled` | `boolean` | `false` | ✓ | Disable the header button |
|
|
46
|
+
| `label` | `string` | `''` | | Header text |
|
|
47
|
+
|
|
48
|
+
## Events
|
|
49
|
+
|
|
50
|
+
| Event | Cancelable | Description |
|
|
51
|
+
|-------|------------|-------------|
|
|
52
|
+
| `expand` | ✓ | Before the body expands |
|
|
53
|
+
| `collapse` | ✓ | Before the body collapses |
|
|
54
|
+
|
|
55
|
+
## Methods
|
|
56
|
+
|
|
57
|
+
| Method | Description |
|
|
58
|
+
|--------|-------------|
|
|
59
|
+
| `expand()` | Expand; returns `false` if cancelled or already open |
|
|
60
|
+
| `collapse()` | Collapse; returns `false` if cancelled or already closed |
|
|
61
|
+
| `toggle()` | Switch to the opposite state |
|
|
62
|
+
|
|
63
|
+
## CSS Parts
|
|
64
|
+
|
|
65
|
+
| Part | Description |
|
|
66
|
+
|------|-------------|
|
|
67
|
+
| `header` | Header button |
|
|
68
|
+
| `icon` | Disclosure chevron |
|
|
69
|
+
| `label` | Header text area |
|
|
70
|
+
| `content` | Body wrapper |
|
|
@@ -50,6 +50,12 @@ Text input field with prefix/suffix slots and label. Add `u-option` children for
|
|
|
50
50
|
| `autofocus` | `boolean` | `false` | — | Auto-focus on render |
|
|
51
51
|
| `autocomplete` | `AutoFill` | — | — | Browser autocomplete |
|
|
52
52
|
| `spellcheck` | `boolean` | `false` | — | Spellcheck |
|
|
53
|
+
| `dirname` | `string` | — | — | Native `dirname` — submits the text direction with the form |
|
|
54
|
+
| `inputmode` | `'none'\|'text'\|'decimal'\|'numeric'\|'tel'\|'search'\|'email'\|'url'` | — | — | Virtual keyboard hint |
|
|
55
|
+
| `enterkeyhint` | `'enter'\|'done'\|'go'\|'next'\|'previous'\|'search'\|'send'` | — | — | Enter-key label hint |
|
|
56
|
+
| `autocorrect` | `boolean` | `false` | — | Native autocorrect (Safari/iOS) |
|
|
57
|
+
| `autocapitalize` | `'off'\|'none'\|'on'\|'sentences'\|'words'\|'characters'` | `'off'` | — | Auto-capitalization behavior |
|
|
58
|
+
| `size` | `number` | — | — | Native `size` — visible width in characters |
|
|
53
59
|
| `disabled` | `boolean` | `false` | ✓ | Disabled |
|
|
54
60
|
| `readonly` | `boolean` | `false` | ✓ | Read-only |
|
|
55
61
|
| `required` | `boolean` | `false` | ✓ | Required |
|
|
@@ -6,11 +6,14 @@ import '@iyulab/components/dist/components/panel/UPanel.js';
|
|
|
6
6
|
|
|
7
7
|
**Tag:** `u-panel`
|
|
8
8
|
|
|
9
|
-
General-purpose content panel. Matches a tab or tree node when using `value`.
|
|
9
|
+
General-purpose content panel. Matches a tab or tree node when using `value`.
|
|
10
|
+
|
|
11
|
+
> Looking for a collapsible section? Use [`u-expander`](expander.md). `u-panel` previously
|
|
12
|
+
> declared a `collapsible` property that was never implemented — it has been removed.
|
|
10
13
|
|
|
11
14
|
```html
|
|
12
|
-
<!-- Standalone
|
|
13
|
-
<u-panel
|
|
15
|
+
<!-- Standalone panel -->
|
|
16
|
+
<u-panel>Content inside panel</u-panel>
|
|
14
17
|
|
|
15
18
|
<!-- Inside u-tab-panel (matched by value) -->
|
|
16
19
|
<u-tab-panel>
|
|
@@ -35,4 +38,3 @@ General-purpose content panel. Matches a tab or tree node when using `value`. Su
|
|
|
35
38
|
|----------|------|---------|---------|-------------|
|
|
36
39
|
| `value` | `string` | `''` | ✓ | Identifier used for tab/tree matching |
|
|
37
40
|
| `disabled` | `boolean` | `false` | ✓ | Disable the panel |
|
|
38
|
-
| `collapsible` | `boolean` | `false` | ✓ | Allow the panel to collapse |
|
|
@@ -17,6 +17,9 @@ Placeholder shape displayed while content is loading.
|
|
|
17
17
|
|
|
18
18
|
<!-- Card placeholder -->
|
|
19
19
|
<u-skeleton shape="rounded" width="100%" height="120px" effect="shimmer"></u-skeleton>
|
|
20
|
+
|
|
21
|
+
<!-- Paragraph / list-row placeholder: 3 stacked bars, last one shortened -->
|
|
22
|
+
<u-skeleton lines="3" height="1em"></u-skeleton>
|
|
20
23
|
```
|
|
21
24
|
|
|
22
25
|
---
|
|
@@ -35,6 +38,7 @@ Placeholder shape displayed while content is loading.
|
|
|
35
38
|
| `effect` | `'none'\|'pulse'\|'shimmer'` | `'shimmer'` | ✓ | Animation style |
|
|
36
39
|
| `width` | `string` | — | — | CSS width value |
|
|
37
40
|
| `height` | `string` | — | — | CSS height value |
|
|
41
|
+
| `lines` | `number` | — | ✓ | Draw N stacked bars instead of one (takes effect at 2+); the last bar is shortened |
|
|
38
42
|
|
|
39
43
|
## CSS Custom Properties
|
|
40
44
|
|
|
@@ -44,3 +48,11 @@ Placeholder shape displayed while content is loading.
|
|
|
44
48
|
| `--skeleton-height` | Override height |
|
|
45
49
|
| `--skeleton-color` | Base placeholder color |
|
|
46
50
|
| `--skeleton-shimmer-color` | Shimmer highlight color |
|
|
51
|
+
| `--skeleton-line-gap` | Gap between bars when `lines` is set (default `0.5em`) |
|
|
52
|
+
| `--skeleton-last-line-width` | Width of the last bar when `lines` is set (default `60%`) |
|
|
53
|
+
|
|
54
|
+
## CSS Parts
|
|
55
|
+
|
|
56
|
+
| Part | Description |
|
|
57
|
+
|------|-------------|
|
|
58
|
+
| `line` | One bar of a multi-line placeholder (only rendered when `lines` is set) |
|
|
@@ -21,8 +21,8 @@ Tab-based content switcher. Pair each `u-tab` with a `u-panel` of the same `valu
|
|
|
21
21
|
<u-panel value="settings">Settings content</u-panel>
|
|
22
22
|
</u-tab-panel>
|
|
23
23
|
|
|
24
|
-
<!-- Card variant,
|
|
25
|
-
<u-tab-panel variant="card" placement="left"
|
|
24
|
+
<!-- Card variant, left placement -->
|
|
25
|
+
<u-tab-panel variant="card" placement="left">
|
|
26
26
|
<u-tab value="a" removable>Tab A</u-tab>
|
|
27
27
|
<u-panel value="a">Panel A</u-panel>
|
|
28
28
|
</u-tab-panel>
|
|
@@ -46,8 +46,7 @@ Tab-based content switcher. Pair each `u-tab` with a `u-panel` of the same `valu
|
|
|
46
46
|
| `value` | `string` | `''` | ✓ | Currently active tab value |
|
|
47
47
|
| `variant` | `'line'\|'card'\|'pill'\|'plain'` | `'line'` | ✓ | Tab bar style |
|
|
48
48
|
| `placement` | `'top'\|'bottom'\|'left'\|'right'` | `'top'` | ✓ | Tab bar position |
|
|
49
|
-
| `
|
|
50
|
-
| `draggable` | `boolean` | `false` | ✓ | Allow reordering tabs by drag |
|
|
49
|
+
| `draggable` | `boolean` | `false` | ✓ | Native drag attribute — **no built-in reordering** |
|
|
51
50
|
| `disabled` | `boolean` | `false` | ✓ | Disable all tabs |
|
|
52
51
|
|
|
53
52
|
### Events
|
|
@@ -84,7 +83,7 @@ Tab-based content switcher. Pair each `u-tab` with a `u-panel` of the same `valu
|
|
|
84
83
|
| `value` | `string` | `''` | ✓ | Matches a `u-panel` with the same `value` |
|
|
85
84
|
| `disabled` | `boolean` | `false` | ✓ | Disable the tab |
|
|
86
85
|
| `removable` | `boolean` | `false` | ✓ | Show close/remove button |
|
|
87
|
-
| `draggable` | `boolean` | `false` | ✓ |
|
|
86
|
+
| `draggable` | `boolean` | `false` | ✓ | Native drag attribute — **no built-in reordering** |
|
|
88
87
|
|
|
89
88
|
### Events
|
|
90
89
|
|
|
@@ -37,14 +37,31 @@ For interactive chips (selectable/removable), use [`u-chip`](./chip.md) instead.
|
|
|
37
37
|
| Property | Type | Default | Reflect | Description |
|
|
38
38
|
|----------|------|---------|---------|-------------|
|
|
39
39
|
| `variant` | `'solid'\|'surface'\|'filled'\|'outlined'` | `'filled'` | ✓ | Visual style |
|
|
40
|
-
| `color` | `'neutral'\|'blue'\|'green'\|'yellow'\|'red'\|'orange'\|'teal'\|'cyan'\|'purple'\|'pink'` | `'neutral'` | ✓ | Color |
|
|
40
|
+
| `color` | `'neutral'\|'primary'\|'info'\|'success'\|'warning'\|'danger'\|'blue'\|'green'\|'yellow'\|'red'\|'orange'\|'teal'\|'cyan'\|'purple'\|'pink'` | `'neutral'` | ✓ | Color |
|
|
41
41
|
| `rounded` | `boolean` | `false` | ✓ | Pill shape |
|
|
42
|
+
| `icon` | `boolean` | `false` | ✓ | Adds a status icon so the meaning survives without color (`info`/`success`/`warning`/`danger` only) |
|
|
42
43
|
|
|
43
44
|
## CSS Parts
|
|
44
45
|
|
|
45
46
|
| Part | Description |
|
|
46
47
|
|------|-------------|
|
|
47
48
|
| `content` | Inner content wrapper |
|
|
49
|
+
| `icon` | Status icon (rendered only with `icon` + a semantic `color`) |
|
|
50
|
+
|
|
51
|
+
## Color axes
|
|
52
|
+
|
|
53
|
+
`color` carries **two** axes. The **role** axis (`primary`·`info`·`success`·`warning`·`danger`)
|
|
54
|
+
means *semantics* — it follows re-branding and inherits the contrast contract. The **decorative**
|
|
55
|
+
axis (`blue`·`purple` …) means *the color itself* and is deliberately immune to re-branding.
|
|
56
|
+
|
|
57
|
+
`icon` only applies to the four **status** roles — `neutral` and `primary` are not states, and the
|
|
58
|
+
decorative axis carries no meaning, so no icon is drawn there.
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<!-- distinguishable in grayscale / for color-vision deficiency -->
|
|
62
|
+
<u-tag color="danger" icon>Failed</u-tag>
|
|
63
|
+
<u-tag color="success" icon>Done</u-tag>
|
|
64
|
+
```
|
|
48
65
|
|
|
49
66
|
## CSS Custom Properties
|
|
50
67
|
|
|
@@ -34,6 +34,11 @@ Multi-line text input with auto-resize and optional character counter. Form-asso
|
|
|
34
34
|
| `autofocus` | `boolean` | `false` | — | Auto-focus on render |
|
|
35
35
|
| `autocomplete` | `AutoFill` | — | — | Browser autocomplete |
|
|
36
36
|
| `spellcheck` | `boolean` | `false` | — | Spellcheck |
|
|
37
|
+
| `dirname` | `string` | — | — | Native `dirname` — submits the text direction with the form |
|
|
38
|
+
| `inputmode` | `'none'\|'text'\|'decimal'\|'numeric'\|'tel'\|'search'\|'email'\|'url'` | — | — | Virtual keyboard hint |
|
|
39
|
+
| `enterkeyhint` | `'enter'\|'done'\|'go'\|'next'\|'previous'\|'search'\|'send'` | — | — | Enter-key label hint |
|
|
40
|
+
| `autocorrect` | `boolean` | `false` | — | Native autocorrect (Safari/iOS) |
|
|
41
|
+
| `autocapitalize` | `'off'\|'none'\|'on'\|'sentences'\|'words'\|'characters'` | `'off'` | — | Auto-capitalization behavior |
|
|
37
42
|
| `disabled` | `boolean` | `false` | ✓ | Disable |
|
|
38
43
|
| `readonly` | `boolean` | `false` | ✓ | Read-only |
|
|
39
44
|
| `required` | `boolean` | `false` | ✓ | Required |
|
|
@@ -43,8 +43,7 @@ Hierarchical tree with support for selection, checkboxes, cascade check, and dra
|
|
|
43
43
|
| `selectLeaf` | `boolean` | `false` | ✓ | Only leaf nodes are selectable |
|
|
44
44
|
| `checkable` | `boolean` | `false` | ✓ | Show checkboxes |
|
|
45
45
|
| `checkCascade` | `boolean` | `false` | ✓ | Parent/child checkbox cascade |
|
|
46
|
-
| `draggable` | `boolean` | `false` | ✓ |
|
|
47
|
-
| `droppable` | `boolean` | `false` | ✓ | Enable drop |
|
|
46
|
+
| `draggable` | `boolean` | `false` | ✓ | Native drag attribute — **no built-in drag & drop** |
|
|
48
47
|
| `trigger` | `'item'\|'icon'` | `'item'` | — | Click target to expand/collapse |
|
|
49
48
|
|
|
50
49
|
### Events
|
|
@@ -98,8 +97,7 @@ Hierarchical tree with support for selection, checkboxes, cascade check, and dra
|
|
|
98
97
|
| `selectable` | `boolean` | `false` | ✓ | This item can be selected |
|
|
99
98
|
| `checkable` | `boolean` | `false` | ✓ | Show checkbox |
|
|
100
99
|
| `loading` | `boolean` | `false` | ✓ | Loading state (async children) |
|
|
101
|
-
| `draggable` | `boolean` | `false` | ✓ |
|
|
102
|
-
| `droppable` | `boolean` | `false` | ✓ | Accepts drops |
|
|
100
|
+
| `draggable` | `boolean` | `false` | ✓ | Native drag attribute — **no built-in drag & drop** |
|
|
103
101
|
| `trigger` | `'item'\|'icon'` | `'item'` | — | Expand trigger target |
|
|
104
102
|
|
|
105
103
|
### Events
|
|
@@ -64,13 +64,33 @@ const current = Theme.get(); // 'system' | 'light' | 'dark' | undefined
|
|
|
64
64
|
|
|
65
65
|
### Brand color customization
|
|
66
66
|
|
|
67
|
+
**Recommended — derive the whole ramp from one seed:**
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { Theme } from '@iyulab/components';
|
|
71
|
+
|
|
72
|
+
Theme.accent('#7c3aed'); // computes --u-primary-color-{weakest,weaker,weak,…,strong} + txt
|
|
73
|
+
Theme.accent(null); // back to the sheet defaults
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The computed ramp satisfies the contrast contract this library tests against — text on the
|
|
77
|
+
accent surface ≥ 4.5:1, accent text on the page background ≥ 4.5:1, `-strong` distinguishable
|
|
78
|
+
from `-color`, and `-weak` usable as a non-text graphic (≥ 3:1). It is **recalculated when the
|
|
79
|
+
theme changes**, because those targets are relative to the page background.
|
|
80
|
+
|
|
81
|
+
**Manual override** — you must set the steps you use, not just one:
|
|
82
|
+
|
|
67
83
|
```css
|
|
68
84
|
:root {
|
|
69
|
-
--u-primary-color: #
|
|
85
|
+
--u-primary-color-weak: #a78bfa; /* graphics on the page background */
|
|
86
|
+
--u-primary-color: #7c3aed; /* accent surface */
|
|
87
|
+
--u-primary-color-strong: #5b21b6; /* text/icons on the page background */
|
|
88
|
+
--u-primary-txt-color: #ffffff; /* text on the accent surface */
|
|
70
89
|
}
|
|
71
90
|
```
|
|
72
91
|
|
|
73
|
-
|
|
92
|
+
⚠ Setting `--u-primary-color` alone is **not enough**: hover/focus/link colors resolve from
|
|
93
|
+
`--u-primary-color-strong`, so they stay on the default ramp and your brand looks half-applied.
|
|
74
94
|
|
|
75
95
|
---
|
|
76
96
|
|
|
@@ -22,17 +22,34 @@ await Theme.init({
|
|
|
22
22
|
});
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Brand accent
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
**Recommended — derive the whole ramp from one seed:**
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
Theme.accent('#7c3aed'); // computes --u-primary-color-{weakest…strong} + --u-primary-txt-color
|
|
31
|
+
Theme.accent(null); // back to the sheet defaults
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The computed ramp satisfies the contrast contract this library tests against — text on the accent
|
|
35
|
+
surface ≥ 4.5:1, accent text on the page background ≥ 4.5:1, `-strong` distinguishable from
|
|
36
|
+
`-color`, and `-weak` usable as a non-text graphic (≥ 3:1). It is **recalculated when the theme
|
|
37
|
+
changes**, because those targets are relative to the page background.
|
|
38
|
+
|
|
39
|
+
**Manual override** — set the steps you use, not just one:
|
|
28
40
|
|
|
29
41
|
```css
|
|
30
42
|
:root {
|
|
31
|
-
--u-primary-color: #
|
|
43
|
+
--u-primary-color-weak: #a78bfa; /* graphics on the page background */
|
|
44
|
+
--u-primary-color: #7c3aed; /* accent surface */
|
|
45
|
+
--u-primary-color-strong: #5b21b6; /* text/icons on the page background */
|
|
46
|
+
--u-primary-txt-color: #ffffff; /* text on the accent surface */
|
|
32
47
|
}
|
|
33
48
|
```
|
|
34
49
|
|
|
35
|
-
|
|
50
|
+
> ⚠ Overriding `--u-primary-color` **alone is not enough**. Hover, focus and link colors resolve
|
|
51
|
+
> from `--u-primary-color-strong`, so they stay on the default ramp and the brand looks
|
|
52
|
+
> half-applied. (The sheet derives no step from `--u-primary-color` — measured: 0 references.)
|
|
36
53
|
|
|
37
54
|
## API
|
|
38
55
|
|
|
@@ -41,6 +58,8 @@ With this single token override, components such as buttons, checkbox/radio/swit
|
|
|
41
58
|
| `Theme.init(options?)` | `Promise<void>` | Initialize and apply theme |
|
|
42
59
|
| `Theme.get()` | `ThemeType \| undefined` | Get current theme |
|
|
43
60
|
| `Theme.set(theme)` | `void` | Set theme (`'light'`, `'dark'`, `'system'`) |
|
|
61
|
+
| `Theme.resolved()` | `'light' | 'dark'` | The theme actually applied — use this, not `get()`, for brightness branches |
|
|
62
|
+
| `Theme.accent(seed)` | `void` | Derive the `--u-primary-*` ramp from a brand color; `null` clears it |
|
|
44
63
|
| `Theme.isInitialized` | `boolean` | Whether `init()` has been called |
|
|
45
64
|
|
|
46
65
|
## Types
|