@iyulab/components 1.8.1 → 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 (52) hide show
  1. package/CHANGELOG.md +12 -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/popover/UPopover.d.ts +29 -2
  6. package/dist/components/popover/UPopover.js +23 -0
  7. package/dist/react/UAlert.d.ts +1 -0
  8. package/dist/react/UAvatar.d.ts +1 -0
  9. package/dist/react/UBadge.d.ts +1 -0
  10. package/dist/react/UBreadcrumb.d.ts +1 -0
  11. package/dist/react/UBreadcrumbItem.d.ts +1 -0
  12. package/dist/react/UButton.d.ts +1 -0
  13. package/dist/react/UButtonGroup.d.ts +1 -0
  14. package/dist/react/UCard.d.ts +1 -0
  15. package/dist/react/UCarousel.d.ts +1 -0
  16. package/dist/react/UCheckbox.d.ts +1 -0
  17. package/dist/react/UChip.d.ts +1 -0
  18. package/dist/react/UCopyButton.d.ts +1 -0
  19. package/dist/react/UDialog.d.ts +1 -0
  20. package/dist/react/UDivider.d.ts +1 -0
  21. package/dist/react/UDrawer.d.ts +1 -0
  22. package/dist/react/UField.d.ts +1 -0
  23. package/dist/react/UForm.d.ts +1 -0
  24. package/dist/react/UIcon.d.ts +1 -0
  25. package/dist/react/UIconButton.d.ts +1 -0
  26. package/dist/react/UInput.d.ts +1 -0
  27. package/dist/react/UMenu.d.ts +1 -0
  28. package/dist/react/UMenuItem.d.ts +1 -0
  29. package/dist/react/UOption.d.ts +1 -0
  30. package/dist/react/UPanel.d.ts +1 -0
  31. package/dist/react/UPopover.d.ts +1 -0
  32. package/dist/react/UProgressBar.d.ts +1 -0
  33. package/dist/react/UProgressRing.d.ts +1 -0
  34. package/dist/react/URadio.d.ts +1 -0
  35. package/dist/react/URating.d.ts +1 -0
  36. package/dist/react/USelect.d.ts +1 -0
  37. package/dist/react/USkeleton.d.ts +1 -0
  38. package/dist/react/USlider.d.ts +1 -0
  39. package/dist/react/USpinner.d.ts +1 -0
  40. package/dist/react/USplitPanel.d.ts +1 -0
  41. package/dist/react/USwitch.d.ts +1 -0
  42. package/dist/react/UTab.d.ts +1 -0
  43. package/dist/react/UTabPanel.d.ts +1 -0
  44. package/dist/react/UTag.d.ts +1 -0
  45. package/dist/react/UTextarea.d.ts +1 -0
  46. package/dist/react/UTooltip.d.ts +1 -0
  47. package/dist/react/UTree.d.ts +1 -0
  48. package/dist/react/UTreeItem.d.ts +1 -0
  49. package/package.json +4 -2
  50. package/plugins/vite-plugin-react-wrapper.js +6 -0
  51. package/skills/iyulab-components/references/components/popover.md +17 -1
  52. package/skills/iyulab-components/references/extensions/floating.md +11 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
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
+
3
15
  ## [1.8.1] - 2026-07-24
4
16
 
5
17
  ### 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;
@@ -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,
@@ -6,6 +6,7 @@ import { type HideEventDetail } from '../events/HideEvent';
6
6
  export declare const UAlert: React.ForwardRefExoticComponent<
7
7
  Omit<Partial<UAlertElement>, keyof React.HTMLAttributes<UAlertElement>>
8
8
  & Omit<React.HTMLAttributes<UAlertElement>, 'onShow' | 'onHide'>
9
+ & React.RefAttributes<UAlertElement>
9
10
  & {
10
11
  onShow?: (event: CustomEvent<ShowEventDetail>) => void;
11
12
  onHide?: (event: CustomEvent<HideEventDetail>) => void;
@@ -4,6 +4,7 @@ import { UAvatar as UAvatarElement } from '../components/avatar/UAvatar';
4
4
  export declare const UAvatar: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UAvatarElement>, keyof React.HTMLAttributes<UAvatarElement>>
6
6
  & Omit<React.HTMLAttributes<UAvatarElement>, never>
7
+ & React.RefAttributes<UAvatarElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UBadge as UBadgeElement } from '../components/badge/UBadge';
4
4
  export declare const UBadge: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UBadgeElement>, keyof React.HTMLAttributes<UBadgeElement>>
