sellmate-design-system-react 9.0.0-beta.90 → 9.0.0-beta.92
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/AGENTS.md +40 -13
- package/dist/components/SChatInput/README.md +8 -2
- package/dist/components/SChatInput/SChatInput.d.ts +54 -4
- package/dist/components/SDatePicker/README.md +1 -0
- package/dist/components/SDatePicker/SDatePicker.d.ts +9 -1
- package/dist/components/SDateRangePicker/README.md +1 -0
- package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +9 -1
- package/dist/components/SExpansionItem/README.md +1 -0
- package/dist/components/SExpansionItem/SExpansionItem.d.ts +10 -0
- package/dist/components/SGhostButton/README.md +3 -1
- package/dist/components/SGhostButton/SGhostButton.d.ts +18 -3
- package/dist/components/SGnbSystem/README.md +6 -2
- package/dist/components/SGnbSystem/SGnbSystem.d.ts +3 -3
- package/dist/components/SGnbSystem/gnbSystem.config.d.ts +5 -3
- package/dist/components/STimePicker/README.md +1 -1
- package/dist/components/STimePicker/STimePicker.d.ts +3 -7
- package/dist/components/STimePicker/TimeListbox.d.ts +21 -0
- package/dist/components/STimeRangePicker/README.md +2 -2
- package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +5 -1
- package/dist/index.cjs +644 -626
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +644 -626
- package/dist/index.js.map +1 -1
- package/dist/lib/date.d.ts +9 -0
- package/dist/lib/mode-listbox.d.ts +10 -0
- package/dist/llms-full.txt +62 -21
- package/dist/llms.txt +40 -13
- package/dist/styles.css +6 -35
- package/dist/theme.css +0 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4486,6 +4486,8 @@ var SGhostButton = /* @__PURE__ */ forwardRef(function SGhostButton2({
|
|
|
4486
4486
|
ariaExpanded,
|
|
4487
4487
|
tooltipText,
|
|
4488
4488
|
disabled = false,
|
|
4489
|
+
href,
|
|
4490
|
+
target,
|
|
4489
4491
|
onClick,
|
|
4490
4492
|
anchorClassName,
|
|
4491
4493
|
anchorStyle,
|
|
@@ -4509,51 +4511,74 @@ var SGhostButton = /* @__PURE__ */ forwardRef(function SGhostButton2({
|
|
|
4509
4511
|
cancelHide();
|
|
4510
4512
|
hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
|
|
4511
4513
|
};
|
|
4512
|
-
const s = SIZE[size];
|
|
4513
|
-
const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
|
|
4514
4514
|
const text = (tooltipText ?? "").trim();
|
|
4515
4515
|
const hasTooltip = text !== "";
|
|
4516
|
-
const
|
|
4516
|
+
const [prevHasTooltip, setPrevHasTooltip] = useState(hasTooltip);
|
|
4517
|
+
if (prevHasTooltip !== hasTooltip) {
|
|
4518
|
+
setPrevHasTooltip(hasTooltip);
|
|
4519
|
+
if (!hasTooltip) setOpen(false);
|
|
4520
|
+
}
|
|
4521
|
+
const s = SIZE[size];
|
|
4522
|
+
const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
|
|
4523
|
+
const shared = {
|
|
4524
|
+
"aria-disabled": disabled ? "true" : void 0,
|
|
4525
|
+
"aria-label": ariaLabel.trim() || void 0,
|
|
4526
|
+
"tabIndex": disabled ? -1 : void 0,
|
|
4527
|
+
"onClick": (e) => {
|
|
4528
|
+
if (disabled) {
|
|
4529
|
+
e.preventDefault();
|
|
4530
|
+
return;
|
|
4531
|
+
}
|
|
4532
|
+
onClick?.(e);
|
|
4533
|
+
},
|
|
4534
|
+
"className": cn(
|
|
4535
|
+
"group/ghost-button relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
|
|
4536
|
+
disabled ? "cursor-not-allowed" : "cursor-pointer",
|
|
4537
|
+
"focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
|
|
4538
|
+
className
|
|
4539
|
+
),
|
|
4540
|
+
"style": {
|
|
4541
|
+
width: `var(--cmp-ghostButton-${size}-size)`,
|
|
4542
|
+
height: `var(--cmp-ghostButton-${size}-size)`,
|
|
4543
|
+
borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
|
|
4544
|
+
...style
|
|
4545
|
+
}
|
|
4546
|
+
};
|
|
4547
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4548
|
+
!disabled && /* @__PURE__ */ jsx(
|
|
4549
|
+
"span",
|
|
4550
|
+
{
|
|
4551
|
+
"aria-hidden": true,
|
|
4552
|
+
className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover/ghost-button:opacity-5",
|
|
4553
|
+
style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
|
|
4554
|
+
}
|
|
4555
|
+
),
|
|
4556
|
+
/* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
|
|
4557
|
+
] });
|
|
4558
|
+
const btn2 = href != null ? (
|
|
4559
|
+
// 비활성 링크는 주소를 뗀다 — `<a>` 에는 disabled 가 없어서 주소가 남으면 가운데 클릭으로 열린다.
|
|
4560
|
+
/* @__PURE__ */ jsx(
|
|
4561
|
+
"a",
|
|
4562
|
+
{
|
|
4563
|
+
ref,
|
|
4564
|
+
href: disabled ? void 0 : href,
|
|
4565
|
+
target,
|
|
4566
|
+
rel: target === "_blank" ? "noopener noreferrer" : void 0,
|
|
4567
|
+
role: disabled ? "link" : void 0,
|
|
4568
|
+
...shared,
|
|
4569
|
+
children: content
|
|
4570
|
+
}
|
|
4571
|
+
)
|
|
4572
|
+
) : /* @__PURE__ */ jsx(
|
|
4517
4573
|
"button",
|
|
4518
4574
|
{
|
|
4519
4575
|
ref,
|
|
4520
4576
|
type: "button",
|
|
4521
4577
|
disabled,
|
|
4522
|
-
"aria-disabled": disabled ? "true" : void 0,
|
|
4523
|
-
"aria-label": ariaLabel.trim() || void 0,
|
|
4524
4578
|
"aria-pressed": ariaPressed,
|
|
4525
4579
|
"aria-expanded": ariaExpanded,
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
if (disabled) {
|
|
4529
|
-
e.preventDefault();
|
|
4530
|
-
return;
|
|
4531
|
-
}
|
|
4532
|
-
onClick?.(e);
|
|
4533
|
-
},
|
|
4534
|
-
className: cn(
|
|
4535
|
-
"group/ghost-button relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
|
|
4536
|
-
disabled ? "cursor-not-allowed" : "cursor-pointer",
|
|
4537
|
-
"focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
|
|
4538
|
-
className
|
|
4539
|
-
),
|
|
4540
|
-
style: {
|
|
4541
|
-
width: `var(--cmp-ghostButton-${size}-size)`,
|
|
4542
|
-
height: `var(--cmp-ghostButton-${size}-size)`,
|
|
4543
|
-
borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
|
|
4544
|
-
...style
|
|
4545
|
-
},
|
|
4546
|
-
children: [
|
|
4547
|
-
!disabled && /* @__PURE__ */ jsx(
|
|
4548
|
-
"span",
|
|
4549
|
-
{
|
|
4550
|
-
"aria-hidden": true,
|
|
4551
|
-
className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover/ghost-button:opacity-5",
|
|
4552
|
-
style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
|
|
4553
|
-
}
|
|
4554
|
-
),
|
|
4555
|
-
/* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
|
|
4556
|
-
]
|
|
4580
|
+
...shared,
|
|
4581
|
+
children: content
|
|
4557
4582
|
}
|
|
4558
4583
|
);
|
|
4559
4584
|
if (!hasTooltip) return btn2;
|
|
@@ -5281,6 +5306,9 @@ var FIELD_BORDER = 1;
|
|
|
5281
5306
|
var TEXTAREA_MIN_HEIGHT = `calc(var(--cmp-chatInput-buttonFrame-height) - ${FIELD_BORDER * 2}px)`;
|
|
5282
5307
|
var BUTTON_BOTTOM_INSET = `calc((var(--cmp-chatInput-buttonFrame-height) - var(--cmp-ghostButton-md-size)) / 2)`;
|
|
5283
5308
|
var DEFAULT_MAX_ROWS = 5;
|
|
5309
|
+
function BarButton(props) {
|
|
5310
|
+
return /* @__PURE__ */ jsx(SGhostButton, { ...props, size: "md", style: { marginBottom: BUTTON_BOTTOM_INSET } });
|
|
5311
|
+
}
|
|
5284
5312
|
var SChatInputImpl = /* @__PURE__ */ forwardRef(function SChatInput({
|
|
5285
5313
|
value = "",
|
|
5286
5314
|
onValueChange,
|
|
@@ -5288,7 +5316,13 @@ var SChatInputImpl = /* @__PURE__ */ forwardRef(function SChatInput({
|
|
|
5288
5316
|
disabled = false,
|
|
5289
5317
|
onSend,
|
|
5290
5318
|
hasAttachments = false,
|
|
5319
|
+
onAttachFiles,
|
|
5320
|
+
attachAccept,
|
|
5321
|
+
attachMultiple = true,
|
|
5291
5322
|
onAttach,
|
|
5323
|
+
attachDisabled = false,
|
|
5324
|
+
attachTooltip,
|
|
5325
|
+
sendDisabled = false,
|
|
5292
5326
|
onPasteFiles,
|
|
5293
5327
|
busy = false,
|
|
5294
5328
|
onStop,
|
|
@@ -5300,11 +5334,23 @@ var SChatInputImpl = /* @__PURE__ */ forwardRef(function SChatInput({
|
|
|
5300
5334
|
...rest
|
|
5301
5335
|
}, ref) {
|
|
5302
5336
|
const hasText = value.trim() !== "";
|
|
5303
|
-
const canSend = !disabled && !busy && (hasText || hasAttachments);
|
|
5304
|
-
const send =
|
|
5337
|
+
const canSend = !disabled && !busy && !sendDisabled && (hasText || hasAttachments);
|
|
5338
|
+
const send = () => {
|
|
5305
5339
|
if (!canSend) return;
|
|
5306
5340
|
onSend?.(hasText ? value : "");
|
|
5307
|
-
}
|
|
5341
|
+
};
|
|
5342
|
+
const fileInputRef = useRef(null);
|
|
5343
|
+
const hasAttachButton = onAttachFiles != null || onAttach != null;
|
|
5344
|
+
const attachLocked = disabled || attachDisabled;
|
|
5345
|
+
const handleAttachClick = () => {
|
|
5346
|
+
if (onAttachFiles != null) fileInputRef.current?.click();
|
|
5347
|
+
else onAttach?.();
|
|
5348
|
+
};
|
|
5349
|
+
const handleFilesPicked = (e) => {
|
|
5350
|
+
const files = Array.from(e.target.files ?? []);
|
|
5351
|
+
e.target.value = "";
|
|
5352
|
+
if (files.length > 0) onAttachFiles?.(files);
|
|
5353
|
+
};
|
|
5308
5354
|
const handleKeyDown = (e) => {
|
|
5309
5355
|
rest.onKeyDown?.(e);
|
|
5310
5356
|
if (e.key !== "Enter" || e.shiftKey || e.nativeEvent.isComposing) return;
|
|
@@ -5317,19 +5363,31 @@ var SChatInputImpl = /* @__PURE__ */ forwardRef(function SChatInput({
|
|
|
5317
5363
|
const files = Array.from(e.clipboardData.files);
|
|
5318
5364
|
if (files.length === 0) return;
|
|
5319
5365
|
e.preventDefault();
|
|
5366
|
+
if (attachDisabled) return;
|
|
5320
5367
|
onPasteFiles(files);
|
|
5321
5368
|
};
|
|
5322
5369
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col bg-(--cmp-chatInput-bg)", className), style, children: [
|
|
5323
5370
|
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-(--cmp-chatInput-gap) p-(--cmp-chatInput-padding)", children: [
|
|
5324
|
-
|
|
5325
|
-
|
|
5371
|
+
hasAttachButton && /* @__PURE__ */ jsx(
|
|
5372
|
+
BarButton,
|
|
5326
5373
|
{
|
|
5327
5374
|
icon: "attachFile",
|
|
5328
|
-
size: "md",
|
|
5329
5375
|
ariaLabel: "\uD30C\uC77C \uCCA8\uBD80",
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5376
|
+
tooltipText: attachTooltip,
|
|
5377
|
+
disabled: attachLocked,
|
|
5378
|
+
onClick: handleAttachClick
|
|
5379
|
+
}
|
|
5380
|
+
),
|
|
5381
|
+
onAttachFiles != null && /* @__PURE__ */ jsx(
|
|
5382
|
+
"input",
|
|
5383
|
+
{
|
|
5384
|
+
ref: fileInputRef,
|
|
5385
|
+
type: "file",
|
|
5386
|
+
className: "hidden",
|
|
5387
|
+
accept: attachAccept,
|
|
5388
|
+
multiple: attachMultiple,
|
|
5389
|
+
disabled: attachLocked,
|
|
5390
|
+
onChange: handleFilesPicked
|
|
5333
5391
|
}
|
|
5334
5392
|
),
|
|
5335
5393
|
/* @__PURE__ */ jsx(
|
|
@@ -5353,26 +5411,26 @@ var SChatInputImpl = /* @__PURE__ */ forwardRef(function SChatInput({
|
|
|
5353
5411
|
onPaste: handlePaste
|
|
5354
5412
|
}
|
|
5355
5413
|
),
|
|
5356
|
-
busy ?
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5414
|
+
busy ? (
|
|
5415
|
+
// disabled 는 모두 잠근다 — 응답 도중 대화가 끝나면 정지도 할 수 없다
|
|
5416
|
+
/* @__PURE__ */ jsx(
|
|
5417
|
+
BarButton,
|
|
5418
|
+
{
|
|
5419
|
+
icon: "stop",
|
|
5420
|
+
intent: "danger",
|
|
5421
|
+
ariaLabel: "\uC751\uB2F5 \uC911\uC9C0",
|
|
5422
|
+
disabled,
|
|
5423
|
+
onClick: onStop
|
|
5424
|
+
}
|
|
5425
|
+
)
|
|
5366
5426
|
) : /* @__PURE__ */ jsx(
|
|
5367
|
-
|
|
5427
|
+
BarButton,
|
|
5368
5428
|
{
|
|
5369
5429
|
icon: "send",
|
|
5370
|
-
size: "md",
|
|
5371
5430
|
intent: "subAction",
|
|
5372
5431
|
ariaLabel: "\uC804\uC1A1",
|
|
5373
5432
|
disabled: !canSend,
|
|
5374
|
-
onClick: send
|
|
5375
|
-
style: { marginBottom: BUTTON_BOTTOM_INSET }
|
|
5433
|
+
onClick: send
|
|
5376
5434
|
}
|
|
5377
5435
|
)
|
|
5378
5436
|
] }),
|
|
@@ -6613,6 +6671,7 @@ var SExpansionItem = /* @__PURE__ */ forwardRef(
|
|
|
6613
6671
|
depth = 1,
|
|
6614
6672
|
leading,
|
|
6615
6673
|
trailing,
|
|
6674
|
+
headerAction,
|
|
6616
6675
|
bodyPadding = true,
|
|
6617
6676
|
bordered = false,
|
|
6618
6677
|
expanded,
|
|
@@ -6652,12 +6711,92 @@ var SExpansionItem = /* @__PURE__ */ forwardRef(
|
|
|
6652
6711
|
);
|
|
6653
6712
|
const leadingNode = typeof leading === "function" ? leading({ expanded: isExpanded, selected: isSelected, disabled }) : leading;
|
|
6654
6713
|
const hasSupportingText = supportingText != null && supportingText !== "";
|
|
6714
|
+
const hasHeaderAction = headerAction != null && headerAction !== false;
|
|
6655
6715
|
const id = useId();
|
|
6716
|
+
const surfaceClass = cn(
|
|
6717
|
+
isExpanded && "border-b border-(--cmp-listExpansionItem-title-border)",
|
|
6718
|
+
hasChevronInteraction ? "hover:bg-(--sys-color-bg-neutralBright)" : !isExpanded && "hover:bg-(--cmp-listExpansionItem-title-bg-hover)",
|
|
6719
|
+
// 펼침 배경(파랑)은 bordered 일 때만. 카드처럼 테두리로 경계가 잡힌
|
|
6720
|
+
// 아이템에서만 배경이 선택 상태를 나타낸다.
|
|
6721
|
+
// 테두리 없는 리스트에서는 accentStripe 가 그 역할을 하므로,
|
|
6722
|
+
// 배경까지 깔면 항목 경계가 없는 목록에서 선택 표시가 두 겹이 된다.
|
|
6723
|
+
isExpanded && isSelected && bordered && "bg-(--cmp-listExpansionItem-title-bg-expanded)"
|
|
6724
|
+
);
|
|
6725
|
+
const hoverHandlers = {
|
|
6726
|
+
onMouseEnter: () => setHovered(true),
|
|
6727
|
+
onMouseLeave: () => setHovered(false)
|
|
6728
|
+
};
|
|
6656
6729
|
useEffect(() => {
|
|
6657
6730
|
if (!isExpanded) return;
|
|
6658
6731
|
parentDescendantContext?.reportExpandedDescendant(1);
|
|
6659
6732
|
return () => parentDescendantContext?.reportExpandedDescendant(-1);
|
|
6660
6733
|
}, [isExpanded, parentDescendantContext]);
|
|
6734
|
+
const toggleButton = /* @__PURE__ */ jsxs(
|
|
6735
|
+
"button",
|
|
6736
|
+
{
|
|
6737
|
+
type: "button",
|
|
6738
|
+
disabled,
|
|
6739
|
+
"aria-expanded": isExpanded,
|
|
6740
|
+
"aria-controls": id,
|
|
6741
|
+
...hasHeaderAction ? {} : hoverHandlers,
|
|
6742
|
+
onClick: (event) => {
|
|
6743
|
+
if (disabled) return;
|
|
6744
|
+
const next = !isExpanded;
|
|
6745
|
+
if (expanded === void 0) setInternalExpanded(next);
|
|
6746
|
+
onToggle?.(next, event);
|
|
6747
|
+
},
|
|
6748
|
+
className: cn(
|
|
6749
|
+
"group/expansion-item-button flex w-full cursor-pointer items-center justify-between border-0 text-left text-(--cmp-listExpansionItem-title-text-default) focus-visible:outline focus-visible:outline-2 focus-visible:outline-(--sys-color-border-accent) disabled:cursor-not-allowed disabled:text-(--sys-color-disabled-text)",
|
|
6750
|
+
titleTypo,
|
|
6751
|
+
// 헤더 액션이 있으면 면은 바깥 줄이 그린다 — 버튼은 남은 폭만 차지한다
|
|
6752
|
+
hasHeaderAction ? "min-w-0 flex-1" : surfaceClass,
|
|
6753
|
+
// 높이는 세로 패딩과 내용이 정한다 — 제목 줄에 높이를 못 박지 않는다 (SListItem 과 동일)
|
|
6754
|
+
dense ? "py-(--cmp-listExpansionItem-title-paddingY-dense)" : "py-(--cmp-listExpansionItem-title-paddingY-normal)",
|
|
6755
|
+
"gap-(--cmp-listExpansionItem-title-gap)",
|
|
6756
|
+
// 헤더 액션이 있으면 오른쪽 끝 여백은 액션 칸이 갖고, 버튼은 액션과의 간격만 남긴다
|
|
6757
|
+
hasHeaderAction ? "pr-(--cmp-listExpansionItem-title-gap)" : "pr-(--cmp-listExpansionItem-title-paddingX)",
|
|
6758
|
+
depth === 2 ? "pl-[36px]" : depth === 3 ? "pl-[56px]" : "pl-[16px]",
|
|
6759
|
+
isExpanded && "text-(--cmp-listExpansionItem-title-text-expanded)"
|
|
6760
|
+
),
|
|
6761
|
+
children: [
|
|
6762
|
+
!leading && /* @__PURE__ */ jsx(
|
|
6763
|
+
"span",
|
|
6764
|
+
{
|
|
6765
|
+
className: cn(
|
|
6766
|
+
"flex transition-transform duration-200 ease-out motion-reduce:transition-none",
|
|
6767
|
+
isExpanded && "rotate-180"
|
|
6768
|
+
),
|
|
6769
|
+
children: /* @__PURE__ */ jsx(SIcon, { name: "caretDown", size: 12, color: "var(--sys-color-fg-quaternary)" })
|
|
6770
|
+
}
|
|
6771
|
+
),
|
|
6772
|
+
leadingNode,
|
|
6773
|
+
/* @__PURE__ */ jsxs(
|
|
6774
|
+
"span",
|
|
6775
|
+
{
|
|
6776
|
+
className: cn(
|
|
6777
|
+
"min-w-0 flex-1",
|
|
6778
|
+
supportingTextPosition === "bottom" ? "flex flex-col" : "flex items-center gap-[8px] whitespace-nowrap"
|
|
6779
|
+
),
|
|
6780
|
+
children: [
|
|
6781
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: title }),
|
|
6782
|
+
hasSupportingText && /* @__PURE__ */ jsx(
|
|
6783
|
+
"span",
|
|
6784
|
+
{
|
|
6785
|
+
className: cn(
|
|
6786
|
+
"typo-body-sm-default min-w-0 truncate text-(--sys-color-fg-secondary)",
|
|
6787
|
+
supportingTextPosition === "right" && "flex-1"
|
|
6788
|
+
),
|
|
6789
|
+
children: supportingText
|
|
6790
|
+
}
|
|
6791
|
+
)
|
|
6792
|
+
]
|
|
6793
|
+
}
|
|
6794
|
+
),
|
|
6795
|
+
trailing,
|
|
6796
|
+
showInteractionChevron && /* @__PURE__ */ jsx(SIcon, { name: "chevronRight", size: 12, color: interactionChevronColor })
|
|
6797
|
+
]
|
|
6798
|
+
}
|
|
6799
|
+
);
|
|
6661
6800
|
return /* @__PURE__ */ jsxs(
|
|
6662
6801
|
"div",
|
|
6663
6802
|
{
|
|
@@ -6680,79 +6819,13 @@ var SExpansionItem = /* @__PURE__ */ forwardRef(
|
|
|
6680
6819
|
className: "pointer-events-none absolute bottom-[-1px] left-0 top-0 z-[1] w-(--cmp-listItem-accentStripe-width) bg-(--cmp-listItem-accentStripe-color)"
|
|
6681
6820
|
}
|
|
6682
6821
|
),
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
{
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
onMouseEnter: () => setHovered(true),
|
|
6691
|
-
onMouseLeave: () => setHovered(false),
|
|
6692
|
-
onClick: (event) => {
|
|
6693
|
-
if (disabled) return;
|
|
6694
|
-
const next = !isExpanded;
|
|
6695
|
-
if (expanded === void 0) setInternalExpanded(next);
|
|
6696
|
-
onToggle?.(next, event);
|
|
6697
|
-
},
|
|
6698
|
-
className: cn(
|
|
6699
|
-
"group/expansion-item-button flex w-full cursor-pointer items-center justify-between border-0 text-left text-(--cmp-listExpansionItem-title-text-default) focus-visible:outline focus-visible:outline-2 focus-visible:outline-(--sys-color-border-accent) disabled:cursor-not-allowed disabled:text-(--sys-color-disabled-text)",
|
|
6700
|
-
titleTypo,
|
|
6701
|
-
isExpanded && "border-b border-(--cmp-listExpansionItem-title-border)",
|
|
6702
|
-
hasChevronInteraction ? "hover:bg-(--sys-color-bg-neutralBright)" : !isExpanded && "hover:bg-(--cmp-listExpansionItem-title-bg-hover)",
|
|
6703
|
-
// 높이는 세로 패딩과 내용이 정한다 — 제목 줄에 높이를 못 박지 않는다 (SListItem 과 동일)
|
|
6704
|
-
dense ? "py-(--cmp-listExpansionItem-title-paddingY-dense)" : "py-(--cmp-listExpansionItem-title-paddingY-normal)",
|
|
6705
|
-
"gap-(--cmp-listExpansionItem-title-gap)",
|
|
6706
|
-
"pr-(--cmp-listExpansionItem-title-paddingX)",
|
|
6707
|
-
depth === 2 ? "pl-[36px]" : depth === 3 ? "pl-[56px]" : "pl-[16px]",
|
|
6708
|
-
isExpanded && cn(
|
|
6709
|
-
"text-(--cmp-listExpansionItem-title-text-expanded)",
|
|
6710
|
-
// 펼침 배경(파랑)은 bordered 일 때만. 카드처럼 테두리로 경계가 잡힌
|
|
6711
|
-
// 아이템에서만 배경이 선택 상태를 나타낸다.
|
|
6712
|
-
// 테두리 없는 리스트에서는 accentStripe 가 그 역할을 하므로,
|
|
6713
|
-
// 배경까지 깔면 항목 경계가 없는 목록에서 선택 표시가 두 겹이 된다.
|
|
6714
|
-
isSelected && bordered && "bg-(--cmp-listExpansionItem-title-bg-expanded)"
|
|
6715
|
-
)
|
|
6716
|
-
),
|
|
6717
|
-
children: [
|
|
6718
|
-
!leading && /* @__PURE__ */ jsx(
|
|
6719
|
-
"span",
|
|
6720
|
-
{
|
|
6721
|
-
className: cn(
|
|
6722
|
-
"flex transition-transform duration-200 ease-out motion-reduce:transition-none",
|
|
6723
|
-
isExpanded && "rotate-180"
|
|
6724
|
-
),
|
|
6725
|
-
children: /* @__PURE__ */ jsx(SIcon, { name: "caretDown", size: 12, color: "var(--sys-color-fg-quaternary)" })
|
|
6726
|
-
}
|
|
6727
|
-
),
|
|
6728
|
-
leadingNode,
|
|
6729
|
-
/* @__PURE__ */ jsxs(
|
|
6730
|
-
"span",
|
|
6731
|
-
{
|
|
6732
|
-
className: cn(
|
|
6733
|
-
"min-w-0 flex-1",
|
|
6734
|
-
supportingTextPosition === "bottom" ? "flex flex-col" : "flex items-center gap-[8px] whitespace-nowrap"
|
|
6735
|
-
),
|
|
6736
|
-
children: [
|
|
6737
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: title }),
|
|
6738
|
-
hasSupportingText && /* @__PURE__ */ jsx(
|
|
6739
|
-
"span",
|
|
6740
|
-
{
|
|
6741
|
-
className: cn(
|
|
6742
|
-
"typo-body-sm-default min-w-0 truncate text-(--sys-color-fg-secondary)",
|
|
6743
|
-
supportingTextPosition === "right" && "flex-1"
|
|
6744
|
-
),
|
|
6745
|
-
children: supportingText
|
|
6746
|
-
}
|
|
6747
|
-
)
|
|
6748
|
-
]
|
|
6749
|
-
}
|
|
6750
|
-
),
|
|
6751
|
-
trailing,
|
|
6752
|
-
showInteractionChevron && /* @__PURE__ */ jsx(SIcon, { name: "chevronRight", size: 12, color: interactionChevronColor })
|
|
6753
|
-
]
|
|
6754
|
-
}
|
|
6755
|
-
),
|
|
6822
|
+
hasHeaderAction ? (
|
|
6823
|
+
// 헤더 액션은 여닫는 버튼의 형제다 — 안에 넣으면 버튼이 중첩되고 누를 때마다 여닫힌다
|
|
6824
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center", surfaceClass), ...hoverHandlers, children: [
|
|
6825
|
+
toggleButton,
|
|
6826
|
+
/* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-(--cmp-listExpansionItem-title-gap) pr-(--cmp-listExpansionItem-title-paddingX)", children: headerAction })
|
|
6827
|
+
] })
|
|
6828
|
+
) : toggleButton,
|
|
6756
6829
|
/* @__PURE__ */ jsx(SExpansionItemDescendantContext.Provider, { value: descendantContextValue, children: /* @__PURE__ */ jsx(
|
|
6757
6830
|
"div",
|
|
6758
6831
|
{
|
|
@@ -13321,9 +13394,20 @@ var SystemButton = /* @__PURE__ */ forwardRef(function SystemButton2({
|
|
|
13321
13394
|
}
|
|
13322
13395
|
),
|
|
13323
13396
|
label && /* @__PURE__ */ jsx("span", { className: "typo-body-sm-semibold min-w-0 truncate", children: label }),
|
|
13324
|
-
count != null && count > 0 && //
|
|
13397
|
+
count != null && count > 0 && // 개수는 채운 배지로 단다 — 배경·글자색·치수는 xs deep 태그 토큰이 쥔다(시안).
|
|
13398
|
+
// 배지 색은 고정이다: 흰 면은 파랑, 어두운 면(dark·darker)은 노랑 바탕에 검은 글자.
|
|
13399
|
+
// 배지 오른쪽은 버튼 여백(alertButton.paddingX)만으로는 좁다 — 시안이 4px 을 더 준다.
|
|
13325
13400
|
// 대응 토큰이 없어 디자인 값을 그대로 쓴다.
|
|
13326
|
-
/* @__PURE__ */ jsx(
|
|
13401
|
+
/* @__PURE__ */ jsx(
|
|
13402
|
+
STag,
|
|
13403
|
+
{
|
|
13404
|
+
size: "xs",
|
|
13405
|
+
deep: true,
|
|
13406
|
+
color: color === "light" ? "blue" : "yellow",
|
|
13407
|
+
label: count.toLocaleString(),
|
|
13408
|
+
className: "me-[4px] shrink-0"
|
|
13409
|
+
}
|
|
13410
|
+
)
|
|
13327
13411
|
]
|
|
13328
13412
|
}
|
|
13329
13413
|
);
|
|
@@ -14202,7 +14286,7 @@ var SStepper = /* @__PURE__ */ forwardRef(function SStepper2({
|
|
|
14202
14286
|
items.findIndex((item4, index) => getItemValue(item4, index) === value)
|
|
14203
14287
|
);
|
|
14204
14288
|
const lastCompletedIndex = items.reduce(
|
|
14205
|
-
(
|
|
14289
|
+
(lastIndex2, item4, index) => item4.completed === true ? index : lastIndex2,
|
|
14206
14290
|
-1
|
|
14207
14291
|
);
|
|
14208
14292
|
const resolvedClickableIndex = lastCompletedIndex + 1;
|
|
@@ -14644,12 +14728,25 @@ function monthCells(year, month) {
|
|
|
14644
14728
|
return cells;
|
|
14645
14729
|
}
|
|
14646
14730
|
var WEEK_LABELS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
|
|
14731
|
+
var WEEKDAY_SUFFIX_EM = 0.25 + 3 * 1;
|
|
14732
|
+
var weekdaySuffixWidth = (fontSize) => Math.ceil(fontSize * WEEKDAY_SUFFIX_EM);
|
|
14733
|
+
function withWeekday(value) {
|
|
14734
|
+
const [date = "", ...rest] = value.split(" ");
|
|
14735
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
|
14736
|
+
if (!match) return value;
|
|
14737
|
+
const day = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])).getDay();
|
|
14738
|
+
if (Number.isNaN(day)) return value;
|
|
14739
|
+
return [date, `${WEEK_LABELS[day]}\uC694\uC77C`, ...rest].join(" ");
|
|
14740
|
+
}
|
|
14647
14741
|
|
|
14648
14742
|
// src/components/SCalendarBoard/calendar-board-layout.ts
|
|
14649
14743
|
function eventEndDate(event) {
|
|
14650
14744
|
const end = event.endDate;
|
|
14651
14745
|
return end != null && end > event.date ? end : event.date;
|
|
14652
14746
|
}
|
|
14747
|
+
function compareDate(a, b) {
|
|
14748
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
14749
|
+
}
|
|
14653
14750
|
function layoutCalendarBoard({
|
|
14654
14751
|
cells,
|
|
14655
14752
|
events,
|
|
@@ -14695,7 +14792,7 @@ function layoutCalendarBoard({
|
|
|
14695
14792
|
flush(to);
|
|
14696
14793
|
});
|
|
14697
14794
|
runs.sort(
|
|
14698
|
-
(a, b) => a.startIndex - b.startIndex ||
|
|
14795
|
+
(a, b) => a.startIndex - b.startIndex || compareDate(a.event.date, b.event.date) || compareDate(eventEndDate(b.event), eventEndDate(a.event)) || a.order - b.order
|
|
14699
14796
|
);
|
|
14700
14797
|
const weekLanes = Array.from({ length: weekCount }, () => []);
|
|
14701
14798
|
for (const run of runs) {
|
|
@@ -17968,7 +18065,8 @@ var STableBar = /* @__PURE__ */ forwardRef(function STableBar2({ title, total, s
|
|
|
17968
18065
|
);
|
|
17969
18066
|
});
|
|
17970
18067
|
var NO_KEYS = /* @__PURE__ */ new Set();
|
|
17971
|
-
var
|
|
18068
|
+
var EXPAND_MOTION = { duration: 220, easing: "ease-out" };
|
|
18069
|
+
var COLLAPSE_MOTION = { duration: 140, easing: "ease-in" };
|
|
17972
18070
|
function STreeTable({
|
|
17973
18071
|
rows = [],
|
|
17974
18072
|
columns = [],
|
|
@@ -18015,32 +18113,88 @@ function STreeTable({
|
|
|
18015
18113
|
if (expandedKeys === void 0) setInnerExpandedKeys(next);
|
|
18016
18114
|
onExpandedChange?.(next, row);
|
|
18017
18115
|
};
|
|
18018
|
-
const visibleRows = useMemo(
|
|
18019
|
-
|
|
18020
|
-
|
|
18021
|
-
|
|
18022
|
-
|
|
18023
|
-
|
|
18024
|
-
|
|
18025
|
-
|
|
18026
|
-
|
|
18027
|
-
|
|
18028
|
-
|
|
18029
|
-
|
|
18116
|
+
const visibleRows = useMemo(
|
|
18117
|
+
() => flattenRows(rows, (row) => expandedSet.has(keyOf(row)), childrenOf),
|
|
18118
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18119
|
+
[rows, expandedSet, rowKey, childrenKey]
|
|
18120
|
+
);
|
|
18121
|
+
const tableRef = useRef(null);
|
|
18122
|
+
const [closingKeys, setClosingKeys] = useState(NO_KEYS);
|
|
18123
|
+
const [prevExpand, setPrevExpand] = useState({ rows, expandedSet });
|
|
18124
|
+
if (prevExpand.rows !== rows || prevExpand.expandedSet !== expandedSet) {
|
|
18125
|
+
setPrevExpand({ rows, expandedSet });
|
|
18126
|
+
let nextClosing = NO_KEYS;
|
|
18127
|
+
if (prevExpand.rows === rows && motionAllowed()) {
|
|
18128
|
+
const collapsed = [...prevExpand.expandedSet].filter((key) => !expandedSet.has(key));
|
|
18129
|
+
const expanded = [...expandedSet].filter((key) => !prevExpand.expandedSet.has(key));
|
|
18130
|
+
if (collapsed.length > 0 && expanded.length === 0) nextClosing = new Set(collapsed);
|
|
18131
|
+
}
|
|
18132
|
+
if (nextClosing !== closingKeys) setClosingKeys(nextClosing);
|
|
18133
|
+
}
|
|
18134
|
+
const renderedRows = useMemo(
|
|
18135
|
+
() => closingKeys.size === 0 ? visibleRows : flattenRows(
|
|
18136
|
+
rows,
|
|
18137
|
+
(row) => expandedSet.has(keyOf(row)) || closingKeys.has(keyOf(row)),
|
|
18138
|
+
childrenOf
|
|
18139
|
+
),
|
|
18140
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18141
|
+
[visibleRows, closingKeys]
|
|
18142
|
+
);
|
|
18143
|
+
const visibleKeys = useMemo(
|
|
18144
|
+
() => new Set(visibleRows.map(keyOf)),
|
|
18145
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18146
|
+
[visibleRows]
|
|
18147
|
+
);
|
|
18148
|
+
const motionRef = useRef(null);
|
|
18030
18149
|
const prevVisibleRef = useRef(null);
|
|
18031
18150
|
useIsomorphicLayoutEffect(() => {
|
|
18032
|
-
const keys = new Set(visibleRows.map(keyOf));
|
|
18033
18151
|
const prev = prevVisibleRef.current;
|
|
18034
|
-
prevVisibleRef.current = { rows, keys };
|
|
18035
|
-
|
|
18036
|
-
|
|
18037
|
-
|
|
18038
|
-
|
|
18039
|
-
|
|
18040
|
-
if (
|
|
18041
|
-
|
|
18042
|
-
|
|
18043
|
-
|
|
18152
|
+
prevVisibleRef.current = { rows, keys: visibleKeys };
|
|
18153
|
+
const running = motionRef.current;
|
|
18154
|
+
if (running?.done) {
|
|
18155
|
+
running.cancel();
|
|
18156
|
+
motionRef.current = null;
|
|
18157
|
+
}
|
|
18158
|
+
if (!prev || prev.rows !== rows) {
|
|
18159
|
+
motionRef.current?.cancel();
|
|
18160
|
+
motionRef.current = null;
|
|
18161
|
+
return;
|
|
18162
|
+
}
|
|
18163
|
+
const added = new Set([...visibleKeys].filter((key) => !prev.keys.has(key)));
|
|
18164
|
+
const removed = new Set([...prev.keys].filter((key) => !visibleKeys.has(key)));
|
|
18165
|
+
if (added.size === 0 && removed.size === 0) return;
|
|
18166
|
+
let from = added.size > 0 ? 0 : 1;
|
|
18167
|
+
if (motionRef.current) {
|
|
18168
|
+
const current = motionRef.current;
|
|
18169
|
+
const reversing = current.to === 0 && sameKeys(added, current.keys) || current.to === 1 && sameKeys(removed, current.keys);
|
|
18170
|
+
if (reversing) from = current.openness();
|
|
18171
|
+
current.cancel();
|
|
18172
|
+
motionRef.current = null;
|
|
18173
|
+
}
|
|
18174
|
+
const scrollEl = tableRef.current?.getScrollElement();
|
|
18175
|
+
const keys = added.size > 0 ? added : removed;
|
|
18176
|
+
const canAnimate = scrollEl != null && motionAllowed() && (added.size === 0 || removed.size === 0);
|
|
18177
|
+
const motion = canAnimate ? startRowMotion({
|
|
18178
|
+
scrollEl,
|
|
18179
|
+
rowKeys: renderedRows.map(keyOf),
|
|
18180
|
+
blockKeys: keys,
|
|
18181
|
+
from,
|
|
18182
|
+
to: added.size > 0 ? 1 : 0
|
|
18183
|
+
}) : null;
|
|
18184
|
+
if (motion) {
|
|
18185
|
+
motionRef.current = motion;
|
|
18186
|
+
if (motion.to === 0) {
|
|
18187
|
+
void motion.finished.then((completed) => {
|
|
18188
|
+
if (!completed || motionRef.current !== motion) return;
|
|
18189
|
+
motion.done = true;
|
|
18190
|
+
setClosingKeys(NO_KEYS);
|
|
18191
|
+
});
|
|
18192
|
+
}
|
|
18193
|
+
} else if (closingKeys.size > 0) {
|
|
18194
|
+
setClosingKeys(NO_KEYS);
|
|
18195
|
+
}
|
|
18196
|
+
}, [renderedRows]);
|
|
18197
|
+
useEffect(() => () => motionRef.current?.cancel(), []);
|
|
18044
18198
|
const collectSelectable = (row) => canSelectRow(row) ? [row, ...childrenOf(row).flatMap(collectSelectable)] : [];
|
|
18045
18199
|
const selectionOf = (keys) => {
|
|
18046
18200
|
const state2 = /* @__PURE__ */ new Map();
|
|
@@ -18136,8 +18290,9 @@ function STreeTable({
|
|
|
18136
18290
|
STable2,
|
|
18137
18291
|
{
|
|
18138
18292
|
...rest,
|
|
18293
|
+
ref: tableRef,
|
|
18139
18294
|
columns: tableColumns,
|
|
18140
|
-
rows:
|
|
18295
|
+
rows: renderedRows,
|
|
18141
18296
|
rowKey,
|
|
18142
18297
|
resizable,
|
|
18143
18298
|
selectable,
|
|
@@ -18145,14 +18300,113 @@ function STreeTable({
|
|
|
18145
18300
|
isRowIndeterminate: selectionState ? (row) => !!selectionState.get(keyOf(row))?.indeterminate : void 0,
|
|
18146
18301
|
rowClassName: (row) => cn(
|
|
18147
18302
|
rowClassName?.(row),
|
|
18148
|
-
//
|
|
18149
|
-
|
|
18303
|
+
// 접히는 중인 행은 이미 접힌 행이다 — 사라지는 동안 눌리지 않게 한다
|
|
18304
|
+
!visibleKeys.has(keyOf(row)) && "pointer-events-none"
|
|
18150
18305
|
) || void 0,
|
|
18151
18306
|
selected: tableSelected,
|
|
18152
18307
|
onSelectedChange: handleSelectedChange
|
|
18153
18308
|
}
|
|
18154
18309
|
);
|
|
18155
18310
|
}
|
|
18311
|
+
function flattenRows(nodes, isOpen, childrenOf) {
|
|
18312
|
+
const out = [];
|
|
18313
|
+
const walk = (list) => {
|
|
18314
|
+
list.forEach((row) => {
|
|
18315
|
+
out.push(row);
|
|
18316
|
+
if (isOpen(row)) walk(childrenOf(row));
|
|
18317
|
+
});
|
|
18318
|
+
};
|
|
18319
|
+
walk(nodes);
|
|
18320
|
+
return out;
|
|
18321
|
+
}
|
|
18322
|
+
function sameKeys(a, b) {
|
|
18323
|
+
return a.size === b.size && [...a].every((key) => b.has(key));
|
|
18324
|
+
}
|
|
18325
|
+
function motionAllowed() {
|
|
18326
|
+
if (typeof window === "undefined" || typeof Element.prototype.animate !== "function")
|
|
18327
|
+
return false;
|
|
18328
|
+
return !window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
18329
|
+
}
|
|
18330
|
+
function startRowMotion({
|
|
18331
|
+
scrollEl,
|
|
18332
|
+
rowKeys,
|
|
18333
|
+
blockKeys,
|
|
18334
|
+
from,
|
|
18335
|
+
to
|
|
18336
|
+
}) {
|
|
18337
|
+
const spec = to === 1 ? EXPAND_MOTION : COLLAPSE_MOTION;
|
|
18338
|
+
const duration = spec.duration * Math.abs(to - from);
|
|
18339
|
+
if (duration < 1) return null;
|
|
18340
|
+
const trs = [...scrollEl.querySelectorAll("tr[data-row-index]")].map((tr) => {
|
|
18341
|
+
const key = rowKeys[Number(tr.dataset.rowIndex)];
|
|
18342
|
+
return {
|
|
18343
|
+
tr,
|
|
18344
|
+
inBlock: key !== void 0 && blockKeys.has(key),
|
|
18345
|
+
height: tr.getBoundingClientRect().height
|
|
18346
|
+
};
|
|
18347
|
+
});
|
|
18348
|
+
const viewBottom = scrollEl.scrollTop + scrollEl.clientHeight;
|
|
18349
|
+
const openAt = (offset) => from + (to - from) * offset;
|
|
18350
|
+
const translate = (shift2, s) => `translateY(${-shift2 * (1 - s)}px)`;
|
|
18351
|
+
const timing = {
|
|
18352
|
+
duration,
|
|
18353
|
+
easing: spec.easing,
|
|
18354
|
+
// 접힘은 남겨 둔 행이 빠질 때까지 끝 위치를 붙잡는다 — 놓으면 그 사이 한 프레임 제자리로 튄다
|
|
18355
|
+
fill: to === 0 ? "forwards" : "none"
|
|
18356
|
+
};
|
|
18357
|
+
const animations = [];
|
|
18358
|
+
let shift = 0;
|
|
18359
|
+
let blockHeight = 0;
|
|
18360
|
+
let y = 0;
|
|
18361
|
+
trs.forEach(({ tr, inBlock, height }, i) => {
|
|
18362
|
+
if (inBlock && !trs[i - 1]?.inBlock) {
|
|
18363
|
+
blockHeight = 0;
|
|
18364
|
+
for (let j = i; j < trs.length && trs[j].inBlock; j++) blockHeight += trs[j].height;
|
|
18365
|
+
shift += blockHeight;
|
|
18366
|
+
y = 0;
|
|
18367
|
+
}
|
|
18368
|
+
if (shift === 0 || tr.offsetTop - shift > viewBottom) return;
|
|
18369
|
+
if (!inBlock) {
|
|
18370
|
+
animations.push(
|
|
18371
|
+
tr.animate(
|
|
18372
|
+
[{ transform: translate(shift, from) }, { transform: translate(shift, to) }],
|
|
18373
|
+
timing
|
|
18374
|
+
)
|
|
18375
|
+
);
|
|
18376
|
+
return;
|
|
18377
|
+
}
|
|
18378
|
+
const rowY = y;
|
|
18379
|
+
const clipAt = (s) => Math.min(height, Math.max(0, blockHeight * (1 - s) - rowY));
|
|
18380
|
+
const bends = [1 - rowY / blockHeight, 1 - (rowY + height) / blockHeight].map((s) => (s - from) / (to - from)).filter((offset) => offset > 0 && offset < 1);
|
|
18381
|
+
const offsets = [0, ...bends, 1].sort((a, b) => a - b);
|
|
18382
|
+
animations.push(
|
|
18383
|
+
tr.animate(
|
|
18384
|
+
offsets.map((offset) => ({
|
|
18385
|
+
offset,
|
|
18386
|
+
transform: translate(shift, openAt(offset)),
|
|
18387
|
+
clipPath: `inset(${clipAt(openAt(offset))}px 0 0 0)`
|
|
18388
|
+
})),
|
|
18389
|
+
timing
|
|
18390
|
+
)
|
|
18391
|
+
);
|
|
18392
|
+
y += height;
|
|
18393
|
+
});
|
|
18394
|
+
if (animations.length === 0) return null;
|
|
18395
|
+
return {
|
|
18396
|
+
keys: blockKeys,
|
|
18397
|
+
to,
|
|
18398
|
+
done: false,
|
|
18399
|
+
finished: Promise.all(animations.map((animation) => animation.finished)).then(
|
|
18400
|
+
() => true,
|
|
18401
|
+
() => false
|
|
18402
|
+
),
|
|
18403
|
+
openness: () => {
|
|
18404
|
+
const progress = animations[0].effect?.getComputedTiming().progress;
|
|
18405
|
+
return progress == null ? to : openAt(progress);
|
|
18406
|
+
},
|
|
18407
|
+
cancel: () => animations.forEach((animation) => animation.cancel())
|
|
18408
|
+
};
|
|
18409
|
+
}
|
|
18156
18410
|
var OWN_CONTROL_SELECTOR = 'a, button, input, select, textarea, label, [role="button"], [role="checkbox"], [contenteditable="true"]';
|
|
18157
18411
|
function isOwnControl(target, cell) {
|
|
18158
18412
|
if (!(target instanceof Element)) return false;
|
|
@@ -19131,13 +19385,21 @@ function SCalendar({
|
|
|
19131
19385
|
}
|
|
19132
19386
|
);
|
|
19133
19387
|
}
|
|
19388
|
+
|
|
19389
|
+
// src/lib/mode-listbox.ts
|
|
19390
|
+
var MODE_LIST_HEIGHT = 240;
|
|
19391
|
+
var MODE_ITEM_HEIGHT = 28;
|
|
19392
|
+
var modeListItemClass = (selected, disabled) => cn(
|
|
19393
|
+
"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]",
|
|
19394
|
+
disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
|
|
19395
|
+
!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)]",
|
|
19396
|
+
!disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
|
|
19397
|
+
);
|
|
19134
19398
|
var MODE_NOUN = {
|
|
19135
19399
|
date: "\uB0A0\uC9DC",
|
|
19136
19400
|
month: "\uC6D4",
|
|
19137
19401
|
year: "\uC5F0\uB3C4"
|
|
19138
19402
|
};
|
|
19139
|
-
var DATEPICKER_MODE_LIST_HEIGHT = 240;
|
|
19140
|
-
var DATEPICKER_MODE_ITEM_HEIGHT = 28;
|
|
19141
19403
|
var YEAR_LIST_EDGE_BUFFER = 24;
|
|
19142
19404
|
var YEAR_LIST_EXPAND_SIZE = 80;
|
|
19143
19405
|
var MONTHS = Array.from({ length: 12 }, (_, i) => i + 1);
|
|
@@ -19188,7 +19450,7 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19188
19450
|
if (!list) return;
|
|
19189
19451
|
const index = anchorYearRef.current - rangeStartRef.current;
|
|
19190
19452
|
syncingScrollRef.current = true;
|
|
19191
|
-
list.scrollTop = index *
|
|
19453
|
+
list.scrollTop = index * MODE_ITEM_HEIGHT;
|
|
19192
19454
|
requestAnimationFrame(() => {
|
|
19193
19455
|
syncingScrollRef.current = false;
|
|
19194
19456
|
});
|
|
@@ -19197,7 +19459,7 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19197
19459
|
const list = listRef.current;
|
|
19198
19460
|
if (!list) return;
|
|
19199
19461
|
if (syncingScrollRef.current) return;
|
|
19200
|
-
if (list.scrollTop <
|
|
19462
|
+
if (list.scrollTop < MODE_ITEM_HEIGHT * YEAR_LIST_EDGE_BUFFER) {
|
|
19201
19463
|
const previousHeight = list.scrollHeight;
|
|
19202
19464
|
setRange((current) => ({
|
|
19203
19465
|
start: current.start - YEAR_LIST_EXPAND_SIZE,
|
|
@@ -19213,7 +19475,7 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19213
19475
|
});
|
|
19214
19476
|
return;
|
|
19215
19477
|
}
|
|
19216
|
-
if (list.scrollHeight - list.scrollTop - list.clientHeight <
|
|
19478
|
+
if (list.scrollHeight - list.scrollTop - list.clientHeight < MODE_ITEM_HEIGHT * YEAR_LIST_EDGE_BUFFER) {
|
|
19217
19479
|
setRange((current) => ({
|
|
19218
19480
|
start: current.start,
|
|
19219
19481
|
end: current.end + YEAR_LIST_EXPAND_SIZE
|
|
@@ -19228,8 +19490,8 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19228
19490
|
"aria-label": "year",
|
|
19229
19491
|
className: "h-full overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
19230
19492
|
style: {
|
|
19231
|
-
paddingTop: `calc((${
|
|
19232
|
-
paddingBottom: `calc((${
|
|
19493
|
+
paddingTop: `calc((${MODE_LIST_HEIGHT}px - ${MODE_ITEM_HEIGHT}px) / 2)`,
|
|
19494
|
+
paddingBottom: `calc((${MODE_LIST_HEIGHT}px - ${MODE_ITEM_HEIGHT}px) / 2)`
|
|
19233
19495
|
},
|
|
19234
19496
|
onScroll: handleScroll,
|
|
19235
19497
|
children: years.map((year) => {
|
|
@@ -19242,12 +19504,7 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19242
19504
|
role: "option",
|
|
19243
19505
|
"aria-selected": selected,
|
|
19244
19506
|
disabled,
|
|
19245
|
-
className:
|
|
19246
|
-
"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]",
|
|
19247
|
-
disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
|
|
19248
|
-
!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)]",
|
|
19249
|
-
!disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
|
|
19250
|
-
),
|
|
19507
|
+
className: modeListItemClass(selected, disabled),
|
|
19251
19508
|
onClick: () => !disabled && onCommit(year),
|
|
19252
19509
|
children: year
|
|
19253
19510
|
},
|
|
@@ -19303,7 +19560,7 @@ function MonthWheel({ year, value, selectable, onCommit }) {
|
|
|
19303
19560
|
if (!list) return;
|
|
19304
19561
|
list.scrollTop = Math.max(
|
|
19305
19562
|
0,
|
|
19306
|
-
(initialValueRef.current - 1) *
|
|
19563
|
+
(initialValueRef.current - 1) * MODE_ITEM_HEIGHT - (list.clientHeight - MODE_ITEM_HEIGHT) / 2
|
|
19307
19564
|
);
|
|
19308
19565
|
}, []);
|
|
19309
19566
|
return /* @__PURE__ */ jsx("div", { className: "h-full w-full overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]", children: /* @__PURE__ */ jsx(
|
|
@@ -19323,12 +19580,7 @@ function MonthWheel({ year, value, selectable, onCommit }) {
|
|
|
19323
19580
|
role: "option",
|
|
19324
19581
|
"aria-selected": selected,
|
|
19325
19582
|
disabled,
|
|
19326
|
-
className:
|
|
19327
|
-
"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]",
|
|
19328
|
-
disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
|
|
19329
|
-
!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)]",
|
|
19330
|
-
!disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
|
|
19331
|
-
),
|
|
19583
|
+
className: modeListItemClass(selected, disabled),
|
|
19332
19584
|
onClick: () => !disabled && onCommit(month),
|
|
19333
19585
|
children: [
|
|
19334
19586
|
month,
|
|
@@ -19407,6 +19659,7 @@ function SDatePicker({
|
|
|
19407
19659
|
placeholder,
|
|
19408
19660
|
selectable,
|
|
19409
19661
|
disabled = false,
|
|
19662
|
+
showWeekday = false,
|
|
19410
19663
|
clearable = false,
|
|
19411
19664
|
width,
|
|
19412
19665
|
maxWidth,
|
|
@@ -19436,6 +19689,7 @@ function SDatePicker({
|
|
|
19436
19689
|
const [focused, setFocused] = useState(false);
|
|
19437
19690
|
const [ruleError, setRuleError] = useState("");
|
|
19438
19691
|
const hasValue = value != null && value !== "";
|
|
19692
|
+
const weekday = showWeekday && mode === "date";
|
|
19439
19693
|
const resolvedPlaceholder = placeholder ?? (mode === "year" ? "YYYY" : mode === "month" ? "YYYY-MM" : "YYYY-MM-DD");
|
|
19440
19694
|
useFormField({
|
|
19441
19695
|
name,
|
|
@@ -19448,7 +19702,7 @@ function SDatePicker({
|
|
|
19448
19702
|
});
|
|
19449
19703
|
const resolvedError = error || ruleError !== "";
|
|
19450
19704
|
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
19451
|
-
const
|
|
19705
|
+
const baseDims = size === "md" ? {
|
|
19452
19706
|
padX: 16,
|
|
19453
19707
|
gap: 12,
|
|
19454
19708
|
icon: 20,
|
|
@@ -19473,6 +19727,13 @@ function SDatePicker({
|
|
|
19473
19727
|
lineHeight: 20,
|
|
19474
19728
|
radius: 4
|
|
19475
19729
|
};
|
|
19730
|
+
const weekdayW = weekday ? weekdaySuffixWidth(baseDims.fontSize) : 0;
|
|
19731
|
+
const dims = {
|
|
19732
|
+
...baseDims,
|
|
19733
|
+
minW: baseDims.minW + weekdayW,
|
|
19734
|
+
fieldMinW: baseDims.fieldMinW + weekdayW,
|
|
19735
|
+
fieldMaxW: weekdayW ? `calc(${fieldWidthValue(baseDims.fieldMaxW)} + ${weekdayW}px)` : baseDims.fieldMaxW
|
|
19736
|
+
};
|
|
19476
19737
|
const select = (date) => {
|
|
19477
19738
|
onValueChange?.(date);
|
|
19478
19739
|
if (rules && rules.length > 0) setRuleError(runRules(date, rules));
|
|
@@ -19557,7 +19818,7 @@ function SDatePicker({
|
|
|
19557
19818
|
lineHeight: `${dims.lineHeight}px`,
|
|
19558
19819
|
color: disabled ? "var(--cmp-datepicker-text-disabled)" : hasValue ? "var(--cmp-datepicker-text-default)" : "var(--cmp-field-hint-color)"
|
|
19559
19820
|
},
|
|
19560
|
-
children: hasValue ? value : resolvedPlaceholder
|
|
19821
|
+
children: hasValue ? weekday ? withWeekday(value) : value : resolvedPlaceholder
|
|
19561
19822
|
}
|
|
19562
19823
|
),
|
|
19563
19824
|
clearable && hasValue && !disabled && /* @__PURE__ */ jsx(
|
|
@@ -19951,6 +20212,7 @@ function SDateRangePicker({
|
|
|
19951
20212
|
selectable,
|
|
19952
20213
|
maxRange,
|
|
19953
20214
|
showTimePicker = false,
|
|
20215
|
+
showWeekday = false,
|
|
19954
20216
|
disabled = false,
|
|
19955
20217
|
clearable = false,
|
|
19956
20218
|
width,
|
|
@@ -19982,8 +20244,10 @@ function SDateRangePicker({
|
|
|
19982
20244
|
const [pendingStart, setPendingStart] = useState(null);
|
|
19983
20245
|
const hasValue = value != null && value[0] !== "" && value[1] !== "";
|
|
19984
20246
|
const hasDisplay = pendingStart != null || hasValue;
|
|
19985
|
-
const
|
|
19986
|
-
const
|
|
20247
|
+
const rawStart = pendingStart ?? (hasValue ? value[0] : "");
|
|
20248
|
+
const rawEnd = pendingStart ? "" : hasValue ? value[1] : "";
|
|
20249
|
+
const startText = showWeekday && rawStart ? withWeekday(rawStart) : rawStart;
|
|
20250
|
+
const endText = showWeekday && rawEnd ? withWeekday(rawEnd) : rawEnd;
|
|
19987
20251
|
useFormField({
|
|
19988
20252
|
name,
|
|
19989
20253
|
validate: () => {
|
|
@@ -19995,7 +20259,7 @@ function SDateRangePicker({
|
|
|
19995
20259
|
});
|
|
19996
20260
|
const resolvedError = error || ruleError !== "";
|
|
19997
20261
|
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
19998
|
-
const
|
|
20262
|
+
const baseDims = size === "md" ? {
|
|
19999
20263
|
padX: 16,
|
|
20000
20264
|
gap: 12,
|
|
20001
20265
|
icon: 20,
|
|
@@ -20019,6 +20283,13 @@ function SDateRangePicker({
|
|
|
20019
20283
|
lineHeight: 20,
|
|
20020
20284
|
radius: 4
|
|
20021
20285
|
};
|
|
20286
|
+
const weekdayW = showWeekday ? weekdaySuffixWidth(baseDims.fontSize) * 2 : 0;
|
|
20287
|
+
const dims = {
|
|
20288
|
+
...baseDims,
|
|
20289
|
+
minW: baseDims.minW + weekdayW,
|
|
20290
|
+
fieldMinW: baseDims.fieldMinW + weekdayW,
|
|
20291
|
+
fieldMaxW: weekdayW ? `calc(${fieldWidthValue(baseDims.fieldMaxW)} + ${weekdayW}px)` : baseDims.fieldMaxW
|
|
20292
|
+
};
|
|
20022
20293
|
const select = (r3) => {
|
|
20023
20294
|
setPendingStart(null);
|
|
20024
20295
|
onValueChange?.(r3);
|
|
@@ -26020,7 +26291,7 @@ var isAtResetTarget = (field, value) => {
|
|
|
26020
26291
|
var isInlineField = (field) => isInlineDateField(field) || field.type === "custom";
|
|
26021
26292
|
var isFixedField = (field) => field.fixed === true || field.required === true;
|
|
26022
26293
|
var isAddableField = (field) => !isFixedField(field);
|
|
26023
|
-
var
|
|
26294
|
+
var sameKeys2 = (a, b) => a.length === b.length && a.every((key, index) => key === b[index]);
|
|
26024
26295
|
var isGroupSatisfied = (group, values) => {
|
|
26025
26296
|
if (!group.rule) return true;
|
|
26026
26297
|
const keys = group.fields.map((f) => f.key);
|
|
@@ -26203,7 +26474,7 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
|
|
|
26203
26474
|
const kept = current.filter((key) => known.has(key));
|
|
26204
26475
|
const additions = fields.filter((field) => isFixedField(field) && !kept.includes(field.key)).map((field) => field.key);
|
|
26205
26476
|
const next = [...kept, ...additions];
|
|
26206
|
-
return
|
|
26477
|
+
return sameKeys2(current, next) ? current : next;
|
|
26207
26478
|
});
|
|
26208
26479
|
}, [fields, values]);
|
|
26209
26480
|
useEffect(() => {
|
|
@@ -28396,212 +28667,171 @@ function formatTimeDisplay(value, type) {
|
|
|
28396
28667
|
const displayHour = hour % 12 === 0 ? 12 : hour % 12;
|
|
28397
28668
|
return `${meridiem} ${padTimePart(displayHour)}:${padTimePart(minute)}`;
|
|
28398
28669
|
}
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28670
|
+
function scrollSelectedIntoView(list, index, center) {
|
|
28671
|
+
const top = index * MODE_ITEM_HEIGHT;
|
|
28672
|
+
if (center) {
|
|
28673
|
+
list.scrollTop = Math.max(0, top - (list.clientHeight - MODE_ITEM_HEIGHT) / 2);
|
|
28674
|
+
return;
|
|
28675
|
+
}
|
|
28676
|
+
if (top < list.scrollTop) list.scrollTop = top;
|
|
28677
|
+
else if (top + MODE_ITEM_HEIGHT > list.scrollTop + list.clientHeight)
|
|
28678
|
+
list.scrollTop = top + MODE_ITEM_HEIGHT - list.clientHeight;
|
|
28679
|
+
}
|
|
28680
|
+
function lastIndex(arr, pred) {
|
|
28681
|
+
for (let i = arr.length - 1; i >= 0; i--) if (pred(arr[i], i)) return i;
|
|
28682
|
+
return -1;
|
|
28683
|
+
}
|
|
28684
|
+
function ListColumn({ ariaLabel, items }) {
|
|
28685
|
+
const listRef = useRef(null);
|
|
28686
|
+
const mountedRef = useRef(false);
|
|
28687
|
+
const selectedIndex = items.findIndex((item4) => item4.selected);
|
|
28688
|
+
const tabIndexTarget = selectedIndex >= 0 ? selectedIndex : items.findIndex((item4) => !item4.disabled);
|
|
28689
|
+
useLayoutEffect(() => {
|
|
28690
|
+
const list = listRef.current;
|
|
28691
|
+
if (!list || selectedIndex < 0) return;
|
|
28692
|
+
scrollSelectedIntoView(list, selectedIndex, !mountedRef.current);
|
|
28693
|
+
mountedRef.current = true;
|
|
28694
|
+
}, [selectedIndex]);
|
|
28695
|
+
const onKeyDown = (event) => {
|
|
28696
|
+
const buttons = [...listRef.current?.querySelectorAll("button") ?? []];
|
|
28697
|
+
const current = buttons.indexOf(document.activeElement);
|
|
28698
|
+
if (current < 0) return;
|
|
28699
|
+
const enabled = (item4, i) => !item4.disabled && i !== current;
|
|
28700
|
+
let next;
|
|
28701
|
+
if (event.key === "ArrowDown") next = items.findIndex((it, i) => i > current && enabled(it, i));
|
|
28702
|
+
else if (event.key === "ArrowUp")
|
|
28703
|
+
next = lastIndex(items, (it, i) => i < current && enabled(it, i));
|
|
28704
|
+
else if (event.key === "Home") next = items.findIndex((it) => !it.disabled);
|
|
28705
|
+
else if (event.key === "End") next = lastIndex(items, (it) => !it.disabled);
|
|
28706
|
+
else return;
|
|
28707
|
+
event.preventDefault();
|
|
28708
|
+
if (next < 0) return;
|
|
28709
|
+
buttons[next]?.focus({ preventScroll: true });
|
|
28710
|
+
if (listRef.current) scrollSelectedIntoView(listRef.current, next, false);
|
|
28711
|
+
};
|
|
28712
|
+
return /* @__PURE__ */ jsx("div", { className: "h-full w-[var(--cmp-datepicker-mode-yearMonth-column-width)] min-w-0 overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]", children: /* @__PURE__ */ jsx(
|
|
28713
|
+
"div",
|
|
28714
|
+
{
|
|
28715
|
+
ref: listRef,
|
|
28716
|
+
role: "listbox",
|
|
28717
|
+
"aria-label": ariaLabel,
|
|
28718
|
+
tabIndex: -1,
|
|
28719
|
+
className: "h-full overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
28720
|
+
onKeyDown,
|
|
28721
|
+
children: items.map((item4, index) => /* @__PURE__ */ jsx(
|
|
28722
|
+
"button",
|
|
28723
|
+
{
|
|
28724
|
+
type: "button",
|
|
28725
|
+
role: "option",
|
|
28726
|
+
"aria-selected": item4.selected,
|
|
28727
|
+
disabled: item4.disabled,
|
|
28728
|
+
tabIndex: index === tabIndexTarget ? 0 : -1,
|
|
28729
|
+
className: cn(modeListItemClass(item4.selected, item4.disabled), "outline-offset-[-2px]"),
|
|
28730
|
+
onClick: () => !item4.disabled && item4.onSelect(),
|
|
28731
|
+
children: item4.label
|
|
28732
|
+
},
|
|
28733
|
+
item4.key
|
|
28734
|
+
))
|
|
28735
|
+
}
|
|
28736
|
+
) });
|
|
28737
|
+
}
|
|
28738
|
+
var HOURS_24 = Array.from({ length: 24 }, (_, i) => i);
|
|
28739
|
+
var HOURS_12 = [12, ...Array.from({ length: 11 }, (_, i) => i + 1)];
|
|
28740
|
+
function TimeListbox({
|
|
28402
28741
|
value,
|
|
28403
28742
|
showMeridiem,
|
|
28404
28743
|
minuteStep,
|
|
28405
|
-
|
|
28744
|
+
isDisabled,
|
|
28745
|
+
onChange,
|
|
28746
|
+
labelPrefix,
|
|
28747
|
+
elevated = true
|
|
28406
28748
|
}) {
|
|
28407
|
-
const
|
|
28408
|
-
const [
|
|
28409
|
-
const [minute, setMinute] = useState(parsed.minute);
|
|
28410
|
-
const [editingPart, setEditingPart] = useState(null);
|
|
28411
|
-
const [inputDraft, setInputDraft] = useState({});
|
|
28749
|
+
const toState = (v) => v ? parseTimeValue(v) : null;
|
|
28750
|
+
const [time, setTime] = useState(() => toState(value));
|
|
28412
28751
|
useEffect(() => {
|
|
28413
|
-
|
|
28414
|
-
|
|
28415
|
-
|
|
28416
|
-
const
|
|
28417
|
-
const
|
|
28418
|
-
const
|
|
28419
|
-
|
|
28420
|
-
|
|
28421
|
-
|
|
28422
|
-
|
|
28423
|
-
|
|
28424
|
-
|
|
28425
|
-
|
|
28426
|
-
if (next
|
|
28427
|
-
|
|
28428
|
-
|
|
28429
|
-
const changeMinute = (delta) => {
|
|
28430
|
-
const next = minute + delta;
|
|
28431
|
-
if (next > 59) {
|
|
28432
|
-
commit((hour + 1) % 24, next - 60);
|
|
28433
|
-
return;
|
|
28434
|
-
}
|
|
28435
|
-
if (next < 0) {
|
|
28436
|
-
commit((hour + 23) % 24, next + 60);
|
|
28437
|
-
return;
|
|
28438
|
-
}
|
|
28439
|
-
commit(hour, next);
|
|
28440
|
-
};
|
|
28441
|
-
const changeHour = (delta) => commit((hour + delta + 24) % 24, minute);
|
|
28442
|
-
const sanitizeInput = (next) => next.replace(/\D/g, "").slice(0, 2);
|
|
28443
|
-
const beginEdit = (part, currentValue) => {
|
|
28444
|
-
setEditingPart(part);
|
|
28445
|
-
setInputDraft((prev) => ({ ...prev, [part]: String(currentValue).padStart(2, "0") }));
|
|
28446
|
-
};
|
|
28447
|
-
const commitHourDraft = (draft) => {
|
|
28448
|
-
const clamped = clampTimePart(Number(draft), showMeridiem ? 1 : 0, showMeridiem ? 12 : 23);
|
|
28449
|
-
const nextHour = showMeridiem ? meridiem === "PM" ? clamped === 12 ? 12 : clamped + 12 : clamped === 12 ? 0 : clamped : clamped;
|
|
28450
|
-
commit(nextHour, minute);
|
|
28451
|
-
return clamped;
|
|
28452
|
-
};
|
|
28453
|
-
const commitMinuteDraft = (draft) => {
|
|
28454
|
-
const clamped = clampTimePart(Number(draft), 0, 59);
|
|
28455
|
-
commit(hour, clamped);
|
|
28456
|
-
return clamped;
|
|
28457
|
-
};
|
|
28458
|
-
const endEdit = (part) => {
|
|
28459
|
-
const draft = inputDraft[part];
|
|
28460
|
-
if (part === "hour" && draft != null && draft !== "") {
|
|
28461
|
-
commitHourDraft(draft);
|
|
28462
|
-
}
|
|
28463
|
-
if (part === "minute" && draft != null && draft !== "") {
|
|
28464
|
-
commitMinuteDraft(draft);
|
|
28465
|
-
}
|
|
28466
|
-
setEditingPart(null);
|
|
28467
|
-
setInputDraft({});
|
|
28468
|
-
};
|
|
28469
|
-
const inputStyle = {
|
|
28470
|
-
width: "var(--cmp-timepicker-selector-time-input-width)",
|
|
28471
|
-
height: "var(--cmp-textinput-sm-height)",
|
|
28472
|
-
borderRadius: "var(--cmp-textinput-sm-radius)",
|
|
28473
|
-
border: "var(--cmp-textinput-borderWidth) solid var(--cmp-textinput-border-default)",
|
|
28474
|
-
color: "var(--cmp-textinput-text-default)",
|
|
28475
|
-
fontSize: 12,
|
|
28476
|
-
lineHeight: "20px"
|
|
28752
|
+
setTime(toState(value));
|
|
28753
|
+
}, [value]);
|
|
28754
|
+
const step = Math.max(1, Math.floor(minuteStep));
|
|
28755
|
+
const minutes = Array.from({ length: Math.ceil(60 / step) }, (_, i) => i * step);
|
|
28756
|
+
const blocked = (h, m) => isDisabled?.(h * 60 + m) ?? false;
|
|
28757
|
+
const hourBlocked = (h) => minutes.every((m) => blocked(h, m));
|
|
28758
|
+
const commit = (hours, preferred, part) => {
|
|
28759
|
+
let next = blocked(preferred.hour, preferred.minute) ? null : preferred;
|
|
28760
|
+
for (const h of hours) {
|
|
28761
|
+
if (next) break;
|
|
28762
|
+
const m = minutes.find((mm) => !blocked(h, mm));
|
|
28763
|
+
if (m !== void 0) next = { hour: h, minute: m };
|
|
28764
|
+
}
|
|
28765
|
+
if (!next) return;
|
|
28766
|
+
setTime(next);
|
|
28767
|
+
onChange(formatTimeValue(next.hour, next.minute), part);
|
|
28477
28768
|
};
|
|
28478
|
-
const
|
|
28479
|
-
|
|
28480
|
-
|
|
28481
|
-
|
|
28482
|
-
|
|
28483
|
-
|
|
28484
|
-
|
|
28485
|
-
|
|
28486
|
-
|
|
28487
|
-
|
|
28488
|
-
|
|
28489
|
-
|
|
28490
|
-
|
|
28491
|
-
|
|
28492
|
-
|
|
28493
|
-
|
|
28494
|
-
onClick: () => isHour ? changeHour(1) : changeMinute(minuteStep)
|
|
28495
|
-
}
|
|
28496
|
-
),
|
|
28497
|
-
/* @__PURE__ */ jsx(
|
|
28498
|
-
"input",
|
|
28499
|
-
{
|
|
28500
|
-
"aria-label": isHour ? "\uC2DC" : "\uBD84",
|
|
28501
|
-
inputMode: "numeric",
|
|
28502
|
-
value: displayValue2,
|
|
28503
|
-
placeholder: "-",
|
|
28504
|
-
onFocus: () => beginEdit(type, val),
|
|
28505
|
-
onBlur: () => endEdit(type),
|
|
28506
|
-
onChange: (event) => {
|
|
28507
|
-
const draft = sanitizeInput(event.target.value);
|
|
28508
|
-
setInputDraft((prev) => ({ ...prev, [type]: draft }));
|
|
28509
|
-
if (draft === "") return;
|
|
28510
|
-
const raw = Number(draft);
|
|
28511
|
-
if (isHour) {
|
|
28512
|
-
if (draft.length < 2) return;
|
|
28513
|
-
const clamped2 = commitHourDraft(draft);
|
|
28514
|
-
const displayDraft2 = raw === clamped2 ? draft : String(clamped2);
|
|
28515
|
-
setInputDraft((prev) => ({ ...prev, [type]: displayDraft2 }));
|
|
28516
|
-
return;
|
|
28517
|
-
}
|
|
28518
|
-
if (draft.length < 2) return;
|
|
28519
|
-
const clamped = commitMinuteDraft(draft);
|
|
28520
|
-
const displayDraft = raw === clamped ? draft : String(clamped);
|
|
28521
|
-
setInputDraft((prev) => ({ ...prev, [type]: displayDraft }));
|
|
28522
|
-
},
|
|
28523
|
-
className: "box-border bg-white text-center outline-none",
|
|
28524
|
-
style: inputStyle
|
|
28525
|
-
}
|
|
28526
|
-
),
|
|
28527
|
-
/* @__PURE__ */ jsx(
|
|
28528
|
-
SGhostButton,
|
|
28529
|
-
{
|
|
28530
|
-
icon: "chevronDown",
|
|
28531
|
-
size: "xs",
|
|
28532
|
-
ariaLabel: `${isHour ? "\uC2DC" : "\uBD84"} \uAC10\uC18C`,
|
|
28533
|
-
onClick: () => isHour ? changeHour(-1) : changeMinute(-minuteStep)
|
|
28534
|
-
}
|
|
28535
|
-
)
|
|
28536
|
-
]
|
|
28769
|
+
const hour = time?.hour;
|
|
28770
|
+
const minute = time?.minute ?? 0;
|
|
28771
|
+
const meridiem = hour === void 0 ? void 0 : hour >= 12 ? "PM" : "AM";
|
|
28772
|
+
const prefix = labelPrefix ? `${labelPrefix} ` : "";
|
|
28773
|
+
const meridiemItems = ["AM", "PM"].map((item4) => {
|
|
28774
|
+
const base = item4 === "AM" ? 0 : 12;
|
|
28775
|
+
const range = HOURS_24.slice(base, base + 12);
|
|
28776
|
+
return {
|
|
28777
|
+
key: item4,
|
|
28778
|
+
label: item4 === "AM" ? "\uC624\uC804" : "\uC624\uD6C4",
|
|
28779
|
+
selected: meridiem === item4,
|
|
28780
|
+
disabled: range.every(hourBlocked),
|
|
28781
|
+
onSelect: () => {
|
|
28782
|
+
if (meridiem === item4) return;
|
|
28783
|
+
const h = hour === void 0 ? base : hour % 12 + base;
|
|
28784
|
+
commit(range, { hour: h, minute }, "meridiem");
|
|
28537
28785
|
}
|
|
28538
|
-
|
|
28539
|
-
};
|
|
28786
|
+
};
|
|
28787
|
+
});
|
|
28788
|
+
const hourItems = showMeridiem ? HOURS_12.map((display) => {
|
|
28789
|
+
const h = display % 12 + (meridiem === "PM" ? 12 : 0);
|
|
28790
|
+
return {
|
|
28791
|
+
key: String(display),
|
|
28792
|
+
label: `${padTimePart(display)}\uC2DC`,
|
|
28793
|
+
selected: hour === h,
|
|
28794
|
+
disabled: hourBlocked(h),
|
|
28795
|
+
onSelect: () => commit([h], { hour: h, minute }, "hour")
|
|
28796
|
+
};
|
|
28797
|
+
}) : HOURS_24.map((h) => ({
|
|
28798
|
+
key: String(h),
|
|
28799
|
+
label: `${padTimePart(h)}\uC2DC`,
|
|
28800
|
+
selected: hour === h,
|
|
28801
|
+
disabled: hourBlocked(h),
|
|
28802
|
+
onSelect: () => commit([h], { hour: h, minute }, "hour")
|
|
28803
|
+
}));
|
|
28804
|
+
const minuteHour = hour ?? (meridiem === "PM" ? 12 : 0);
|
|
28805
|
+
const minuteItems = minutes.map((m) => ({
|
|
28806
|
+
key: String(m),
|
|
28807
|
+
label: `${padTimePart(m)}\uBD84`,
|
|
28808
|
+
selected: time != null && minute === m,
|
|
28809
|
+
disabled: blocked(minuteHour, m),
|
|
28810
|
+
onSelect: () => commit([minuteHour], { hour: minuteHour, minute: m }, "minute")
|
|
28811
|
+
}));
|
|
28812
|
+
const divider = /* @__PURE__ */ jsx(
|
|
28813
|
+
"div",
|
|
28814
|
+
{
|
|
28815
|
+
"aria-hidden": true,
|
|
28816
|
+
className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
|
|
28817
|
+
}
|
|
28818
|
+
);
|
|
28540
28819
|
return /* @__PURE__ */ jsxs(
|
|
28541
28820
|
"div",
|
|
28542
28821
|
{
|
|
28543
|
-
className:
|
|
28544
|
-
|
|
28545
|
-
|
|
28546
|
-
|
|
28547
|
-
|
|
28548
|
-
},
|
|
28822
|
+
className: cn(
|
|
28823
|
+
"inline-flex gap-[var(--cmp-datepicker-mode-yearMonth-gap)] overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]",
|
|
28824
|
+
elevated && "rounded-[var(--cmp-datepicker-mode-radius)] shadow-normal-sm"
|
|
28825
|
+
),
|
|
28826
|
+
style: { height: MODE_LIST_HEIGHT },
|
|
28549
28827
|
children: [
|
|
28550
|
-
showMeridiem && /* @__PURE__ */
|
|
28551
|
-
|
|
28552
|
-
|
|
28553
|
-
|
|
28554
|
-
|
|
28555
|
-
|
|
28556
|
-
|
|
28557
|
-
children: /* @__PURE__ */ jsx("div", { className: "isolate flex", children: ["AM", "PM"].map((item4, index) => {
|
|
28558
|
-
const selected = item4 === meridiem;
|
|
28559
|
-
return /* @__PURE__ */ jsx(
|
|
28560
|
-
"button",
|
|
28561
|
-
{
|
|
28562
|
-
type: "button",
|
|
28563
|
-
onClick: () => setMeridiem(item4),
|
|
28564
|
-
className: cn(
|
|
28565
|
-
"box-border h-[var(--cmp-textinput-sm-height)] w-[61px] cursor-pointer border border-solid bg-white text-center text-[12px] font-medium leading-[20px]",
|
|
28566
|
-
index === 0 ? "rounded-l-[var(--cmp-select-sm-radius)]" : "rounded-r-[var(--cmp-select-sm-radius)]",
|
|
28567
|
-
index === 0 ? "-mr-px" : "",
|
|
28568
|
-
selected ? "z-[1] border-[var(--cmp-timepicker-border-focus)] text-[var(--cmp-timepicker-border-focus)]" : "border-[var(--cmp-timepicker-border-default)] text-[var(--cmp-field-hint-color)]"
|
|
28569
|
-
),
|
|
28570
|
-
children: item4 === "AM" ? "\uC624\uC804" : "\uC624\uD6C4"
|
|
28571
|
-
},
|
|
28572
|
-
item4
|
|
28573
|
-
);
|
|
28574
|
-
}) })
|
|
28575
|
-
}
|
|
28576
|
-
),
|
|
28577
|
-
showMeridiem && /* @__PURE__ */ jsx("div", { className: "h-px w-full bg-[var(--sys-color-divider-default)]" }),
|
|
28578
|
-
/* @__PURE__ */ jsxs(
|
|
28579
|
-
"div",
|
|
28580
|
-
{
|
|
28581
|
-
className: "flex items-center justify-center",
|
|
28582
|
-
style: {
|
|
28583
|
-
gap: "var(--cmp-timepicker-selector-time-gap)",
|
|
28584
|
-
padding: "var(--cmp-timepicker-selector-section-paddingY) var(--cmp-timepicker-selector-section-paddingX)"
|
|
28585
|
-
},
|
|
28586
|
-
children: [
|
|
28587
|
-
renderColumn("hour"),
|
|
28588
|
-
/* @__PURE__ */ jsx(
|
|
28589
|
-
"span",
|
|
28590
|
-
{
|
|
28591
|
-
className: "text-center",
|
|
28592
|
-
style: {
|
|
28593
|
-
width: "var(--cmp-timepicker-selector-colon-size)",
|
|
28594
|
-
color: "var(--cmp-field-hint-color)",
|
|
28595
|
-
fontSize: "var(--cmp-timepicker-selector-colon-size)",
|
|
28596
|
-
lineHeight: "var(--cmp-timepicker-selector-colon-size)"
|
|
28597
|
-
},
|
|
28598
|
-
children: ":"
|
|
28599
|
-
}
|
|
28600
|
-
),
|
|
28601
|
-
renderColumn("minute")
|
|
28602
|
-
]
|
|
28603
|
-
}
|
|
28604
|
-
)
|
|
28828
|
+
showMeridiem && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
28829
|
+
/* @__PURE__ */ jsx(ListColumn, { ariaLabel: `${prefix}\uC624\uC804/\uC624\uD6C4`, items: meridiemItems }),
|
|
28830
|
+
divider
|
|
28831
|
+
] }),
|
|
28832
|
+
/* @__PURE__ */ jsx(ListColumn, { ariaLabel: `${prefix}\uC2DC`, items: hourItems }),
|
|
28833
|
+
divider,
|
|
28834
|
+
/* @__PURE__ */ jsx(ListColumn, { ariaLabel: `${prefix}\uBD84`, items: minuteItems })
|
|
28605
28835
|
]
|
|
28606
28836
|
}
|
|
28607
28837
|
);
|
|
@@ -28639,11 +28869,9 @@ function STimePicker({
|
|
|
28639
28869
|
style
|
|
28640
28870
|
}) {
|
|
28641
28871
|
const portalContainer = usePortalContainer();
|
|
28642
|
-
const triggerRef = useRef(null);
|
|
28643
28872
|
const [open, setOpen] = useState(false);
|
|
28644
28873
|
const triggerId = useFieldControlId();
|
|
28645
28874
|
const triggerTextId = `${triggerId}-text`;
|
|
28646
|
-
const [popoverAlignOffset, setPopoverAlignOffset] = useState(0);
|
|
28647
28875
|
const [focused, setFocused] = useState(false);
|
|
28648
28876
|
const [ruleError, setRuleError] = useState("");
|
|
28649
28877
|
const dims = TIMEPICKER_SIZE_CONFIG[size] ?? TIMEPICKER_SIZE_CONFIG.sm;
|
|
@@ -28664,11 +28892,6 @@ function STimePicker({
|
|
|
28664
28892
|
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
28665
28893
|
const commitOpen = (next) => {
|
|
28666
28894
|
if (disabled) return;
|
|
28667
|
-
if (next) {
|
|
28668
|
-
const triggerWidth = triggerRef.current?.getBoundingClientRect().width ?? 0;
|
|
28669
|
-
const selectorWidth = effectiveShowMeridiem ? MERIDIEM_TIME_SELECTOR_WIDTH : TIME_SELECTOR_WIDTH;
|
|
28670
|
-
setPopoverAlignOffset(Math.max(0, (triggerWidth - selectorWidth) / 2));
|
|
28671
|
-
}
|
|
28672
28895
|
setOpen(next);
|
|
28673
28896
|
onOpenChange?.(next);
|
|
28674
28897
|
if (!next && rules && rules.length > 0) setRuleError(runRules(value, rules));
|
|
@@ -28707,7 +28930,6 @@ function STimePicker({
|
|
|
28707
28930
|
/* @__PURE__ */ jsx(RPopover13.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
28708
28931
|
"div",
|
|
28709
28932
|
{
|
|
28710
|
-
ref: triggerRef,
|
|
28711
28933
|
id: triggerId,
|
|
28712
28934
|
role: "button",
|
|
28713
28935
|
"aria-disabled": disabled,
|
|
@@ -28771,20 +28993,22 @@ function STimePicker({
|
|
|
28771
28993
|
RPopover13.Content,
|
|
28772
28994
|
{
|
|
28773
28995
|
side: "bottom",
|
|
28774
|
-
align: "
|
|
28996
|
+
align: "center",
|
|
28775
28997
|
sideOffset: 4,
|
|
28776
|
-
alignOffset: popoverAlignOffset,
|
|
28777
28998
|
className: cn(
|
|
28778
28999
|
"z-50 max-w-[var(--radix-popover-content-available-width)]",
|
|
28779
29000
|
FLOATING_SLIDE_ANIM
|
|
28780
29001
|
),
|
|
28781
29002
|
children: /* @__PURE__ */ jsx(
|
|
28782
|
-
|
|
29003
|
+
TimeListbox,
|
|
28783
29004
|
{
|
|
28784
29005
|
value: normalized,
|
|
28785
29006
|
showMeridiem: effectiveShowMeridiem,
|
|
28786
29007
|
minuteStep,
|
|
28787
|
-
onChange:
|
|
29008
|
+
onChange: (time, part) => {
|
|
29009
|
+
commitValue(time);
|
|
29010
|
+
if (part === "minute") commitOpen(false);
|
|
29011
|
+
}
|
|
28788
29012
|
}
|
|
28789
29013
|
)
|
|
28790
29014
|
}
|
|
@@ -28814,7 +29038,6 @@ function formatTimeRangeDisplay(value, type) {
|
|
|
28814
29038
|
if (value == null || value[0] === "" || value[1] === "") return "";
|
|
28815
29039
|
return `${formatTimeDisplay(value[0], type)} ~ ${formatTimeDisplay(value[1], type)}`;
|
|
28816
29040
|
}
|
|
28817
|
-
var TIME_RANGE_SELECTOR_WIDTH = 345;
|
|
28818
29041
|
var MIDDAY_FIELD_MIN_WIDTH = {
|
|
28819
29042
|
sm: 178,
|
|
28820
29043
|
md: 216
|
|
@@ -28823,6 +29046,10 @@ var MIDDAY_FIELD_MAX_WIDTH = {
|
|
|
28823
29046
|
sm: "lg",
|
|
28824
29047
|
md: "lg"
|
|
28825
29048
|
};
|
|
29049
|
+
var toMinutes = (time) => {
|
|
29050
|
+
const { hour, minute } = parseTimeValue(time);
|
|
29051
|
+
return hour * 60 + minute;
|
|
29052
|
+
};
|
|
28826
29053
|
function RangeTimeSelector({
|
|
28827
29054
|
value,
|
|
28828
29055
|
showMeridiem,
|
|
@@ -28830,233 +29057,32 @@ function RangeTimeSelector({
|
|
|
28830
29057
|
rangeOrder,
|
|
28831
29058
|
onChange
|
|
28832
29059
|
}) {
|
|
28833
|
-
const range =
|
|
28834
|
-
const
|
|
28835
|
-
const
|
|
28836
|
-
|
|
28837
|
-
|
|
28838
|
-
const getMeridiem = (side) => getParts(side).hour >= 12 ? "PM" : "AM";
|
|
28839
|
-
const getDisplayHour = (side) => {
|
|
28840
|
-
const { hour } = getParts(side);
|
|
28841
|
-
if (!showMeridiem) return hour;
|
|
28842
|
-
const displayHour = hour % 12;
|
|
28843
|
-
return displayHour === 0 ? 12 : displayHour;
|
|
28844
|
-
};
|
|
28845
|
-
const commit = (side, hour, minute, direction = "unknown") => {
|
|
28846
|
-
const next = formatTimeValue(hour, minute);
|
|
28847
|
-
onChange(
|
|
28848
|
-
normalizeNextRange(side === "start" ? [next, range[1]] : [range[0], next], side, direction)
|
|
28849
|
-
);
|
|
28850
|
-
};
|
|
28851
|
-
const getTimeMinutes2 = (time) => {
|
|
28852
|
-
const { hour, minute } = parseTimeValue(time);
|
|
28853
|
-
return hour * 60 + minute;
|
|
28854
|
-
};
|
|
28855
|
-
const normalizeNextRange = (nextRange, changedSide, direction) => {
|
|
28856
|
-
if (rangeOrder === "allow-cross-day") return nextRange;
|
|
28857
|
-
const [start, end] = nextRange;
|
|
28858
|
-
const [currentStart, currentEnd] = range;
|
|
28859
|
-
if (getTimeMinutes2(start) <= getTimeMinutes2(end)) {
|
|
28860
|
-
const shouldKeepSynced = autoAdjustedRef.current?.side === changedSide && currentStart === currentEnd && start !== end && (direction === "unknown" || autoAdjustedRef.current.direction === direction);
|
|
28861
|
-
if (shouldKeepSynced) {
|
|
28862
|
-
return changedSide === "start" ? [start, start] : [end, end];
|
|
28863
|
-
}
|
|
28864
|
-
autoAdjustedRef.current = null;
|
|
28865
|
-
return nextRange;
|
|
28866
|
-
}
|
|
28867
|
-
autoAdjustedRef.current = {
|
|
28868
|
-
side: changedSide,
|
|
28869
|
-
direction: changedSide === "start" ? "increase" : "decrease"
|
|
28870
|
-
};
|
|
28871
|
-
return changedSide === "start" ? [start, start] : [end, end];
|
|
28872
|
-
};
|
|
28873
|
-
const changeHour = (side, delta) => {
|
|
28874
|
-
const { hour, minute } = getParts(side);
|
|
28875
|
-
commit(side, (hour + delta + 24) % 24, minute, delta > 0 ? "increase" : "decrease");
|
|
28876
|
-
};
|
|
28877
|
-
const changeMinute = (side, delta) => {
|
|
28878
|
-
const { hour, minute } = getParts(side);
|
|
28879
|
-
const next = minute + delta;
|
|
28880
|
-
if (next > 59) {
|
|
28881
|
-
commit(side, (hour + 1) % 24, next - 60, delta > 0 ? "increase" : "decrease");
|
|
28882
|
-
return;
|
|
28883
|
-
}
|
|
28884
|
-
if (next < 0) {
|
|
28885
|
-
commit(side, (hour + 23) % 24, next + 60, delta > 0 ? "increase" : "decrease");
|
|
29060
|
+
const range = value ?? ["00:00", "23:59"];
|
|
29061
|
+
const strict = rangeOrder !== "allow-cross-day";
|
|
29062
|
+
const change = (side, time) => {
|
|
29063
|
+
if (side === "end") {
|
|
29064
|
+
onChange([range[0], time]);
|
|
28886
29065
|
return;
|
|
28887
29066
|
}
|
|
28888
|
-
|
|
28889
|
-
};
|
|
28890
|
-
const setMeridiem = (side, next) => {
|
|
28891
|
-
const current = getMeridiem(side);
|
|
28892
|
-
if (current === next) return;
|
|
28893
|
-
const { hour, minute } = getParts(side);
|
|
28894
|
-
commit(side, next === "AM" ? hour - 12 : hour + 12, minute);
|
|
28895
|
-
};
|
|
28896
|
-
const getEditingKey = (side, part) => `${side}-${part}`;
|
|
28897
|
-
const sanitizeInput = (next) => next.replace(/\D/g, "").slice(0, 2);
|
|
28898
|
-
const beginEdit = (side, part, currentValue) => {
|
|
28899
|
-
const key = getEditingKey(side, part);
|
|
28900
|
-
setEditingKey(key);
|
|
28901
|
-
setInputDraft((prev) => ({ ...prev, [key]: String(currentValue).padStart(2, "0") }));
|
|
28902
|
-
};
|
|
28903
|
-
const commitHourDraft = (side, draft) => {
|
|
28904
|
-
const clamped = clampTimePart(Number(draft), showMeridiem ? 1 : 0, showMeridiem ? 12 : 23);
|
|
28905
|
-
const parts = getParts(side);
|
|
28906
|
-
const meridiem = getMeridiem(side);
|
|
28907
|
-
const nextHour = showMeridiem ? meridiem === "PM" ? clamped === 12 ? 12 : clamped + 12 : clamped === 12 ? 0 : clamped : clamped;
|
|
28908
|
-
commit(side, nextHour, parts.minute);
|
|
28909
|
-
return clamped;
|
|
28910
|
-
};
|
|
28911
|
-
const commitMinuteDraft = (side, draft) => {
|
|
28912
|
-
const { hour } = getParts(side);
|
|
28913
|
-
const clamped = clampTimePart(Number(draft), 0, 59);
|
|
28914
|
-
commit(side, hour, clamped);
|
|
28915
|
-
return clamped;
|
|
28916
|
-
};
|
|
28917
|
-
const endEdit = (side, part) => {
|
|
28918
|
-
const key = getEditingKey(side, part);
|
|
28919
|
-
const draft = inputDraft[key];
|
|
28920
|
-
if (part === "hour" && draft != null && draft !== "") {
|
|
28921
|
-
commitHourDraft(side, draft);
|
|
28922
|
-
}
|
|
28923
|
-
if (part === "minute" && draft != null && draft !== "") {
|
|
28924
|
-
commitMinuteDraft(side, draft);
|
|
28925
|
-
}
|
|
28926
|
-
setEditingKey(null);
|
|
28927
|
-
setInputDraft({});
|
|
28928
|
-
};
|
|
28929
|
-
const inputStyle = {
|
|
28930
|
-
width: "var(--cmp-timepicker-selector-time-input-width)",
|
|
28931
|
-
height: "var(--cmp-textinput-sm-height)",
|
|
28932
|
-
borderRadius: "var(--cmp-textinput-sm-radius)",
|
|
28933
|
-
border: "var(--cmp-textinput-borderWidth) solid var(--cmp-textinput-border-default)",
|
|
28934
|
-
color: "var(--cmp-textinput-text-default)",
|
|
28935
|
-
fontSize: 12,
|
|
28936
|
-
lineHeight: "20px"
|
|
28937
|
-
};
|
|
28938
|
-
const renderColumn = (side, kind) => {
|
|
28939
|
-
const isHour = kind === "hour";
|
|
28940
|
-
const { minute } = getParts(side);
|
|
28941
|
-
const val = isHour ? getDisplayHour(side) : minute;
|
|
28942
|
-
const currentEditingKey = getEditingKey(side, kind);
|
|
28943
|
-
const displayValue2 = editingKey === currentEditingKey ? inputDraft[currentEditingKey] ?? "" : String(val).padStart(2, "0");
|
|
28944
|
-
return /* @__PURE__ */ jsxs(
|
|
28945
|
-
"div",
|
|
28946
|
-
{
|
|
28947
|
-
className: "flex flex-col items-center justify-center",
|
|
28948
|
-
style: { gap: "var(--cmp-timepicker-selector-time-column-gap)" },
|
|
28949
|
-
children: [
|
|
28950
|
-
/* @__PURE__ */ jsx(
|
|
28951
|
-
SGhostButton,
|
|
28952
|
-
{
|
|
28953
|
-
icon: "chevronUp",
|
|
28954
|
-
size: "xs",
|
|
28955
|
-
ariaLabel: `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"} \uC99D\uAC00`,
|
|
28956
|
-
onClick: () => isHour ? changeHour(side, 1) : changeMinute(side, minuteStep)
|
|
28957
|
-
}
|
|
28958
|
-
),
|
|
28959
|
-
/* @__PURE__ */ jsx(
|
|
28960
|
-
"input",
|
|
28961
|
-
{
|
|
28962
|
-
"aria-label": `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"}`,
|
|
28963
|
-
inputMode: "numeric",
|
|
28964
|
-
value: displayValue2,
|
|
28965
|
-
placeholder: "-",
|
|
28966
|
-
onFocus: () => beginEdit(side, kind, val),
|
|
28967
|
-
onBlur: () => endEdit(side, kind),
|
|
28968
|
-
onChange: (event) => {
|
|
28969
|
-
const draft = sanitizeInput(event.target.value);
|
|
28970
|
-
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: draft }));
|
|
28971
|
-
if (draft === "") return;
|
|
28972
|
-
const raw = Number(draft);
|
|
28973
|
-
if (isHour) {
|
|
28974
|
-
if (draft.length < 2) return;
|
|
28975
|
-
const clamped2 = commitHourDraft(side, draft);
|
|
28976
|
-
const displayDraft2 = raw === clamped2 ? draft : String(clamped2);
|
|
28977
|
-
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: displayDraft2 }));
|
|
28978
|
-
return;
|
|
28979
|
-
}
|
|
28980
|
-
if (draft.length < 2) return;
|
|
28981
|
-
const clamped = commitMinuteDraft(side, draft);
|
|
28982
|
-
const displayDraft = raw === clamped ? draft : String(clamped);
|
|
28983
|
-
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: displayDraft }));
|
|
28984
|
-
},
|
|
28985
|
-
className: "box-border bg-white text-center outline-none",
|
|
28986
|
-
style: inputStyle
|
|
28987
|
-
}
|
|
28988
|
-
),
|
|
28989
|
-
/* @__PURE__ */ jsx(
|
|
28990
|
-
SGhostButton,
|
|
28991
|
-
{
|
|
28992
|
-
icon: "chevronDown",
|
|
28993
|
-
size: "xs",
|
|
28994
|
-
ariaLabel: `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"} \uAC10\uC18C`,
|
|
28995
|
-
onClick: () => isHour ? changeHour(side, -1) : changeMinute(side, -minuteStep)
|
|
28996
|
-
}
|
|
28997
|
-
)
|
|
28998
|
-
]
|
|
28999
|
-
}
|
|
29000
|
-
);
|
|
29067
|
+
onChange([time, strict && toMinutes(time) > toMinutes(range[1]) ? time : range[1]]);
|
|
29001
29068
|
};
|
|
29002
29069
|
const renderPanel = (side, title) => /* @__PURE__ */ jsxs(
|
|
29003
29070
|
"div",
|
|
29004
29071
|
{
|
|
29005
|
-
className: "flex flex-col items-center
|
|
29006
|
-
style: { gap: "var(--cmp-timepicker-selector-range-
|
|
29072
|
+
className: "flex flex-col items-center",
|
|
29073
|
+
style: { gap: "var(--cmp-timepicker-selector-range-heading-gap)" },
|
|
29007
29074
|
children: [
|
|
29008
|
-
/* @__PURE__ */
|
|
29009
|
-
|
|
29010
|
-
|
|
29011
|
-
className: "flex flex-col items-center",
|
|
29012
|
-
style: { gap: "var(--cmp-timepicker-selector-range-heading-gap)" },
|
|
29013
|
-
children: [
|
|
29014
|
-
/* @__PURE__ */ jsx("div", { className: "whitespace-nowrap text-center text-[12px] font-bold leading-[20px] text-[var(--cmp-timepicker-selector-range-heading-color)]", children: title }),
|
|
29015
|
-
showMeridiem && /* @__PURE__ */ jsx("div", { className: "isolate flex", children: ["AM", "PM"].map((item4, index) => {
|
|
29016
|
-
const selected = item4 === getMeridiem(side);
|
|
29017
|
-
return /* @__PURE__ */ jsx(
|
|
29018
|
-
"button",
|
|
29019
|
-
{
|
|
29020
|
-
type: "button",
|
|
29021
|
-
onClick: () => setMeridiem(side, item4),
|
|
29022
|
-
className: cn(
|
|
29023
|
-
"box-border h-[var(--cmp-textinput-sm-height)] w-[61px] cursor-pointer border border-solid bg-white text-center text-[12px] font-medium leading-[20px]",
|
|
29024
|
-
index === 0 ? "rounded-l-[var(--cmp-select-sm-radius)] -mr-px" : "rounded-r-[var(--cmp-select-sm-radius)]",
|
|
29025
|
-
selected ? "z-[1] border-[var(--cmp-timepicker-border-focus)] text-[var(--cmp-timepicker-border-focus)]" : "border-[var(--cmp-timepicker-border-default)] text-[var(--cmp-field-hint-color)]"
|
|
29026
|
-
),
|
|
29027
|
-
children: item4 === "AM" ? "\uC624\uC804" : "\uC624\uD6C4"
|
|
29028
|
-
},
|
|
29029
|
-
item4
|
|
29030
|
-
);
|
|
29031
|
-
}) })
|
|
29032
|
-
]
|
|
29033
|
-
}
|
|
29034
|
-
),
|
|
29035
|
-
/* @__PURE__ */ jsxs(
|
|
29036
|
-
"div",
|
|
29075
|
+
/* @__PURE__ */ jsx("div", { className: "whitespace-nowrap text-center text-[12px] font-bold leading-[20px] text-[var(--cmp-timepicker-selector-range-heading-color)]", children: title }),
|
|
29076
|
+
/* @__PURE__ */ jsx(
|
|
29077
|
+
TimeListbox,
|
|
29037
29078
|
{
|
|
29038
|
-
|
|
29039
|
-
|
|
29040
|
-
|
|
29041
|
-
|
|
29042
|
-
|
|
29043
|
-
|
|
29044
|
-
|
|
29045
|
-
/* @__PURE__ */ jsx(
|
|
29046
|
-
"span",
|
|
29047
|
-
{
|
|
29048
|
-
className: "text-center",
|
|
29049
|
-
style: {
|
|
29050
|
-
width: "var(--cmp-timepicker-selector-colon-size)",
|
|
29051
|
-
color: "var(--cmp-field-hint-color)",
|
|
29052
|
-
fontSize: "var(--cmp-timepicker-selector-colon-size)",
|
|
29053
|
-
lineHeight: "var(--cmp-timepicker-selector-colon-size)"
|
|
29054
|
-
},
|
|
29055
|
-
children: ":"
|
|
29056
|
-
}
|
|
29057
|
-
),
|
|
29058
|
-
renderColumn(side, "minute")
|
|
29059
|
-
]
|
|
29079
|
+
value: side === "start" ? range[0] : range[1],
|
|
29080
|
+
showMeridiem,
|
|
29081
|
+
minuteStep,
|
|
29082
|
+
labelPrefix: side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC",
|
|
29083
|
+
elevated: false,
|
|
29084
|
+
isDisabled: strict && side === "end" ? (minutes) => minutes < toMinutes(range[0]) : void 0,
|
|
29085
|
+
onChange: (time) => change(side, time)
|
|
29060
29086
|
}
|
|
29061
29087
|
)
|
|
29062
29088
|
]
|
|
@@ -29115,11 +29141,9 @@ function STimeRangePicker({
|
|
|
29115
29141
|
style
|
|
29116
29142
|
}) {
|
|
29117
29143
|
const portalContainer = usePortalContainer();
|
|
29118
|
-
const triggerRef = useRef(null);
|
|
29119
29144
|
const [open, setOpen] = useState(false);
|
|
29120
29145
|
const triggerId = useFieldControlId();
|
|
29121
29146
|
const triggerTextId = `${triggerId}-text`;
|
|
29122
|
-
const [popoverAlignOffset, setPopoverAlignOffset] = useState(0);
|
|
29123
29147
|
const [focused, setFocused] = useState(false);
|
|
29124
29148
|
const [ruleError, setRuleError] = useState("");
|
|
29125
29149
|
const dims = TIMEPICKER_SIZE_CONFIG[size] ?? TIMEPICKER_SIZE_CONFIG.sm;
|
|
@@ -29143,10 +29167,6 @@ function STimeRangePicker({
|
|
|
29143
29167
|
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
29144
29168
|
const commitOpen = (next) => {
|
|
29145
29169
|
if (disabled) return;
|
|
29146
|
-
if (next) {
|
|
29147
|
-
const triggerWidth = triggerRef.current?.getBoundingClientRect().width ?? 0;
|
|
29148
|
-
setPopoverAlignOffset(Math.max(0, (triggerWidth - TIME_RANGE_SELECTOR_WIDTH) / 2));
|
|
29149
|
-
}
|
|
29150
29170
|
setOpen(next);
|
|
29151
29171
|
onOpenChange?.(next);
|
|
29152
29172
|
if (!next && rules && rules.length > 0) setRuleError(runRules(value, rules));
|
|
@@ -29185,7 +29205,6 @@ function STimeRangePicker({
|
|
|
29185
29205
|
/* @__PURE__ */ jsx(RPopover13.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
29186
29206
|
"div",
|
|
29187
29207
|
{
|
|
29188
|
-
ref: triggerRef,
|
|
29189
29208
|
id: triggerId,
|
|
29190
29209
|
role: "button",
|
|
29191
29210
|
"aria-disabled": disabled,
|
|
@@ -29249,9 +29268,8 @@ function STimeRangePicker({
|
|
|
29249
29268
|
RPopover13.Content,
|
|
29250
29269
|
{
|
|
29251
29270
|
side: "bottom",
|
|
29252
|
-
align: "
|
|
29271
|
+
align: "center",
|
|
29253
29272
|
sideOffset: 4,
|
|
29254
|
-
alignOffset: popoverAlignOffset,
|
|
29255
29273
|
className: cn(
|
|
29256
29274
|
"z-50 max-w-[var(--radix-popover-content-available-width)]",
|
|
29257
29275
|
FLOATING_SLIDE_ANIM
|