sellmate-design-system-react 9.0.0-beta.17 → 9.0.0-beta.19

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.
Files changed (32) hide show
  1. package/AGENTS.md +70 -0
  2. package/README.md +5 -0
  3. package/dist/components/SChipFilter/README.md +14 -5
  4. package/dist/components/SChipFilter/SChipFilter.d.ts +10 -3
  5. package/dist/components/SChipFilter/index.d.ts +1 -1
  6. package/dist/components/SDatePicker/README.md +5 -1
  7. package/dist/components/SDatePicker/SDatePicker.d.ts +20 -3
  8. package/dist/components/SDateRangePicker/README.md +2 -1
  9. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +9 -2
  10. package/dist/components/SDivider/README.md +2 -0
  11. package/dist/components/SDraggableItem/README.md +1 -0
  12. package/dist/components/SDraggableItem/SDraggableItem.d.ts +2 -0
  13. package/dist/components/SDraggableList/README.md +5 -0
  14. package/dist/components/SDraggableList/SDraggableList.d.ts +10 -0
  15. package/dist/components/SDraggableList/index.d.ts +1 -1
  16. package/dist/components/SGhostButton/README.md +2 -0
  17. package/dist/components/SIcon/SIcon.d.ts +1 -1
  18. package/dist/components/SIcon/icons.gen.d.ts +2 -0
  19. package/dist/components/STable/README.md +7 -1
  20. package/dist/components/STable/STable.d.ts +39 -5
  21. package/dist/components/STextLink/README.md +3 -0
  22. package/dist/components/STextLink/STextLink.d.ts +2 -0
  23. package/dist/index.cjs +463 -89
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.js +463 -89
  26. package/dist/index.js.map +1 -1
  27. package/dist/llms-full.txt +105 -7
  28. package/dist/llms.txt +70 -0
  29. package/dist/styles.css +16 -0
  30. package/eslint/lib/table-column.mjs +12 -3
  31. package/eslint/rules/table-column-width.mjs +3 -2
  32. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1203,6 +1203,45 @@ var Icon_close = (p) => {
1203
1203
  }
1204
1204
  );
1205
1205
  };
1206
+ var Icon_company = (p) => {
1207
+ const { size = 24, ...rest } = p;
1208
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1209
+ "svg",
1210
+ {
1211
+ width: size,
1212
+ height: size,
1213
+ viewBox: "0 0 24 24",
1214
+ fill: "none",
1215
+ xmlns: "http://www.w3.org/2000/svg",
1216
+ ...rest,
1217
+ children: [
1218
+ /* @__PURE__ */ jsxRuntime.jsx(
1219
+ "path",
1220
+ {
1221
+ d: "M14.25 8.5C14.6642 8.5 15 8.83579 15 9.25C15 9.66421 14.6642 10 14.25 10H9.75C9.33579 10 9 9.66421 9 9.25C9 8.83579 9.33579 8.5 9.75 8.5H14.25Z",
1222
+ fill: "currentColor"
1223
+ }
1224
+ ),
1225
+ /* @__PURE__ */ jsxRuntime.jsx(
1226
+ "path",
1227
+ {
1228
+ d: "M14.25 5.5C14.6642 5.5 15 5.83579 15 6.25C15 6.66421 14.6642 7 14.25 7H9.75C9.33579 7 9 6.66421 9 6.25C9 5.83579 9.33579 5.5 9.75 5.5H14.25Z",
1229
+ fill: "currentColor"
1230
+ }
1231
+ ),
1232
+ /* @__PURE__ */ jsxRuntime.jsx(
1233
+ "path",
1234
+ {
1235
+ fillRule: "evenodd",
1236
+ clipRule: "evenodd",
1237
+ d: "M16 2.25C16.9665 2.25 17.75 3.0335 17.75 4V6.25H20.25C21.2165 6.25 22 7.0335 22 8V20C22 20.9665 21.2165 21.75 20.25 21.75H3.75C2.7835 21.75 2 20.9665 2 20V10C2 9.0335 2.7835 8.25 3.75 8.25H6.25V4C6.25 3.0335 7.0335 2.25 8 2.25H16ZM3.75 9.75C3.61193 9.75 3.5 9.86193 3.5 10V20C3.5 20.1381 3.61193 20.25 3.75 20.25H6.25V9.75H3.75ZM8 3.75C7.86193 3.75 7.75 3.86193 7.75 4V20.25H9.25V16C9.25 15.0335 10.0335 14.25 11 14.25H13C13.9665 14.25 14.75 15.0335 14.75 16V20.25H16.25V4C16.25 3.86193 16.1381 3.75 16 3.75H8ZM11 15.75C10.8619 15.75 10.75 15.8619 10.75 16V20.25H13.25V16C13.25 15.8619 13.1381 15.75 13 15.75H11ZM17.75 20.25H20.25C20.3881 20.25 20.5 20.1381 20.5 20V8C20.5 7.86193 20.3881 7.75 20.25 7.75H17.75V20.25Z",
1238
+ fill: "currentColor"
1239
+ }
1240
+ )
1241
+ ]
1242
+ }
1243
+ );
1244
+ };
1206
1245
  var Icon_copy = (p) => {
1207
1246
  const { size = 24, ...rest } = p;
1208
1247
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2426,6 +2465,27 @@ var Icon_row = (p) => {
2426
2465
  }
2427
2466
  );
2428
2467
  };