6
6
  & Omit<React.HTMLAttributes<UBadgeElement>, never>
7
+ & React.RefAttributes<UBadgeElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UBreadcrumb as UBreadcrumbElement } from '../components/breadcrumb/UBre
4
4
  export declare const UBreadcrumb: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UBreadcrumbElement>, keyof React.HTMLAttributes<UBreadcrumbElement>>
6
6
  & Omit<React.HTMLAttributes<UBreadcrumbElement>, never>
7
+ & React.RefAttributes<UBreadcrumbElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -5,6 +5,7 @@ import { type NavigateEventDetail } from '../events/NavigateEvent';
5
5
  export declare const UBreadcrumbItem: React.ForwardRefExoticComponent<
6
6
  Omit<Partial<UBreadcrumbItemElement>, keyof React.HTMLAttributes<UBreadcrumbItemElement>>
7
7
  & Omit<React.HTMLAttributes<UBreadcrumbItemElement>, 'onNavigate'>
8
+ & React.RefAttributes<UBreadcrumbItemElement>
8
9
  & {
9
10
  onNavigate?: (event: CustomEvent<NavigateEventDetail>) => void;
10
11
  }
@@ -4,6 +4,7 @@ import { UButton as UButtonElement } from '../components/button/UButton';
4
4
  export declare const UButton: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UButtonElement>, keyof React.HTMLAttributes<UButtonElement>>
6
6
  & Omit<React.HTMLAttributes<UButtonElement>, never>
7
+ & React.RefAttributes<UButtonElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UButtonGroup as UButtonGroupElement } from '../components/button-group/
4
4
  export declare const UButtonGroup: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UButtonGroupElement>, keyof React.HTMLAttributes<UButtonGroupElement>>
6
6
  & Omit<React.HTMLAttributes<UButtonGroupElement>, never>
7
+ & React.RefAttributes<UButtonGroupElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UCard as UCardElement } from '../components/card/UCard';
4
4
  export declare const UCard: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UCardElement>, keyof React.HTMLAttributes<UCardElement>>
6
6
  & Omit<React.HTMLAttributes<UCardElement>, never>
7
+ & React.RefAttributes<UCardElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UCarousel as UCarouselElement } from '../components/carousel/UCarousel'
4
4
  export declare const UCarousel: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UCarouselElement>, keyof React.HTMLAttributes<UCarouselElement>>
6
6
  & Omit<React.HTMLAttributes<UCarouselElement>, never>
7
+ & React.RefAttributes<UCarouselElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UCheckbox as UCheckboxElement } from '../components/checkbox/UCheckbox'
4
4
  export declare const UCheckbox: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UCheckboxElement>, keyof React.HTMLAttributes<UCheckboxElement>>
6
6
  & Omit<React.HTMLAttributes<UCheckboxElement>, 'onChange'>
7
+ & React.RefAttributes<UCheckboxElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -6,6 +6,7 @@ import { type RemoveEventDetail } from '../events/RemoveEvent';
6
6
  export declare const UChip: React.ForwardRefExoticComponent<
7
7
  Omit<Partial<UChipElement>, keyof React.HTMLAttributes<UChipElement>>
8
8
  & Omit<React.HTMLAttributes<UChipElement>, 'onPick' | 'onRemove'>
9
+ & React.RefAttributes<UChipElement>
9
10
  & {
10
11
  onPick?: (event: CustomEvent<PickEventDetail>) => void;
11
12
  onRemove?: (event: CustomEvent<RemoveEventDetail>) => void;
@@ -4,6 +4,7 @@ import { UCopyButton as UCopyButtonElement } from '../components/copy-button/UCo
4
4
  export declare const UCopyButton: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UCopyButtonElement>, keyof React.HTMLAttributes<UCopyButtonElement>>
6
6
  & Omit<React.HTMLAttributes<UCopyButtonElement>, 'onCopy'>
7
+ & React.RefAttributes<UCopyButtonElement>
7
8
  & {
8
9
  onCopy?: (event: CustomEvent) => void;
9
10
  }
@@ -4,6 +4,7 @@ import { UDialog as UDialogElement } from '../components/dialog/UDialog';
4
4
  export declare const UDialog: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UDialogElement>, keyof React.HTMLAttributes<UDialogElement>>
6
6
  & Omit<React.HTMLAttributes<UDialogElement>, never>
7
+ & React.RefAttributes<UDialogElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UDivider as UDividerElement } from '../components/divider/UDivider';
4
4
  export declare const UDivider: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UDividerElement>, keyof React.HTMLAttributes<UDividerElement>>
