@bestow-ui/react 1.4.0 → 1.5.0

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
@@ -143,7 +143,7 @@ module.exports = __toCommonJS(src_exports);
143
143
 
144
144
  // src/theme/contract.css.ts
145
145
  var themeClass = { light: "tkagtb0", dark: "tkagtb1" };
146
- var vars = { space: { "0": "var(--bestow-space-0)", "1": "var(--bestow-space-1)", "2": "var(--bestow-space-2)", "3": "var(--bestow-space-3)", "4": "var(--bestow-space-4)", "5": "var(--bestow-space-5)", "6": "var(--bestow-space-6)", "8": "var(--bestow-space-8)", "10": "var(--bestow-space-10)", "12": "var(--bestow-space-12)", "16": "var(--bestow-space-16)" }, radius: { none: "var(--bestow-radius-none)", sm: "var(--bestow-radius-sm)", md: "var(--bestow-radius-md)", lg: "var(--bestow-radius-lg)", xl: "var(--bestow-radius-xl)", full: "var(--bestow-radius-full)" }, font: { family: { sans: "var(--bestow-font-family-sans)", mono: "var(--bestow-font-family-mono)" }, size: { xs: "var(--bestow-font-size-xs)", sm: "var(--bestow-font-size-sm)", md: "var(--bestow-font-size-md)", lg: "var(--bestow-font-size-lg)", xl: "var(--bestow-font-size-xl)", "2xl": "var(--bestow-font-size-2xl)", "3xl": "var(--bestow-font-size-3xl)", "4xl": "var(--bestow-font-size-4xl)" }, weight: { regular: "var(--bestow-font-weight-regular)", medium: "var(--bestow-font-weight-medium)", semibold: "var(--bestow-font-weight-semibold)", bold: "var(--bestow-font-weight-bold)" }, lineHeight: { tight: "var(--bestow-font-line-height-tight)", normal: "var(--bestow-font-line-height-normal)", relaxed: "var(--bestow-font-line-height-relaxed)" } }, duration: { fast: "var(--bestow-duration-fast)", normal: "var(--bestow-duration-normal)", slow: "var(--bestow-duration-slow)" }, zIndex: { overlay: "var(--bestow-z-index-overlay)", modal: "var(--bestow-z-index-modal)", dropdown: "var(--bestow-z-index-dropdown)", toast: "var(--bestow-z-index-toast)", tooltip: "var(--bestow-z-index-tooltip)" }, color: { bg: { "default": "var(--bestow-color-bg-default)", subtle: "var(--bestow-color-bg-subtle)", muted: "var(--bestow-color-bg-muted)", inverse: "var(--bestow-color-bg-inverse)", overlay: "var(--bestow-color-bg-overlay)" }, fg: { "default": "var(--bestow-color-fg-default)", muted: "var(--bestow-color-fg-muted)", subtle: "var(--bestow-color-fg-subtle)", inverse: "var(--bestow-color-fg-inverse)" }, border: { "default": "var(--bestow-color-border-default)", strong: "var(--bestow-color-border-strong)", focus: "var(--bestow-color-border-focus)", input: "var(--bestow-color-border-input)", inputHover: "var(--bestow-color-border-input-hover)" }, primary: { solid: "var(--bestow-color-primary-solid)", solidHover: "var(--bestow-color-primary-solid-hover)", contrast: "var(--bestow-color-primary-contrast)", subtle: "var(--bestow-color-primary-subtle)", subtleHover: "var(--bestow-color-primary-subtle-hover)", text: "var(--bestow-color-primary-text)" }, neutral: { solid: "var(--bestow-color-neutral-solid)", solidHover: "var(--bestow-color-neutral-solid-hover)", contrast: "var(--bestow-color-neutral-contrast)", subtle: "var(--bestow-color-neutral-subtle)", subtleHover: "var(--bestow-color-neutral-subtle-hover)", text: "var(--bestow-color-neutral-text)" }, danger: { solid: "var(--bestow-color-danger-solid)", solidHover: "var(--bestow-color-danger-solid-hover)", contrast: "var(--bestow-color-danger-contrast)", subtle: "var(--bestow-color-danger-subtle)", subtleHover: "var(--bestow-color-danger-subtle-hover)", text: "var(--bestow-color-danger-text)" }, success: { solid: "var(--bestow-color-success-solid)", solidHover: "var(--bestow-color-success-solid-hover)", contrast: "var(--bestow-color-success-contrast)", subtle: "var(--bestow-color-success-subtle)", subtleHover: "var(--bestow-color-success-subtle-hover)", text: "var(--bestow-color-success-text)" }, warning: { solid: "var(--bestow-color-warning-solid)", solidHover: "var(--bestow-color-warning-solid-hover)", contrast: "var(--bestow-color-warning-contrast)", subtle: "var(--bestow-color-warning-subtle)", subtleHover: "var(--bestow-color-warning-subtle-hover)", text: "var(--bestow-color-warning-text)" } }, shadow: { sm: "var(--bestow-shadow-sm)", md: "var(--bestow-shadow-md)", lg: "var(--bestow-shadow-lg)" } };
146
+ var vars = { space: { "0": "var(--bestow-space-0)", "1": "var(--bestow-space-1)", "2": "var(--bestow-space-2)", "3": "var(--bestow-space-3)", "4": "var(--bestow-space-4)", "5": "var(--bestow-space-5)", "6": "var(--bestow-space-6)", "8": "var(--bestow-space-8)", "10": "var(--bestow-space-10)", "12": "var(--bestow-space-12)", "16": "var(--bestow-space-16)" }, radius: { none: "var(--bestow-radius-none)", sm: "var(--bestow-radius-sm)", md: "var(--bestow-radius-md)", lg: "var(--bestow-radius-lg)", xl: "var(--bestow-radius-xl)", full: "var(--bestow-radius-full)" }, font: { family: { sans: "var(--bestow-font-family-sans)", mono: "var(--bestow-font-family-mono)" }, size: { xs: "var(--bestow-font-size-xs)", sm: "var(--bestow-font-size-sm)", md: "var(--bestow-font-size-md)", lg: "var(--bestow-font-size-lg)", xl: "var(--bestow-font-size-xl)", "2xl": "var(--bestow-font-size-2xl)", "3xl": "var(--bestow-font-size-3xl)", "4xl": "var(--bestow-font-size-4xl)" }, weight: { regular: "var(--bestow-font-weight-regular)", medium: "var(--bestow-font-weight-medium)", semibold: "var(--bestow-font-weight-semibold)", bold: "var(--bestow-font-weight-bold)" }, lineHeight: { tight: "var(--bestow-font-line-height-tight)", normal: "var(--bestow-font-line-height-normal)", relaxed: "var(--bestow-font-line-height-relaxed)" } }, duration: { fast: "var(--bestow-duration-fast)", normal: "var(--bestow-duration-normal)", slow: "var(--bestow-duration-slow)" }, zIndex: { overlay: "var(--bestow-z-index-overlay)", modal: "var(--bestow-z-index-modal)", dropdown: "var(--bestow-z-index-dropdown)", toast: "var(--bestow-z-index-toast)", tooltip: "var(--bestow-z-index-tooltip)" }, color: { bg: { "default": "var(--bestow-color-bg-default)", subtle: "var(--bestow-color-bg-subtle)", muted: "var(--bestow-color-bg-muted)", inverse: "var(--bestow-color-bg-inverse)", overlay: "var(--bestow-color-bg-overlay)" }, fg: { "default": "var(--bestow-color-fg-default)", muted: "var(--bestow-color-fg-muted)", subtle: "var(--bestow-color-fg-subtle)", inverse: "var(--bestow-color-fg-inverse)" }, border: { "default": "var(--bestow-color-border-default)", strong: "var(--bestow-color-border-strong)", focus: "var(--bestow-color-border-focus)", input: "var(--bestow-color-border-input)", inputHover: "var(--bestow-color-border-input-hover)", control: "var(--bestow-color-border-control)" }, primary: { solid: "var(--bestow-color-primary-solid)", solidHover: "var(--bestow-color-primary-solid-hover)", contrast: "var(--bestow-color-primary-contrast)", subtle: "var(--bestow-color-primary-subtle)", subtleHover: "var(--bestow-color-primary-subtle-hover)", text: "var(--bestow-color-primary-text)" }, neutral: { solid: "var(--bestow-color-neutral-solid)", solidHover: "var(--bestow-color-neutral-solid-hover)", contrast: "var(--bestow-color-neutral-contrast)", subtle: "var(--bestow-color-neutral-subtle)", subtleHover: "var(--bestow-color-neutral-subtle-hover)", text: "var(--bestow-color-neutral-text)" }, danger: { solid: "var(--bestow-color-danger-solid)", solidHover: "var(--bestow-color-danger-solid-hover)", contrast: "var(--bestow-color-danger-contrast)", subtle: "var(--bestow-color-danger-subtle)", subtleHover: "var(--bestow-color-danger-subtle-hover)", text: "var(--bestow-color-danger-text)" }, success: { solid: "var(--bestow-color-success-solid)", solidHover: "var(--bestow-color-success-solid-hover)", contrast: "var(--bestow-color-success-contrast)", subtle: "var(--bestow-color-success-subtle)", subtleHover: "var(--bestow-color-success-subtle-hover)", text: "var(--bestow-color-success-text)" }, warning: { solid: "var(--bestow-color-warning-solid)", solidHover: "var(--bestow-color-warning-solid-hover)", contrast: "var(--bestow-color-warning-contrast)", subtle: "var(--bestow-color-warning-subtle)", subtleHover: "var(--bestow-color-warning-subtle-hover)", text: "var(--bestow-color-warning-text)" } }, shadow: { sm: "var(--bestow-shadow-sm)", md: "var(--bestow-shadow-md)", lg: "var(--bestow-shadow-lg)" } };
147
147
 
