@particle-network/ui-native 0.1.4-beta.2 → 0.1.4-beta.3

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.
@@ -11,27 +11,31 @@ import { UXPressable } from "../UXPressable/index.js";
11
11
  import date_button from "./date-button.js";
12
12
  import { DateWheelPicker } from "./date-wheel-picker.js";
13
13
  import { TimeWheelPicker } from "./time-wheel-picker.js";
14
- import { hasTimeInFormat } from "./utils.js";
15
- const UXDatePicker = ({ value, defaultValue, onConfirm = ()=>null, format = 'YYYY/MM/DD', minDate, maxDate, placeholder, isDisabled = false, isReadOnly = false, ...uxPressableProps })=>{
14
+ import useFormat from "./useFormat.js";
15
+ import { hasTimeOnly } from "./utils.js";
16
+ const UXDatePicker = ({ title, value, defaultValue, onConfirm = ()=>null, format = 'YYYY/MM/DD', minDate, maxDate, placeholder, isDisabled = false, isReadOnly = false, ...uxPressableProps })=>{
16
17
  const [isOpen, setIsOpen] = useState(false);
17
18
  const [pickerType, setPickerType] = useState('date');
18
19
  const [internalValue, setInternalValue] = useState(value || defaultValue || new Date());
19
20
  const i18n = useI18n();
20
- const [dateFormat, timeFormat] = format.split(' ');
21
- const showTimePicker = hasTimeInFormat(format);
22
21
  const displayPlaceholder = placeholder ?? format;
22
+ const { dateFormat, timeFormat, displayTitle } = useFormat({
23
+ format,
24
+ title
25
+ });
23
26
  useEffect(()=>{
24
27
  if (void 0 !== value) setInternalValue(value);
25
28
  }, [
26
29
  value
27
30
  ]);
28
- const handleOpenModal = useCallback((picker)=>{
31
+ const handleOpenModal = useCallback(()=>{
29
32
  if (isDisabled || isReadOnly) return;
30
- setPickerType(picker);
33
+ setPickerType(hasTimeOnly(format) ? 'time' : 'date');
31
34
  setIsOpen(true);
32
35
  }, [
33
36
  isDisabled,
34
- isReadOnly
37
+ isReadOnly,
38
+ format
35
39
  ]);
36
40
  const handleConfirm = useCallback(()=>{
37
41
  onConfirm(internalValue);
@@ -46,7 +50,7 @@ const UXDatePicker = ({ value, defaultValue, onConfirm = ()=>null, format = 'YYY
46
50
  return /*#__PURE__*/ jsxs(Fragment, {
47
51
  children: [
48
52
  /*#__PURE__*/ jsx(UXPressable, {
49
- onPress: ()=>handleOpenModal('date'),
53
+ onPress: ()=>handleOpenModal(),
50
54
  ...uxPressableProps,
51
55
  children: /*#__PURE__*/ jsx(UXInput, {
52
56
  containerStyle: {
@@ -57,7 +61,7 @@ const UXDatePicker = ({ value, defaultValue, onConfirm = ()=>null, format = 'YYY
57
61
  isReadOnly: isReadOnly,
58
62
  placeholder: displayPlaceholder,
59
63
  pointerEvents: "none",
60
- value: value ? dayjs(value).format(format) : format
64
+ value: value ? dayjs(value).format(format) : void 0
61
65
  })
62
66
  }),
63
67
  /*#__PURE__*/ jsx(UXModal, {
@@ -74,7 +78,7 @@ const UXDatePicker = ({ value, defaultValue, onConfirm = ()=>null, format = 'YYY
74
78
  scrollViewProps: {
75
79
  scrollEnabled: false
76
80
  },
77
- title: i18n.datePicker.confirm,
81
+ title: displayTitle,
78
82
  onClose: handleClose,
79
83
  children: /*#__PURE__*/ jsxs(VStack, {
80
84
  fullWidth: true,
@@ -85,12 +89,12 @@ const UXDatePicker = ({ value, defaultValue, onConfirm = ()=>null, format = 'YYY
85
89
  items: "center",
86
90
  gap: 8,
87
91
  children: [
88
- /*#__PURE__*/ jsx(date_button, {
92
+ dateFormat && /*#__PURE__*/ jsx(date_button, {
89
93
  isSelected: 'date' === pickerType,
90
94
  text: dayjs(internalValue).format(dateFormat),
91
95
  onPress: ()=>setPickerType('date')
92
96
  }),
93
- showTimePicker && /*#__PURE__*/ jsx(date_button, {
97
+ timeFormat && /*#__PURE__*/ jsx(date_button, {
94
98
  isSelected: 'time' === pickerType,
95
99
  text: dayjs(internalValue).format(timeFormat),
96
100
  onPress: ()=>setPickerType('time')
@@ -13,8 +13,9 @@ import { UXPressable } from "../UXPressable/index.js";
13
13
  import date_button from "./date-button.js";
14
14
  import { DateWheelPicker } from "./date-wheel-picker.js";
15
15
  import { TimeWheelPicker } from "./time-wheel-picker.js";
16
- import { hasTimeInFormat } from "./utils.js";
17
- const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format = 'YYYY/MM/DD', minDate, maxDate, startPlaceholder, endPlaceholder, isDisabled = false, isReadOnly = false, ...hStackProps })=>{
16
+ import useFormat from "./useFormat.js";
17
+ import { hasTimeOnly } from "./utils.js";
18
+ const UXDateRangePicker = ({ title, value, defaultValue, onConfirm = ()=>null, format = 'YYYY/MM/DD', minDate, maxDate, startPlaceholder, endPlaceholder, isDisabled = false, isReadOnly = false, ...hStackProps })=>{
18
19
  const [isOpen, setIsOpen] = useState(false);
19
20
  const [currentEditType, setCurrentEditType] = useState('start');
20
21
  const [pickerType, setPickerType] = useState('date');
@@ -22,10 +23,13 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
22
23
  const [internalEnd, setInternalEnd] = useState(value?.end || defaultValue?.end || new Date());
23
24
  const [error, setError] = useState('');
24
25
  const i18n = useI18n();
25
- const [dateFormat, timeFormat] = format.split(' ');
26
- const showTimePicker = hasTimeInFormat(format);
27
26
  const displayStartPlaceholder = startPlaceholder ?? format;
28
27
  const displayEndPlaceholder = endPlaceholder ?? format;
28
+ const { dateFormat, timeFormat, displayTitle } = useFormat({
29
+ format,
30
+ title,
31
+ rangeMode: true
32
+ });
29
33
  useEffect(()=>{
30
34
  if (value?.start !== void 0) setInternalStart(value?.start);
31
35
  if (value?.end !== void 0) setInternalEnd(value?.end);
@@ -35,12 +39,13 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
35
39
  const handleOpenModal = useCallback((type, picker)=>{
36
40
  if (isDisabled || isReadOnly) return;
37
41
  setCurrentEditType(type);
38
- setPickerType(picker);
42
+ setPickerType(picker || (hasTimeOnly(format) ? 'time' : 'date'));
39
43
  setError('');
40
44
  setIsOpen(true);
41
45
  }, [
42
46
  isDisabled,
43
- isReadOnly
47
+ isReadOnly,
48
+ format
44
49
  ]);
45
50
  const handleConfirm = useCallback(()=>{
46
51
  if (!internalStart || !internalEnd) return void setError(i18n.datePicker.errorBothRequired);
@@ -68,7 +73,7 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
68
73
  children: [
69
74
  /*#__PURE__*/ jsx(UXPressable, {
70
75
  fill: true,
71
- onPress: ()=>handleOpenModal('start', 'date'),
76
+ onPress: ()=>handleOpenModal('start'),
72
77
  children: /*#__PURE__*/ jsx(UXInput, {
73
78
  containerStyle: {
74
79
  flex: 1
@@ -78,7 +83,7 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
78
83
  isReadOnly: isReadOnly,
79
84
  placeholder: displayStartPlaceholder,
80
85
  pointerEvents: "none",
81
- value: value?.start ? dayjs(value?.start).format(format) : format
86
+ value: value?.start ? dayjs(value?.start).format(format) : void 0
82
87
  })
83
88
  }),
84
89
  /*#__PURE__*/ jsx(Icon, {
@@ -88,7 +93,7 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
88
93
  }),
89
94
  /*#__PURE__*/ jsx(UXPressable, {
90
95
  fill: true,
91
- onPress: ()=>handleOpenModal('end', 'date'),
96
+ onPress: ()=>handleOpenModal('end'),
92
97
  children: /*#__PURE__*/ jsx(UXInput, {
93
98
  containerStyle: {
94
99
  flex: 1
@@ -98,7 +103,7 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
98
103
  isReadOnly: isReadOnly,
99
104
  placeholder: displayEndPlaceholder,
100
105
  pointerEvents: "none",
101
- value: value?.end ? dayjs(value?.end).format(format) : format
106
+ value: value?.end ? dayjs(value?.end).format(format) : void 0
102
107
  })
103
108
  })
104
109
  ]
@@ -127,7 +132,7 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
127
132
  scrollViewProps: {
128
133
  scrollEnabled: false
129
134
  },
130
- title: "Time Range",
135
+ title: displayTitle,
131
136
  onClose: handleClose,
132
137
  children: /*#__PURE__*/ jsxs(VStack, {
133
138
  fullWidth: true,
@@ -138,12 +143,12 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
138
143
  items: "center",
139
144
  gap: 8,
140
145
  children: [
141
- /*#__PURE__*/ jsx(date_button, {
146
+ dateFormat && /*#__PURE__*/ jsx(date_button, {
142
147
  isSelected: 'start' === currentEditType && 'date' === pickerType,
143
148
  text: dayjs(internalStart).format(dateFormat),
144
149
  onPress: ()=>handleOpenModal('start', 'date')
145
150
  }),
146
- showTimePicker && /*#__PURE__*/ jsx(date_button, {
151
+ timeFormat && /*#__PURE__*/ jsx(date_button, {
147
152
  isSelected: 'start' === currentEditType && 'time' === pickerType,
148
153
  text: dayjs(internalStart).format(timeFormat),
149
154
  onPress: ()=>handleOpenModal('start', 'time')
@@ -153,12 +158,12 @@ const UXDateRangePicker = ({ value, defaultValue, onConfirm = ()=>null, format =
153
158
  color: "tertiary",
154
159
  size: 16
155
160
  }),
156
- /*#__PURE__*/ jsx(date_button, {
161
+ dateFormat && /*#__PURE__*/ jsx(date_button, {
157
162
  isSelected: 'end' === currentEditType && 'date' === pickerType,
158
163
  text: dayjs(internalEnd).format(dateFormat),
159
164
  onPress: ()=>handleOpenModal('end', 'date')
160
165
  }),
161
- showTimePicker && /*#__PURE__*/ jsx(date_button, {
166
+ timeFormat && /*#__PURE__*/ jsx(date_button, {
162
167
  isSelected: 'end' === currentEditType && 'time' === pickerType,
163
168
  text: dayjs(internalEnd).format(timeFormat),
164
169
  onPress: ()=>handleOpenModal('end', 'time')
@@ -37,6 +37,10 @@ export interface UXDatePickerProps extends Omit<UXPressableProps, 'children'> {
37
37
  * 是否只读
38
38
  */
39
39
  isReadOnly?: boolean;
40
+ /**
41
+ * 弹窗标题
42
+ */
43
+ title?: string;
40
44
  }
41
45
  export interface UXDateRangePickerProps extends Omit<HStackProps, 'children'> {
42
46
  /**
@@ -79,6 +83,10 @@ export interface UXDateRangePickerProps extends Omit<HStackProps, 'children'> {
79
83
  * 是否只读
80
84
  */
81
85
  isReadOnly?: boolean;
86
+ /**
87
+ * 弹窗标题
88
+ */
89
+ title?: string;
82
90
  }
83
91
  export interface DateRangeValue {
84
92
  start?: Date;
@@ -0,0 +1,10 @@
1
+ declare const useFormat: ({ format, title, rangeMode }: {
2
+ format: string;
3
+ title?: string;
4
+ rangeMode?: boolean;
5
+ }) => {
6
+ dateFormat: string | null;
7
+ timeFormat: string | null;
8
+ displayTitle: string;
9
+ };
10
+ export default useFormat;
@@ -0,0 +1,26 @@
1
+ import { useMemo } from "react";
2
+ import { useI18n } from "../../hooks/index.js";
3
+ import { getDateFormat, getTimeFormat, hasDateOnly, hasTimeOnly } from "./utils.js";
4
+ const useFormat = ({ format, title, rangeMode = false })=>{
5
+ const i18n = useI18n();
6
+ const dateFormat = getDateFormat(format);
7
+ const timeFormat = getTimeFormat(format);
8
+ const displayTitle = useMemo(()=>{
9
+ if (title) return title;
10
+ if (hasDateOnly(format)) return rangeMode ? i18n.datePicker.selectDateRange : i18n.datePicker.selectDate;
11
+ if (hasTimeOnly(format)) return rangeMode ? i18n.datePicker.selectTimeRange : i18n.datePicker.selectTime;
12
+ return rangeMode ? i18n.datePicker.selectDateAndTimeRange : i18n.datePicker.selectDateAndTime;
13
+ }, [
14
+ format,
15
+ title,
16
+ i18n,
17
+ rangeMode
18
+ ]);
19
+ return {
20
+ dateFormat,
21
+ timeFormat,
22
+ displayTitle
23
+ };
24
+ };
25
+ const date_picker_useFormat = useFormat;
26
+ export { date_picker_useFormat as default };
@@ -1,6 +1,36 @@
1
+ /**
2
+ * 判断格式字符串是否包含日期部分
3
+ * @param {string} format - 时间格式字符串
4
+ * @returns {boolean} - 是否包含日期
5
+ */
6
+ export declare function hasDateInFormat(format: string): boolean;
1
7
  /**
2
8
  * 判断格式字符串是否包含时间部分
3
- * @param format 格式字符串
4
- * @returns 是否包含时间
9
+ * @param {string} format - 时间格式字符串
10
+ * @returns {boolean} - 是否包含时间
11
+ */
12
+ export declare function hasTimeInFormat(format: string): boolean;
13
+ /**
14
+ * 判断格式字符串是否只包含日期(不含时间)
15
+ * @param {string} format - 时间格式字符串
16
+ * @returns {boolean} - 是否只有日期
17
+ */
18
+ export declare function hasDateOnly(format: string): boolean;
19
+ /**
20
+ * 判断格式字符串是否只包含时间(不含日期)
21
+ * @param {string} format - 时间格式字符串
22
+ * @returns {boolean} - 是否只有时间
23
+ */
24
+ export declare function hasTimeOnly(format: string): boolean;
25
+ /**
26
+ * 提取格式字符串中的日期部分
27
+ * @param {string} format - 时间格式字符串
28
+ * @returns {string|null} - 日期格式字符串,如果不包含日期则返回 null
29
+ */
30
+ export declare function getDateFormat(format: string): string | null;
31
+ /**
32
+ * 提取格式字符串中的时间部分
33
+ * @param {string} format - 时间格式字符串
34
+ * @returns {string|null} - 时间格式字符串,如果不包含时间则返回 null
5
35
  */
6
- export declare const hasTimeInFormat: (format: string) => boolean;
36
+ export declare function getTimeFormat(format: string): string | null;
@@ -1,2 +1,77 @@
1
- const hasTimeInFormat = (format)=>/[HhmsAa]/.test(format);
2
- export { hasTimeInFormat };
1
+ function hasDateInFormat(format) {
2
+ const datePatterns = [
3
+ /Y{2,4}/,
4
+ /M{1,2}/,
5
+ /D{1,2}/
6
+ ];
7
+ return datePatterns.some((pattern)=>pattern.test(format));
8
+ }
9
+ function hasTimeInFormat(format) {
10
+ const timePatterns = [
11
+ /H{1,2}/,
12
+ /h{1,2}/,
13
+ /m{1,2}/,
14
+ /s{1,2}/,
15
+ /A/i
16
+ ];
17
+ return timePatterns.some((pattern)=>pattern.test(format));
18
+ }
19
+ function hasDateOnly(format) {
20
+ return hasDateInFormat(format) && !hasTimeInFormat(format);
21
+ }
22
+ function hasTimeOnly(format) {
23
+ return hasTimeInFormat(format) && !hasDateInFormat(format);
24
+ }
25
+ function getDateFormat(format) {
26
+ if (!hasDateInFormat(format)) return null;
27
+ if (hasDateOnly(format)) return format.trim();
28
+ const separators = [
29
+ ' ',
30
+ 'T',
31
+ ',',
32
+ '\t'
33
+ ];
34
+ for (const sep of separators)if (format.includes(sep)) {
35
+ const parts = format.split(sep);
36
+ for (const part of parts)if (hasDateInFormat(part) && !hasTimeInFormat(part)) return part.trim();
37
+ }
38
+ const datePatterns = [
39
+ /Y{2,4}[/-]M{1,2}[/-]D{1,2}/,
40
+ /Y{2,4}M{1,2}D{1,2}/,
41
+ /D{1,2}[/-]M{1,2}[/-]Y{2,4}/,
42
+ /M{1,2}[/-]D{1,2}[/-]Y{2,4}/
43
+ ];
44
+ for (const pattern of datePatterns){
45
+ const match = format.match(pattern);
46
+ if (match) return match[0];
47
+ }
48
+ return format.trim();
49
+ }
50
+ function getTimeFormat(format) {
51
+ if (!hasTimeInFormat(format)) return null;
52
+ if (hasTimeOnly(format)) return format.trim();
53
+ const separators = [
54
+ ' ',
55
+ 'T',
56
+ ',',
57
+ '\t'
58
+ ];
59
+ for (const sep of separators)if (format.includes(sep)) {
60
+ const parts = format.split(sep);
61
+ const timeParts = [];
62
+ for (const part of parts)if (hasTimeInFormat(part) || /AM|PM|am|pm/i.test(part)) timeParts.push(part.trim());
63
+ if (timeParts.length > 0) return timeParts.join(' ');
64
+ }
65
+ const timePatterns = [
66
+ /[Hh]{1,2}:[m]{1,2}:[s]{1,2}\s*(?:AM|PM|am|pm)?/,
67
+ /[Hh]{1,2}:[m]{1,2}\s*(?:AM|PM|am|pm)?/,
68
+ /[Hh]{1,2}[m]{1,2}[s]{1,2}\s*(?:AM|PM|am|pm)?/,
69
+ /[Hh]{1,2}[m]{1,2}\s*(?:AM|PM|am|pm)?/
70
+ ];
71
+ for (const pattern of timePatterns){
72
+ const match = format.match(pattern);
73
+ if (match) return match[0].trim();
74
+ }
75
+ return format.trim();
76
+ }
77
+ export { getDateFormat, getTimeFormat, hasDateInFormat, hasDateOnly, hasTimeInFormat, hasTimeOnly };
@@ -10,6 +10,12 @@ export declare const en: {
10
10
  cancel: string;
11
11
  errorBothRequired: string;
12
12
  errorStartBeforeEnd: string;
13
+ selectDate: string;
14
+ selectTime: string;
15
+ selectDateAndTime: string;
16
+ selectDateRange: string;
17
+ selectTimeRange: string;
18
+ selectDateAndTimeRange: string;
13
19
  };
14
20
  modal: {
15
21
  confirm: string;
@@ -9,7 +9,13 @@ const en = {
9
9
  confirm: 'Confirm',
10
10
  cancel: 'Cancel',
11
11
  errorBothRequired: 'Both start and end dates are required',
12
- errorStartBeforeEnd: 'Start date can not be after end date'
12
+ errorStartBeforeEnd: 'Start date can not be after end date',
13
+ selectDate: 'Select Date',
14
+ selectTime: 'Select Time',
15
+ selectDateAndTime: 'Select Date and Time',
16
+ selectDateRange: 'Select Date Range',
17
+ selectTimeRange: 'Select Time Range',
18
+ selectDateAndTimeRange: 'Select Date and Time Range'
13
19
  },
14
20
  modal: {
15
21
  confirm: 'Confirm',
@@ -14,6 +14,12 @@ export declare const locales: {
14
14
  cancel: string;
15
15
  errorBothRequired: string;
16
16
  errorStartBeforeEnd: string;
17
+ selectDate: string;
18
+ selectTime: string;
19
+ selectDateAndTime: string;
20
+ selectDateRange: string;
21
+ selectTimeRange: string;
22
+ selectDateAndTimeRange: string;
17
23
  };
18
24
  modal: {
19
25
  confirm: string;
@@ -48,6 +54,12 @@ export declare const locales: {
48
54
  cancel: string;
49
55
  errorBothRequired: string;
50
56
  errorStartBeforeEnd: string;
57
+ selectDate: string;
58
+ selectTime: string;
59
+ selectDateAndTime: string;
60
+ selectDateRange: string;
61
+ selectTimeRange: string;
62
+ selectDateAndTimeRange: string;
51
63
  };
52
64
  modal: {
53
65
  confirm: string;
@@ -83,6 +95,12 @@ export declare function getLocaleText(locale: Locale): {
83
95
  cancel: string;
84
96
  errorBothRequired: string;
85
97
  errorStartBeforeEnd: string;
98
+ selectDate: string;
99
+ selectTime: string;
100
+ selectDateAndTime: string;
101
+ selectDateRange: string;
102
+ selectTimeRange: string;
103
+ selectDateAndTimeRange: string;
86
104
  };
87
105
  modal: {
88
106
  confirm: string;
@@ -9,7 +9,13 @@ const zh = {
9
9
  confirm: '确认',
10
10
  cancel: '取消',
11
11
  errorBothRequired: '请输入开始和结束日期',
12
- errorStartBeforeEnd: '开始日期不能晚于结束日期'
12
+ errorStartBeforeEnd: '开始日期不能晚于结束日期',
13
+ selectDate: '选择日期',
14
+ selectTime: '选择时间',
15
+ selectDateAndTime: '选择日期和时间',
16
+ selectDateRange: '选择起止日期',
17
+ selectTimeRange: '选择起止时间',
18
+ selectDateAndTimeRange: '选择起止日期和时间'
13
19
  },
14
20
  modal: {
15
21
  confirm: '确认',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@particle-network/ui-native",
3
- "version": "0.1.4-beta.2",
3
+ "version": "0.1.4-beta.3",
4
4
  "main": "./entry.js",
5
5
  "react-native": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -91,8 +91,8 @@
91
91
  "vite": "^6.3.5",
92
92
  "zustand": "^5.0.8",
93
93
  "@particle-network/lintstaged-config": "0.1.0",
94
- "@particle-network/icons": "0.1.3-beta.3",
95
- "@particle-network/eslint-config": "0.3.0"
94
+ "@particle-network/eslint-config": "0.3.0",
95
+ "@particle-network/icons": "0.1.3-beta.3"
96
96
  },
97
97
  "overrides": {
98
98
  "react-docgen-typescript": "2.2.2",