sellmate-design-system-react 9.0.0-beta.89 → 9.0.0-beta.90

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.cjs CHANGED
@@ -14,6 +14,7 @@ var RDropdown5 = require('@radix-ui/react-dropdown-menu');
14
14
  var RSwitch = require('@radix-ui/react-switch');
15
15
  var RDialog = require('@radix-ui/react-dialog');
16
16
  var Ariakit = require('@ariakit/react');
17
+ var RSlider = require('@radix-ui/react-slider');
17
18
 
18
19
  function _interopNamespace(e) {
19
20
  if (e && e.__esModule) return e;
@@ -40,6 +41,7 @@ var RDropdown5__namespace = /*#__PURE__*/_interopNamespace(RDropdown5);
40
41
  var RSwitch__namespace = /*#__PURE__*/_interopNamespace(RSwitch);
41
42
  var RDialog__namespace = /*#__PURE__*/_interopNamespace(RDialog);
42
43
  var Ariakit__namespace = /*#__PURE__*/_interopNamespace(Ariakit);
44
+ var RSlider__namespace = /*#__PURE__*/_interopNamespace(RSlider);
43
45
 
44
46
  var isSdSpacing = (value) => /^sd-\d+$/.test(value);
45
47
  var SD_SPACING_GROUPS = [
@@ -4898,10 +4900,16 @@ var ALIGN = {
4898
4900
  end: "flex-end"
4899
4901
  };
4900
4902
  var FOCUS_RING_SHADOW = "0px 0px 4px 0px #0071ff66";
4903
+ var fieldLabelId = (controlId) => `${controlId}-label`;
4904
+ function useFieldControlId(id) {
4905
+ const autoId = react.useId();
4906
+ return id ?? autoId;
4907
+ }
4901
4908
  var cssLen = (v) => v === void 0 || v === "" || v === 0 ? void 0 : typeof v === "number" ? `${v}px` : v;
4902
4909
  var SField = /* @__PURE__ */ react.forwardRef(function SField2({
4903
4910
  name,
4904
4911
  label = "",
4912
+ controlId,
4905
4913
  subLabel = "",
4906
4914
  icon,
4907
4915
  iconColor,
@@ -5024,7 +5032,19 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
5024
5032
  style: { marginRight: s.labelGap }
5025
5033
  }
5026
5034
  ),
5027
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
5035
+ /* @__PURE__ */ jsxRuntime.jsx(
5036
+ "label",
5037
+ {
5038
+ id: controlId ? fieldLabelId(controlId) : void 0,
5039
+ htmlFor: controlId,
5040
+ className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap",
5041
+ onClick: () => {
5042
+ const control = controlId ? document.getElementById(controlId) : null;
5043
+ if (control && !("labels" in control)) control.focus();
5044
+ },
5045
+ children: label
5046
+ }
5047
+ ),
5028
5048
  subLabel !== "" && // field 라벨 토큰이 아니라 기본 타이포 토큰을 쓴다 — 보조 라벨은 굵기·색이 라벨과
5029
5049
  // 따로 가므로 field 토큰에 묶지 않는다. 크기도 size 와 무관하게 body/sm 고정.
5030
5050
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5176,6 +5196,7 @@ var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
5176
5196
  const minHeight = heightForRows(MIN_ROWS);
5177
5197
  const maxHeight = autogrow && maxRows != null ? heightForRows(maxRows) : void 0;
5178
5198
  const [ruleError, setRuleError] = react.useState("");
5199
+ const controlId = useFieldControlId(rest.id);
5179
5200
  const innerRef = react.useRef(null);
5180
5201
  const setRefs2 = react.useCallback(
5181
5202
  (node) => {
@@ -5217,6 +5238,7 @@ var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
5217
5238
  SField,
5218
5239
  {
5219
5240
  label,
5241
+ controlId,
5220
5242
  labelPosition,
5221
5243
  subLabel,
5222
5244
  labelWidth,
@@ -5243,6 +5265,7 @@ var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
5243
5265
  "textarea",
5244
5266
  {
5245
5267
  ref: setRefs2,
5268
+ id: controlId,
5246
5269
  value: value ?? "",
5247
5270
  rows,
5248
5271
  disabled,
@@ -5293,7 +5316,9 @@ var SChatInputImpl = /* @__PURE__ */ react.forwardRef(function SChatInput({
5293
5316
  placeholder = DEFAULT_PLACEHOLDER,
5294
5317
  disabled = false,
5295
5318
  onSend,
5319
+ hasAttachments = false,
5296
5320
  onAttach,
5321
+ onPasteFiles,
5297
5322
  busy = false,
5298
5323
  onStop,
5299
5324
  footerAction,
@@ -5303,17 +5328,26 @@ var SChatInputImpl = /* @__PURE__ */ react.forwardRef(function SChatInput({
5303
5328
  textareaClassName,
5304
5329
  ...rest
5305
5330
  }, ref) {
5306
- const canSend = !disabled && !busy && value.trim() !== "";
5331
+ const hasText = value.trim() !== "";
5332
+ const canSend = !disabled && !busy && (hasText || hasAttachments);
5307
5333
  const send = react.useCallback(() => {
5308
5334
  if (!canSend) return;
5309
- onSend?.(value);
5310
- }, [canSend, onSend, value]);
5335
+ onSend?.(hasText ? value : "");
5336
+ }, [canSend, hasText, onSend, value]);
5311
5337
  const handleKeyDown = (e) => {
5312
5338
  rest.onKeyDown?.(e);
5313
5339
  if (e.key !== "Enter" || e.shiftKey || e.nativeEvent.isComposing) return;
5314
5340
  e.preventDefault();
5315
5341
  send();
5316
5342
  };
5343
+ const handlePaste = (e) => {
5344
+ rest.onPaste?.(e);
5345
+ if (onPasteFiles == null || e.defaultPrevented) return;
5346
+ const files = Array.from(e.clipboardData.files);
5347
+ if (files.length === 0) return;
5348
+ e.preventDefault();
5349
+ onPasteFiles(files);
5350
+ };
5317
5351
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col bg-(--cmp-chatInput-bg)", className), style, children: [
5318
5352
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-(--cmp-chatInput-gap) p-(--cmp-chatInput-padding)", children: [
5319
5353
  onAttach != null && /* @__PURE__ */ jsxRuntime.jsx(
@@ -5344,7 +5378,8 @@ var SChatInputImpl = /* @__PURE__ */ react.forwardRef(function SChatInput({
5344
5378
  textareaClassName,
5345
5379
  textareaStyle: { minHeight: TEXTAREA_MIN_HEIGHT },
5346
5380
  ...rest,
5347
- onKeyDown: handleKeyDown
5381
+ onKeyDown: handleKeyDown,
5382
+ onPaste: handlePaste
5348
5383
  }
5349
5384
  ),
5350
5385
  busy ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -5605,6 +5640,7 @@ var SChatMessageImpl = /* @__PURE__ */ react.forwardRef(function SChatMessage({
5605
5640
  readStatus,
5606
5641
  avatarSrc,
5607
5642
  avatarAlt,
5643
+ showAvatar = true,
5608
5644
  continued = false,
5609
5645
  variant = "default",
5610
5646
  actions,
@@ -5635,7 +5671,7 @@ var SChatMessageImpl = /* @__PURE__ */ react.forwardRef(function SChatMessage({
5635
5671
  ),
5636
5672
  ...rest,
5637
5673
  children: [
5638
- !outgoing && /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: avatarSrc, alt: avatarAlt, placeholder: continued }),
5674
+ !outgoing && showAvatar && /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: avatarSrc, alt: avatarAlt, placeholder: continued }),
5639
5675
  /* @__PURE__ */ jsxRuntime.jsxs(
5640
5676
  "div",
5641
5677
  {
@@ -6782,6 +6818,7 @@ var SListItemImpl = /* @__PURE__ */ react.forwardRef(function SListItem({
6782
6818
  className,
6783
6819
  onMouseEnter,
6784
6820
  onMouseLeave,
6821
+ onKeyDown,
6785
6822
  ...rest
6786
6823
  }, ref) {
6787
6824
  const [hovered, setHovered] = react.useState(false);
@@ -6805,15 +6842,29 @@ var SListItemImpl = /* @__PURE__ */ react.forwardRef(function SListItem({
6805
6842
  setHovered(false);
6806
6843
  onMouseLeave?.(event);
6807
6844
  };
6845
+ const pressable = isInteractive && rest.onClick != null;
6846
+ const handleKeyDown = (event) => {
6847
+ onKeyDown?.(event);
6848
+ if (!pressable || event.defaultPrevented || event.target !== event.currentTarget) return;
6849
+ if (event.key === "Enter" || event.key === " ") {
6850
+ event.preventDefault();
6851
+ event.currentTarget.click();
6852
+ }
6853
+ };
6808
6854
  return /* @__PURE__ */ jsxRuntime.jsxs(
6809
6855
  "div",
6810
6856
  {
6811
6857
  ref,
6812
6858
  "aria-disabled": disabled || void 0,
6859
+ role: pressable ? "button" : void 0,
6860
+ tabIndex: pressable ? 0 : void 0,
6813
6861
  onMouseEnter: handleMouseEnter,
6814
6862
  onMouseLeave: handleMouseLeave,
6863
+ onKeyDown: handleKeyDown,
6815
6864
  className: cn(
6816
6865
  "group/list-item relative flex w-full items-center justify-between bg-(--cmp-listItem-bg-default) text-left text-(--cmp-listItem-text-default)",
6866
+ // 링을 안쪽으로 그린다 — bordered 항목은 overflow-hidden 이라 바깥 링이 잘린다
6867
+ pressable && "focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-(--sys-color-border-accent)",
6817
6868
  titleTypo,
6818
6869
  disabled ? "pointer-events-none cursor-not-allowed text-(--sys-color-disabled-text)" : isSelected ? cn(
6819
6870
  "cursor-pointer text-(--cmp-listItem-text-selected)",
@@ -7497,6 +7548,7 @@ function SCheckbox({
7497
7548
  val,
7498
7549
  onValueChange,
7499
7550
  label = "",
7551
+ ariaLabel,
7500
7552
  disabled = false,
7501
7553
  inverse = false,
7502
7554
  className,
@@ -7537,6 +7589,7 @@ function SCheckbox({
7537
7589
  checked: state2,
7538
7590
  disabled,
7539
7591
  onCheckedChange: handleChange,
7592
+ "aria-label": ariaLabel,
7540
7593
  className: cn(
7541
7594
  "flex h-[var(--cmp-checkbox-size)] w-[var(--cmp-checkbox-size)] items-center justify-center rounded-[var(--cmp-checkbox-radius)] border-[length:var(--cmp-checkbox-borderWidth)] border-solid bg-[var(--cb-bg)] border-[color:var(--cb-border)] outline-none",
7542
7595
  disabled ? "cursor-not-allowed" : "cursor-pointer",
@@ -15737,6 +15790,7 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
15737
15790
  const baseId = react.useId();
15738
15791
  const listboxId = `${baseId}-listbox`;
15739
15792
  const activeOptionId = `${baseId}-active-option`;
15793
+ const triggerId = `${baseId}-trigger`;
15740
15794
  const [win, setWin] = react.useState({ start: 0, end: 0, top: 0, bottom: 0 });
15741
15795
  const winRef = react.useRef(win);
15742
15796
  winRef.current = win;
@@ -16223,6 +16277,7 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
16223
16277
  SField,
16224
16278
  {
16225
16279
  label,
16280
+ controlId: triggerId,
16226
16281
  labelPosition,
16227
16282
  subLabel,
16228
16283
  labelWidth,
@@ -16248,8 +16303,10 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
16248
16303
  "button",
16249
16304
  {
16250
16305
  ref: triggerRef,
16306
+ id: triggerId,
16251
16307
  type: "button",
16252
16308
  disabled: effectiveDisabled,
16309
+ "aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerId}` : void 0,
16253
16310
  "aria-haspopup": "listbox",
16254
16311
  "aria-expanded": open,
16255
16312
  "aria-controls": open ? listboxId : void 0,
@@ -16345,7 +16402,12 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
16345
16402
  event.preventDefault();
16346
16403
  requestAnimationFrame(() => searchInputRef.current?.focus());
16347
16404
  },
16348
- onPointerDownOutside: () => {
16405
+ onPointerDownOutside: (event) => {
16406
+ const target = event.target;
16407
+ if (label && target?.closest?.(`[id="${fieldLabelId(triggerId)}"]`)) {
16408
+ event.preventDefault();
16409
+ return;
16410
+ }
16349
16411
  dismissedByPointerRef.current = true;
16350
16412
  },
16351
16413
  onCloseAutoFocus: (event) => {
@@ -18265,6 +18327,7 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
18265
18327
  }, ref) {
18266
18328
  const [visible, setVisible] = react.useState(false);
18267
18329
  const [ruleError, setRuleError] = react.useState("");
18330
+ const controlId = useFieldControlId(rest.id);
18268
18331
  const f = FONT2[size];
18269
18332
  const isPassword = type === "password";
18270
18333
  const inputType = isPassword ? visible ? "text" : "password" : type;
@@ -18289,6 +18352,7 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
18289
18352
  SField,
18290
18353
  {
18291
18354
  label,
18355
+ controlId,
18292
18356
  labelPosition,
18293
18357
  subLabel,
18294
18358
  labelWidth,
@@ -18328,6 +18392,7 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
18328
18392
  "input",
18329
18393
  {
18330
18394
  ref: mergeRefs(ref, valueTooltip.valueRef),
18395
+ id: controlId,
18331
18396
  type: inputType,
18332
18397
  value: value ?? "",
18333
18398
  disabled,
@@ -18503,6 +18568,7 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
18503
18568
  const numValue = applyDecimalLimit(toNumber(value), decimalLimit);
18504
18569
  const [display, setDisplay] = react.useState(() => formatWithCommas(numValue));
18505
18570
  const [ruleError, setRuleError] = react.useState("");
18571
+ const controlId = useFieldControlId();
18506
18572
  const [rangeError, setRangeError] = react.useState("");
18507
18573
  const innerRef = react.useRef(null);
18508
18574
  const valueTooltip = useTruncatedValueTooltip({ enabled: readOnly && !disabled, text: display });
@@ -18652,6 +18718,7 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
18652
18718
  SField,
18653
18719
  {
18654
18720
  label,
18721
+ controlId,
18655
18722
  labelPosition,
18656
18723
  subLabel,
18657
18724
  labelWidth,
@@ -18691,6 +18758,7 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
18691
18758
  "input",
18692
18759
  {
18693
18760
  ref: setRefs2,
18761
+ id: controlId,
18694
18762
  name,
18695
18763
  type: "text",
18696
18764
  inputMode: allowDecimal ? "decimal" : "numeric",
@@ -19392,6 +19460,8 @@ function SDatePicker({
19392
19460
  }) {
19393
19461
  const portalContainer = usePortalContainer();
19394
19462
  const [open, setOpen] = react.useState(false);
19463
+ const triggerId = useFieldControlId();
19464
+ const triggerTextId = `${triggerId}-text`;
19395
19465
  const [focused, setFocused] = react.useState(false);
19396
19466
  const [ruleError, setRuleError] = react.useState("");
19397
19467
  const hasValue = value != null && value !== "";
@@ -19448,6 +19518,7 @@ function SDatePicker({
19448
19518
  return /* @__PURE__ */ jsxRuntime.jsx(
19449
19519
  SField,
19450
19520
  {
19521
+ controlId: triggerId,
19451
19522
  label,
19452
19523
  labelPosition,
19453
19524
  subLabel,
@@ -19474,8 +19545,10 @@ function SDatePicker({
19474
19545
  /* @__PURE__ */ jsxRuntime.jsx(RPopover13__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
19475
19546
  "div",
19476
19547
  {
19548
+ id: triggerId,
19477
19549
  role: "button",
19478
19550
  "aria-disabled": disabled,
19551
+ "aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerTextId}` : void 0,
19479
19552
  tabIndex: disabled ? -1 : 0,
19480
19553
  "data-name": name,
19481
19554
  onFocus: () => setFocused(true),
@@ -19505,6 +19578,7 @@ function SDatePicker({
19505
19578
  /* @__PURE__ */ jsxRuntime.jsx(
19506
19579
  "span",
19507
19580
  {
19581
+ id: triggerTextId,
19508
19582
  className: "flex-1 truncate text-center",
19509
19583
  style: {
19510
19584
  minWidth: dims.minW,
@@ -19931,6 +20005,8 @@ function SDateRangePicker({
19931
20005
  }) {
19932
20006
  const portalContainer = usePortalContainer();
19933
20007
  const [open, setOpen] = react.useState(false);
20008
+ const triggerId = useFieldControlId();
20009
+ const triggerTextId = `${triggerId}-text`;
19934
20010
  const [ruleError, setRuleError] = react.useState("");
19935
20011
  const [pendingStart, setPendingStart] = react.useState(null);
19936
20012
  const hasValue = value != null && value[0] !== "" && value[1] !== "";
@@ -19987,6 +20063,7 @@ function SDateRangePicker({
19987
20063
  return /* @__PURE__ */ jsxRuntime.jsx(
19988
20064
  SField,
19989
20065
  {
20066
+ controlId: triggerId,
19990
20067
  label,
19991
20068
  labelPosition,
19992
20069
  subLabel,
@@ -20022,8 +20099,10 @@ function SDateRangePicker({
20022
20099
  /* @__PURE__ */ jsxRuntime.jsx(RPopover13__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
20023
20100
  "div",
20024
20101
  {
20102
+ id: triggerId,
20025
20103
  role: "button",
20026
20104
  "aria-disabled": disabled,
20105
+ "aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerTextId}` : void 0,
20027
20106
  tabIndex: disabled ? -1 : 0,
20028
20107
  "data-name": name,
20029
20108
  onKeyDown: (event) => {
@@ -20051,6 +20130,7 @@ function SDateRangePicker({
20051
20130
  /* @__PURE__ */ jsxRuntime.jsx(
20052
20131
  "span",
20053
20132
  {
20133
+ id: triggerTextId,
20054
20134
  className: cn(
20055
20135
  "flex-1 items-center",
20056
20136
  hasDisplay ? "flex" : "block truncate text-center"
@@ -20313,6 +20393,8 @@ function SFilePicker({
20313
20393
  }) {
20314
20394
  const autoId = react.useId();
20315
20395
  const resolvedName = name ?? autoId;
20396
+ const triggerId = `${autoId}-trigger`;
20397
+ const displayTextId = `${autoId}-text`;
20316
20398
  const inputRef = react.useRef(null);
20317
20399
  const contentBoxRef = react.useRef(null);
20318
20400
  const [ruleError, setRuleError] = react.useState("");
@@ -20413,10 +20495,12 @@ function SFilePicker({
20413
20495
  /* @__PURE__ */ jsxRuntime.jsxs(
20414
20496
  "button",
20415
20497
  {
20498
+ id: triggerId,
20416
20499
  type: "button",
20417
20500
  disabled,
20418
20501
  onClick: open,
20419
20502
  "aria-label": placeholder,
20503
+ "aria-labelledby": label ? `${fieldLabelId(triggerId)} ${displayTextId}` : void 0,
20420
20504
  className: cn(
20421
20505
  "flex min-w-0 flex-1 items-center bg-transparent text-left",
20422
20506
  gapCls,
@@ -20434,6 +20518,7 @@ function SFilePicker({
20434
20518
  /* @__PURE__ */ jsxRuntime.jsx(
20435
20519
  "span",
20436
20520
  {
20521
+ id: displayTextId,
20437
20522
  className: cn(
20438
20523
  "min-w-0 flex-1 truncate text-[12px]",
20439
20524
  inlinePlaceholder && "text-[color:var(--cmp-filepicker-text-placeholder)] transition-colors group-hover/filepicker:text-[color:var(--cmp-filepicker-text-default)]"
@@ -20481,6 +20566,7 @@ function SFilePicker({
20481
20566
  {
20482
20567
  borderless: inline,
20483
20568
  label,
20569
+ controlId: triggerId,
20484
20570
  labelPosition,
20485
20571
  subLabel,
20486
20572
  labelWidth,
@@ -25037,8 +25123,12 @@ var EditorBody = /* @__PURE__ */ react.forwardRef(function EditorBody2({
25037
25123
  bubbleMenu,
25038
25124
  onImageUpload,
25039
25125
  editorClassName,
25040
- editorStyle
25126
+ editorStyle,
25127
+ controlId,
25128
+ labelled
25041
25129
  }, ref) {
25130
+ const labelledRef = react.useRef(labelled);
25131
+ labelledRef.current = labelled;
25042
25132
  const placeholderRef = react.useRef(placeholder);
25043
25133
  placeholderRef.current = placeholder;
25044
25134
  const handlers = react.useRef({ onInput, onFocusChange });
@@ -25053,6 +25143,10 @@ var EditorBody = /* @__PURE__ */ react.forwardRef(function EditorBody2({
25053
25143
  editable,
25054
25144
  // 소비 앱이 Next.js RSC 라면 첫 렌더가 서버에서 일어난다 — 즉시 렌더하면 하이드레이션이 어긋난다
25055
25145
  immediatelyRender: false,
25146
+ // 본문은 contenteditable 이라 <label for> 가 닿지 않는다 — 레이블 id 로 이름을 잇는다
25147
+ editorProps: {
25148
+ attributes: () => labelledRef.current ? { "id": controlId, "aria-labelledby": fieldLabelId(controlId) } : { id: controlId }
25149
+ },
25056
25150
  onUpdate: ({ editor: e }) => {
25057
25151
  const html = e.isEmpty ? "" : e.getHTML();
25058
25152
  htmlRef.current = html;
@@ -25271,6 +25365,7 @@ var SEditor = /* @__PURE__ */ react.forwardRef(function SEditor2({
25271
25365
  }, ref) {
25272
25366
  const [ruleError, setRuleError] = react.useState("");
25273
25367
  const [selfFocused, setSelfFocused] = react.useState(false);
25368
+ const controlId = useFieldControlId();
25274
25369
  const htmlRef = react.useRef(value ?? defaultValue ?? "");
25275
25370
  const bodyRef = react.useRef(null);
25276
25371
  const pendingFocusRef = react.useRef(false);
@@ -25341,6 +25436,7 @@ var SEditor = /* @__PURE__ */ react.forwardRef(function SEditor2({
25341
25436
  {
25342
25437
  name,
25343
25438
  label,
25439
+ controlId,
25344
25440
  labelPosition,
25345
25441
  subLabel,
25346
25442
  labelWidth,
@@ -25404,6 +25500,8 @@ var SEditor = /* @__PURE__ */ react.forwardRef(function SEditor2({
25404
25500
  toolbar: resolvedToolbar,
25405
25501
  bubbleMenu: resolvedBubbleMenu,
25406
25502
  onImageUpload,
25503
+ controlId,
25504
+ labelled: !!label,
25407
25505
  editorClassName,
25408
25506
  editorStyle
25409
25507
  }
@@ -25411,6 +25509,302 @@ var SEditor = /* @__PURE__ */ react.forwardRef(function SEditor2({
25411
25509
  }
25412
25510
  );
25413
25511
  });
25512
+ var TRACK = 6;
25513
+ var THUMB = 16;
25514
+ var PRESS_SCALE = 1.2;
25515
+ var PRESS_GROW = THUMB * (PRESS_SCALE - 1) / 2;
25516
+ var SHADOW_Y = 2;
25517
+ var SHADOW_BLUR = 4;
25518
+ var KNOB_BLEED = Math.ceil(PRESS_GROW + SHADOW_BLUR);
25519
+ var MARK_DOT = 4;
25520
+ var BUBBLE_HEIGHT = 18;
25521
+ var BUBBLE_GAP = 4;
25522
+ var ROW_HEIGHT = { sm: 28, md: 36 };
25523
+ var COLOR = {
25524
+ track: "var(--sys-color-border-default)",
25525
+ trackDisabled: "var(--sys-color-disabled-bg)",
25526
+ range: "var(--sys-color-bg-accent)",
25527
+ rangeDisabled: "var(--color-brilliantblue-40)",
25528
+ thumbBg: "var(--sys-color-bg-frame)",
25529
+ markOn: "var(--sys-color-bg-frame)",
25530
+ markOff: "var(--sys-color-border-strong)",
25531
+ markOffDisabled: "var(--sys-color-disabled-border)",
25532
+ markLabel: "var(--sys-color-fg-tertiary)",
25533
+ bubbleBg: "var(--sys-color-bg-deep)",
25534
+ bubbleText: "var(--sys-color-fg-inverse)"
25535
+ };
25536
+ var thumbAlignedLeft = (pct) => `calc(${pct}% + ${THUMB / 2 * (1 - pct / 50)}px)`;
25537
+ var toPercent = (v, min, max) => max === min ? 0 : (v - min) / (max - min) * 100;
25538
+ var SSlider = /* @__PURE__ */ react.forwardRef(function SSlider2({
25539
+ value,
25540
+ onValueChange,
25541
+ onValueCommit,
25542
+ min = 0,
25543
+ max = 100,
25544
+ step = 1,
25545
+ marks,
25546
+ showValue = "drag",
25547
+ formatValue = String,
25548
+ size = "sm",
25549
+ rules,
25550
+ name,
25551
+ status,
25552
+ label,
25553
+ labelPosition,
25554
+ subLabel,
25555
+ labelWidth,
25556
+ icon,
25557
+ iconColor,
25558
+ labelTooltip,
25559
+ labelTooltipProps,
25560
+ hint,
25561
+ error,
25562
+ errorMessage,
25563
+ width,
25564
+ disabled = false,
25565
+ readOnly = false,
25566
+ className,
25567
+ style
25568
+ }, ref) {
25569
+ const isRange = Array.isArray(value);
25570
+ const values = isRange ? value : [value ?? min];
25571
+ const toOut = (v) => isRange ? [v[0], v[1]] : v[0];
25572
+ const [ruleError, setRuleError] = react.useState("");
25573
+ useFormField({
25574
+ name,
25575
+ validate: () => {
25576
+ const msg = runRules(value, rules);
25577
+ setRuleError(msg);
25578
+ return msg === "";
25579
+ },
25580
+ resetValidation: () => setRuleError("")
25581
+ });
25582
+ const resolvedError = error || ruleError !== "";
25583
+ const resolvedErrorMessage = ruleError !== "" ? ruleError : errorMessage;
25584
+ const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
25585
+ const [dragging, setDragging] = react.useState(false);
25586
+ const [focusIndex, setFocusIndex] = react.useState(null);
25587
+ const thumbId = useFieldControlId();
25588
+ const wrapRef = react.useRef(null);
25589
+ const bubbleRefs = react.useRef([]);
25590
+ const layoutKey = `${values.join(",")}|${values.map(formatValue).join(",")}`;
25591
+ react.useLayoutEffect(() => {
25592
+ const wrap = wrapRef.current;
25593
+ if (!wrap) return;
25594
+ const layout = () => {
25595
+ const outer = wrap.clientWidth;
25596
+ const track = outer - KNOB_BLEED * 2;
25597
+ bubbleRefs.current.forEach((el, i) => {
25598
+ if (!el || values[i] === void 0) return;
25599
+ const pct = toPercent(values[i], min, max);
25600
+ const center = KNOB_BLEED + track * pct / 100 + THUMB / 2 * (1 - pct / 50);
25601
+ const half = el.offsetWidth / 2;
25602
+ const shift = center - half < 0 ? half - center : center + half > outer ? outer - half - center : 0;
25603
+ el.style.transform = `translateX(calc(-50% + ${shift}px))`;
25604
+ });
25605
+ };
25606
+ layout();
25607
+ const ro = new ResizeObserver(layout);
25608
+ ro.observe(wrap);
25609
+ return () => ro.disconnect();
25610
+ }, [layoutKey, min, max, showValue]);
25611
+ const rowHeight = ROW_HEIGHT[size];
25612
+ const rowGap = (rowHeight - THUMB) / 2;
25613
+ const topSpace = Math.max(
25614
+ 0,
25615
+ KNOB_BLEED - rowGap,
25616
+ showValue === "none" ? 0 : BUBBLE_HEIGHT + BUBBLE_GAP + PRESS_GROW - rowGap
25617
+ );
25618
+ const bottomSpace = Math.max(0, KNOB_BLEED + SHADOW_Y - rowGap);
25619
+ const markList = marks === true ? Array.from({ length: Math.floor((max - min) / step) + 1 }, (_, i) => ({
25620
+ value: min + i * step
25621
+ })) : Array.isArray(marks) ? marks.filter((m) => m.value >= min && m.value <= max) : [];
25622
+ const hasMarkLabels = markList.some((m) => m.label);
25623
+ const [rangeStart, rangeEnd] = isRange ? [values[0], values[1]] : [min, values[0]];
25624
+ const interactive = !disabled && !readOnly;
25625
+ return /* @__PURE__ */ jsxRuntime.jsx(
25626
+ SField,
25627
+ {
25628
+ name,
25629
+ label,
25630
+ controlId: thumbId,
25631
+ labelPosition,
25632
+ subLabel,
25633
+ labelWidth,
25634
+ labelHeight: topSpace > 0 ? rowHeight + topSpace * 2 : void 0,
25635
+ icon,
25636
+ iconColor,
25637
+ labelTooltip,
25638
+ labelTooltipProps,
25639
+ hint,
25640
+ size,
25641
+ error: resolvedError,
25642
+ status: resolvedStatus,
25643
+ errorMessage: resolvedErrorMessage,
25644
+ width,
25645
+ disabled,
25646
+ readOnly,
25647
+ borderless: true,
25648
+ multiline: true,
25649
+ className,
25650
+ style,
25651
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
25652
+ "div",
25653
+ {
25654
+ ref: wrapRef,
25655
+ className: "flex w-full flex-col",
25656
+ style: { padding: `${topSpace}px ${KNOB_BLEED}px ${bottomSpace}px` },
25657
+ children: [
25658
+ /* @__PURE__ */ jsxRuntime.jsxs(
25659
+ RSlider__namespace.Root,
25660
+ {
25661
+ ref,
25662
+ value: values,
25663
+ min,
25664
+ max,
25665
+ step,
25666
+ disabled,
25667
+ minStepsBetweenThumbs: 0,
25668
+ onValueChange: (v) => {
25669
+ if (!interactive) return;
25670
+ const next = toOut(v);
25671
+ onValueChange?.(next);
25672
+ if (ruleError !== "") setRuleError(runRules(next, rules));
25673
+ },
25674
+ onValueCommit: (v) => {
25675
+ if (!interactive) return;
25676
+ const next = toOut(v);
25677
+ onValueCommit?.(next);
25678
+ if (rules && rules.length > 0) setRuleError(runRules(next, rules));
25679
+ },
25680
+ onPointerDown: () => interactive && setDragging(true),
25681
+ onPointerUp: () => setDragging(false),
25682
+ onLostPointerCapture: () => setDragging(false),
25683
+ className: cn(
25684
+ "relative flex w-full touch-none items-center select-none",
25685
+ // 읽기 전용은 포인터로 잡히지 않게 한다 — 키보드 포커스는 남겨 값을 읽을 수 있게 한다
25686
+ readOnly && "pointer-events-none"
25687
+ ),
25688
+ style: { height: rowHeight },
25689
+ children: [
25690
+ /* @__PURE__ */ jsxRuntime.jsx(
25691
+ RSlider__namespace.Track,
25692
+ {
25693
+ className: "relative grow overflow-hidden rounded-full",
25694
+ style: { height: TRACK, background: disabled ? COLOR.trackDisabled : COLOR.track },
25695
+ children: /* @__PURE__ */ jsxRuntime.jsx(
25696
+ RSlider__namespace.Range,
25697
+ {
25698
+ className: "absolute h-full",
25699
+ style: { background: disabled ? COLOR.rangeDisabled : COLOR.range }
25700
+ }
25701
+ )
25702
+ }
25703
+ ),
25704
+ markList.map((m) => {
25705
+ const on = m.value >= rangeStart && m.value <= rangeEnd;
25706
+ return /* @__PURE__ */ jsxRuntime.jsx(
25707
+ "span",
25708
+ {
25709
+ "aria-hidden": true,
25710
+ className: "pointer-events-none absolute top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full",
25711
+ style: {
25712
+ left: thumbAlignedLeft(toPercent(m.value, min, max)),
25713
+ width: MARK_DOT,
25714
+ height: MARK_DOT,
25715
+ background: on ? COLOR.markOn : disabled ? COLOR.markOffDisabled : COLOR.markOff
25716
+ }
25717
+ },
25718
+ m.value
25719
+ );
25720
+ }),
25721
+ values.map((v, i) => {
25722
+ const pressed = interactive && dragging && focusIndex === i;
25723
+ return /* @__PURE__ */ jsxRuntime.jsxs(
25724
+ RSlider__namespace.Thumb,
25725
+ {
25726
+ id: i === 0 ? thumbId : void 0,
25727
+ "aria-label": isRange ? `${label ?? ""} ${i === 0 ? "\uCD5C\uC19F\uAC12" : "\uCD5C\uB313\uAC12"}`.trim() : label,
25728
+ "aria-valuetext": formatValue(v),
25729
+ "aria-readonly": readOnly || void 0,
25730
+ onFocus: () => setFocusIndex(i),
25731
+ onBlur: () => setFocusIndex(null),
25732
+ className: cn(
25733
+ "group/thumb relative block rounded-full outline-none",
25734
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--sys-color-field-border-focus)",
25735
+ interactive ? "cursor-grab active:cursor-grabbing" : disabled && "cursor-not-allowed"
25736
+ ),
25737
+ style: { width: THUMB, height: THUMB },
25738
+ children: [
25739
+ /* @__PURE__ */ jsxRuntime.jsx(
25740
+ "span",
25741
+ {
25742
+ "aria-hidden": true,
25743
+ className: "absolute inset-0 rounded-full border-3 border-solid shadow-[0_2px_4px_rgba(0,0,0,0.16)] transition-transform duration-150 ease-out",
25744
+ style: {
25745
+ background: COLOR.thumbBg,
25746
+ borderColor: disabled ? COLOR.rangeDisabled : COLOR.range,
25747
+ transform: pressed ? `scale(${PRESS_SCALE})` : void 0
25748
+ }
25749
+ }
25750
+ ),
25751
+ showValue !== "none" && /* @__PURE__ */ jsxRuntime.jsx(
25752
+ "span",
25753
+ {
25754
+ ref: (el) => {
25755
+ bubbleRefs.current[i] = el;
25756
+ },
25757
+ "aria-hidden": true,
25758
+ className: cn(
25759
+ // 줄 높이로 가운데를 맞추면 줄 상자에 내림 획 자리가 들어 있어 숫자가 위로 1px 가까이 뜬다.
25760
+ // 글자 상자를 대문자 높이~기준선으로 잘라(text-box) 그 상자를 flex 로 가운데 둔다.
25761
+ "typo-body-xs-medium pointer-events-none absolute left-1/2 flex items-center rounded-[6px] px-[6px] whitespace-nowrap transition-opacity",
25762
+ showValue === "always" || pressed ? "opacity-100" : "opacity-0 group-hover/thumb:opacity-100 group-focus-visible/thumb:opacity-100",
25763
+ // 비활성·읽기 전용에서는 조작이 없으므로 drag 말풍선도 띄우지 않는다
25764
+ showValue === "drag" && !interactive && "hidden"
25765
+ ),
25766
+ style: {
25767
+ bottom: `calc(100% + ${BUBBLE_GAP + (pressed ? PRESS_GROW : 0)}px)`,
25768
+ height: BUBBLE_HEIGHT,
25769
+ transform: "translateX(-50%)",
25770
+ background: COLOR.bubbleBg,
25771
+ color: COLOR.bubbleText
25772
+ },
25773
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "[text-box:trim-both_cap_alphabetic]", children: formatValue(v) })
25774
+ }
25775
+ )
25776
+ ]
25777
+ },
25778
+ i
25779
+ );
25780
+ })
25781
+ ]
25782
+ }
25783
+ ),
25784
+ hasMarkLabels && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full", style: { height: BUBBLE_HEIGHT }, children: markList.map((m) => {
25785
+ if (!m.label) return null;
25786
+ const pct = toPercent(m.value, min, max);
25787
+ const align = pct <= 0 ? "translateX(0)" : pct >= 100 ? "translateX(-100%)" : "translateX(-50%)";
25788
+ return /* @__PURE__ */ jsxRuntime.jsx(
25789
+ "span",
25790
+ {
25791
+ className: "typo-body-xs-medium absolute top-0 whitespace-nowrap",
25792
+ style: {
25793
+ left: pct <= 0 ? 0 : pct >= 100 ? "100%" : thumbAlignedLeft(pct),
25794
+ transform: align,
25795
+ color: disabled ? "var(--sys-color-disabled-text)" : COLOR.markLabel
25796
+ },
25797
+ children: m.label
25798
+ },
25799
+ m.value
25800
+ );
25801
+ }) })
25802
+ ]
25803
+ }
25804
+ )
25805
+ }
25806
+ );
25807
+ });
25414
25808
  var CHIPFILTER_LISTBOX_WIDTH = 240;
25415
25809
  var CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH = 246;
25416
25810
  var EDITOR_LISTBOX_MAX_HEIGHT = "min(640px, var(--radix-popover-content-available-height))";
@@ -27291,6 +27685,24 @@ function EmptyItem({ children }) {
27291
27685
  }
27292
27686
  );
27293
27687
  }
27688
+ function splitBySeparators(text, separators) {
27689
+ const seps = separators.filter((s) => s !== "");
27690
+ if (seps.length === 0) return { parts: [], rest: text };
27691
+ const pieces = [];
27692
+ let from = 0;
27693
+ for (let i = 0; i < text.length; ) {
27694
+ const sep = seps.find((s) => text.startsWith(s, i));
27695
+ if (sep) {
27696
+ pieces.push(text.slice(from, i));
27697
+ i += sep.length;
27698
+ from = i;
27699
+ } else {
27700
+ i += 1;
27701
+ }
27702
+ }
27703
+ const parts = pieces.map((p) => p.trim()).filter((p) => p !== "");
27704
+ return { parts, rest: text.slice(from) };
27705
+ }
27294
27706
  var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27295
27707
  values = [],
27296
27708
  onValueChange,
@@ -27301,7 +27713,7 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27301
27713
  disabledChips = [],
27302
27714
  size = "sm",
27303
27715
  disabled = false,
27304
- placeholder = "\uD0DC\uADF8 \uC785\uB825 (Enter\uB85C \uB4F1\uB85D / \uCF64\uB9C8\uB85C \uAD6C\uBD84 / \uB744\uC5B4\uC4F0\uAE30 \uBD88\uAC00)",
27716
+ placeholder: placeholderProp,
27305
27717
  name,
27306
27718
  rules,
27307
27719
  error,
@@ -27314,6 +27726,9 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27314
27726
  recommendedItems = [],
27315
27727
  loadingRecommendedItems = false,
27316
27728
  dropdownMinWidth = 200,
27729
+ commitOnBlur = false,
27730
+ separators,
27731
+ ariaLabel,
27317
27732
  label,
27318
27733
  labelPosition,
27319
27734
  subLabel,
@@ -27329,7 +27744,9 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27329
27744
  className,
27330
27745
  style
27331
27746
  }, ref) {
27747
+ const placeholder = placeholderProp ?? (separators?.includes(",") ? "\uD0DC\uADF8 \uC785\uB825 (Enter\xB7\uCF64\uB9C8\uB85C \uB4F1\uB85D)" : "\uD0DC\uADF8 \uC785\uB825 (Enter\uB85C \uB4F1\uB85D)");
27332
27748
  const autoId = react.useId();
27749
+ const controlId = `${autoId}-input`;
27333
27750
  const resolvedName = name ?? autoId;
27334
27751
  const listboxId = `${autoId}-listbox`;
27335
27752
  const activeOptionId = `${autoId}-active-option`;
@@ -27376,12 +27793,17 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27376
27793
  const isMax = maxCount != null && values.length >= maxCount;
27377
27794
  const resolvedDropdownMinWidth = dropdownMinWidth == null ? void 0 : typeof dropdownMinWidth === "number" ? `${dropdownMinWidth}px` : dropdownMinWidth;
27378
27795
  const commit = (next) => onValueChange?.(next);
27796
+ const addChips = (vs, rest = "") => {
27797
+ const room = maxCount == null ? vs.length : Math.max(maxCount - values.length, 0);
27798
+ const added = vs.slice(0, room);
27799
+ if (added.length > 0) commit([...values, ...added]);
27800
+ setInputValue(rest);
27801
+ setActiveIndex(-1);
27802
+ onInput?.(rest);
27803
+ };
27379
27804
  const addChip = (v) => {
27380
27805
  if (maxCount != null && values.length >= maxCount) return;
27381
- commit([...values, v]);
27382
- setInputValue("");
27383
- setActiveIndex(-1);
27384
- onInput?.("");
27806
+ addChips([v]);
27385
27807
  inputRef.current?.focus();
27386
27808
  };
27387
27809
  const removeChip = (i) => {
@@ -27466,6 +27888,7 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27466
27888
  SField,
27467
27889
  {
27468
27890
  label,
27891
+ controlId,
27469
27892
  labelPosition,
27470
27893
  subLabel,
27471
27894
  labelWidth,
@@ -27564,7 +27987,9 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27564
27987
  "input",
27565
27988
  {
27566
27989
  ref: inputRef,
27990
+ id: controlId,
27567
27991
  name: resolvedName,
27992
+ "aria-label": ariaLabel,
27568
27993
  size: 1,
27569
27994
  role: "combobox",
27570
27995
  "aria-expanded": showDropdown,
@@ -27588,8 +28013,24 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27588
28013
  placeholder: values.length === 0 ? placeholder : "",
27589
28014
  disabled,
27590
28015
  value: inputValue,
28016
+ onPaste: (e) => {
28017
+ if (!separators) return;
28018
+ const pasted = e.clipboardData.getData("text");
28019
+ if (!separators.some((s) => s !== "" && pasted.includes(s))) return;
28020
+ e.preventDefault();
28021
+ const { parts, rest } = splitBySeparators(inputValue + pasted, separators);
28022
+ const tail = rest.trim();
28023
+ addChips(tail === "" ? parts : [...parts, tail]);
28024
+ },
27591
28025
  onChange: (e) => {
27592
28026
  const nextValue = e.target.value;
28027
+ if (separators && !isComposingRef.current) {
28028
+ const { parts, rest } = splitBySeparators(nextValue, separators);
28029
+ if (parts.length > 0 || rest !== nextValue) {
28030
+ addChips(parts, rest);
28031
+ return;
28032
+ }
28033
+ }
27593
28034
  const wasEmpty = inputValue === "";
27594
28035
  const isEmpty = nextValue === "";
27595
28036
  if (wasEmpty !== isEmpty) {
@@ -27604,8 +28045,11 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27604
28045
  onCompositionStart: () => {
27605
28046
  isComposingRef.current = true;
27606
28047
  },
27607
- onCompositionEnd: () => {
28048
+ onCompositionEnd: (e) => {
27608
28049
  isComposingRef.current = false;
28050
+ if (!separators) return;
28051
+ const { parts, rest } = splitBySeparators(e.currentTarget.value, separators);
28052
+ if (parts.length > 0 || rest !== e.currentTarget.value) addChips(parts, rest);
27609
28053
  },
27610
28054
  onFocus: () => {
27611
28055
  setFocused(true);
@@ -27615,6 +28059,8 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
27615
28059
  onBlur: () => {
27616
28060
  setFocused(false);
27617
28061
  setActiveIndex(-1);
28062
+ if (commitOnBlur && inputValue.trim() !== "" && !isMax)
28063
+ addChips([inputValue.trim()]);
27618
28064
  onBlur?.();
27619
28065
  }
27620
28066
  }
@@ -27808,6 +28254,7 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
27808
28254
  }, ref) {
27809
28255
  const f = FONT3[size];
27810
28256
  const [ruleError, setRuleError] = react.useState("");
28257
+ const controlId = useFieldControlId();
27811
28258
  const hasValue = value !== void 0 && value !== null && String(value) !== "";
27812
28259
  useFormField({
27813
28260
  name,
@@ -27828,6 +28275,7 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
27828
28275
  SField,
27829
28276
  {
27830
28277
  label,
28278
+ controlId,
27831
28279
  labelPosition,
27832
28280
  subLabel,
27833
28281
  labelWidth,
@@ -27853,16 +28301,21 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
27853
28301
  ...style
27854
28302
  },
27855
28303
  children: /* @__PURE__ */ jsxRuntime.jsxs(
27856
- "label",
28304
+ "div",
27857
28305
  {
28306
+ role: "presentation",
27858
28307
  className: "flex h-full w-full items-center",
27859
28308
  style: { gap: f.gap, padding: `0 ${f.paddingX}` },
28309
+ onClick: (e) => {
28310
+ if (e.target === e.currentTarget) e.currentTarget.querySelector("input")?.focus();
28311
+ },
27860
28312
  children: [
27861
28313
  prefix,
27862
28314
  /* @__PURE__ */ jsxRuntime.jsx(
27863
28315
  "input",
27864
28316
  {
27865
28317
  ref: mergeRefs(ref, valueTooltip.valueRef),
28318
+ id: controlId,
27866
28319
  name,
27867
28320
  type: "text",
27868
28321
  value: value ?? "",
@@ -27900,7 +28353,7 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
27900
28353
  {
27901
28354
  icon: "close",
27902
28355
  size: "xxs",
27903
- ariaLabel: "clear",
28356
+ ariaLabel: "\uC9C0\uC6B0\uAE30",
27904
28357
  onClick: () => {
27905
28358
  onValueChange?.("");
27906
28359
  setRuleError(runRules("", rules));
@@ -28217,6 +28670,8 @@ function STimePicker({
28217
28670
  const portalContainer = usePortalContainer();
28218
28671
  const triggerRef = react.useRef(null);
28219
28672
  const [open, setOpen] = react.useState(false);
28673
+ const triggerId = useFieldControlId();
28674
+ const triggerTextId = `${triggerId}-text`;
28220
28675
  const [popoverAlignOffset, setPopoverAlignOffset] = react.useState(0);
28221
28676
  const [focused, setFocused] = react.useState(false);
28222
28677
  const [ruleError, setRuleError] = react.useState("");
@@ -28254,6 +28709,7 @@ function STimePicker({
28254
28709
  return /* @__PURE__ */ jsxRuntime.jsx(
28255
28710
  SField,
28256
28711
  {
28712
+ controlId: triggerId,
28257
28713
  label,
28258
28714
  labelPosition,
28259
28715
  subLabel,
@@ -28281,8 +28737,10 @@ function STimePicker({
28281
28737
  "div",
28282
28738
  {
28283
28739
  ref: triggerRef,
28740
+ id: triggerId,
28284
28741
  role: "button",
28285
28742
  "aria-disabled": disabled,
28743
+ "aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerTextId}` : void 0,
28286
28744
  tabIndex: disabled ? -1 : 0,
28287
28745
  "data-name": name,
28288
28746
  onFocus: () => setFocused(true),
@@ -28312,6 +28770,7 @@ function STimePicker({
28312
28770
  /* @__PURE__ */ jsxRuntime.jsx(
28313
28771
  "span",
28314
28772
  {
28773
+ id: triggerTextId,
28315
28774
  className: "flex-1 truncate text-center",
28316
28775
  style: {
28317
28776
  minWidth: dims.minWidth,
@@ -28687,6 +29146,8 @@ function STimeRangePicker({
28687
29146
  const portalContainer = usePortalContainer();
28688
29147
  const triggerRef = react.useRef(null);
28689
29148
  const [open, setOpen] = react.useState(false);
29149
+ const triggerId = useFieldControlId();
29150
+ const triggerTextId = `${triggerId}-text`;
28690
29151
  const [popoverAlignOffset, setPopoverAlignOffset] = react.useState(0);
28691
29152
  const [focused, setFocused] = react.useState(false);
28692
29153
  const [ruleError, setRuleError] = react.useState("");
@@ -28726,6 +29187,7 @@ function STimeRangePicker({
28726
29187
  return /* @__PURE__ */ jsxRuntime.jsx(
28727
29188
  SField,
28728
29189
  {
29190
+ controlId: triggerId,
28729
29191
  label,
28730
29192
  labelPosition,
28731
29193
  subLabel,
@@ -28753,8 +29215,10 @@ function STimeRangePicker({
28753
29215
  "div",
28754
29216
  {
28755
29217
  ref: triggerRef,
29218
+ id: triggerId,
28756
29219
  role: "button",
28757
29220
  "aria-disabled": disabled,
29221
+ "aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerTextId}` : void 0,
28758
29222
  tabIndex: disabled ? -1 : 0,
28759
29223
  "data-name": name,
28760
29224
  onFocus: () => setFocused(true),
@@ -28784,6 +29248,7 @@ function STimeRangePicker({
28784
29248
  /* @__PURE__ */ jsxRuntime.jsx(
28785
29249
  "span",
28786
29250
  {
29251
+ id: triggerTextId,
28787
29252
  className: "flex-1 truncate text-center",
28788
29253
  style: {
28789
29254
  minWidth: dims.minWidth,
@@ -28990,6 +29455,7 @@ exports.SSearchInput = SSearchInput;
28990
29455
  exports.SSectionHeaderCard = SSectionHeaderCard2;
28991
29456
  exports.SSelect = SSelect2;
28992
29457
  exports.SSkeleton = SSkeleton;
29458
+ exports.SSlider = SSlider;
28993
29459
  exports.SSplitter = SSplitter2;
28994
29460
  exports.SStepper = SStepper;
28995
29461
  exports.SSwitch = SSwitch;
@@ -29023,6 +29489,7 @@ exports.TIMEPICKER_SIZES = TIMEPICKER_SIZES;
29023
29489
  exports.TIMEPICKER_SIZE_CONFIG = TIMEPICKER_SIZE_CONFIG;
29024
29490
  exports.buttonPreset = buttonPreset;
29025
29491
  exports.cn = cn;
29492
+ exports.fieldLabelId = fieldLabelId;
29026
29493
  exports.fieldWidthValue = fieldWidthValue;
29027
29494
  exports.fileExtension = fileExtension;
29028
29495
  exports.findGnbAncestors = findGnbAncestors;