sellmate-design-system-react 9.0.0-beta.0 → 9.0.0-beta.3

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/index.js CHANGED
@@ -3113,6 +3113,209 @@ var SIcon = /* @__PURE__ */ forwardRef(function SIcon2({ name, size = 24, color,
3113
3113
  }
3114
3114
  );
3115
3115
  });
3116
+ var VIEWBOX = 80;
3117
+ var RADIUS = 34;
3118
+ var STROKE = 12;
3119
+ var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
3120
+ var COLOR_MAP = {
3121
+ primary: { arc: "var(--cmp-progress-color-active)", track: "var(--cmp-progress-color-track)" },
3122
+ // inverse: 어두운 배경용 — arc 흰색, track 은 흰색 40%(회색 링)
3123
+ inverse: { arc: "var(--color-white)", track: "rgba(255,255,255,0.4)" },
3124
+ error: { arc: "var(--color-red-75)", track: "var(--cmp-progress-color-track)" },
3125
+ complete: { arc: "#01BB4B", track: "var(--cmp-progress-color-track)" },
3126
+ // neutral: 콘텐츠가 아직 없는 자리를 채우는 용도 — arc 도 회색이라 진행률보다 "대기"로 읽힌다
3127
+ neutral: { arc: "var(--color-grey-35)", track: "var(--cmp-progress-color-track)" }
3128
+ };
3129
+ var DEFAULT_SIZE = 80;
3130
+ var clamp = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
3131
+ var SCircleProgress = /* @__PURE__ */ forwardRef(
3132
+ function SCircleProgress2({
3133
+ value = 0,
3134
+ type = "primary",
3135
+ indeterminate = false,
3136
+ size = DEFAULT_SIZE,
3137
+ label,
3138
+ innerValue = false,
3139
+ className,
3140
+ style
3141
+ }, ref) {
3142
+ const v = clamp(value);
3143
+ const dimension = typeof size === "number" ? `${size}px` : size;
3144
+ const colors = COLOR_MAP[type];
3145
+ const c = VIEWBOX / 2;
3146
+ const percentColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : colors.arc;
3147
+ const filled = v / 100 * CIRCUMFERENCE;
3148
+ const showPercent = !indeterminate;
3149
+ const percentNode = /* @__PURE__ */ jsxs(
3150
+ "span",
3151
+ {
3152
+ className: cn(
3153
+ "text-center text-[16px] font-bold",
3154
+ innerValue ? (
3155
+ // leading-[normal]: 26px leading 은 폰트 content(19px) 위아래로 홀수(7px)만큼 남아
3156
+ // 3px/4px 로 쪼개지고 글자가 링 중심보다 위로 뜬다. (leading-normal 은 1.5 라 동일 문제)
3157
+ "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 leading-[normal]"
3158
+ ) : "leading-[26px]"
3159
+ ),
3160
+ style: { color: percentColor },
3161
+ children: [
3162
+ Math.round(v),
3163
+ "%"
3164
+ ]
3165
+ }
3166
+ );
3167
+ return /* @__PURE__ */ jsxs(
3168
+ "div",
3169
+ {
3170
+ ref,
3171
+ className: cn(
3172
+ "inline-flex flex-col items-center gap-[var(--cmp-progress-circular-gap)]",
3173
+ className
3174
+ ),
3175
+ style,
3176
+ children: [
3177
+ /* @__PURE__ */ jsxs("div", { className: cn(innerValue && "relative"), children: [
3178
+ /* @__PURE__ */ jsxs(
3179
+ "svg",
3180
+ {
3181
+ viewBox: `0 0 ${VIEWBOX} ${VIEWBOX}`,
3182
+ xmlns: "http://www.w3.org/2000/svg",
3183
+ className: cn(indeterminate && "origin-center animate-circle-spin"),
3184
+ style: { width: dimension, height: dimension },
3185
+ children: [
3186
+ /* @__PURE__ */ jsx(
3187
+ "circle",
3188
+ {
3189
+ cx: c,
3190
+ cy: c,
3191
+ r: RADIUS,
3192
+ fill: "none",
3193
+ strokeWidth: STROKE,
3194
+ style: { stroke: colors.track }
3195
+ }
3196
+ ),
3197
+ /* @__PURE__ */ jsx(
3198
+ "circle",
3199
+ {
3200
+ cx: c,
3201
+ cy: c,
3202
+ r: RADIUS,
3203
+ fill: "none",
3204
+ strokeWidth: STROKE,
3205
+ strokeLinecap: "round",
3206
+ className: cn(
3207
+ "origin-center",
3208
+ indeterminate ? "animate-circle-arc" : "-rotate-90 transition-[stroke-dasharray] duration-[400ms] ease-linear"
3209
+ ),
3210
+ style: {
3211
+ stroke: colors.arc,
3212
+ ...indeterminate ? {} : { strokeDasharray: `${filled} ${CIRCUMFERENCE}`, strokeDashoffset: 0 }
3213
+ }
3214
+ }
3215
+ )
3216
+ ]
3217
+ }
3218
+ ),
3219
+ showPercent && innerValue && percentNode
3220
+ ] }),
3221
+ showPercent && !innerValue && percentNode,
3222
+ label && /* @__PURE__ */ jsx("span", { className: "text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-progress-color-label)]", children: label })
3223
+ ]
3224
+ }
3225
+ );
3226
+ }
3227
+ );
3228
+ var IMAGE_FITS = ["cover", "contain", "fill", "none", "scale-down"];
3229
+ var EMPTY_ICON_SIZE = "60cqh";
3230
+ var SPINNER_SIZE = "45cqh";
3231
+ var SImage = /* @__PURE__ */ forwardRef(function SImage2({
3232
+ src,
3233
+ alt = "",
3234
+ ratio = 1,
3235
+ fit = "cover",
3236
+ position = "50% 50%",
3237
+ loading: loading2 = "lazy",
3238
+ loadingShowDelay = 0,
3239
+ noSpinner = false,
3240
+ loadingSlot,
3241
+ errorSlot,
3242
+ imgProps,
3243
+ onLoad,
3244
+ onError,
3245
+ className,
3246
+ style,
3247
+ ...rest
3248
+ }, ref) {
3249
+ const [status, setStatus] = useState("loading");
3250
+ const [spinnerVisible, setSpinnerVisible] = useState(loadingShowDelay <= 0);
3251
+ const [trackedSrc, setTrackedSrc] = useState(src);
3252
+ const imgRef = useRef(null);
3253
+ if (src !== trackedSrc) {
3254
+ setTrackedSrc(src);
3255
+ setStatus("loading");
3256
+ }
3257
+ useEffect(() => {
3258
+ if (!src) return;
3259
+ const node = imgRef.current;
3260
+ if (node?.complete && node.naturalWidth > 0) {
3261
+ setStatus((current) => current === "loading" ? "loaded" : current);
3262
+ }
3263
+ }, [src]);
3264
+ useEffect(() => {
3265
+ if (status !== "loading" || loadingShowDelay <= 0) {
3266
+ setSpinnerVisible(status === "loading");
3267
+ return;
3268
+ }
3269
+ setSpinnerVisible(false);
3270
+ const timer = setTimeout(() => setSpinnerVisible(true), loadingShowDelay);
3271
+ return () => clearTimeout(timer);
3272
+ }, [status, loadingShowDelay, src]);
3273
+ const isEmpty = !src;
3274
+ const showFallback = isEmpty || status === "error";
3275
+ const showSpinner = !isEmpty && status === "loading" && !noSpinner && spinnerVisible;
3276
+ return /* @__PURE__ */ jsxs(
3277
+ "div",
3278
+ {
3279
+ ref,
3280
+ className: cn("relative overflow-hidden bg-(--cmp-image-bg)", className),
3281
+ style: { containerType: "size", aspectRatio: ratio, ...style },
3282
+ ...rest,
3283
+ children: [
3284
+ !isEmpty && /* @__PURE__ */ jsx(
3285
+ "img",
3286
+ {
3287
+ ...imgProps,
3288
+ ref: imgRef,
3289
+ src,
3290
+ alt,
3291
+ loading: loading2,
3292
+ onLoad: () => {
3293
+ setStatus("loaded");
3294
+ onLoad?.(src);
3295
+ },
3296
+ onError: (event) => {
3297
+ setStatus("error");
3298
+ onError?.(event);
3299
+ },
3300
+ className: cn(
3301
+ "h-full w-full",
3302
+ // 로딩 중에도 자리를 지켜야 브라우저가 계속 받아온다 — 숨기되 없애지 않는다
3303
+ status === "loaded" ? "visible" : "invisible",
3304
+ imgProps?.className
3305
+ ),
3306
+ style: {
3307
+ objectFit: fit,
3308
+ objectPosition: position,
3309
+ ...imgProps?.style
3310
+ }
3311
+ }
3312
+ ),
3313
+ showFallback && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: errorSlot ?? /* @__PURE__ */ jsx(SIcon, { name: "image", size: EMPTY_ICON_SIZE, color: "var(--cmp-image-icon)" }) }),
3314
+ showSpinner && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: loadingSlot ?? /* @__PURE__ */ jsx(SCircleProgress, { indeterminate: true, type: "neutral", size: SPINNER_SIZE }) })
3315
+ ]
3316
+ }
3317
+ );
3318
+ });
3116
3319
  var BADGE_COLORS = [
3117
3320
  "red",
3118
3321
  "orange",
@@ -5554,14 +5757,14 @@ var SCallout = /* @__PURE__ */ forwardRef(function SCallout2({ type = "default",
5554
5757
  );
5555
5758
  });
5556
5759
  var FILL_COLOR = {
5557
- primary: "var(--cmp-progress-active-color)",
5558
- error: "var(--cmp-progress-error-color)",
5559
- complete: "var(--cmp-progress-success-color)"
5760
+ primary: "var(--cmp-progress-color-active)",
5761
+ error: "var(--cmp-progress-color-error)",
5762
+ complete: "var(--cmp-progress-color-success)"
5560
5763
  };
5561
- var clamp = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5764
+ var clamp2 = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5562
5765
  var SLinearProgress = /* @__PURE__ */ forwardRef(
5563
5766
  function SLinearProgress2({ value = 0, type = "primary", indeterminate = false, label, className, style }, ref) {
5564
- const v = clamp(value);
5767
+ const v = clamp2(value);
5565
5768
  const fill = FILL_COLOR[type];
5566
5769
  const valueColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : fill;
5567
5770
  const valueText = `${Math.round(v)}%`;
@@ -5608,115 +5811,6 @@ var SLinearProgress = /* @__PURE__ */ forwardRef(
5608
5811
  );
5609
5812
  }
5610
5813
  );
5611
- var VIEWBOX = 80;
5612
- var RADIUS = 34;
5613
- var STROKE = 12;
5614
- var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
5615
- var COLOR_MAP = {
5616
- primary: { arc: "var(--cmp-progress-active-color)", track: "var(--cmp-progress-color-track)" },
5617
- // inverse: 어두운 배경용 — arc 흰색, track 은 흰색 40%(회색 링)
5618
- inverse: { arc: "var(--color-white)", track: "rgba(255,255,255,0.4)" },
5619
- error: { arc: "var(--color-red-75)", track: "var(--cmp-progress-color-track)" },
5620
- complete: { arc: "#01BB4B", track: "var(--cmp-progress-color-track)" }
5621
- };
5622
- var clamp2 = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5623
- var SCircleProgress = /* @__PURE__ */ forwardRef(
5624
- function SCircleProgress2({
5625
- value = 0,
5626
- type = "primary",
5627
- indeterminate = false,
5628
- label,
5629
- innerValue = false,
5630
- className,
5631
- style
5632
- }, ref) {
5633
- const v = clamp2(value);
5634
- const colors = COLOR_MAP[type];
5635
- const c = VIEWBOX / 2;
5636
- const percentColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : colors.arc;
5637
- const filled = v / 100 * CIRCUMFERENCE;
5638
- const showPercent = !indeterminate;
5639
- const percentNode = /* @__PURE__ */ jsxs(
5640
- "span",
5641
- {
5642
- className: cn(
5643
- "text-center text-[16px] font-bold",
5644
- innerValue ? (
5645
- // leading-[normal]: 26px leading 은 폰트 content(19px) 위아래로 홀수(7px)만큼 남아
5646
- // 3px/4px 로 쪼개지고 글자가 링 중심보다 위로 뜬다. (leading-normal 은 1.5 라 동일 문제)
5647
- "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 leading-[normal]"
5648
- ) : "leading-[26px]"
5649
- ),
5650
- style: { color: percentColor },
5651
- children: [
5652
- Math.round(v),
5653
- "%"
5654
- ]
5655
- }
5656
- );
5657
- return /* @__PURE__ */ jsxs(
5658
- "div",
5659
- {
5660
- ref,
5661
- className: cn(
5662
- "inline-flex flex-col items-center gap-[var(--cmp-progress-circular-gap)]",
5663
- className
5664
- ),
5665
- style,
5666
- children: [
5667
- /* @__PURE__ */ jsxs("div", { className: cn(innerValue && "relative"), children: [
5668
- /* @__PURE__ */ jsxs(
5669
- "svg",
5670
- {
5671
- viewBox: `0 0 ${VIEWBOX} ${VIEWBOX}`,
5672
- xmlns: "http://www.w3.org/2000/svg",
5673
- className: cn(
5674
- "h-[80px] w-[80px]",
5675
- indeterminate && "origin-center animate-circle-spin"
5676
- ),
5677
- children: [
5678
- /* @__PURE__ */ jsx(
5679
- "circle",
5680
- {
5681
- cx: c,
5682
- cy: c,
5683
- r: RADIUS,
5684
- fill: "none",
5685
- strokeWidth: STROKE,
5686
- style: { stroke: colors.track }
5687
- }
5688
- ),
5689
- /* @__PURE__ */ jsx(
5690
- "circle",
5691
- {
5692
- cx: c,
5693
- cy: c,
5694
- r: RADIUS,
5695
- fill: "none",
5696
- strokeWidth: STROKE,
5697
- strokeLinecap: "round",
5698
- className: cn(
5699
- "origin-center",
5700
- indeterminate ? "animate-circle-arc" : "-rotate-90 transition-[stroke-dasharray] duration-[400ms] ease-linear"
5701
- ),
5702
- style: {
5703
- stroke: colors.arc,
5704
- ...indeterminate ? {} : { strokeDasharray: `${filled} ${CIRCUMFERENCE}`, strokeDashoffset: 0 }
5705
- }
5706
- }
5707
- )
5708
- ]
5709
- }
5710
- ),
5711
- showPercent && innerValue && percentNode
5712
- ] }),
5713
- showPercent && !innerValue && percentNode,
5714
- label && /* @__PURE__ */ jsx("span", { className: "text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-progress-color-label)]", children: label })
5715
- ]
5716
- }
5717
- );
5718
- }
5719
- );
5720
5814
 
5721
5815
  // src/lib/global-overlay.ts
5722
5816
  var GLOBAL_OVERLAY_ATTR = "data-s-global-overlay";
@@ -6202,10 +6296,15 @@ function SSwitch({
6202
6296
  }
6203
6297
  );
6204
6298
  }