148
148
  // src/theme/color.ts
149
149
  var import_tokens = require("@bestow-ui/tokens");
@@ -326,9 +326,11 @@ function themeToStyle(theme, mode) {
326
326
  // src/theme/presets.ts
327
327
  var strongInputBorders = /* @__PURE__ */ Object.freeze({
328
328
  // 라이트: gray600 / gray700 — 흰 배경 5.18:1, bg.subtle(gray50) 4.91:1
329
- light: { color: { border: { input: "#666e76", inputHover: "#495057" } } },
329
+ // 체크 컨트롤: gray600 — 흰 배경 5.18:1
330
+ light: { color: { border: { input: "#666e76", inputHover: "#495057", control: "#666e76" } } },
330
331
  // 다크: gray500 / gray300 — gray600 은 bg.subtle(gray900) 위에서 2.98:1 이라 한 단계 밝게. bg.default 8.57:1, bg.subtle 7.43:1
331
- dark: { color: { border: { input: "#adb5bd", inputHover: "#dee2e6" } } }
332
+ // 체크 컨트롤: gray500 — 다크 기본값(border.strong)과 같다(이미 8.57:1)
333
+ dark: { color: { border: { input: "#adb5bd", inputHover: "#dee2e6", control: "#adb5bd" } } }
332
334
  });
333
335
 
334
336
  // src/i18n/en.ts
@@ -2139,6 +2141,7 @@ var toggle2 = "c2pbq71 c2pbq70";
2139
2141
 
2140
2142
  // src/components/Combobox/Combobox.tsx
2141
2143
  var import_jsx_runtime29 = require("react/jsx-runtime");
2144
+ var NO_RESULTS_ANNOUNCE_DELAY = 800;
2142
2145
  var defaultFilter = (option2, query) => option2.label.toLowerCase().includes(query.trim().toLowerCase());
2143
2146
  var Combobox = /* @__PURE__ */ (0, import_react32.forwardRef)(function Combobox2(props, ref) {
2144
2147
  const {
@@ -2264,6 +2267,14 @@ var Combobox = /* @__PURE__ */ (0, import_react32.forwardRef)(function Combobox2
2264
2267
  document.getElementById(optionId(activeIndex))?.scrollIntoView?.({ block: "nearest" });
2265
2268
  }, [open, activeIndex]);
2266
2269
  const hasOptions = filtered.length > 0;
2270
+ const noResults = open && !disabled && !hasOptions;
2271
+ const [announcedFor, setAnnouncedFor] = (0, import_react32.useState)(void 0);
2272
+ (0, import_react32.useEffect)(() => {
2273
+ if (!noResults) return;
2274
+ const timer2 = setTimeout(() => setAnnouncedFor(query), NO_RESULTS_ANNOUNCE_DELAY);
2275
+ return () => clearTimeout(timer2);
2276
+ }, [noResults, query]);
2277
+ const announceNoResults = noResults && announcedFor === query;
2267
2278
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: cx(field, fullWidth && fieldFullWidth), children: [
2268
2279
  label7 && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("label", { id: labelId, htmlFor: inputId, className: label2, children: [
2269
2280
  label7,
@@ -2393,10 +2404,14 @@ var Combobox = /* @__PURE__ */ (0, import_react32.forwardRef)(function Combobox2
2393
2404
  );
2394
2405
  })
2395
2406
  }
2396
- ) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: empty, role: "status", children: emptyMessage })
2407
+ ) : (
2408
+ // 스크린리더 알림은 아래의 항상 있는 status 영역이 맡는다 (여기는 화면 표시만)
2409
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: empty, children: emptyMessage })
2410
+ )
2397
2411
  }
