sellmate-design-system-react 9.0.0-beta.76 → 9.0.0-beta.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -15587,6 +15587,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
15587
15587
  name,
15588
15588
  options = [],
15589
15589
  type = "default",
15590
+ valueDisplay = "text",
15590
15591
  showSearch = false,
15591
15592
  showSelectAll = false,
15592
15593
  allSelectedLabel = "\uC804\uCCB4",
@@ -15619,6 +15620,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
15619
15620
  hint,
15620
15621
  errorMessage,
15621
15622
  width,
15623
+ size = "sm",
15622
15624
  className,
15623
15625
  style
15624
15626
  }, ref) {
@@ -15923,6 +15925,16 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
15923
15925
  }
15924
15926
  return labelByValue.get(selected[0]) ?? valueOptions.get(selected[0])?.label ?? selected[0];
15925
15927
  })();
15928
+ const showChips = valueDisplay === "chip" && multi && selected.length > 0 && !allSelected;
15929
+ const disabledValues = useMemo(
15930
+ () => new Set(flat.filter((o) => !o.isGroup && o.disabled).map((o) => o.value)),
15931
+ [flat]
15932
+ );
15933
+ const chips = showChips ? selected.map((v) => ({
15934
+ value: v,
15935
+ label: labelByValue.get(v) ?? valueOptions.get(v)?.label ?? v,
15936
+ removable: !effectiveDisabled && !disabledValues.has(v)
15937
+ })) : [];
15926
15938
  const isPlaceholder = selected.length === 0;
15927
15939
  const showTriggerSpinner = loading2 && !effectiveDisabled;
15928
15940
  const showClear = clearable && !effectiveDisabled && !isPlaceholder;
@@ -15930,6 +15942,12 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
15930
15942
  emit3([]);
15931
15943
  if (rules && rules.length > 0) setRuleError(runRules(multi ? [] : "", rules));
15932
15944
  };