6299
+ var SIZE_CLASSES = {
6300
+ xs: "h-[24px] px-[8px] text-[12px] font-normal leading-[20px]",
6301
+ sm: "h-[28px] px-[12px] text-[14px] font-medium leading-[24px]"
6302
+ };
6205
6303
  function SToggle({
6206
6304
  value = false,
6207
6305
  label = "",
6208
6306
  disabled = false,
6307
+ size = "sm",
6209
6308
  onValueChange,
6210
6309
  className,
6211
6310
  style
@@ -6221,7 +6320,8 @@ function SToggle({
6221
6320
  "aria-pressed": value,
6222
6321
  onClick: () => !disabled && onValueChange?.(!value),
6223
6322
  className: cn(
6224
- "box-border inline-flex h-[28px] items-center justify-center whitespace-nowrap rounded-full border border-solid px-[12px] text-[14px] font-medium leading-[24px] transition-colors",
6323
+ "box-border inline-flex items-center justify-center whitespace-nowrap rounded-full border border-solid transition-colors",
6324
+ SIZE_CLASSES[size],
6225
6325
  disabled ? "cursor-not-allowed" : "cursor-pointer",
6226
6326
  // 미선택(off) hover 시 파란 채움 + 흰 글자
6227
6327
  !disabled && !value && "hover:!bg-[var(--cmp-toggle-bg-hover)] hover:!text-[color:var(--cmp-toggle-content-hover)] hover:!border-[color:var(--cmp-toggle-bg-hover)]",
@@ -12028,12 +12128,244 @@ function SCalendar({
12028
12128
  }
12029
12129
  );
12030
12130
  }
12131
+ var DATEPICKER_MODE_LIST_HEIGHT = 240;
12132
+ var DATEPICKER_MODE_ITEM_HEIGHT = 28;
12133
+ var YEAR_LIST_EDGE_BUFFER = 24;
12134
+ var YEAR_LIST_EXPAND_SIZE = 80;
12135
+ var MONTHS = Array.from({ length: 12 }, (_, i) => i + 1);
12136
+ var getTodayParts = () => {
12137
+ const today = /* @__PURE__ */ new Date();
12138
+ return {
12139
+ year: today.getFullYear(),
12140
+ month: today.getMonth() + 1
12141
+ };
12142
+ };
12143
+ var parseYear = (value) => {
12144
+ const year = Number(value?.slice(0, 4));
12145
+ return Number.isFinite(year) && year > 0 ? year : void 0;
12146
+ };
12147
+ var parseMonth = (value) => {
12148
+ const month = Number(value?.slice(5, 7));
12149
+ return Number.isFinite(month) && month >= 1 && month <= 12 ? month : void 0;
12150
+ };
12151
+ var formatMonthValue = (year, month) => `${year}-${String(month).padStart(2, "0")}`;
12152
+ var isYearDisabled = (year, selectable) => {
12153
+ if (!selectable) return false;
12154
+ const [start, end] = selectable;
12155
+ if (start !== "" && year < Number(start.slice(0, 4))) return true;
12156
+ if (end !== "" && year > Number(end.slice(0, 4))) return true;
12157
+ return false;
12158
+ };
12159
+ var isMonthDisabled = (year, month, selectable) => {
12160
+ if (!selectable) return false;
12161
+ const value = formatMonthValue(year, month);
12162
+ const [start, end] = selectable;
12163
+ if (start !== "" && value < start.slice(0, 7)) return true;
12164
+ if (end !== "" && value > end.slice(0, 7)) return true;
12165
+ return false;
12166
+ };
12167
+ function YearWheel({ value, anchorYear, selectable, onCommit }) {
12168
+ const listRef = useRef(null);
12169
+ const syncingScrollRef = useRef(false);
12170
+ const anchorYearRef = useRef(anchorYear);
12171
+ const [range, setRange] = useState(() => ({
12172
+ start: anchorYear - YEAR_LIST_EXPAND_SIZE,
12173
+ end: anchorYear + YEAR_LIST_EXPAND_SIZE
12174
+ }));
12175
+ const rangeStartRef = useRef(range.start);
12176
+ rangeStartRef.current = range.start;
12177
+ const years = Array.from({ length: range.end - range.start + 1 }, (_, i) => range.start + i);
12178
+ useLayoutEffect(() => {
12179
+ const list = listRef.current;
12180
+ if (!list) return;
12181
+ const index = anchorYearRef.current - rangeStartRef.current;
12182
+ syncingScrollRef.current = true;
12183
+ list.scrollTop = index * DATEPICKER_MODE_ITEM_HEIGHT;
12184
+ requestAnimationFrame(() => {
12185
+ syncingScrollRef.current = false;
12186
+ });
12187
+ }, []);
12188
+ const handleScroll = () => {
12189
+ const list = listRef.current;
12190
+ if (!list) return;
12191
+ if (syncingScrollRef.current) return;
12192
+ if (list.scrollTop < DATEPICKER_MODE_ITEM_HEIGHT * YEAR_LIST_EDGE_BUFFER) {
12193
+ const previousHeight = list.scrollHeight;
12194
+ setRange((current) => ({
12195
+ start: current.start - YEAR_LIST_EXPAND_SIZE,
12196
+ end: current.end
12197
+ }));
12198
+ requestAnimationFrame(() => {
12199
+ if (!listRef.current) return;
12200
+ syncingScrollRef.current = true;
12201
+ listRef.current.scrollTop += listRef.current.scrollHeight - previousHeight;
12202
+ requestAnimationFrame(() => {
12203
+ syncingScrollRef.current = false;
12204
+ });
12205
+ });
12206
+ return;
12207
+ }
12208
+ if (list.scrollHeight - list.scrollTop - list.clientHeight < DATEPICKER_MODE_ITEM_HEIGHT * YEAR_LIST_EDGE_BUFFER) {
12209
+ setRange((current) => ({
12210
+ start: current.start,
12211
+ end: current.end + YEAR_LIST_EXPAND_SIZE
12212
+ }));
12213
+ }
12214
+ };
12215
+ return /* @__PURE__ */ jsx("div", { className: "h-full w-full overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]", children: /* @__PURE__ */ jsx(
12216
+ "div",
12217
+ {
12218
+ ref: listRef,
12219
+ role: "listbox",
12220
+ "aria-label": "year",
12221
+ className: "h-full overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
12222
+ style: {
12223
+ paddingTop: `calc((${DATEPICKER_MODE_LIST_HEIGHT}px - ${DATEPICKER_MODE_ITEM_HEIGHT}px) / 2)`,
12224
+ paddingBottom: `calc((${DATEPICKER_MODE_LIST_HEIGHT}px - ${DATEPICKER_MODE_ITEM_HEIGHT}px) / 2)`
12225
+ },
12226
+ onScroll: handleScroll,
12227
+ children: years.map((year) => {
12228
+ const selected = year === value;
12229
+ const disabled = isYearDisabled(year, selectable);
12230
+ return /* @__PURE__ */ jsx(
12231
+ "button",
12232
+ {
12233
+ type: "button",
12234
+ role: "option",
12235
+ "aria-selected": selected,
12236
+ disabled,
12237
+ className: cn(
12238
+ "flex w-full items-center justify-center gap-[var(--cmp-datepicker-mode-item-gap)] border-0 px-[var(--cmp-datepicker-mode-item-paddingX)] py-[var(--cmp-datepicker-mode-item-paddingY)] text-center font-base text-12 leading-[20px]",
12239
+ disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
12240
+ !disabled && !selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-default)] hover:bg-[var(--cmp-datepicker-mode-item-bg-hover)] hover:text-[color:var(--cmp-datepicker-mode-item-content-hover)]",
12241
+ !disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
12242
+ ),
12243
+ onClick: () => !disabled && onCommit(year),
12244
+ children: year
12245
+ },
12246
+ year
12247
+ );
12248
+ })
12249
+ }
12250
+ ) });
12251
+ }
12252
+ function MonthWheel({ year, value, selectable, onCommit }) {
12253
+ const listRef = useRef(null);
12254
+ const initialValueRef = useRef(value);
12255
+ useLayoutEffect(() => {
12256
+ const list = listRef.current;
12257
+ if (!list) return;
12258
+ list.scrollTop = Math.max(
12259
+ 0,
12260
+ (initialValueRef.current - 1) * DATEPICKER_MODE_ITEM_HEIGHT - (list.clientHeight - DATEPICKER_MODE_ITEM_HEIGHT) / 2
12261
+ );
12262
+ }, []);
12263
+ return /* @__PURE__ */ jsx("div", { className: "h-full w-full overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]", children: /* @__PURE__ */ jsx(
12264
+ "div",
12265
+ {
12266
+ ref: listRef,
12267
+ role: "listbox",
12268
+ "aria-label": "month",
12269
+ className: "h-full overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
12270
+ children: MONTHS.map((month) => {
12271
+ const selected = month === value;
12272
+ const disabled = isMonthDisabled(year, month, selectable);
12273
+ return /* @__PURE__ */ jsxs(
12274
+ "button",
12275
+ {
12276
+ type: "button",
12277
+ role: "option",
12278
+ "aria-selected": selected,
12279
+ disabled,
12280
+ className: cn(
12281
+ "flex w-full items-center justify-center gap-[var(--cmp-datepicker-mode-item-gap)] border-0 px-[var(--cmp-datepicker-mode-item-paddingX)] py-[var(--cmp-datepicker-mode-item-paddingY)] text-center font-base text-12 leading-[20px]",
12282
+ disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
12283
+ !disabled && !selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-default)] hover:bg-[var(--cmp-datepicker-mode-item-bg-hover)] hover:text-[color:var(--cmp-datepicker-mode-item-content-hover)]",
12284
+ !disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
12285
+ ),
12286
+ onClick: () => !disabled && onCommit(month),
12287
+ children: [
12288
+ month,
12289
+ "\uC6D4"
12290
+ ]
12291
+ },
12292
+ month
12293
+ );
12294
+ })
12295
+ }
12296
+ ) });
12297
+ }
12298
+ function DatePickerListbox({
12299
+ mode,
12300
+ value,
12301
+ selectable,
12302
+ onValueChange,
12303
+ onClose
12304
+ }) {
12305
+ const today = getTodayParts();
12306
+ const [year, setYear] = useState(() => parseYear(value) ?? today.year);
12307
+ const [month, setMonth] = useState(() => parseMonth(value) ?? today.month);
12308
+ const commitYear = (nextYear) => {
12309
+ setYear(nextYear);
12310
+ if (mode === "year") {
12311
+ onValueChange?.(String(nextYear));
12312
+ onClose();
12313
+ return;
12314
+ }
12315
+ onValueChange?.(formatMonthValue(nextYear, month));
12316
+ };
12317
+ const commitMonth = (nextMonth) => {
12318
+ setMonth(nextMonth);
12319
+ onValueChange?.(formatMonthValue(year, nextMonth));
12320
+ onClose();
12321
+ };
12322
+ return /* @__PURE__ */ jsxs(
12323
+ "div",
12324
+ {
12325
+ className: cn(
12326
+ "inline-flex h-[240px] overflow-hidden rounded-[var(--cmp-datepicker-mode-radius)] bg-[var(--cmp-datepicker-mode-bg)] shadow-normal-sm",
12327
+ mode === "month" ? "gap-[var(--cmp-datepicker-mode-yearMonth-gap)]" : "w-[var(--cmp-datepicker-mode-year-width)]"
12328
+ ),
12329
+ children: [
12330
+ /* @__PURE__ */ jsx(
12331
+ "div",
12332
+ {
12333
+ className: cn(
12334
+ "min-w-0",
12335
+ mode === "month" ? "w-[var(--cmp-datepicker-mode-yearMonth-column-width)]" : "w-full"
12336
+ ),
12337
+ children: /* @__PURE__ */ jsx(
12338
+ YearWheel,
12339
+ {
12340
+ value: year,
12341
+ anchorYear: today.year,
12342
+ selectable,
12343
+ onCommit: commitYear
12344
+ }
12345
+ )
12346
+ }
12347
+ ),
12348
+ mode === "month" && /* @__PURE__ */ jsxs(Fragment, { children: [
12349
+ /* @__PURE__ */ jsx(
12350
+ "div",
12351
+ {
12352
+ "aria-hidden": true,
12353
+ className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
12354
+ }
12355
+ ),
12356
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsx(MonthWheel, { year, value: month, selectable, onCommit: commitMonth }) })
12357
+ ] })
12358
+ ]
12359
+ }
12360
+ );
12361
+ }
12031
12362
  function SDatePicker({
12032
12363
  value,
12033
12364
  onValueChange,
12034
12365
  onViewChange,
12366
+ mode = "date",
12035
12367
  size = "sm",
12036
- placeholder = "YYYY-MM-DD",
12368
+ placeholder,
12037
12369
  selectable,
12038
12370
  disabled = false,
12039
12371
  width,
@@ -12059,6 +12391,7 @@ function SDatePicker({
12059
12391
  const [focused, setFocused] = useState(false);
12060
12392
  const [ruleError, setRuleError] = useState("");
12061
12393
  const hasValue = value != null && value !== "";
12394
+ const resolvedPlaceholder = placeholder ?? (mode === "year" ? "YYYY" : mode === "month" ? "YYYY-MM" : "YYYY-MM-DD");
12062
12395
  useFormField({
12063
12396
  name,
12064
12397
  validate: () => {
@@ -12070,12 +12403,34 @@ function SDatePicker({
12070
12403
  });
12071
12404
  const resolvedError = error || ruleError !== "";
12072
12405
  const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
12073
- const dims = size === "md" ? { padX: 16, gap: 12, icon: 20, minW: 104, fontSize: 14, lineHeight: 24, radius: 6 } : { padX: 12, gap: 8, icon: 16, minW: 80, fontSize: 12, lineHeight: 20, radius: 4 };
12406
+ const dims = size === "md" ? {
12407
+ padX: 16,
12408
+ gap: 12,
12409
+ icon: 20,
12410
+ minW: 104,
12411
+ fieldMinW: 168,
12412
+ fontSize: 14,
12413
+ lineHeight: 24,
12414
+ radius: 6
12415
+ } : {
12416
+ padX: 12,
12417
+ gap: 8,
12418
+ icon: 16,
12419
+ minW: 80,
12420
+ fieldMinW: 128,
12421
+ fontSize: 12,
12422
+ lineHeight: 20,
12423
+ radius: 4
12424
+ };
12074
12425
  const select = (date) => {
12075
12426
  onValueChange?.(date);
12076
12427
  if (rules && rules.length > 0) setRuleError(runRules(date, rules));
12077
12428
  setOpen(false);
12078
12429
  };
12430
+ const selectListboxValue = (nextValue) => {
12431
+ onValueChange?.(nextValue);
12432
+ if (rules && rules.length > 0) setRuleError(runRules(nextValue, rules));
12433
+ };
12079
12434
  return /* @__PURE__ */ jsx(
12080
12435
  SField,
12081
12436
  {
@@ -12094,6 +12449,7 @@ function SDatePicker({
12094
12449
  focused: focused || open,
12095
12450
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12096
12451
  width,
12452
+ minWidth: dims.fieldMinW,
12097
12453
  disabled,
12098
12454
  className,
12099
12455
  style,
@@ -12128,7 +12484,7 @@ function SDatePicker({
12128
12484
  lineHeight: `${dims.lineHeight}px`,
12129
12485
  color: disabled ? "var(--cmp-datepicker-text-disabled)" : hasValue ? "var(--cmp-datepicker-text-default)" : "var(--cmp-field-hint-color)"
12130
12486
  },
12131
- children: hasValue ? value : placeholder
12487
+ children: hasValue ? value : resolvedPlaceholder
12132
12488
  }
12133
12489
  )
12134
12490
  ]
@@ -12144,7 +12500,7 @@ function SDatePicker({
12144
12500
  "z-50 max-w-[var(--radix-popover-content-available-width)]",
12145
12501
  FLOATING_SLIDE_ANIM
12146
12502
  ),
12147
- children: /* @__PURE__ */ jsx(
12503
+ children: mode === "date" ? /* @__PURE__ */ jsx(
12148
12504
  SCalendar,
12149
12505
  {
12150
12506
  value: value ?? void 0,
@@ -12153,6 +12509,15 @@ function SDatePicker({
12153
12509
  onValueChange: select,
12154
12510
  onViewChange
12155
12511
  }
12512
+ ) : /* @__PURE__ */ jsx(
12513
+ DatePickerListbox,
12514
+ {
12515
+ mode,
12516
+ value,
12517
+ selectable,
12518
+ onValueChange: selectListboxValue,
12519
+ onClose: () => setOpen(false)
12520
+ }
12156
12521
  )
12157
12522
  }
12158
12523
  ) })
@@ -16360,6 +16725,6 @@ function STimeRangePicker({
16360
16725
  );
16361
16726
  }
16362
16727
 
16363
- export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, GNB_COLORS, GNB_COMMON, GNB_FOLD_MS, GNB_FULL_LOGO_WIDTH, GNB_HEADER, GNB_MENU, GNB_MENU_WIDTH, GNB_RAIL, GNB_RAIL_WIDTH, ICONS, PAGE_BACKGROUNDS, RangeCalendar, SActionModal, SBadge, SBarcodeInput, SButton, SCROLL_AREA_AXES, SCalendar, SCallout, SCard, SCheckbox, SChip2 as SChip, SChipFilter, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDateRangePicker, SDivider, SDraggableGroup, SDraggableItem2 as SDraggableItem, SDraggableList, SDraggableProvider, SDrawer, SDropdownButton2 as SDropdownButton, SExpansionItem, SExpansionList2 as SExpansionList, SField, SFilePicker, SForm, SGhostButton, SGnb, SGuide, SIcon, SInput, SKeyValueTable, SLAYOUT_MIN_WIDTH, SLayout, SLayoutContext, SLinearProgress, SList2 as SList, SListItem2 as SListItem, SLoadingContainer, SLoadingModal, SLoadingViewport, SModal, SModalOutlet, SNumberInput, SPage, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioGroup, SSPLITTER_UNITS, SScrollArea, SSectionHeaderCard2 as SSectionHeaderCard, SSelect2 as SSelect, SSplitter2 as SSplitter, SStepper, SSwitch, STEPPER_SIZES, STable, STableBar, STabs, STag, STextLink, STextarea, STimePicker, STimeRangePicker, SToast, SToastContainer, SToggle, STooltip2 as STooltip, STree2 as STree, STreeItem2 as STreeItem, TAG_COLORS, TAG_SHAPES, TAG_SIZES, TIMEPICKER_SIZES, TIMEPICKER_SIZE_CONFIG, buttonPreset, cn, findGnbAncestors, findGnbRailValue, isColorKey, loading, resolveColor, useSLayout };
16728
+ export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, GNB_COLORS, GNB_COMMON, GNB_FOLD_MS, GNB_FULL_LOGO_WIDTH, GNB_HEADER, GNB_MENU, GNB_MENU_WIDTH, GNB_RAIL, GNB_RAIL_WIDTH, ICONS, IMAGE_FITS, PAGE_BACKGROUNDS, RangeCalendar, SActionModal, SBadge, SBarcodeInput, SButton, SCROLL_AREA_AXES, SCalendar, SCallout, SCard, SCheckbox, SChip2 as SChip, SChipFilter, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDateRangePicker, SDivider, SDraggableGroup, SDraggableItem2 as SDraggableItem, SDraggableList, SDraggableProvider, SDrawer, SDropdownButton2 as SDropdownButton, SExpansionItem, SExpansionList2 as SExpansionList, SField, SFilePicker, SForm, SGhostButton, SGnb, SGuide, SIcon, SImage, SInput, SKeyValueTable, SLAYOUT_MIN_WIDTH, SLayout, SLayoutContext, SLinearProgress, SList2 as SList, SListItem2 as SListItem, SLoadingContainer, SLoadingModal, SLoadingViewport, SModal, SModalOutlet, SNumberInput, SPage, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioGroup, SSPLITTER_UNITS, SScrollArea, SSectionHeaderCard2 as SSectionHeaderCard, SSelect2 as SSelect, SSplitter2 as SSplitter, SStepper, SSwitch, STEPPER_SIZES, STable, STableBar, STabs, STag, STextLink, STextarea, STimePicker, STimeRangePicker, SToast, SToastContainer, SToggle, STooltip2 as STooltip, STree2 as STree, STreeItem2 as STreeItem, TAG_COLORS, TAG_SHAPES, TAG_SIZES, TIMEPICKER_SIZES, TIMEPICKER_SIZE_CONFIG, buttonPreset, cn, findGnbAncestors, findGnbRailValue, isColorKey, loading, resolveColor, useSLayout };
16364
16729
  //# sourceMappingURL=index.js.map
16365
16730
  //# sourceMappingURL=index.js.map