@utilitywarehouse/hearth-react-native 0.10.0 → 0.11.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +8 -0
- package/build/components/Avatar/Avatar.d.ts +6 -0
- package/build/components/Avatar/Avatar.js +80 -0
- package/build/components/Avatar/Avatar.props.d.ts +28 -0
- package/build/components/Avatar/Avatar.props.js +1 -0
- package/build/components/Avatar/index.d.ts +2 -0
- package/build/components/Avatar/index.js +1 -0
- package/build/components/DateInput/DateInput.d.ts +6 -0
- package/build/components/DateInput/DateInput.js +19 -0
- package/build/components/DateInput/DateInput.props.d.ts +79 -0
- package/build/components/DateInput/DateInput.props.js +1 -0
- package/build/components/DateInput/DateInputSegment.d.ts +20 -0
- package/build/components/DateInput/DateInputSegment.js +31 -0
- package/build/components/DateInput/index.d.ts +2 -0
- package/build/components/DateInput/index.js +1 -0
- package/build/components/index.d.ts +2 -0
- package/build/components/index.js +2 -0
- package/build/utils/getInitials.d.ts +1 -0
- package/build/utils/getInitials.js +8 -0
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.js +1 -0
- package/docs/components/AllComponents.web.tsx +18 -1
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.docs.mdx +105 -0
- package/src/components/Avatar/Avatar.props.ts +31 -0
- package/src/components/Avatar/Avatar.stories.tsx +77 -0
- package/src/components/Avatar/Avatar.tsx +136 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/DateInput/DateInput.docs.mdx +163 -0
- package/src/components/DateInput/DateInput.props.ts +80 -0
- package/src/components/DateInput/DateInput.stories.tsx +269 -0
- package/src/components/DateInput/DateInput.tsx +117 -0
- package/src/components/DateInput/DateInputSegment.tsx +83 -0
- package/src/components/DateInput/index.ts +2 -0
- package/src/components/index.ts +2 -0
- package/src/utils/getInitials.ts +7 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-native';
|
|
2
|
+
import { TickSmallIcon, WarningSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { View } from 'react-native';
|
|
5
|
+
import { Button, Card, Flex, Heading } from '../../components';
|
|
6
|
+
import { DateInput } from './';
|
|
7
|
+
|
|
8
|
+
const DateInputMeta: Meta<typeof DateInput> = {
|
|
9
|
+
title: 'Stories / DateInput',
|
|
10
|
+
component: DateInput,
|
|
11
|
+
parameters: {
|
|
12
|
+
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
|
|
13
|
+
layout: 'centered',
|
|
14
|
+
},
|
|
15
|
+
argTypes: {
|
|
16
|
+
label: {
|
|
17
|
+
control: 'text',
|
|
18
|
+
},
|
|
19
|
+
helperText: {
|
|
20
|
+
control: 'text',
|
|
21
|
+
},
|
|
22
|
+
validationStatus: {
|
|
23
|
+
control: 'select',
|
|
24
|
+
options: ['initial', 'valid', 'invalid'],
|
|
25
|
+
},
|
|
26
|
+
disabled: {
|
|
27
|
+
control: 'boolean',
|
|
28
|
+
},
|
|
29
|
+
required: {
|
|
30
|
+
control: 'boolean',
|
|
31
|
+
},
|
|
32
|
+
hideDay: {
|
|
33
|
+
control: 'boolean',
|
|
34
|
+
},
|
|
35
|
+
hideMonth: {
|
|
36
|
+
control: 'boolean',
|
|
37
|
+
},
|
|
38
|
+
hideYear: {
|
|
39
|
+
control: 'boolean',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default DateInputMeta;
|
|
45
|
+
|
|
46
|
+
type Story = StoryObj<typeof DateInput>;
|
|
47
|
+
|
|
48
|
+
export const Playground: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
label: 'Date',
|
|
51
|
+
helperText: 'Helper text',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const DateOfBirth: Story = {
|
|
56
|
+
render: () => {
|
|
57
|
+
return <DateInput label="Date of birth" helperText="Enter your date of birth" required />;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const CardExpiry: Story = {
|
|
62
|
+
render: () => {
|
|
63
|
+
const [month, setMonth] = useState('');
|
|
64
|
+
const [year, setYear] = useState('');
|
|
65
|
+
return (
|
|
66
|
+
<DateInput
|
|
67
|
+
label="Card expiry"
|
|
68
|
+
helperText="Enter the expiry month and year"
|
|
69
|
+
monthValue={month}
|
|
70
|
+
yearValue={year}
|
|
71
|
+
onMonthChange={setMonth}
|
|
72
|
+
onYearChange={setYear}
|
|
73
|
+
hideDay
|
|
74
|
+
required
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const YearOnly: Story = {
|
|
81
|
+
render: () => {
|
|
82
|
+
return <DateInput label="Year" helperText="Enter the year" hideDay hideMonth />;
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const Validation: Story = {
|
|
87
|
+
render: () => {
|
|
88
|
+
const [day, setDay] = useState('01');
|
|
89
|
+
const [month, setMonth] = useState('02');
|
|
90
|
+
const [year, setYear] = useState('2025');
|
|
91
|
+
return (
|
|
92
|
+
<View style={{ gap: 16 }}>
|
|
93
|
+
<DateInput
|
|
94
|
+
label="Valid date"
|
|
95
|
+
dayValue={day}
|
|
96
|
+
monthValue={month}
|
|
97
|
+
yearValue={year}
|
|
98
|
+
onDayChange={setDay}
|
|
99
|
+
onMonthChange={setMonth}
|
|
100
|
+
onYearChange={setYear}
|
|
101
|
+
validationStatus="valid"
|
|
102
|
+
validText="Date is valid"
|
|
103
|
+
helperIcon={TickSmallIcon}
|
|
104
|
+
required
|
|
105
|
+
/>
|
|
106
|
+
<DateInput
|
|
107
|
+
label="Invalid date"
|
|
108
|
+
dayValue="32"
|
|
109
|
+
monthValue="13"
|
|
110
|
+
yearValue="2025"
|
|
111
|
+
validationStatus="invalid"
|
|
112
|
+
invalidText="Please enter a valid date"
|
|
113
|
+
helperIcon={WarningSmallIcon}
|
|
114
|
+
required
|
|
115
|
+
/>
|
|
116
|
+
</View>
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const Disabled: Story = {
|
|
122
|
+
render: () => {
|
|
123
|
+
return (
|
|
124
|
+
<DateInput
|
|
125
|
+
label="Date of birth"
|
|
126
|
+
helperText="This field is disabled"
|
|
127
|
+
dayValue="15"
|
|
128
|
+
monthValue="06"
|
|
129
|
+
yearValue="1990"
|
|
130
|
+
disabled
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const DefaultValue: Story = {
|
|
137
|
+
render: () => {
|
|
138
|
+
return <DateInput label="Date of birth" dayValue="01" monthValue="01" yearValue="2000" />;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const WithCustomValidation: Story = {
|
|
143
|
+
render: () => {
|
|
144
|
+
const [day, setDay] = useState('');
|
|
145
|
+
const [month, setMonth] = useState('');
|
|
146
|
+
const [year, setYear] = useState('');
|
|
147
|
+
|
|
148
|
+
const validateDate = () => {
|
|
149
|
+
if (!day || !month || !year) return { status: 'initial' as const, message: '' };
|
|
150
|
+
|
|
151
|
+
const dayNum = parseInt(day, 10);
|
|
152
|
+
const monthNum = parseInt(month, 10);
|
|
153
|
+
const yearNum = parseInt(year, 10);
|
|
154
|
+
|
|
155
|
+
// Basic validation
|
|
156
|
+
if (dayNum < 1 || dayNum > 31) {
|
|
157
|
+
return { status: 'invalid' as const, message: 'Day must be between 1 and 31' };
|
|
158
|
+
}
|
|
159
|
+
if (monthNum < 1 || monthNum > 12) {
|
|
160
|
+
return { status: 'invalid' as const, message: 'Month must be between 1 and 12' };
|
|
161
|
+
}
|
|
162
|
+
if (yearNum < 1900 || yearNum > new Date().getFullYear()) {
|
|
163
|
+
return {
|
|
164
|
+
status: 'invalid' as const,
|
|
165
|
+
message: `Year must be between 1900 and ${new Date().getFullYear()}`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Check valid date
|
|
170
|
+
const date = new Date(yearNum, monthNum - 1, dayNum);
|
|
171
|
+
if (
|
|
172
|
+
date.getDate() !== dayNum ||
|
|
173
|
+
date.getMonth() !== monthNum - 1 ||
|
|
174
|
+
date.getFullYear() !== yearNum
|
|
175
|
+
) {
|
|
176
|
+
return { status: 'invalid' as const, message: 'Please enter a valid date' };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { status: 'valid' as const, message: 'Valid date' };
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const validation = validateDate();
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<DateInput
|
|
186
|
+
label="Date of birth"
|
|
187
|
+
helperText="Enter a valid date between 1900 and today"
|
|
188
|
+
dayValue={day}
|
|
189
|
+
monthValue={month}
|
|
190
|
+
yearValue={year}
|
|
191
|
+
onDayChange={setDay}
|
|
192
|
+
onMonthChange={setMonth}
|
|
193
|
+
onYearChange={setYear}
|
|
194
|
+
validationStatus={validation.status}
|
|
195
|
+
validText={validation.status === 'valid' ? validation.message : undefined}
|
|
196
|
+
invalidText={validation.status === 'invalid' ? validation.message : undefined}
|
|
197
|
+
required
|
|
198
|
+
/>
|
|
199
|
+
);
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const FlexibleSegments: Story = {
|
|
204
|
+
render: () => (
|
|
205
|
+
<View style={{ gap: 16 }}>
|
|
206
|
+
<DateInput label="Full date" helperText="DD/MM/YYYY" />
|
|
207
|
+
<DateInput label="Month and year" helperText="MM/YYYY" hideDay required />
|
|
208
|
+
<DateInput label="Year only" helperText="YYYY" hideDay hideMonth required />
|
|
209
|
+
</View>
|
|
210
|
+
),
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export const GroupingInputs: Story = {
|
|
214
|
+
render: () => (
|
|
215
|
+
<Flex space="sm">
|
|
216
|
+
<Heading size="lg">Event Registration</Heading>
|
|
217
|
+
<Card variant="subtle" gap="250">
|
|
218
|
+
<DateInput label="Date of birth" helperText="Enter your date of birth" required />
|
|
219
|
+
<DateInput
|
|
220
|
+
label="Event date preference"
|
|
221
|
+
helperText="Select your preferred date"
|
|
222
|
+
required={false}
|
|
223
|
+
/>
|
|
224
|
+
</Card>
|
|
225
|
+
</Flex>
|
|
226
|
+
),
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export const WithState: Story = {
|
|
230
|
+
render: () => {
|
|
231
|
+
const [day, setDay] = useState('');
|
|
232
|
+
const [month, setMonth] = useState('');
|
|
233
|
+
const [year, setYear] = useState('');
|
|
234
|
+
|
|
235
|
+
const handleReset = () => {
|
|
236
|
+
setDay('');
|
|
237
|
+
setMonth('');
|
|
238
|
+
setYear('');
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const handleSetToday = () => {
|
|
242
|
+
const today = new Date();
|
|
243
|
+
setDay(String(today.getDate()).padStart(2, '0'));
|
|
244
|
+
setMonth(String(today.getMonth() + 1).padStart(2, '0'));
|
|
245
|
+
setYear(String(today.getFullYear()));
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<Flex space="md">
|
|
250
|
+
<DateInput
|
|
251
|
+
label="Date"
|
|
252
|
+
helperText="Select or enter a date"
|
|
253
|
+
dayValue={day}
|
|
254
|
+
monthValue={month}
|
|
255
|
+
yearValue={year}
|
|
256
|
+
onDayChange={setDay}
|
|
257
|
+
onMonthChange={setMonth}
|
|
258
|
+
onYearChange={setYear}
|
|
259
|
+
/>
|
|
260
|
+
<Flex space="xs">
|
|
261
|
+
<Button onPress={handleSetToday}>Set to Today</Button>
|
|
262
|
+
<Button onPress={handleReset} variant="solid">
|
|
263
|
+
Reset
|
|
264
|
+
</Button>
|
|
265
|
+
</Flex>
|
|
266
|
+
</Flex>
|
|
267
|
+
);
|
|
268
|
+
},
|
|
269
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
3
|
+
import { FormField } from '../FormField';
|
|
4
|
+
import type { DateInputProps } from './DateInput.props';
|
|
5
|
+
import DateInputSegment from './DateInputSegment';
|
|
6
|
+
|
|
7
|
+
const DateInput = ({
|
|
8
|
+
label,
|
|
9
|
+
helperText,
|
|
10
|
+
helperIcon,
|
|
11
|
+
validationStatus = 'initial',
|
|
12
|
+
validText,
|
|
13
|
+
invalidText,
|
|
14
|
+
disabled,
|
|
15
|
+
readonly,
|
|
16
|
+
required,
|
|
17
|
+
hideDay = false,
|
|
18
|
+
hideMonth = false,
|
|
19
|
+
hideYear = false,
|
|
20
|
+
dayPlaceholder = 'DD',
|
|
21
|
+
monthPlaceholder = 'MM',
|
|
22
|
+
yearPlaceholder = 'YYYY',
|
|
23
|
+
dayValue,
|
|
24
|
+
monthValue,
|
|
25
|
+
yearValue,
|
|
26
|
+
onDayChange,
|
|
27
|
+
onMonthChange,
|
|
28
|
+
onYearChange,
|
|
29
|
+
onDayFocus,
|
|
30
|
+
onMonthFocus,
|
|
31
|
+
onYearFocus,
|
|
32
|
+
onDayBlur,
|
|
33
|
+
onMonthBlur,
|
|
34
|
+
onYearBlur,
|
|
35
|
+
...props
|
|
36
|
+
}: DateInputProps) => {
|
|
37
|
+
return (
|
|
38
|
+
<FormField
|
|
39
|
+
label={label}
|
|
40
|
+
helperText={helperText}
|
|
41
|
+
helperIcon={helperIcon}
|
|
42
|
+
validationStatus={validationStatus}
|
|
43
|
+
validText={validText}
|
|
44
|
+
invalidText={invalidText}
|
|
45
|
+
disabled={disabled}
|
|
46
|
+
readonly={readonly}
|
|
47
|
+
required={required}
|
|
48
|
+
style={styles.wrap}
|
|
49
|
+
{...props}
|
|
50
|
+
>
|
|
51
|
+
<View style={styles.container}>
|
|
52
|
+
{!hideDay && (
|
|
53
|
+
<DateInputSegment
|
|
54
|
+
label="Day"
|
|
55
|
+
placeholder={dayPlaceholder}
|
|
56
|
+
value={dayValue}
|
|
57
|
+
onChange={onDayChange}
|
|
58
|
+
onFocus={onDayFocus}
|
|
59
|
+
onBlur={onDayBlur}
|
|
60
|
+
disabled={disabled}
|
|
61
|
+
required={required}
|
|
62
|
+
readonly={readonly}
|
|
63
|
+
validationStatus={validationStatus}
|
|
64
|
+
maxLength={2}
|
|
65
|
+
testID="date-input-day"
|
|
66
|
+
/>
|
|
67
|
+
)}
|
|
68
|
+
{!hideMonth && (
|
|
69
|
+
<DateInputSegment
|
|
70
|
+
label="Month"
|
|
71
|
+
placeholder={monthPlaceholder}
|
|
72
|
+
value={monthValue}
|
|
73
|
+
onChange={onMonthChange}
|
|
74
|
+
onFocus={onMonthFocus}
|
|
75
|
+
onBlur={onMonthBlur}
|
|
76
|
+
disabled={disabled}
|
|
77
|
+
required={required}
|
|
78
|
+
readonly={readonly}
|
|
79
|
+
validationStatus={validationStatus}
|
|
80
|
+
maxLength={2}
|
|
81
|
+
testID="date-input-month"
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
{!hideYear && (
|
|
85
|
+
<DateInputSegment
|
|
86
|
+
label="Year"
|
|
87
|
+
placeholder={yearPlaceholder}
|
|
88
|
+
value={yearValue}
|
|
89
|
+
onChange={onYearChange}
|
|
90
|
+
onFocus={onYearFocus}
|
|
91
|
+
onBlur={onYearBlur}
|
|
92
|
+
disabled={disabled}
|
|
93
|
+
required={required}
|
|
94
|
+
readonly={readonly}
|
|
95
|
+
validationStatus={validationStatus}
|
|
96
|
+
maxLength={4}
|
|
97
|
+
testID="date-input-year"
|
|
98
|
+
/>
|
|
99
|
+
)}
|
|
100
|
+
</View>
|
|
101
|
+
</FormField>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
DateInput.displayName = 'DateInput';
|
|
106
|
+
|
|
107
|
+
const styles = StyleSheet.create(theme => ({
|
|
108
|
+
wrap: {
|
|
109
|
+
gap: theme.components.input.gap,
|
|
110
|
+
},
|
|
111
|
+
container: {
|
|
112
|
+
flexDirection: 'row',
|
|
113
|
+
gap: theme.components.input.date.gap,
|
|
114
|
+
},
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
export default DateInput;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
3
|
+
import { BodyText } from '../BodyText';
|
|
4
|
+
import { Input } from '../Input';
|
|
5
|
+
import type { DateInputProps } from './DateInput.props';
|
|
6
|
+
|
|
7
|
+
interface DateInputSegmentProps {
|
|
8
|
+
label: string;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
value?: string;
|
|
11
|
+
onChange?: (text: string) => void;
|
|
12
|
+
onFocus?: DateInputProps['onDayFocus'];
|
|
13
|
+
onBlur?: DateInputProps['onDayBlur'];
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
required?: boolean;
|
|
16
|
+
validationStatus?: DateInputProps['validationStatus'];
|
|
17
|
+
maxLength?: number;
|
|
18
|
+
readonly?: boolean;
|
|
19
|
+
testID?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const DateInputSegment = ({
|
|
23
|
+
label,
|
|
24
|
+
placeholder,
|
|
25
|
+
value,
|
|
26
|
+
onChange,
|
|
27
|
+
onFocus,
|
|
28
|
+
onBlur,
|
|
29
|
+
disabled,
|
|
30
|
+
validationStatus,
|
|
31
|
+
maxLength,
|
|
32
|
+
readonly,
|
|
33
|
+
testID,
|
|
34
|
+
}: DateInputSegmentProps) => {
|
|
35
|
+
styles.useVariants({ disabled });
|
|
36
|
+
return (
|
|
37
|
+
<View style={styles.container}>
|
|
38
|
+
<BodyText size="md" style={styles.label}>
|
|
39
|
+
{label}
|
|
40
|
+
</BodyText>
|
|
41
|
+
<Input
|
|
42
|
+
value={value}
|
|
43
|
+
onChangeText={onChange}
|
|
44
|
+
onFocus={onFocus}
|
|
45
|
+
onBlur={onBlur}
|
|
46
|
+
placeholder={disabled ? undefined : placeholder}
|
|
47
|
+
keyboardType="number-pad"
|
|
48
|
+
maxLength={maxLength}
|
|
49
|
+
testID={testID}
|
|
50
|
+
accessibilityLabel={label}
|
|
51
|
+
disabled={disabled}
|
|
52
|
+
validationStatus={validationStatus}
|
|
53
|
+
readonly={readonly}
|
|
54
|
+
style={styles.input}
|
|
55
|
+
/>
|
|
56
|
+
</View>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
DateInputSegment.displayName = 'DateInputSegment';
|
|
61
|
+
|
|
62
|
+
const styles = StyleSheet.create(theme => ({
|
|
63
|
+
container: {
|
|
64
|
+
flex: 1,
|
|
65
|
+
gap: theme.components.input.gap,
|
|
66
|
+
maxWidth: 96,
|
|
67
|
+
},
|
|
68
|
+
label: {
|
|
69
|
+
variants: {
|
|
70
|
+
disabled: {
|
|
71
|
+
true: {
|
|
72
|
+
opacity: theme.opacity.disabled,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
input: {
|
|
78
|
+
flex: 1,
|
|
79
|
+
maxWidth: 96,
|
|
80
|
+
},
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
export default DateInputSegment;
|
package/src/components/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Custom
|
|
2
2
|
export * from './Accordion';
|
|
3
3
|
export * from './Alert';
|
|
4
|
+
export * from './Avatar';
|
|
4
5
|
export * from './Badge';
|
|
5
6
|
export * from './Banner';
|
|
6
7
|
export * from './BodyText';
|
|
@@ -13,6 +14,7 @@ export * from './Center';
|
|
|
13
14
|
export * from './Checkbox';
|
|
14
15
|
export * from './Container';
|
|
15
16
|
export * from './CurrencyInput';
|
|
17
|
+
export * from './DateInput';
|
|
16
18
|
export * from './DatePicker';
|
|
17
19
|
export * from './DatePickerInput';
|
|
18
20
|
export * from './DescriptionList';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function getInitials(name?: string) {
|
|
2
|
+
if (!name) return undefined;
|
|
3
|
+
const regex = new RegExp(/(\p{L}{1})\p{L}+/gu);
|
|
4
|
+
const names = [...name.matchAll(regex)];
|
|
5
|
+
const initials = (names.shift()?.[1] || '') + (names.pop()?.[1] || '');
|
|
6
|
+
return initials.toUpperCase();
|
|
7
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { default as formatThousands } from './formatThousands';
|
|
|
3
3
|
export { default as getFlattenedColorValue } from './getFlattenedColorValue';
|
|
4
4
|
export { default as getStyleValue } from './getStyleValue';
|
|
5
5
|
export { default as hexWithOpacity } from './hexWithOpacity';
|
|
6
|
+
export { getInitials } from './getInitials';
|
|
6
7
|
export { default as isEqual } from './isEqual';
|
|
7
8
|
export * from './styleUtils';
|
|
8
9
|
export * from './themeValueHelpers';
|