@meta-1/design 0.0.161 → 0.0.162

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meta-1/design",
3
- "version": "0.0.161",
3
+ "version": "0.0.162",
4
4
  "keywords": [
5
5
  "easykit",
6
6
  "design",
@@ -1,4 +1,4 @@
1
- import { forwardRef, useContext, useState } from "react";
1
+ import { forwardRef, type MouseEvent, useContext, useEffect, useState } from "react";
2
2
  import { Cross2Icon } from "@radix-ui/react-icons";
3
3
  import { addDays, format } from "date-fns";
4
4
  import get from "lodash/get";
@@ -13,52 +13,128 @@ export type DatePickerProps = {
13
13
  preset?: boolean;
14
14
  className?: string;
15
15
  allowClear?: boolean;
16
+ value?: Date;
17
+ onChange?: (value: Date | undefined) => void;
18
+ visible?: boolean;
19
+ onSelect?: (value: Date) => void;
16
20
  };
17
21
 
18
22
  export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((props, _ref) => {
19
- const { preset = false, allowClear = false } = props;
20
- const [date, setDate] = useState<Date>();
23
+ const {
24
+ placeholder,
25
+ format: formatProp,
26
+ preset = false,
27
+ allowClear = false,
28
+ className,
29
+ value,
30
+ onChange,
31
+ visible,
32
+ onSelect,
33
+ } = props;
34
+
35
+ const hasValueProp = Object.hasOwn(props, "value");
36
+ const isValueControlled = hasValueProp;
37
+ const [internalDate, setInternalDate] = useState<Date | undefined>(value);
21
38
  const [presetValue, setPresetValue] = useState<string>("");
39
+ const hasVisibleProp = Object.hasOwn(props, "visible");
40
+ const isOpenControlled = hasVisibleProp;
41
+ const [internalOpen, setInternalOpen] = useState(false);
42
+
43
+ useEffect(() => {
44
+ if (isValueControlled) {
45
+ setInternalDate(value);
46
+ }
47
+ }, [isValueControlled, value]);
22
48
 
23
49
  const config = useContext(UIXContext);
24
50
  const locale = get(config.locale, "DatePicker.locale");
25
- const formatConfig = props.format || get(config.locale, "DatePicker.format") || "yyyy-MM-dd";
51
+ const formatConfig = formatProp || get(config.locale, "DatePicker.format") || "yyyy-MM-dd";
26
52
  const options = get(config.locale, "DatePicker.options");
53
+ const selectedDate = isValueControlled ? value : internalDate;
54
+ const popoverOpen = isOpenControlled ? visible : internalOpen;
27
55
 
28
- const calendar = (
29
- <Calendar
30
- locale={locale}
31
- mode="single"
32
- onSelect={(v) => {
33
- setDate(v);
34
- setPresetValue("");
35
- }}
36
- selected={date}
37
- />
38
- );
56
+ const closePopover = () => {
57
+ if (!isOpenControlled) {
58
+ setInternalOpen(false);
59
+ }
60
+ };
61
+
62
+ const handleSelect = (nextDate?: Date) => {
63
+ if (!nextDate) {
64
+ return;
65
+ }
66
+ if (!isValueControlled) {
67
+ setInternalDate(nextDate);
68
+ }
69
+ setPresetValue("");
70
+ onChange?.(nextDate);
71
+ closePopover();
72
+ onSelect?.(nextDate);
73
+ };
74
+
75
+ const handlePresetChange = (valueStr: string) => {
76
+ setPresetValue(valueStr);
77
+ const offset = Number.parseInt(valueStr, 10);
78
+ if (Number.isNaN(offset)) {
79
+ if (!isValueControlled) {
80
+ setInternalDate(undefined);
81
+ }
82
+ onChange?.(undefined);
83
+ return;
84
+ }
85
+ const nextDate = addDays(new Date(), offset);
86
+ if (!isValueControlled) {
87
+ setInternalDate(nextDate);
88
+ }
89
+ onChange?.(nextDate);
90
+ closePopover();
91
+ };
92
+
93
+ const handleClear = (event: MouseEvent<SVGSVGElement | HTMLSpanElement>) => {
94
+ event.preventDefault();
95
+ event.stopPropagation();
96
+ if (!isValueControlled) {
97
+ setInternalDate(undefined);
98
+ }
99
+ setPresetValue("");
100
+ onChange?.(undefined);
101
+ };
102
+
103
+ const handleOpenChange = (nextOpen: boolean) => {
104
+ if (!isOpenControlled) {
105
+ setInternalOpen(nextOpen);
106
+ }
107
+ };
108
+
109
+ const calendar = <Calendar locale={locale} mode="single" onSelect={handleSelect} selected={selectedDate} />;
39
110
 
40
111
  return (
41
- <Popover>
112
+ <Popover onOpenChange={handleOpenChange} open={popoverOpen}>
42
113
  <PopoverTrigger asChild>
43
114
  <Button
44
115
  className={cn(
45
116
  "group w-full justify-start space-x-1 text-left font-normal",
46
- !date && "text-muted-foreground",
47
- props.className,
117
+ !selectedDate && "text-muted-foreground",
118
+ className,
48
119
  )}
49
120
  variant="outline"
50
121
  >
51
122
  <CalendarIcon className="mr-2 h-4 w-4" />
52
- <span className="flex-1">{date ? format(date, formatConfig) : props.placeholder}</span>
53
- {allowClear && date ? (
54
- <Cross2Icon
55
- className="hidden group-hover:block"
56
- onClick={(e) => {
57
- setDate(undefined);
58
- setPresetValue("");
59
- e.stopPropagation();
123
+ <span className="flex-1">{selectedDate ? format(selectedDate, formatConfig) : placeholder}</span>
124
+ {allowClear && selectedDate ? (
125
+ <span
126
+ aria-label="Clear date"
127
+ className="hidden cursor-pointer items-center justify-center group-hover:flex"
128
+ onClick={handleClear}
129
+ onPointerDown={(pointerEvent) => {
130
+ pointerEvent.preventDefault();
131
+ pointerEvent.stopPropagation();
60
132
  }}
61
- />
133
+ role="button"
134
+ tabIndex={-1}
135
+ >
136
+ <Cross2Icon />
137
+ </span>
62
138
  ) : null}
63
139
  </Button>
64
140
  </PopoverTrigger>
@@ -67,10 +143,7 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((props, _r
67
143
  <>
68
144
  <Select
69
145
  className="w-full"
70
- onChange={(value) => {
71
- setDate(addDays(new Date(), Number.parseInt(value, 10)));
72
- setPresetValue(value);
73
- }}
146
+ onChange={handlePresetChange}
74
147
  options={options || []}
75
148
  placeholder="请选择"
76
149
  value={presetValue}