@telepix-lab/telepix-ui 0.7.0 → 0.7.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.
package/dist/esm/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { forwardRef, useRef, Fragment, createElement, useState } from 'react';
2
+ import { forwardRef, useEffect, useRef, useId, Fragment, createElement, useState } from 'react';
3
+ import { id } from 'react-day-picker/locale';
3
4
 
4
5
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
5
6
 
@@ -3303,28 +3304,28 @@ function cn(...inputs) {
3303
3304
  const StateColorContainer = ({ groupName = "state", isInverted, className, }) => {
3304
3305
  const fillClassMap = {
3305
3306
  wrapper: {
3306
- normal: "group-hover/wrapper:bg-fill-hovered bg-fill-default group-active/wrapper:bg-fill-pressed group-aria-selected/wrapper:bg-fill-selected",
3307
- invert: "group-hover/wrapper:bg-fill-invert-hovered group-active/wrapper:bg-fill-invert-pressed bg-fill-invert-default group-aria-selected/wrapper:bg-fill-invert-selected",
3307
+ normal: "group-hover/wrapper:bg-fill-hovered bg-fill-default group-active/wrapper:bg-fill-pressed group-data-[focused=true]/wrapper:bg-fill-selected",
3308
+ invert: "group-hover/wrapper:bg-fill-invert-hovered group-active/wrapper:bg-fill-invert-pressed bg-fill-invert-default group-data-[focused=true]/wrapper:bg-fill-invert-selected",
3308
3309
  },
3309
3310
  item: {
3310
- normal: "group-hover/item:bg-fill-hovered bg-fill-default group-active/item:bg-fill-pressed group-aria-selected/item:bg-fill-selected",
3311
- invert: "group-hover/item:bg-fill-invert-hovered group-active/item:bg-fill-invert-pressed bg-fill-invert-default group-aria-selected/item:bg-fill-invert-selected",
3311
+ normal: "group-hover/item:bg-fill-hovered bg-fill-default group-active/item:bg-fill-pressed group-data-[focused=true]/item:bg-fill-selected",
3312
+ invert: "group-hover/item:bg-fill-invert-hovered group-active/item:bg-fill-invert-pressed bg-fill-invert-default group-data-[focused=true]/item:bg-fill-invert-selected",
3312
3313
  },
3313
3314
  option: {
3314
- normal: "group-hover/option:bg-fill-hovered bg-fill-default group-active/option:bg-fill-pressed group-aria-selected/option:bg-fill-selected",
3315
- invert: "group-hover/option:bg-fill-invert-hovered group-active/option:bg-fill-invert-pressed bg-fill-invert-default group-aria-selected/option:bg-fill-invert-selected",
3315
+ normal: "group-hover/option:bg-fill-hovered bg-fill-default group-active/option:bg-fill-pressed group-data-[focused=true]/option:bg-fill-selected",
3316
+ invert: "group-hover/option:bg-fill-invert-hovered group-active/option:bg-fill-invert-pressed bg-fill-invert-default group-data-[focused=true]/option:bg-fill-invert-selected",
3316
3317
  },
3317
3318
  tab: {
3318
- normal: "group-hover/tab:bg-fill-hovered bg-fill-default group-active/tab:bg-fill-pressed group-aria-selected/tab:bg-fill-selected",
3319
- invert: "group-hover/tab:bg-fill-invert-hovered group-active/tab:bg-fill-invert-pressed bg-fill-invert-default group-aria-selected/tab:bg-fill-invert-selected",
3319
+ normal: "group-hover/tab:bg-fill-hovered bg-fill-default group-active/tab:bg-fill-pressed group-data-[focused=true]/tab:bg-fill-selected",
3320
+ invert: "group-hover/tab:bg-fill-invert-hovered group-active/tab:bg-fill-invert-pressed bg-fill-invert-default group-data-[focused=true]/tab:bg-fill-invert-selected",
3320
3321
  },
3321
3322
  chip: {
3322
- normal: "group-hover/chip:bg-fill-hovered bg-fill-default group-active/chip:bg-fill-pressed group-aria-selected/chip:bg-fill-selected",
3323
- invert: "group-hover/chip:bg-fill-invert-hovered group-active/chip:bg-fill-invert-pressed bg-fill-invert-default group-aria-selected/chip:bg-fill-invert-selected",
3323
+ normal: "group-hover/chip:bg-fill-hovered bg-fill-default group-active/chip:bg-fill-pressed group-data-[focused=true]/chip:bg-fill-selected",
3324
+ invert: "group-hover/chip:bg-fill-invert-hovered group-active/chip:bg-fill-invert-pressed bg-fill-invert-default group-data-[focused=true]/chip:bg-fill-invert-selected",
3324
3325
  },
3325
3326
  state: {
3326
- normal: "group-hover/state:bg-fill-hovered bg-fill-default group-active/state:bg-fill-pressed group-aria-selected/state:bg-fill-selected",
3327
- invert: "group-hover/state:bg-fill-invert-hovered group-active/state:bg-fill-invert-pressed bg-fill-invert-default group-aria-selected/state:bg-fill-invert-selected",
3327
+ normal: "group-hover/state:bg-fill-hovered bg-fill-default group-active/state:bg-fill-pressed group-data-[focused=true]/state:bg-fill-selected",
3328
+ invert: "group-hover/state:bg-fill-invert-hovered group-active/state:bg-fill-invert-pressed bg-fill-invert-default group-data-[focused=true]/state:bg-fill-invert-selected",
3328
3329
  },
3329
3330
  };
3330
3331
  const group = groupName || "state";
@@ -3372,25 +3373,25 @@ const Button = forwardRef(({ variant = "accent", size = "regular", fullWidth = f
3372
3373
  if (variant === BUTTON_VARIANTS.TEXT_DIM) {
3373
3374
  return [
3374
3375
  "h-auto py-0.5 px-0 text-body leading-body-compact gap-1.5",
3375
- "bg-page-l-null text-comp-mono-subtle-default hover:text-comp-mono-default aria-selected:text-comp-mono-default disabled:bg-page-l-null disabled:text-comp-disabled disabled:hover:text-comp-disabled",
3376
+ "bg-page-l-null text-comp-mono-subtle-default hover:text-comp-mono-default data-[focused=true]:text-comp-mono-default disabled:bg-page-l-null disabled:text-comp-disabled disabled:hover:text-comp-disabled",
3376
3377
  ];
3377
3378
  }
3378
3379
  const sizeClassName = {
3379
3380
  small: "h-7 py-0 px-1 text-label leading-label-compact gap-1",
3380
3381
  regular: "h-9 py-0 px-2 text-body leading-body-compact gap-1.5",
3381
- large: "h-12 py-0 px-3 text-body leading-body-compact gap-2",
3382
+ large: "h-12 py-0 px-3 text-base leading-base-compact gap-2",
3382
3383
  }[size];
3383
3384
  const variantClassName = {
3384
3385
  accent: "bg-page-accent-l0 disabled:bg-pure-transparent text-comp-accent-default disabled:text-comp-disabled",
3385
- outline: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-default disabled:text-comp-disabled border border-border-bound disabled:border-pure-transparent aria-selected:border-border-selected",
3386
- outline_to_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-default disabled:text-comp-disabled border border-border-bound disabled:border-pure-transparent aria-selected:border-border-selected aria-selected:bg-page-accent-l0 aria-selected:text-comp-accent-default",
3386
+ outline: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-default disabled:text-comp-disabled border border-border-bound disabled:border-pure-transparent data-[focused=true]:border-border-selected",
3387
+ outline_to_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-default disabled:text-comp-disabled border border-border-bound disabled:border-pure-transparent data-[focused=true]:border-border-selected data-[focused=true]:bg-page-accent-l0 data-[focused=true]:text-comp-accent-default",
3387
3388
  ghost: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-default disabled:text-comp-disabled",
3388
3389
  thumbnail_ghost: "bg-page-l-null disabled:bg-fill-disabled text-comp-mono-default disabled:text-comp-disabled",
3389
- ghost_to_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-default disabled:text-comp-disabled aria-selected:bg-page-accent-l0 aria-selected:text-comp-accent-default",
3390
- subtle_filled: "bg-page-l3 disabled:bg-fill-disabled text-comp-mono-subtle-default disabled:text-comp-disabled aria-selected:text-comp-mono-default",
3391
- outline_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-accent-default disabled:text-comp-disabled border border-border-accent-bound disabled:border-pure-transparent aria-selected:border-border-accent-selected",
3390
+ ghost_to_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-default disabled:text-comp-disabled data-[focused=true]:bg-page-accent-l0 data-[focused=true]:text-comp-accent-default",
3391
+ subtle_filled: "bg-page-l3 disabled:bg-fill-disabled text-comp-mono-subtle-default disabled:text-comp-disabled data-[focused=true]:text-comp-mono-default",
3392
+ outline_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-accent-default disabled:text-comp-disabled border border-border-accent-bound disabled:border-pure-transparent data-[focused=true]:border-border-accent-selected",
3392
3393
  ghost_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-accent-default disabled:text-comp-disabled",
3393
- ghost_dim: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-subtle-default disabled:text-comp-disabled aria-selected:text-comp-mono-default",
3394
+ ghost_dim: "bg-page-l-null disabled:bg-page-l-null text-comp-mono-subtle-default disabled:text-comp-disabled data-[focused=true]:text-comp-mono-default",
3394
3395
  text_dim: "", // 위에서 처리되므로 여기서는 빈 문자열
3395
3396
  }[variant];
3396
3397
  return [sizeClassName, variantClassName];
@@ -3409,22 +3410,22 @@ const Button = forwardRef(({ variant = "accent", size = "regular", fullWidth = f
3409
3410
  return "size-5";
3410
3411
  return "size-6"; // 기본값은 regular 크기
3411
3412
  };
3412
- return (jsxs("button", { ref: ref, disabled: disabled ?? isLoading, className: cn("flex group/state items-center justify-center relative box-border m-0 border-transparent outline-none cursor-pointer select-none align-middle appearance-none text-center transition-normal font-medium w-auto rounded-md overflow-hidden pointer-events-auto disabled:pointer-events-none disabled:cursor-not-allowed", ...getClassName(size, variant), fullWidth && "w-full", className), ...rest, children: [variant !== BUTTON_VARIANTS.TEXT_DIM && (jsx(StateColorContainer, { isInverted: variant === BUTTON_VARIANTS.ACCENT, className: stateContainerClassName })), isLoading && (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: cn("lucide lucide-loader-circle-icon lucide-loader-circle absolute animate-spin", size === BUTTON_SIZES.SMALL &&
3413
+ return (jsxs("button", { ref: ref, disabled: disabled ?? isLoading, "aria-busy": isLoading, "aria-disabled": disabled ?? isLoading, "aria-pressed": rest["aria-pressed"], className: cn("flex group/state items-center justify-center relative box-border m-0 border-transparent outline-none border cursor-pointer select-none align-middle appearance-none text-center transition-normal font-medium w-auto rounded-md pointer-events-auto disabled:pointer-events-none disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-solid focus-visible:outline-offset-2 focus-visible:outline-border-focused", ...getClassName(size, variant), fullWidth && "w-full", className), ...rest, children: [variant !== BUTTON_VARIANTS.TEXT_DIM && (jsx(StateColorContainer, { isInverted: variant === BUTTON_VARIANTS.ACCENT, className: cn("-inset-px", stateContainerClassName) })), isLoading && (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: cn("lucide lucide-loader-circle-icon lucide-loader-circle absolute animate-spin", size === BUTTON_SIZES.SMALL &&
3413
3414
  "size-4 top-[calc(50%-8px)] left-[calc(50%-8px)]", size === BUTTON_SIZES.REGULAR &&
3414
3415
  "size-5 top-[calc(50%-10px)] left-[calc(50%-10px)]", size === BUTTON_SIZES.LARGE &&
3415
3416
  "size-6 top-[calc(50%-12px)] left-[calc(50%-12px)]"), children: jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) })), leftIcon && (jsx("div", { className: cn("flex items-center justify-center relative", variant === BUTTON_VARIANTS.THUMBNAIL_GHOST &&
3416
- "aspect-square [&_img]:rounded-sm", getThumbnailSize(variant, size)), children: leftIcon })), children && (jsx("div", { className: "flex-1", style: { visibility: isLoading ? "hidden" : "visible" }, children: children })), rightIcon && (jsx("div", { className: cn("flex items-center justify-center relative", getThumbnailSize(variant, size)), children: rightIcon }))] }));
3417
+ "aspect-square [&_img]:rounded-sm", getThumbnailSize(variant, size)), children: leftIcon })), children && (jsx("div", { className: "flex-1 truncate", style: { visibility: isLoading ? "hidden" : "visible" }, children: children })), rightIcon && (jsx("div", { className: cn("flex items-center justify-center relative", getThumbnailSize(variant, size)), children: rightIcon }))] }));
3417
3418
  });
