@iyulab/components 1.39.0 → 1.40.1

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 (108) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +24 -0
  3. package/dist/_virtual/{_@oxc-project_runtime@0.148.0 → _@oxc-project_runtime@0.149.0}/helpers/esm/decorate.js +1 -1
  4. package/dist/_virtual/{_@oxc-project_runtime@0.148.0 → _@oxc-project_runtime@0.149.0}/helpers/esm/decorateMetadata.js +1 -1
  5. package/dist/_virtual/_glob-assets_raw/dark.css.e8ac080d.js +1 -1
  6. package/dist/_virtual/_glob-assets_raw/light.css.637cb2f8.js +1 -1
  7. package/dist/assets/locales/ar.json.js +6 -2
  8. package/dist/assets/locales/de.json.js +6 -2
  9. package/dist/assets/locales/en.json.js +6 -2
  10. package/dist/assets/locales/es.json.js +6 -2
  11. package/dist/assets/locales/fr.json.js +6 -2
  12. package/dist/assets/locales/id.json.js +6 -2
  13. package/dist/assets/locales/ja.json.js +6 -2
  14. package/dist/assets/locales/ko.json.js +6 -2
  15. package/dist/assets/locales/pt-BR.json.js +6 -2
  16. package/dist/assets/locales/ru.json.js +6 -2
  17. package/dist/assets/locales/th.json.js +6 -2
  18. package/dist/assets/locales/vi.json.js +6 -2
  19. package/dist/assets/locales/zh-CN.json.js +6 -2
  20. package/dist/assets/locales/zh-TW.json.js +6 -2
  21. package/dist/components/UFloatingElement.js +2 -2
  22. package/dist/components/UFormControlElement.js +2 -2
  23. package/dist/components/UOverlayElement.js +2 -2
  24. package/dist/components/alert/UAlert.js +2 -2
  25. package/dist/components/avatar/UAvatar.js +2 -2
  26. package/dist/components/badge/UBadge.js +2 -2
  27. package/dist/components/breadcrumb/UBreadcrumb.js +1 -1
  28. package/dist/components/breadcrumb/UBreadcrumb.styles.js +1 -1
  29. package/dist/components/breadcrumb-item/UBreadcrumbItem.js +2 -2
  30. package/dist/components/breadcrumb-item/UBreadcrumbItem.styles.js +1 -1
  31. package/dist/components/button/UButton.js +2 -2
  32. package/dist/components/button-group/UButtonGroup.js +2 -2
  33. package/dist/components/card/UCard.js +2 -2
  34. package/dist/components/carousel/UCarousel.js +2 -2
  35. package/dist/components/checkbox/UCheckbox.js +2 -2
  36. package/dist/components/checkbox/UCheckbox.styles.js +7 -1
  37. package/dist/components/chip/UChip.js +2 -2
  38. package/dist/components/chip/UChip.styles.js +7 -0
  39. package/dist/components/copy-button/UCopyButton.js +2 -2
  40. package/dist/components/date-picker/UDatePicker.js +2 -2
  41. package/dist/components/date-picker/UDatePicker.styles.js +2 -2
  42. package/dist/components/dialog/UDialog.js +2 -2
  43. package/dist/components/divider/UDivider.js +2 -2
  44. package/dist/components/divider/UDivider.styles.js +1 -1
  45. package/dist/components/drawer/UDrawer.js +2 -2
  46. package/dist/components/expander/UExpander.js +2 -2
  47. package/dist/components/expander/UExpander.styles.js +1 -1
  48. package/dist/components/field/UField.js +2 -2
  49. package/dist/components/field/UField.styles.js +1 -1
  50. package/dist/components/file-input/UFileInput.js +2 -2
  51. package/dist/components/file-input/UFileInput.styles.js +2 -2
  52. package/dist/components/form/UForm.js +2 -2
  53. package/dist/components/icon/UIcon.js +13 -6
  54. package/dist/components/icon-button/UIconButton.js +2 -2
  55. package/dist/components/input/UInput.d.ts +15 -1
  56. package/dist/components/input/UInput.js +62 -12
  57. package/dist/components/input/UInput.styles.js +25 -6
  58. package/dist/components/menu/UMenu.js +2 -2
  59. package/dist/components/menu-item/UMenuItem.js +2 -2
  60. package/dist/components/option/UOption.js +2 -2
  61. package/dist/components/panel/UPanel.js +2 -2
  62. package/dist/components/popover/UPopover.js +23 -7
  63. package/dist/components/progress-bar/UProgressBar.js +2 -2
  64. package/dist/components/progress-ring/UProgressRing.js +2 -2
  65. package/dist/components/radio/URadio.js +2 -2
  66. package/dist/components/rating/URating.js +2 -2
  67. package/dist/components/rating/URating.styles.js +6 -0
  68. package/dist/components/select/USelect.d.ts +7 -1
  69. package/dist/components/select/USelect.js +19 -8
  70. package/dist/components/select/USelect.styles.js +18 -5
  71. package/dist/components/skeleton/USkeleton.js +2 -2
  72. package/dist/components/slider/USlider.d.ts +3 -1
  73. package/dist/components/slider/USlider.js +2 -2
  74. package/dist/components/slider/USlider.styles.js +19 -2
  75. package/dist/components/spinner/USpinner.js +2 -2
  76. package/dist/components/split-panel/USplitPanel.d.ts +43 -2
  77. package/dist/components/split-panel/USplitPanel.js +127 -11
  78. package/dist/components/split-panel/USplitPanel.styles.js +37 -6
  79. package/dist/components/switch/USwitch.js +2 -2
  80. package/dist/components/switch/USwitch.styles.js +8 -3
  81. package/dist/components/tab/UTab.js +2 -2
  82. package/dist/components/tab/UTab.styles.js +1 -1
  83. package/dist/components/tab-panel/UTabPanel.js +3 -3
  84. package/dist/components/tag/UTag.js +2 -2
  85. package/dist/components/text/UText.js +2 -2
  86. package/dist/components/text/UText.styles.js +1 -1
  87. package/dist/components/textarea/UTextarea.d.ts +5 -0
  88. package/dist/components/textarea/UTextarea.js +2 -2
  89. package/dist/components/textarea/UTextarea.styles.js +6 -3
  90. package/dist/components/tooltip/UTooltip.js +2 -2
  91. package/dist/components/tree/UTree.js +2 -2
  92. package/dist/components/tree-item/UTreeItem.js +2 -2
  93. package/dist/components/tree-item/UTreeItem.styles.js +50 -13
  94. package/dist/styles/dark.css +4 -1
  95. package/dist/styles/light.css +7 -4
  96. package/dist/utilities/Locale.d.ts +1 -1
  97. package/dist/utilities/Toast.d.ts +34 -0
  98. package/dist/utilities/Toast.js +57 -0
  99. package/dist/utilities/devWarning.d.ts +17 -0
  100. package/dist/utilities/devWarning.js +5 -0
  101. package/package.json +4 -4
  102. package/skills/iyulab-components/references/components/dialog.md +19 -0
  103. package/skills/iyulab-components/references/components/input.md +17 -0
  104. package/skills/iyulab-components/references/components/panel.md +12 -0
  105. package/skills/iyulab-components/references/components/select.md +9 -0
  106. package/skills/iyulab-components/references/components/slider.md +1 -1
  107. package/skills/iyulab-components/references/components/split-panel.md +41 -3
  108. package/skills/iyulab-components/references/components/tab-panel.md +18 -0
