sellmate-design-system-react 0.8.3 → 0.8.4

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 (68) hide show
  1. package/dist/components/SBarcodeInput/SBarcodeInput.cjs +78 -2
  2. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  3. package/dist/components/SBarcodeInput/SBarcodeInput.js +78 -2
  4. package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
  5. package/dist/components/SChipInput/SChipInput.cjs +78 -2
  6. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  7. package/dist/components/SChipInput/SChipInput.js +78 -2
  8. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  9. package/dist/components/SDatePicker/SDatePicker.cjs +78 -2
  10. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  11. package/dist/components/SDatePicker/SDatePicker.js +78 -2
  12. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  13. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +78 -2
  14. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  15. package/dist/components/SDateRangePicker/SDateRangePicker.js +78 -2
  16. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  17. package/dist/components/SField/SField.cjs +78 -2
  18. package/dist/components/SField/SField.cjs.map +1 -1
  19. package/dist/components/SField/SField.js +78 -2
  20. package/dist/components/SField/SField.js.map +1 -1
  21. package/dist/components/SFilePicker/SFilePicker.cjs +78 -2
  22. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  23. package/dist/components/SFilePicker/SFilePicker.js +78 -2
  24. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  25. package/dist/components/SInput/SInput.cjs +78 -2
  26. package/dist/components/SInput/SInput.cjs.map +1 -1
  27. package/dist/components/SInput/SInput.js +78 -2
  28. package/dist/components/SInput/SInput.js.map +1 -1
  29. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +224 -118
  30. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  31. package/dist/components/SKeyValueTable/SKeyValueTable.js +224 -118
  32. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  33. package/dist/components/SNumberInput/SNumberInput.cjs +78 -2
  34. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  35. package/dist/components/SNumberInput/SNumberInput.js +78 -2
  36. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  37. package/dist/components/SSelect/SSelect.cjs +155 -49
  38. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  39. package/dist/components/SSelect/SSelect.js +155 -49
  40. package/dist/components/SSelect/SSelect.js.map +1 -1
  41. package/dist/components/SSelect/select.config.d.ts +7 -0
  42. package/dist/components/STable/STable.cjs +155 -49
  43. package/dist/components/STable/STable.cjs.map +1 -1
  44. package/dist/components/STable/STable.js +155 -49
  45. package/dist/components/STable/STable.js.map +1 -1
  46. package/dist/components/STextarea/STextarea.cjs +78 -2
  47. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  48. package/dist/components/STextarea/STextarea.js +78 -2
  49. package/dist/components/STextarea/STextarea.js.map +1 -1
  50. package/dist/components/STimePicker/STimePicker.cjs +78 -2
  51. package/dist/components/STimePicker/STimePicker.cjs.map +1 -1
  52. package/dist/components/STimePicker/STimePicker.js +78 -2
  53. package/dist/components/STimePicker/STimePicker.js.map +1 -1
  54. package/dist/components/STimeRangePicker/STimeRangePicker.cjs +78 -2
  55. package/dist/components/STimeRangePicker/STimeRangePicker.cjs.map +1 -1
  56. package/dist/components/STimeRangePicker/STimeRangePicker.js +78 -2
  57. package/dist/components/STimeRangePicker/STimeRangePicker.js.map +1 -1
  58. package/dist/components/STooltip/STooltip.cjs +78 -2
  59. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  60. package/dist/components/STooltip/STooltip.d.ts +1 -1
  61. package/dist/components/STooltip/STooltip.js +78 -2
  62. package/dist/components/STooltip/STooltip.js.map +1 -1
  63. package/dist/index.cjs +155 -49
  64. package/dist/index.cjs.map +1 -1
  65. package/dist/index.js +155 -49
  66. package/dist/index.js.map +1 -1
  67. package/dist/styles.css +4 -8
  68. package/package.json +1 -1
@@ -3443,7 +3443,14 @@ function useBody(content, message = []) {
3443
3443
  if (content != null) return content;
3444
3444
  if (message.length === 0) return null;
3445
3445
  const singleLine = message.length === 1;
3446
- return /* @__PURE__ */ jsx(Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsx("div", { className: singleLine ? "leading-[24px]" : void 0, children: line }, i)) });
3446
+ return /* @__PURE__ */ jsx(Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsx(
3447
+ "div",
3448
+ {
3449
+ className: singleLine ? "leading-[24px]" : void 0,
3450
+ dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(line) }
3451
+ },
3452
+ i
3453
+ )) });
3447
3454
  }
