@ankhorage/zora 2.4.5 → 2.4.6
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/CHANGELOG.md +6 -0
- package/README.md +188 -0
- package/dist/components/date-picker/DatePicker.d.ts +4 -0
- package/dist/components/date-picker/DatePicker.d.ts.map +1 -0
- package/dist/components/date-picker/DatePicker.js +144 -0
- package/dist/components/date-picker/DatePicker.js.map +1 -0
- package/dist/components/date-picker/index.d.ts +3 -0
- package/dist/components/date-picker/index.d.ts.map +1 -0
- package/dist/components/date-picker/index.js +2 -0
- package/dist/components/date-picker/index.js.map +1 -0
- package/dist/components/date-picker/meta.d.ts +9 -0
- package/dist/components/date-picker/meta.d.ts.map +1 -0
- package/dist/components/date-picker/meta.js +9 -0
- package/dist/components/date-picker/meta.js.map +1 -0
- package/dist/components/date-picker/types.d.ts +18 -0
- package/dist/components/date-picker/types.d.ts.map +1 -0
- package/dist/components/date-picker/types.js +2 -0
- package/dist/components/date-picker/types.js.map +1 -0
- package/dist/components/time-picker/TimePicker.d.ts +4 -0
- package/dist/components/time-picker/TimePicker.d.ts.map +1 -0
- package/dist/components/time-picker/TimePicker.js +80 -0
- package/dist/components/time-picker/TimePicker.js.map +1 -0
- package/dist/components/time-picker/index.d.ts +3 -0
- package/dist/components/time-picker/index.d.ts.map +1 -0
- package/dist/components/time-picker/index.js +2 -0
- package/dist/components/time-picker/index.js.map +1 -0
- package/dist/components/time-picker/meta.d.ts +9 -0
- package/dist/components/time-picker/meta.d.ts.map +1 -0
- package/dist/components/time-picker/meta.js +9 -0
- package/dist/components/time-picker/meta.js.map +1 -0
- package/dist/components/time-picker/types.d.ts +19 -0
- package/dist/components/time-picker/types.d.ts.map +1 -0
- package/dist/components/time-picker/types.js +2 -0
- package/dist/components/time-picker/types.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/metadata/componentMeta.d.ts.map +1 -1
- package/dist/metadata/componentMeta.js +4 -0
- package/dist/metadata/componentMeta.js.map +1 -1
- package/package.json +1 -1
- package/src/components/date-picker/DatePicker.tsx +242 -0
- package/src/components/date-picker/index.ts +2 -0
- package/src/components/date-picker/meta.ts +10 -0
- package/src/components/date-picker/types.ts +20 -0
- package/src/components/time-picker/TimePicker.tsx +152 -0
- package/src/components/time-picker/index.ts +2 -0
- package/src/components/time-picker/meta.ts +10 -0
- package/src/components/time-picker/types.ts +21 -0
- package/src/index.ts +4 -0
- package/src/metadata/componentMeta.ts +4 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Field } from '@ankhorage/surface';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ScrollView } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { Stack } from '../../foundation';
|
|
6
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
7
|
+
import { ActionSheet, ActionSheetItem } from '../action-sheet';
|
|
8
|
+
import { Button } from '../button';
|
|
9
|
+
import { Text } from '../text';
|
|
10
|
+
import type { TimePickerProps } from './types';
|
|
11
|
+
|
|
12
|
+
const MINUTES_PER_DAY = 24 * 60;
|
|
13
|
+
const DEFAULT_STEP_MINUTES = 30;
|
|
14
|
+
|
|
15
|
+
function renderLabel(label: React.ReactNode, description: React.ReactNode | undefined) {
|
|
16
|
+
return (
|
|
17
|
+
<Stack gap="xs">
|
|
18
|
+
<Text variant="label" weight="semiBold">
|
|
19
|
+
{label}
|
|
20
|
+
</Text>
|
|
21
|
+
{description ? (
|
|
22
|
+
<Text emphasis="muted" variant="bodySmall">
|
|
23
|
+
{description}
|
|
24
|
+
</Text>
|
|
25
|
+
) : null}
|
|
26
|
+
</Stack>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseTimeToMinutes(value: string | undefined): number | undefined {
|
|
31
|
+
if (!value) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
36
|
+
if (!match) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const hours = Number(match[1]);
|
|
41
|
+
const minutes = Number(match[2]);
|
|
42
|
+
if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return hours * 60 + minutes;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function formatMinutes(value: number): string {
|
|
50
|
+
const hours = Math.floor(value / 60);
|
|
51
|
+
const minutes = value % 60;
|
|
52
|
+
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function resolveStepMinutes(stepMinutes: number | undefined): number {
|
|
56
|
+
if (stepMinutes === undefined || !Number.isFinite(stepMinutes) || stepMinutes <= 0) {
|
|
57
|
+
return DEFAULT_STEP_MINUTES;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return Math.max(1, Math.floor(stepMinutes));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function generateTimeOptions({
|
|
64
|
+
minTime,
|
|
65
|
+
maxTime,
|
|
66
|
+
stepMinutes,
|
|
67
|
+
}: Pick<TimePickerProps, 'maxTime' | 'minTime' | 'stepMinutes'>): string[] {
|
|
68
|
+
const step = resolveStepMinutes(stepMinutes);
|
|
69
|
+
const min = parseTimeToMinutes(minTime) ?? 0;
|
|
70
|
+
const max = parseTimeToMinutes(maxTime) ?? MINUTES_PER_DAY - 1;
|
|
71
|
+
const options: string[] = [];
|
|
72
|
+
|
|
73
|
+
for (let minute = 0; minute < MINUTES_PER_DAY; minute += step) {
|
|
74
|
+
if (minute >= min && minute <= max) {
|
|
75
|
+
options.push(formatMinutes(minute));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return options;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function TimePickerInner({
|
|
83
|
+
themeId: _themeId,
|
|
84
|
+
mode: _mode,
|
|
85
|
+
value,
|
|
86
|
+
onValueChange,
|
|
87
|
+
label,
|
|
88
|
+
description,
|
|
89
|
+
error,
|
|
90
|
+
placeholder = 'Choose time',
|
|
91
|
+
minTime,
|
|
92
|
+
maxTime,
|
|
93
|
+
stepMinutes,
|
|
94
|
+
disabled = false,
|
|
95
|
+
required = false,
|
|
96
|
+
formatTime,
|
|
97
|
+
testID,
|
|
98
|
+
}: TimePickerProps) {
|
|
99
|
+
const [visible, setVisible] = React.useState(false);
|
|
100
|
+
const options = React.useMemo(
|
|
101
|
+
() => generateTimeOptions({ maxTime, minTime, stepMinutes }),
|
|
102
|
+
[maxTime, minTime, stepMinutes],
|
|
103
|
+
);
|
|
104
|
+
const displayValue = value ? (formatTime ? formatTime(value) : value) : placeholder;
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<Field
|
|
108
|
+
disabled={disabled}
|
|
109
|
+
errorText={error}
|
|
110
|
+
invalid={Boolean(error)}
|
|
111
|
+
label={label ? renderLabel(label, description) : undefined}
|
|
112
|
+
required={required}
|
|
113
|
+
testID={testID}
|
|
114
|
+
>
|
|
115
|
+
<Button
|
|
116
|
+
disabled={disabled}
|
|
117
|
+
onPress={() => setVisible(true)}
|
|
118
|
+
testID={testID ? `${testID}-trigger` : undefined}
|
|
119
|
+
trailingIcon={{ name: 'chevron-down-outline' }}
|
|
120
|
+
variant="outline"
|
|
121
|
+
>
|
|
122
|
+
{displayValue}
|
|
123
|
+
</Button>
|
|
124
|
+
<ActionSheet
|
|
125
|
+
description={description}
|
|
126
|
+
onDismiss={() => setVisible(false)}
|
|
127
|
+
testID={testID ? `${testID}-sheet` : undefined}
|
|
128
|
+
title={label ?? 'Choose time'}
|
|
129
|
+
visible={visible}
|
|
130
|
+
>
|
|
131
|
+
<ScrollView style={{ maxHeight: 360 }}>
|
|
132
|
+
<Stack gap="xxs">
|
|
133
|
+
{options.map((option) => (
|
|
134
|
+
<ActionSheetItem
|
|
135
|
+
key={option}
|
|
136
|
+
label={formatTime ? formatTime(option) : option}
|
|
137
|
+
onPress={() => {
|
|
138
|
+
onValueChange?.(option);
|
|
139
|
+
setVisible(false);
|
|
140
|
+
}}
|
|
141
|
+
selected={option === value}
|
|
142
|
+
testID={testID ? `${testID}-option-${option}` : undefined}
|
|
143
|
+
/>
|
|
144
|
+
))}
|
|
145
|
+
</Stack>
|
|
146
|
+
</ScrollView>
|
|
147
|
+
</ActionSheet>
|
|
148
|
+
</Field>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const TimePicker = withZoraThemeScope(TimePickerInner);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ZoraComponentMeta } from '../../metadata';
|
|
2
|
+
|
|
3
|
+
export const timePickerMeta = {
|
|
4
|
+
name: 'TimePicker',
|
|
5
|
+
category: 'component',
|
|
6
|
+
directManifestNode: false,
|
|
7
|
+
allowedChildren: [],
|
|
8
|
+
note: 'Code-facing ActionSheet-backed time picker; not represented as a manifest node in v1.',
|
|
9
|
+
props: {},
|
|
10
|
+
} as const satisfies ZoraComponentMeta;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
4
|
+
|
|
5
|
+
export type TimePickerValue = string | null;
|
|
6
|
+
|
|
7
|
+
export interface TimePickerProps extends ZoraBaseProps {
|
|
8
|
+
value: TimePickerValue;
|
|
9
|
+
onValueChange?: (value: TimePickerValue) => void;
|
|
10
|
+
label?: React.ReactNode;
|
|
11
|
+
description?: React.ReactNode;
|
|
12
|
+
error?: React.ReactNode;
|
|
13
|
+
placeholder?: React.ReactNode;
|
|
14
|
+
minTime?: string;
|
|
15
|
+
maxTime?: string;
|
|
16
|
+
stepMinutes?: number;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
required?: boolean;
|
|
19
|
+
formatTime?: (value: string) => React.ReactNode;
|
|
20
|
+
testID?: string;
|
|
21
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,8 @@ export type {
|
|
|
35
35
|
DataTableSortState,
|
|
36
36
|
} from './components/data-table';
|
|
37
37
|
export { DataTable } from './components/data-table';
|
|
38
|
+
export type { DatePickerProps, DatePickerValue } from './components/date-picker';
|
|
39
|
+
export { DatePicker } from './components/date-picker';
|
|
38
40
|
export type { DrawerProps } from './components/drawer';
|
|
39
41
|
export { Drawer } from './components/drawer';
|
|
40
42
|
export type {
|
|
@@ -132,6 +134,8 @@ export type {
|
|
|
132
134
|
export { Text } from './components/text';
|
|
133
135
|
export type { TextareaProps } from './components/textarea';
|
|
134
136
|
export { Textarea } from './components/textarea';
|
|
137
|
+
export type { TimePickerProps, TimePickerValue } from './components/time-picker';
|
|
138
|
+
export { TimePicker } from './components/time-picker';
|
|
135
139
|
export type { ToastOptions, ToastProps, ToastProviderProps, ToastStatus } from './components/toast';
|
|
136
140
|
export { Toast, ToastProvider, useToast } from './components/toast';
|
|
137
141
|
export type { ToolbarActionProps, ToolbarPosition, ToolbarProps } from './components/toolbar';
|
|
@@ -10,6 +10,7 @@ import { checkboxGroupMeta, checkboxMeta } from '../components/checkbox/meta';
|
|
|
10
10
|
import { chipMeta } from '../components/chip/meta';
|
|
11
11
|
import { chipGroupMeta } from '../components/chip-group/meta';
|
|
12
12
|
import { dataTableMeta } from '../components/data-table/meta';
|
|
13
|
+
import { datePickerMeta } from '../components/date-picker/meta';
|
|
13
14
|
import { drawerMeta } from '../components/drawer/meta';
|
|
14
15
|
import { formActionsMeta, formErrorMeta, formFieldMeta, formMeta } from '../components/form/meta';
|
|
15
16
|
import { headingMeta } from '../components/heading/meta';
|
|
@@ -37,6 +38,7 @@ import {
|
|
|
37
38
|
import { tabsMeta } from '../components/tabs/meta';
|
|
38
39
|
import { textMeta } from '../components/text/meta';
|
|
39
40
|
import { textareaMeta } from '../components/textarea/meta';
|
|
41
|
+
import { timePickerMeta } from '../components/time-picker/meta';
|
|
40
42
|
import { toastMeta, toastProviderMeta } from '../components/toast/meta';
|
|
41
43
|
import { toolbarActionMeta, toolbarMeta } from '../components/toolbar/meta';
|
|
42
44
|
import { foundationMetas } from '../foundation/meta';
|
|
@@ -96,6 +98,7 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = {
|
|
|
96
98
|
Chip: chipMeta,
|
|
97
99
|
ChipGroup: chipGroupMeta,
|
|
98
100
|
DataTable: dataTableMeta,
|
|
101
|
+
DatePicker: datePickerMeta,
|
|
99
102
|
Drawer: drawerMeta,
|
|
100
103
|
DropdownMenu: dropdownMenuMeta,
|
|
101
104
|
Form: formMeta,
|
|
@@ -126,6 +129,7 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = {
|
|
|
126
129
|
Tabs: tabsMeta,
|
|
127
130
|
Text: textMeta,
|
|
128
131
|
Textarea: textareaMeta,
|
|
132
|
+
TimePicker: timePickerMeta,
|
|
129
133
|
Toast: toastMeta,
|
|
130
134
|
ToastProvider: toastProviderMeta,
|
|
131
135
|
Toolbar: toolbarMeta,
|