2468
+ var Icon_rowDense = (p) => {
2469
+ const { size = 24, ...rest } = p;
2470
+ return /* @__PURE__ */ jsxRuntime.jsx(
2471
+ "svg",
2472
+ {
2473
+ width: size,
2474
+ height: size,
2475
+ viewBox: "0 0 24 24",
2476
+ fill: "none",
2477
+ xmlns: "http://www.w3.org/2000/svg",
2478
+ ...rest,
2479
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2480
+ "path",
2481
+ {
2482
+ d: "M7.25 2C7.66421 2 8 2.33579 8 2.75V21.25C8 21.6642 7.66421 22 7.25 22H5.75C5.33579 22 5 21.6642 5 21.25V2.75C5 2.33579 5.33579 2 5.75 2H7.25ZM12.75 2C13.1642 2 13.5 2.33579 13.5 2.75V21.25C13.5 21.6642 13.1642 22 12.75 22H11.25C10.8358 22 10.5 21.6642 10.5 21.25V2.75C10.5 2.33579 10.8358 2 11.25 2H12.75ZM18.25 2C18.6642 2 19 2.33579 19 2.75V21.25C19 21.6642 18.6642 22 18.25 22H16.75C16.3358 22 16 21.6642 16 21.25V2.75C16 2.33579 16.3358 2 16.75 2H18.25Z",
2483
+ fill: "currentColor"
2484
+ }
2485
+ )
2486
+ }
2487
+ );
2488
+ };
2429
2489
  var Icon_save = (p) => {
2430
2490
  const { size = 24, ...rest } = p;
2431
2491
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3074,6 +3134,7 @@ var ICONS = {
3074
3134
  clockFill: Icon_clockFill,
3075
3135
  clockOutline: Icon_clockOutline,
3076
3136
  close: Icon_close,
3137
+ company: Icon_company,
3077
3138
  copy: Icon_copy,
3078
3139
  coupon: Icon_coupon,
3079
3140
  dashboard: Icon_dashboard,
@@ -3111,6 +3172,7 @@ var ICONS = {
3111
3172
  reset: Icon_reset,
3112
3173
  robot: Icon_robot,
3113
3174
  row: Icon_row,
3175
+ rowDense: Icon_rowDense,
3114
3176
  save: Icon_save,
3115
3177
  search: Icon_search,
3116
3178
  send: Icon_send,
@@ -3429,6 +3491,7 @@ var SDraggableItemView = /* @__PURE__ */ react.forwardRef(
3429
3491
  supportingText,
3430
3492
  supportingTextPosition,
3431
3493
  trailing,
3494
+ depth,
3432
3495
  accentStripe,
3433
3496
  bordered,
3434
3497
  selected,
@@ -3440,6 +3503,7 @@ var SDraggableItemView = /* @__PURE__ */ react.forwardRef(
3440
3503
  dragHandleProps,
3441
3504
  onDragHandleMouseDown,
3442
3505
  className,
3506
+ style,
3443
3507
  ...rest
3444
3508
  }, ref) {
3445
3509
  const renderState = { hovered, dragging, disabled };
@@ -3449,12 +3513,19 @@ var SDraggableItemView = /* @__PURE__ */ react.forwardRef(
3449
3513
  const trailingNode = renderSlot(trailing, renderState);
3450
3514
  const titleTypo = size === "sm" ? selected ? "typo-item-sm-selected" : "typo-item-sm-default" : selected ? "typo-item-md-selected" : "typo-item-md-default";
3451
3515
  const paddingYClass = dense ? size === "sm" ? "py-(--cmp-listDraggableItem-paddingY-sm-dense)" : "py-(--cmp-listDraggableItem-paddingY-md-dense)" : size === "sm" ? "py-(--cmp-listDraggableItem-paddingY-sm-normal)" : "py-(--cmp-listDraggableItem-paddingY-md-normal)";
3516
+ const normalizedDepth = Math.max(1, Math.floor(depth));
3517
+ const depthOffset = (normalizedDepth - 1) * 20;
3518
+ const depthStyle = depthOffset === 0 ? style : {
3519
+ ...style,
3520
+ paddingLeft: `calc(var(--cmp-listDraggableItem-paddingX) + ${depthOffset}px)`
3521
+ };
3452
3522
  return /* @__PURE__ */ jsxRuntime.jsxs(
3453
3523
  "div",
3454
3524
  {
3455
3525
  ref,
3456
3526
  "aria-disabled": disabled || void 0,
3457
3527
  "data-dragging": dragging || void 0,
3528
+ style: depthStyle,
3458
3529
  className: cn(
3459
3530
  "relative flex w-full items-center gap-(--cmp-listDraggableItem-gap) overflow-hidden px-(--cmp-listDraggableItem-paddingX)",
3460
3531
  titleTypo,
@@ -3543,6 +3614,7 @@ var SDraggableItemImpl = /* @__PURE__ */ react.forwardRef(function SDraggableIte
3543
3614
  supportingText,
3544
3615
  supportingTextPosition = "right",
3545
3616
  trailing,
3617
+ depth = 1,
3546
3618
  accentStripe = false,
3547
3619
  bordered = false,
3548
3620
  selected = false,
@@ -3616,6 +3688,7 @@ var SDraggableItemImpl = /* @__PURE__ */ react.forwardRef(function SDraggableIte
3616
3688
  supportingText,
3617
3689
  supportingTextPosition,
3618
3690
  trailing,
3691
+ depth,
3619
3692
  accentStripe,
3620
3693
  bordered,
3621
3694
  selected,
@@ -3651,6 +3724,7 @@ var SDraggableItemImpl = /* @__PURE__ */ react.forwardRef(function SDraggableIte
3651
3724
  supportingText,
3652
3725
  supportingTextPosition,
3653
3726
  trailing,
3727
+ depth,
3654
3728
  accentStripe,
3655
3729
  bordered,
3656
3730
  selected,
@@ -3900,6 +3974,42 @@ function reorder(items, fromIndex, toIndex) {
3900
3974
  nextItems.splice(toIndex, 0, movedItem);
3901
3975
  return nextItems;
3902
3976
  }
3977
+ function clampDepth(depth, maxDepth) {
3978
+ return Math.max(1, Math.min(maxDepth, Math.floor(depth)));
3979
+ }
3980
+ function getSubtreeEnd(items, startIndex, getDepth) {
3981
+ const startDepth = getDepth(items[startIndex], startIndex);
3982
+ let endIndex = startIndex + 1;
3983
+ while (endIndex < items.length && getDepth(items[endIndex], endIndex) > startDepth) {
3984
+ endIndex += 1;
3985
+ }
3986
+ return endIndex;
3987
+ }
3988
+ function getInsertionIndex(items, target, getDepth) {
3989
+ if (target.intent === "before") return target.index;
3990
+ if (target.intent === "inside") return target.index + 1;
3991
+ return getSubtreeEnd(items, target.index, getDepth);
3992
+ }
3993
+ function moveWithDepth(items, fromIndex, target, getDepth, setDepth, maxDepth) {
3994
+ const fromEndIndex = getSubtreeEnd(items, fromIndex, getDepth);
3995
+ if (target.index >= fromIndex && target.index < fromEndIndex) return items;
3996
+ const movedItems = items.slice(fromIndex, fromEndIndex);
3997
+ const remainingItems = [...items.slice(0, fromIndex), ...items.slice(fromEndIndex)];
3998
+ const rawInsertionIndex = getInsertionIndex(items, target, getDepth);
3999
+ const insertionIndex = rawInsertionIndex > fromIndex ? rawInsertionIndex - movedItems.length : rawInsertionIndex;
4000
+ const sourceDepth = getDepth(items[fromIndex], fromIndex);
4001
+ const targetDepth = getDepth(items[target.index], target.index);
4002
+ const nextDepth = target.intent === "inside" ? targetDepth + 1 : targetDepth;
4003
+ const depthDelta = clampDepth(nextDepth, maxDepth) - sourceDepth;
4004
+ const nextMovedItems = setDepth && depthDelta !== 0 ? movedItems.map(
4005
+ (item, index) => setDepth(item, clampDepth(getDepth(item, fromIndex + index) + depthDelta, maxDepth))
4006
+ ) : movedItems;
4007
+ return [
4008
+ ...remainingItems.slice(0, insertionIndex),
4009
+ ...nextMovedItems,
4010
+ ...remainingItems.slice(insertionIndex)
4011
+ ];
4012
+ }
3903
4013
  function SDraggableListInner({
3904
4014
  items,
3905
4015
  getKey,
@@ -3909,6 +4019,10 @@ function SDraggableListInner({
3909
4019
  defaultSelectedKey,
3910
4020
  onSelectedKeyChange,
3911
4021
  getDisabled,
4022
+ getDepth,
4023
+ setDepth,
4024
+ getCanHaveChildren,
4025
+ maxDepth = 3,
3912
4026
  listId,
3913
4027
  group: groupName,
3914
4028
  useGap,
@@ -3918,19 +4032,24 @@ function SDraggableListInner({
3918
4032
  const dragContext = useSDraggableGroup();
3919
4033
  const dragGroup = groupName ?? dragContext?.defaultGroup;
3920
4034
  const [draggingIndex, setDraggingIndex] = react.useState(null);
3921
- const [dropIndex, setDropIndex] = react.useState(null);
4035
+ const [dropTarget, setDropTarget] = react.useState(null);
3922
4036
  const [internalSelectedKey, setInternalSelectedKey] = react.useState(
3923
4037
  defaultSelectedKey
3924
4038
  );
3925
4039
  const listRef = react.useRef(null);
3926
4040
  const fromIndexRef = react.useRef(null);
3927
4041
  const currentSelectedKey = selectedKey ?? internalSelectedKey;
4042
+ const usesDepth = getDepth != null;
3928
4043
  const startDrag = react.useCallback((event, index) => {
3929
4044
  event.preventDefault();
3930
4045
  fromIndexRef.current = index;
3931
4046
  setDraggingIndex(index);
3932
- setDropIndex(index);
4047
+ setDropTarget({ index, intent: "before" });
3933
4048
  }, []);
4049
+ const readDepth = react.useCallback(
4050
+ (item, index) => clampDepth(getDepth?.(item, index) ?? 1, maxDepth),
4051
+ [getDepth, maxDepth]
4052
+ );
3934
4053
  react.useEffect(() => {
3935
4054
  if (dragContext || draggingIndex == null) return void 0;
3936
4055
  const handleMouseMove = (event) => {
@@ -3938,25 +4057,65 @@ function SDraggableListInner({
3938
4057
  "[data-draggable-list-item]"
3939
4058
  );
3940
4059
  if (!itemElements) return;
3941
- let nextDropIndex = items.length;
3942
- itemElements.forEach((itemElement, index) => {
4060
+ let nextDropTarget = items.length === 0 ? { index: 0, intent: "before" } : { index: items.length - 1, intent: "after" };
4061
+ const draggingEndIndex = usesDepth && draggingIndex != null ? getSubtreeEnd(items, draggingIndex, readDepth) : draggingIndex + 1;
4062
+ let previousDropIndex = null;
4063
+ for (let index = 0; index < itemElements.length; index += 1) {
4064
+ const itemElement = itemElements[index];
3943
4065
  const rect = itemElement.getBoundingClientRect();
3944
- if (event.clientY < rect.top + rect.height / 2 && nextDropIndex === items.length) {
3945
- nextDropIndex = index;
4066
+ if (index >= draggingIndex && index < draggingEndIndex) {
4067
+ if (event.clientY <= rect.bottom) {
4068
+ if (previousDropIndex != null) {
4069
+ nextDropTarget = { index: previousDropIndex, intent: "after" };
4070
+ } else {
4071
+ nextDropTarget = { index: draggingIndex, intent: "before" };
4072
+ }
4073
+ break;
4074
+ }
4075
+ continue;
3946
4076
  }
3947
- });
3948
- setDropIndex(nextDropIndex);
4077
+ if (event.clientY < rect.top) {
4078
+ nextDropTarget = { index, intent: "before" };
4079
+ break;
4080
+ }
4081
+ if (event.clientY > rect.bottom) {
4082
+ previousDropIndex = index;
4083
+ nextDropTarget = { index, intent: "after" };
4084
+ continue;
4085
+ }
4086
+ const ratio = (event.clientY - rect.top) / rect.height;
4087
+ if (!usesDepth) {
4088
+ nextDropTarget = ratio < 0.5 ? { index, intent: "before" } : { index, intent: "after" };
4089
+ break;
4090
+ }
4091
+ const item = items[index];
4092
+ const canHaveChildren = (getCanHaveChildren?.(item, index) ?? true) && readDepth(item, index) < maxDepth;
4093
+ if (ratio < 0.25) {
4094
+ nextDropTarget = { index, intent: "before" };
4095
+ } else if (ratio > 0.75 || !canHaveChildren) {
4096
+ nextDropTarget = { index, intent: "after" };
4097
+ } else {
4098
+ nextDropTarget = { index, intent: "inside" };
4099
+ }
4100
+ break;
4101
+ }
4102
+ setDropTarget(nextDropTarget);
3949
4103
  };
3950
4104
  const handleMouseUp = () => {
3951
4105
  const fromIndex = fromIndexRef.current;
3952
- const nextDropIndex = dropIndex;
3953
- if (fromIndex != null && nextDropIndex != null) {
3954
- const toIndex = nextDropIndex > fromIndex ? nextDropIndex - 1 : nextDropIndex;
3955
- if (fromIndex !== toIndex) onChange?.(reorder(items, fromIndex, toIndex));
4106
+ const nextDropTarget = dropTarget;
4107
+ if (fromIndex != null && nextDropTarget != null && items[nextDropTarget.index]) {
4108
+ const insertionIndex = getInsertionIndex(items, nextDropTarget, () => 1);
4109
+ const nextItems = usesDepth ? moveWithDepth(items, fromIndex, nextDropTarget, readDepth, setDepth, maxDepth) : reorder(
4110
+ items,
4111
+ fromIndex,
4112
+ insertionIndex > fromIndex ? insertionIndex - 1 : insertionIndex
4113
+ );
4114
+ if (nextItems !== items) onChange?.(nextItems);
3956
4115
  }
3957
4116
  fromIndexRef.current = null;
3958
4117
  setDraggingIndex(null);
3959
- setDropIndex(null);
4118
+ setDropTarget(null);
3960
4119
  };
3961
4120
  window.addEventListener("mousemove", handleMouseMove);
3962
4121
  window.addEventListener("mouseup", handleMouseUp);
@@ -3964,12 +4123,25 @@ function SDraggableListInner({
3964
4123
  window.removeEventListener("mousemove", handleMouseMove);
3965
4124
  window.removeEventListener("mouseup", handleMouseUp);
3966
4125
  };
3967
- }, [draggingIndex, dropIndex, items, onChange, dragContext]);
4126
+ }, [
4127
+ draggingIndex,
4128
+ dropTarget,
4129
+ items,
4130
+ onChange,
4131
+ dragContext,
4132
+ getDepth,
4133
+ usesDepth,
4134
+ readDepth,
4135
+ setDepth,
4136
+ maxDepth,
4137
+ getCanHaveChildren
4138
+ ]);
3968
4139
  if (dragContext && (!listId || !dragGroup)) {
3969
4140
  throw new Error("SDraggableList: Provider \uC548\uC5D0\uC11C\uB294 group\uACFC listId\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.");
3970
4141
  }
3971
4142
  const groupDrop = dragContext?.drop;
3972
- const currentDropIndex = dragContext ? groupDrop?.group === dragGroup && groupDrop?.listId === listId ? groupDrop?.index ?? null : null : dropIndex;
4143
+ const currentDropIndex = dragContext ? groupDrop?.group === dragGroup && groupDrop?.listId === listId ? groupDrop?.index ?? null : null : dropTarget?.index;
4144
+ const currentDropIntent = dragContext ? void 0 : dropTarget?.intent;
3973
4145
  const isDropTarget = dragContext ? groupDrop?.group === dragGroup && groupDrop?.listId === listId : draggingIndex != null;
3974
4146
  const showEmptyDrop = Boolean(dragContext && isDropTarget && items.length === 0);
3975
4147
  const beforeIndicatorPosition = useGap ? "before:top-[-5px]" : "before:top-0";
@@ -4000,19 +4172,30 @@ function SDraggableListInner({
4000
4172
  children: items.map((item, index) => {
4001
4173
  const itemKey = getKey(item, index);
4002
4174
  const disabled = getDisabled?.(item, index) ?? false;
4003
- const showBefore = isDropTarget && currentDropIndex === index;
4004
- const showAfter = isDropTarget && currentDropIndex === items.length && index === items.length - 1;
4175
+ const depth = readDepth(item, index);
4176
+ const showBefore = isDropTarget && currentDropIndex === index && (dragContext || currentDropIntent === "before");
4177
+ const showInside = isDropTarget && currentDropIndex === index && !dragContext && usesDepth && currentDropIntent === "inside";
4178
+ const showAfter = isDropTarget && (dragContext ? currentDropIndex === items.length && index === items.length - 1 : currentDropIndex === index && currentDropIntent === "after");
4179
+ const selectItem = () => {
4180
+ if (disabled) return;
4181
+ setInternalSelectedKey(itemKey);
4182
+ onSelectedKeyChange?.(itemKey);
4183
+ };
4184
+ const handleItemKeyDown = (event) => {
4185
+ if (event.key !== "Enter" && event.key !== " ") return;
4186
+ event.preventDefault();
4187
+ selectItem();
4188
+ };
4005
4189
  return /* @__PURE__ */ jsxRuntime.jsx(
4006
4190
  "div",
4007
4191
  {
4008
4192
  "data-draggable-list-item": true,
4193
+ role: "button",
4194
+ tabIndex: disabled ? -1 : 0,
4009
4195
  "aria-disabled": disabled || void 0,
4010
- onClick: () => {
4011
- if (disabled) return;
4012
- setInternalSelectedKey(itemKey);
4013
- onSelectedKeyChange?.(itemKey);
4014
- },
4015
- className: `relative ${showBefore ? `before:absolute before:left-0 before:right-0 ${beforeIndicatorPosition} before:z-10 before:h-[2px] before:bg-(--sys-color-border-accent) before:content-['']` : ""} ${showAfter ? `after:absolute after:left-0 after:right-0 ${afterIndicatorPosition} after:z-10 after:h-[2px] after:bg-(--sys-color-border-accent) after:content-['']` : ""}`,
4196
+ onClick: selectItem,
4197
+ onKeyDown: handleItemKeyDown,
4198
+ className: `relative ${showBefore ? `before:absolute before:left-0 before:right-0 ${beforeIndicatorPosition} before:z-10 before:h-[2px] before:bg-(--sys-color-border-accent) before:content-['']` : ""} ${showAfter ? `after:absolute after:left-0 after:right-0 ${afterIndicatorPosition} after:z-10 after:h-[2px] after:bg-(--sys-color-border-accent) after:content-['']` : ""} ${showInside ? "outline outline-2 outline-offset-[-2px] outline-(--sys-color-border-accent)" : ""}`,
4016
4199
  children: renderItem(item, index, {
4017
4200
  onDragHandleMouseDown: (event) => {
4018
4201
  if (disabled) return;
@@ -4020,7 +4203,8 @@ function SDraggableListInner({
4020
4203
  dragContext.startDrag(event, dragGroup, listId, index, itemKey);
4021
4204
  } else startDrag(event, index);
4022
4205
  },
4023
- selected: currentSelectedKey === itemKey
4206
+ selected: currentSelectedKey === itemKey,
4207
+ depth
4024
4208
  })
4025
4209
  },
4026
4210
  itemKey
@@ -6104,6 +6288,7 @@ var STextLink = /* @__PURE__ */ react.forwardRef(function STextLink2({
6104
6288
  label = "",
6105
6289
  icon,
6106
6290
  iconColor,
6291
+ iconRotate,
6107
6292
  labelClass,
6108
6293
  rightArrow = "none",
6109
6294
  underline = false,
@@ -6146,7 +6331,7 @@ var STextLink = /* @__PURE__ */ react.forwardRef(function STextLink2({
6146
6331
  style: { gap: s.gap, color: contentColor, ...style },
6147
6332
  ...rest,
6148
6333
  children: [
6149
- icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.iconSize, color: resolvedIconColor }),
6334
+ icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.iconSize, color: resolvedIconColor, rotate: iconRotate }),
6150
6335
  /* @__PURE__ */ jsxRuntime.jsx(
6151
6336
  "span",
6152
6337
  {
@@ -11124,11 +11309,13 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11124
11309
  noDataSlot,
11125
11310
  isLoading = false,
11126
11311
  dense = false,
11312
+ onDenseChange,
11127
11313
  noHover = false,
11128
11314
  pagination,
11129
11315
  onPageChange,
11130
11316
  useInternalPagination = false,
11131
11317
  useRowsPerPageSelect = false,
11318
+ useDensityToggle = false,
11132
11319
  rowsPerPageOption = DEFAULT_ROWS_PER_PAGE_OPTION,
11133
11320
  onRowsPerPageChange,
11134
11321
  useVirtualScroll = false,
@@ -11524,21 +11711,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11524
11711
  col.align === "right" && "justify-end"
11525
11712
  ),
11526
11713
  children: col.renderHeader ? col.renderHeader(col, i) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11527
- col.icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: col.icon, size: 16, color: col.iconColor }),
11528
11714
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: col.label }),
11529
- isSortable && // 미정렬은 updown 을 default(회색)로 — 정렬할 수 있다는 것 자체가
11530
- // 헤더에 보여야 한다. 정렬 중이면 action 색으로 올려 활성임을 알린다.
11531
- // 라벨이 truncate 돼도 버튼은 남도록 shrink-0.
11532
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
11533
- SGhostButton,
11534
- {
11535
- size: "xxs",
11536
- intent: sortDir ? "action" : "default",
11537
- icon: sortDir === "asc" ? "arrowUp" : sortDir === "desc" ? "arrowDown" : "updown",
11538
- ariaLabel: `${col.label} \uC815\uB82C`,
11539
- onClick: () => toggleSort(col)
11540
- }
11541
- ) }),
11542
11715
  col.helpText && col.helpText.length > 0 && // 라벨이 좁아져 truncate 되더라도 아이콘까지 함께 줄어들면 안 되므로
11543
11716
  // shrink-0 래퍼로 감싼다 (STooltip 의 className 은 본문 쪽으로 간다).
11544
11717
  // 아이콘·크기·색은 SKeyValueTable 헤더 도움말과 동일하게 맞춘다.
@@ -11552,6 +11725,50 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11552
11725
  placement: "top",
11553
11726
  message: col.helpText
11554
11727
  }
11728
+ ) }),
11729
+ col.editable && /* @__PURE__ */ jsxRuntime.jsx(
11730
+ SIcon,
11731
+ {
11732
+ name: "pageEdit",
11733
+ size: 12,
11734
+ color: "var(--sys-color-link-accent)",
11735
+ label: "\uC218\uC815 \uAC00\uB2A5",
11736
+ className: "shrink-0"
11737
+ }
11738
+ ),
11739
+ col.navigable && /* @__PURE__ */ jsxRuntime.jsx(
11740
+ SIcon,
11741
+ {
11742
+ name: "pageMove",
11743
+ size: 12,
11744
+ color: "var(--sys-color-link-accent)",
11745
+ label: "\uB2E4\uB978 \uD654\uBA74\uC73C\uB85C \uC774\uB3D9",
11746
+ className: "shrink-0"
11747
+ }
11748
+ ),
11749
+ col.required && // 라벨이 truncate 돼도 필수 표시는 남아야 하므로 shrink-0.
11750
+ // 아이콘·크기·색은 SKeyValueTable 헤더 필수 표시와 동일하게 맞춘다.
11751
+ /* @__PURE__ */ jsxRuntime.jsx(
11752
+ SIcon,
11753
+ {
11754
+ name: "asterisk",
11755
+ size: 12,
11756
+ color: "var(--sys-color-fg-accent)",
11757
+ className: "shrink-0"
11758
+ }
11759
+ ),
11760
+ isSortable && // 미정렬은 updown 을 default(회색)로 — 정렬할 수 있다는 것 자체가
11761
+ // 헤더에 보여야 한다. 정렬 중이면 action 색으로 올려 활성임을 알린다.
11762
+ // 라벨이 truncate 돼도 버튼은 남도록 shrink-0.
11763
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
11764
+ SGhostButton,
11765
+ {
11766
+ size: "xxs",
11767
+ intent: sortDir ? "action" : "default",
11768
+ icon: sortDir === "asc" ? "arrowUp" : sortDir === "desc" ? "arrowDown" : "updown",
11769
+ ariaLabel: `${col.label} \uC815\uB82C`,
11770
+ onClick: () => toggleSort(col)
11771
+ }
11555
11772
  ) })
11556
11773
  ] })
11557
11774
  }
@@ -11709,16 +11926,32 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11709
11926
  ),
11710
11927
  children: [
11711
11928
  /* @__PURE__ */ jsxRuntime.jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11712
- useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsxRuntime.jsx(
11713
- SSelect2,
11714
- {
11715
- options: rowsPerPageOption,
11716
- value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11717
- emitValue: true,
11718
- onValueChange: (v) => changeRowsPerPage(Number(v)),
11719
- width: 128
11720
- }
11721
- ) })
11929
+ (useDensityToggle || useRowsPerPageSelect) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-[10px] top-1/2 flex -translate-y-1/2 items-center gap-[12px]", children: [
11930
+ useDensityToggle && // 라벨·아이콘은 현재 밀도가 아니라 **누르면 되는 상태**를 가리킨다 (dense 면 "넓게 보기").
11931
+ // row·rowDense 는 세로 막대라 90도 돌려 가로 행으로 쓴다.
11932
+ /* @__PURE__ */ jsxRuntime.jsx(
11933
+ STextLink,
11934
+ {
11935
+ size: "sm",
11936
+ icon: dense ? "row" : "rowDense",
11937
+ iconRotate: 90,
11938
+ iconColor: "var(--sys-color-fg-quaternary)",
11939
+ label: dense ? "\uB113\uAC8C \uBCF4\uAE30" : "\uC881\uAC8C \uBCF4\uAE30",
11940
+ onClick: () => onDenseChange?.(!dense)
11941
+ }
11942
+ ),
11943
+ useDensityToggle && useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx(SDivider, { vertical: true, className: "my-[6px]" }),
11944
+ useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx(
11945
+ SSelect2,
11946
+ {
11947
+ options: rowsPerPageOption,
11948
+ value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11949
+ emitValue: true,
11950
+ onValueChange: (v) => changeRowsPerPage(Number(v)),
11951
+ width: 128
11952
+ }
11953
+ )
11954
+ ] })
11722
11955
  ]
11723
11956
  }
11724
11957
  )
@@ -12814,6 +13047,11 @@ function SCalendar({
12814
13047
  }
12815
13048
  );
12816
13049
  }
13050
+ var MODE_NOUN = {
13051
+ date: "\uB0A0\uC9DC",
13052
+ month: "\uC6D4",
13053
+ year: "\uC5F0\uB3C4"
13054
+ };
12817
13055
  var DATEPICKER_MODE_LIST_HEIGHT = 240;
12818
13056
  var DATEPICKER_MODE_ITEM_HEIGHT = 28;
12819
13057
  var YEAR_LIST_EDGE_BUFFER = 24;
@@ -13085,7 +13323,9 @@ function SDatePicker({
13085
13323
  placeholder,
13086
13324
  selectable,
13087
13325
  disabled = false,
13326
+ clearable = false,
13088
13327
  width,
13328
+ maxWidth,
13089
13329
  name,
13090
13330
  rules,
13091
13331
  status,
@@ -13128,7 +13368,8 @@ function SDatePicker({
13128
13368
  fieldMinW: 168,
13129
13369
  // 값 길이가 YYYY-MM-DD 로 정해져 있어, width="100%" 를 받아도 행 전체로
13130
13370
  // 늘어나지 않게 상한을 건다 (SKeyValueTable 이 모든 컨트롤에 100% 를 넘긴다).
13131
- // px 가 아니라 등급이라 --cmp-field-width-* 가 바뀌면 함께 따라간다
13371
+ // px 가 아니라 등급이라 --cmp-field-width-* 가 바뀌면 함께 따라간다.
13372
+ // 이 기본 상한이 걸리적거리는 자리(폭이 이미 좁게 정해진 팝오버 등)는 maxWidth prop 으로 덮는다
13132
13373
  fieldMaxW: "lg",
13133
13374
  fontSize: 14,
13134
13375
  lineHeight: 24,
@@ -13149,6 +13390,10 @@ function SDatePicker({
13149
13390
  if (rules && rules.length > 0) setRuleError(runRules(date, rules));
13150
13391
  setOpen(false);
13151
13392
  };
13393
+ const clear = () => {
13394
+ onValueChange?.(null);
13395
+ if (rules && rules.length > 0) setRuleError(runRules(null, rules));
13396
+ };
13152
13397
  const selectListboxValue = (nextValue) => {
13153
13398
  onValueChange?.(nextValue);
13154
13399
  if (rules && rules.length > 0) setRuleError(runRules(nextValue, rules));
@@ -13172,20 +13417,31 @@ function SDatePicker({
13172
13417
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
13173
13418
  width,
13174
13419
  minWidth: dims.fieldMinW,
13175
- maxWidth: dims.fieldMaxW,
13420
+ maxWidth: maxWidth ?? dims.fieldMaxW,
13176
13421
  disabled,
13177
13422
  className,
13178
13423
  style,
13179
13424
  children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: (o) => !disabled && setOpen(o), children: [
13180
13425
  /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
13181
- "button",
13426
+ "div",
13182
13427
  {
13183
- type: "button",
13184
- name,
13185
- disabled,
13428
+ role: "button",
13429
+ "aria-disabled": disabled,
13430
+ tabIndex: disabled ? -1 : 0,
13431
+ "data-name": name,
13186
13432
  onFocus: () => setFocused(true),
13187
13433
  onBlur: () => setFocused(false),
13188
- className: "flex h-full w-full items-center overflow-hidden bg-transparent outline-none disabled:cursor-not-allowed",
13434
+ onKeyDown: (event) => {
13435
+ if (disabled) return;
13436
+ if (event.key === "Enter" || event.key === " ") {
13437
+ event.preventDefault();
13438
+ setOpen(!open);
13439
+ }
13440
+ },
13441
+ className: cn(
13442
+ "flex h-full w-full items-center overflow-hidden bg-transparent outline-none",
13443
+ disabled ? "cursor-not-allowed" : "cursor-pointer"
13444
+ ),
13189
13445
  style: { padding: `0 ${dims.padX}px`, gap: dims.gap, borderRadius: dims.radius },
13190
13446
  children: [
13191
13447
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -13209,6 +13465,18 @@ function SDatePicker({
13209
13465
  },
13210
13466
  children: hasValue ? value : resolvedPlaceholder
13211
13467
  }
13468
+ ),
13469
+ clearable && hasValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
13470
+ SGhostButton,
13471
+ {
13472
+ icon: "close",
13473
+ size: "xxs",
13474
+ ariaLabel: `${MODE_NOUN[mode]} \uC9C0\uC6B0\uAE30`,
13475
+ onClick: (event) => {
13476
+ event.stopPropagation();
13477
+ clear();
13478
+ }
13479
+ }
13212
13480
  )
13213
13481
  ]
13214
13482
  }
@@ -13588,6 +13856,7 @@ function SDateRangePicker({
13588
13856
  maxRange,
13589
13857
  useTimePicker = false,
13590
13858
  disabled = false,
13859
+ clearable = false,
13591
13860
  width,
13592
13861
  name,
13593
13862
  rules,
@@ -13655,6 +13924,11 @@ function SDateRangePicker({
13655
13924
  if (rules && rules.length > 0) setRuleError(runRules(r, rules));
13656
13925
  if (!useTimePicker) setOpen(false);
13657
13926
  };
13927
+ const clear = () => {
13928
+ setPendingStart(null);
13929
+ onValueChange?.(null);
13930
+ if (rules && rules.length > 0) setRuleError(runRules(null, rules));
13931
+ };
13658
13932
  const resolvedPlaceholder = useTimePicker && placeholder === "YYYY-MM-DD ~ YYYY-MM-DD" ? "YYYY-MM-DD HH:mm ~ YYYY-MM-DD HH:mm" : placeholder;
13659
13933
  return /* @__PURE__ */ jsxRuntime.jsx(
13660
13934
  SField,
@@ -13690,12 +13964,23 @@ function SDateRangePicker({
13690
13964
  },
13691
13965
  children: [
13692
13966
  /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
13693
- "button",
13967
+ "div",
13694
13968
  {
13695
- type: "button",
13696
- name,
13697
- disabled,
13698
- className: "flex h-full w-full items-center overflow-hidden bg-transparent outline-none disabled:cursor-not-allowed",
13969
+ role: "button",
13970
+ "aria-disabled": disabled,
13971
+ tabIndex: disabled ? -1 : 0,
13972
+ "data-name": name,
13973
+ onKeyDown: (event) => {
13974
+ if (disabled) return;
13975
+ if (event.key === "Enter" || event.key === " ") {
13976
+ event.preventDefault();
13977
+ setOpen(!open);
13978
+ }
13979
+ },
13980
+ className: cn(
13981
+ "flex h-full w-full items-center overflow-hidden bg-transparent outline-none",
13982
+ disabled ? "cursor-not-allowed" : "cursor-pointer"
13983
+ ),
13699
13984
  style: { padding: `0 ${dims.padX}px`, gap: dims.gap, borderRadius: dims.radius },
13700
13985
  children: [
13701
13986
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -13726,6 +14011,18 @@ function SDateRangePicker({
13726
14011
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: endText })
13727
14012
  ] }) : resolvedPlaceholder
13728
14013
  }
14014
+ ),
14015
+ clearable && hasDisplay && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
14016
+ SGhostButton,
14017
+ {
14018
+ icon: "close",
14019
+ size: "xxs",
14020
+ ariaLabel: "\uAE30\uAC04 \uC9C0\uC6B0\uAE30",
14021
+ onClick: (event) => {
14022
+ event.stopPropagation();
14023
+ clear();
14024
+ }
14025
+ }
13729
14026
  )
13730
14027
  ]
13731
14028
  }
@@ -14990,6 +15287,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14990
15287
  );
14991
15288
  const [openKey, setOpenKey] = react.useState(null);
14992
15289
  const [addOpen, setAddOpen] = react.useState(false);
15290
+ const pendingOpenKeyRef = react.useRef(null);
14993
15291
  const triggerRefs = react.useRef(/* @__PURE__ */ new Map());
14994
15292
  const initializedDefaultsRef = react.useRef(/* @__PURE__ */ new Set());
14995
15293
  const prevOpenKeyRef = react.useRef(null);
@@ -15065,6 +15363,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
15065
15363
  const hasAddButton = fields.some(isAddableField);
15066
15364
  const hasRightActions = hasAddButton || showReset;
15067
15365
  const currentOpenField = fields.find((field) => field.key === openKey);
15366
+ const menuOpen = openKey != null || addOpen;
15068
15367
  const editorPopoverStyle = react.useMemo(() => {
15069
15368
  if (currentOpenField?.type === "keyword") {
15070
15369
  return { width: CHIPFILTER_LISTBOX_WIDTH };
@@ -15123,7 +15422,13 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
15123
15422
  onAddFilter?.(key);
15124
15423
  setAddOpen(false);
15125
15424
  const field = fields.find((f) => f.key === key);
15126
- if (field && !isInlineField(field)) requestAnimationFrame(() => setOpenKey(key));
15425
+ if (field && !isInlineField(field)) {
15426
+ pendingOpenKeyRef.current = key;
15427
+ requestAnimationFrame(() => {
15428
+ triggerRefs.current.get(key)?.focus();
15429
+ setOpenKey(key);
15430
+ });
15431
+ }
15127
15432
  };
15128
15433
  const rootStyle = react.useMemo(
15129
15434
  () => ({
@@ -15166,6 +15471,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
15166
15471
  {
15167
15472
  message,
15168
15473
  open: invalidGroups.has(runGroupIndex),
15474
+ placement: menuOpen ? "top" : "bottom",
15169
15475
  children: runBuffer
15170
15476
  },
15171
15477
  `group-${runGroupIndex}`
@@ -15179,14 +15485,16 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
15179
15485
  activeFields.forEach((field, index) => {
15180
15486
  const inline = isInlineField(field);
15181
15487
  const groupIndex = groupIndexByKey.get(field.key) ?? 0;
15182
- const warning = field.warning || invalidGroups.has(groupIndex);
15488
+ const warning = invalidGroups.has(groupIndex);
15183
15489
  const prevField = index > 0 ? activeFields[index - 1] : null;
15184
15490
  const prevGroupIndex = prevField ? groupIndexByKey.get(prevField.key) ?? 0 : null;
15185
15491
  const needsLeadingDivider = index === 0 ? false : inline || prevField != null && isInlineField(prevField) || groupIndex !== prevGroupIndex && groups[groupIndex]?.divider === true;
15492
+ const hoistDivider = needsLeadingDivider && groupIndex !== runGroupIndex;
15186
15493
  if (groupIndex !== runGroupIndex) {
15187
15494
  flushRun();
15188
15495
  runGroupIndex = groupIndex;
15189
15496
  }
15497
+ if (hoistDivider) nodes.push(/* @__PURE__ */ jsxRuntime.jsx(Divider, { inset: true }, `${field.key}-divider`));
15190
15498
  const addable = isAddableField(field);
15191
15499
  const showClear = addable || !isAtDefaultValue(field, values[field.key]);
15192
15500
  const handleClear = () => {
@@ -15250,7 +15558,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
15250
15558
  field.key
15251
15559
  );
15252
15560
  runBuffer.push(
15253
- needsLeadingDivider ? /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
15561
+ needsLeadingDivider && !hoistDivider ? /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
15254
15562
  /* @__PURE__ */ jsxRuntime.jsx(Divider, { inset: true }),
15255
15563
  node
15256
15564
  ] }, `${field.key}-wrap`) : node
@@ -15298,6 +15606,11 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
15298
15606
  side: "bottom",
15299
15607
  align: "start",
15300
15608
  sideOffset: 4,
15609
+ onCloseAutoFocus: (event) => {
15610
+ if (pendingOpenKeyRef.current == null) return;
15611
+ pendingOpenKeyRef.current = null;
15612
+ event.preventDefault();
15613
+ },
15301
15614
  className: cn("z-50 min-w-[180px]", FLOATING_SLIDE_ANIM),
15302
15615
  children: /* @__PURE__ */ jsxRuntime.jsx(ListBox, { children: inactiveFields.length > 0 ? inactiveFields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(MenuItem, { onClick: () => addField(field.key), children: field.label }, field.key)) : /* @__PURE__ */ jsxRuntime.jsx(EmptyItem, { children: "\uCD94\uAC00\uD560 \uD544\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }) })
15303
15616
  }
@@ -15348,6 +15661,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
15348
15661
  value: values[currentOpenField.key],
15349
15662
  disabled: disabled || currentOpenField.disabled,
15350
15663
  onValueChange: (next) => emitValue(currentOpenField.key, next),
15664
+ onValueCommit: (next) => emitValueAndSearch(currentOpenField.key, next),
15351
15665
  onClose: () => setOpenKey(null)
15352
15666
  }
15353
15667
  )
@@ -15375,9 +15689,20 @@ function Divider({ inset, top } = {}) {
15375
15689
  function GroupTooltip({
15376
15690
  message,
15377
15691
  open,
15692
+ placement,
15378
15693
  children
15379
15694
  }) {
15380
- return /* @__PURE__ */ jsxRuntime.jsx(STooltip2, { trigger: "none", open, placement: "top", tooltipType: "warning", message: [message], children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-[var(--cmp-chipFilter-chip-group)]", children }) });
15695
+ return /* @__PURE__ */ jsxRuntime.jsx(
15696
+ STooltip2,
15697
+ {
15698
+ trigger: "none",
15699
+ open,
15700
+ placement,
15701
+ tooltipType: "warning",
15702
+ message: [message],
15703
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-[var(--cmp-chipFilter-chip-group)]", children })
15704
+ }
15705
+ );
15381
15706
  }
15382
15707
  function FilterChip({
15383
15708
  field,
@@ -15487,6 +15812,7 @@ function EditorPanel({
15487
15812
  value,
15488
15813
  disabled,
15489
15814
  onValueChange,
15815
+ onValueCommit,
15490
15816
  onClose
15491
15817
  }) {
15492
15818
  const wrapsInPaddedTop = field.type === "date" || field.type === "period";
@@ -15506,7 +15832,8 @@ function EditorPanel({
15506
15832
  field,
15507
15833
  value,
15508
15834
  disabled,
15509
- onValueChange
15835
+ onValueChange,
15836
+ onValueCommit
15510
15837
  }
15511
15838
  ) }) : /* @__PURE__ */ jsxRuntime.jsx(
15512
15839
  FilterEditor,
@@ -15514,7 +15841,8 @@ function EditorPanel({
15514
15841
  field,
15515
15842
  value,
15516
15843
  disabled,
15517
- onValueChange
15844
+ onValueChange,
15845
+ onValueCommit
15518
15846
  }
15519
15847
  ),
15520
15848
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -15566,7 +15894,8 @@ function FilterEditor({
15566
15894
  field,
15567
15895
  value,
15568
15896
  disabled,
15569
- onValueChange
15897
+ onValueChange,
15898
+ onValueCommit
15570
15899
  }) {
15571
15900
  if (field.type === "period") {
15572
15901
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -15597,7 +15926,8 @@ function FilterEditor({
15597
15926
  field,
15598
15927
  value: isKeywordValue(value) ? value : keywordEntries(value),
15599
15928
  disabled,
15600
- onValueChange
15929
+ onValueChange,
15930
+ onValueCommit
15601
15931
  }
15602
15932
  );
15603
15933
  }
@@ -15639,7 +15969,9 @@ function DatePresetEditor({
15639
15969
  onValueChange,
15640
15970
  selectable: field.selectable,
15641
15971
  disabled,
15642
- placeholder: field.placeholder
15972
+ placeholder: field.placeholder,
15973
+ width: "100%",
15974
+ maxWidth: "100%"
15643
15975
  }
15644
15976
  );
15645
15977
  }
@@ -15699,7 +16031,8 @@ function PeriodPresetEditor({
15699
16031
  onValueChange: updateInputValue,
15700
16032
  selectable: field.selectable,
15701
16033
  disabled,
15702
- width: "100%"
16034
+ width: "100%",
16035
+ maxWidth: "100%"
15703
16036
  }
15704
16037
  );
15705
16038
  }
@@ -15712,19 +16045,18 @@ function PeriodPresetEditor({
15712
16045
  onValueChange: updateInputValue,
15713
16046
  selectable: field.selectable,
15714
16047
  disabled,
15715
- width: "100%"
16048
+ width: "100%",
16049
+ maxWidth: "100%"
15716
16050
  }
15717
16051
  );
15718
16052
  }
15719
16053
  if (selectedUnit === "quarter") {
15720
16054
  return /* @__PURE__ */ jsxRuntime.jsx(
15721
- SSelect2,
16055
+ SRadioButton,
15722
16056
  {
15723
16057
  value: typeof periodValue.value === "number" ? periodValue.value : 1,
15724
16058
  onValueChange: updateInputValue,
15725
- emitValue: true,
15726
16059
  disabled,
15727
- width: "100%",
15728
16060
  options: [
15729
16061
  { value: 1, label: "1\uBD84\uAE30" },
15730
16062
  { value: 2, label: "2\uBD84\uAE30" },
@@ -15736,13 +16068,11 @@ function PeriodPresetEditor({
15736
16068
  }
15737
16069
  if (selectedUnit === "half") {
15738
16070
  return /* @__PURE__ */ jsxRuntime.jsx(
15739
- SSelect2,
16071
+ SRadioButton,
15740
16072
  {
15741
16073
  value: typeof periodValue.value === "string" ? periodValue.value : "first",
15742
16074
  onValueChange: updateInputValue,
15743
- emitValue: true,
15744
16075
  disabled,
15745
- width: "100%",
15746
16076
  options: [
15747
16077
  { value: "first", label: "\uC0C1\uBC18\uAE30" },
15748
16078
  { value: "second", label: "\uD558\uBC18\uAE30" }
@@ -15759,7 +16089,8 @@ function PeriodPresetEditor({
15759
16089
  onValueChange: updateInputValue,
15760
16090
  selectable: field.selectable,
15761
16091
  disabled,
15762
- width: "100%"
16092
+ width: "100%",
16093
+ maxWidth: "100%"
15763
16094
  }
15764
16095
  );
15765
16096
  }
@@ -15787,7 +16118,6 @@ function PeriodPresetEditor({
15787
16118
  },
15788
16119
  preset.value
15789
16120
  )) }),
15790
- /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "h-px w-full", style: { background: CHIPFILTER.listboxBorder } }),
15791
16121
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full [&>*]:w-full", children: renderInput() })
15792
16122
  ] });
15793
16123
  }
@@ -15909,6 +16239,7 @@ function InlineDatePresetFilter({
15909
16239
  ) : badge)
15910
16240
  ] });
15911
16241
  }
16242
+ var CSV_PASTE_SEPARATOR = /[,\r\n\t]+/;
15912
16243
  var MATCH_MODE_OPTIONS = [
15913
16244
  { value: "contains", label: "\uD3EC\uD568" },
15914
16245
  { value: "exact", label: "\uC77C\uCE58" }
@@ -15921,20 +16252,61 @@ function KeywordEditor({
15921
16252
  field,
15922
16253
  value,
15923
16254
  disabled,
15924
- onValueChange
16255
+ onValueChange,
16256
+ onValueCommit
15925
16257
  }) {
15926
16258
  const entries3 = keywordEntries(value);
15927
16259
  const [draft, setDraft] = react.useState("");
15928
16260
  const [mode, setMode] = react.useState(
15929
16261
  isKeywordValue(value) ? value.mode : "contains"
15930
16262
  );
15931
- const emit3 = (nextEntries) => {
15932
- onValueChange(field.matchModes ? { keywords: nextEntries, mode } : nextEntries);
16263
+ const inputRef = react.useRef(null);
16264
+ react.useEffect(() => {
16265
+ if (disabled) return;
16266
+ inputRef.current?.focus();
16267
+ }, [disabled]);
16268
+ const emit3 = (nextEntries, nextMode = mode) => {
16269
+ onValueChange(field.matchModes ? { keywords: nextEntries, mode: nextMode } : nextEntries);
15933
16270
  };
15934
- const addEntry = (raw) => {
16271
+ const commit = (nextEntries) => {
16272
+ onValueCommit(field.matchModes ? { keywords: nextEntries, mode } : nextEntries);
16273
+ };
16274
+ const changeMode = (next) => {
16275
+ setMode(next);
16276
+ emit3(entries3, next);
16277
+ };
16278
+ const newEntries = (candidates) => candidates.reduce((acc, raw) => {
15935
16279
  const next = raw.trim();
15936
- if (next === "" || entries3.includes(next)) return;
15937
- emit3([...entries3, next]);
16280
+ if (next === "" || entries3.includes(next) || acc.includes(next)) return acc;
16281
+ return [...acc, next];
16282
+ }, []);
16283
+ const addEntry = (raw) => {
16284
+ const added = newEntries([raw]);
16285
+ if (added.length === 0) {
16286
+ setDraft("");
16287
+ return;
16288
+ }
16289
+ commit([...entries3, ...added]);
16290
+ setDraft("");
16291
+ };
16292
+ const changeDraft = (next) => {
16293
+ if (field.input !== "csv" || !next.includes(",")) {
16294
+ setDraft(next);
16295
+ return;
16296
+ }
16297
+ const parts = next.split(",");
16298
+ const tail = parts.pop() ?? "";
16299
+ const added = newEntries(parts);
16300
+ if (added.length > 0) commit([...entries3, ...added]);
16301
+ setDraft(tail);
16302
+ };
16303
+ const onPaste = (event) => {
16304
+ if (field.input !== "csv") return;
16305
+ const text = event.clipboardData.getData("text");
16306
+ if (!CSV_PASTE_SEPARATOR.test(text)) return;
16307
+ event.preventDefault();
16308
+ const added = newEntries((draft + text).split(CSV_PASTE_SEPARATOR));
16309
+ if (added.length > 0) commit([...entries3, ...added]);
15938
16310
  setDraft("");
15939
16311
  };
15940
16312
  const removeEntry = (entry) => {
@@ -15966,7 +16338,7 @@ function KeywordEditor({
15966
16338
  options: field.excludeMode ? [...MATCH_MODE_OPTIONS, EXCLUDE_MATCH_MODE_OPTION] : MATCH_MODE_OPTIONS,
15967
16339
  value: mode,
15968
16340
  disabled,
15969
- onValueChange: (next) => setMode(next)
16341
+ onValueChange: (next) => changeMode(next)
15970
16342
  }
15971
16343
  )
15972
16344
  ] }),
@@ -15975,10 +16347,12 @@ function KeywordEditor({
15975
16347
  /* @__PURE__ */ jsxRuntime.jsx(
15976
16348
  "input",
15977
16349
  {
16350
+ ref: inputRef,
15978
16351
  value: draft,
15979
16352
  disabled,
15980
- placeholder: field.placeholder ?? "\uC785\uB825 \uD6C4 Enter\uB85C \uCD94\uAC00",
15981
- onChange: (event) => setDraft(event.target.value),
16353
+ placeholder: field.placeholder ?? (field.input === "csv" ? "\uB2E4\uC911 \uD0A4\uC6CC\uB4DC\uB294 \uCF64\uB9C8(,)\uB85C \uAD6C\uBD84" : "\uC785\uB825 \uD6C4 Enter\uB85C \uCD94\uAC00"),
16354
+ onChange: (event) => changeDraft(event.target.value),
16355
+ onPaste,
15982
16356
  onKeyDown,
15983
16357
  className: "min-w-0 flex-1 border-none bg-transparent text-[12px] leading-[20px] text-[color:var(--sys-color-field-text-default)] outline-none placeholder:text-[color:var(--sys-color-field-text-placeholder)] disabled:cursor-not-allowed"
15984
16358
  }