6
6
  & Omit<React.HTMLAttributes<UDividerElement>, never>
7
+ & React.RefAttributes<UDividerElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UDrawer as UDrawerElement } from '../components/drawer/UDrawer';
4
4
  export declare const UDrawer: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UDrawerElement>, keyof React.HTMLAttributes<UDrawerElement>>
6
6
  & Omit<React.HTMLAttributes<UDrawerElement>, never>
7
+ & React.RefAttributes<UDrawerElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UField as UFieldElement } from '../components/field/UField';
4
4
  export declare const UField: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UFieldElement>, keyof React.HTMLAttributes<UFieldElement>>
6
6
  & Omit<React.HTMLAttributes<UFieldElement>, never>
7
+ & React.RefAttributes<UFieldElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UForm as UFormElement } from '../components/form/UForm';
4
4
  export declare const UForm: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UFormElement>, keyof React.HTMLAttributes<UFormElement>>
6
6
  & Omit<React.HTMLAttributes<UFormElement>, 'onChange'>
7
+ & React.RefAttributes<UFormElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -4,6 +4,7 @@ import { UIcon as UIconElement } from '../components/icon/UIcon';
4
4
  export declare const UIcon: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UIconElement>, keyof React.HTMLAttributes<UIconElement>>
6
6
  & Omit<React.HTMLAttributes<UIconElement>, never>
7
+ & React.RefAttributes<UIconElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UIconButton as UIconButtonElement } from '../components/icon-button/UIc
4
4
  export declare const UIconButton: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UIconButtonElement>, keyof React.HTMLAttributes<UIconButtonElement>>
6
6
  & Omit<React.HTMLAttributes<UIconButtonElement>, never>
7
+ & React.RefAttributes<UIconButtonElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UInput as UInputElement } from '../components/input/UInput';
4
4
  export declare const UInput: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UInputElement>, keyof React.HTMLAttributes<UInputElement>>
6
6
  & Omit<React.HTMLAttributes<UInputElement>, 'onInput' | 'onChange'>
7
+ & React.RefAttributes<UInputElement>
7
8
  & {
8
9
  onInput?: (event: CustomEvent) => void;
9
10
  onChange?: (event: CustomEvent) => void;
@@ -4,6 +4,7 @@ import { UMenu as UMenuElement } from '../components/menu/UMenu';
4
4
  export declare const UMenu: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UMenuElement>, keyof React.HTMLAttributes<UMenuElement>>
6
6
  & Omit<React.HTMLAttributes<UMenuElement>, 'onChange'>
7
+ & React.RefAttributes<UMenuElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -5,6 +5,7 @@ import { type PickEventDetail } from '../events/PickEvent';
5
5
  export declare const UMenuItem: React.ForwardRefExoticComponent<
6
6
  Omit<Partial<UMenuItemElement>, keyof React.HTMLAttributes<UMenuItemElement>>
7
7
  & Omit<React.HTMLAttributes<UMenuItemElement>, 'onPick'>
8
+ & React.RefAttributes<UMenuItemElement>
8
9
  & {
9
10
  onPick?: (event: CustomEvent<PickEventDetail>) => void;
10
11
  }
@@ -4,6 +4,7 @@ import { UOption as UOptionElement } from '../components/option/UOption';
4
4
  export declare const UOption: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UOptionElement>, keyof React.HTMLAttributes<UOptionElement>>
6
6
  & Omit<React.HTMLAttributes<UOptionElement>, never>
7
+ & React.RefAttributes<UOptionElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UPanel as UPanelElement } from '../components/panel/UPanel';
4
4
  export declare const UPanel: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UPanelElement>, keyof React.HTMLAttributes<UPanelElement>>
6
6
  & Omit<React.HTMLAttributes<UPanelElement>, never>
7
+ & React.RefAttributes<UPanelElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UPopover as UPopoverElement } from '../components/popover/UPopover';
4
4
  export declare const UPopover: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UPopoverElement>, keyof React.HTMLAttributes<UPopoverElement>>
6
6
  & Omit<React.HTMLAttributes<UPopoverElement>, 'onShow' | 'onHide'>
