@iyulab/components 1.8.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/components/UFloatingElement.d.ts +5 -0
  3. package/dist/components/UFloatingElement.js +10 -3
  4. package/dist/components/UFloatingElement.styles.js +10 -0
  5. package/dist/components/input/UInput.js +2 -0
  6. package/dist/components/option/UOption.d.ts +2 -0
  7. package/dist/components/option/UOption.js +9 -0
  8. package/dist/components/popover/UPopover.d.ts +29 -2
  9. package/dist/components/popover/UPopover.js +23 -0
  10. package/dist/components/radio/URadio.js +5 -1
  11. package/dist/components/rating/URating.js +6 -1
  12. package/dist/components/select/USelect.d.ts +4 -0
  13. package/dist/components/select/USelect.js +14 -1
  14. package/dist/components/slider/USlider.js +8 -0
  15. package/dist/components/textarea/UTextarea.js +2 -0
  16. package/dist/react/UAlert.d.ts +4 -1
  17. package/dist/react/UAvatar.d.ts +4 -1
  18. package/dist/react/UBadge.d.ts +4 -1
  19. package/dist/react/UBreadcrumb.d.ts +4 -1
  20. package/dist/react/UBreadcrumbItem.d.ts +4 -1
  21. package/dist/react/UButton.d.ts +4 -1
  22. package/dist/react/UButtonGroup.d.ts +4 -1
  23. package/dist/react/UCard.d.ts +4 -1
  24. package/dist/react/UCarousel.d.ts +4 -1
  25. package/dist/react/UCheckbox.d.ts +4 -1
  26. package/dist/react/UChip.d.ts +4 -1
  27. package/dist/react/UCopyButton.d.ts +4 -1
  28. package/dist/react/UDialog.d.ts +4 -1
  29. package/dist/react/UDivider.d.ts +4 -1
  30. package/dist/react/UDrawer.d.ts +4 -1
  31. package/dist/react/UField.d.ts +4 -1
  32. package/dist/react/UForm.d.ts +4 -1
  33. package/dist/react/UIcon.d.ts +4 -1
  34. package/dist/react/UIconButton.d.ts +4 -1
  35. package/dist/react/UInput.d.ts +4 -1
  36. package/dist/react/UMenu.d.ts +4 -1
  37. package/dist/react/UMenuItem.d.ts +4 -1
  38. package/dist/react/UOption.d.ts +4 -1
  39. package/dist/react/UPanel.d.ts +4 -1
  40. package/dist/react/UPopover.d.ts +4 -1
  41. package/dist/react/UProgressBar.d.ts +4 -1
  42. package/dist/react/UProgressRing.d.ts +4 -1
  43. package/dist/react/URadio.d.ts +4 -1
  44. package/dist/react/URating.d.ts +4 -1
  45. package/dist/react/USelect.d.ts +4 -1
  46. package/dist/react/USkeleton.d.ts +4 -1
  47. package/dist/react/USlider.d.ts +4 -1
  48. package/dist/react/USpinner.d.ts +4 -1
  49. package/dist/react/USplitPanel.d.ts +4 -1
  50. package/dist/react/USwitch.d.ts +4 -1
  51. package/dist/react/UTab.d.ts +4 -1
  52. package/dist/react/UTabPanel.d.ts +4 -1
  53. package/dist/react/UTag.d.ts +4 -1
  54. package/dist/react/UTextarea.d.ts +4 -1
  55. package/dist/react/UTooltip.d.ts +4 -1
  56. package/dist/react/UTree.d.ts +4 -1
  57. package/dist/react/UTreeItem.d.ts +4 -1
  58. package/package.json +4 -2
  59. package/plugins/vite-plugin-react-wrapper.js +18 -1
  60. package/skills/iyulab-components/references/components/popover.md +17 -1
  61. package/skills/iyulab-components/references/extensions/floating.md +11 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.9.0] - 2026-07-28
4
+
5
+ ### Fixed
6
+ - **열린 팝오버가 페이지 스크롤 한 번에 닫히던 결함 수정** (u-select listbox·u-input combobox 실사용 및 E2E 구동 불능 — U-Platform 실측, ISSUE-node-packages-20260724-uselect-playwright-driveability). `UPopover.dismiss` 기본값의 `scroll` 이 document 캡처 단계로 등록되는 한편 `UFloatingElement.show()` 는 **모든** 플로팅 엘리먼트에 floating-ui `autoUpdate`(스크롤 시 앵커 추종 재배치)를 설치한다 — 같은 스크롤 이벤트에 재배치와 닫기가 동시에 걸리고 닫기가 이기는 자기모순이었다. 이제 `scroll` 은 **앵커가 스크롤로 의미를 잃을 때만** 닫는다: 실제 엘리먼트에 앵커된 팝오버(드롭다운·제안 목록·서브메뉴·툴팁)는 재배치되어 열린 채 유지되고, `trigger="contextmenu"` 의 좌표 기반 가상 앵커는 종전대로 닫힌다. Playwright 의 "scroll into view" 가 목록을 닫아 `getByRole('option').click()` 이 완주하지 못하던 문제도 함께 해소된다. 회귀 테스트 3건 추가.
7
+ - **`dismiss: 'resize'` 도 같은 원칙으로 정합** — `autoUpdate` 는 `ancestorResize` 로 window 리사이즈도 구독해 재배치하므로 scroll 과 동일한 자기모순이었다. 이제 리사이즈도 좌표 기반 가상 앵커에서만 닫는다. 이로써 모바일에서 `u-select searchable` 의 검색 입력을 탭할 때 가상 키보드가 레이아웃 뷰포트를 줄이며 발생시키는 리사이즈로 드롭다운이 닫히던 문제가 해소된다. 회귀 테스트 2건 추가.
8
+
9
+ - **React 래퍼가 `ref` 를 타입 수준에서 거부하던 문제 수정** — 생성된 `.d.ts` 가 `React.ForwardRefExoticComponent<P>` 를 쓰면서 P 에 `React.RefAttributes` 를 넣지 않았다. `ForwardRefExoticComponent<P>` 는 ref 를 자동으로 더해 주지 않으므로, `@lit/react` 가 런타임에 ref 를 정상 전달하는데도(`ReactWebComponent` 자신은 `PropsWithoutRef<…> & React.RefAttributes<I>` 로 선언돼 있다) `<UButton ref={…} />` 가 TS2322 로 거부됐다. 이제 42개 래퍼 전부가 ref 를 받으며, ref 타입은 `HTMLElement` 가 아니라 **해당 엘리먼트 클래스**로 좁혀진다. 신설 React 타입 스모크가 첫 실행에서 잡아낸 결함이다.
10
+
11
+ ### Added
12
+ - **React strict 소비 타입 스모크** — 소비자가 React 19 + TS strict 에서 겪은 래퍼 결함들(JSX children TS2747, 이벤트 핸들러 교집합, 위 ref 누락)은 전부 **런타임에 드러나지 않는 타입 실패**라 vitest 로는 잡히지 않는다. `tests/types/react-consumption.tsx` 를 두고 `tsconfig.react-smoke.json` 전용 프로젝트로 컴파일하며, 검증 대상이 빌드 산출물(`dist/react/*.d.ts`)이므로 `build` 파이프라인 끝에 건다(`npm run build` 에 포함). 소비자와 동일한 진입점 `@iyulab/components/react` 를 통과시켜 `exports` 맵까지 함께 검증한다.
13
+ - **앵커 이탈 시 팝오버 자동 숨김** — 위 수정으로 실앵커 팝오버가 스크롤에 닫히지 않게 되면서 드러난 케이스 대응. `strategy="fixed"` 팝오버는 overflow 조상에 클립되지 않으므로(그것이 fixed 를 쓰는 이유), 앵커가 스크롤 패널 밖으로 나가면 팝오버만 화면에 남아 무관한 콘텐츠를 덮을 수 있었다. floating-ui `hide` middleware 를 `UFloatingElement.reposition()` 에 도입해, 앵커가 클리핑 영역(스크롤 컨테이너·뷰포트)을 완전히 벗어나면 `anchor-hidden` 속성이 붙고 엘리먼트가 숨겨진다. **닫히지는 않으므로**(`open` 유지) 되돌려 스크롤하면 열린 상태 그대로 복귀한다. `anchor-hidden` 은 읽기 전용 파생 상태이며 스타일 훅으로 사용할 수 있다. 회귀 테스트 2건 추가.
14
+
15
+ ## [1.8.1] - 2026-07-24
16
+
17
+ ### Fixed
18
+ - **u-field 합성 폼 컨트롤이 접근성 트리에서 이름 없이 노출되던 문제 수정** (WCAG 1.3.1·4.1.2 — U-Platform T8-1 Playwright E2E 실측, ISSUE-20260723-uinput-label-a11y). 라벨은 `u-field` 의 별도 섀도 스코프에 렌더되어 `label[for]` 로 컨트롤과 연결될 수 없었고, 섀도 경계 탓에 cross-root `aria-labelledby` 도 현 브라우저에서 신뢰성 있게 배송되지 않는다. 이제 각 컴포넌트가 자신의 접근 이름 호스트에 라벨을 `aria-label`(설명은 `aria-description`)로 미러링한다 — `u-input`/`u-textarea`(네이티브 컨트롤), `u-rating`/`u-radio`(`role=radiogroup`), `u-select`(`role=combobox` + `aria-expanded`/`aria-haspopup`/`aria-controls`), `u-slider`(`role=slider` + `aria-valuenow`/`valuemin`/`valuemax`/`valuetext`). 아울러 `u-option` 이 사용 맥락에 맞는 자식 역할·상태를 노출한다 — radiogroup(`marker='radio'`) 안에서는 `role=radio` + `aria-checked`, listbox(`u-select`/`u-input` combobox) 에서는 `role=option` + `aria-selected`(+ `aria-disabled`) — 이름만 있고 비어 보이던 radiogroup/combobox 위젯이 자식까지 정합하게 읽힌다. `u-rating` 심볼(`role=radio`)도 커밋 값 기준 `aria-checked` 를 노출한다. `getByLabel`/`getByRole({name})` 로케이터와 스크린리더가 컨트롤을 이름으로 인식한다.
19
+ - **React 래퍼 `.d.ts` 타이핑 2건 수정** (React 19 + TS strict 컴파일 차단 — U-CMMS 실측, ISSUE-20260722-react-wrapper-typing). (1) `React.HTMLAttributes` 의 이벤트 핸들러(`onChange` 등)와 래퍼의 CustomEvent 시그니처가 교집합되어 어떤 핸들러도 대입 불가였던 것을, HTMLAttributes 쪽 동명 이벤트 키를 `Omit` 해 CustomEvent 시그니처만 남기도록 교정. (2) `Partial<Element>` 의 DOM `children: HTMLCollection` 이 JSX children 을 가려 `<UButton>text</UButton>` 이 TS2747 로 실패하던 것을, `Omit<Partial<Element>, keyof HTMLAttributes>` 로 DOM 전용 키를 제거하고 React 친화 타입(`children: ReactNode`)이 HTMLAttributes 에서 공급되도록 교정. 소비자의 `as unknown as ComponentType` 우회 제거 가능.
20
+
3
21
  ## [1.8.0] - 2026-07-22
