@ceed/cds 1.39.0 → 1.40.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
@@ -417,13 +417,17 @@ var IconButton_default = IconButton;
417
417
  var import_react5 = require("react");
418
418
  function useControlledState(controlledValue, defaultValue, onChange) {
419
419
  const { current: isControlled } = (0, import_react5.useRef)(controlledValue !== void 0);
420
- const [internalValue, setInternalValue] = (0, import_react5.useState)(controlledValue ?? defaultValue);
421
- const displayValue = isControlled ? controlledValue : internalValue;
420
+ const [internalValue, setInternalValue] = (0, import_react5.useState)(controlledValue !== void 0 ? controlledValue : defaultValue);
421
+ const hasWarnedRef = (0, import_react5.useRef)(false);
422
422
  (0, import_react5.useEffect)(() => {
423
- if (isControlled) {
424
- setInternalValue(controlledValue);
425
- }
423
+ if (false) return;
424
+ if (hasWarnedRef.current || isControlled === (controlledValue !== void 0)) return;
425
+ hasWarnedRef.current = true;
426
+ console.warn(
427
+ isControlled ? "useControlledState: A component is changing a controlled input to be uncontrolled. Pass `null` instead of `undefined` to keep it controlled with an empty value." : "useControlledState: A component is changing an uncontrolled input to be controlled. Pass `null` instead of `undefined` for the initial empty value to make it controlled from the first render."
428
+ );
426
429
  }, [isControlled, controlledValue]);
430
+ const displayValue = isControlled ? controlledValue : internalValue;
427
431
  const handleChange = (0, import_react5.useCallback)(
428
432
  (value) => {
429
433
  const newValue = typeof value === "function" ? value(displayValue) : value;
@@ -530,6 +534,7 @@ var AutocompleteListBox = import_react6.default.forwardRef((props, ref) => {
530
534
  )
531
535
  ))));
532
536
  });