15945
+ const handleRemoveChip = (target) => {
15946
+ const next = selected.filter((v) => v !== target);
15947
+ emit3(next);
15948
+ if (rules && rules.length > 0) setRuleError(runRules(next, rules));
15949
+ triggerRef.current?.focus();
15950
+ };
15933
15951
  const handleSelect = (nextValue, index, extend = false) => {
15934
15952
  if (multi) {
15935
15953
  const anchor = rangeAnchorRef.current;
@@ -16085,6 +16103,8 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
16085
16103
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16086
16104
  disabled: effectiveDisabled,
16087
16105
  width,
16106
+ size,
16107
+ multiline: showChips,
16088
16108
  className,
16089
16109
  style,
16090
16110
  children: [
@@ -16102,55 +16122,59 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
16102
16122
  "aria-busy": showTriggerSpinner || void 0,
16103
16123
  onClick: () => requestOpen(!open),
16104
16124
  onKeyDown: handleKeyDown,
16105
- className: "flex h-full w-full cursor-pointer items-center justify-between gap-[8px] bg-transparent px-[12px] text-left text-[12px] leading-[20px] outline-none disabled:cursor-not-allowed",
16125
+ className: cn(
16126
+ "flex h-full w-full cursor-pointer items-center justify-between bg-transparent text-left outline-none disabled:cursor-not-allowed",
16127
+ size === "md" ? "typo-body-md-default gap-(--cmp-select-md-gap) px-(--cmp-select-md-paddingX)" : "typo-body-sm-default gap-(--cmp-select-sm-gap) px-(--cmp-select-sm-paddingX)"
16128
+ ),
16106
16129
  style: {
16130
+ // 칩 한 줄이 기본 높이 안에서 세로 가운데에 오는 여백. 줄이 늘면 같은 여백을 유지한 채 트리거가 커진다.
16131
+ // sm 칩은 높이가 라벨 줄높이(typo-body-xs-medium)라 남는 높이가 4px 로 떨어진다.
16132
+ paddingBlock: showChips ? size === "md" ? "calc((var(--cmp-select-md-height) - var(--cmp-select-md-borderWidth) * 2 - var(--cmp-chip-height)) / 2)" : "var(--spacing-sd-4)" : void 0,
16107
16133
  color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
16108
16134
  },
16109
16135
  children: [
16110
- isHtmlLabel(triggerText) ? /* @__PURE__ */ jsx(
16111
- "span",
16112
- {
16113
- className: "truncate",
16114
- dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(triggerText) }
16115
- }
16116
- ) : /* @__PURE__ */ jsx("span", { className: "truncate", children: triggerText }),
16117
- /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-[8px]", children: [
16118
- showClear && /* @__PURE__ */ jsxs(
16136
+ showChips ? (
16137
+ // md 는 SChip 과 같은 모양(높이 --cmp-chip-height)이다. SChip 을 그대로 넣지 않는 것은
16138
+ // SChip 의 × 가 <button> 이라 트리거 button 안에 중첩할 수 없어서다 — 모양만 같은 토큰으로 맞춘다.
16139
+ // sm 은 28px 트리거에 들어가도록 낮춘 칩 — 높이를 주지 않고 라벨 줄높이(typo-body-xs-medium)가 정한다.
16140
+ // 두 크기 모두 패딩·라벨↔× 간격은 chip 토큰 그대로.
16141
+ /* @__PURE__ */ jsx(
16119
16142
  "span",
16120
16143
  {
16121
- role: "button",
16122
- tabIndex: 0,
16123
- "aria-label": "\uC120\uD0DD \uC9C0\uC6B0\uAE30",
16124
- className: "group/select-clear relative inline-flex shrink-0 cursor-pointer items-center justify-center",
16125
- style: {
16126
- width: "var(--cmp-ghostButton-xxs-size)",
16127
- height: "var(--cmp-ghostButton-xxs-size)",
16128
- borderRadius: "var(--cmp-ghostButton-radius-sm)"
16129
- },
16130
- onClick: (event) => {
16131
- event.preventDefault();
16132
- event.stopPropagation();
16133
- handleClear();
16134
- },
16135
- onKeyDown: (event) => {
16136
- if (event.key !== "Enter" && event.key !== " ") return;
16137
- event.preventDefault();
16138
- event.stopPropagation();
16139
- handleClear();
16140
- },
16141
- children: [
16142
- /* @__PURE__ */ jsx(
16143
- "span",
16144
- {
16145
- "aria-hidden": true,
16146
- className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover/select-clear:opacity-5",
16147
- style: { background: "var(--cmp-ghostButton-default-hover-bg)" }
16148
- }
16149
- ),
16150
- /* @__PURE__ */ jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
16151
- ]
16144
+ className: cn(
16145
+ "flex min-w-0 flex-1 flex-wrap items-center",
16146
+ size === "md" ? "gap-(--cmp-chipInput-chip-gap)" : "gap-sd-6"
16147
+ ),
16148
+ children: chips.map((chip) => /* @__PURE__ */ jsxs(
16149
+ "span",
16150
+ {
16151
+ className: cn(
16152
+ "box-border inline-flex min-w-0 max-w-full items-center gap-(--cmp-chip-gap) overflow-hidden whitespace-nowrap px-(--cmp-chip-paddingX)",
16153
+ size === "md" ? "h-(--cmp-chip-height) text-[12px] font-medium leading-[20px]" : "typo-body-xs-medium"
16154
+ ),
16155
+ style: {
16156
+ borderRadius: "var(--cmp-chip-radius)",
16157
+ background: chip.removable ? "var(--cmp-chip-bg-default)" : "var(--cmp-chip-bg-disabled)",
16158
+ color: chip.removable ? "var(--cmp-chip-content-default)" : "var(--cmp-chip-content-disabled)"
16159
+ },
16160
+ children: [
16161
+ /* @__PURE__ */ jsx(TriggerLabel, { label: chip.label }),
16162
+ chip.removable && /* @__PURE__ */ jsx(
16163
+ TriggerRemoveButton,
16164
+ {
16165
+ ariaLabel: `${isHtmlLabel(chip.label) ? extractText(chip.label) : chip.label} \uC120\uD0DD \uD574\uC81C`,
16166
+ onRemove: () => handleRemoveChip(chip.value)
16167
+ }
16168
+ )
16169
+ ]
16170
+ },
16171
+ chip.value
16172
+ ))
16152
16173
  }
16153
- ),
16174
+ )
16175
+ ) : /* @__PURE__ */ jsx(TriggerLabel, { label: triggerText }),
16176
+ /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-[8px]", children: [
16177
+ showClear && /* @__PURE__ */ jsx(TriggerRemoveButton, { ariaLabel: "\uC120\uD0DD \uC9C0\uC6B0\uAE30", onRemove: handleClear }),
16154
16178
  showTriggerSpinner ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "inline-flex shrink-0", "data-select-trigger-spinner": true, children: /* @__PURE__ */ jsx(SCircleProgress, { indeterminate: true, type: "neutral", size: 12 }) }) : /* @__PURE__ */ jsx(
16155
16179
  SIcon,
16156
16180
  {
@@ -16482,6 +16506,47 @@ function ItemList({
16482
16506
  bottomSpacer > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, style: { height: bottomSpacer } })
16483
16507
  ] });