@@ -58,49 +58,86 @@ var styles = css`
58
58
  white-space: nowrap;
59
59
  }
60
60
 
61
- /* ── toggler ── */
61
+ /* ── toggler ──
62
+ The box that takes the pointer is 24x24 (WCAG 2.2 SC 2.5.8); the square you see is still
63
+ 18x18, drawn by the pseudo-element. The negative margins give the extra 3px on each side
64
+ back, so the glyph, the label and the row height sit exactly where they did — the extra
65
+ area lands on the header's own padding and the gap before the label, never on another
66
+ target. */
62
67
  .prefix-toggler {
68
+ position: relative;
69
+ box-sizing: border-box;
63
70
  flex-shrink: 0;
64
- margin-right: 4px;
71
+ margin: -3px 1px -3px -3px;
65
72
  display: inline-flex;
66
73
  align-items: center;
67
74
  justify-content: center;
68
- width: 18px;
69
- height: 18px;
75
+ width: 24px;
76
+ height: 24px;
77
+ }
78
+ .prefix-toggler::before {
79
+ content: "";
80
+ position: absolute;
81
+ inset: 3px;
70
82
  border-radius: var(--u-radius-sm, 3px);
83
+ transition: background-color var(--u-duration-fast, 140ms) var(--u-ease-standard, cubic-bezier(0.2, 0, 0, 1));
84
+ }
85
+ .prefix-toggler > * {
86
+ position: relative;
71
87
  }
72
88
  .prefix-toggler u-icon {
73
89
  font-size: 12px;
74
90
  pointer-events: none;
75
91
  }
76
- :host(:not([disabled])[trigger="icon"]) .prefix-toggler:hover,
77
- :host(:not([disabled])[trigger="icon"]:focus-visible) .prefix-toggler {
92
+ :host(:not([disabled])[trigger="icon"]) .prefix-toggler:hover::before,
93
+ :host(:not([disabled])[trigger="icon"]:focus-visible) .prefix-toggler::before {
78
94
  background-color: var(--u-bg-color-hover, #F5F5F5);
79
95
  }
96
+ /* A checkable row puts the checkbox 4px after the toggle. Centred 24px areas for both would
97
+ overlap by 3px, so here the toggle takes its extra width entirely on the left (header
98
+ padding), and the padding keeps the glyph centred on its visible square. */
99
+ :host([checkable]) .prefix-toggler {
100
+ margin: -3px 4px -3px -6px;
101
+ padding-left: 6px;
102
+ }
103
+ :host([checkable]) .prefix-toggler::before {
104
+ inset: 3px 0 3px 6px;
105
+ }
80
106
 
81
- /* ── checkbox ── */
107
+ /* ── checkbox ──
108
+ Same split as the toggle: a 24x24 box takes the pointer, the 16x16 box you see is the
109
+ pseudo-element, and the negative margins keep every visible edge where it was. */
82
110
  .prefix-checkbox {
111
+ position: relative;
112
+ box-sizing: border-box;
83
113
  flex-shrink: 0;
84
- margin-right: 4px;
114
+ margin: -4px 0 -4px -4px;
85
115
  display: inline-flex;
86
116
  align-items: center;
87
117
  justify-content: center;
88
- width: 16px;
89
- height: 16px;
118
+ width: 24px;
119
+ height: 24px;
120
+ cursor: pointer;
121
+ }
122
+ .prefix-checkbox::before {
123
+ content: "";
124
+ position: absolute;
125
+ inset: 4px;
126
+ box-sizing: border-box;
90
127
  border-radius: var(--u-radius-sm, 3px);
91
128
  border: 1.5px solid var(--u-input-border-color, #E0E0E0);
92
129
  background-color: transparent;
93
130
  transition: background-color var(--u-duration-fast, 140ms) var(--u-ease-standard, cubic-bezier(0.2, 0, 0, 1)), border-color var(--u-duration-fast, 140ms) var(--u-ease-standard, cubic-bezier(0.2, 0, 0, 1));
94
- cursor: pointer;
95
131
  }
96
132
  .prefix-checkbox u-icon {
133
+ position: relative;
97
134
  visibility: hidden;
98
135
  font-size: 12px;
99
136
  color: #fff;
100
137
  pointer-events: none;
101
138
  }
102
- .prefix-checkbox[checked],
103
- .prefix-checkbox[indeterminate] {
139
+ .prefix-checkbox[checked]::before,
140
+ .prefix-checkbox[indeterminate]::before {
104
141
  border-color: var(--tree-item-color);
105
142
  background-color: var(--tree-item-color);
106
143
  }
@@ -242,7 +242,10 @@
242
242
  --u-txt-color-hover: var(--u-primary-color-strong);
243
243
  --u-txt-color-active: var(--u-primary-color-strong);
244
244
  --u-txt-color-disabled: var(--u-neutral-500);
245
- --u-txt-color-weak: var(--u-neutral-700);
245
+ /* 라이트와 같은 기준 — 놓이는 모든 면에서 AA. neutral-700(#8A8A8A)은 바탕 5.43 ·
246
+ 올림면(neutral-300) 4.16 · active 면(neutral-400) 3.15 로 미달이었다.
247
+ neutral-800(#B0B0B0) = 8.64 · 6.62 · 5.01. 본문(neutral-900)과의 대비비 1.46. */
248
+ --u-txt-color-weak: var(--u-neutral-800);
246
249
  --u-txt-color-strong: var(--u-neutral-1000);
247
250
 
248
251
  --u-link-txt-color: var(--u-primary-color-strong);
@@ -252,10 +252,13 @@
252
252
  --u-txt-color-hover: var(--u-primary-color-strong);
253
253
  --u-txt-color-active: var(--u-primary-color-strong);
254
254
  --u-txt-color-disabled: var(--u-neutral-400);
255
- /* neutral-500(#9E9E9E)은 흰 배경 대비 2.68 로 WCAG AA(4.5) 미달이었다.
256
- neutral-600(#757575) = 4.61 로 통과한다. 다크 매핑(neutral-700 = 5.43)은
257
- 원래 정상이라 손대지 않는다 — 라이트 한쪽만의 결함이었다. */
258
- --u-txt-color-weak: var(--u-neutral-600);
255
+ /* 보조 텍스트는 «놓이는 모든 면»에서 AA(4.5)를 받쳐야 한다 — 바탕만이 아니다.
256
+ neutral-600(#757575)은 바탕 4.61 에서만 통과하고 올림면(neutral-50) 4.41 ·
257
+ active 면(neutral-200) 3.97 로 미달이었다(표 머리·카드 위 보조 텍스트).
258
+ neutral-700(#616161) = 바탕 6.19 · 올림면 5.93 · active 5.34.
259
+ 본문(neutral-900)과의 대비비는 2.60 으로, 보조 텍스트를 모든 표면 위에서
260
+ 읽히게 보장하는 흔한 디자인 시스템 기준(본문 대비 약 1.3~1.8)보다 위계가 넓다. */
261
+ --u-txt-color-weak: var(--u-neutral-700);
259
262
  --u-txt-color-strong: var(--u-neutral-1000);
260
263
 
261
264
  --u-link-txt-color: var(--u-primary-color-strong);
@@ -12,7 +12,7 @@ export type SupportedLocale = 'en' | 'ko' | 'ja' | 'zh-CN' | 'zh-TW' | 'es' | 'f
12
12
  /** 내장 로케일은 자동완성되지만, 임의의 BCP47 태그도 그대로 받는다. */
13
13
  export type LocaleTag = SupportedLocale | (string & {});
14
14
  /** 라이브러리가 제공하는 chrome 문자열 키. 지금은 검증 메시지뿐이지만 이후 다른 UI 문구도 추가될 수 있다. */
15
- export type LocaleMessageKey = 'valueMissing' | 'badInput' | 'typeMismatch' | 'patternMismatch' | 'rangeUnderflow' | 'rangeOverflow' | 'stepMismatch' | 'tooShort' | 'tooLong' | 'increment' | 'decrement' | 'clear' | 'today' | 'time' | 'showPassword' | 'hidePassword' | 'previousSlide' | 'nextSlide' | 'goToSlide' | 'chooseDate' | 'previousMonth' | 'nextMonth' | 'close' | 'copy' | 'copied' | 'chooseFile' | 'noFileChosen' | 'filesSelected';
15
+ export type LocaleMessageKey = 'valueMissing' | 'badInput' | 'typeMismatch' | 'patternMismatch' | 'rangeUnderflow' | 'rangeOverflow' | 'stepMismatch' | 'tooShort' | 'tooLong' | 'increment' | 'decrement' | 'clear' | 'today' | 'time' | 'showPassword' | 'hidePassword' | 'previousSlide' | 'nextSlide' | 'goToSlide' | 'chooseDate' | 'previousMonth' | 'nextMonth' | 'close' | 'copy' | 'copied' | 'chooseFile' | 'noFileChosen' | 'filesSelected' | 'resizePanels' | 'search';
16
16
  type LocaleTable = Record<LocaleMessageKey, string>;
17
17
  /**
18
18
  * 한 패키지(또는 앱 영역)의 문자열 묶음.
@@ -68,4 +68,38 @@ export declare class Toast {
68
68
  * 않는다**. 낡은 항목은 버리고 새로 만든다.
69
69
  */
70
70
  private static getOrCreateContainer;
71
+ /**
72
+ * 알림 컨테이너를 **네이티브 top layer** 로 올린다(`popover="manual"`).
73
+ *
74
+ * ## 왜 — z-index 로는 원리적으로 닿지 않는 자리가 있다
75
+ *
76
+ * `OverlayManager` 의 «이름 있는 띠» 계약은 **우리 오버레이보다 위**를 보장한다(실측:
77
+ * 컨테이너 `z-index: 11000`). 그러나 ***top layer 는 z-index 축 밖에 있다*** — 소비앱이
78
+ * 네이티브 `<dialog>.showModal()` 을 열면 그 다이얼로그는 **어떤 z-index 보다도 위**에
79
+ * 서고, 우리 토스트는 가려진다. 실측(cycle-477, 크로미움 `elementFromPoint`):
80
+ *
81
+ * ```
82
+ * containerZIndex 11000 · notificationZIndex 11000 · hitTag "DIALOG" · toastReachable false
83
+ * ```
84
+ *
85
+ * 알림 채널의 제1원칙이 ***«지각되지 않는 경보는 경보가 아니다»*** 이므로, 그 채널은 겹침을
86
+ * **다투지 않고** 전용 층을 가져야 한다. `popover="manual"` 은 dismiss 계약도 포커스 트랩도
87
+ * 걸지 않고 **top layer 승격만** 하므로 이 표면에 정확히 맞는다.
88
+ *
89
+ * ## ⚠ 범위 — `document.body` 컨테이너만이다
90
+ *
91
+ * `target` 이 주어진 컨테이너는 `position: absolute` 로 **그 엘리먼트 기준**으로 놓인다.
92
+ * top layer 원소는 조상의 배치 문맥에서 떨어져 나오므로 그 좌표계가 통째로 깨진다 ⇒
93
+ * 타깃 기준 컨테이너는 **종전 z-index 경로를 그대로 쓴다.** 이것이 시범 범위이고,
94
+ * 오버레이 전면 전환(`u-dialog`·`u-drawer` 등)은 별개 결정이다(`ROADMAP.md` §C-B).
95
+ *
96
+ * ## ⚠ z-index 를 지우지 않는다
97
+ *
98
+ * `showPopover` 가 없는 브라우저(또는 호출이 거부되는 상태)에서는 **아무 일도 일어나지
99
+ * 않고 종전 동작이 그대로 남아야** 한다 — 그래서 승격은 순수 «추가»이고, 실패는 조용히
100
+ * 삼킨다(알림을 띄우려다 예외를 던지는 것은 이 표면에서 최악의 실패다).
101
+ */
102
+ private static preparePopover;
103
+ /** 연결된 뒤에만 부를 수 있다 — 실패하면 종전 동작(z-index 띠)으로 조용히 돌아간다. */
104
+ private static showTopLayer;
71
105
  }
@@ -93,6 +93,7 @@ var Toast = class {
93
93
  container.style.display = "flex";
94
94
  container.style.gap = "10px";
95
95
  const isTargeted = target !== document.body;
96
+ const popoverReady = isTargeted ? false : this.preparePopover(container);
96
97
  if (isTargeted) {
97
98
  container.style.position = "absolute";
98
99
  if (getComputedStyle(target).position === "static") target.style.position = "relative";
@@ -122,9 +123,65 @@ var Toast = class {
122
123
  }
123
124
  container.style.transform = transformParts.length ? transformParts.join(" ") : "";
124
125
  target.appendChild(container);
126
+ if (popoverReady) this.showTopLayer(container);
125
127
  byPosition.set(position, container);
126
128
  return container;
127
129
  }
130
+ /**
131
+ * 알림 컨테이너를 **네이티브 top layer** 로 올린다(`popover="manual"`).
132
+ *
133
+ * ## 왜 — z-index 로는 원리적으로 닿지 않는 자리가 있다
134
+ *
135
+ * `OverlayManager` 의 «이름 있는 띠» 계약은 **우리 오버레이보다 위**를 보장한다(실측:
136
+ * 컨테이너 `z-index: 11000`). 그러나 ***top layer 는 z-index 축 밖에 있다*** — 소비앱이
137
+ * 네이티브 `<dialog>.showModal()` 을 열면 그 다이얼로그는 **어떤 z-index 보다도 위**에
138
+ * 서고, 우리 토스트는 가려진다. 실측(cycle-477, 크로미움 `elementFromPoint`):
139
+ *
140
+ * ```
141
+ * containerZIndex 11000 · notificationZIndex 11000 · hitTag "DIALOG" · toastReachable false
142
+ * ```
143
+ *
144
+ * 알림 채널의 제1원칙이 ***«지각되지 않는 경보는 경보가 아니다»*** 이므로, 그 채널은 겹침을
145
+ * **다투지 않고** 전용 층을 가져야 한다. `popover="manual"` 은 dismiss 계약도 포커스 트랩도
146
+ * 걸지 않고 **top layer 승격만** 하므로 이 표면에 정확히 맞는다.
147
+ *
148
+ * ## ⚠ 범위 — `document.body` 컨테이너만이다
149
+ *
150
+ * `target` 이 주어진 컨테이너는 `position: absolute` 로 **그 엘리먼트 기준**으로 놓인다.
151
+ * top layer 원소는 조상의 배치 문맥에서 떨어져 나오므로 그 좌표계가 통째로 깨진다 ⇒
152
+ * 타깃 기준 컨테이너는 **종전 z-index 경로를 그대로 쓴다.** 이것이 시범 범위이고,
153
+ * 오버레이 전면 전환(`u-dialog`·`u-drawer` 등)은 별개 결정이다(`ROADMAP.md` §C-B).
154
+ *
155
+ * ## ⚠ z-index 를 지우지 않는다
156
+ *
157
+ * `showPopover` 가 없는 브라우저(또는 호출이 거부되는 상태)에서는 **아무 일도 일어나지
158
+ * 않고 종전 동작이 그대로 남아야** 한다 — 그래서 승격은 순수 «추가»이고, 실패는 조용히
159
+ * 삼킨다(알림을 띄우려다 예외를 던지는 것은 이 표면에서 최악의 실패다).
160
+ */
161
+ static preparePopover(container) {
162
+ if (typeof container.showPopover !== "function") return false;
163
+ container.setAttribute("popover", "manual");
164
+ container.style.inset = "auto";
165
+ container.style.margin = "0";
166
+ container.style.border = "none";
167
+ container.style.padding = "0";
168
+ container.style.background = "transparent";
169
+ container.style.color = "inherit";
170
+ container.style.overflow = "visible";
171
+ container.style.width = "auto";
172
+ container.style.height = "auto";
173
+ container.style.maxWidth = "none";
174
+ container.style.maxHeight = "none";
175
+ return true;
176
+ }
177
+ /** 연결된 뒤에만 부를 수 있다 — 실패하면 종전 동작(z-index 띠)으로 조용히 돌아간다. */
178
+ static showTopLayer(container) {
179
+ try {
180
+ container.showPopover();
181
+ } catch {
182
+ container.removeAttribute("popover");
183
+ }
184
+ }
128
185
  };
129
186
  //#endregion
130
187
  export { Toast };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 개발 모드 «사용 안내» 경고 — 오류 경로가 아닌, ***조용히 틀리는*** 상태를 한 번 알린다.
3
+ *
4
+ * 이 패키지에는 같은 모양의 경고가 이미 있다(`UElement.warnIfTokensMissing` — 토큰 시트 부재).
5
+ * 공통 규약: `[@iyulab/components]` 네임스페이스 · 개발 모드에서만(`import.meta.env.DEV`) ·
6
+ * 같은 키로는 **한 번만** · 무엇이 틀렸는지가 아니라 **무엇을 하면 되는지**를 적는다.
7
+ *
8
+ * 이 파일이 생긴 계기: 소비앱의 메뉴 30개가 해석되지 않는 아이콘 이름으로 **한꺼번에 같은
9
+ * 큐브 폴백**을 그렸는데 신호가 0 이었다(docket #265 R3). 폴백은 의도된 것이라(접힌 사이드바에서
10
+ * 아이콘이 없으면 메뉴가 «높이만 있는 빈 줄» 이 된다) 제거가 답이 아니다 — **없는 것은 경고다.**
11
+ * 같은 부류가 셋 더 있었다: 높이 제약이 없어 18px 로 붕괴하는 `u-split-panel`, 가상화가 꺼지는
12
+ * `flex-table`, 133px 로 앉는 `modern-app` 셸 — 모두 «에러 없이 틀린 화면» 이다(HD-61 ⒝).
13
+ */
14
+ /** `key` 당 한 번만, 개발 모드에서만 `console.warn` 한다. 프로덕션 빌드에서는 호출 자체가 no-op 이다. */
15
+ export declare function devWarnOnce(key: string, message: string): void;
16
+ /** 테스트용 — 가드를 비운다. */
17
+ export declare function resetDevWarnings(): void;
@@ -0,0 +1,5 @@
1
+ //#region src/utilities/devWarning.ts
2
+ /** `key` 당 한 번만, 개발 모드에서만 `console.warn` 한다. 프로덕션 빌드에서는 호출 자체가 no-op 이다. */
3
+ function devWarnOnce(key, message) {}
4
+ //#endregion
5
+ export { devWarnOnce };
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.39.0",
4
+ "version": "1.40.1",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "components",
@@ -83,16 +83,16 @@
83
83
  }
84
84
  },
85
85
  "devDependencies": {
86
- "@eslint/js": "^9.39.4",
86
+ "@eslint/js": "^10.0.1",
87
87
  "@types/node": "^26.1.1",
88
88
  "@types/react": "^19.2.14",
89
89
  "@vitest/browser-playwright": "^4.1.10",
90
- "eslint": "^9.39.4",
90
+ "eslint": "^10.1.0",
91
91
  "eslint-plugin-lit": "^2.3.1",
92
92
  "globals": "^17.7.0",
93
93
  "happy-dom": "^20.10.6",
94
94
  "react": "^19.2.7",
95
- "typescript": "^5.9.3",
95
+ "typescript": "^6.0.2",
96
96
  "typescript-eslint": "^8.64.0",
97
97
  "vite": "^8.1.4",
98
98
  "vite-plugin-dts": "^5.0.3",
@@ -25,6 +25,25 @@ For programmatic usage (`Dialog.alert`, `Dialog.confirm`, `Dialog.prompt`), see
25
25
 
26
26
  ---
27
27
 
28
+ ## Sizing
29
+
30
+ The panel is **content-sized, capped at 90%** of the dialog's own box — 90% of the height and of
31
+ the width. Measured in an 896px-tall viewport: a short dialog renders a 235px panel, and the same
32
+ dialog carrying 3,000px of body content stops at 806px.
33
+
34
+ Past that cap the **body is the only scroll region**. The `header` and `footer` slots are
35
+ `flex-shrink: 0`, so the title and the actions stay visible however long the content grows.
36
+
37
+ There is no size property. To make the dialog smaller than 90% of the viewport, give the **host** a
38
+ height — that is what the 90% is measured against:
39
+
40
+ ```css
41
+ u-dialog { height: 400px; } /* the panel then caps at 360px */
42
+ ```
43
+
44
+ ⚠ That lever only shrinks: a height on the host lowers the panel's maximum, it does not reserve a
45
+ panel of that size. A short dialog stays short.
46
+
28
47
  ## Slots
29
48
 
30
49
  | Name | Description |
@@ -81,6 +81,23 @@ for a KRW amount) is the consumer's call — the library does not infer one from
81
81
  | `input` | Fires on every keystroke |
82
82
  | `change` | Fires when value is committed |
83
83
 
84
+ ## Form submission
85
+
86
+ Pressing Enter in the field submits its `<form>`, as in a native text field (HTML implicit
87
+ submission): the form's first submit button is activated — `<button type="submit">` or
88
+ `<u-button type="submit">`; a disabled one does nothing — and with no submit button the form is
89
+ submitted only when it has no other single-line field. Enter that confirms an IME composition,
90
+ Enter with a modifier key, and Enter on a highlighted suggestion (`u-option`) do not submit.
91
+ Call `preventDefault()` on the `keydown` to cancel it. Do not also submit on Enter yourself.
92
+
93
+ ```html
94
+ <form @submit=${onSubmit}>
95
+ <u-input name="user" required></u-input>
96
+ <u-input name="password" type="password" required></u-input>
97
+ <u-button type="submit">Sign in</u-button>
98
+ </form>
99
+ ```
100
+
84
101
  ## Methods
85
102
 
86
103
  | Method | Description |
@@ -26,6 +26,18 @@ General-purpose content panel. Matches a tab or tree node when using `value`.
26
26
 
27
27
  ---
28
28
 
29
+ ## Sizing
30
+
31
+ `u-panel` has `overflow: auto` and no height of its own, so it behaves two ways — both intended:
32
+
33
+ - **No height** — it grows with its content. Nothing is clipped and no scrollbar appears.
34
+ - **A height (or a height-bearing parent)** — it becomes the scroll container for its content.
35
+
36
+ ```html
37
+ <u-panel style="height: 240px">…</u-panel> <!-- scrolls its content -->
38
+ <u-panel>…</u-panel> <!-- grows with its content -->
39
+ ```
40
+
29
41
  ## Slots
30
42
 
31
43
  | Name | Description |
@@ -70,6 +70,15 @@ Dropdown select with single or multiple selection, search, and clear support. Fo
70
70
  | `focus(options?)` | Focus the trigger |
71
71
  | `blur()` | Blur the trigger |
72
72
 
73
+ ## Keyboard & accessibility
74
+
75
+ The trigger is a `role="combobox"`. Without `searchable` the dropdown holds a `role="listbox"`
76
+ and opening it focuses the first option. With `searchable` the dropdown is a `role="dialog"`
77
+ (named by `label`, or "Search") that holds the search field and the listbox; opening it focuses
78
+ the search field, so typing filters at once. From the search field `ArrowDown`/`ArrowUp` move to
79
+ the first/last visible option; on an option, arrows, `Home`/`End`, `Enter`/`Space` (select) and
80
+ `Escape` (close) work as usual.
81
+
73
82
  ## CSS Parts
74
83
 
75
84
  | Part | Description |
@@ -84,7 +84,7 @@ Range slider. Single-thumb or dual-thumb (range) mode. Supports marks, value dis
84
84
  | `--slider-fill-color` | Track fill color |
85
85
  | `--slider-track-height` | Track height |
86
86
  | `--slider-track-color` | Track background color |
87
- | `--slider-thumb-size` | Thumb diameter |
87
+ | `--slider-thumb-size` | Thumb diameter (**visible circle only** — the pointer target keeps a 24x24 floor per WCAG 2.2 SC 2.5.8, so lowering this does not shrink it) |
88
88
  | `--slider-thumb-color` | Thumb fill color |
89
89
  | `--slider-thumb-border-color` | Thumb border color |
90
90
  | `--slider-mark-size` | Mark dot size |
@@ -15,7 +15,7 @@ Resizable two-panel layout. Drag the splitter to adjust panel sizes.
15
15
  </u-split-panel>
16
16
 
17
17
  <!-- Vertical split -->
18
- <u-split-panel orientation="vertical" default-ratio="[30,70]">
18
+ <u-split-panel orientation="vertical" default-ratio="30,70" style="height: 400px;">
19
19
  <div>Top panel</div>
20
20
  <div>Bottom panel</div>
21
21
  </u-split-panel>
@@ -30,6 +30,27 @@ Resizable two-panel layout. Drag the splitter to adjust panel sizes.
30
30
 
31
31
  ---
32
32
 
33
+ ## Sizing
34
+
35
+ **Give the host a height.** This is a shell: `:host` is `height: 100%` with `overflow: hidden`,
36
+ so the panels divide whatever height the host has — and an unconstrained host has none to divide.
37
+
38
+ ```html
39
+ <u-split-panel style="height: 400px">…</u-split-panel>
40
+ ```
41
+
42
+ ⚠ Leave the height off — or nest the component in a container that has no height of its own — and
43
+ it collapses to a thin strip (measured: 18px, with 900px-tall panel content), the content is
44
+ clipped by `overflow: hidden`, and there is no scrollbar to reach it. No error, nothing in the
45
+ console. Every example below assumes a height on the host for this reason.
46
+
47
+ A height-bearing ancestor works the same way (measured):
48
+
49
+ ```css
50
+ .page { height: 100%; display: flex; flex-direction: column; }
51
+ .page u-split-panel { flex: 1 1 auto; min-height: 0; }
52
+ ```
53
+
33
54
  ## Slots
34
55
 
35
56
  | Name | Description |
@@ -42,7 +63,7 @@ Resizable two-panel layout. Drag the splitter to adjust panel sizes.
42
63
  | Property | Type | Default | Reflect | Description |
43
64
  |----------|------|---------|---------|-------------|
44
65
  | `orientation` | `'horizontal'\|'vertical'` | `'horizontal'` | ✓ | Split direction |
45
- | `defaultRatio` | `number[]` | `[]` | ✓ | Initial size ratios (e.g. `[30, 70]`) |
66
+ | `defaultRatio` | `number[]` | `[]` | ✓ | Initial size ratios. Attribute form is comma-separated, without brackets: `default-ratio="30,70"`. Ratios are shares of the space between the handles |
46
67
  | `ratio` | `number[]` | `[]` | ✓ | Current size ratios |
47
68
  | `disabled` | `boolean` | `false` | ✓ | Prevent resizing |
48
69
  | `lazy` | `boolean` | `false` | ✓ | Show preview while dragging, commit on release |
@@ -55,11 +76,28 @@ Resizable two-panel layout. Drag the splitter to adjust panel sizes.
55
76
  | `shift` | Dragging in progress |
56
77
  | `shift-end` | Drag ends |
57
78
 
79
+ ## Keyboard & Accessibility
80
+
81
+ Each handle is a focusable `role="separator"` following the WAI-ARIA APG *Window Splitter*
82
+ pattern. Its value is the share of the panel **before** it (`aria-valuenow`, `0` to the combined
83
+ share of the two panels it sits between — `100` with two panels), and it is named by the
84
+ `resizePanels` locale message.
85
+
86
+ | Key | Effect |
87
+ |-----|--------|
88
+ | `←` / `→` (horizontal) · `↑` / `↓` (vertical) | Shrink / grow the panel before the handle by 5% |
89
+ | `Home` / `End` | Give the panel before the handle its smallest / largest size |
90
+ | `Enter` | Collapse the panel before the handle; press again to restore its previous size |
91
+
92
+ In a right-to-left layout `←` still moves the handle left. A keyboard change fires `shift-start`,
93
+ `shift` and `shift-end` in turn, so listening to `shift-end` alone covers both pointer and keyboard.
94
+
58
95
  ## CSS Custom Properties
59
96
 
60
97
  | Property | Description |
61
98
  |----------|-------------|
62
- | `--splitter-size` | Splitter handle thickness |
99
+ | `--splitter-size` | Thickness of the visible line (default `4px`) |
100
+ | `--splitter-hit-size` | Thickness of the area that takes the pointer (default `24px`, WCAG 2.5.8). It occupies layout space between the panels, so it never covers a panel's scrollbar. The handle is the larger of the two; set it to `var(--splitter-size)` for a flush handle |
63
101
  | `--splitter-color` | Splitter default color |
64
102
  | `--splitter-color-hover` | Splitter color on hover |
65
103
  | `--splitter-color-active` | Splitter color while dragging |
@@ -32,6 +32,24 @@ Tab-based content switcher. Pair each `u-tab` with a `u-panel` of the same `valu
32
32
 
33
33
  ## u-tab-panel
34
34
 
35
+ ### Sizing
36
+
37
+ `u-tab-panel` is a flex shell with no height of its own, so it behaves two ways — both intended:
38
+
39
+ - **No height** — it grows with the active panel's content; nothing is clipped and no scrollbar
40
+ appears (measured: a 900px panel under a `top` tab bar gives a 935px-tall component — the 34px bar
41
+ plus the content).
42
+ - **A height, or a height-bearing parent** — the **content area** becomes the scroll region and the
43
+ tab bar keeps its place (measured: 300px tall → a 265px content area with 635px to scroll).
44
+
45
+ ```html
46
+ <u-tab-panel style="height: 300px">…</u-tab-panel>
47
+ ```
48
+
49
+ The tab bar scrolls on its own axis when the tabs overflow it — horizontally for `placement="top"`
50
+ and `"bottom"`, vertically for `"left"` and `"right"` — so a long tab list never pushes the content
51
+ out of the component.
52
+
35
53
  ### Slots
36
54
 
37
55
  | Name | Description |