4
22
 
5
23
  ### Fixed
@@ -8,6 +8,11 @@ export type FloatingStrategy = 'absolute' | 'fixed';
8
8
  *
9
9
  * @event show - 엘리먼트가 표시되기 전에 발생합니다. 이벤트 핸들러에서 false를 반환하면 표시가 취소됩니다.
10
10
  * @event hide - 엘리먼트가 숨겨지기 전에 발생합니다. 이벤트 핸들러에서 false를 반환하면 숨김이 취소됩니다.
11
+ *
12
+ * @attr {boolean} anchor-hidden - **읽기 전용 파생 상태**(설정하지 마세요). 앵커가 클리핑
13
+ * 영역(스크롤 컨테이너·뷰포트) 밖으로 완전히 벗어난 동안 자동으로 붙습니다. 이때 엘리먼트는
14
+ * 숨겨지지만 **닫히지는 않으며**(`open` 유지), 앵커가 다시 보이면 그대로 복귀합니다.
15
+ * 스타일 훅으로 사용할 수 있습니다: `my-popover[anchor-hidden] { … }`
11
16
  */
12
17
  export declare class UFloatingElement extends UElement {
13
18
  static styles: CSSResultGroup;
@@ -4,14 +4,19 @@ import { styles } from "./UFloatingElement.styles.js";
4
4
  import __decorateMetadata from "../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/decorateMetadata.js";
5
5
  import __decorate from "../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/decorate.js";
6
6
  import { property, state } from "lit/decorators.js";
7
- import { arrow, autoPlacement, autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
7
+ import { arrow, autoPlacement, autoUpdate, computePosition, flip, hide, offset, shift } from "@floating-ui/dom";
8
8
  //#region src/components/UFloatingElement.ts
9
9
  /**
10
10
  * UFloatingElement는 팝오버, 툴팁 등 화면에 떠 있는 엘리먼트를 구현하기 위한 기본 클래스입니다.
11
11
  * 이 클래스를 상속하여 커스텀 팝오버 컴포넌트를 만들 수 있습니다.
12
- *
12
+ *
13
13
  * @event show - 엘리먼트가 표시되기 전에 발생합니다. 이벤트 핸들러에서 false를 반환하면 표시가 취소됩니다.
14
14
  * @event hide - 엘리먼트가 숨겨지기 전에 발생합니다. 이벤트 핸들러에서 false를 반환하면 숨김이 취소됩니다.
15
+ *
16
+ * @attr {boolean} anchor-hidden - **읽기 전용 파생 상태**(설정하지 마세요). 앵커가 클리핑
17
+ * 영역(스크롤 컨테이너·뷰포트) 밖으로 완전히 벗어난 동안 자동으로 붙습니다. 이때 엘리먼트는
18
+ * 숨겨지지만 **닫히지는 않으며**(`open` 유지), 앵커가 다시 보이면 그대로 복귀합니다.
19
+ * 스타일 훅으로 사용할 수 있습니다: `my-popover[anchor-hidden] { … }`
15
20
  */
16
21
  var UFloatingElement = class extends UElement {
17
22
  constructor(..._args) {
@@ -134,7 +139,8 @@ var UFloatingElement = class extends UElement {
134
139
  offset(this.offset),
135
140
  shift({ mainAxis: this.shift }),
136
141
  this.placement ? flip() : autoPlacement(),
137
- ...this.arrowEl ? [arrow({ element: this.arrowEl })] : []
142
+ ...this.arrowEl ? [arrow({ element: this.arrowEl })] : [],
143
+ hide()
138
144
  ]
139
145
  });
140
146
  const config = {
@@ -164,6 +170,7 @@ var UFloatingElement = class extends UElement {
164
170
  top: `${position.y}px`,
165
171
  transformOrigin: config.transform
166
172
  });
173
+ this.toggleAttribute("anchor-hidden", !!position.middlewareData.hide?.referenceHidden);
167
174
  const arrowData = position.middlewareData.arrow;
168
175
  if (!this.arrowEl || !arrowData) return;
169
176
  Object.assign(this.arrowEl.style, {
@@ -34,6 +34,16 @@ var styles = css`
34
34
  position: fixed;
35
35
  }
36
36
 
37
+ /* 앵커가 클리핑 영역(스크롤 컨테이너·뷰포트) 밖으로 나가 참조를 잃은 동안은 숨긴다.
38
+ 닫지는 않는다 — 앵커가 다시 보이면 열린 상태 그대로 복귀한다.
39
+ 특히 strategy="fixed" 는 overflow 조상에 클립되지 않으므로(그것이 fixed 를 쓰는 이유),
40
+ 이 규칙이 없으면 앵커가 스크롤 패널 밖으로 나간 뒤에도 팝오버만 화면에 남는다.
41
+ :host([open]) 과 명시도가 같으므로 반드시 그 뒤에 와야 이긴다. */
42
+ :host([anchor-hidden]) {
43
+ visibility: hidden;
44
+ pointer-events: none;
45
+ }
46
+
37
47
  /* 화살표: clip-path로 삼각형 */
38
48
  #arrow {
39
49
  position: absolute;
@@ -148,6 +148,8 @@ var UInput = class UInput extends UFormControlElement {
148
148
 
149
149
  <input part="input"
150
150
  type=${this.type === "password" && this.showPassword ? "text" : this.type}
151
+ aria-label=${ifDefined(this.label)}
152
+ aria-description=${ifDefined(this.description)}
151
153
  name=${ifDefined(this.name)}
152
154
  ?required=${this.required}
153
155
  ?disabled=${this.disabled}
@@ -1,3 +1,4 @@
1
+ import { PropertyValues } from 'lit';
1
2
  import { UElement } from '../UElement.js';
2
3
  export type OptionMarker = 'radio' | 'check';
3
4
  /**
@@ -31,6 +32,7 @@ export declare class UOption extends UElement {
31
32
  /** 옵션 고유값 */
32
33
  value: string;
33
34
  connectedCallback(): void;
35
+ protected updated(changed: PropertyValues): void;
34
36
  render(): import('lit-html').TemplateResult<1>;
35
37
  private renderMarker;
36
38
  /** 라벨(기본 slot)의 텍스트 노드만 추출한다 — 엘리먼트 자식(예: 설명용 `<p>`)은 제외. */
@@ -21,6 +21,15 @@ var UOption = class UOption extends UElement {
21
21
  this.setAttribute("role", "option");
22
22
  this.setAttribute("tabindex", this.disabled ? "-1" : "0");
23
23
  }
24
+ updated(changed) {
25
+ super.updated(changed);
26
+ const isRadio = this.marker === "radio";
27
+ this.setAttribute("role", isRadio ? "radio" : "option");
28
+ this.removeAttribute(isRadio ? "aria-selected" : "aria-checked");
29
+ this.setAttribute(isRadio ? "aria-checked" : "aria-selected", String(this.selected));
30
+ this.setAttribute("aria-disabled", String(this.disabled));
31
+ this.setAttribute("tabindex", this.disabled ? "-1" : "0");
32
+ }
24
33
  render() {
25
34
  return html`
26
35
  ${this.renderMarker()}
@@ -29,8 +29,10 @@ export declare class UPopover extends UFloatingElement {
29
29
  * 팝오버를 닫는 조건 (쉼표 구분하여 복수 지정 가능)
30
30
  * - `click`: 외부 클릭 시 닫기
31
31
  * - `escape`: Escape 키 시 닫기
32
- * - `scroll`: 스크롤 닫기
33
- * - `resize`: 윈도우 리사이즈 닫기
32
+ * - `scroll` / `resize`: 뷰포트 기하 변화로 **앵커가 의미를 잃을 때** 닫기 — 즉
33
+ * `contextmenu` 처럼 좌표 기반 가상 앵커에 붙은 경우에만 닫는다. 실제 엘리먼트에 앵커된
34
+ * 팝오버는 `autoUpdate` 가 스크롤·리사이즈를 따라 재배치하므로 닫지 않는다
35
+ * (`isCoordinateAnchored` 참조).
34
36
  */
35
37
  dismiss: PopoverDismiss[];
36
38
  /** 닫기 조건에 포함시킬 이벤트 목록 (쉼표 구분) */
@@ -58,7 +60,32 @@ export declare class UPopover extends UFloatingElement {
58
60
  private cancelSafeTimer;
59
61
  private handleDocumentClick;
60
62
  private handleDocumentKeydown;
63
+ /**
64
+ * 현재 표시 중이며 앵커가 **좌표 기반 가상 엘리먼트**인지 여부.
65
+ *
66
+ * 뷰포트 기하 변화(스크롤·리사이즈)는 그 자체로 닫기 사유가 아니다. `UFloatingElement.show()`
67
+ * 가 모든 플로팅 엘리먼트에 `autoUpdate` 를 설치하고, `autoUpdate` 는 조상 스크롤과 리사이즈를
68
+ * 모두 구독해 재배치한다 — 여기서 hide 하면 재배치 로직과 정면으로 모순된다. 실제로 그 모순이
69
+ * 열린 listbox(u-select·u-input combobox)를 페이지 스크롤 한 번에 닫아버렸다.
70
+ * 앵커가 클리핑 영역 밖으로 나가는 경우는 `hide` middleware 가 별도로 처리한다(닫지 않고 숨김).
71
+ *
72
+ * 닫아야 하는 경우는 **앵커가 좌표라서 기하 변화에 의미를 잃는 경우**뿐이다 —
73
+ * `trigger="contextmenu"` 의 가상 앵커는 이벤트 발생 시점의 clientX/clientY 에 고정돼 있어
74
+ * 콘텐츠가 스크롤·리플로우되면 가리키던 대상과 어긋난다.
75
+ *
76
+ * `targetEl` 이 없으면 표시 중이 아니므로 닫을 것도 없다 — 이 가드가 없으면 닫힌 팝오버가
77
+ * 스크롤 이벤트마다 `hide()`(→ `await updateComplete`) 를 돌려 인스턴스 수만큼 churn 이 된다.
78
+ * 단 `show()` 는 showDelay 타이머보다 **먼저** `targetEl` 을 세우므로, 가상 앵커의 대기 중
79
+ * show 취소는 이 가드를 통과해 그대로 보존된다.
80
+ */
81
+ private get isCoordinateAnchored();
61
82
  private handleDocumentScroll;
83
+ /**
84
+ * 리사이즈도 스크롤과 같은 뷰포트 기하 변화다 — `autoUpdate` 가 `ancestorResize` 로 window
85
+ * 리사이즈를 구독해 재배치하므로 실앵커 팝오버를 닫을 이유가 없다. 닫으면 모바일에서
86
+ * `u-select searchable` 의 검색 입력을 탭하는 순간 가상 키보드가 레이아웃 뷰포트를 줄이며
87
+ * 리사이즈를 발생시켜 드롭다운이 닫혀버린다.
88
+ */
62
89
  private handleWindowResize;
63
90
  private handlePopoverEvent;
64
91
  }
@@ -87,9 +87,11 @@ var UPopover = class UPopover extends UFloatingElement {
87
87
  this.hide();
88
88
  };
89
89
  this.handleDocumentScroll = () => {
90
+ if (!this.isCoordinateAnchored) return;
90
91
  this.hide();
91
92
  };
92
93
  this.handleWindowResize = () => {
94
+ if (!this.isCoordinateAnchored) return;
93
95
  this.hide();
94
96
  };
95
97
  this.handlePopoverEvent = () => {
@@ -210,6 +212,27 @@ var UPopover = class UPopover extends UFloatingElement {
210
212
  clearTimeout(this.safeTimer);
211
213
  this.safeTimer = void 0;
212
214
  }
215
+ /**
216
+ * 현재 표시 중이며 앵커가 **좌표 기반 가상 엘리먼트**인지 여부.
217
+ *
218
+ * 뷰포트 기하 변화(스크롤·리사이즈)는 그 자체로 닫기 사유가 아니다. `UFloatingElement.show()`
219
+ * 가 모든 플로팅 엘리먼트에 `autoUpdate` 를 설치하고, `autoUpdate` 는 조상 스크롤과 리사이즈를
220
+ * 모두 구독해 재배치한다 — 여기서 hide 하면 재배치 로직과 정면으로 모순된다. 실제로 그 모순이
221
+ * 열린 listbox(u-select·u-input combobox)를 페이지 스크롤 한 번에 닫아버렸다.
222
+ * 앵커가 클리핑 영역 밖으로 나가는 경우는 `hide` middleware 가 별도로 처리한다(닫지 않고 숨김).
223
+ *
224
+ * 닫아야 하는 경우는 **앵커가 좌표라서 기하 변화에 의미를 잃는 경우**뿐이다 —
225
+ * `trigger="contextmenu"` 의 가상 앵커는 이벤트 발생 시점의 clientX/clientY 에 고정돼 있어
226
+ * 콘텐츠가 스크롤·리플로우되면 가리키던 대상과 어긋난다.
227
+ *
228
+ * `targetEl` 이 없으면 표시 중이 아니므로 닫을 것도 없다 — 이 가드가 없으면 닫힌 팝오버가
229
+ * 스크롤 이벤트마다 `hide()`(→ `await updateComplete`) 를 돌려 인스턴스 수만큼 churn 이 된다.
230
+ * 단 `show()` 는 showDelay 타이머보다 **먼저** `targetEl` 을 세우므로, 가상 앵커의 대기 중
231
+ * show 취소는 이 가드를 통과해 그대로 보존된다.
232
+ */
233
+ get isCoordinateAnchored() {
234
+ return !!this.targetEl && !(this.targetEl instanceof Element);
235
+ }
213
236
  };
214
237
  __decorate([property({
215
238
  type: Boolean,
@@ -7,6 +7,7 @@ import { UOption } from "../option/UOption.js";
7
7
  import { styles } from "./URadio.styles.js";
8
8
  import { html } from "lit";
9
9
  import { customElement, property, state } from "lit/decorators.js";
10
+ import { ifDefined } from "lit/directives/if-defined.js";
10
11
  //#region src/components/radio/URadio.ts
11
12
  var URadio = class URadio extends UFormControlElement {
12
13
  constructor(..._args) {
@@ -91,7 +92,10 @@ var URadio = class URadio extends UFormControlElement {
91
92
  .description=${this.description}
92
93
  .validationMessage=${this.validationMessage}
93
94
  >
94
- <div class="container" part="container">
95
+ <div class="container" part="container"
96
+ role="radiogroup"
97
+ aria-label=${ifDefined(this.label)}
98
+ aria-description=${ifDefined(this.description)}>
95
99
  <slot @slotchange=${this.handleSlotChange}></slot>
96
100
  </div>
97
101
  </u-field>
@@ -7,6 +7,7 @@ import "../field/UField.js";
7
7
  import { styles } from "./URating.styles.js";
8
8
  import { html } from "lit";
9
9
  import { customElement, property, state } from "lit/decorators.js";
10
+ import { ifDefined } from "lit/directives/if-defined.js";
10
11
  //#region src/components/rating/URating.ts
11
12
  var URating = class URating extends UFormControlElement {
12
13
  constructor(..._args) {
@@ -110,13 +111,17 @@ var URating = class URating extends UFormControlElement {
110
111
  .description=${this.description}
111
112
  .validationMessage=${this.validationMessage}
112
113
  >
113
- <div class="symbols" part="container" role="radiogroup">
114
+ <div class="symbols" part="container" role="radiogroup"
115
+ aria-label=${ifDefined(this.label)}
116
+ aria-description=${ifDefined(this.description)}>
114
117
  ${Array.from({ length: this.max }, (_, i) => {
115
118
  const score = i + 1;
116
119
  const fill = Math.max(0, Math.min(1, value - score + 1));
117
120
  return html`
118
121
  <span class="symbol" part="symbol"
119
122
  role="radio"
123
+ aria-checked=${score === Math.round(this.value || 0) ? "true" : "false"}
124
+ aria-disabled=${this.interactive ? "false" : "true"}
120
125
  tabindex=${this.interactive ? "0" : "-1"}
121
126
  data-score=${score}
122
127
  @pointermove=${this.handleSymbolPointerMove}
@@ -48,6 +48,10 @@ export declare class USelect extends UFormControlElement<string | string[]> {
48
48
  containerEl?: HTMLElement;
49
49
  popoverEl?: UPopover;
50
50
  private options;
51
+ /** aria-expanded 배선용 팝오버 열림 상태 */
52
+ private open;
53
+ /** combobox → listbox 연결용 고유 id (aria-controls) */
54
+ private readonly listboxId;
51
55
  protected shouldValidate(changed: PropertyValues): boolean;
52
56
  private get valueAsString();
53
57
  private get valueAsArray();
@@ -10,6 +10,7 @@ import { UPopover } from "../popover/UPopover.js";
10
10
  import { styles } from "./USelect.styles.js";
11
11
  import { html } from "lit";
12
12
  import { customElement, property, query, state } from "lit/decorators.js";
13
+ import { ifDefined } from "lit/directives/if-defined.js";
13
14
  //#region src/components/select/USelect.ts
14
15
  var USelect = class USelect extends UFormControlElement {
15
16
  constructor(..._args) {
@@ -20,6 +21,8 @@ var USelect = class USelect extends UFormControlElement {
20
21
  this.clearable = false;
21
22
  this.loading = false;
22
23
  this.options = [];
24
+ this.open = false;
25
+ this.listboxId = `u-select-listbox-${Math.random().toString(36).slice(2, 8)}`;
23
26
  this.handleSlotChange = (e) => {
24
27
  this.cleanup(this.options);
25
28
  const slot = e.target;
@@ -158,7 +161,13 @@ var USelect = class USelect extends UFormControlElement {
158
161
  ${this.valueAsArray.length} / ${this.maxCount}
159
162
  </span>
160
163
 
161
- <div class="container" part="container" tabindex=${this.disabled ? "-1" : "0"}>
164
+ <div class="container" part="container" tabindex=${this.disabled ? "-1" : "0"}
165
+ role="combobox"
166
+ aria-label=${ifDefined(this.label)}
167
+ aria-description=${ifDefined(this.description)}
168
+ aria-haspopup="listbox"
169
+ aria-expanded=${this.open}
170
+ aria-controls=${this.listboxId}>
162
171
  <slot name="prefix"></slot>
163
172
  <slot name="display">${this.renderContent()}</slot>
164
173
  <slot name="suffix"></slot>
@@ -181,11 +190,14 @@ var USelect = class USelect extends UFormControlElement {
181
190
  </u-field>
182
191
 
183
192
  <u-popover part="popover"
193
+ id=${this.listboxId}
184
194
  role="listbox"
185
195
  scrollable
186
196
  autofocus
187
197
  for=".container"
188
198
  trigger="click"
199
+ @show=${() => this.open = true}
200
+ @hide=${() => this.open = false}
189
201
  strategy="fixed"
190
202
  placement="bottom-start"
191
203
  offset="1"
@@ -322,6 +334,7 @@ __decorate([property({ converter: { fromAttribute: (value) => {
322
334
  __decorate([query(".container", true), __decorateMetadata("design:type", typeof HTMLElement === "undefined" ? Object : HTMLElement)], USelect.prototype, "containerEl", void 0);
323
335
  __decorate([query("u-popover", true), __decorateMetadata("design:type", typeof UPopover === "undefined" ? Object : UPopover)], USelect.prototype, "popoverEl", void 0);
324
336
  __decorate([state(), __decorateMetadata("design:type", Array)], USelect.prototype, "options", void 0);
337
+ __decorate([state(), __decorateMetadata("design:type", Boolean)], USelect.prototype, "open", void 0);
325
338
  USelect = __decorate([customElement("u-select")], USelect);
326
339
  //#endregion
327
340
  export { USelect };
@@ -7,6 +7,7 @@ import "../field/UField.js";
7
7
  import { styles } from "./USlider.styles.js";
8
8
  import { html } from "lit";
9
9
  import { customElement, property, query, state } from "lit/decorators.js";
10
+ import { ifDefined } from "lit/directives/if-defined.js";
10
11
  //#region src/components/slider/USlider.ts
11
12
  var USlider = class USlider extends UFormControlElement {
12
13
  constructor(..._args) {
@@ -217,6 +218,13 @@ var USlider = class USlider extends UFormControlElement {
217
218
  return html`
218
219
  <div class="thumb" part=${isEnd ? "thumb-end" : "thumb"}
219
220
  role="slider"
221
+ aria-label=${ifDefined(this.label)}
222
+ aria-valuenow=${val}
223
+ aria-valuemin=${this.min}
224
+ aria-valuemax=${this.max}
225
+ aria-valuetext=${this.formatValue(val)}
226
+ aria-orientation="horizontal"
227
+ aria-disabled=${this.disabled ? "true" : "false"}
220
228
  ?hidden=${isEnd && !this.range}
221
229
  style="left: ${pct}%"
222
230
  tabindex=${this.disabled || isEnd && !this.range ? -1 : 0}
@@ -56,6 +56,8 @@ var UTextarea = class UTextarea extends UFormControlElement {
56
56
  <div class="container" part="container">
57
57
  <textarea part="textarea"
58
58
  scrollable
59
+ aria-label=${ifDefined(this.label)}
60
+ aria-description=${ifDefined(this.description)}
59
61
  ?required=${this.required}
60
62
  ?disabled=${this.disabled}
61
63
  ?readonly=${this.readonly}
@@ -4,7 +4,10 @@ import { type ShowEventDetail } from '../events/ShowEvent';
4
4
  import { type HideEventDetail } from '../events/HideEvent';
5
5
 
6
6
  export declare const UAlert: React.ForwardRefExoticComponent<
7
- Partial<UAlertElement> & React.HTMLAttributes<UAlertElement> & {
7
+ Omit<Partial<UAlertElement>, keyof React.HTMLAttributes<UAlertElement>>
8
+ & Omit<React.HTMLAttributes<UAlertElement>, 'onShow' | 'onHide'>
9
+ & React.RefAttributes<UAlertElement>
10
+ & {
8
11
  onShow?: (event: CustomEvent<ShowEventDetail>) => void;
9
12
  onHide?: (event: CustomEvent<HideEventDetail>) => void;
10
13
  }
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UAvatar as UAvatarElement } from '../components/avatar/UAvatar';
3
3
 
4
4
  export declare const UAvatar: React.ForwardRefExoticComponent<
5
- Partial<UAvatarElement> & React.HTMLAttributes<UAvatarElement> & {
5
+ Omit<Partial<UAvatarElement>, keyof React.HTMLAttributes<UAvatarElement>>
6
+ & Omit<React.HTMLAttributes<UAvatarElement>, never>
7
+ & React.RefAttributes<UAvatarElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UBadge as UBadgeElement } from '../components/badge/UBadge';
3
3
 
4
4
  export declare const UBadge: React.ForwardRefExoticComponent<
5
- Partial<UBadgeElement> & React.HTMLAttributes<UBadgeElement> & {
5
+ Omit<Partial<UBadgeElement>, keyof React.HTMLAttributes<UBadgeElement>>
6
+ & Omit<React.HTMLAttributes<UBadgeElement>, never>
7
+ & React.RefAttributes<UBadgeElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UBreadcrumb as UBreadcrumbElement } from '../components/breadcrumb/UBreadcrumb';
3
3
 
4
4
  export declare const UBreadcrumb: React.ForwardRefExoticComponent<
5
- Partial<UBreadcrumbElement> & React.HTMLAttributes<UBreadcrumbElement> & {
5
+ Omit<Partial<UBreadcrumbElement>, keyof React.HTMLAttributes<UBreadcrumbElement>>
6
+ & Omit<React.HTMLAttributes<UBreadcrumbElement>, never>
7
+ & React.RefAttributes<UBreadcrumbElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -3,7 +3,10 @@ import { UBreadcrumbItem as UBreadcrumbItemElement } from '../components/breadcr
3
3
  import { type NavigateEventDetail } from '../events/NavigateEvent';
4
4
 
5
5
  export declare const UBreadcrumbItem: React.ForwardRefExoticComponent<
6
- Partial<UBreadcrumbItemElement> & React.HTMLAttributes<UBreadcrumbItemElement> & {
6
+ Omit<Partial<UBreadcrumbItemElement>, keyof React.HTMLAttributes<UBreadcrumbItemElement>>
7
+ & Omit<React.HTMLAttributes<UBreadcrumbItemElement>, 'onNavigate'>
8
+ & React.RefAttributes<UBreadcrumbItemElement>
9
+ & {
7
10
  onNavigate?: (event: CustomEvent<NavigateEventDetail>) => void;
8
11
  }
9
12
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UButton as UButtonElement } from '../components/button/UButton';
3
3
 
4
4
  export declare const UButton: React.ForwardRefExoticComponent<
5
- Partial<UButtonElement> & React.HTMLAttributes<UButtonElement> & {
5
+ Omit<Partial<UButtonElement>, keyof React.HTMLAttributes<UButtonElement>>
6
+ & Omit<React.HTMLAttributes<UButtonElement>, never>
7
+ & React.RefAttributes<UButtonElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UButtonGroup as UButtonGroupElement } from '../components/button-group/UButtonGroup';
3
3
 
4
4
  export declare const UButtonGroup: React.ForwardRefExoticComponent<
5
- Partial<UButtonGroupElement> & React.HTMLAttributes<UButtonGroupElement> & {
5
+ Omit<Partial<UButtonGroupElement>, keyof React.HTMLAttributes<UButtonGroupElement>>
6
+ & Omit<React.HTMLAttributes<UButtonGroupElement>, never>
7
+ & React.RefAttributes<UButtonGroupElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UCard as UCardElement } from '../components/card/UCard';
3
3
 
4
4
  export declare const UCard: React.ForwardRefExoticComponent<
5
- Partial<UCardElement> & React.HTMLAttributes<UCardElement> & {
5
+ Omit<Partial<UCardElement>, keyof React.HTMLAttributes<UCardElement>>
6
+ & Omit<React.HTMLAttributes<UCardElement>, never>
7
+ & React.RefAttributes<UCardElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UCarousel as UCarouselElement } from '../components/carousel/UCarousel';
3
3
 
4
4
  export declare const UCarousel: React.ForwardRefExoticComponent<
5
- Partial<UCarouselElement> & React.HTMLAttributes<UCarouselElement> & {
5
+ Omit<Partial<UCarouselElement>, keyof React.HTMLAttributes<UCarouselElement>>
6
+ & Omit<React.HTMLAttributes<UCarouselElement>, never>
7
+ & React.RefAttributes<UCarouselElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UCheckbox as UCheckboxElement } from '../components/checkbox/UCheckbox';
3
3
 
4
4
  export declare const UCheckbox: React.ForwardRefExoticComponent<
5
- Partial<UCheckboxElement> & React.HTMLAttributes<UCheckboxElement> & {
5
+ Omit<Partial<UCheckboxElement>, keyof React.HTMLAttributes<UCheckboxElement>>
6
+ & Omit<React.HTMLAttributes<UCheckboxElement>, 'onChange'>
7
+ & React.RefAttributes<UCheckboxElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -4,7 +4,10 @@ import { type PickEventDetail } from '../events/PickEvent';
4
4
  import { type RemoveEventDetail } from '../events/RemoveEvent';
5
5
 
6
6
  export declare const UChip: React.ForwardRefExoticComponent<
7
- Partial<UChipElement> & React.HTMLAttributes<UChipElement> & {
7
+ Omit<Partial<UChipElement>, keyof React.HTMLAttributes<UChipElement>>
8
+ & Omit<React.HTMLAttributes<UChipElement>, 'onPick' | 'onRemove'>
9
+ & React.RefAttributes<UChipElement>
10
+ & {
8
11
  onPick?: (event: CustomEvent<PickEventDetail>) => void;
9
12
  onRemove?: (event: CustomEvent<RemoveEventDetail>) => void;
10
13
  }
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UCopyButton as UCopyButtonElement } from '../components/copy-button/UCopyButton';
3
3
 
4
4
  export declare const UCopyButton: React.ForwardRefExoticComponent<
5
- Partial<UCopyButtonElement> & React.HTMLAttributes<UCopyButtonElement> & {
5
+ Omit<Partial<UCopyButtonElement>, keyof React.HTMLAttributes<UCopyButtonElement>>
6
+ & Omit<React.HTMLAttributes<UCopyButtonElement>, 'onCopy'>
7
+ & React.RefAttributes<UCopyButtonElement>
8
+ & {
6
9
  onCopy?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UDialog as UDialogElement } from '../components/dialog/UDialog';
3
3
 
4
4
  export declare const UDialog: React.ForwardRefExoticComponent<
5
- Partial<UDialogElement> & React.HTMLAttributes<UDialogElement> & {
5
+ Omit<Partial<UDialogElement>, keyof React.HTMLAttributes<UDialogElement>>
6
+ & Omit<React.HTMLAttributes<UDialogElement>, never>
7
+ & React.RefAttributes<UDialogElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UDivider as UDividerElement } from '../components/divider/UDivider';
3
3
 
4
4
  export declare const UDivider: React.ForwardRefExoticComponent<
5
- Partial<UDividerElement> & React.HTMLAttributes<UDividerElement> & {
5
+ Omit<Partial<UDividerElement>, keyof React.HTMLAttributes<UDividerElement>>
6
+ & Omit<React.HTMLAttributes<UDividerElement>, never>
7
+ & React.RefAttributes<UDividerElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UDrawer as UDrawerElement } from '../components/drawer/UDrawer';
3
3
 
4
4
  export declare const UDrawer: React.ForwardRefExoticComponent<
5
- Partial<UDrawerElement> & React.HTMLAttributes<UDrawerElement> & {
5
+ Omit<Partial<UDrawerElement>, keyof React.HTMLAttributes<UDrawerElement>>
6
+ & Omit<React.HTMLAttributes<UDrawerElement>, never>
7
+ & React.RefAttributes<UDrawerElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UField as UFieldElement } from '../components/field/UField';
3
3
 
4
4
  export declare const UField: React.ForwardRefExoticComponent<
5
- Partial<UFieldElement> & React.HTMLAttributes<UFieldElement> & {
5
+ Omit<Partial<UFieldElement>, keyof React.HTMLAttributes<UFieldElement>>
6
+ & Omit<React.HTMLAttributes<UFieldElement>, never>
7
+ & React.RefAttributes<UFieldElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UForm as UFormElement } from '../components/form/UForm';
3
3
 
4
4
  export declare const UForm: React.ForwardRefExoticComponent<
5
- Partial<UFormElement> & React.HTMLAttributes<UFormElement> & {
5
+ Omit<Partial<UFormElement>, keyof React.HTMLAttributes<UFormElement>>
6
+ & Omit<React.HTMLAttributes<UFormElement>, 'onChange'>
7
+ & React.RefAttributes<UFormElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UIcon as UIconElement } from '../components/icon/UIcon';
3
3
 
4
4
  export declare const UIcon: React.ForwardRefExoticComponent<
5
- Partial<UIconElement> & React.HTMLAttributes<UIconElement> & {
5
+ Omit<Partial<UIconElement>, keyof React.HTMLAttributes<UIconElement>>
6
+ & Omit<React.HTMLAttributes<UIconElement>, never>
7
+ & React.RefAttributes<UIconElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UIconButton as UIconButtonElement } from '../components/icon-button/UIconButton';
3
3
 
4
4
  export declare const UIconButton: React.ForwardRefExoticComponent<
5
- Partial<UIconButtonElement> & React.HTMLAttributes<UIconButtonElement> & {
5
+ Omit<Partial<UIconButtonElement>, keyof React.HTMLAttributes<UIconButtonElement>>
6
+ & Omit<React.HTMLAttributes<UIconButtonElement>, never>
7
+ & React.RefAttributes<UIconButtonElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UInput as UInputElement } from '../components/input/UInput';
3
3
 
4
4
  export declare const UInput: React.ForwardRefExoticComponent<
5
- Partial<UInputElement> & React.HTMLAttributes<UInputElement> & {
5
+ Omit<Partial<UInputElement>, keyof React.HTMLAttributes<UInputElement>>
6
+ & Omit<React.HTMLAttributes<UInputElement>, 'onInput' | 'onChange'>
7
+ & React.RefAttributes<UInputElement>
8
+ & {
6
9
  onInput?: (event: CustomEvent) => void;
7
10
  onChange?: (event: CustomEvent) => void;
8
11
  }
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UMenu as UMenuElement } from '../components/menu/UMenu';
3
3
 
4
4
  export declare const UMenu: React.ForwardRefExoticComponent<
5
- Partial<UMenuElement> & React.HTMLAttributes<UMenuElement> & {
5
+ Omit<Partial<UMenuElement>, keyof React.HTMLAttributes<UMenuElement>>
6
+ & Omit<React.HTMLAttributes<UMenuElement>, 'onChange'>
7
+ & React.RefAttributes<UMenuElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -3,7 +3,10 @@ import { UMenuItem as UMenuItemElement } from '../components/menu-item/UMenuItem
3
3
  import { type PickEventDetail } from '../events/PickEvent';
4
4
 
5
5
  export declare const UMenuItem: React.ForwardRefExoticComponent<
6
- Partial<UMenuItemElement> & React.HTMLAttributes<UMenuItemElement> & {
6
+ Omit<Partial<UMenuItemElement>, keyof React.HTMLAttributes<UMenuItemElement>>
7
+ & Omit<React.HTMLAttributes<UMenuItemElement>, 'onPick'>
8
+ & React.RefAttributes<UMenuItemElement>
9
+ & {
7
10
  onPick?: (event: CustomEvent<PickEventDetail>) => void;
8
11
  }
9
12
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UOption as UOptionElement } from '../components/option/UOption';
3
3
 
4
4
  export declare const UOption: React.ForwardRefExoticComponent<
5
- Partial<UOptionElement> & React.HTMLAttributes<UOptionElement> & {
5
+ Omit<Partial<UOptionElement>, keyof React.HTMLAttributes<UOptionElement>>
6
+ & Omit<React.HTMLAttributes<UOptionElement>, never>
7
+ & React.RefAttributes<UOptionElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UPanel as UPanelElement } from '../components/panel/UPanel';
3
3
 
4
4
  export declare const UPanel: React.ForwardRefExoticComponent<
5
- Partial<UPanelElement> & React.HTMLAttributes<UPanelElement> & {
5
+ Omit<Partial<UPanelElement>, keyof React.HTMLAttributes<UPanelElement>>
6
+ & Omit<React.HTMLAttributes<UPanelElement>, never>
7
+ & React.RefAttributes<UPanelElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UPopover as UPopoverElement } from '../components/popover/UPopover';
3
3
 
4
4
  export declare const UPopover: React.ForwardRefExoticComponent<
5
- Partial<UPopoverElement> & React.HTMLAttributes<UPopoverElement> & {
5
+ Omit<Partial<UPopoverElement>, keyof React.HTMLAttributes<UPopoverElement>>
6
+ & Omit<React.HTMLAttributes<UPopoverElement>, 'onShow' | 'onHide'>
7
+ & React.RefAttributes<UPopoverElement>
8
+ & {
6
9
  onShow?: (event: CustomEvent) => void;
7
10
  onHide?: (event: CustomEvent) => void;
8
11
  }
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UProgressBar as UProgressBarElement } from '../components/progress-bar/UProgressBar';
3
3
 
4
4
  export declare const UProgressBar: React.ForwardRefExoticComponent<
5
- Partial<UProgressBarElement> & React.HTMLAttributes<UProgressBarElement> & {
5
+ Omit<Partial<UProgressBarElement>, keyof React.HTMLAttributes<UProgressBarElement>>
6
+ & Omit<React.HTMLAttributes<UProgressBarElement>, never>
7
+ & React.RefAttributes<UProgressBarElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UProgressRing as UProgressRingElement } from '../components/progress-ring/UProgressRing';
3
3
 
4
4
  export declare const UProgressRing: React.ForwardRefExoticComponent<
5
- Partial<UProgressRingElement> & React.HTMLAttributes<UProgressRingElement> & {
5
+ Omit<Partial<UProgressRingElement>, keyof React.HTMLAttributes<UProgressRingElement>>
6
+ & Omit<React.HTMLAttributes<UProgressRingElement>, never>
7
+ & React.RefAttributes<UProgressRingElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { URadio as URadioElement } from '../components/radio/URadio';
3
3
 
4
4
  export declare const URadio: React.ForwardRefExoticComponent<
5
- Partial<URadioElement> & React.HTMLAttributes<URadioElement> & {
5
+ Omit<Partial<URadioElement>, keyof React.HTMLAttributes<URadioElement>>
6
+ & Omit<React.HTMLAttributes<URadioElement>, 'onChange'>
7
+ & React.RefAttributes<URadioElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { URating as URatingElement } from '../components/rating/URating';
3
3
 
4
4
  export declare const URating: React.ForwardRefExoticComponent<
5
- Partial<URatingElement> & React.HTMLAttributes<URatingElement> & {
5
+ Omit<Partial<URatingElement>, keyof React.HTMLAttributes<URatingElement>>
6
+ & Omit<React.HTMLAttributes<URatingElement>, 'onChange'>
7
+ & React.RefAttributes<URatingElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { USelect as USelectElement } from '../components/select/USelect';
3
3
 
4
4
  export declare const USelect: React.ForwardRefExoticComponent<
5
- Partial<USelectElement> & React.HTMLAttributes<USelectElement> & {
5
+ Omit<Partial<USelectElement>, keyof React.HTMLAttributes<USelectElement>>
6
+ & Omit<React.HTMLAttributes<USelectElement>, 'onChange'>
7
+ & React.RefAttributes<USelectElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { USkeleton as USkeletonElement } from '../components/skeleton/USkeleton';
3
3
 
4
4
  export declare const USkeleton: React.ForwardRefExoticComponent<
5
- Partial<USkeletonElement> & React.HTMLAttributes<USkeletonElement> & {
5
+ Omit<Partial<USkeletonElement>, keyof React.HTMLAttributes<USkeletonElement>>
6
+ & Omit<React.HTMLAttributes<USkeletonElement>, never>
7
+ & React.RefAttributes<USkeletonElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { USlider as USliderElement } from '../components/slider/USlider';
3
3
 
4
4
  export declare const USlider: React.ForwardRefExoticComponent<
5
- Partial<USliderElement> & React.HTMLAttributes<USliderElement> & {
5
+ Omit<Partial<USliderElement>, keyof React.HTMLAttributes<USliderElement>>
6
+ & Omit<React.HTMLAttributes<USliderElement>, 'onChange'>
7
+ & React.RefAttributes<USliderElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { USpinner as USpinnerElement } from '../components/spinner/USpinner';
3
3
 
4
4
  export declare const USpinner: React.ForwardRefExoticComponent<
5
- Partial<USpinnerElement> & React.HTMLAttributes<USpinnerElement> & {
5
+ Omit<Partial<USpinnerElement>, keyof React.HTMLAttributes<USpinnerElement>>
6
+ & Omit<React.HTMLAttributes<USpinnerElement>, never>
7
+ & React.RefAttributes<USpinnerElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -3,7 +3,10 @@ import { USplitPanel as USplitPanelElement } from '../components/split-panel/USp
3
3
  import { type ShiftEventDetail } from '../events/ShiftEvent';
4
4
 
5
5
  export declare const USplitPanel: React.ForwardRefExoticComponent<
6
- Partial<USplitPanelElement> & React.HTMLAttributes<USplitPanelElement> & {
6
+ Omit<Partial<USplitPanelElement>, keyof React.HTMLAttributes<USplitPanelElement>>
7
+ & Omit<React.HTMLAttributes<USplitPanelElement>, 'onShiftStart' | 'onShift' | 'onShiftEnd'>
8
+ & React.RefAttributes<USplitPanelElement>
9
+ & {
7
10
  onShiftStart?: (event: CustomEvent<ShiftEventDetail>) => void;
8
11
  onShift?: (event: CustomEvent<ShiftEventDetail>) => void;
9
12
  onShiftEnd?: (event: CustomEvent<ShiftEventDetail>) => void;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { USwitch as USwitchElement } from '../components/switch/USwitch';
3
3
 
4
4
  export declare const USwitch: React.ForwardRefExoticComponent<
5
- Partial<USwitchElement> & React.HTMLAttributes<USwitchElement> & {
5
+ Omit<Partial<USwitchElement>, keyof React.HTMLAttributes<USwitchElement>>
6
+ & Omit<React.HTMLAttributes<USwitchElement>, 'onChange'>
7
+ & React.RefAttributes<USwitchElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -3,7 +3,10 @@ import { UTab as UTabElement } from '../components/tab/UTab';
3
3
  import { type RemoveEventDetail } from '../events/RemoveEvent';
4
4
 
5
5
  export declare const UTab: React.ForwardRefExoticComponent<
6
- Partial<UTabElement> & React.HTMLAttributes<UTabElement> & {
6
+ Omit<Partial<UTabElement>, keyof React.HTMLAttributes<UTabElement>>
7
+ & Omit<React.HTMLAttributes<UTabElement>, 'onRemove'>
8
+ & React.RefAttributes<UTabElement>
9
+ & {
7
10
  onRemove?: (event: CustomEvent<RemoveEventDetail>) => void;
8
11
  }
9
12
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UTabPanel as UTabPanelElement } from '../components/tab-panel/UTabPanel';
3
3
 
4
4
  export declare const UTabPanel: React.ForwardRefExoticComponent<
5
- Partial<UTabPanelElement> & React.HTMLAttributes<UTabPanelElement> & {
5
+ Omit<Partial<UTabPanelElement>, keyof React.HTMLAttributes<UTabPanelElement>>
6
+ & Omit<React.HTMLAttributes<UTabPanelElement>, 'onChange'>
7
+ & React.RefAttributes<UTabPanelElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UTag as UTagElement } from '../components/tag/UTag';
3
3
 
4
4
  export declare const UTag: React.ForwardRefExoticComponent<
5
- Partial<UTagElement> & React.HTMLAttributes<UTagElement> & {
5
+ Omit<Partial<UTagElement>, keyof React.HTMLAttributes<UTagElement>>
6
+ & Omit<React.HTMLAttributes<UTagElement>, never>
7
+ & React.RefAttributes<UTagElement>
8
+ & {
6
9
  }
7
10
  >;
8
11
 
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UTextarea as UTextareaElement } from '../components/textarea/UTextarea';
3
3
 
4
4
  export declare const UTextarea: React.ForwardRefExoticComponent<
5
- Partial<UTextareaElement> & React.HTMLAttributes<UTextareaElement> & {
5
+ Omit<Partial<UTextareaElement>, keyof React.HTMLAttributes<UTextareaElement>>
6
+ & Omit<React.HTMLAttributes<UTextareaElement>, 'onInput' | 'onChange'>
7
+ & React.RefAttributes<UTextareaElement>
8
+ & {
6
9
  onInput?: (event: CustomEvent) => void;
7
10
  onChange?: (event: CustomEvent) => void;
8
11
  }
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UTooltip as UTooltipElement } from '../components/tooltip/UTooltip';
3
3
 
4
4
  export declare const UTooltip: React.ForwardRefExoticComponent<
5
- Partial<UTooltipElement> & React.HTMLAttributes<UTooltipElement> & {
5
+ Omit<Partial<UTooltipElement>, keyof React.HTMLAttributes<UTooltipElement>>
6
+ & Omit<React.HTMLAttributes<UTooltipElement>, 'onShow' | 'onHide'>
7
+ & React.RefAttributes<UTooltipElement>
8
+ & {
6
9
  onShow?: (event: CustomEvent) => void;
7
10
  onHide?: (event: CustomEvent) => void;
8
11
  }
@@ -2,7 +2,10 @@ import React from 'react';
2
2
  import { UTree as UTreeElement } from '../components/tree/UTree';
3
3
 
4
4
  export declare const UTree: React.ForwardRefExoticComponent<
5
- Partial<UTreeElement> & React.HTMLAttributes<UTreeElement> & {
5
+ Omit<Partial<UTreeElement>, keyof React.HTMLAttributes<UTreeElement>>
6
+ & Omit<React.HTMLAttributes<UTreeElement>, 'onChange'>
7
+ & React.RefAttributes<UTreeElement>
8
+ & {
6
9
  onChange?: (event: CustomEvent) => void;
7
10
  }
8
11
  >;
@@ -6,7 +6,10 @@ import { type PickEventDetail } from '../events/PickEvent';
6
6
  import { type CheckEventDetail } from '../events/CheckEvent';
7
7
 
8
8
  export declare const UTreeItem: React.ForwardRefExoticComponent<
9
- Partial<UTreeItemElement> & React.HTMLAttributes<UTreeItemElement> & {
9
+ Omit<Partial<UTreeItemElement>, keyof React.HTMLAttributes<UTreeItemElement>>
10
+ & Omit<React.HTMLAttributes<UTreeItemElement>, 'onExpand' | 'onCollapse' | 'onPick' | 'onCheck'>
11
+ & React.RefAttributes<UTreeItemElement>
12
+ & {
10
13
  onExpand?: (event: CustomEvent<ExpandEventDetail>) => void;
11
14
  onCollapse?: (event: CustomEvent<CollapseEventDetail>) => void;
12
15
  onPick?: (event: CustomEvent<PickEventDetail>) => void;
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.8.0",
4
+ "version": "1.9.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "components",
@@ -45,7 +45,8 @@
45
45
  "lint": "eslint src/",
46
46
  "lint:fix": "eslint src/ --fix",
47
47
  "build:plugins": "tsc -p plugins/tsconfig.json",
48
- "build": "eslint && vite build && npm run build:plugins"
48
+ "build": "eslint && vite build && npm run build:plugins && npm run test:react-types",
49
+ "test:react-types": "tsc -p tsconfig.react-smoke.json"
49
50
  },
50
51
  "dependencies": {
51
52
  "@floating-ui/dom": "^1.8.0",
@@ -67,6 +68,7 @@
67
68
  "devDependencies": {
68
69
  "@eslint/js": "^9.39.4",
69
70
  "@types/node": "^26.1.1",
71
+ "@types/react": "^19.2.14",
70
72
  "@vitest/browser-playwright": "^4.1.10",
71
73
  "eslint": "^9.39.4",
72
74
  "eslint-plugin-lit": "^2.3.1",
@@ -190,10 +190,27 @@ export const ${className} = createComponent({
190
190
  return ` ${e.reactName}?: (event: ${type}) => void;`;
191
191
  }).join('\n') + '\n'
192
192
  : '';
193
+ // 래퍼 이벤트 prop(onChange 등)은 React.HTMLAttributes 의 동명 핸들러와 교집합되면
194
+ // 어떤 시그니처도 대입 불가가 된다. HTMLAttributes 쪽에서 해당 키를 제거해 CustomEvent
195
+ // 시그니처만 남긴다. 이벤트가 없으면 Omit<…, never> 로 HTMLAttributes 원형 유지.
196
+ const eventKeyUnion = events.length > 0
197
+ ? events.map(e => `'${e.reactName}'`).join(' | ')
198
+ : 'never';
199
+ // Partial<Element> 은 DOM 프로퍼티(children: HTMLCollection 등)를 포함해 React 의 JSX
200
+ // children/이벤트/className 등과 충돌한다. keyof React.HTMLAttributes 를 제거해 컴포넌트
201
+ // 고유 prop만 남기고, React 친화 타입(children: ReactNode 포함)은 HTMLAttributes 가 제공한다.
202
+ //
203
+ // `React.RefAttributes` 를 반드시 교집합에 넣는다 — `ForwardRefExoticComponent<P>` 는 P 에
204
+ // ref 를 자동으로 더해 주지 않는다. @lit/react 의 `ReactWebComponent` 자신이
205
+ // `PropsWithoutRef<…> & React.RefAttributes<I>` 로 선언돼 있으므로(런타임은 ref 를 전달한다),
206
+ // 이것을 빠뜨리면 **런타임은 되는데 타입만 거부하는** 괴리가 생긴다.
193
207
  const dts = `${dtsImports.join('\n')}
194
208
 
195
209
  export declare const ${className}: React.ForwardRefExoticComponent<
196
- Partial<${className}Element> & React.HTMLAttributes<${className}Element> & {
210
+ Omit<Partial<${className}Element>, keyof React.HTMLAttributes<${className}Element>>
211
+ & Omit<React.HTMLAttributes<${className}Element>, ${eventKeyUnion}>
212
+ & React.RefAttributes<${className}Element>
213
+ & {
197
214
  ${eventTypes} }
198
215
  >;
199
216
 
@@ -45,9 +45,25 @@ Inherits all `UFloatingElement` properties (see [floating.md](../extensions/floa
45
45
  | `showDelay` | `number` | `0` | — | Open delay in ms |
46
46
  | `hideDelay` | `number` | `0` | — | Close delay in ms |
47
47
  | `trigger` | `'click'\|'contextmenu'\|'hover'\|'focus'\|'manual'` | `'click'` | ✓ | Open trigger |
48
- | `dismiss` | `string[]` | `['click','escape','scroll','resize']` | ✓ | Close triggers |
48
+ | `dismiss` | `string[]` | `['click','escape','scroll','resize']` | ✓ | Close triggers — see note below |
49
49
  | `autofocus` | `boolean` | `false` | ✓ | Focus first focusable element on open |
50
50
 
51
+ ### `dismiss` semantics
52
+
53
+ `click` and `escape` express user intent and always close the popover.
54
+
55
+ `scroll` and `resize` are viewport-geometry changes and **only close the popover when it is
56
+ anchored to a coordinate** — i.e. the virtual anchor created by `trigger="contextmenu"`, whose
57
+ `clientX`/`clientY` stop matching whatever they pointed at once the page reflows.
58
+
59
+ A popover anchored to a **real element** is not closed by scrolling or resizing: `autoUpdate`
60
+ keeps it pinned to its anchor. If the anchor scrolls out of its clipping area entirely, the
61
+ popover is *hidden* rather than closed (see `anchor-hidden` in
62
+ [extensions/floating.md](../extensions/floating.md)) and returns when the anchor comes back.
63
+
64
+ > Before 1.8.2 `scroll`/`resize` closed unconditionally, which contradicted `autoUpdate` and
65
+ > collapsed open `u-select`/`u-input` listboxes on any page scroll.
66
+
51
67
  ## Events
52
68
 
53
69
  | Event | Cancelable | Description |
@@ -45,6 +45,17 @@ export class MyDropdown extends UFloatingElement {
45
45
  | `showDelay` | `number` | `0` | — | Open delay (ms) |
46
46
  | `hideDelay` | `number` | `0` | — | Close delay (ms) |
47
47
 
48
+ ## Derived state
49
+
50
+ | Attribute | Description |
51
+ |-----------|-------------|
52
+ | `anchor-hidden` | **Read-only — do not set.** Applied automatically while the anchor is fully outside its clipping area (scroll container or viewport). The element is hidden but **not closed** (`open` stays `true`) and returns as soon as the anchor is visible again. Available as a style hook: `my-dropdown[anchor-hidden] { … }` |
53
+
54
+ This matters most with `strategy="fixed"`, which is used precisely so the floating element
55
+ escapes `overflow` ancestor clipping — without `anchor-hidden` the panel would keep covering
56
+ unrelated content after its anchor scrolled out of the panel. Backed by the `hide` middleware
57
+ of `@floating-ui/dom`. Added in 1.8.2.
58
+
48
59
  ## Methods
49
60
 
50
61
  | Method | Returns | Description |