@hjmds/design-contracts 1.7.0 → 1.9.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/THIRD_PARTY_NOTICES.md +58 -0
- package/dist/affix.d.ts +8 -0
- package/dist/affix.d.ts.map +1 -0
- package/dist/affix.js +7 -0
- package/dist/affix.js.map +1 -0
- package/dist/agreement.d.ts +3 -3
- package/dist/agreement.js +1 -1
- package/dist/agreement.js.map +1 -1
- package/dist/asset.d.ts +1 -1
- package/dist/asset.js +1 -1
- package/dist/asset.js.map +1 -1
- package/dist/base-recipes.d.ts +12 -12
- package/dist/base-recipes.d.ts.map +1 -1
- package/dist/base-recipes.js +15 -14
- package/dist/base-recipes.js.map +1 -1
- package/dist/behaviors.d.ts +1 -1
- package/dist/catalog.d.ts +445 -651
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +149 -101
- package/dist/catalog.js.map +1 -1
- package/dist/color-picker.d.ts +11 -0
- package/dist/color-picker.d.ts.map +1 -0
- package/dist/color-picker.js +20 -0
- package/dist/color-picker.js.map +1 -0
- package/dist/command-palette.d.ts +3 -3
- package/dist/component-contracts.d.ts +4 -4
- package/dist/component-contracts.d.ts.map +1 -1
- package/dist/component-contracts.js +5 -4
- package/dist/component-contracts.js.map +1 -1
- package/dist/component-definitions.d.ts +1 -9
- package/dist/component-definitions.d.ts.map +1 -1
- package/dist/component-definitions.js +1 -9
- package/dist/component-definitions.js.map +1 -1
- package/dist/component-recipes.d.ts +76 -83
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +29 -49
- package/dist/component-recipes.js.map +1 -1
- package/dist/component-references.d.ts +10 -25
- package/dist/component-references.d.ts.map +1 -1
- package/dist/component-references.js +5 -8
- package/dist/component-references.js.map +1 -1
- package/dist/data-table.d.ts +3 -3
- package/dist/data-table.js +1 -1
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.d.ts +2 -2
- package/dist/icon-button-recipe.d.ts +2 -2
- package/dist/icon-button-recipe.js +1 -1
- package/dist/icon-button-recipe.js.map +1 -1
- package/dist/image.d.ts +2 -2
- package/dist/image.js +2 -2
- package/dist/image.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interaction-adapters.d.ts +53 -0
- package/dist/interaction-adapters.d.ts.map +1 -0
- package/dist/interaction-adapters.js +45 -0
- package/dist/interaction-adapters.js.map +1 -0
- package/dist/internal/thinking-orb/braid.d.ts +3 -0
- package/dist/internal/thinking-orb/braid.d.ts.map +1 -0
- package/dist/internal/thinking-orb/braid.js +47 -0
- package/dist/internal/thinking-orb/braid.js.map +1 -0
- package/dist/internal/thinking-orb/core.d.ts +60 -0
- package/dist/internal/thinking-orb/core.d.ts.map +1 -0
- package/dist/internal/thinking-orb/core.js +86 -0
- package/dist/internal/thinking-orb/core.js.map +1 -0
- package/dist/internal/thinking-orb/lattice.d.ts +5 -0
- package/dist/internal/thinking-orb/lattice.d.ts.map +1 -0
- package/dist/internal/thinking-orb/lattice.js +187 -0
- package/dist/internal/thinking-orb/lattice.js.map +1 -0
- package/dist/internal/thinking-orb/morph.d.ts +3 -0
- package/dist/internal/thinking-orb/morph.d.ts.map +1 -0
- package/dist/internal/thinking-orb/morph.js +127 -0
- package/dist/internal/thinking-orb/morph.js.map +1 -0
- package/dist/internal/thinking-orb/orbits.d.ts +3 -0
- package/dist/internal/thinking-orb/orbits.d.ts.map +1 -0
- package/dist/internal/thinking-orb/orbits.js +68 -0
- package/dist/internal/thinking-orb/orbits.js.map +1 -0
- package/dist/internal/thinking-orb/presets.d.ts +21 -0
- package/dist/internal/thinking-orb/presets.d.ts.map +1 -0
- package/dist/internal/thinking-orb/presets.js +77 -0
- package/dist/internal/thinking-orb/presets.js.map +1 -0
- package/dist/internal/thinking-orb/profiles.d.ts +8 -0
- package/dist/internal/thinking-orb/profiles.d.ts.map +1 -0
- package/dist/internal/thinking-orb/profiles.js +158 -0
- package/dist/internal/thinking-orb/profiles.js.map +1 -0
- package/dist/internal/thinking-orb/registry.d.ts +9 -0
- package/dist/internal/thinking-orb/registry.d.ts.map +1 -0
- package/dist/internal/thinking-orb/registry.js +27 -0
- package/dist/internal/thinking-orb/registry.js.map +1 -0
- package/dist/internal/thinking-orb/ribbon.d.ts +3 -0
- package/dist/internal/thinking-orb/ribbon.d.ts.map +1 -0
- package/dist/internal/thinking-orb/ribbon.js +87 -0
- package/dist/internal/thinking-orb/ribbon.js.map +1 -0
- package/dist/internal/thinking-orb/types.d.ts +15 -0
- package/dist/internal/thinking-orb/types.d.ts.map +1 -0
- package/dist/internal/thinking-orb/types.js +4 -0
- package/dist/internal/thinking-orb/types.js.map +1 -0
- package/dist/internal/thinking-orb/web.d.ts +3 -0
- package/dist/internal/thinking-orb/web.d.ts.map +1 -0
- package/dist/internal/thinking-orb/web.js +92 -0
- package/dist/internal/thinking-orb/web.js.map +1 -0
- package/dist/masonry.d.ts +17 -0
- package/dist/masonry.d.ts.map +1 -0
- package/dist/masonry.js +18 -0
- package/dist/masonry.js.map +1 -0
- package/dist/menubar.d.ts +3 -3
- package/dist/menubar.js +1 -1
- package/dist/menubar.js.map +1 -1
- package/dist/number-field.d.ts +2 -2
- package/dist/otp-field.d.ts +1 -1
- package/dist/password-field.d.ts +2 -2
- package/dist/popover.d.ts +7 -8
- package/dist/popover.d.ts.map +1 -1
- package/dist/popover.js +2 -0
- package/dist/popover.js.map +1 -1
- package/dist/qr-code-recipe.d.ts +7 -0
- package/dist/qr-code-recipe.d.ts.map +1 -0
- package/dist/qr-code-recipe.js +2 -0
- package/dist/qr-code-recipe.js.map +1 -0
- package/dist/qr-code.d.ts +16 -0
- package/dist/qr-code.d.ts.map +1 -0
- package/dist/qr-code.js +37 -0
- package/dist/qr-code.js.map +1 -0
- package/dist/semantic-colors.d.ts +1 -1
- package/dist/semantic-colors.js +1 -1
- package/dist/semantic-colors.js.map +1 -1
- package/dist/showcase.d.ts +5 -1
- package/dist/showcase.d.ts.map +1 -1
- package/dist/showcase.js +53 -12
- package/dist/showcase.js.map +1 -1
- package/dist/sidebar.d.ts +3 -3
- package/dist/sidebar.js +1 -1
- package/dist/sidebar.js.map +1 -1
- package/dist/tag.d.ts +4 -4
- package/dist/tag.js +3 -3
- package/dist/tag.js.map +1 -1
- package/dist/tags-input.d.ts +3 -3
- package/dist/tags-input.js +1 -1
- package/dist/tags-input.js.map +1 -1
- package/dist/text-formats.d.ts +2 -2
- package/dist/text-formats.js +2 -2
- package/dist/text-formats.js.map +1 -1
- package/dist/thinking-orb-recipe.d.ts +15 -0
- package/dist/thinking-orb-recipe.d.ts.map +1 -0
- package/dist/thinking-orb-recipe.js +9 -0
- package/dist/thinking-orb-recipe.js.map +1 -0
- package/dist/thinking-orb.d.ts +25 -0
- package/dist/thinking-orb.d.ts.map +1 -0
- package/dist/thinking-orb.js +40 -0
- package/dist/thinking-orb.js.map +1 -0
- package/dist/toast-liquid.d.ts +116 -0
- package/dist/toast-liquid.d.ts.map +1 -0
- package/dist/toast-liquid.js +103 -0
- package/dist/toast-liquid.js.map +1 -0
- package/dist/toast.d.ts +7 -1
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +5 -0
- package/dist/toast.js.map +1 -1
- package/dist/toggle-group.d.ts +2 -2
- package/dist/toggle-group.js +3 -3
- package/dist/toggle-group.js.map +1 -1
- package/dist/transfer-list.d.ts +3 -3
- package/dist/transfer-list.js +1 -1
- package/dist/transfer-list.js.map +1 -1
- package/dist/tree.d.ts +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/virtual-list.d.ts +13 -0
- package/dist/virtual-list.d.ts.map +1 -0
- package/dist/virtual-list.js +13 -0
- package/dist/virtual-list.js.map +1 -0
- package/dist/watermark.d.ts +15 -0
- package/dist/watermark.d.ts.map +1 -0
- package/dist/watermark.js +11 -0
- package/dist/watermark.js.map +1 -0
- package/docs/affix.md +27 -63
- package/docs/anchor.md +5 -4
- package/docs/ant-design-coverage.md +8 -12
- package/docs/architecture.md +6 -2
- package/docs/authoring-brief.md +3 -2
- package/docs/bottom-navigation.md +7 -6
- package/docs/breadcrumb.md +5 -2
- package/docs/calendar.md +6 -3
- package/docs/carousel.md +2 -2
- package/docs/cascader.md +11 -0
- package/docs/catalog-cleanup.md +7 -0
- package/docs/catalog-decision-status.md +8 -3
- package/docs/catalog-freeze.json +9 -7
- package/docs/collapsible.md +12 -0
- package/docs/color-picker.md +30 -72
- package/docs/command-palette.md +5 -3
- package/docs/confirm-popover.md +5 -0
- package/docs/consumer-policy.md +29 -12
- package/docs/context-menu.md +12 -2
- package/docs/data-table.md +11 -6
- package/docs/date-picker.md +6 -4
- package/docs/date-range.md +4 -2
- package/docs/description-list.md +8 -9
- package/docs/dialog.md +39 -0
- package/docs/expansion-roadmap.md +11 -7
- package/docs/file-picker.md +5 -3
- package/docs/form.md +16 -4
- package/docs/generated/component-maturity.md +91 -99
- package/docs/generated/renderer-evidence.json +2043 -1059
- package/docs/generated/renderer-evidence.md +156 -145
- package/docs/generated/showcase-manifest.json +476 -625
- package/docs/layout.md +8 -9
- package/docs/library-gap-analysis.md +1 -1
- package/docs/masonry.md +11 -0
- package/docs/mentions.md +14 -7
- package/docs/optional-adapters.md +126 -0
- package/docs/otp-field.md +2 -1
- package/docs/pagination.md +4 -1
- package/docs/password-field.md +2 -1
- package/docs/popover.md +9 -6
- package/docs/promotion-candidates.md +2 -1
- package/docs/qr-code.md +11 -65
- package/docs/rating.md +5 -0
- package/docs/react-native-completion.md +1 -1
- package/docs/result.md +8 -5
- package/docs/screen-chrome.md +2 -1
- package/docs/sheet.md +8 -2
- package/docs/showcase.md +10 -2
- package/docs/side-panel.md +12 -6
- package/docs/sidebar.md +3 -0
- package/docs/splitter.md +6 -6
- package/docs/stable-core.md +92 -0
- package/docs/stable-promotion.md +47 -43
- package/docs/tags-input.md +5 -2
- package/docs/theme-palette.md +27 -0
- package/docs/thinking-orb.md +124 -0
- package/docs/time-picker.md +5 -0
- package/docs/timeline.md +2 -3
- package/docs/toast.md +6 -0
- package/docs/tooltip.md +2 -1
- package/docs/tour.md +14 -8
- package/docs/transfer-list.md +24 -21
- package/docs/tree-select.md +7 -1
- package/docs/tree.md +12 -7
- package/docs/upload-item.md +7 -3
- package/docs/virtual-list.md +10 -67
- package/docs/watermark.md +26 -58
- package/package.json +54 -3
- package/docs/app-provider.md +0 -50
- package/docs/border-beam.md +0 -66
- package/docs/chart.md +0 -33
- package/docs/utility.md +0 -53
package/docs/catalog-freeze.json
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "카탈로그 확장 동결. 이 목록에 없는 이름을 catalog.ts에 추가하면 test/catalog-freeze.test.ts가 실패한다.",
|
|
2
|
+
"$comment": "카탈로그 확장 동결. 이 목록에 없는 이름을 catalog.ts에 추가하면 test/catalog-freeze.test.ts가 실패한다. 2026-09-29 사용자 요청으로 도입 제외 후보 네 개 삭제; 기존 dataviz/provider API는 유지.",
|
|
3
3
|
"frozenAt": "2026-09-26",
|
|
4
|
-
"reason": "2026-09-26 설계 점검: 111개 중 stable 4개였고, 세 제품 이상이 쓰는 약 30개가 beta에 묶여 있었다. 1.5.0에서 13개를 실제 증거로 승격했지만
|
|
4
|
+
"reason": "2026-09-26 설계 점검: 111개 중 stable 4개였고, 세 제품 이상이 쓰는 약 30개가 beta에 묶여 있었다. 1.5.0에서 13개를 실제 증거로 승격했지만 입력·접근성 동작 증거가 필요한 핵심(Checkbox, Switch, Dialog, Sheet, SegmentedControl, RadioGroup, Link, PasswordField 등)은 아직 beta다. 폭을 넓히기 전에 이것들을 먼저 올린다.",
|
|
5
5
|
"unfreezeWhen": "위 핵심 beta가 stable이 되면 동결을 푼다. 그 전에 추가가 필요하면 이 파일의 exceptions에 이름·날짜·근거(ADR 또는 이슈)를 적는다.",
|
|
6
|
-
"exceptions": [
|
|
6
|
+
"exceptions": [
|
|
7
|
+
{
|
|
8
|
+
"name": "ThinkingOrb",
|
|
9
|
+
"date": "2026-09-29",
|
|
10
|
+
"rationale": "사용자가 Thinking Orbs의 HJM 흡수를 명시적으로 요청함. 기존 Spinner를 교체하지 않는 AI 상태 표시이며 선택형 경로로 격리. docs/thinking-orb.md"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
7
13
|
"names": [
|
|
8
14
|
"Accordion",
|
|
9
15
|
"Affix",
|
|
10
16
|
"Agreement",
|
|
11
17
|
"AlertDialog",
|
|
12
18
|
"Anchor",
|
|
13
|
-
"AppProvider",
|
|
14
19
|
"AspectRatio",
|
|
15
20
|
"Asset",
|
|
16
21
|
"AuthProviderButton",
|
|
17
22
|
"AuthScreenLayout",
|
|
18
23
|
"Avatar",
|
|
19
24
|
"Badge",
|
|
20
|
-
"BorderBeam",
|
|
21
25
|
"BottomCTA",
|
|
22
26
|
"BottomInfo",
|
|
23
27
|
"BottomNavigation",
|
|
@@ -27,7 +31,6 @@
|
|
|
27
31
|
"Card",
|
|
28
32
|
"Carousel",
|
|
29
33
|
"Cascader",
|
|
30
|
-
"Chart",
|
|
31
34
|
"Checkbox",
|
|
32
35
|
"CheckboxGroup",
|
|
33
36
|
"Chip",
|
|
@@ -112,7 +115,6 @@
|
|
|
112
115
|
"Tree",
|
|
113
116
|
"TreeSelect",
|
|
114
117
|
"UploadItem",
|
|
115
|
-
"Utility",
|
|
116
118
|
"VirtualList",
|
|
117
119
|
"VisuallyHidden",
|
|
118
120
|
"Watermark"
|
package/docs/collapsible.md
CHANGED
|
@@ -17,3 +17,15 @@ chrome이 따라오고 그걸 다시 CSS로 지우게 된다. 그래서 별도
|
|
|
17
17
|
가리키지 않으면 사용자는 방금 나타난 것을 찾아야 한다.
|
|
18
18
|
|
|
19
19
|
**Web·Native 공통.** 네이티브도 `button` role에 expanded 상태를 그대로 쓴다.
|
|
20
|
+
|
|
21
|
+
## Renderer evidence
|
|
22
|
+
|
|
23
|
+
- Web keyboard proof tabs to the native button, toggles with Enter, then closes with Space;
|
|
24
|
+
it checks `aria-expanded`, the controlled region relationship, and that closed content leaves
|
|
25
|
+
the tree. The narrow long-copy case places a long unbroken label in the trigger to verify that
|
|
26
|
+
disclosure chrome does not widen its container.
|
|
27
|
+
- Native renderer tests inspect the `button` role and expanded state, invoke the rendered
|
|
28
|
+
`Pressable` action, and verify that opening and closing adds and removes the content. This is
|
|
29
|
+
host-renderer evidence; it does not claim physical keyboard, VoiceOver, or TalkBack verification.
|
|
30
|
+
- Native and Web long-copy fixtures put the long label in the trigger itself. The child body stays
|
|
31
|
+
short so the scenario tests the constrained interactive label rather than arbitrary content.
|
package/docs/color-picker.md
CHANGED
|
@@ -1,72 +1,30 @@
|
|
|
1
|
-
# ColorPicker —
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
1. **색 공간 표현.** hex/RGB/HSB/alpha 중 무엇을 공개 API로 삼을지, 변환 규칙을 어디
|
|
33
|
-
소유할지부터 새로 정해야 한다 — 이 저장소의 어떤 기존 컴포넌트도 색 값 자체를
|
|
34
|
-
입력 데이터로 다루지 않는다(`semanticColors`/`ColorReference`는 전부 제품이 아니라
|
|
35
|
-
시스템이 미리 고정한 토큰이다).
|
|
36
|
-
2. **대비·접근성.** 사용자가 고른 색이 텍스트/배경 대비 4.5:1을 만족하는지, 선택 UI
|
|
37
|
-
자체의 포커스 인디케이터가 색상 스와치 배경과 충분한 대비를 유지하는지를 계약이
|
|
38
|
-
보장해야 한다 — Statistic의 trend, UploadItem의 상태처럼 이 저장소는 "색으로만
|
|
39
|
-
말하지 않는다"를 지켜왔는데, ColorPicker는 그 규칙과 정반대로 색 자체가 선택 결과인
|
|
40
|
-
컴포넌트라 이 원칙을 어떻게 지킬지부터 새로 설계해야 한다(스와치에 값 텍스트를
|
|
41
|
-
병기하는 것 정도는 쉽지만, 색맹 사용자를 위한 팔레트 순서·명도 대비 규칙은 가볍지
|
|
42
|
-
않다).
|
|
43
|
-
3. **입력 방식.** 팔레트 탭, 텍스트 hex 입력, 슬라이더(hue/saturation/brightness) 최소
|
|
44
|
-
셋을 하나의 컴포넌트가 조율해야 하고, 셋 사이의 값 동기화(hex 입력 중 오타 상태를
|
|
45
|
-
팔레트에 어떻게 반영하는지)는 NumberField/Slider보다 훨씬 큰 상태 기계다.
|
|
46
|
-
|
|
47
|
-
수요 없이 이 세 갈래를 먼저 짜면 다음 사람이 "실제로 무엇을 위해 이렇게 무거운가"를
|
|
48
|
-
또 물어야 하는 계약이 된다. 반대로 실사용처가 나오면, 그 화면이 실제로 필요한 것은
|
|
49
|
-
이 셋 중 일부뿐일 가능성이 높다(예: 구단 색 하나를 브랜드 팔레트에서만 고르는 화면이면
|
|
50
|
-
색 공간 변환도 hex 입력도 필요 없다) — 지금 전체를 설계하면 그 실제 요구보다 큰 계약이
|
|
51
|
-
된다.
|
|
52
|
-
|
|
53
|
-
## 만들지 않은 것
|
|
54
|
-
|
|
55
|
-
`src/color-picker.ts`, `test/color-picker.test.ts`는 없다. catalog의
|
|
56
|
-
`{ name: "ColorPicker", category: "input", platform: "web", status: "planned" }` row와
|
|
57
|
-
`antDesignReferenceComponents`의 `ColorPicker → ColorPicker` crosswalk(`relationship:
|
|
58
|
-
"direct"`)는 건드리지 않는다 — 이름 자리를 지우는 것이 아니라, 지금 채울 계약이 없다는
|
|
59
|
-
것이다.
|
|
60
|
-
|
|
61
|
-
## 뒤집힐 조건
|
|
62
|
-
|
|
63
|
-
다음 중 하나가 실제로 측정되면 이 판정을 다시 연다.
|
|
64
|
-
|
|
65
|
-
1. BurnTok 또는 Yajalal에 사용자가 임의의 색을 고르는 실제 화면 요구가 나온다(예: 커스텀
|
|
66
|
-
테마, 사용자 지정 하이라이트 색).
|
|
67
|
-
2. 요구가 팔레트에서 미리 정의된 색 중 하나만 고르는 좁은 범위로 확인되면, ColorPicker
|
|
68
|
-
전체가 아니라 `Chip` 또는 `RadioGroup`의 시각 변형으로 더 가볍게 흡수될 수 있는지부터
|
|
69
|
-
먼저 검토한다 — hex 입력·색 공간 변환이 필요 없는 한 별도 컴포넌트를 열 이유가
|
|
70
|
-
약해진다.
|
|
71
|
-
3. 요구가 hex/RGB 자유 입력까지 포함하면, 이 문서의 세 갈래(색 공간/대비/입력 방식)를
|
|
72
|
-
실제 화면의 좁은 요구에 맞춰 하나씩만 계약하고 나머지는 열지 않는다.
|
|
1
|
+
# ColorPicker — Web sRGB 색상 입력
|
|
2
|
+
|
|
3
|
+
2026-09-30: 사용자가 남은 세 후보의 개발을 명시적으로 요청하여 기존 수요 대기 결정을 대체했다.
|
|
4
|
+
React Native와 Flutter 구현은 포함하지 않는다. 계약은 HEX sRGB를 저장값으로 선택했다.
|
|
5
|
+
HSV/RGB 입력기를 동시에 만드는 대신 브라우저 색상 선택기와 HEX 입력을 연결해 값 표현을 하나로 유지한다.
|
|
6
|
+
|
|
7
|
+
`@hjmds/react/color-picker`의 `ColorPicker`는 controlled `value`와 `onValueChange`를 받는다.
|
|
8
|
+
`label`과 `labels.color`, `labels.hex`, `labels.opacity`, `labels.invalid`는 제품에서 번역하여 전달한다.
|
|
9
|
+
`alpha`가 false면 `#rrggbb`, true면 `#rrggbbaa`를 내보낸다. 3·4자리 축약 HEX도 입력할 수 있다. alpha가 켜진 상태에서 3·6자리 HEX를 입력하면 색만 바꾸고 현재 투명도를 유지한다(네이티브 색상 입력과 같다).
|
|
10
|
+
alpha를 끈 상태에서 불투명하지 않은 값을 전달하면 투명도를 조용히 버리지 않고 거부한다.
|
|
11
|
+
|
|
12
|
+
- HEX는 Enter/blur에 확정한다. 잘못된 입력은 오류를 보여주고 외부 값은 유지한다.
|
|
13
|
+
Escape는 마지막 controlled 값으로 복구하며 Enter가 상위 폼을 제출하지 않게 한다.
|
|
14
|
+
- 브라우저 색상 선택기는 RGB를 선택하고 기존 alpha를 보존한다. 정확한 선택기 모양은 OS 소유다.
|
|
15
|
+
- 불투명도 range는 0–100%이며 키보드 화살표/Home/End를 지원한다. 8비트 alpha로 반올림한다.
|
|
16
|
+
- `presets`는 HEX 배열이며 색상값을 읽을 수 있는 버튼이다. 중복값은 정규화 후 제거한다.
|
|
17
|
+
- `disabled`는 fieldset을 통해 모든 입력과 팔레트를 비활성화한다.
|
|
18
|
+
- 제어부는 HJM canvas·border·focus 토큰을 사용한다. 견본의 사용자 색상은 콘텐츠 데이터다.
|
|
19
|
+
선택색을 제품의 본문/배경으로 사용할 때의 대비는 제품이 확인한다.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<ColorPicker label={t('color.title')} labels={{
|
|
23
|
+
color: t('color.choose'), hex: t('color.hex'), opacity: t('color.opacity'), invalid: t('color.invalid'),
|
|
24
|
+
}} value={color} onValueChange={setColor} alpha presets={['#b94627ff', '#338844ff']} />
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
계약: [color-picker.ts](../src/color-picker.ts). UI: [renderer](../../react/src/color-picker.tsx).
|
|
28
|
+
사용 예제: [Web additions](../../../showcase/web/src/patterns/WebAdditions.stories.tsx).
|
|
29
|
+
검증: [계약 회귀](../test/web-additions.test.ts), [브라우저 동작](../../react/test/web-additions.browser.test.tsx),
|
|
30
|
+
[환경 행렬](../../react/test/scenario-matrix.browser.test.tsx). 브라우저/OS 색상 팝업 자체의 내부 UI는 HJM 검증 대상이 아니다.
|
package/docs/command-palette.md
CHANGED
|
@@ -113,12 +113,14 @@ heading을 가지므로) — CommandPalette는 다르다: `role="dialog"` 표면
|
|
|
113
113
|
|
|
114
114
|
## 검증 화면
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
이 조사 당시 제품 채택은 미확인이었다. 2026-09-29부터 제품 채택은 관측으로 분리하며,
|
|
117
|
+
현재 성숙도는 catalog와 [승격 기준](stable-promotion.md)을 따른다.
|
|
117
118
|
|
|
118
119
|
## Web renderer (2026-09-18)
|
|
119
120
|
|
|
120
|
-
`@hjmds/react/command-palette`의 `CommandPalette`가 이 계약을 실행한다.
|
|
121
|
-
|
|
121
|
+
`@hjmds/react/command-palette`의 `CommandPalette`가 이 계약을 실행한다. Web은 키보드 검색·
|
|
122
|
+
실행·dismiss와 320px의 긴 한글 명령 설명 proof를 통과해 2026-09-29 stable로 승격한다.
|
|
123
|
+
Native는 `unsupported`다. 전역 단축키는 제품 소유다.
|
|
122
124
|
|
|
123
125
|
- **모달 takeover다.** Dialog·Sheet·SidePanel과 같은 모달 스택·스크롤 락·배경 격리를
|
|
124
126
|
공유한다(`packages/react/src/modal.tsx`). 별도 `modal` 축은 없다.
|
package/docs/confirm-popover.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# ConfirmPopover — Popover 위의 확인 조합
|
|
2
2
|
|
|
3
|
+
> 2026-09-30 정리: 구현된 조합을 미구현으로 집계하지 않도록 독립 Planned 행과 catalog ID를 제거했다.
|
|
4
|
+
> 기존 조합 예제와 공개 helper는 유지하며, 참조표는 실제 구성 컴포넌트로 연결한다.
|
|
5
|
+
> 아래 날짜별 설계 기록의 행 유지 지시는 당시 판단이며 이 결정으로 대체한다.
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
## 문제로 제기된 것
|
|
4
9
|
|
|
5
10
|
Ant Design `Popconfirm`은 "삭제하시겠습니까?" 같은 확인을 Modal보다 가벼운
|
package/docs/consumer-policy.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# HJM 소비 앱 정책
|
|
2
2
|
|
|
3
3
|
상태: **Normative**
|
|
4
|
-
정책 버전: **1.
|
|
4
|
+
정책 버전: **1.4.0**
|
|
5
5
|
적용 대상: 신규 HJM Web·React Native 앱과 기존 앱의 새 화면
|
|
6
6
|
|
|
7
7
|
이 정책 원문은 다음 package patch release부터
|
|
@@ -26,25 +26,38 @@
|
|
|
26
26
|
|
|
27
27
|
## 2. 성숙도별 채택
|
|
28
28
|
|
|
29
|
-
|
|
|
29
|
+
| 사용할 surface 상태 | 신규 앱 기본 정책 | 추가 조건 |
|
|
30
30
|
| --- | --- | --- |
|
|
31
31
|
| `stable` | 사용 가능 | 제품 흐름·copy 검증 |
|
|
32
|
-
| `beta` |
|
|
32
|
+
| `beta` | 필요에 따라 선택 가능 | 사용 표면·미완료 항목 확인, 채택 기록과 관련 회귀 검사 |
|
|
33
33
|
| `planned` | 사용 금지 | HJM package에서 구현·승격한 뒤 채택 |
|
|
34
34
|
| `deprecated` | 신규 사용 금지 | 공지된 migration 기한 안에 제거 |
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
`surfaceStatus`가 사용 플랫폼의 채택 기준입니다. contract `status`가 stable이어도
|
|
37
|
+
다른 renderer까지 안정화됐다는 뜻은 아닙니다. 예를 들어 TopBar는 Web stable / Native beta입니다.
|
|
38
|
+
|
|
39
|
+
신규 앱 scaffold의 기본 선택은 Stable Core입니다(SHOULD). Beta 사용은 정책 예외가 아닙니다.
|
|
40
|
+
앱의 기존 contract 또는 TASKS 한 곳에 컴포넌트·표면, 사용하는 이유, 담당자와 관련 회귀
|
|
41
|
+
검사를 기록해야 합니다(MUST). 여러 Beta를 한 기록으로 묶을 수 있고, 별도 ADR·대안 비교·
|
|
42
|
+
재검토 날짜는 요구하지 않습니다. 실제 기기·브라우저 QA는 바뀐 사용자 흐름과 플랫폼 위험에
|
|
43
|
+
맞춰 앱 릴리스에서 수행합니다. Beta라는 이유만으로 제품 전체 QA를 반복하지 않습니다.
|
|
44
|
+
|
|
45
|
+
2026-09-29 점검에서 HJM 자체 검증을 통과한 표시 컴포넌트도 제품 수·배포 이력 때문에
|
|
46
|
+
승격이 막혔습니다. 1.4.0은 컴포넌트 호환성 약속과 제품 릴리스 검증을 분리합니다.
|
|
47
|
+
[승격 기준](stable-promotion.md)을 따르며 기존 versioned app profile의 검사 요건은 그
|
|
48
|
+
profile을 명시적으로 갱신하기 전까지 유지합니다.
|
|
39
49
|
|
|
40
50
|
### 2.1 Stable Core와 필수 foundation bridge
|
|
41
51
|
|
|
42
|
-
1.5.0
|
|
52
|
+
1.5.0에서 Stable Core는 17개 컴포넌트였습니다. 1.4까지의 `Surface`, `Button`, `Field`, `TextArea`에
|
|
43
53
|
더해, 첫 제품 화면에 필요한 foundation인 `Text`, `Icon`, `Stack`, `Container`,
|
|
44
54
|
`DesignSystemProvider`와 세 제품 이상이 쓰는 `IconButton`, `Badge`, `Card`, `Tag`, `Notice`,
|
|
45
55
|
`Progress`, `Spinner`, `Skeleton`이 stable로 승격됐습니다. 승격 근거는 이름뿐이던 시나리오 증거를
|
|
46
56
|
실제 계산 스타일 검사로 바꾼 뒤 두 renderer에서 요구 시나리오가 모두 통과한 것입니다
|
|
47
|
-
([Stable Core](stable-core.md)).
|
|
57
|
+
([Stable Core](stable-core.md)). 이번 개정에서는 `Divider`, `Section`, `ListRow`, `Statistic`,
|
|
58
|
+
`DescriptionList`, `EmptyState`, `Result`, `Heading`, `Top`, `BottomCTA`를 양쪽에서 승격하고,
|
|
59
|
+
`TopBar`는 계약·Web만 승격했습니다. 계약/Web stable은 28개, Native stable은 27개입니다.
|
|
60
|
+
배포된 패키지의 catalog가 소비 앱의 기준입니다. 남은 beta를 `stable`로
|
|
48
61
|
간주하거나 `betaAdoptions: []`로 기록하는 것은 허용하지 않습니다(MUST NOT).
|
|
49
62
|
|
|
50
63
|
HJM-APP-STANDARD가 versioned `requiredFoundations` 목록을 선언한 경우 다음 bridge를
|
|
@@ -55,8 +68,8 @@ HJM-APP-STANDARD가 versioned `requiredFoundations` 목록을 선언한 경우
|
|
|
55
68
|
- 각 앱은 실제 adapter와 사용 환경에 대한 evidence를 소유합니다(MUST). `draft` /
|
|
56
69
|
`incubating` contract에서는 evidence가 `planned`일 수 있지만, `active` 또는 release gate는
|
|
57
70
|
timestamp가 있는 `verified` evidence 없이는 통과하면 안 됩니다(MUST NOT).
|
|
58
|
-
- 중앙 필수 목록 밖의 `beta`는
|
|
59
|
-
|
|
71
|
+
- 중앙 필수 목록 밖의 `beta`는 제품별 optional adoption이며 §2의 간단한 채택 기록을
|
|
72
|
+
사용합니다. 예전 profile이 ADR·verified evidence를 요구하면 해당 profile 갱신 때 이관합니다.
|
|
60
73
|
- foundation이 catalog에서 `stable`로 승격되면 기존 표준의 의미를 조용히 바꾸지 않고
|
|
61
74
|
다음 versioned app profile에서 bridge 목록을 줄입니다(MUST).
|
|
62
75
|
|
|
@@ -194,7 +207,7 @@ on-color 대비, 색에 의존하지 않는 상태 표지, reduce-motion을 제
|
|
|
194
207
|
4. 원시 색·간격·radius의 화면 레이어 유입 방지 검사
|
|
195
208
|
5. typecheck, unit/component test, production build
|
|
196
209
|
6. light/dark, 100–200% 글자 크기, LTR/RTL, reduce motion 중 앱이 지원한다고 선언한 환경
|
|
197
|
-
7. optional Beta component를 쓴다면
|
|
210
|
+
7. optional Beta component를 쓴다면 §2의 채택 기록과 변경한 사용자 흐름의 회귀 검사
|
|
198
211
|
|
|
199
212
|
앱이 지원하지 않는 플랫폼이나 환경은 가짜 통과 script로 만들지 않고 app contract에서
|
|
200
213
|
`not-applicable`과 이유를 선언해야 합니다(MUST).
|
|
@@ -218,8 +231,12 @@ package로 승격하지 않습니다.
|
|
|
218
231
|
|
|
219
232
|
- exact fixed-train dependency와 Provider가 CI에서 검증되는가?
|
|
220
233
|
- Stable은 기본 사용하고(1.5.0 이전 profile이면 필수 foundation은 중앙 목록+앱 evidence로),
|
|
221
|
-
optional Beta는
|
|
234
|
+
optional Beta는 채택 기록과 관련 회귀 검사로 추적되는가?
|
|
222
235
|
- screen 코드가 semantic token·component를 우회하지 않는가?
|
|
223
236
|
- 같은 상태가 Web/RN에서 같은 의미와 자연스러운 플랫폼 행동을 갖는가?
|
|
224
237
|
- 로고를 제거해도 제품의 핵심 콘텐츠와 정보 구조가 다른 제품과 구분되는가?
|
|
225
238
|
- 제품 고유 표현을 제거해도 정보 위계·피드백·접근성에서는 HJM의 결이 남는가?
|
|
239
|
+
|
|
240
|
+
## Optional presentation adapters
|
|
241
|
+
|
|
242
|
+
The [optional adapter contract](optional-adapters.md) lists platform-specific entries and exact peer requirements. These extensions do not promote the canonical component or establish consumer/device compatibility. Native adapters require separately verified development clients; base imports retain their existing dependency contract.
|
package/docs/context-menu.md
CHANGED
|
@@ -12,12 +12,22 @@
|
|
|
12
12
|
|
|
13
13
|
**키보드로 열 때의 앵커는 초점 요소의 상자다.** 좌표가 없다고 (0,0)에 띄우면 메뉴가 방금
|
|
14
14
|
초점이 있던 항목과 무관해 보인다. `resolveContextMenuAnchor`가 이 판단을 갖고 있고,
|
|
15
|
-
좌표도 초점 상자도 없으면 렌더링 대신 던진다.
|
|
15
|
+
좌표도 초점 상자도 없으면 렌더링 대신 던진다. 실제 메뉴가 포털에 마운트된 뒤 메뉴에 초점을
|
|
16
|
+
옮기고 `aria-activedescendant`로 활성 항목을 알린다. 열기 직전의 초점 요소는 보관한다.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
**Escape 또는 항목 실행으로 닫으면 열었던 요소에 초점을 돌린다.** 바깥을 눌러 닫을 때는
|
|
19
|
+
사용자가 누른 바깥 요소의 초점을 유지한다. 좌표에서 열렸다고 초점까지 떠 있으면 다음 Tab이
|
|
18
20
|
문서 처음으로 간다.
|
|
19
21
|
|
|
22
|
+
**화면 가장자리와 긴 항목도 다룬다.** 메뉴가 포털에 표시된 다음 실제 크기로 위치를 화면 안에
|
|
23
|
+
맞춘다. 긴 한글·공백 없는 항목명은 줄바꿈해 전체 명령을 읽을 수 있게 한다. 임의의 고정 크기
|
|
24
|
+
추정은 글꼴과 줄바꿈에 따라 메뉴가 다시 화면 밖으로 나갈 수 있어 사용하지 않는다.
|
|
25
|
+
|
|
20
26
|
**브라우저 기본 메뉴를 제품이 소유한 표면에서만 대체한다.** 텍스트 선택·링크·이미지 위의
|
|
21
27
|
기본 메뉴를 빼앗으면 복사·새 탭 열기를 사용자에게서 뺏는 것이다.
|
|
22
28
|
|
|
23
29
|
**Web 전용.** 네이티브의 길게 누르기 메뉴는 OS가 제공하는 표면이라 같은 계약이 아니다.
|
|
30
|
+
|
|
31
|
+
React 구현은 `packages/react/test/context-menu.interactions.browser.test.tsx`에서 Chromium으로
|
|
32
|
+
포인터 좌표 열기, 키보드 열기, 활성 항목 안내, Escape·실행 후 초점 복귀, 작은 화면에서의 긴
|
|
33
|
+
항목 배치를 검증한다.
|
package/docs/data-table.md
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
`id`와 `disabled?`만 가진 훨씬 좁은 타입이다 — Collection 기본 계약 중 실제로
|
|
29
29
|
행에 맞는 부분만 가져오고, 맞지 않는 부분(label/textValue/typeahead)은 그대로
|
|
30
30
|
두었다.
|
|
31
|
-
- `Pagination`(다른 저작자가 이번 배치에서 계약)과 `LoadMore`(이미
|
|
31
|
+
- `Pagination`(다른 저작자가 이번 배치에서 계약)과 `LoadMore`(이미 stable)는 DataTable이
|
|
32
32
|
**소유하지 않고 합성**한다. `asyncState`가 `loadingMore`일 때 그 아래 어느
|
|
33
33
|
컴포넌트를 두는지는 제품 선택이다 — Native 긴 목록에 LoadMore를 쓰듯 Web 표에도
|
|
34
34
|
같은 패턴이 통한다.
|
|
@@ -75,13 +75,14 @@
|
|
|
75
75
|
`native` 필드는 breadcrumb·form 같은 다른 Web 전용 계약과 같은 빈 배열
|
|
76
76
|
자리표시자다.
|
|
77
77
|
|
|
78
|
-
**검증 화면.**
|
|
79
|
-
|
|
78
|
+
**검증 화면.** 제품 vertical slice는 필요하지 않다. Renderer 계약을 정렬·tri-state selection·
|
|
79
|
+
async 상태와 narrow long-copy 회귀로 확인하고, 소비 제품의 데이터 정렬·페이지네이션은 계속
|
|
80
|
+
제품 소유로 둔다. 채택 여부는 [stable 승격 기준](stable-promotion.md)과 별도다.
|
|
80
81
|
|
|
81
82
|
## Web renderer (2026-09-18)
|
|
82
83
|
|
|
83
|
-
`@hjmds/react/data-table`의 `DataTable`이 이 계약을 실행한다.
|
|
84
|
-
Native `unsupported`다.
|
|
84
|
+
`@hjmds/react/data-table`의 `DataTable`이 이 계약을 실행한다. Web renderer는 전용 keyboard·
|
|
85
|
+
selection·320px long-copy proof를 통과해 2026-09-29 stable로 승격한다. Native는 `unsupported`다.
|
|
85
86
|
|
|
86
87
|
- **기존 `Table`과 겹치지 않는다.** `@hjmds/react`의 `Table`(advanced-display)은 열·행을
|
|
87
88
|
그려 주는 표시용이고 선택·tri-state·async 상태·정렬 상태 순환이 없다. DataTable은 그
|
|
@@ -96,6 +97,10 @@ Native `unsupported`다.
|
|
|
96
97
|
- **셀 하나에 focusable 컨트롤은 최대 하나다.** roving tabindex grid 탐색을 도입하지 않고
|
|
97
98
|
기본 tab 순서를 쓴다.
|
|
98
99
|
- **페이지네이션은 표 아래에 조합한다.** `footer` slot은 제품이 채우고 표가 소유하지 않는다.
|
|
100
|
+
- 키보드는 표 전체를 하나의 ARIA grid로 만들지 않고 native Tab 순서를 쓴다. 실제 브라우저 입력으로
|
|
101
|
+
select-all·정렬·행 선택 버튼이 Tab 순서에 있고 Space/Enter로 동작하는지 확인한다. 방향키 셀 탐색은
|
|
102
|
+
계약에 없다 — 제품 수요가 확인되지 않은 full grid interaction은 별도 계약으로 다룬다.
|
|
99
103
|
- 로컬 검증: `test/data-table.browser.test.tsx` 5개(header 안 정렬 버튼과 aria-sort 3단계,
|
|
100
104
|
tri-state 선택과 disabled 제외, 단일 선택 radio 의미, 셀당 컨트롤 하나와 기본 tab 순서,
|
|
101
|
-
async 상태 발표와 footer 조합)
|
|
105
|
+
async 상태 발표와 footer 조합), `test/data-table.keyboard.browser.test.tsx` 1개(실제 Tab·Enter·Space
|
|
106
|
+
입력), 그리고 `Patterns/CommandPalette`의 표 화면.
|
package/docs/date-picker.md
CHANGED
|
@@ -78,7 +78,9 @@ Select의 축과 동일하되 `content`(idle/loading/loadingMore/error)는 없
|
|
|
78
78
|
|
|
79
79
|
## 검증 화면
|
|
80
80
|
|
|
81
|
-
first-party Web dialog·Native Sheet renderer
|
|
82
|
-
`
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
first-party Web dialog·Native Sheet renderer의 필수 시나리오가 연결되어 양쪽 surface가
|
|
82
|
+
`stable`이다. Web Chromium 검사는 트리거 열기, 실제 날짜 셀 키보드 이동·선택, 닫힘과 focus
|
|
83
|
+
복귀를 확인한다. Native renderer 검사는 날짜 host action에 따른 선택·닫힘을 확인한다.
|
|
84
|
+
두 renderer 모두 접근성 이름, 긴 label, 환경 matrix 검사를 통과한다. 실제 iOS/Android 기기와
|
|
85
|
+
제품 폼의 스크린 리더 검증은 소비 앱 릴리스 QA에 속하며, Yajalal vertical slice나 제품 채택
|
|
86
|
+
수는 이 공개 API 성숙도 판단의 조건으로 두지 않는다([Stable 승격 기준](stable-promotion.md)).
|
package/docs/date-range.md
CHANGED
|
@@ -27,5 +27,7 @@
|
|
|
27
27
|
확정된 구간과 hover 미리보기를 함께 처리한다 — renderer가 두 벌의 칠하기 규칙을 만들지
|
|
28
28
|
않게 한다.
|
|
29
29
|
|
|
30
|
-
**Native.**
|
|
31
|
-
|
|
30
|
+
**Native.** 같은 Calendar 격자에서 날짜 host action이 범위의 시작·중간·끝으로 반영되고,
|
|
31
|
+
접근성 이름에도 해당 상태가 포함된다. 시각 범위는 Web에서 band와 hover preview로, Native에서
|
|
32
|
+
날짜 아래 점과 이름으로 표현한다. 두 renderer의 환경·접근성 matrix와 날짜 선택 회귀가 통과해
|
|
33
|
+
각 지원 surface의 계약은 stable이다. 기기·VoiceOver·TalkBack 검증은 소비 제품 QA에서 한다.
|
package/docs/description-list.md
CHANGED
|
@@ -62,14 +62,13 @@ Web/Native 모두 `resolveDescriptionListColumnCount`가 돌려준 열 수로 CS
|
|
|
62
62
|
flex wrap 레이아웃을 만듭니다. 실제 측정된 컨테이너 폭과 시스템 폰트 배율은 renderer가
|
|
63
63
|
공급하고, 이 계약은 그 두 입력을 받아 열 수만 결정합니다.
|
|
64
64
|
|
|
65
|
-
## 현재 검증과
|
|
65
|
+
## 현재 검증과 제품 채택
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
공통 descriptor와 `resolveDescriptionListColumnCount`를 소비하는 Web/RN renderer는
|
|
68
|
+
[2026-09-29 승격](stable-core.md)의 대상이다. Web의 `<dl>/<dt>/<dd>`와 폭 기반 열 전환,
|
|
69
|
+
Native의 라벨-값 접근성 노드와 text-scale 기반 열 전환은 renderer 회귀로 검증한다.
|
|
70
|
+
필수 환경 시나리오가 모두 연결됐으며 계약·양쪽 surface를 stable로 올린다.
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
명시적인 debt로 남는다.
|
|
72
|
+
과거 후보로 든 야잘알 화면은 단일 열 List/ListRow 조합이므로 DescriptionList 채택으로
|
|
73
|
+
세지 않는다. 제품의 1/2열 화면과 실제 기기 QA는 해당 앱 릴리스에서 확인하며,
|
|
74
|
+
[승격 기준](stable-promotion.md)에 따라 제품 채택 부재를 성숙도 관문으로 쓰지 않는다.
|
package/docs/dialog.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Dialog
|
|
2
|
+
|
|
3
|
+
Dialog provides a modal boundary for a task that needs the user's attention. This
|
|
4
|
+
contract was recorded during the 2026-09-29 promotion audit because Web and Native
|
|
5
|
+
already had different title APIs and dismissal sources, while there was no single
|
|
6
|
+
component guide describing their shared behavior.
|
|
7
|
+
|
|
8
|
+
## Accessible name and modal boundary
|
|
9
|
+
|
|
10
|
+
- Web renders `role="dialog"`, `aria-modal="true"`, a title reference, and an
|
|
11
|
+
optional description reference. When opened, focus enters the dialog, stays in
|
|
12
|
+
the active modal, and returns to the trigger (or the explicit return-focus
|
|
13
|
+
target) after dismissal.
|
|
14
|
+
- Native renders a `role="dialog"` modal boundary with
|
|
15
|
+
`accessibilityViewIsModal`. A string `title` names the dialog. If `title` is a
|
|
16
|
+
React element, pass `accessibilityTitle`; native renderers cannot derive a
|
|
17
|
+
reliable accessible name from arbitrary element children.
|
|
18
|
+
- The close action uses the required localized `closeLabel` on both renderers.
|
|
19
|
+
Dialog titles, descriptions, and body copy wrap instead of being clipped to one
|
|
20
|
+
line. Web constrains the dialog to the viewport and allows its content to
|
|
21
|
+
scroll.
|
|
22
|
+
|
|
23
|
+
## Dismissal behavior
|
|
24
|
+
|
|
25
|
+
Dismiss requests are ignored while `busy` is true or `dismissible` is false.
|
|
26
|
+
Web reports `escape`, `outside`, or `close-action`; Native reports `back`,
|
|
27
|
+
`outside`, or `close-action`. A Native primary or secondary action runs its
|
|
28
|
+
callback and requests `close-action`; controlled Dialog owners decide when the
|
|
29
|
+
dialog actually closes. While an async operation is pending, keep `open` true and
|
|
30
|
+
set `busy` so repeated action and dismissal attempts are disabled.
|
|
31
|
+
|
|
32
|
+
## Renderer proof
|
|
33
|
+
|
|
34
|
+
The React browser tests cover focus entry, Tab containment, Escape dismissal,
|
|
35
|
+
focus restoration, busy dismissal guards, and long title/description/body copy in
|
|
36
|
+
a narrow viewport. React Native renderer tests cover modal role/name/state,
|
|
37
|
+
action labels and close requests, back/outside dismissal, busy guards, and long
|
|
38
|
+
title/description copy. These tests exercise renderer contracts; device and
|
|
39
|
+
screen-reader behavior remains a separate consumer validation concern.
|
|
@@ -194,8 +194,9 @@ semantic tone은 독립이고 arrow/minus와 visible copy를 함께 사용합니
|
|
|
194
194
|
|
|
195
195
|
Yajalal의 선수·FA 기록 vertical slice에서 기존 StatGrid 호환 adapter와 새 Statistic renderer를
|
|
196
196
|
함께 검증했습니다. 좁은 폭에서는 1열까지 reflow하고 큰 글자에서도 값을 줄 수로 자르지 않으며,
|
|
197
|
-
각 통계의 label/value/hint/trend를 독립된 접근성 이름으로
|
|
198
|
-
|
|
197
|
+
각 통계의 label/value/hint/trend를 독립된 접근성 이름으로 유지해 Statistic을 첫 beta
|
|
198
|
+
renderer로 도입했습니다. 현재 maturity는 [Stable Core](stable-core.md)의 surface 목록을
|
|
199
|
+
따릅니다.
|
|
199
200
|
|
|
200
201
|
Select/Combobox는 Menu의 collection contract를 공유하지만 role과 dismiss behavior는 별도입니다.
|
|
201
202
|
Web은 listbox popup, Native는 Sheet를 사용합니다. Native 긴 목록에는 페이지 번호보다
|
|
@@ -209,15 +210,18 @@ manual fallback을 유지합니다.
|
|
|
209
210
|
BurnTok 홈 피드 vertical slice에서 Web IntersectionObserver와 항상 보이는 manual fallback,
|
|
210
211
|
Native FlatList onEndReached와 footer fallback을 같은 controller에 연결했습니다. 두 renderer 모두
|
|
211
212
|
탭과 다음 offset으로 requestKey를 만들고 실제 페이지 요청 Promise가 끝날 때까지 중복 호출을
|
|
212
|
-
차단하며 loading/error/complete 접근성 상태를
|
|
213
|
+
차단하며 loading/error/complete 접근성 상태를 검증해 LoadMore를 beta renderer로 도입했습니다.
|
|
214
|
+
현재 Web·Native stable evidence와 소비 앱 경계는 [stable promotion log](stable-core.md)에
|
|
215
|
+
기록합니다.
|
|
213
216
|
|
|
214
217
|
BottomNavigation은 route source-of-truth를 복제하지 않고 `selectedKey`를 input으로만 받습니다.
|
|
215
218
|
Web renderer는 실제 link와 `aria-current`, Native renderer는 navigator의 preventable tabPress와
|
|
216
219
|
long press를 보존합니다. 숫자 badge는 visual subtree를 접근성에서 숨기고 item root에 합성된
|
|
217
220
|
이름을 한 번만 전달합니다. BurnTok 중앙 생성 action은 `center-gap`에 놓이는 sibling primary
|
|
218
221
|
action이며 destination collection에는 들어가지 않습니다. BurnTok Web/RN과 Yajalal RN 실제
|
|
219
|
-
navigation에 적용해 route state·disabled/reselect·긴 글자·safe area·RTL 계약을
|
|
220
|
-
BottomNavigation을 beta로
|
|
222
|
+
navigation에 적용해 route state·disabled/reselect·긴 글자·safe area·RTL 계약을 검증해
|
|
223
|
+
BottomNavigation을 beta renderer로 도입했습니다. 현재 Web·Native stable evidence는
|
|
224
|
+
[stable promotion log](stable-core.md)에 기록합니다.
|
|
221
225
|
|
|
222
226
|
선행 타입은 Select의 nullable stable key와 Combobox의 `selectedKey`/`inputValue` 분리를
|
|
223
227
|
고정합니다. local filtering과 server-driven external filtering도 구분해 선택 상태와 비동기
|
|
@@ -233,7 +237,7 @@ state도 selection state와 별도 controlled/uncontrolled 축으로 유지합
|
|
|
233
237
|
|
|
234
238
|
surface별 `planned → beta` gate는 public renderer export와 package CI가 실행하는 canonical
|
|
235
239
|
`default` proof를 요구합니다. 실제 제품 vertical slice는 별도의 adoption evidence이며,
|
|
236
|
-
없다면
|
|
240
|
+
없다면 채택 관측으로 남기며 stable 승격을 막지 않습니다(2026-09-29 [기준 개정](stable-promotion.md)). 제품 채택을 검토하는
|
|
237
241
|
과정에서 **전제가 이미 바뀌어 있던 사례**도 나왔습니다.
|
|
238
242
|
|
|
239
243
|
`Calendar`·`DatePicker`를 위임할 때 "야잘알 일정 찾기가 월 달력 격자를 자체 구현 중"이라는
|
|
@@ -244,7 +248,7 @@ surface별 `planned → beta` gate는 public renderer export와 package CI가
|
|
|
244
248
|
|
|
245
249
|
이후 DatePicker와 Calendar에는 first-party Web·Native renderer와 canonical 환경 증거가 추가되어
|
|
246
250
|
`beta`로 승격됐다. 다만 위 실측은 여전히 유효하다. **제품 adoption evidence는 없고**,
|
|
247
|
-
Yajalal의 날짜 레일을 DatePicker 채택으로 세지 않는다.
|
|
251
|
+
Yajalal의 날짜 레일을 DatePicker 채택으로 세지 않는다. 성숙도는 제품 채택 여부 대신 현재 renderer 필수 증거로 판정한다.
|
|
248
252
|
|
|
249
253
|
**교훈**: 위임할 때 준 실사용처 전제를 저작자가 **확인하게** 해야 한다. 리드의 기억은
|
|
250
254
|
커밋 하나로 낡는다.
|
package/docs/file-picker.md
CHANGED
|
@@ -54,6 +54,8 @@ FilePicker는 파일 선택 의도만 소유하고, 진행·성공·실패 표
|
|
|
54
54
|
- 거부 발표는 색에 의존하지 않는다 — `reason`과 한계값으로 제품이 만든 문장을
|
|
55
55
|
live 영역/에러 카피로 보여준다.
|
|
56
56
|
|
|
57
|
-
**검증
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
**검증 범위.** Web의 native file input/dropzone과 Native의 이름 있는 제품 adapter action,
|
|
58
|
+
양쪽의 같은 선택 판정 resolver를 renderer 회귀로 검증한다. Native adapter가 실제 OS
|
|
59
|
+
document/image picker를 여는지는 제품 통합 책임이며 이 패키지는 이를 구현하거나 기기에서
|
|
60
|
+
검증했다고 주장하지 않는다. 소비 앱은 해당 adapter를 연결하고 제품별 릴리스 QA에서 OS
|
|
61
|
+
동작을 확인한다.
|
package/docs/form.md
CHANGED
|
@@ -98,10 +98,22 @@ dispose()가 submitting 중 호출되면 그 attempt는 interrupted로 한 번
|
|
|
98
98
|
live-region 동등물(`AccessibilityInfo.announceForAccessibility` 또는 동등 API)로
|
|
99
99
|
발표. `submitting` 중에는 제출 버튼이 `accessibilityState.busy`를 얻습니다.
|
|
100
100
|
|
|
101
|
+
Native 소비자는 제품 검증 결과로 `resolveFirstInvalidFieldFocusTarget`을 호출해
|
|
102
|
+
선택한 입력의 ref를 `Form.firstInvalidFieldRef`에 전달합니다. ref가 있으면 제출 버튼은
|
|
103
|
+
`TextInput.focus()`와 `AccessibilityInfo.setAccessibilityFocus()`를 실행하고 `onSubmit`을
|
|
104
|
+
호출하지 않습니다. ref가 없으면 기존 제출 경로를 따릅니다. Form은 필드 순서·오류 상태를
|
|
105
|
+
등록하거나 추론하지 않습니다.
|
|
106
|
+
|
|
101
107
|
## 검증 화면
|
|
102
108
|
|
|
103
|
-
Form은
|
|
104
|
-
|
|
109
|
+
Form은 surface별 상태를 갖습니다. Web은 실제 Chromium 입력·Enter 제출, 비동기 중복 제출 방지,
|
|
110
|
+
필드·form 오류 semantics와 긴 자식 콘텐츠·환경 matrix 검증을 통과해 stable입니다. Native는
|
|
111
|
+
현재 값 입력·제출·busy 상태와, 제품이 선택한 첫 무효 필드로 Native 키보드 및 accessibility
|
|
112
|
+
focus를 전달하는 host-action 회귀까지 검증합니다. 테스트는 React Native host 동작을 확인하며,
|
|
113
|
+
실기기 VoiceOver/TalkBack 경험은 별도 검증 근거로 기록해야 합니다.
|
|
114
|
+
|
|
115
|
+
BurnTok Web의 `apps/web/src/app/ideas/page.tsx`는 2필드 생성 흐름에서 제품 소유 검증과 첫 오류
|
|
116
|
+
필드 focus를 연결한 소비 사례입니다.
|
|
105
117
|
|
|
106
118
|
- `apps/web/src/lib/form-contract.ts`: `formRecipe` 간격, `createFormSubmitSession`,
|
|
107
119
|
`resolveFirstInvalidFieldFocusTarget`을 제품 validation과 React lifecycle에 연결합니다.
|
|
@@ -111,8 +123,8 @@ Form은 이제 `beta`입니다. BurnTok Web의
|
|
|
111
123
|
- `apps/web/src/components/ui/AppTextField.tsx`: input/textarea ref를 실제 control까지
|
|
112
124
|
전달해 focus 계약이 설명에 그치지 않게 합니다.
|
|
113
125
|
|
|
114
|
-
|
|
115
|
-
|
|
126
|
+
제품 채택 수는 stable blocker가 아닙니다. 실기기 VoiceOver/TalkBack 확인은 제품 QA 범위이며,
|
|
127
|
+
HJM renderer의 Native action 회귀와 사용 가능한 자동 검증을 기준으로 surface 성숙도를 평가합니다.
|
|
116
128
|
|
|
117
129
|
## 공개하지 않기로 한 것
|
|
118
130
|
|