@ceed/cds 1.5.4 → 1.5.6-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -327,25 +327,26 @@ var IconButton_default = IconButton;
327
327
  import { useState, useCallback, useEffect, useRef } from "react";
328
328
  function useControlledState(controlledValue, defaultValue, onChange, options) {
329
329
  const { current: isControlled } = useRef(controlledValue !== void 0);
330
- const [displayValue, setDisplayValue] = useState(
330
+ const [internalValue, setInternalValue] = useState(
331
331
  controlledValue ?? defaultValue
332
332
  );
333
+ const displayValue = isControlled ? controlledValue : internalValue;
333
334
  useEffect(() => {
334
335
  if (isControlled) {
335
- setDisplayValue(controlledValue ?? defaultValue);
336
+ setInternalValue(controlledValue);
336
337
  }
337
- }, [controlledValue, defaultValue, isControlled]);
338
+ }, [isControlled, controlledValue]);
338
339
  const handleChange = useCallback(
339
340
  (value) => {
340
341
  const newValue = typeof value === "function" ? value(displayValue) : value;
341
- if (options?.disableStrict || !isControlled) {
342
- setDisplayValue(newValue);
342
+ if (!isControlled) {
343
+ setInternalValue(newValue);
343
344
  }
344
345
  onChange?.(newValue);
345
346
  },
346
- [isControlled, onChange, displayValue, options]
347
+ [isControlled, onChange, displayValue]
347
348
  );
348
- return [displayValue, handleChange];
349
+ return [displayValue, handleChange, isControlled];
349
350
  }
350
351
 
351
352
  // src/components/Autocomplete/Autocomplete.tsx