7
+ & React.RefAttributes<UPopoverElement>
7
8
  & {
8
9
  onShow?: (event: CustomEvent) => void;
9
10
  onHide?: (event: CustomEvent) => void;
@@ -4,6 +4,7 @@ import { UProgressBar as UProgressBarElement } from '../components/progress-bar/
4
4
  export declare const UProgressBar: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UProgressBarElement>, keyof React.HTMLAttributes<UProgressBarElement>>
6
6
  & Omit<React.HTMLAttributes<UProgressBarElement>, never>
7
+ & React.RefAttributes<UProgressBarElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UProgressRing as UProgressRingElement } from '../components/progress-ri
4
4
  export declare const UProgressRing: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UProgressRingElement>, keyof React.HTMLAttributes<UProgressRingElement>>
6
6
  & Omit<React.HTMLAttributes<UProgressRingElement>, never>
7
+ & React.RefAttributes<UProgressRingElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { URadio as URadioElement } from '../components/radio/URadio';
4
4
  export declare const URadio: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<URadioElement>, keyof React.HTMLAttributes<URadioElement>>
6
6
  & Omit<React.HTMLAttributes<URadioElement>, 'onChange'>
7
+ & React.RefAttributes<URadioElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -4,6 +4,7 @@ import { URating as URatingElement } from '../components/rating/URating';
4
4
  export declare const URating: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<URatingElement>, keyof React.HTMLAttributes<URatingElement>>
6
6
  & Omit<React.HTMLAttributes<URatingElement>, 'onChange'>
7
+ & React.RefAttributes<URatingElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -4,6 +4,7 @@ import { USelect as USelectElement } from '../components/select/USelect';
4
4
  export declare const USelect: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<USelectElement>, keyof React.HTMLAttributes<USelectElement>>
6
6
  & Omit<React.HTMLAttributes<USelectElement>, 'onChange'>
7
+ & React.RefAttributes<USelectElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -4,6 +4,7 @@ import { USkeleton as USkeletonElement } from '../components/skeleton/USkeleton'
4
4
  export declare const USkeleton: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<USkeletonElement>, keyof React.HTMLAttributes<USkeletonElement>>
6
6
  & Omit<React.HTMLAttributes<USkeletonElement>, never>
7
+ & React.RefAttributes<USkeletonElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { USlider as USliderElement } from '../components/slider/USlider';
4
4
  export declare const USlider: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<USliderElement>, keyof React.HTMLAttributes<USliderElement>>
6
6
  & Omit<React.HTMLAttributes<USliderElement>, 'onChange'>
7
+ & React.RefAttributes<USliderElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -4,6 +4,7 @@ import { USpinner as USpinnerElement } from '../components/spinner/USpinner';
4
4
  export declare const USpinner: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<USpinnerElement>, keyof React.HTMLAttributes<USpinnerElement>>
6
6
  & Omit<React.HTMLAttributes<USpinnerElement>, never>
7
+ & React.RefAttributes<USpinnerElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -5,6 +5,7 @@ import { type ShiftEventDetail } from '../events/ShiftEvent';
5
5
  export declare const USplitPanel: React.ForwardRefExoticComponent<
6
6
  Omit<Partial<USplitPanelElement>, keyof React.HTMLAttributes<USplitPanelElement>>
7
7
  & Omit<React.HTMLAttributes<USplitPanelElement>, 'onShiftStart' | 'onShift' | 'onShiftEnd'>