3418
3419
  Button.displayName = "Button";
3419
3420
  /**
3420
3421
  * Text Dim 스타일의 버튼 컴포넌트입니다.<br/>
3421
3422
  * 텍스트 중심의 보조 액션(secondary / subtle action)에 적합한 low-emphasis 스타일을 제공합니다.<br/>
3422
- * 기본적으로 낮은 명도의 텍스트를 사용하며, hover 시 명도가 상승하고 selected 시 accent 색상으로 강조됩니다.<br/>
3423
+ * 기본적으로 낮은 명도의 텍스트를 사용하며, hover 시 명도가 상승하고 focused 시 accent 색상으로 강조됩니다.<br/>
3423
3424
  * <br/>
3424
3425
  * ### 사용 예시
3425
3426
  * ```tsx
3426
3427
  * <ButtonTextDim>자세히 보기</ButtonTextDim>
3427
- * <ButtonTextDim aria-selected="true">선택됨</ButtonTextDim>
3428
+ * <ButtonTextDim data-focused="true">포커스됨</ButtonTextDim>
3428
3429
  * ```
3429
3430
  */
3430
3431
  const ButtonTextDim = forwardRef((props, ref) => {
@@ -3467,6 +3468,33 @@ const CardFooter = forwardRef(({ className, ...rest }, ref) => {
3467
3468
  return (jsx("div", { ...rest, ref: ref, className: cn("py-4 px-6", className), children: rest.children }));
3468
3469
  });
3469
3470
 
3471
+ /**
3472
+ * 전역 포커스 가시성 제어 훅
3473
+ *
3474
+ * - 마우스 클릭 시: body.using-mouse 추가 → outline 제거
3475
+ * - Tab 키로 이동 시: using-mouse 제거 → outline 정상 표시
3476
+ *
3477
+ * Telepix-UI의 focus-visible outline 정책을 보완하기 위해 사용.
3478
+ */
3479
+ function useFocusVisibleMode() {
3480
+ useEffect(() => {
3481
+ const handleMouseDown = () => {
3482
+ document.body.classList.add("using-mouse");
3483
+ };
3484
+ const handleKeyDown = (e) => {
3485
+ if (e.key === "Tab") {
3486
+ document.body.classList.remove("using-mouse");
3487
+ }
3488
+ };
3489
+ window.addEventListener("mousedown", handleMouseDown);
3490
+ window.addEventListener("keydown", handleKeyDown);
3491
+ return () => {
3492
+ window.removeEventListener("mousedown", handleMouseDown);
3493
+ window.removeEventListener("keydown", handleKeyDown);
3494
+ };
3495
+ }, []);
3496
+ }
3497
+
3470
3498
  const TEXT_SIZES = {
3471
3499
  XSMALL: "xsmall",
3472
3500
  LABEL: "label",
@@ -3742,22 +3770,27 @@ const Input = forwardRef(({ size = "regular", variant = "outlined", leftIcon, ri
3742
3770
  ref.current = node;
3743
3771
  innerRef.current = node;
3744
3772
  };
3745
- return (jsxs(Fragment, { children: [jsxs("div", { "aria-disabled": rest.disabled, className: cn("relative group/state flex items-center justify-center rounded-lg border bg-Page-l-null border-border-bound focus-within:border-border-focused has-[input:disabled]:border-transparent", variant === INPUT_VARIANTS.OUTLINED &&
3773
+ const generatedId = useId();
3774
+ const inputId = id ?? `input-${generatedId}`;
3775
+ useFocusVisibleMode();
3776
+ return (jsxs(Fragment, { children: [jsxs("div", { "aria-disabled": rest.disabled, className: cn("relative group/state flex items-center justify-center rounded-lg border bg-Page-l-null border-border-bound has-[input:disabled]:border-transparent has-input-focus-visible", variant === INPUT_VARIANTS.OUTLINED &&
3746
3777
  "border border-border-bound focus-within:border-border-focused", variant === INPUT_VARIANTS.FILLED &&
3747
- "bg-page-l3 border-transparent focus-within:border-transparent", size === INPUT_SIZES.SMALL &&
3778
+ "bg-page-l3 border-transparent focus-within:border-border-focused", size === INPUT_SIZES.SMALL &&
3748
3779
  "py-1.5 px-2 text-label leading-label-compact font-medium gap-1", size === INPUT_SIZES.REGULAR &&
3749
3780
  "py-1.5 px-2 text-body leading-body-compact font-medium gap-2", size === INPUT_SIZES.LARGE &&
3750
3781
  "py-[11px] px-4 text-base leading-base-compact font-medium gap-3", isError &&
3751
- "border-comp-chroma-error focus-within:border-comp-chroma-error", wrapperClassName), onClick: () => {
3782
+ "border-comp-chroma-error focus-within:border-comp-chroma-error focus-within:outline-comp-chroma-error", wrapperClassName), onClick: () => {
3752
3783
  innerRef.current?.focus();
3753
- }, children: [jsx(StateColorContainer, { className: cn("group-focus-within/state:bg-page-l-null", (!useHoverStyle || rest.disabled) && "hover:bg-fill-default") }), leftIcon && (jsx("div", { className: cn("flex items-center justify-center text-comp-mono-default", isError && "text-comp-chroma-error"), children: leftIcon })), jsx("input", { ref: mergedRef, className: cn("border-none outline-none p-0 flex-1 bg-transparent min-w-0 text-comp-mono-default placeholder:text-comp-mono-subtle-default focus:placeholder:text-comp-mono-subtle-selected disabled:text-comp-disabled disabled:placeholder:text-comp-disabled", useHoverStyle &&
3784
+ }, children: [jsx(StateColorContainer, { className: cn("group-focus-within/state:bg-page-l-null", (!useHoverStyle || rest.disabled) && "hover:bg-fill-default") }), leftIcon && (jsx("div", { className: cn("flex items-center justify-center text-comp-mono-default", isError && "text-comp-chroma-error"), children: leftIcon })), jsx("input", { id: inputId.toString(), ref: mergedRef, "aria-invalid": isError, "aria-describedby": isError && errorMessage
3785
+ ? `${rest.id || "input"}-error`
3786
+ : undefined, className: cn("border-none outline-none p-0 flex-1 bg-transparent min-w-0 text-comp-mono-default placeholder:text-comp-mono-subtle-default focus:placeholder:text-comp-mono-subtle-selected disabled:text-comp-disabled disabled:placeholder:text-comp-disabled focus-visible:outline-none", useHoverStyle &&
3754
3787
  "hover:placeholder:text-comp-mono-subtle-hovered", size === INPUT_SIZES.SMALL &&
3755
3788
  "text-label leading-label-compact font-medium", size === INPUT_SIZES.REGULAR &&
3756
3789
  "text-body leading-body-compact font-medium", size === INPUT_SIZES.LARGE &&
3757
3790
  "text-base leading-base-compact font-medium", isError &&
3758
3791
  "placeholder:text-comp-chroma-error text-comp-chroma-error hover:placeholder:text-comp-error focus:placeholder:text-comp-error", rest.type === "number" &&
3759
3792
  !useIndicator &&
3760
- "[-moz-appearance:_textfield] [&::-webkit-inner-spin-button]:m-0 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:m-0 [&::-webkit-outer-spin-button]:appearance-none", rest.className), ...rest }), rightIcon && (jsx("div", { className: cn("flex items-center justify-center text-comp-mono-default", isError && "text-comp-chroma-error"), children: rightIcon }))] }), errorMessage && (jsx(Text, { variant: "medLabelMedCompact", className: "text-comp-chroma-error mt-2", children: errorMessage }))] }));
3793
+ "[-moz-appearance:_textfield] [&::-webkit-inner-spin-button]:m-0 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:m-0 [&::-webkit-outer-spin-button]:appearance-none", rest.className), ...rest }), rightIcon && (jsx("div", { className: cn("flex items-center justify-center text-comp-mono-default", isError && "text-comp-chroma-error"), children: rightIcon }))] }), errorMessage && (jsx(Text, { id: `${inputId}-error`, variant: "medLabelMedCompact", className: "text-comp-chroma-error mt-2", role: "alert", "aria-live": "polite", children: errorMessage }))] }));
3761
3794
  });
3762
3795
  Input.displayName = "Input";
3763
3796
 
@@ -3765,8 +3798,10 @@ const NOTIFY_ITEM_VARIANTS = {
3765
3798
  GHOST: "ghost",
3766
3799
  DYNAMIC_ACCENT: "dynamicAccent",
3767
3800
  };
3768
- const NotifyItem = forwardRef(({ title, description, timestamp, icon, actionIcon, variant = NOTIFY_ITEM_VARIANTS.GHOST, unread = false, disabled, className, ...rest }, ref) => {
3769
- return (jsxs("div", { ref: ref, "data-unread": unread, "data-disabled": disabled, className: cn("relative group/state inline-flex items-center min-h-9 w-full p-1.5 rounded cursor-pointer border border-transparent", variant === NOTIFY_ITEM_VARIANTS.GHOST && [
3801
+ const NotifyItem = forwardRef(({ title, description, timestamp, icon, actionIcon, variant = NOTIFY_ITEM_VARIANTS.GHOST, unread = false, disabled, className, onClick, ...rest }, ref) => {
3802
+ const ariaLabel = rest["aria-label"] ||
3803
+ `${title}. ${description}${unread ? ". 읽지 않음" : ". 읽음"}${timestamp ? `. ${timestamp}` : ""}`;
3804
+ return (jsxs("div", { ref: ref, role: onClick ? "button" : "article", tabIndex: onClick && !disabled ? 0 : undefined, "data-unread": unread, "data-disabled": disabled, "aria-label": ariaLabel, "aria-disabled": disabled, "aria-readonly": !onClick, "aria-live": unread ? "polite" : "off", className: cn("relative group/state inline-flex items-center min-h-9 w-full p-1.5 rounded cursor-pointer border border-transparent", variant === NOTIFY_ITEM_VARIANTS.GHOST && [
3770
3805
  !disabled && "bg-page-l-null *:text-comp-mono-subtle-default",
3771
3806
  "data-[unread=true]:border-border-bound data-[unread=true]:bg-page-l3 data-[unread=true]:*:text-comp-mono-default",
3772
3807
  ], variant === NOTIFY_ITEM_VARIANTS.DYNAMIC_ACCENT && [
@@ -3774,7 +3809,16 @@ const NotifyItem = forwardRef(({ title, description, timestamp, icon, actionIcon
3774
3809
  "data-[unread=true]:bg-page-accent-l0 data-[unread=true]:*:text-comp-accent-default",
3775
3810
  ], disabled && [
3776
3811
  "cursor-not-allowed pointer-events-none bg-page-l-null *:text-comp-disabled",
3777
- ], className), ...rest, children: [icon && (jsx("div", { className: "size-9 flex justify-center items-center flex-shrink-0", children: icon })), jsxs("div", { className: "flex-1 self-stretch px-1.5 inline-flex flex-col justify-center items-start gap-1 min-w-0 text-inherit", children: [jsx(Text, { variant: "medBodyMedCompact", className: "text-inherit", children: title }), jsx(Text, { variant: "regularLabelCompact", className: cn("text-inherit", variant === NOTIFY_ITEM_VARIANTS.GHOST &&
3812
+ ], onClick &&
3813
+ !disabled &&
3814
+ "focus-visible:outline focus-visible:outline-offset-2 focus-visible:outline-border-focused focus-visible:outline-solid", className), onClick: disabled ? undefined : onClick, onKeyDown: onClick && !disabled
3815
+ ? (e) => {
3816
+ if (e.key === "Enter" || e.key === " ") {
3817
+ e.preventDefault();
3818
+ onClick(e);
3819
+ }
3820
+ }
3821
+ : undefined, ...rest, children: [icon && (jsx("div", { className: "size-9 flex justify-center items-center flex-shrink-0", children: icon })), jsxs("div", { className: "flex-1 self-stretch px-1.5 inline-flex flex-col justify-center items-start gap-1 min-w-0 text-inherit", children: [jsx(Text, { variant: "medBodyMedCompact", className: "text-inherit", children: title }), jsx(Text, { variant: "regularLabelCompact", className: cn("text-inherit", variant === NOTIFY_ITEM_VARIANTS.GHOST &&
3778
3822
  "group-data-[unread=true]/state:text-comp-mono-subtle-default", variant === NOTIFY_ITEM_VARIANTS.DYNAMIC_ACCENT &&
3779
3823
  !disabled &&
3780
3824
  "group-data-[unread=false]/state:text-comp-mono-subtle-default"), children: description })] }), timestamp && (jsx("div", { className: "h-9 flex justify-center items-center flex-shrink-0", children: jsx(Text, { variant: "medLabelMedCompact", className: cn("text-inherit", variant === NOTIFY_ITEM_VARIANTS.DYNAMIC_ACCENT &&
@@ -3953,7 +3997,10 @@ const PaginationItem = (props) => {
3953
3997
  return jsx("li", { "data-slot": "pagination-item", ...props });
3954
3998
  };
3955
3999
  const PaginationLink = ({ className, isActive, size = "regular", variant = "ghost", ...rest }) => {
3956
- return (jsx(Button, { "aria-current": isActive ? "page" : undefined, "data-slot": "pagination-link", "data-active": isActive, size: size, variant: variant, className: cn("h-9 min-h-9", isActive && "bg-fill-selected text-comp-mono-default", className), ...rest }));
4000
+ return (jsx(Button, { "aria-current": isActive ? "page" : undefined, "aria-label": rest["aria-label"] ||
4001
+ (isActive
4002
+ ? `현재 페이지, ${rest.children}`
4003
+ : `페이지 ${rest.children}로 이동`), "data-slot": "pagination-link", "data-active": isActive, size: size, variant: variant, className: cn("h-9 min-h-9", isActive && "bg-fill-selected text-comp-mono-default", className), ...rest }));
3957
4004
  };
3958
4005
  const PaginationPrevious = ({ className, ...rest }) => {
3959
4006
  return (jsx(PaginationLink, { "aria-label": "Go to previous page", className: cn("flex items-center justify-center mr-4", className), leftIcon: jsx(ChevronLeft, { size: 20 }), ...rest }));
@@ -3962,14 +4009,14 @@ const PaginationNext = ({ className, ...rest }) => {
3962
4009
  return (jsx(PaginationLink, { "aria-label": "Go to next page", className: cn("ml-4", className), leftIcon: jsx(ChevronRight, { size: 20 }), ...rest }));
3963
4010
  };
3964
4011
  const PaginationEllipsis = ({ className, ...rest }) => {
3965
- return (jsx("span", { "aria-hidden": true, "data-slot": "pagination-ellipsis", className: cn("flex items-center justify-center size-9 rounded-md hover:bg-fill-mono-hovered", className), ...rest, children: jsx(Ellipsis, { className: "size-4 stroke-comp-mono-subtle-default" }) }));
4012
+ return (jsx("span", { "aria-hidden": "true", role: "presentation", "data-slot": "pagination-ellipsis", className: cn("flex items-center justify-center size-9 rounded-md hover:bg-fill-mono-hovered", className), ...rest, children: jsx(Ellipsis, { className: "size-4 stroke-comp-mono-subtle-default", "aria-hidden": "true", focusable: false }) }));
3966
4013
  };
3967
4014
 
3968
4015
  const Tag = forwardRef(({ icon, value, variant = "filled", size = "regular", className, onDeleteClick, onClick, useSelectedStyle = false, ...props }, ref) => {
3969
- const [isSelected, setIsSelected] = useState(false);
4016
+ const [isFocused, setIsFocused] = useState(false);
3970
4017
  const handleClick = (event) => {
3971
4018
  if (useSelectedStyle)
3972
- setIsSelected((prev) => !prev);
4019
+ setIsFocused((prev) => !prev);
3973
4020
  onClick?.(event);
3974
4021
  };
3975
4022
  const handleDeleteClick = (event) => {
@@ -3980,7 +4027,9 @@ const Tag = forwardRef(({ icon, value, variant = "filled", size = "regular", cla
3980
4027
  `bg-page-l4 text-comp-mono-subtle-default data-disabled:text-comp-disabled data-disabled:bg-page-l-null`, variant === "accent" &&
3981
4028
  `bg-page-accent-l0 text-comp-accent-default data-disabled:text-comp-disabled data-disabled:bg-page-l-null`, size === "regular" &&
3982
4029
  "text-body font-medium leading-body-compact px-2 py-1 gap-2", size === "small" &&
3983
- "text-label font-medium leading-label-compact px-1.5 py-0.5 gap-1.5", className), onClick: handleClick, "data-selected": isSelected, "aria-selected": isSelected, ...props, children: [icon && (jsx("span", { className: "flex items-center justify-center size-4 border-none bg-transparent outline-none p-0", children: icon })), jsx("span", { className: "flex-1 text-nowrap overflow-hidden text-ellipsis text-inherit text-size-inherit leading-inherit font-inherit", children: value.label }), onDeleteClick && (jsx("span", { className: "flex items-center justify-center size-4 border-none bg-transparent outline-none p-0 cursor-pointer text-comp-mono-default group-disabled/state:text-comp-disabled", onClick: handleDeleteClick, children: jsx(X, {}) })), jsx(StateColorContainer, { className: cn(useSelectedStyle
4030
+ "text-label font-medium leading-label-compact px-1.5 py-0.5 gap-1.5", className), onClick: handleClick, "data-focused": isFocused, ...props, children: [icon && (jsx("span", { className: "flex items-center justify-center size-4 border-none bg-transparent outline-none p-0", children: icon })), jsx("span", { className: cn("flex-1 truncate text-inherit text-size-inherit font-inherit", size === "small" &&
4031
+ "leading-[calc(var(--spacing-label-compact)+3px)]", size === "regular" &&
4032
+ "leading-[calc(var(--spacing-body-compact)+3px)]"), children: value.label }), onDeleteClick && (jsx("span", { className: "flex items-center justify-center size-4 border-none bg-transparent outline-none p-0 cursor-pointer text-inherit group-disabled/state:text-comp-disabled", onClick: handleDeleteClick, children: jsx(X, {}) })), jsx(StateColorContainer, { className: cn(useSelectedStyle
3984
4033
  ? ""
3985
4034
  : "group-active/state:bg-fill-default group-active/state:border-transparent group-disabled/state:bg-fill-default"), isInverted: variant === "accent" })] }));
3986
4035
  });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 전역 포커스 가시성 제어 훅
3
+ *
4
+ * - 마우스 클릭 시: body.using-mouse 추가 → outline 제거
5
+ * - Tab 키로 이동 시: using-mouse 제거 → outline 정상 표시
6
+ *
7
+ * Telepix-UI의 focus-visible outline 정책을 보완하기 위해 사용.
8
+ */
9
+ export declare function useFocusVisibleMode(): void;
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  interface InteractiveListItemProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
3
- isSelected?: boolean;
4
3
  disabled?: boolean;
4
+ isSelected?: boolean;
5
5
  showHoverActions?: boolean;
6
6
  }
7
7
  export declare const InteractiveListItem: React.ForwardRefExoticComponent<Omit<InteractiveListItemProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -7,6 +7,7 @@ interface NotifyItemProps extends React.HTMLAttributes<HTMLDivElement> {
7
7
  variant?: "ghost" | "dynamicAccent";
8
8
  unread?: boolean;
9
9
  disabled?: boolean;
10
+ onClick?: (e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void;
10
11
  }
11
12
  export declare const NotifyItem: import("react").ForwardRefExoticComponent<NotifyItemProps & import("react").RefAttributes<HTMLDivElement>>;
12
13
  export {};
@@ -1,5 +1,5 @@
1
- import { ReactNode } from "react";
2
1
  import { RadioGroup as RadioGroupRadix } from "radix-ui";
2
+ import { ReactNode } from "react";
3
3
  import { RadioGroupOrientation } from "./types";
4
4
  interface RadioGroupProps extends RadioGroupRadix.RadioGroupProps {
5
5
  className?: string;
@@ -1,5 +1,5 @@
1
- import React from "react";
2
1
  import { Select as SelectRadix } from "radix-ui";
2
+ import React from "react";
3
3
  import { SelectTriggerSize, SelectTriggerVariant } from "./types";
4
4
  /**
5
5
  * Select 컴포넌트는 드롭다운 선택 상자를 생성하는 데 사용됩니다.<br/>
@@ -14,9 +14,9 @@ import { SelectTriggerSize, SelectTriggerVariant } from "./types";
14
14
  * <Select>
15
15
  * <SelectTrigger placeholder="Select an option" />
16
16
  * <SelectContent>
17
- * <SelectItem value="option1">Option 1</SelectItem>
18
- * <SelectItem value="option2">Option 2</SelectItem>
19
- * <SelectItem value="option3">Option 3</SelectItem>
17
+ * <SelectItem value="option1" textValue="Option 1"></SelectItem>
18
+ * <SelectItem value="option2" textValue="Option 2"></SelectItem>
19
+ * <SelectItem value="option3" textValue="Option 3"></SelectItem>
20
20
  * </SelectContent>
21
21
  * </Select>
22
22
  */
@@ -3,7 +3,7 @@ export declare const Table: React.ForwardRefExoticComponent<Omit<React.DetailedH
3
3
  export declare const TableHeader: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.TableHTMLAttributes<HTMLTableRowElement>, HTMLTableRowElement>, "ref"> & React.RefAttributes<HTMLTableRowElement>>;
4
4
  export declare const TableBody: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.TableHTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>, "ref"> & React.RefAttributes<HTMLTableSectionElement>>;
5
5
  interface TableRowProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableRowElement>, HTMLTableRowElement> {
6
- isSelected?: boolean;
6
+ isFocused?: boolean;
7
7
  shouldLastBorderRender?: boolean;
8
8
  }
9
9
  export declare const TableRow: React.ForwardRefExoticComponent<Omit<TableRowProps, "ref"> & React.RefAttributes<HTMLTableRowElement>>;
package/dist/index.d.ts CHANGED
@@ -50,12 +50,12 @@ declare const Button: React__default.ForwardRefExoticComponent<Omit<ButtonProps,
50
50
  /**
51
51
  * Text Dim 스타일의 버튼 컴포넌트입니다.<br/>
52
52
  * 텍스트 중심의 보조 액션(secondary / subtle action)에 적합한 low-emphasis 스타일을 제공합니다.<br/>
53
- * 기본적으로 낮은 명도의 텍스트를 사용하며, hover 시 명도가 상승하고 selected 시 accent 색상으로 강조됩니다.<br/>
53
+ * 기본적으로 낮은 명도의 텍스트를 사용하며, hover 시 명도가 상승하고 focused 시 accent 색상으로 강조됩니다.<br/>
54
54
  * <br/>
55
55
  * ### 사용 예시
56
56
  * ```tsx
57
57
  * <ButtonTextDim>자세히 보기</ButtonTextDim>
58
- * <ButtonTextDim aria-selected="true">선택됨</ButtonTextDim>
58
+ * <ButtonTextDim data-focused="true">포커스됨</ButtonTextDim>
59
59
  * ```
60
60
  */
61
61
  declare const ButtonTextDim: React__default.ForwardRefExoticComponent<Omit<Omit<ButtonProps, "variant">, "ref"> & React__default.RefAttributes<HTMLButtonElement>>;
@@ -141,6 +141,7 @@ interface NotifyItemProps extends React.HTMLAttributes<HTMLDivElement> {
141
141
  variant?: "ghost" | "dynamicAccent";
142
142
  unread?: boolean;
143
143
  disabled?: boolean;
144
+ onClick?: (e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void;
144
145
  }
145
146
  declare const NotifyItem: React$1.ForwardRefExoticComponent<NotifyItemProps & React$1.RefAttributes<HTMLDivElement>>;
146
147