537
+ var EMPTY_MULTIPLE_VALUE = [];
533
538
  var autocompleteDeleteSize = {
534
539
  sm: "20px",
535
540
  md: "24px",
@@ -572,9 +577,9 @@ function Autocomplete(props) {
572
577
  className,
573
578
  ...innerProps
574
579
  } = props;
575
- const [_value, setValue] = useControlledState(
580
+ const [rawValue, setValue] = useControlledState(
576
581
  props.value,
577
- props.defaultValue || "",
582
+ props.defaultValue ?? null,
578
583
  (0, import_react6.useCallback)(
579
584
  (value2) => onChange?.({
580
585
  target: {
@@ -585,6 +590,7 @@ function Autocomplete(props) {
585
590
  [onChange, props.name]
586
591
  )
587
592
  );
593
+ const _value = rawValue ?? (props.multiple ? EMPTY_MULTIPLE_VALUE : "");
588
594
  const options = (0, import_react6.useMemo)(
589
595
  () => props.options.map((option) => {
590
596
  if (typeof option !== "object") {
@@ -645,7 +651,7 @@ function Autocomplete(props) {
645
651
  (event, value2) => {
646
652
  const newValue = value2;
647
653
  const _value2 = Array.isArray(newValue) ? newValue.map((value3) => value3.value) : newValue?.value;
648
- setValue(_value2);
654
+ setValue(_value2 ?? null);
649
655
  if (Array.isArray(newValue) && newValue.map((value3) => optionMap.get(value3.value)) || optionMap.get(newValue?.value)) {
650
656
  onChangeComplete?.({
651
657
  ...event,
@@ -1967,6 +1973,10 @@ var Input = import_react18.default.forwardRef((props, ref) => {
1967
1973
  onChange,
1968
1974
  name,
1969
1975
  type,
1976
+ // NOTE: value/defaultValue는 아래에서 직접 다루므로 innerProps 스프레드에서 제외한다.
1977
+ // defaultValue가 스프레드에 남으면 항상 정의되는 value와 함께 DOM input에 도달해 React가 경고한다.
1978
+ value: valueProp,
1979
+ defaultValue: defaultValueProp,
1970
1980
  // NOTE: 스타일 관련된 props는 최상위 엘리먼트에 적용한다.
1971
1981
  sx,
1972
1982
  className,
@@ -1979,8 +1989,8 @@ var Input = import_react18.default.forwardRef((props, ref) => {
1979
1989
  }
1980
1990
  const [passwordVisible, setPasswordVisible] = (0, import_react18.useState)(false);
1981
1991
  const [value, setValue] = useControlledState(
1982
- props.value,
1983
- props.defaultValue,
1992
+ valueProp,
1993
+ defaultValueProp ?? null,
1984
1994
  (0, import_react18.useCallback)(
1985
1995
  (value2) => {
1986
1996
  onChange?.({
@@ -1999,8 +2009,9 @@ var Input = import_react18.default.forwardRef((props, ref) => {
1999
2009
  setValue(e.target.value);
2000
2010
  };
2001
2011
  const handleClear = () => {
2002
- setValue(props.defaultValue || "");
2012
+ setValue(defaultValueProp || "");
2003
2013
  };
2014
+ const displayValue = valueProp !== void 0 ? valueProp : value;
2004
2015
  const handleTogglePasswordVisibility = () => {
2005
2016
  setPasswordVisible((prev) => !prev);
2006
2017
  };
@@ -2010,7 +2021,7 @@ var Input = import_react18.default.forwardRef((props, ref) => {
2010
2021
  const input = /* @__PURE__ */ import_react18.default.createElement(
2011
2022
  MotionInput,
2012
2023
  {
2013
- value,
2024
+ value: displayValue ?? "",
2014
2025
  name,
2015
2026
  onChange: handleChange,
2016
2027
  required,
@@ -2031,7 +2042,7 @@ var Input = import_react18.default.forwardRef((props, ref) => {
2031
2042
  "aria-label": passwordVisible ? "Hide password" : "Show password"
2032
2043
  },
2033
2044
  passwordVisible ? /* @__PURE__ */ import_react18.default.createElement(import_VisibilityOff.default, null) : /* @__PURE__ */ import_react18.default.createElement(import_Visibility.default, null)
2034
- )) : null : enableClearable ? /* @__PURE__ */ import_react18.default.createElement(Stack_default, { gap: 1, direction: "row" }, innerProps.endDecorator, value && /* @__PURE__ */ import_react18.default.createElement(
2045
+ )) : null : enableClearable ? /* @__PURE__ */ import_react18.default.createElement(Stack_default, { gap: 1, direction: "row" }, innerProps.endDecorator, displayValue && /* @__PURE__ */ import_react18.default.createElement(
2035
2046
  IconButton_default,
2036
2047
  {
2037
2048
  onMouseDown: (e) => e.preventDefault(),
@@ -2269,7 +2280,7 @@ var CurrencyInput = import_react19.default.forwardRef(function CurrencyInput2(in
2269
2280
  const { symbol, thousandSeparator, decimalSeparator, placeholder, fixedDecimalScale, decimalScale } = useCurrencySetting(props);
2270
2281
  const [_value, setValue] = useControlledState(
2271
2282
  props.value,
2272
- props.defaultValue,
2283
+ props.defaultValue ?? null,
2273
2284
  (0, import_react19.useCallback)((value2) => onChange?.({ target: { name, value: value2 } }), [onChange, name])
2274
2285
  );
2275
2286
  const value = (0, import_react19.useMemo)(() => {
@@ -2284,22 +2295,17 @@ var CurrencyInput = import_react19.default.forwardRef(function CurrencyInput2(in
2284
2295
  }
2285
2296
  return props.max;
2286
2297
  }, [props.max, useMinorUnit, decimalScale]);
2287
- const [isOverLimit, setIsOverLimit] = (0, import_react19.useState)(!!max && !!value && value > max);
2298
+ const isOverLimit = (0, import_react19.useMemo)(() => max != null && value != null && value > max, [max, value]);
2288
2299
  const handleChange = (0, import_react19.useCallback)(
2289
2300
  (event) => {
2290
2301
  if (event.target.value === "") {
2291
- setValue(void 0);
2302
+ setValue(null);
2292
2303
  return;
2293
2304
  }
2294
2305
  const amount = useMinorUnit ? Number(decimalSeparator ? event.target.value?.replace(decimalSeparator, "") : event.target.value) : Number(event.target.value);
2295
- if (!!max && Number(event.target.value) > max) {
2296
- setIsOverLimit(true);
2297
- } else {
2298
- setIsOverLimit(false);
2299
- }
2300
2306
  setValue(amount);
2301
2307
  },
2302
- [decimalSeparator, max, useMinorUnit, setValue]
2308
+ [decimalSeparator, useMinorUnit, setValue]
2303
2309
  );
2304
2310
  return /* @__PURE__ */ import_react19.default.createElement(
2305
2311
  CurrencyInputRoot,
@@ -2977,11 +2983,15 @@ var DatePicker = (0, import_react21.forwardRef)((inProps, ref) => {
2977
2983
  } = props;
2978
2984
  const innerRef = (0, import_react21.useRef)(null);
2979
2985
  const buttonRef = (0, import_react21.useRef)(null);
2980
- const [value, setValue] = useControlledState(
2986
+ const [rawValue, setValue] = useControlledState(
2981
2987
  props.value,
2982
- props.defaultValue || "",
2983
- (0, import_react21.useCallback)((value2) => onChange?.({ target: { name: props.name, value: value2 } }), [props.name, onChange])
2988
+ props.defaultValue ?? null,
2989
+ (0, import_react21.useCallback)(
2990
+ (value2) => onChange?.({ target: { name: props.name, value: value2 ?? "" } }),
2991
+ [props.name, onChange]
2992
+ )
2984
2993
  );
2994
+ const value = rawValue ?? "";
2985
2995
  const isAlphabeticDisplay = hasAlphabeticMonth(displayFormat);
2986
2996
  const [displayValue, setDisplayValue] = (0, import_react21.useState)(
2987
2997
  () => value ? formatValueString(parseDate(value, format), displayFormat, locale) : ""
@@ -4088,8 +4098,10 @@ function useDataTableRenderer({
4088
4098
  () => pinnedColumns?.right?.filter((f) => visibleFieldSet.has(f)),
4089
4099
  [pinnedColumns?.right, visibleFieldSet]
4090
4100
  );
4101
+ const inferredFieldsKey = JSON.stringify(Object.keys(rows[0] || {}));
4102
+ const inferredFields = (0, import_react26.useMemo)(() => JSON.parse(inferredFieldsKey), [inferredFieldsKey]);
4091
4103
  const columns = (0, import_react26.useMemo)(() => {
4092
- const baseColumns = visibleColumns.length > 0 ? visibleColumns : reorderedColumns.length > 0 ? [] : Object.keys(rows[0] || {}).map((key) => ({
4104
+ const baseColumns = visibleColumns.length > 0 ? visibleColumns : reorderedColumns.length > 0 ? [] : inferredFields.map((key) => ({
4093
4105
  field: key
4094
4106
  }));
4095
4107
  return baseColumns.map((column) => {
@@ -4113,7 +4125,7 @@ function useDataTableRenderer({
4113
4125
  }, [
4114
4126
  visibleColumns,
4115
4127
  reorderedColumns,
4116
- rows,
4128
+ inferredFields,
4117
4129
  effectivePinnedLeft,
4118
4130
  effectivePinnedRight,
4119
4131
  visibleColumnsByField,
@@ -5264,11 +5276,15 @@ var DateRangePicker = (0, import_react30.forwardRef)((inProps, ref) => {
5264
5276
  } = props;
5265
5277
  const innerRef = (0, import_react30.useRef)(null);
5266
5278
  const buttonRef = (0, import_react30.useRef)(null);
5267
- const [value, setValue] = useControlledState(
5279
+ const [rawValue, setValue] = useControlledState(
5268
5280
  props.value,
5269
- props.defaultValue || "",
5270
- (0, import_react30.useCallback)((value2) => onChange?.({ target: { name: props.name, value: value2 } }), [props.name, onChange])
5281
+ props.defaultValue ?? null,
5282
+ (0, import_react30.useCallback)(
5283
+ (value2) => onChange?.({ target: { name: props.name, value: value2 ?? "" } }),
5284
+ [props.name, onChange]
5285
+ )
5271
5286
  );
5287
+ const value = rawValue ?? "";
5272
5288
  const isAlphabeticDisplay = hasAlphabeticMonth2(displayFormat);
5273
5289
  const [displayValue, setDisplayValue] = (0, import_react30.useState)(
5274
5290
  () => value ? formatValueString2(parseDates(value, format), displayFormat, locale) : ""
@@ -5639,6 +5655,7 @@ var import_react35 = __toESM(require("react"));
5639
5655
  var import_Search = __toESM(require("@mui/icons-material/Search"));
5640
5656
  var import_joy45 = require("@mui/joy");
5641
5657
  var import_react_virtual3 = require("@tanstack/react-virtual");
5658
+ var EMPTY_VALUE = [];
5642
5659
  function LabelWithTooltip(props) {
5643
5660
  const { label, size } = props;
5644
5661
  const labelContentRef = (0, import_react35.useRef)(null);
@@ -5686,11 +5703,12 @@ function FilterableCheckboxGroup(inProps) {
5686
5703
  } = props;
5687
5704
  const [searchTerm, setSearchTerm] = (0, import_react35.useState)("");
5688
5705
  const [sortedOptions, setSortedOptions] = (0, import_react35.useState)(options);
5689
- const [internalValue, setInternalValue] = useControlledState(
5706
+ const [rawValue, setInternalValue] = useControlledState(
5690
5707
  value,
5691
- value ?? [],
5692
- (0, import_react35.useCallback)((newValue) => onChange?.(newValue), [onChange])
5708
+ value ?? EMPTY_VALUE,
5709
+ (0, import_react35.useCallback)((newValue) => onChange?.(newValue ?? []), [onChange])
5693
5710
  );
5711
+ const internalValue = rawValue ?? EMPTY_VALUE;
5694
5712
  const parentRef = (0, import_react35.useRef)(null);
5695
5713
  const isInitialSortRef = (0, import_react35.useRef)(false);
5696
5714
  const prevOptionsRef = (0, import_react35.useRef)([...options]);
@@ -6186,11 +6204,15 @@ var MonthPicker = (0, import_react39.forwardRef)((inProps, ref) => {
6186
6204
  } = props;
6187
6205
  const innerRef = (0, import_react39.useRef)(null);
6188
6206
  const buttonRef = (0, import_react39.useRef)(null);
6189
- const [value, setValue, isControlled] = useControlledState(
6207
+ const [rawValue, setValue, isControlled] = useControlledState(
6190
6208
  props.value,
6191
- props.defaultValue || "",
6192
- (0, import_react39.useCallback)((value2) => onChange?.({ target: { name: props.name, value: value2 } }), [props.name, onChange])
6209
+ props.defaultValue ?? null,
6210
+ (0, import_react39.useCallback)(
6211
+ (value2) => onChange?.({ target: { name: props.name, value: value2 ?? "" } }),
6212
+ [props.name, onChange]
6213
+ )
6193
6214
  );
6215
+ const value = rawValue ?? "";
6194
6216
  const getFormattedDisplayValue = (0, import_react39.useCallback)(
6195
6217
  (inputValue) => {
6196
6218
  if (!inputValue) return "";
@@ -6472,11 +6494,15 @@ var MonthRangePicker = (0, import_react40.forwardRef)((inProps, ref) => {
6472
6494
  const isAlphabeticDisplay = hasAlphabeticMonth3(displayFormat);
6473
6495
  const innerRef = (0, import_react40.useRef)(null);
6474
6496
  const buttonRef = (0, import_react40.useRef)(null);
6475
- const [value, setValue] = useControlledState(
6497
+ const [rawValue, setValue] = useControlledState(
6476
6498
  props.value,
6477
- props.defaultValue || "",
6478
- (0, import_react40.useCallback)((value2) => onChange?.({ target: { name: props.name, value: value2 } }), [props.name, onChange])
6499
+ props.defaultValue ?? null,
6500
+ (0, import_react40.useCallback)(
6501
+ (value2) => onChange?.({ target: { name: props.name, value: value2 ?? "" } }),
6502
+ [props.name, onChange]
6503
+ )
6479
6504
  );
6505
+ const value = rawValue ?? "";
6480
6506
  const [displayValue, setDisplayValue] = (0, import_react40.useState)(
6481
6507
  () => value ? formatValueString4(parseDates2(value), displayFormat, locale) : ""
6482
6508
  );
@@ -6808,34 +6834,30 @@ var PercentageInput = import_react44.default.forwardRef(
6808
6834
  } = props;
6809
6835
  const [_value, setValue] = useControlledState(
6810
6836
  props.value,
6811
- props.defaultValue,
6837
+ props.defaultValue ?? null,
6812
6838
  (0, import_react44.useCallback)((value2) => onChange?.({ target: { name, value: value2 } }), [onChange, name])
6813
6839
  );
6814
- const [internalError, setInternalError] = (0, import_react44.useState)(
6815
- max && _value && _value > max || min && _value && _value < min
6816
- );
6817
6840
  const value = (0, import_react44.useMemo)(() => {
6818
6841
  if (_value && useMinorUnit) {
6819
6842
  return _value / Math.pow(10, maxDecimalScale);
6820
6843
  }
6821
6844
  return _value;
6822
6845
  }, [_value, useMinorUnit, maxDecimalScale]);
6846
+ const internalError = (0, import_react44.useMemo)(
6847
+ () => value != null && (max != null && value > max || min != null && value < min),
6848
+ [max, min, value]
6849
+ );
6823
6850
  const handleChange = (0, import_react44.useCallback)(
6824
6851
  (event) => {
6825
6852
  if (event.target.value === "") {
6826
- setValue(void 0);
6853
+ setValue(null);
6827
6854
  return;
6828
6855
  }
6829
6856
  const originalAmount = Number(event.target.value);
6830
- if (min && originalAmount < min || max && originalAmount > max) {
6831
- setInternalError(true);
6832
- } else {
6833
- setInternalError(false);
6834
- }
6835
6857
  const amount = useMinorUnit ? padDecimal(originalAmount, maxDecimalScale) : originalAmount;
6836
6858
  setValue(amount);
6837
6859
  },
6838
- [setValue, useMinorUnit, maxDecimalScale, min, max]
6860
+ [setValue, useMinorUnit, maxDecimalScale]
6839
6861
  );
6840
6862
  return /* @__PURE__ */ import_react44.default.createElement(
6841
6863
  PercentageInputRoot,