@lawkit/ui 0.1.58 → 0.1.59

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.
@@ -0,0 +1,21 @@
1
+ export interface UseControllableStateOptions<T> {
2
+ /** 제어 값 — `undefined`면 uncontrolled 모드 */
3
+ value?: T;
4
+ /** uncontrolled 모드 초기값 (lazy initializer 지원) */
5
+ defaultValue: T | (() => T);
6
+ /** 변경 콜백 — controlled/uncontrolled 무관하게 항상 호출 (기존 컴포넌트 동작) */
7
+ onChange?: (value: T) => void;
8
+ }
9
+ /**
10
+ * controlled(`value` 전달) / uncontrolled(`defaultValue`) 상태를 통합하는 훅.
11
+ *
12
+ * 기존 컴포넌트들의 패턴을 그대로 추출:
13
+ * ```ts
14
+ * const isControlled = controlledValue !== undefined;
15
+ * const value = isControlled ? controlledValue : internalValue;
16
+ * const set = (next) => { if (!isControlled) setInternalValue(next); onChange?.(next); };
17
+ * ```
18
+ *
19
+ * 반환된 setter는 React의 state setter처럼 참조가 안정적이다.
20
+ */
21
+ export declare function useControllableState<T>({ value, defaultValue, onChange, }: UseControllableStateOptions<T>): [T, (next: T) => void];
@@ -0,0 +1,27 @@
1
+ import { RefObject } from 'react';
2
+ export interface UseDismissibleLayerOptions {
3
+ /** 레이어 활성 여부 (보통 open) */
4
+ enabled: boolean;
5
+ /** dismiss 동작 (ESC / 바깥 클릭 공통) */
6
+ onDismiss: () => void;
7
+ /** 바깥 클릭 판정 기준 요소 — `closeOnOutsideClick`일 때 필수 */
8
+ ref?: RefObject<HTMLElement | null>;
9
+ /** Escape 키로 닫기 (기본 true) */
10
+ closeOnEscape?: boolean;
11
+ /** 바깥 mousedown으로 닫기 (기본 true, `ref` 필요) */
12
+ closeOnOutsideClick?: boolean;
13
+ /** ESC 처리 시 `e.stopPropagation()` 호출 (Modal 계열 기존 동작) */
14
+ stopEscapePropagation?: boolean;
15
+ /** ESC 전용 dismiss — 지정 시 ESC에서는 `onDismiss` 대신 호출 (CalendarPopover의 onClose 동반 호출용) */
16
+ onEscape?: () => void;
17
+ }
18
+ /**
19
+ * 오버레이 dismiss(ESC 키 + 바깥 mousedown) 감지 훅.
20
+ *
21
+ * 기존 컴포넌트들이 각자 등록하던 document 레벨 리스너를 추출한 것으로,
22
+ * 판정/등록 방식을 그대로 유지한다:
23
+ * - 바깥 클릭: `mousedown`에서 `ref.current.contains(e.target)` 검사
24
+ * - ESC: `keydown`에서 `e.key === "Escape"` 검사
25
+ * - 각 오버레이가 독립적으로 리스너를 갖는 구조 (중첩 시 각자 닫힘 — 기존과 동일)
26
+ */
27
+ export declare function useDismissibleLayer({ enabled, onDismiss, ref, closeOnEscape, closeOnOutsideClick, stopEscapePropagation, onEscape, }: UseDismissibleLayerOptions): void;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 닫힘 트랜지션 동안 마운트를 유지하는 훅.
3
+ *
4
+ * open → 즉시 마운트, close → `exiting` 상태로 전환 클래스 적용 후
5
+ * `exitDuration`(ms) 타이머가 끝나면 언마운트. (jsdom에서 transitionend가
6
+ * 오지 않는 문제를 피하려고 기존 Drawer가 쓰던 setTimeout 방식 그대로)
7
+ *
8
+ * @returns `mounted` — 렌더 유지 여부(`open || exiting`), `exiting` — 닫힘 트랜지션 중 여부
9
+ */
10
+ export declare function usePresence(open: boolean, exitDuration: number): {
11
+ mounted: boolean;
12
+ exiting: boolean;
13
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * body 스크롤락 획득/복원 훅.
3
+ *
4
+ * 기존 Modal/SweetAlert/FullScreenModal/Drawer의 패턴 그대로:
5
+ * 활성화 시 `document.body.style.overflow`를 `"hidden"`으로 바꾸고,
6
+ * 해제 시 이전 인라인 값으로 복원한다.
7
+ * (중첩 시 안쪽 오버레이가 바깥의 `"hidden"`을 이전 값으로 복원 — 기존 동작 동일)
8
+ */
9
+ export declare function useScrollLock(enabled: boolean): void;
@@ -3,27 +3,27 @@ export declare const sprinkles: ((props: {
3
3
  mobile?: "flex" | "grid" | "block" | "inline-flex" | "none" | undefined;
4
4
  tablet?: "flex" | "grid" | "block" | "inline-flex" | "none" | undefined;
5
5
  desktop?: "flex" | "grid" | "block" | "inline-flex" | "none" | undefined;
6
- } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<2 | 1 | 3, "flex" | "grid" | "block" | "inline-flex" | "none" | null>;
6
+ } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<1 | 2 | 3, "flex" | "grid" | "block" | "inline-flex" | "none" | null>;
7
7
  alignItems?: ("stretch" | "center" | {
8
8
  mobile?: "stretch" | "center" | undefined;
9
9
  tablet?: "stretch" | "center" | undefined;
10
10
  desktop?: "stretch" | "center" | undefined;
11
- } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<2 | 1 | 3, "stretch" | "center" | null>;
11
+ } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<1 | 2 | 3, "stretch" | "center" | null>;
12
12
  justifyContent?: ("space-between" | "center" | "flex-start" | {
13
13
  mobile?: "space-between" | "center" | "flex-start" | undefined;
14
14
  tablet?: "space-between" | "center" | "flex-start" | undefined;
15
15
  desktop?: "space-between" | "center" | "flex-start" | undefined;
16
- } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<2 | 1 | 3, "space-between" | "center" | "flex-start" | null>;
16
+ } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<1 | 2 | 3, "space-between" | "center" | "flex-start" | null>;
17
17
  width?: ("100%" | {
18
18
  mobile?: "100%" | undefined;
19
19
  tablet?: "100%" | undefined;
20
20
  desktop?: "100%" | undefined;
21
- } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<2 | 1 | 3, "100%" | null>;
21
+ } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<1 | 2 | 3, "100%" | null>;
22
22
  gap?: ("x1" | "x2" | "x3" | "x4" | "x5" | "x6" | {
23
23
  mobile?: "x1" | "x2" | "x3" | "x4" | "x5" | "x6" | undefined;
24
24
  tablet?: "x1" | "x2" | "x3" | "x4" | "x5" | "x6" | undefined;
25
25
  desktop?: "x1" | "x2" | "x3" | "x4" | "x5" | "x6" | undefined;
26
- } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<2 | 1 | 3, "x1" | "x2" | "x3" | "x4" | "x5" | "x6" | null>;
26
+ } | undefined) | import('@vanilla-extract/sprinkles').ResponsiveArray<1 | 2 | 3, "x1" | "x2" | "x3" | "x4" | "x5" | "x6" | null>;
27
27
  }) => string) & {
28
28
  properties: Set<"alignItems" | "display" | "justifyContent" | "width" | "gap">;
29
29
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawkit/ui",
3
- "version": "0.1.58",
3
+ "version": "0.1.59",
4
4
  "type": "module",
5
5
  "description": "LDS Design System — React component library with design tokens",
6
6
  "main": "./dist/index.js",