@@ -782,8 +783,11 @@ var getCalendarDates = (date) => {
782
783
  var getYearName = (date, locale) => {
783
784
  return date.toLocaleString(locale, { year: "numeric" });
784
785
  };
785
- var getMonthName = (date, locale) => {
786
- return date.toLocaleString(locale, { year: "numeric", month: "long" });
786
+ var getMonthName = (date, locale, options) => {
787
+ return date.toLocaleString(locale, {
788
+ month: "long",
789
+ ...options?.hideYear ? {} : { year: "numeric" }
790
+ });
787
791
  };
788
792
  var getMonthNameFromIndex = (index, locale) => {
789
793
  return new Date(0, index).toLocaleString(locale, { month: "short" });
@@ -4534,7 +4538,6 @@ import React31, {
4534
4538
  useRef as useRef6,
4535
4539
  useState as useState9
4536
4540
  } from "react";
4537
- import { IMaskInput as IMaskInput3, IMask as IMask3 } from "react-imask";
4538
4541
  import CalendarTodayIcon3 from "@mui/icons-material/CalendarToday";
4539
4542
  import { styled as styled20, useThemeProps as useThemeProps6 } from "@mui/joy";
4540
4543
  import { FocusTrap as FocusTrap3, ClickAwayListener as ClickAwayListener3, Popper as Popper4 } from "@mui/base";
@@ -4560,60 +4563,35 @@ var MonthPickerRoot = styled20("div", {
4560
4563
  })({
4561
4564
  width: "100%"
4562
4565
  });
4563
- var formatValueString3 = (date, format) => {
4564
- let month = `${date.getMonth() + 1}`;
4565
- const year = date.getFullYear();
4566
- if (Number(month) < 10) month = "0" + month;
4567
- return format.replace(/YYYY/g, year.toString()).replace(/MM/g, month);
4568
- };
4569
- var formatToPattern3 = (format) => {
4570
- return format.replace(/YYYY/g, "Y").replace(/MM/g, "M").replace(/[^YM\s]/g, (match) => `${match}\``);
4566
+ var formatValueString3 = (date, format, locale = "default") => {
4567
+ const year = date.getFullYear().toString();
4568
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
4569
+ const monthName = getMonthName(date, locale, { hideYear: true });
4570
+ const day = "01";
4571
+ return format.replace(/MMMM/g, monthName).replace(/MM/g, month).replace(/DD/g, day).replace(/YYYY/g, year);
4571
4572
  };
4572
- function parseDate3(dateString) {
4573
+ function parseDate3(dateString, format) {
4574
+ const dateParts = dateString.split(/[-./\s]/);
4575
+ const formatParts = format.split(/[-./\s]/);
4576
+ if (formatParts.length !== dateParts.length) {
4577
+ throw new Error("Invalid date string or format");
4578
+ }
4573
4579
  let month, year;
4574
- const cleanDateString = dateString.replace(/[^\d]/g, "");
4575
- if (dateString.match(/\d{1,2}.\d{4}/)) {
4576
- month = cleanDateString.slice(0, 2);
4577
- year = cleanDateString.slice(2);
4578
- } else if (dateString.match(/\d{4}.\d{1,2}/)) {
4579
- year = cleanDateString.slice(0, 4);
4580
- month = cleanDateString.slice(4);
4580
+ const day = 1;
4581
+ for (let i = 0; i < formatParts.length; i++) {
4582
+ const value = parseInt(dateParts[i], 10);
4583
+ switch (formatParts[i]) {
4584
+ case "MM":
4585
+ month = value - 1;
4586
+ break;
4587
+ case "YYYY":
4588
+ year = value;
4589
+ break;
4590
+ }
4581
4591
  }
4582
- const date = /* @__PURE__ */ new Date(`${year}/${month}`);
4583
- return date;
4592
+ const result = new Date(year, month, day);
4593
+ return result;
4584
4594
  }
4585
- var TextMaskAdapter7 = React31.forwardRef(
4586
- function TextMaskAdapter8(props, ref) {
4587
- const { onChange, format, ...other } = props;
4588
- return /* @__PURE__ */ React31.createElement(
4589
- IMaskInput3,
4590
- {
4591
- ...other,
4592
- inputRef: ref,
4593
- onAccept: (value) => onChange({ target: { name: props.name, value } }),
4594
- mask: Date,
4595
- pattern: formatToPattern3(format),
4596
- blocks: {
4597
- M: {
4598
- mask: IMask3.MaskedRange,
4599
- from: 1,
4600
- to: 12,
4601
- maxLength: 2
4602
- },
4603
- Y: {
4604
- mask: IMask3.MaskedRange,
4605
- from: 1900,
4606
- to: 9999
4607
- }
4608
- },
4609
- format: (date) => formatValueString3(date, format),
4610
- parse: parseDate3,
4611
- autofix: "pad",
4612
- overwrite: true
4613
- }
4614
- );
4615
- }
4616
- );
4617
4595
  var MonthPicker = forwardRef9(
4618
4596
  (inProps, ref) => {
4619
4597
  const props = useThemeProps6({ props: inProps, name: "MonthPicker" });
@@ -4631,12 +4609,20 @@ var MonthPicker = forwardRef9(
4631
4609
  // NOTE: 스타일 관련된 props는 최상위 엘리먼트에 적용한다.
4632
4610
  sx,
4633
4611
  className,
4634
- format = "YYYY/MM",
4612
+ /**
4613
+ * NOTE: 현재 CalendarDate는 YYYY-MM-DD로 개발하고 있어 date를 포함하여 value를 관리한다.
4614
+ * @see https://ecubelabs.atlassian.net/wiki/spaces/SW/pages/1938784349/Date#CalendarDate
4615
+ * @see https://github.com/Ecube-Labs/hds/pull/145
4616
+ */
4617
+ format = "YYYY/MM/DD",
4618
+ displayFormat = "YYYY/MM",
4635
4619
  size,
4620
+ locale,
4636
4621
  ...innerProps
4637
4622
  } = props;
4638
4623
  const innerRef = useRef6(null);
4639
- const [value, setValue] = useControlledState(
4624
+ const buttonRef = useRef6(null);
4625
+ const [value, setValue, isControlled] = useControlledState(
4640
4626
  props.value,
4641
4627
  props.defaultValue || "",
4642
4628
  useCallback11(
@@ -4645,6 +4631,24 @@ var MonthPicker = forwardRef9(
4645
4631
  ),
4646
4632
  { disableStrict: true }
4647
4633
  );
4634
+ const getFormattedDisplayValue = useCallback11(
4635
+ (inputValue) => {
4636
+ if (!inputValue) return "";
4637
+ try {
4638
+ return formatValueString3(
4639
+ parseDate3(inputValue, format),
4640
+ displayFormat,
4641
+ locale
4642
+ );
4643
+ } catch (e) {
4644
+ return inputValue;
4645
+ }
4646
+ },
4647
+ [format, displayFormat, locale]
4648
+ );
4649
+ const [displayValue, setDisplayValue] = useState9(
4650
+ () => getFormattedDisplayValue(value)
4651
+ );
4648
4652
  const [anchorEl, setAnchorEl] = useState9(null);
4649
4653
  const open = Boolean(anchorEl);
4650
4654
  useEffect8(() => {
@@ -4655,11 +4659,18 @@ var MonthPicker = forwardRef9(
4655
4659
  useImperativeHandle4(ref, () => innerRef.current, [
4656
4660
  innerRef
4657
4661
  ]);
4662
+ useEffect8(() => {
4663
+ setDisplayValue(getFormattedDisplayValue(value));
4664
+ }, [value, getFormattedDisplayValue]);
4658
4665
  const handleChange = useCallback11(
4659
4666
  (event) => {
4660
- setValue(event.target.value);
4667
+ const newValue = event.target.value;
4668
+ setValue(newValue);
4669
+ if (!isControlled) {
4670
+ setDisplayValue(getFormattedDisplayValue(newValue));
4671
+ }
4661
4672
  },
4662
- [setValue]
4673
+ [setValue, getFormattedDisplayValue, isControlled]
4663
4674
  );
4664
4675
  const handleCalendarToggle = useCallback11(
4665
4676
  (event) => {
@@ -4668,6 +4679,13 @@ var MonthPicker = forwardRef9(
4668
4679
  },
4669
4680
  [anchorEl, setAnchorEl, innerRef]
4670
4681
  );
4682
+ const handleInputMouseDown = useCallback11(
4683
+ (event) => {
4684
+ event.preventDefault();
4685
+ buttonRef.current?.focus();
4686
+ },
4687
+ [buttonRef]
4688
+ );
4671
4689
  return /* @__PURE__ */ React31.createElement(MonthPickerRoot, null, /* @__PURE__ */ React31.createElement(FocusTrap3, { open: true }, /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement(
4672
4690
  Input_default,
4673
4691
  {
@@ -4675,13 +4693,21 @@ var MonthPicker = forwardRef9(
4675
4693
  color: error ? "danger" : innerProps.color,
4676
4694
  ref: innerRef,
4677
4695
  size,
4678
- value,
4679
- onChange: handleChange,
4680
- placeholder: format,
4696
+ value: displayValue,
4697
+ placeholder: displayFormat,
4681
4698
  disabled,
4682
4699
  required,
4683
4700
  slotProps: {
4684
- input: { component: TextMaskAdapter7, ref: innerRef, format }
4701
+ input: {
4702
+ ref: innerRef,
4703
+ format: displayFormat,
4704
+ sx: {
4705
+ "&:hover": {
4706
+ cursor: "default"
4707
+ }
4708
+ },
4709
+ onMouseDown: handleInputMouseDown
4710
+ }
4685
4711
  },
4686
4712
  error,
4687
4713
  className,
@@ -4693,12 +4719,14 @@ var MonthPicker = forwardRef9(
4693
4719
  endDecorator: /* @__PURE__ */ React31.createElement(
4694
4720
  IconButton_default,
4695
4721
  {
4722
+ ref: buttonRef,
4696
4723
  variant: "plain",
4697
4724
  onClick: handleCalendarToggle,
4698
4725
  "aria-label": "Toggle Calendar",
4699
4726
  "aria-controls": "month-picker-popper",
4700
4727
  "aria-haspopup": "dialog",
4701
- "aria-expanded": open
4728
+ "aria-expanded": open,
4729
+ disabled
4702
4730
  },
4703
4731
  /* @__PURE__ */ React31.createElement(CalendarTodayIcon3, null)
4704
4732
  ),
@@ -4734,7 +4762,7 @@ var MonthPicker = forwardRef9(
4734
4762
  handleChange({
4735
4763
  target: {
4736
4764
  name: props.name,
4737
- value: formatValueString3(date, format)
4765
+ value: formatValueString3(date, format, locale)
4738
4766
  }
4739
4767
  });
4740
4768
  setAnchorEl(null);
@@ -4742,7 +4770,8 @@ var MonthPicker = forwardRef9(
4742
4770
  minDate: minDate ? new Date(minDate) : void 0,
4743
4771
  maxDate: maxDate ? new Date(maxDate) : void 0,
4744
4772
  disableFuture,
4745
- disablePast
4773
+ disablePast,
4774
+ locale
4746
4775
  }
4747
4776
  ), /* @__PURE__ */ React31.createElement(
4748
4777
  DialogActions_default,
@@ -4784,7 +4813,7 @@ import React32, {
4784
4813
  useRef as useRef7,
4785
4814
  useState as useState10
4786
4815
  } from "react";
4787
- import { IMaskInput as IMaskInput4, IMask as IMask4 } from "react-imask";
4816
+ import { IMaskInput as IMaskInput3, IMask as IMask3 } from "react-imask";
4788
4817
  import CalendarTodayIcon4 from "@mui/icons-material/CalendarToday";
4789
4818
  import { styled as styled21, useThemeProps as useThemeProps7 } from "@mui/joy";
4790
4819
  import { FocusTrap as FocusTrap4, ClickAwayListener as ClickAwayListener4, Popper as Popper5 } from "@mui/base";
@@ -4838,29 +4867,29 @@ var parseDates2 = (str) => {
4838
4867
  const date2 = str.split(" - ")[1] || "";
4839
4868
  return [parseDate4(date1), parseDate4(date2)];
4840
4869
  };
4841
- var formatToPattern4 = (format) => {
4870
+ var formatToPattern3 = (format) => {
4842
4871
  return `${format} - ${format}`.replace(/YYYY/g, "Y").replace(/MM/g, "m").replace(/[^YMm\s]/g, (match) => `${match}\``);
4843
4872
  };
4844
- var TextMaskAdapter9 = React32.forwardRef(
4845
- function TextMaskAdapter10(props, ref) {
4873
+ var TextMaskAdapter7 = React32.forwardRef(
4874
+ function TextMaskAdapter8(props, ref) {
4846
4875
  const { onChange, format, ...other } = props;
4847
4876
  return /* @__PURE__ */ React32.createElement(
4848
- IMaskInput4,
4877
+ IMaskInput3,
4849
4878
  {
4850
4879
  ...other,
4851
4880
  inputRef: ref,
4852
4881
  onAccept: (value) => onChange({ target: { name: props.name, value } }),
4853
4882
  mask: Date,
4854
- pattern: formatToPattern4(format),
4883
+ pattern: formatToPattern3(format),
4855
4884
  blocks: {
4856
4885
  m: {
4857
- mask: IMask4.MaskedRange,
4886
+ mask: IMask3.MaskedRange,
4858
4887
  from: 1,
4859
4888
  to: 12,
4860
4889
  maxLength: 2
4861
4890
  },
4862
4891
  Y: {
4863
- mask: IMask4.MaskedRange,
4892
+ mask: IMask3.MaskedRange,
4864
4893
  from: 1900,
4865
4894
  to: 9999
4866
4895
  }
@@ -4952,7 +4981,7 @@ var MonthRangePicker = forwardRef10(
4952
4981
  required,
4953
4982
  placeholder: `${format} - ${format}`,
4954
4983
  slotProps: {
4955
- input: { component: TextMaskAdapter9, ref: innerRef, format }
4984
+ input: { component: TextMaskAdapter7, ref: innerRef, format }
4956
4985
  },
4957
4986
  error,
4958
4987
  className,
@@ -5166,8 +5195,8 @@ var padDecimal = (value, decimalScale) => {
5166
5195
  const [integer, decimal = ""] = `${value}`.split(".");
5167
5196
  return Number(`${integer}${decimal.padEnd(decimalScale, "0")}`);
5168
5197
  };
5169
- var TextMaskAdapter11 = React36.forwardRef(
5170
- function TextMaskAdapter12(props, ref) {
5198
+ var TextMaskAdapter9 = React36.forwardRef(
5199
+ function TextMaskAdapter10(props, ref) {
5171
5200
  const { onChange, min, max, ...innerProps } = props;
5172
5201
  return /* @__PURE__ */ React36.createElement(
5173
5202
  NumericFormat2,
@@ -5263,7 +5292,7 @@ var PercentageInput = React36.forwardRef(function PercentageInput2(inProps, ref)
5263
5292
  helperText,
5264
5293
  slotProps: {
5265
5294
  input: {
5266
- component: TextMaskAdapter11,
5295
+ component: TextMaskAdapter9,
5267
5296
  "aria-label": innerProps["aria-label"],
5268
5297
  decimalScale: maxDecimalScale
5269
5298
  }