2398
2412
  ) })
2399
2413
  ] }),
2414
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { role: "status", className: visuallyHidden, children: announceNoResults ? emptyMessage : "" }),
2400
2415
  name && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("input", { type: "hidden", name, value: selectedValue ?? "" }),
2401
2416
  helperText3 && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { id: helperId, className: helperText({ error }), children: helperText3 })
2402
2417
  ] });
@@ -2863,6 +2878,7 @@ var root10 = "r2jefr0";
2863
2878
 
2864
2879
  // src/components/FileUpload/FileUpload.tsx
2865
2880
  var import_jsx_runtime35 = require("react/jsx-runtime");
2881
+ var REMOVED_ANNOUNCE_DELAY = 500;
2866
2882
  function formatSize(bytes) {
2867
2883
  if (bytes < 1024) return `${bytes} B`;
2868
2884
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(bytes < 10 * 1024 ? 1 : 0)} KB`;
@@ -2913,6 +2929,8 @@ var FileUpload = /* @__PURE__ */ (0, import_react37.forwardRef)(function FileUpl
2913
2929
  const selectButtonRef = (0, import_react37.useRef)(null);
2914
2930
  const removeButtonRefs = (0, import_react37.useRef)([]);
2915
2931
  const focusAfterRemove = (0, import_react37.useRef)(null);
2932
+ const announceTimer = (0, import_react37.useRef)(void 0);
2933
+ (0, import_react37.useEffect)(() => () => clearTimeout(announceTimer.current), []);
2916
2934
  const selectLabel = m.fileSelect ?? "\uD30C\uC77C \uC120\uD0DD";
2917
2935
  const reasonText = (rejection) => {
2918
2936
  const fileName2 = rejection.file.name;
@@ -2921,10 +2939,16 @@ var FileUpload = /* @__PURE__ */ (0, import_react37.forwardRef)(function FileUpl
2921
2939
  if (rejection.reason === "type") return m.fileTypeNotAllowed?.(fileName2) ?? `${fileName2}: \uC62C\uB9B4 \uC218 \uC5C6\uB294 \uD615\uC2DD\uC774\uC5D0\uC694.`;
2922
2940
  return m.fileTooMany?.(maxFiles ?? 0) ?? `\uD30C\uC77C\uC740 ${maxFiles}\uAC1C\uAE4C\uC9C0 \uC62C\uB9B4 \uC218 \uC788\uC5B4\uC694.`;
2923
2941
  };
2924
- const commit = (next, message) => {
2942
+ const announce = (message, delay = 0) => {
2943
+ clearTimeout(announceTimer.current);
2944
+ if (delay === 0) return setAnnouncement(message);
2945
+ setAnnouncement("");
2946
+ announceTimer.current = setTimeout(() => setAnnouncement(message), delay);
2947
+ };
2948
+ const commit = (next, message, delay = 0) => {
2925
2949
  if (!isControlled) setInnerFiles(next);
2926
2950
  onValueChange?.(next);
2927
- setAnnouncement(message);
2951
+ announce(message, delay);
2928
2952
  };
2929
2953
  const addFiles = (incoming) => {
2930
2954
  if (disabled || incoming.length === 0) return;
@@ -2945,14 +2969,14 @@ var FileUpload = /* @__PURE__ */ (0, import_react37.forwardRef)(function FileUpl
2945
2969
  const summary = m.filesSelected?.(next.length) ?? `\uD30C\uC77C ${next.length}\uAC1C \uC120\uD0DD\uB428`;
2946
2970
  const errors2 = [...new Set(rejected.map(reasonText))].join(" ");
2947
2971
  if (accepted.length > 0) commit(next, [summary, errors2].filter(Boolean).join(" "));
2948
- else setAnnouncement(errors2);
2972
+ else announce(errors2);
2949
2973
  };
2950
2974
  const removeAt = (index) => {
2951
2975
  const removed = files[index];
2952
2976
  const next = files.filter((_, i) => i !== index);
2953
2977
  focusAfterRemove.current = Math.min(index, next.length - 1);
2954
2978
  setRejections([]);
2955
- commit(next, m.fileRemoved?.(removed.name) ?? `${removed.name} \uC0AD\uC81C\uB428`);
2979
+ commit(next, m.fileRemoved?.(removed.name) ?? `${removed.name} \uC0AD\uC81C\uB428`, REMOVED_ANNOUNCE_DELAY);
2956
2980
  };
2957
2981
  (0, import_react37.useEffect)(() => {
2958
2982
  if (focusAfterRemove.current === null) return;
@@ -3041,7 +3065,7 @@ var FileUpload = /* @__PURE__ */ (0, import_react37.forwardRef)(function FileUpl
3041
3065
  files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { "aria-label": m.fileList ?? "\uC120\uD0DD\uD55C \uD30C\uC77C", className: list4, children: files.map((file, index) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: item6, children: [
3042
3066
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: fileIcon, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_icons14.FileIcon, { size: 16 }) }),
3043
3067
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: fileName, children: file.name }),
3044
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: fileSize, children: formatSize(file.size) }),
3068
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { id: `${baseId}-size-${index}`, className: fileSize, children: formatSize(file.size) }),
3045
3069
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
3046
3070
  IconButton,
3047
3071
  {
@@ -3050,6 +3074,7 @@ var FileUpload = /* @__PURE__ */ (0, import_react37.forwardRef)(function FileUpl
3050
3074
  },
3051
3075
  size: "sm",
3052
3076
  "aria-label": m.fileRemove?.(file.name) ?? `${file.name} \uC0AD\uC81C`,
3077
+ "aria-describedby": `${baseId}-size-${index}`,
3053
3078
  disabled,
3054
3079
  onClick: () => removeAt(index),
3055
3080
  children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_icons14.XIcon, {})
@@ -3256,9 +3281,12 @@ var NumberInput = /* @__PURE__ */ (0, import_react39.forwardRef)(
3256
3281
  if (parse(field4.raw) !== next) setRaw(toRaw(next));
3257
3282
  }, [value4]);
3258
3283
  const current2 = parse(field4.raw);
3259
- const setValue = (next) => {
3284
+ const valueText = (n) => `${format2(toRaw(n))}${unit2 ?? ""}`;
3285
+ const [announcement, setAnnouncement] = (0, import_react39.useState)("");
3286
+ const setValue = (next, announce = true) => {
3260
3287
  setRaw(toRaw(next));
3261
3288
  emit(next);
3289
+ if (announce) setAnnouncement(valueText(next));
3262
3290
  };
3263
3291
  const stepBy = (delta) => {
3264
3292
  if (disabled || readOnly) return;
@@ -3314,13 +3342,17 @@ var NumberInput = /* @__PURE__ */ (0, import_react39.forwardRef)(
3314
3342
  "aria-valuenow": current2 ?? void 0,
3315
3343
  "aria-valuemin": min,
3316
3344
  "aria-valuemax": max,
3317
- "aria-valuetext": current2 === null ? void 0 : `${field4.display}${unit2 ?? ""}`,
3345
+ "aria-valuetext": current2 === null ? void 0 : valueText(current2),
3318
3346
  value: field4.display,
3319
- onChange: field4.onChange,
3347
+ onChange: (event) => {
3348
+ setAnnouncement("");
3349
+ field4.onChange(event);
3350
+ },
3320
3351
  onKeyDown: handleKeyDown,
3321
3352
  onBlur: (event) => {
3322
- if (current2 !== null && clamp(current2) !== current2) setValue(clamp(current2));
3353
+ if (current2 !== null && clamp(current2) !== current2) setValue(clamp(current2), false);
3323
3354
  else if (current2 === null && field4.raw !== "") setRaw("");
3355
+ setAnnouncement("");
3324
3356
  onBlur?.(event);
3325
3357
  },
3326
3358
  size,
@@ -3335,7 +3367,8 @@ var NumberInput = /* @__PURE__ */ (0, import_react39.forwardRef)(
3335
3367
  ...rest
3336
3368
  }
3337
3369
  ),
3338
- name && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("input", { type: "hidden", name, value: current2 === null ? "" : String(current2) })
3370
+ name && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("input", { type: "hidden", name, value: current2 === null ? "" : String(current2) }),
3371
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { "aria-live": "assertive", "aria-atomic": "true", className: visuallyHidden, children: announcement })
3339
3372
  ] });
3340
3373
  }
3341
3374
  );