@telepix-lab/telepix-ui 0.6.9 → 0.7.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.
@@ -24,3 +24,15 @@ export interface ButtonProps extends React.DetailedHTMLProps<React.ButtonHTMLAtt
24
24
  * ```
25
25
  */
26
26
  export declare const Button: React.ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
27
+ /**
28
+ * Text Dim 스타일의 버튼 컴포넌트입니다.<br/>
29
+ * 텍스트 중심의 보조 액션(secondary / subtle action)에 적합한 low-emphasis 스타일을 제공합니다.<br/>
30
+ * 기본적으로 낮은 명도의 텍스트를 사용하며, hover 시 명도가 상승하고 selected 시 accent 색상으로 강조됩니다.<br/>
31
+ * <br/>
32
+ * ### 사용 예시
33
+ * ```tsx
34
+ * <ButtonTextDim>자세히 보기</ButtonTextDim>
35
+ * <ButtonTextDim aria-selected="true">선택됨</ButtonTextDim>
36
+ * ```
37
+ */
38
+ export declare const ButtonTextDim: React.ForwardRefExoticComponent<Omit<Omit<ButtonProps, "variant">, "ref"> & React.RefAttributes<HTMLButtonElement>>;
@@ -15,5 +15,6 @@ export declare const BUTTON_VARIANTS: {
15
15
  readonly OUTLINE_ACCENT: "outline_accent";
16
16
  readonly GHOST_ACCENT: "ghost_accent";
17
17
  readonly GHOST_DIM: "ghost_dim";
18
+ readonly TEXT_DIM: "text_dim";
18
19
  };
19
20
  export type ButtonVariant = (typeof BUTTON_VARIANTS)[keyof typeof BUTTON_VARIANTS];
@@ -12345,11 +12345,19 @@ const Checkbox = React.forwardRef(({ label, labelClassName, wrapperClassName, ..
12345
12345
  setChecked(value);
12346
12346
  onChange?.(value);
12347
12347
  };
12348
- return (jsxRuntime.jsxs("div", { className: cn("flex items-center gap-2.5 pl-1.5 py-1 relative group/state", !label ? "pr-1.5 rounded-sm" : "pr-3 rounded-lg", wrapperClassName), children: [jsxRuntime.jsx(Checkbox$1, { ref: ref, id: id ?? randomId, ...rest, checked: defaultChecked ? checked : isChecked, className: cn("size-4 p-0 cursor-pointer border-transparent bg-transparent", className), onCheckedChange: onCheckedChange, children: renderCheckboxIcon(defaultChecked ? checked : isChecked, disabled) }), label && (jsxRuntime.jsx("label", { className: cn("text-body leading-body-compact text-comp-mono-default font-medium cursor-pointer", disabled && "text-comp-disabled cursor-default", labelClassName), htmlFor: id ?? randomId, "aria-disabled": disabled, children: label })), jsxRuntime.jsx(StateColorContainer, { className: cn(disabled
12348
+ const handleWrapperClick = (e) => {
12349
+ // CheckboxRadix.Root와 label은 자체 클릭 핸들러를 가지고 있으므로
12350
+ // wrapper 영역(StateColorContainer 포함)을 직접 클릭한 경우에만 토글
12351
+ if (disabled || e.target !== e.currentTarget)
12352
+ return;
12353
+ const currentChecked = defaultChecked ? checked : isChecked;
12354
+ const newValue = !currentChecked;
12355
+ onCheckedChange(newValue);
12356
+ };
12357
+ return (jsxRuntime.jsxs("div", { className: cn("flex items-center gap-2.5 pl-1.5 py-1 relative group/state", !label ? "pr-1.5 rounded-sm" : "pr-3 rounded-lg", !disabled && "cursor-pointer", wrapperClassName), onClick: handleWrapperClick, children: [jsxRuntime.jsx(Checkbox$1, { ref: ref, id: id ?? randomId, ...rest, checked: defaultChecked ? checked : isChecked, className: cn("size-4 p-0 cursor-pointer border-transparent bg-transparent", className), onCheckedChange: onCheckedChange, children: renderCheckboxIcon(defaultChecked ? checked : isChecked, disabled) }), label && (jsxRuntime.jsx("label", { className: cn("text-body leading-body-compact text-comp-mono-default font-medium cursor-pointer", disabled && "text-comp-disabled cursor-default", labelClassName), htmlFor: id ?? randomId, "aria-disabled": disabled, children: label })), jsxRuntime.jsx(StateColorContainer, { className: cn(disabled
12349
12358
  ? "bg-fill-default group-hover/state:bg-fill-default group-active/state:bg-fill-default"
12350
12359
  : "") })] }));
12351
12360
  });
12352
- Checkbox.displayName = "Checkbox";
12353
12361
 
12354
12362
  /**
12355
12363
  * 카드 컴포넌트는 콘텐츠를 그룹화하고 시각적으로 구분하는 데 사용됩니다.<br/>
@@ -13128,6 +13136,7 @@ const Calendar = ({ className, classNames, showOutsideDays = true, ...props }) =
13128
13136
  weekday: "text-comp-mono-subtle-default text-body font-normal leading-body size-9 p-1",
13129
13137
  day: "p-1 bg-transparent",
13130
13138
  day_button: "size-8 rounded-md hover:bg-fill-hovered focus:outline-none text-body leading-3.5 font-medium text-comp-mono-default cursor-pointer",
13139
+ disabled: "[&_button]:text-comp-disabled [&_button]:cursor-not-allowed [&_button]:hover:bg-transparent",
13131
13140
  selected: cn("[&_button]:bg-page-accent-l0 bg-transparent", props.mode === "single" &&
13132
13141
  "[&_button]:text-comp-accent-default [&_button]:hover:bg-page-accent-l0"),
13133
13142
  outside: "[&_button]:text-comp-disabled",
@@ -13147,6 +13156,15 @@ const Calendar = ({ className, classNames, showOutsideDays = true, ...props }) =
13147
13156
  }, ...props }));
13148
13157
  };
13149
13158
 
13159
+ var dayjs_min = {exports: {}};
13160
+
13161
+ (function (module, exports) {
13162
+ !function(t,e){module.exports=e();}(commonjsGlobal,(function(){var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return "["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return !r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return (e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return -t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,c),s=n-i<0,u=e.clone().add(r+(s?-1:1),c);return +(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return {M:c,y:h,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:f}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p="$isDayjsObject",S=function(t){return t instanceof _||!(!t||!t[p])},w=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else {var a=e.name;D[a]=e,i=a;}return !r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,true),this.parse(t),this.$x=this.$x||t.x||{},this[p]=true;}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init();},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds();},m.$utils=function(){return b},m.isValid=function(){return !(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<O(t)},m.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!b.u(e)||e,f=b.p(t),l=function(t,e){var i=b.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return b.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(f){case h:return r?l(1,0):l(31,11);case c:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,false)},m.$set=function(t,e){var n,o=b.p(t),f="set"+(this.$u?"UTC":""),l=(n={},n[a]=f+"Date",n[d]=f+"Date",n[c]=f+"Month",n[h]=f+"FullYear",n[u]=f+"Hours",n[s]=f+"Minutes",n[i]=f+"Seconds",n[r]=f+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===c||o===h){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d;}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[b.p(t)]()},m.add=function(r,f){var d,l=this;r=Number(r);var $=b.p(f),y=function(t){var e=O(l);return b.w(e.date(e.date()+Math.round(t*r)),l)};if($===c)return this.set(c,this.$M+r);if($===h)return this.set(h,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return b.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=b.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,c=n.months,f=n.meridiem,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},d=function(t){return b.s(s%12||12,t,"0")},$=f||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r};return r.replace(y,(function(t,r){return r||function(t){switch(t){case "YY":return String(e.$y).slice(-2);case "YYYY":return b.s(e.$y,4,"0");case "M":return a+1;case "MM":return b.s(a+1,2,"0");case "MMM":return h(n.monthsShort,a,c,3);case "MMMM":return h(c,a);case "D":return e.$D;case "DD":return b.s(e.$D,2,"0");case "d":return String(e.$W);case "dd":return h(n.weekdaysMin,e.$W,o,2);case "ddd":return h(n.weekdaysShort,e.$W,o,3);case "dddd":return o[e.$W];case "H":return String(s);case "HH":return b.s(s,2,"0");case "h":return d(1);case "hh":return d(2);case "a":return $(s,u,true);case "A":return $(s,u,false);case "m":return String(u);case "mm":return b.s(u,2,"0");case "s":return String(e.$s);case "ss":return b.s(e.$s,2,"0");case "SSS":return b.s(e.$ms,3,"0");case "Z":return i}return null}(t)||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=this,M=b.p(d),m=O(r),v=(m.utcOffset()-this.utcOffset())*e,g=this-m,D=function(){return b.m(y,m)};switch(M){case h:$=D()/12;break;case c:$=D();break;case f:$=D()/3;break;case o:$=(g-v)/6048e5;break;case a:$=(g-v)/864e5;break;case u:$=g/n;break;case s:$=g/e;break;case i:$=g/t;break;default:$=g;}return l?$:b.a($)},m.daysInMonth=function(){return this.endOf(c).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=w(t,e,true);return r&&(n.$L=r),n},m.clone=function(){return b.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),k=_.prototype;return O.prototype=k,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",c],["$y",h],["$D",d]].forEach((function(t){k[t[1]]=function(e){return this.$g(e,t[0],t[1])};})),O.extend=function(t,e){return t.$i||(t(e,_,O),t.$i=true),O},O.locale=w,O.isDayjs=S,O.unix=function(t){return O(1e3*t)},O.en=D[g],O.Ls=D,O.p={},O}));
13163
+ } (dayjs_min));
13164
+
13165
+ var dayjs_minExports = dayjs_min.exports;
13166
+ var dayjs = /*@__PURE__*/getDefaultExportFromCjs(dayjs_minExports);
13167
+
13150
13168
  const BUTTON_SIZES = {
13151
13169
  REGULAR: "regular",
13152
13170
  SMALL: "small",
@@ -13154,7 +13172,9 @@ const BUTTON_SIZES = {
13154
13172
  };
13155
13173
  const BUTTON_VARIANTS = {
13156
13174
  ACCENT: "accent",
13157
- THUMBNAIL_GHOST: "thumbnail_ghost"};
13175
+ THUMBNAIL_GHOST: "thumbnail_ghost",
13176
+ TEXT_DIM: "text_dim",
13177
+ };
13158
13178
 
13159
13179
  /**
13160
13180
  * 다양한 스타일과 속성을 지원하는 버튼 컴포넌트입니다.<br/>
@@ -13172,6 +13192,13 @@ const BUTTON_VARIANTS = {
13172
13192
  */
13173
13193
  const Button = React.forwardRef(({ variant = "accent", size = "regular", fullWidth = false, isLoading = false, leftIcon, rightIcon, children, disabled, className, stateContainerClassName, ...rest }, ref) => {
13174
13194
  const getClassName = (size, variant) => {
13195
+ // text_dim variant는 size에 관계없이 고정된 스타일 사용
13196
+ if (variant === BUTTON_VARIANTS.TEXT_DIM) {
13197
+ return [
13198
+ "h-auto py-0.5 px-0 text-body leading-body-compact gap-1.5",
13199
+ "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",
13200
+ ];
13201
+ }
13175
13202
  const sizeClassName = {
13176
13203
  small: "h-7 py-0 px-1 text-label leading-label-compact gap-1",
13177
13204
  regular: "h-9 py-0 px-2 text-body leading-body-compact gap-1.5",
@@ -13188,6 +13215,7 @@ const Button = React.forwardRef(({ variant = "accent", size = "regular", fullWid
13188
13215
  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",
13189
13216
  ghost_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-accent-default disabled:text-comp-disabled",
13190
13217
  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",
13218
+ text_dim: "", // 위에서 처리되므로 여기서는 빈 문자열
13191
13219
  }[variant];
13192
13220
  return [sizeClassName, variantClassName];
13193
13221
  };
@@ -13205,48 +13233,170 @@ const Button = React.forwardRef(({ variant = "accent", size = "regular", fullWid
13205
13233
  return "size-5";
13206
13234
  return "size-6"; // 기본값은 regular 크기
13207
13235
  };
13208
- return (jsxRuntime.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: [jsxRuntime.jsx(StateColorContainer, { isInverted: variant === BUTTON_VARIANTS.ACCENT, className: stateContainerClassName }), isLoading && (jsxRuntime.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 &&
13236
+ return (jsxRuntime.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 && (jsxRuntime.jsx(StateColorContainer, { isInverted: variant === BUTTON_VARIANTS.ACCENT, className: stateContainerClassName })), isLoading && (jsxRuntime.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 &&
13209
13237
  "size-4 top-[calc(50%-8px)] left-[calc(50%-8px)]", size === BUTTON_SIZES.REGULAR &&
13210
13238
  "size-5 top-[calc(50%-10px)] left-[calc(50%-10px)]", size === BUTTON_SIZES.LARGE &&
13211
13239
  "size-6 top-[calc(50%-12px)] left-[calc(50%-12px)]"), children: jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) })), leftIcon && (jsxRuntime.jsx("div", { className: cn("flex items-center justify-center relative", variant === BUTTON_VARIANTS.THUMBNAIL_GHOST &&
13212
13240
  "aspect-square [&_img]:rounded-sm", getThumbnailSize(variant, size)), children: leftIcon })), children && (jsxRuntime.jsx("div", { className: "flex-1", style: { visibility: isLoading ? "hidden" : "visible" }, children: children })), rightIcon && (jsxRuntime.jsx("div", { className: cn("flex items-center justify-center relative", getThumbnailSize(variant, size)), children: rightIcon }))] }));
13213
13241
  });
13214
13242
  Button.displayName = "Button";
13215
-
13216
- var dayjs_min = {exports: {}};
13217
-
13218
- (function (module, exports) {
13219
- !function(t,e){module.exports=e();}(commonjsGlobal,(function(){var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return "["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return !r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return (e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return -t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,c),s=n-i<0,u=e.clone().add(r+(s?-1:1),c);return +(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return {M:c,y:h,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:f}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p="$isDayjsObject",S=function(t){return t instanceof _||!(!t||!t[p])},w=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else {var a=e.name;D[a]=e,i=a;}return !r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,true),this.parse(t),this.$x=this.$x||t.x||{},this[p]=true;}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init();},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds();},m.$utils=function(){return b},m.isValid=function(){return !(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<O(t)},m.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!b.u(e)||e,f=b.p(t),l=function(t,e){var i=b.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return b.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(f){case h:return r?l(1,0):l(31,11);case c:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,false)},m.$set=function(t,e){var n,o=b.p(t),f="set"+(this.$u?"UTC":""),l=(n={},n[a]=f+"Date",n[d]=f+"Date",n[c]=f+"Month",n[h]=f+"FullYear",n[u]=f+"Hours",n[s]=f+"Minutes",n[i]=f+"Seconds",n[r]=f+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===c||o===h){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d;}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[b.p(t)]()},m.add=function(r,f){var d,l=this;r=Number(r);var $=b.p(f),y=function(t){var e=O(l);return b.w(e.date(e.date()+Math.round(t*r)),l)};if($===c)return this.set(c,this.$M+r);if($===h)return this.set(h,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return b.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=b.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,c=n.months,f=n.meridiem,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},d=function(t){return b.s(s%12||12,t,"0")},$=f||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r};return r.replace(y,(function(t,r){return r||function(t){switch(t){case "YY":return String(e.$y).slice(-2);case "YYYY":return b.s(e.$y,4,"0");case "M":return a+1;case "MM":return b.s(a+1,2,"0");case "MMM":return h(n.monthsShort,a,c,3);case "MMMM":return h(c,a);case "D":return e.$D;case "DD":return b.s(e.$D,2,"0");case "d":return String(e.$W);case "dd":return h(n.weekdaysMin,e.$W,o,2);case "ddd":return h(n.weekdaysShort,e.$W,o,3);case "dddd":return o[e.$W];case "H":return String(s);case "HH":return b.s(s,2,"0");case "h":return d(1);case "hh":return d(2);case "a":return $(s,u,true);case "A":return $(s,u,false);case "m":return String(u);case "mm":return b.s(u,2,"0");case "s":return String(e.$s);case "ss":return b.s(e.$s,2,"0");case "SSS":return b.s(e.$ms,3,"0");case "Z":return i}return null}(t)||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=this,M=b.p(d),m=O(r),v=(m.utcOffset()-this.utcOffset())*e,g=this-m,D=function(){return b.m(y,m)};switch(M){case h:$=D()/12;break;case c:$=D();break;case f:$=D()/3;break;case o:$=(g-v)/6048e5;break;case a:$=(g-v)/864e5;break;case u:$=g/n;break;case s:$=g/e;break;case i:$=g/t;break;default:$=g;}return l?$:b.a($)},m.daysInMonth=function(){return this.endOf(c).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=w(t,e,true);return r&&(n.$L=r),n},m.clone=function(){return b.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),k=_.prototype;return O.prototype=k,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",c],["$y",h],["$D",d]].forEach((function(t){k[t[1]]=function(e){return this.$g(e,t[0],t[1])};})),O.extend=function(t,e){return t.$i||(t(e,_,O),t.$i=true),O},O.locale=w,O.isDayjs=S,O.unix=function(t){return O(1e3*t)},O.en=D[g],O.Ls=D,O.p={},O}));
13220
- } (dayjs_min));
13221
-
13222
- var dayjs_minExports = dayjs_min.exports;
13223
- var dayjs = /*@__PURE__*/getDefaultExportFromCjs(dayjs_minExports);
13224
-
13225
- const DatePicker = ({ size = "regular", placeholder = "Pick a date", format = "YYYY. MM. DD.", value, mode = "single", onChange, isInstantClose = false, open: openProp, onOpenChange: onOpenChangeProp, disabled, popoverContentClassName, ...rest }) => {
13226
- const [inputValue, setInputValue] = React.useState(value);
13227
- const [open, setOpen] = React.useState(openProp || false);
13228
- const handleSelect = (date) => {
13229
- if (mode === "single") {
13230
- const value = date;
13231
- onChange?.(value);
13243
+ /**
13244
+ * Text Dim 스타일의 버튼 컴포넌트입니다.<br/>
13245
+ * 텍스트 중심의 보조 액션(secondary / subtle action)에 적합한 low-emphasis 스타일을 제공합니다.<br/>
13246
+ * 기본적으로 낮은 명도의 텍스트를 사용하며, hover 시 명도가 상승하고 selected 시 accent 색상으로 강조됩니다.<br/>
13247
+ * <br/>
13248
+ * ### 사용 예시
13249
+ * ```tsx
13250
+ * <ButtonTextDim>자세히 보기</ButtonTextDim>
13251
+ * <ButtonTextDim aria-selected="true">선택됨</ButtonTextDim>
13252
+ * ```
13253
+ */
13254
+ const ButtonTextDim = React.forwardRef((props, ref) => {
13255
+ return jsxRuntime.jsx(Button, { ref: ref, variant: "text_dim", ...props });
13256
+ });
13257
+ ButtonTextDim.displayName = "ButtonTextDim";
13258
+
13259
+ // Constants
13260
+ const CALENDAR_MODES = {
13261
+ SINGLE: "single",
13262
+ MULTIPLE: "multiple",
13263
+ RANGE: "range",
13264
+ };
13265
+ const DEFAULT_FORMAT = "YYYY. MM. DD.";
13266
+ const DEFAULT_PLACEHOLDER = "Pick a date";
13267
+ const DatePicker = ({ size = "regular", placeholder = DEFAULT_PLACEHOLDER, format = DEFAULT_FORMAT, value, mode = CALENDAR_MODES.SINGLE, onChange, isInstantClose = false, open: openProp, onOpenChange: onOpenChangeProp, disabled, popoverContentClassName, minDate, maxDate, disabledDates, ...rest }) => {
13268
+ // Determine if component is in controlled mode
13269
+ // Using useRef to track initial controlled state (React's recommended approach)
13270
+ const isControlledRef = React.useRef(value !== undefined);
13271
+ const isControlled = isControlledRef.current;
13272
+ const [internalValue, setInternalValue] = React.useState(value);
13273
+ const [internalOpen, setInternalOpen] = React.useState(openProp ?? false);
13274
+ const [lastInteractedDate, setLastInteractedDate] = React.useState(undefined);
13275
+ // Use controlled value if provided, otherwise use internal state
13276
+ const displayValue = isControlled ? value : internalValue;
13277
+ const displayOpen = openProp !== undefined ? openProp : internalOpen;
13278
+ // Update last interacted date based on mode and selected date
13279
+ const updateLastInteractedDate = (date) => {
13280
+ try {
13281
+ if (mode === CALENDAR_MODES.RANGE &&
13282
+ date &&
13283
+ typeof date === "object" &&
13284
+ "from" in date) {
13285
+ const rangeValue = date;
13286
+ if (rangeValue.to) {
13287
+ setLastInteractedDate(rangeValue.to);
13288
+ }
13289
+ else if (rangeValue.from) {
13290
+ setLastInteractedDate(rangeValue.from);
13291
+ }
13292
+ }
13293
+ else if (mode === CALENDAR_MODES.SINGLE && date instanceof Date) {
13294
+ setLastInteractedDate(date);
13295
+ }
13296
+ else if (mode === CALENDAR_MODES.MULTIPLE &&
13297
+ Array.isArray(date) &&
13298
+ date.length > 0) {
13299
+ setLastInteractedDate(date[date.length - 1]);
13300
+ }
13232
13301
  }
13233
- else if (mode === "multiple") {
13234
- const value = date;
13235
- onChange?.(value);
13302
+ catch (error) {
13303
+ console.warn("Error updating last interacted date:", error);
13236
13304
  }
13237
- else if (mode === "range") {
13238
- const value = date;
13239
- onChange?.(value);
13305
+ };
13306
+ const handleSelect = (date) => {
13307
+ updateLastInteractedDate(date);
13308
+ // Always call onChange to notify parent
13309
+ onChange?.(date);
13310
+ // Only update internal state in uncontrolled mode
13311
+ if (!isControlled) {
13312
+ setInternalValue(date);
13240
13313
  }
13241
- setInputValue(date);
13242
- if (isInstantClose && mode === "single") {
13314
+ // Close popover if instant close is enabled (single mode only)
13315
+ if (isInstantClose && mode === CALENDAR_MODES.SINGLE) {
13243
13316
  handleOpenChange(false);
13244
13317
  }
13245
13318
  };
13319
+ // Calculate disabled dates based on minDate, maxDate, range validation, and custom disabled dates
13320
+ // Using react-day-picker's native Matcher format for better performance
13321
+ const isCalendarDisabled = React.useMemo(() => {
13322
+ const matchers = [];
13323
+ // Add minDate constraint using native 'before' matcher
13324
+ if (minDate) {
13325
+ matchers.push({ before: minDate });
13326
+ }
13327
+ // Add maxDate constraint using native 'after' matcher
13328
+ if (maxDate) {
13329
+ matchers.push({ after: maxDate });
13330
+ }
13331
+ // Range mode: when only 'from' is selected, disable all dates before it
13332
+ if (mode === CALENDAR_MODES.RANGE &&
13333
+ displayValue &&
13334
+ typeof displayValue === "object" &&
13335
+ "from" in displayValue) {
13336
+ const rangeValue = displayValue;
13337
+ if (rangeValue.from && !rangeValue.to) {
13338
+ matchers.push({ before: rangeValue.from });
13339
+ }
13340
+ }
13341
+ // Add custom disabled dates
13342
+ if (disabledDates) {
13343
+ if (Array.isArray(disabledDates)) {
13344
+ matchers.push(...disabledDates);
13345
+ }
13346
+ else {
13347
+ matchers.push(disabledDates);
13348
+ }
13349
+ }
13350
+ // Return combined matchers (react-day-picker handles OR logic automatically)
13351
+ return matchers.length > 0 ? matchers : undefined;
13352
+ }, [minDate, maxDate, mode, displayValue, disabledDates]);
13246
13353
  const handleOpenChange = (open) => {
13247
- setOpen(open);
13354
+ // Update internal state only in uncontrolled mode
13355
+ if (openProp === undefined) {
13356
+ setInternalOpen(open);
13357
+ }
13358
+ // Always notify parent
13248
13359
  onOpenChangeProp?.(open);
13249
13360
  };
13361
+ // Calculate default month based on current value
13362
+ const getDefaultMonth = () => {
13363
+ try {
13364
+ // For range mode, prioritize lastInteractedDate if available
13365
+ if (mode === CALENDAR_MODES.RANGE && lastInteractedDate) {
13366
+ return lastInteractedDate;
13367
+ }
13368
+ if (!displayValue) {
13369
+ return undefined; // Use today's date as default when no value
13370
+ }
13371
+ if (mode === CALENDAR_MODES.SINGLE) {
13372
+ return displayValue;
13373
+ }
13374
+ else if (mode === CALENDAR_MODES.MULTIPLE) {
13375
+ const dates = displayValue;
13376
+ if (dates.length > 0) {
13377
+ // Use the most recent date in the array
13378
+ return dates[dates.length - 1];
13379
+ }
13380
+ }
13381
+ else if (mode === CALENDAR_MODES.RANGE) {
13382
+ const range = displayValue;
13383
+ // Use the most recent date (to) if available, otherwise use from
13384
+ if (range.to) {
13385
+ return range.to;
13386
+ }
13387
+ else if (range.from) {
13388
+ return range.from;
13389
+ }
13390
+ }
13391
+ return undefined;
13392
+ }
13393
+ catch (error) {
13394
+ console.warn("Error calculating default month:", error);
13395
+ return undefined;
13396
+ }
13397
+ };
13398
+ // Memoize default month calculation for performance
13399
+ const defaultMonth = React.useMemo(() => getDefaultMonth(), [mode, lastInteractedDate, displayValue]);
13250
13400
  const getDateToFormatString = (date) => {
13251
13401
  if (date instanceof Date) {
13252
13402
  return dayjs(date).format(format);
@@ -13254,15 +13404,17 @@ const DatePicker = ({ size = "regular", placeholder = "Pick a date", format = "Y
13254
13404
  else if (Array.isArray(date)) {
13255
13405
  return date.map((d) => dayjs(d).format(format)).join(", ");
13256
13406
  }
13257
- else if (date &&
13258
- typeof date === "object" &&
13259
- "from" in date &&
13260
- "to" in date) {
13261
- return `${dayjs(date.from).format(format)} - ${dayjs(date.to).format(format)}`;
13407
+ else if (date && typeof date === "object" && "from" in date) {
13408
+ if (date.to) {
13409
+ return `${dayjs(date.from).format(format)} - ${dayjs(date.to).format(format)}`;
13410
+ }
13411
+ return dayjs(date.from).format(format);
13262
13412
  }
13263
13413
  return placeholder ?? "";
13264
13414
  };
13265
- return (jsxRuntime.jsxs(Root2$2, { open: open, onOpenChange: handleOpenChange, children: [jsxRuntime.jsx(Trigger$2, { asChild: true, children: jsxRuntime.jsx(Button, { size: size, fullWidth: true, variant: "outline", leftIcon: jsxRuntime.jsx(Calendar$1, { size: 20 }), className: "justify-start", "aria-selected": open, disabled: disabled, stateContainerClassName: open ? "group-aria-selected/state:bg-fill-default" : undefined, children: getDateToFormatString(inputValue) }) }), jsxRuntime.jsx(Content2$2, { sideOffset: 10, align: "start", className: cn("z-10", popoverContentClassName), ...rest, children: mode === "single" ? (jsxRuntime.jsx(Calendar, { mode: "single", ...rest, selected: inputValue, onSelect: handleSelect })) : mode === "multiple" ? (jsxRuntime.jsx(Calendar, { mode: "multiple", ...rest, selected: Array.isArray(inputValue) ? inputValue : [], onSelect: handleSelect })) : (jsxRuntime.jsx(Calendar, { mode: "range", ...rest, selected: inputValue, onSelect: handleSelect })) })] }));
13415
+ return (jsxRuntime.jsxs(Root2$2, { open: displayOpen, onOpenChange: handleOpenChange, children: [jsxRuntime.jsx(Trigger$2, { asChild: true, children: jsxRuntime.jsx(Button, { size: size, fullWidth: true, variant: "outline", leftIcon: jsxRuntime.jsx(Calendar$1, { size: 20 }), className: "justify-start", "aria-selected": displayOpen, "aria-label": `Date picker, ${getDateToFormatString(displayValue) || placeholder}`, disabled: disabled, stateContainerClassName: displayOpen
13416
+ ? "group-aria-selected/state:bg-fill-default"
13417
+ : undefined, children: getDateToFormatString(displayValue) }) }), jsxRuntime.jsx(Content2$2, { sideOffset: 10, align: "start", className: cn("z-10", popoverContentClassName), ...rest, children: mode === CALENDAR_MODES.SINGLE ? (jsxRuntime.jsx(Calendar, { mode: "single", ...rest, selected: displayValue, onSelect: handleSelect, disabled: isCalendarDisabled, defaultMonth: defaultMonth })) : mode === CALENDAR_MODES.MULTIPLE ? (jsxRuntime.jsx(Calendar, { mode: "multiple", ...rest, selected: Array.isArray(displayValue) ? displayValue : [], onSelect: handleSelect, disabled: isCalendarDisabled, defaultMonth: defaultMonth })) : (jsxRuntime.jsx(Calendar, { mode: "range", ...rest, selected: displayValue, onSelect: handleSelect, disabled: isCalendarDisabled, defaultMonth: defaultMonth })) })] }));
13266
13418
  };
13267
13419
 
13268
13420
  const ContextMenu = Root2$3;
@@ -1,6 +1,6 @@
1
1
  import { Popover } from "radix-ui";
2
+ import { DateRange, Matcher } from "react-day-picker";
2
3
  import { CalendarProps } from "../calendar";
3
- import { DateRange } from "react-day-picker";
4
4
  type AllDatePickerTypes = Date | undefined | DateRange | Date[];
5
5
  interface DatePicker {
6
6
  size: "regular" | "large";
@@ -11,7 +11,21 @@ interface DatePicker {
11
11
  isInstantClose?: boolean;
12
12
  disabled?: boolean;
13
13
  popoverContentClassName?: string;
14
+ minDate?: Date;
15
+ maxDate?: Date;
16
+ /**
17
+ * Custom disabled dates matcher.
18
+ * Note: For optimal performance, memoize this prop in the parent component
19
+ * to avoid unnecessary recalculations.
20
+ *
21
+ * @example
22
+ * const disabledDates = useMemo(() => [
23
+ * new Date(2025, 0, 15),
24
+ * { from: new Date(2025, 1, 10), to: new Date(2025, 1, 15) }
25
+ * ], []);
26
+ */
27
+ disabledDates?: Matcher | Matcher[];
14
28
  }
15
- export type DatePickerProps = DatePicker & CalendarProps & Popover.PopoverProps & Omit<Popover.PopoverContentProps, "className">;
16
- export declare const DatePicker: ({ size, placeholder, format, value, mode, onChange, isInstantClose, open: openProp, onOpenChange: onOpenChangeProp, disabled, popoverContentClassName, ...rest }: DatePickerProps) => import("react/jsx-runtime").JSX.Element;
29
+ export type DatePickerProps = DatePicker & Omit<CalendarProps, "disabled" | "selected" | "onSelect"> & Popover.PopoverProps & Omit<Popover.PopoverContentProps, "className">;
30
+ export declare const DatePicker: ({ size, placeholder, format, value, mode, onChange, isInstantClose, open: openProp, onOpenChange: onOpenChangeProp, disabled, popoverContentClassName, minDate, maxDate, disabledDates, ...rest }: DatePickerProps) => import("react/jsx-runtime").JSX.Element;
17
31
  export {};
@@ -1,10 +1,11 @@
1
- export { Button } from "./button";
1
+ export { Button, ButtonTextDim } from "./button";
2
2
  export { BUTTON_SIZES, BUTTON_VARIANTS } from "./button/types";
3
3
  export type { ButtonSize, ButtonVariant } from "./button/types";
4
4
  export { Card, CardBody, CardContent, CardFooter, CardHeader } from "./card";
5
5
  export { Input } from "./input";
6
6
  export { INPUT_SIZES, INPUT_VARIANTS } from "./input/types";
7
7
  export type { InputSize, InputVariant } from "./input/types";
8
+ export { NotifyItem } from "./notify-item";
8
9
  export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "./pagination";
9
10
  export { StateColorContainer } from "./state-color-container";
10
11
  export type { StateGroupName } from "./state-color-container/types";
package/dist/cjs/index.js CHANGED
@@ -3,24 +3,6 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
5
 
6
- const BUTTON_SIZES = {
7
- REGULAR: "regular",
8
- SMALL: "small",
9
- LARGE: "large",
10
- };
11
- const BUTTON_VARIANTS = {
12
- ACCENT: "accent",
13
- OUTLINE: "outline",
14
- OUTLINE_TO_ACCENT: "outline_to_accent",
15
- GHOST: "ghost",
16
- THUMBNAIL_GHOST: "thumbnail_ghost",
17
- GHOST_TO_ACCENT: "ghost_to_accent",
18
- SUBTLE_FILLED: "subtle_filled",
19
- OUTLINE_ACCENT: "outline_accent",
20
- GHOST_ACCENT: "ghost_accent",
21
- GHOST_DIM: "ghost_dim",
22
- };
23
-
24
6
  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}
25
7
 
26
8
  const CLASS_PART_SEPARATOR = '-';
@@ -3353,6 +3335,25 @@ const StateColorContainer = ({ groupName = "state", isInverted, className, }) =>
3353
3335
  return (jsxRuntime.jsx("div", { className: cn("absolute left-0 top-0 right-0 bottom-0 inset-0 pointer-events-none z-0 rounded-[inherit]", actionClass, className) }));
3354
3336
  };
3355
3337
 
3338
+ const BUTTON_SIZES = {
3339
+ REGULAR: "regular",
3340
+ SMALL: "small",
3341
+ LARGE: "large",
3342
+ };
3343
+ const BUTTON_VARIANTS = {
3344
+ ACCENT: "accent",
3345
+ OUTLINE: "outline",
3346
+ OUTLINE_TO_ACCENT: "outline_to_accent",
3347
+ GHOST: "ghost",
3348
+ THUMBNAIL_GHOST: "thumbnail_ghost",
3349
+ GHOST_TO_ACCENT: "ghost_to_accent",
3350
+ SUBTLE_FILLED: "subtle_filled",
3351
+ OUTLINE_ACCENT: "outline_accent",
3352
+ GHOST_ACCENT: "ghost_accent",
3353
+ GHOST_DIM: "ghost_dim",
3354
+ TEXT_DIM: "text_dim",
3355
+ };
3356
+
3356
3357
  /**
3357
3358
  * 다양한 스타일과 속성을 지원하는 버튼 컴포넌트입니다.<br/>
3358
3359
  * 사용자는 버튼의 변형(variant), 크기(size), 전체 너비(fullWidth), 로딩 상태(isLoading), 아이콘(leftIcon, rightIcon) 등을 지정할 수 있습니다.<br/>
@@ -3369,6 +3370,13 @@ const StateColorContainer = ({ groupName = "state", isInverted, className, }) =>
3369
3370
  */
3370
3371
  const Button = react.forwardRef(({ variant = "accent", size = "regular", fullWidth = false, isLoading = false, leftIcon, rightIcon, children, disabled, className, stateContainerClassName, ...rest }, ref) => {
3371
3372
  const getClassName = (size, variant) => {
3373
+ // text_dim variant는 size에 관계없이 고정된 스타일 사용
3374
+ if (variant === BUTTON_VARIANTS.TEXT_DIM) {
3375
+ return [
3376
+ "h-auto py-0.5 px-0 text-body leading-body-compact gap-1.5",
3377
+ "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",
3378
+ ];
3379
+ }
3372
3380
  const sizeClassName = {
3373
3381
  small: "h-7 py-0 px-1 text-label leading-label-compact gap-1",
3374
3382
  regular: "h-9 py-0 px-2 text-body leading-body-compact gap-1.5",
@@ -3385,6 +3393,7 @@ const Button = react.forwardRef(({ variant = "accent", size = "regular", fullWid
3385
3393
  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",
3386
3394
  ghost_accent: "bg-page-l-null disabled:bg-page-l-null text-comp-accent-default disabled:text-comp-disabled",
3387
3395
  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",
3396
+ text_dim: "", // 위에서 처리되므로 여기서는 빈 문자열
3388
3397
  }[variant];
3389
3398
  return [sizeClassName, variantClassName];
3390
3399
  };
@@ -3402,13 +3411,28 @@ const Button = react.forwardRef(({ variant = "accent", size = "regular", fullWid
3402
3411
  return "size-5";
3403
3412
  return "size-6"; // 기본값은 regular 크기
3404
3413
  };
3405
- return (jsxRuntime.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: [jsxRuntime.jsx(StateColorContainer, { isInverted: variant === BUTTON_VARIANTS.ACCENT, className: stateContainerClassName }), isLoading && (jsxRuntime.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 &&
3414
+ return (jsxRuntime.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 && (jsxRuntime.jsx(StateColorContainer, { isInverted: variant === BUTTON_VARIANTS.ACCENT, className: stateContainerClassName })), isLoading && (jsxRuntime.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 &&
3406
3415
  "size-4 top-[calc(50%-8px)] left-[calc(50%-8px)]", size === BUTTON_SIZES.REGULAR &&
3407
3416
  "size-5 top-[calc(50%-10px)] left-[calc(50%-10px)]", size === BUTTON_SIZES.LARGE &&
3408
3417
  "size-6 top-[calc(50%-12px)] left-[calc(50%-12px)]"), children: jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) })), leftIcon && (jsxRuntime.jsx("div", { className: cn("flex items-center justify-center relative", variant === BUTTON_VARIANTS.THUMBNAIL_GHOST &&
3409
3418
  "aspect-square [&_img]:rounded-sm", getThumbnailSize(variant, size)), children: leftIcon })), children && (jsxRuntime.jsx("div", { className: "flex-1", style: { visibility: isLoading ? "hidden" : "visible" }, children: children })), rightIcon && (jsxRuntime.jsx("div", { className: cn("flex items-center justify-center relative", getThumbnailSize(variant, size)), children: rightIcon }))] }));
3410
3419
  });
3411
3420
  Button.displayName = "Button";
3421
+ /**
3422
+ * Text Dim 스타일의 버튼 컴포넌트입니다.<br/>
3423
+ * 텍스트 중심의 보조 액션(secondary / subtle action)에 적합한 low-emphasis 스타일을 제공합니다.<br/>
3424
+ * 기본적으로 낮은 명도의 텍스트를 사용하며, hover 시 명도가 상승하고 selected 시 accent 색상으로 강조됩니다.<br/>
3425
+ * <br/>
3426
+ * ### 사용 예시
3427
+ * ```tsx
3428
+ * <ButtonTextDim>자세히 보기</ButtonTextDim>
3429
+ * <ButtonTextDim aria-selected="true">선택됨</ButtonTextDim>
3430
+ * ```
3431
+ */
3432
+ const ButtonTextDim = react.forwardRef((props, ref) => {
3433
+ return jsxRuntime.jsx(Button, { ref: ref, variant: "text_dim", ...props });
3434
+ });
3435
+ ButtonTextDim.displayName = "ButtonTextDim";
3412
3436
 
3413
3437
  /**
3414
3438
  * 카드 컴포넌트는 콘텐츠를 그룹화하고 시각적으로 구분하는 데 사용됩니다.<br/>
@@ -3739,6 +3763,28 @@ const Input = react.forwardRef(({ size = "regular", variant = "outlined", leftIc
3739
3763
  });
3740
3764
  Input.displayName = "Input";
3741
3765
 
3766
+ const NOTIFY_ITEM_VARIANTS = {
3767
+ GHOST: "ghost",
3768
+ DYNAMIC_ACCENT: "dynamicAccent",
3769
+ };
3770
+ const NotifyItem = react.forwardRef(({ title, description, timestamp, icon, actionIcon, variant = NOTIFY_ITEM_VARIANTS.GHOST, unread = false, disabled, className, ...rest }, ref) => {
3771
+ return (jsxRuntime.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 && [
3772
+ !disabled && "bg-page-l-null *:text-comp-mono-subtle-default",
3773
+ "data-[unread=true]:border-border-bound data-[unread=true]:bg-page-l3 data-[unread=true]:*:text-comp-mono-default",
3774
+ ], variant === NOTIFY_ITEM_VARIANTS.DYNAMIC_ACCENT && [
3775
+ "bg-page-l4 *:text-comp-mono-default",
3776
+ "data-[unread=true]:bg-page-accent-l0 data-[unread=true]:*:text-comp-accent-default",
3777
+ ], disabled && [
3778
+ "cursor-not-allowed pointer-events-none bg-page-l-null *:text-comp-disabled",
3779
+ ], className), ...rest, children: [icon && (jsxRuntime.jsx("div", { className: "size-9 flex justify-center items-center flex-shrink-0", children: icon })), jsxRuntime.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: [jsxRuntime.jsx(Text, { variant: "medBodyMedCompact", className: "text-inherit", children: title }), jsxRuntime.jsx(Text, { variant: "regularLabelCompact", className: cn("text-inherit", variant === NOTIFY_ITEM_VARIANTS.GHOST &&
3780
+ "group-data-[unread=true]/state:text-comp-mono-subtle-default", variant === NOTIFY_ITEM_VARIANTS.DYNAMIC_ACCENT &&
3781
+ !disabled &&
3782
+ "group-data-[unread=false]/state:text-comp-mono-subtle-default"), children: description })] }), timestamp && (jsxRuntime.jsx("div", { className: "h-9 flex justify-center items-center flex-shrink-0", children: jsxRuntime.jsx(Text, { variant: "medLabelMedCompact", className: cn("text-inherit", variant === NOTIFY_ITEM_VARIANTS.DYNAMIC_ACCENT &&
3783
+ !disabled &&
3784
+ "group-data-[unread=false]/state:text-comp-mono-subtle-default"), children: timestamp }) })), actionIcon && (jsxRuntime.jsx("div", { className: "size-9 flex justify-center items-center flex-shrink-0", children: actionIcon })), jsxRuntime.jsx(StateColorContainer, { groupName: "state", isInverted: variant === NOTIFY_ITEM_VARIANTS.DYNAMIC_ACCENT })] }));
3785
+ });
3786
+ NotifyItem.displayName = "NotifyItem";
3787
+
3742
3788
  /**
3743
3789
  * @license lucide-react v0.513.0 - ISC
3744
3790
  *
@@ -3975,6 +4021,7 @@ TextArea.displayName = "TextArea";
3975
4021
  exports.BUTTON_SIZES = BUTTON_SIZES;
3976
4022
  exports.BUTTON_VARIANTS = BUTTON_VARIANTS;
3977
4023
  exports.Button = Button;
4024
+ exports.ButtonTextDim = ButtonTextDim;
3978
4025
  exports.Card = Card;
3979
4026
  exports.CardBody = CardBody;
3980
4027
  exports.CardContent = CardContent;
@@ -3984,6 +4031,7 @@ exports.FONT_WEIGHTS = FONT_WEIGHTS;
3984
4031
  exports.INPUT_SIZES = INPUT_SIZES;
3985
4032
  exports.INPUT_VARIANTS = INPUT_VARIANTS;
3986
4033
  exports.Input = Input;
4034
+ exports.NotifyItem = NotifyItem;
3987
4035
  exports.Pagination = Pagination;
3988
4036
  exports.PaginationContent = PaginationContent;
3989
4037
  exports.PaginationEllipsis = PaginationEllipsis;
@@ -0,0 +1,12 @@
1
+ interface NotifyItemProps extends React.HTMLAttributes<HTMLDivElement> {
2
+ title: string;
3
+ description: string;
4
+ timestamp?: string;
5
+ icon?: React.ReactNode;
6
+ actionIcon?: React.ReactNode;
7
+ variant?: "ghost" | "dynamicAccent";
8
+ unread?: boolean;
9
+ disabled?: boolean;
10
+ }
11
+ export declare const NotifyItem: import("react").ForwardRefExoticComponent<NotifyItemProps & import("react").RefAttributes<HTMLDivElement>>;
12
+ export {};
package/dist/client.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as React$1 from 'react';
2
2
  import React__default, { ReactNode, FC, Ref } from 'react';
3
3
  import { RadioGroup as RadioGroup$1, Checkbox as Checkbox$1, Dialog as Dialog$1, Select as Select$1, Tooltip as Tooltip$1, Popover, ContextMenu as ContextMenu$1, Switch as Switch$1 } from 'radix-ui';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
- import { DayPicker, DateRange } from 'react-day-picker';
5
+ import { DayPicker, DateRange, Matcher } from 'react-day-picker';
6
6
 
7
7
  declare const RADIO_GROUP_ORIENTAIONS: {
8
8
  readonly HORIZONTAL: "horizontal";
@@ -365,7 +365,7 @@ type CalendarProps = React$1.ComponentProps<typeof DayPicker>;
365
365
  declare const Calendar: ({ className, classNames, showOutsideDays, ...props }: CalendarProps) => react_jsx_runtime.JSX.Element;
366
366
 
367
367
  type AllDatePickerTypes = Date | undefined | DateRange | Date[];
368
- type DatePickerProps = DatePicker & CalendarProps & Popover.PopoverProps & Omit<Popover.PopoverContentProps, "className">;
368
+ type DatePickerProps = DatePicker & Omit<CalendarProps, "disabled" | "selected" | "onSelect"> & Popover.PopoverProps & Omit<Popover.PopoverContentProps, "className">;
369
369
  interface DatePicker {
370
370
  size: "regular" | "large";
371
371
  placeholder?: string;
@@ -375,8 +375,22 @@ interface DatePicker {
375
375
  isInstantClose?: boolean;
376
376
  disabled?: boolean;
377
377
  popoverContentClassName?: string;
378
+ minDate?: Date;
379
+ maxDate?: Date;
380
+ /**
381
+ * Custom disabled dates matcher.
382
+ * Note: For optimal performance, memoize this prop in the parent component
383
+ * to avoid unnecessary recalculations.
384
+ *
385
+ * @example
386
+ * const disabledDates = useMemo(() => [
387
+ * new Date(2025, 0, 15),
388
+ * { from: new Date(2025, 1, 10), to: new Date(2025, 1, 15) }
389
+ * ], []);
390
+ */
391
+ disabledDates?: Matcher | Matcher[];
378
392
  }
379
- declare const DatePicker: ({ size, placeholder, format, value, mode, onChange, isInstantClose, open: openProp, onOpenChange: onOpenChangeProp, disabled, popoverContentClassName, ...rest }: DatePickerProps) => react_jsx_runtime.JSX.Element;
393
+ declare const DatePicker: ({ size, placeholder, format, value, mode, onChange, isInstantClose, open: openProp, onOpenChange: onOpenChangeProp, disabled, popoverContentClassName, minDate, maxDate, disabledDates, ...rest }: DatePickerProps) => react_jsx_runtime.JSX.Element;
380
394
 
381
395
  declare const ContextMenu: FC<ContextMenu$1.ContextMenuProps>;
382
396
  declare const ContextMenuTrigger: FC<ContextMenu$1.ContextMenuTriggerProps>;