16484
16508
  }
16509
+ function TriggerLabel({ label }) {
16510
+ return isHtmlLabel(label) ? /* @__PURE__ */ jsx("span", { className: "truncate", dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(label) } }) : /* @__PURE__ */ jsx("span", { className: "truncate", children: label });
16511
+ }
16512
+ function TriggerRemoveButton({ ariaLabel, onRemove }) {
16513
+ return /* @__PURE__ */ jsxs(
16514
+ "span",
16515
+ {
16516
+ role: "button",
16517
+ tabIndex: 0,
16518
+ "aria-label": ariaLabel,
16519
+ className: "group/select-remove relative inline-flex shrink-0 cursor-pointer items-center justify-center",
16520
+ style: {
16521
+ width: "var(--cmp-ghostButton-xxs-size)",
16522
+ height: "var(--cmp-ghostButton-xxs-size)",
16523
+ borderRadius: "var(--cmp-ghostButton-radius-sm)"
16524
+ },
16525
+ onClick: (event) => {
16526
+ event.preventDefault();
16527
+ event.stopPropagation();
16528
+ onRemove();
16529
+ },
16530
+ onKeyDown: (event) => {
16531
+ if (event.key !== "Enter" && event.key !== " ") return;
16532
+ event.preventDefault();
16533
+ event.stopPropagation();
16534
+ onRemove();
16535
+ },
16536
+ children: [
16537
+ /* @__PURE__ */ jsx(
16538
+ "span",
16539
+ {
16540
+ "aria-hidden": true,
16541
+ className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover/select-remove:opacity-5",
16542
+ style: { background: "var(--cmp-ghostButton-default-hover-bg)" }
16543
+ }
16544
+ ),
16545
+ /* @__PURE__ */ jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
16546
+ ]
16547
+ }
16548
+ );
16549
+ }
16485
16550
  function CheckboxBox({
16486
16551
  state: state2,
16487
16552
  disabled = false,
@@ -16586,6 +16651,7 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
16586
16651
  onVirtualReachEnd,
16587
16652
  tableId,
16588
16653
  onRowClick,
16654
+ onRowDoubleClick,
16589
16655
  className,
16590
16656
  style
16591
16657
  }, ref) {
@@ -17438,13 +17504,18 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
17438
17504
  {
17439
17505
  "data-row-index": rowIndex,
17440
17506
  onClick: () => onRowClick?.(row),
17507
+ onDoubleClick: onRowDoubleClick ? (event) => {
17508
+ const target = event.target;
17509
+ if (target.closest(ROW_INTERACTIVE_SELECTOR)) return;
17510
+ onRowDoubleClick(row);
17511
+ } : void 0,
17441
17512
  onMouseDown: dragSelectOn ? (event) => startDragSelect(rowIndex, event) : void 0,
17442
17513
  onMouseEnter: dragSelectOn ? () => extendDragSelect(rowIndex) : void 0,
17443
17514
  onContextMenu: contextMenuOn ? (event) => openContextMenu(row, event) : void 0,
17444
17515
  className: cn(
17445
17516
  "group/table-row",
17446
17517
  rowHover && "hover:bg-[var(--color-grey-05)]",
17447
- onRowClick && "cursor-pointer",
17518
+ (onRowClick || onRowDoubleClick) && "cursor-pointer",
17448
17519
  dragSelectOn && "select-none"
17449
17520
  ),
17450
17521
  style: { height: virtualScroll ? effectiveRowHeight : rowH },
@@ -17467,6 +17538,7 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
17467
17538
  background: rowLocked ? LOCKED_BG : void 0
17468
17539
  },
17469
17540
  onClick: (e) => e.stopPropagation(),
17541
+ onDoubleClick: (e) => e.stopPropagation(),
17470
17542
  onClickCapture: (e) => {
17471
17543
  shiftPressedRef.current = e.shiftKey;
17472
17544
  if (e.shiftKey) window.getSelection()?.removeAllRanges();
@@ -22538,71 +22610,76 @@ function SRadioButton({
22538
22610
  }) {
22539
22611
  const s = SIZE_CONFIG2[size];
22540
22612
  const [hoveredValue, setHoveredValue] = useState(null);
22541
- return /* @__PURE__ */ jsx(
22542
- "div",
22543
- {
22544
- role: "radiogroup",
22545
- "aria-disabled": disabled,
22546
- className: cn("inline-flex", className),
22547
- style,
22548
- children: options.map((o, i) => {
22549
- const selected = o.value === value;
22550
- const od = disabled || o.disabled;
22551
- const first = i === 0;
22552
- const last = i === options.length - 1;
22553
- const tooltip = showTooltip && o.tooltip ? o.tooltip : void 0;
22554
- const button = /* @__PURE__ */ jsx(
22555
- "button",
22556
- {
22557
- type: "button",
22558
- role: "radio",
22559
- name,
22560
- "aria-checked": selected,
22561
- disabled: od,
22562
- onClick: () => !od && !selected && onValueChange?.(o.value),
22563
- onMouseEnter: tooltip ? () => setHoveredValue(o.value) : void 0,
22564
- onMouseLeave: tooltip ? () => setHoveredValue(null) : void 0,
22565
- onFocus: tooltip ? () => setHoveredValue(o.value) : void 0,
22566
- onBlur: tooltip ? () => setHoveredValue(null) : void 0,
22567
- className: cn(
22568
- "relative box-border grow inline-flex items-center justify-center whitespace-nowrap border border-solid font-medium transition-colors",
22569
- first ? "" : "-ml-px",
22570
- selected ? "z-10" : "",
22571
- !od && !selected && "hover:!bg-[var(--cmp-radioButton-bg-hover)] hover:!text-[color:var(--cmp-radioButton-content-hover)] hover:!border-[color:var(--cmp-radioButton-bg-hover)] hover:z-10",
22572
- od ? "cursor-not-allowed" : "cursor-pointer"
22573
- ),
22574
- style: {
22575
- height: `var(--cmp-radioButton-${size}-height)`,
22576
- padding: `0 var(--cmp-radioButton-${size}-paddingX)`,
22577
- fontSize: `${s.fontSize}px`,
22578
- lineHeight: `${s.lineHeight}px`,
22579
- borderRadius: [
22580
- first ? s.radius : "0",
22581
- last ? s.radius : "0",
22582
- last ? s.radius : "0",
22583
- first ? s.radius : "0"
22584
- ].join(" "),
22585
- background: od ? "var(--cmp-radioButton-bg-disabled)" : "var(--cmp-radioButton-bg-default)",
22586
- borderColor: od ? "var(--cmp-radioButton-border-disabled)" : selected ? "var(--cmp-radioButton-border-select)" : "var(--cmp-radioButton-border-default)",
22587
- color: od ? "var(--cmp-radioButton-content-disabled)" : selected ? "var(--cmp-radioButton-content-select)" : "var(--cmp-radioButton-content-default)"
22613
+ return (
22614
+ // isolate: 선택·hover 버튼의 z-10 은 겹친 테두리(-ml-px) 위로 올리기 위한 값이라 그룹 안에서
22615
+ // 끝나야 한다. 빼면 상위 컨텍스트까지 올라가 모달 안 floating 밴드(z-2)를 이겨, 위에서 연
22616
+ // SSelect listbox 위로 선택된 버튼만 뚫고 그려진다.
22617
+ /* @__PURE__ */ jsx(
22618
+ "div",
22619
+ {
22620
+ role: "radiogroup",
22621
+ "aria-disabled": disabled,
22622
+ className: cn("inline-flex isolate", className),
22623
+ style,
22624
+ children: options.map((o, i) => {
22625
+ const selected = o.value === value;
22626
+ const od = disabled || o.disabled;
22627
+ const first = i === 0;
22628
+ const last = i === options.length - 1;
22629
+ const tooltip = showTooltip && o.tooltip ? o.tooltip : void 0;
22630
+ const button = /* @__PURE__ */ jsx(
22631
+ "button",
22632
+ {
22633
+ type: "button",
22634
+ role: "radio",
22635
+ name,
22636
+ "aria-checked": selected,
22637
+ disabled: od,
22638
+ onClick: () => !od && !selected && onValueChange?.(o.value),
22639
+ onMouseEnter: tooltip ? () => setHoveredValue(o.value) : void 0,
22640
+ onMouseLeave: tooltip ? () => setHoveredValue(null) : void 0,
22641
+ onFocus: tooltip ? () => setHoveredValue(o.value) : void 0,
22642
+ onBlur: tooltip ? () => setHoveredValue(null) : void 0,
22643
+ className: cn(
22644
+ "relative box-border grow inline-flex items-center justify-center whitespace-nowrap border border-solid font-medium transition-colors",
22645
+ first ? "" : "-ml-px",
22646
+ selected ? "z-10" : "",
22647
+ !od && !selected && "hover:!bg-[var(--cmp-radioButton-bg-hover)] hover:!text-[color:var(--cmp-radioButton-content-hover)] hover:!border-[color:var(--cmp-radioButton-bg-hover)] hover:z-10",
22648
+ od ? "cursor-not-allowed" : "cursor-pointer"
22649
+ ),
22650
+ style: {
22651
+ height: `var(--cmp-radioButton-${size}-height)`,
22652
+ padding: `0 var(--cmp-radioButton-${size}-paddingX)`,
22653
+ fontSize: `${s.fontSize}px`,
22654
+ lineHeight: `${s.lineHeight}px`,
22655
+ borderRadius: [
22656
+ first ? s.radius : "0",
22657
+ last ? s.radius : "0",
22658
+ last ? s.radius : "0",
22659
+ first ? s.radius : "0"
22660
+ ].join(" "),
22661
+ background: od ? "var(--cmp-radioButton-bg-disabled)" : "var(--cmp-radioButton-bg-default)",
22662
+ borderColor: od ? "var(--cmp-radioButton-border-disabled)" : selected ? "var(--cmp-radioButton-border-select)" : "var(--cmp-radioButton-border-default)",
22663
+ color: od ? "var(--cmp-radioButton-content-disabled)" : selected ? "var(--cmp-radioButton-content-select)" : "var(--cmp-radioButton-content-default)"
22664
+ },
22665
+ children: o.label
22588
22666
  },
22589
- children: o.label
22590
- },
22591
- o.value
22592
- );
22593
- return tooltip ? /* @__PURE__ */ jsx(
22594
- STooltip2,
22595
- {
22596
- trigger: "none",
22597
- open: hoveredValue === o.value,
22598
- tooltipType: "accent",
22599
- message: [tooltip],
22600
- children: button
22601
- },
22602
- o.value
22603
- ) : button;
22604
- })
22605
- }
22667
+ o.value
22668
+ );
22669
+ return tooltip ? /* @__PURE__ */ jsx(
22670
+ STooltip2,
22671
+ {
22672
+ trigger: "none",
22673
+ open: hoveredValue === o.value,
22674
+ tooltipType: "accent",
22675
+ message: [tooltip],
22676
+ children: button
22677
+ },
22678
+ o.value
22679
+ ) : button;
22680
+ })
22681
+ }
22682
+ )
22606
22683
  );
22607
22684
  }
22608
22685
  function SRadioCard({