3448
3455
  var STooltip = forwardRef(function STooltip2({
3449
3456
  content,
@@ -3501,7 +3508,7 @@ var STooltip = forwardRef(function STooltip2({
3501
3508
  if (disabled || body == null) return /* @__PURE__ */ jsx(Fragment, { children: triggerNode });
3502
3509
  const isHover = trigger === "hover";
3503
3510
  const contentClass = cn(
3504
- "z-50 max-w-xs rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px]",
3511
+ "z-50 w-fit whitespace-nowrap rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px]",
3505
3512
  FLOATING_SLIDE_ANIM,
3506
3513
  className
3507
3514
  );
@@ -3852,6 +3859,16 @@ function flattenOptions(options, depth = 1) {
3852
3859
  return out;
3853
3860
  }
3854
3861
  var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
3862
+ function collectGroupLeafValues(flat, groupIndex) {
3863
+ const group = flat[groupIndex];
3864
+ if (group == null || !group.isGroup) return [];
3865
+ const values = [];
3866
+ for (let i = groupIndex + 1; i < flat.length; i++) {
3867
+ if (flat[i].depth <= group.depth) break;
3868
+ if (!flat[i].isGroup && !flat[i].disabled) values.push(flat[i].value);
3869
+ }
3870
+ return values;
3871
+ }
3855
3872
  var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
3856
3873
  var extractText = (html) => html.replace(/<[^>]*>/g, "");
3857
3874
  var SEARCH_THRESHOLD = 11;
@@ -3977,16 +3994,31 @@ var SSelect = forwardRef(function SSelect2({
3977
3994
  const allValues = selectable.map((o) => o.value);
3978
3995
  const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
3979
3996
  const hasItems = filtered.some((o) => !o.isGroup);
3997
+ const groupLeafValues = useMemo(() => {
3998
+ const map = /* @__PURE__ */ new Map();
3999
+ filtered.forEach((o, i) => {
4000
+ if (o.isGroup) map.set(i, collectGroupLeafValues(filtered, i));
4001
+ });
4002
+ return map;
4003
+ }, [filtered]);
3980
4004
  const navigableItems = useMemo(() => {
3981
4005
  const items = [];
3982
4006
  if (multi && useSelectAll && allValues.length > 0) {
3983
- items.push({ value: "__select_all__", isSelectAll: true });
4007
+ items.push({ key: "__select_all__", isSelectAll: true });
3984
4008
  }
3985
- filtered.forEach((o) => {
3986
- if (!o.isGroup && !o.disabled) items.push({ value: o.value });
4009
+ filtered.forEach((o, i) => {
4010
+ if (o.disabled) return;
4011
+ if (o.isGroup) {
4012
+ if (!multi) return;
4013
+ const leafValues = groupLeafValues.get(i) ?? [];
4014
+ if (leafValues.length === 0) return;
4015
+ items.push({ key: `__group_${i}__`, isGroup: true, leafValues });
4016
+ } else {
4017
+ items.push({ key: o.value, value: o.value });
4018
+ }
3987
4019
  });
3988
4020
  return items;
3989
- }, [allValues.length, filtered, multi, useSelectAll]);
4021
+ }, [allValues.length, filtered, groupLeafValues, multi, useSelectAll]);
3990
4022
  const triggerText = (() => {
3991
4023
  if (selected.length === 0) return placeholder;
3992
4024
  if (multi) {
@@ -4008,6 +4040,17 @@ var SSelect = forwardRef(function SSelect2({
4008
4040
  const handleSelectAll = () => {
4009
4041
  emit(allSelected ? [] : allValues);
4010
4042
  };
4043
+ const handleToggleGroup = (leafValues) => {
4044
+ if (leafValues.length === 0) return;
4045
+ const selectedSet = new Set(selected);
4046
+ const allSelectedInGroup = leafValues.every((v) => selectedSet.has(v));
4047
+ if (allSelectedInGroup) {
4048
+ const remove = new Set(leafValues);
4049
+ emit(selected.filter((v) => !remove.has(v)));
4050
+ } else {
4051
+ emit([...selected, ...leafValues.filter((v) => !selectedSet.has(v))]);
4052
+ }
4053
+ };
4011
4054
  useEffect(() => {
4012
4055
  if (!open) {
4013
4056
  setFocusedIndex(-1);
@@ -4048,6 +4091,10 @@ var SSelect = forwardRef(function SSelect2({
4048
4091
  handleSelectAll();
4049
4092
  return;
4050
4093
  }
4094
+ if (focused.isGroup) {
4095
+ handleToggleGroup(focused.leafValues ?? []);
4096
+ return;
4097
+ }
4051
4098
  handleSelect(focused.value);
4052
4099
  };
4053
4100
  const handleKeyDown = (event) => {
@@ -4211,20 +4258,20 @@ var SSelect = forwardRef(function SSelect2({
4211
4258
  items: filtered,
4212
4259
  multi,
4213
4260
  selected,
4214
- focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
4261
+ groupLeafValues,
4262
+ focusedValue: navigableItems[focusedIndex]?.key,
4215
4263
  showSelectAll: multi && useSelectAll && allValues.length > 0,
4216
4264
  selectAllLabel: allSelectedLabel,
4217
4265
  selectAllSelected: allSelected,
4218
4266
  onSelectAll: handleSelectAll,
4219
4267
  onSelect: handleSelect,
4220
- onHoverValue: (value2) => {
4221
- if (value2 == null) {
4268
+ onToggleGroup: handleToggleGroup,
4269
+ onHoverValue: (key) => {
4270
+ if (key == null) {
4222
4271
  setFocusedIndex(-1);
4223
4272
  return;
4224
4273
  }
4225
- const index = navigableItems.findIndex(
4226
- (item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
4227
- );
4274
+ const index = navigableItems.findIndex((item) => item.key === key);
4228
4275
  if (index >= 0) setFocusedIndex(index);
4229
4276
  }
4230
4277
  }
@@ -4243,20 +4290,23 @@ function ItemList({
4243
4290
  items,
4244
4291
  multi,
4245
4292
  selected,
4293
+ groupLeafValues,
4246
4294
  focusedValue,
4247
4295
  showSelectAll,
4248
4296
  selectAllLabel,
4249
4297
  selectAllSelected,
4250
4298
  onSelectAll,
4251
4299
  onSelect,
4300
+ onToggleGroup,
4252
4301
  onHoverValue
4253
4302
  }) {
4303
+ const selectedSet = useMemo(() => new Set(selected), [selected]);
4254
4304
  const itemBase = cn(
4255
4305
  "group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
4256
4306
  "text-[color:var(--cmp-listItem-content-default)]",
4257
4307
  "disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
4258
4308
  );
4259
- const activeClass = "hover:bg-[var(--cmp-listItem-bg-hover)] hover:text-[color:var(--cmp-listItem-content-hover)] focus:bg-[var(--cmp-listItem-bg-hover)] focus:text-[color:var(--cmp-listItem-content-hover)]";
4309
+ const activeClass = "hover:bg-[var(--cmp-listItem-bg-hover)] hover:text-[color:var(--cmp-listItem-content-hover)] focus-visible:bg-[var(--cmp-listItem-bg-hover)] focus-visible:text-[color:var(--cmp-listItem-content-hover)]";
4260
4310
  return /* @__PURE__ */ jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
4261
4311
  showSelectAll && /* @__PURE__ */ jsxs(
4262
4312
  "button",
@@ -4273,23 +4323,7 @@ function ItemList({
4273
4323
  onMouseLeave: () => onHoverValue(null),
4274
4324
  onClick: onSelectAll,
4275
4325
  children: [
4276
- /* @__PURE__ */ jsx(
4277
- "span",
4278
- {
4279
- className: cn(
4280
- "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
4281
- selectAllSelected ? focusedValue === "__select_all__" ? "border-[color:var(--cmp-checkbox-checked-border-inverse)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-[var(--cmp-checkbox-unchecked-bg-default)]"
4282
- ),
4283
- children: selectAllSelected && /* @__PURE__ */ jsx(
4284
- SIcon,
4285
- {
4286
- name: "checkboxCheck",
4287
- size: 12,
4288
- color: "var(--cmp-checkbox-checked-icon-default)"
4289
- }
4290
- )
4291
- }
4292
- ),
4326
+ /* @__PURE__ */ jsx(CheckboxBox, { state: selectAllSelected, focused: focusedValue === "__select_all__" }),
4293
4327
  /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
4294
4328
  ]
4295
4329
  }
@@ -4299,12 +4333,71 @@ function ItemList({
4299
4333
  const rowKey = `${o.value}-${idx}`;
4300
4334
  if (o.isGroup) {
4301
4335
  const isDepth1 = o.depth === 1;
4336
+ const groupTone = isDepth1 ? "border-t border-solid border-[color:var(--cmp-listItem-depth1-border-color)] bg-[var(--cmp-listItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listItem-depth2-middle-bg)] font-medium";
4337
+ if (multi) {
4338
+ const leafValues = groupLeafValues.get(idx) ?? [];
4339
+ const total = leafValues.length;
4340
+ const selectedCount = leafValues.reduce((n, v) => selectedSet.has(v) ? n + 1 : n, 0);
4341
+ const groupState = total === 0 || selectedCount === 0 ? false : selectedCount === total ? true : null;
4342
+ const groupDisabled = o.disabled || total === 0;
4343
+ const groupFocused = focusedValue === `__group_${idx}__`;
4344
+ return /* @__PURE__ */ jsxs(
4345
+ "button",
4346
+ {
4347
+ type: "button",
4348
+ role: "option",
4349
+ "aria-selected": groupState === true,
4350
+ disabled: groupDisabled,
4351
+ "data-select-focused": groupFocused ? "true" : void 0,
4352
+ "data-select-active": groupFocused ? "true" : void 0,
4353
+ className: cn(
4354
+ "flex w-full select-none items-center gap-[8px] border-none py-[4px] pr-[12px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
4355
+ groupTone,
4356
+ groupDisabled ? "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]" : "cursor-pointer",
4357
+ groupDisabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]",
4358
+ // 포커스 하이라이트는 depth 배경보다 뒤에 둬 tailwind-merge 로 이기게 한다.
4359
+ groupFocused && !groupDisabled && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
4360
+ ),
4361
+ style: { paddingLeft: padLeft },
4362
+ onMouseEnter: () => onHoverValue(`__group_${idx}__`),
4363
+ onMouseLeave: () => onHoverValue(null),
4364
+ onClick: () => onToggleGroup(leafValues),
4365
+ children: [
4366
+ /* @__PURE__ */ jsx(CheckboxBox, { state: groupState, disabled: groupDisabled, focused: groupFocused }),
4367
+ isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
4368
+ "span",
4369
+ {
4370
+ className: "min-w-0 truncate",
4371
+ dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
4372
+ }
4373
+ ) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: o.label }),
4374
+ total > 0 && /* @__PURE__ */ jsxs(
4375
+ "span",
4376
+ {
4377
+ className: cn(
4378
+ "shrink-0 font-medium",
4379
+ groupFocused ? "text-white" : "text-[color:var(--cmp-listItem-content-disabled)]"
4380
+ ),
4381
+ children: [
4382
+ "(",
4383
+ selectedCount,
4384
+ "/",
4385
+ total,
4386
+ ")"
4387
+ ]
4388
+ }
4389
+ )
4390
+ ]
4391
+ },
4392
+ rowKey
4393
+ );
4394
+ }
4302
4395
  return /* @__PURE__ */ jsx(
4303
4396
  "div",
4304
4397
  {
4305
4398
  className: cn(
4306
4399
  "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
4307
- isDepth1 ? "border-t border-solid border-[color:var(--cmp-listItem-depth1-border-color)] bg-[var(--cmp-listItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listItem-depth2-middle-bg)] font-medium",
4400
+ groupTone,
4308
4401
  // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
4309
4402
  // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
4310
4403
  o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
@@ -4340,25 +4433,11 @@ function ItemList({
4340
4433
  onClick: () => onSelect(o.value),
4341
4434
  children: [
4342
4435
  multi && /* @__PURE__ */ jsx(
4343
- "span",
4436
+ CheckboxBox,
4344
4437
  {
4345
- className: cn(
4346
- "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
4347
- o.disabled ? (
4348
- // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
4349
- "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
4350
- ) : cn(
4351
- isSelected ? focusedValue === o.value ? "border-[color:var(--cmp-checkbox-checked-border-inverse)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-[var(--cmp-checkbox-unchecked-bg-default)]"
4352
- )
4353
- ),
4354
- children: isSelected && /* @__PURE__ */ jsx(
4355
- SIcon,
4356
- {
4357
- name: "checkboxCheck",
4358
- size: 12,
4359
- color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
4360
- }
4361
- )
4438
+ state: isSelected,
4439
+ disabled: o.disabled,
4440
+ focused: focusedValue === o.value
4362
4441
  }
4363
4442
  ),
4364
4443
  isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
@@ -4375,6 +4454,33 @@ function ItemList({
4375
4454
  })
4376
4455
  ] });
4377
4456
  }
4457
+ function CheckboxBox({
4458
+ state,
4459
+ disabled = false,
4460
+ focused = false
4461
+ }) {
4462
+ const filled = state === true || state === null;
4463
+ return /* @__PURE__ */ jsx(
4464
+ "span",
4465
+ {
4466
+ className: cn(
4467
+ "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
4468
+ disabled ? (
4469
+ // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
4470
+ "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
4471
+ ) : filled ? focused ? "border-[color:var(--cmp-checkbox-checked-border-inverse)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-[var(--cmp-checkbox-unchecked-bg-default)]"
4472
+ ),
4473
+ children: filled && /* @__PURE__ */ jsx(
4474
+ SIcon,
4475
+ {
4476
+ name: state === null ? "checkboxIndeterminate" : "checkboxCheck",
4477
+ size: 12,
4478
+ color: disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
4479
+ }
4480
+ )
4481
+ }
4482
+ );
4483
+ }
4378
4484
  var DEFAULT_ROW_HEIGHT = 44;
4379
4485
  var DENSE_ROW_HEIGHT = 32;
4380
4486
  var SELECTABLE_COLUMN_WIDTH = 48;