8
+ & React.RefAttributes<USplitPanelElement>
8
9
  & {
9
10
  onShiftStart?: (event: CustomEvent<ShiftEventDetail>) => void;
10
11
  onShift?: (event: CustomEvent<ShiftEventDetail>) => void;
@@ -4,6 +4,7 @@ import { USwitch as USwitchElement } from '../components/switch/USwitch';
4
4
  export declare const USwitch: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<USwitchElement>, keyof React.HTMLAttributes<USwitchElement>>
6
6
  & Omit<React.HTMLAttributes<USwitchElement>, 'onChange'>
7
+ & React.RefAttributes<USwitchElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -5,6 +5,7 @@ import { type RemoveEventDetail } from '../events/RemoveEvent';
5
5
  export declare const UTab: React.ForwardRefExoticComponent<
6
6
  Omit<Partial<UTabElement>, keyof React.HTMLAttributes<UTabElement>>
7
7
  & Omit<React.HTMLAttributes<UTabElement>, 'onRemove'>
8
+ & React.RefAttributes<UTabElement>
8
9
  & {
9
10
  onRemove?: (event: CustomEvent<RemoveEventDetail>) => void;
10
11
  }
@@ -4,6 +4,7 @@ import { UTabPanel as UTabPanelElement } from '../components/tab-panel/UTabPanel
4
4
  export declare const UTabPanel: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UTabPanelElement>, keyof React.HTMLAttributes<UTabPanelElement>>
6
6
  & Omit<React.HTMLAttributes<UTabPanelElement>, 'onChange'>
7
+ & React.RefAttributes<UTabPanelElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -4,6 +4,7 @@ import { UTag as UTagElement } from '../components/tag/UTag';
4
4
  export declare const UTag: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UTagElement>, keyof React.HTMLAttributes<UTagElement>>
6
6
  & Omit<React.HTMLAttributes<UTagElement>, never>
7
+ & React.RefAttributes<UTagElement>
7
8
  & {
8
9
  }
9
10
  >;
@@ -4,6 +4,7 @@ import { UTextarea as UTextareaElement } from '../components/textarea/UTextarea'
4
4
  export declare const UTextarea: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UTextareaElement>, keyof React.HTMLAttributes<UTextareaElement>>
6
6
  & Omit<React.HTMLAttributes<UTextareaElement>, 'onInput' | 'onChange'>
7
+ & React.RefAttributes<UTextareaElement>
7
8
  & {
8
9
  onInput?: (event: CustomEvent) => void;
9
10
  onChange?: (event: CustomEvent) => void;
@@ -4,6 +4,7 @@ import { UTooltip as UTooltipElement } from '../components/tooltip/UTooltip';
4
4
  export declare const UTooltip: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UTooltipElement>, keyof React.HTMLAttributes<UTooltipElement>>
6
6
  & Omit<React.HTMLAttributes<UTooltipElement>, 'onShow' | 'onHide'>
7
+ & React.RefAttributes<UTooltipElement>
7
8
  & {
8
9
  onShow?: (event: CustomEvent) => void;
9
10
  onHide?: (event: CustomEvent) => void;
@@ -4,6 +4,7 @@ import { UTree as UTreeElement } from '../components/tree/UTree';
4
4
  export declare const UTree: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<UTreeElement>, keyof React.HTMLAttributes<UTreeElement>>
6
6
  & Omit<React.HTMLAttributes<UTreeElement>, 'onChange'>
7
+ & React.RefAttributes<UTreeElement>
7
8
  & {
8
9
  onChange?: (event: CustomEvent) => void;
9
10
  }
@@ -8,6 +8,7 @@ import { type CheckEventDetail } from '../events/CheckEvent';
8
8
  export declare const UTreeItem: React.ForwardRefExoticComponent<
9
9
  Omit<Partial<UTreeItemElement>, keyof React.HTMLAttributes<UTreeItemElement>>
10
10
  & Omit<React.HTMLAttributes<UTreeItemElement>, 'onExpand' | 'onCollapse' | 'onPick' | 'onCheck'>
11
+ & React.RefAttributes<UTreeItemElement>
11
12
  & {
12
13
  onExpand?: (event: CustomEvent<ExpandEventDetail>) => void;
13
14
  onCollapse?: (event: CustomEvent<CollapseEventDetail>) => 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.1",
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",
@@ -199,11 +199,17 @@ export const ${className} = createComponent({
199
199
  // Partial<Element> 은 DOM 프로퍼티(children: HTMLCollection 등)를 포함해 React 의 JSX
200
200
  // children/이벤트/className 등과 충돌한다. keyof React.HTMLAttributes 를 제거해 컴포넌트
201
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
+ // 이것을 빠뜨리면 **런타임은 되는데 타입만 거부하는** 괴리가 생긴다.
202
207
  const dts = `${dtsImports.join('\n')}
203
208
 
204
209
  export declare const ${className}: React.ForwardRefExoticComponent<
205
210
  Omit<Partial<${className}Element>, keyof React.HTMLAttributes<${className}Element>>
206
211
  & Omit<React.HTMLAttributes<${className}Element>, ${eventKeyUnion}>
212
+ & React.RefAttributes<${className}Element>
207
213
  & {
208
214
  ${eventTypes} }
209
215
  >;
@@ -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 |