@ankhorage/zora 2.8.3 → 2.8.4
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 +1 -2
- package/dist/components/avatar/Avatar.d.ts.map +1 -1
- package/dist/components/avatar/Avatar.js +16 -8
- package/dist/components/avatar/Avatar.js.map +1 -1
- package/dist/components/date-picker/DatePicker.d.ts.map +1 -1
- package/dist/components/date-picker/DatePicker.js +16 -7
- package/dist/components/date-picker/DatePicker.js.map +1 -1
- package/dist/components/form/useFormController.d.ts.map +1 -1
- package/dist/components/form/useFormController.js +4 -4
- package/dist/components/form/useFormController.js.map +1 -1
- package/dist/components/heading/resolveHeadingRecipe.d.ts.map +1 -1
- package/dist/components/heading/resolveHeadingRecipe.js +18 -2
- package/dist/components/heading/resolveHeadingRecipe.js.map +1 -1
- package/dist/components/text/resolveTextRecipe.d.ts.map +1 -1
- package/dist/components/text/resolveTextRecipe.js +18 -2
- package/dist/components/text/resolveTextRecipe.js.map +1 -1
- package/dist/patterns/auth/oauthProviders.d.ts.map +1 -1
- package/dist/patterns/auth/oauthProviders.js +4 -4
- package/dist/patterns/auth/oauthProviders.js.map +1 -1
- package/dist/patterns/auth/utils.d.ts.map +1 -1
- package/dist/patterns/auth/utils.js +13 -7
- package/dist/patterns/auth/utils.js.map +1 -1
- package/dist/patterns/image-upload-field/uploadFlow.js +1 -1
- package/dist/patterns/image-upload-field/uploadFlow.js.map +1 -1
- package/dist/patterns/selection/resolveSelectionNextIds.js +1 -1
- package/dist/patterns/selection/resolveSelectionNextIds.js.map +1 -1
- package/dist/patterns/theme-composer/ThemeComposer.d.ts +1 -1
- package/dist/patterns/theme-composer/ThemeComposer.d.ts.map +1 -1
- package/dist/patterns/theme-composer/ThemeComposer.js +37 -40
- package/dist/patterns/theme-composer/ThemeComposer.js.map +1 -1
- package/package.json +2 -2
- package/src/components/avatar/Avatar.tsx +16 -8
- package/src/components/date-picker/DatePicker.tsx +18 -7
- package/src/components/form/useFormController.ts +6 -4
- package/src/components/heading/resolveHeadingRecipe.ts +20 -2
- package/src/components/text/resolveTextRecipe.test.ts +20 -2
- package/src/components/text/resolveTextRecipe.ts +20 -2
- package/src/metadata/componentMeta.test.ts +5 -3
- package/src/nativePeerContract.test.ts +11 -1
- package/src/patterns/auth/oauthProviders.ts +6 -7
- package/src/patterns/auth/utils.ts +13 -7
- package/src/patterns/image-upload-field/uploadFlow.ts +1 -1
- package/src/patterns/selection/resolveSelectionNextIds.ts +1 -1
- package/src/patterns/theme-composer/ThemeComposer.tsx +48 -41
|
@@ -44,19 +44,18 @@ const DEFAULT_OAUTH_PROVIDER_LABELS = {
|
|
|
44
44
|
zoom: 'Zoom',
|
|
45
45
|
} as const satisfies Record<string, string>;
|
|
46
46
|
|
|
47
|
-
const
|
|
47
|
+
const oauthProviderIcons = new Map<string, OAuthProviderIconSpec>(
|
|
48
|
+
Object.entries(DEFAULT_OAUTH_PROVIDER_ICONS),
|
|
49
|
+
);
|
|
50
|
+
const oauthProviderLabels = new Map<string, string>(Object.entries(DEFAULT_OAUTH_PROVIDER_LABELS));
|
|
48
51
|
|
|
49
52
|
export function resolveOAuthProviderIcon(providerId: string): OAuthProviderIconSpec | undefined {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return DEFAULT_OAUTH_PROVIDER_ICONS[
|
|
53
|
-
normalizedProviderId as keyof typeof DEFAULT_OAUTH_PROVIDER_ICONS
|
|
54
|
-
];
|
|
53
|
+
return oauthProviderIcons.get(normalizeOAuthProviderId(providerId));
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
export function resolveOAuthProviderLabel(providerId: string): string {
|
|
58
57
|
const normalizedProviderId = normalizeOAuthProviderId(providerId);
|
|
59
|
-
const knownLabel = oauthProviderLabels
|
|
58
|
+
const knownLabel = oauthProviderLabels.get(normalizedProviderId);
|
|
60
59
|
|
|
61
60
|
return knownLabel ?? titleCaseProviderId(normalizedProviderId);
|
|
62
61
|
}
|
|
@@ -3,11 +3,17 @@ import type { AuthIdentifierKind } from './types';
|
|
|
3
3
|
|
|
4
4
|
export const defaultIdentifiers = ['email'] as const satisfies readonly AuthIdentifierKind[];
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
function resolveIdentifierKindLabel(identifier: AuthIdentifierKind): string {
|
|
7
|
+
switch (identifier) {
|
|
8
|
+
case 'phone':
|
|
9
|
+
return 'Phone';
|
|
10
|
+
case 'username':
|
|
11
|
+
return 'Username';
|
|
12
|
+
case 'email':
|
|
13
|
+
default:
|
|
14
|
+
return 'Email';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
11
17
|
|
|
12
18
|
function includesIdentifier(
|
|
13
19
|
identifiers: readonly AuthIdentifierKind[],
|
|
@@ -18,10 +24,10 @@ function includesIdentifier(
|
|
|
18
24
|
|
|
19
25
|
export function resolveIdentifierLabel(identifiers: readonly AuthIdentifierKind[]): string {
|
|
20
26
|
if (identifiers.length === 1) {
|
|
21
|
-
return
|
|
27
|
+
return resolveIdentifierKindLabel(identifiers[0] ?? 'email');
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
return identifiers.map(
|
|
30
|
+
return identifiers.map(resolveIdentifierKindLabel).join(', or ');
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
export function resolveIdentifierType(
|
|
@@ -78,7 +78,7 @@ function formatBytes(value: number): string {
|
|
|
78
78
|
unitIndex += 1;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const unit = units
|
|
81
|
+
const unit = units.at(unitIndex) ?? 'B';
|
|
82
82
|
const rounded = unitIndex === 0 ? Math.round(size) : Math.round(size * 10) / 10;
|
|
83
83
|
return `${rounded} ${unit}`;
|
|
84
84
|
}
|
|
@@ -20,7 +20,7 @@ export function normalizeIds(
|
|
|
20
20
|
export function areIdsEqual(a: readonly string[], b: readonly string[]): boolean {
|
|
21
21
|
if (a.length !== b.length) return false;
|
|
22
22
|
for (let index = 0; index < a.length; index += 1) {
|
|
23
|
-
if (a
|
|
23
|
+
if (a.at(index) !== b.at(index)) return false;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
return true;
|
|
@@ -21,6 +21,15 @@ const HEX_ERROR_MESSAGE = 'Enter a valid 6-digit hex color.';
|
|
|
21
21
|
const HEX_INPUT_PLACEHOLDER = 'Enter hex color';
|
|
22
22
|
const NAME_ERROR_MESSAGE = 'Theme name cannot be empty.';
|
|
23
23
|
|
|
24
|
+
interface InputDraft {
|
|
25
|
+
inputValue: string;
|
|
26
|
+
error: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createInputDraft(sourceValue: string): InputDraft {
|
|
30
|
+
return { inputValue: sourceValue, error: undefined };
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
function isValidHex(value: string): boolean {
|
|
25
34
|
try {
|
|
26
35
|
parseHexColorOrThrow(value);
|
|
@@ -56,56 +65,52 @@ function ThemeComposerInner({
|
|
|
56
65
|
}: ThemeComposerProps) {
|
|
57
66
|
const { theme } = useZoraTheme();
|
|
58
67
|
|
|
59
|
-
const [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const [
|
|
63
|
-
const [
|
|
68
|
+
const [hexDraft, setHexDraft] = React.useState<InputDraft>(() =>
|
|
69
|
+
createInputDraft(value.primaryColor),
|
|
70
|
+
);
|
|
71
|
+
const [nameDraft, setNameDraft] = React.useState<InputDraft>(() => createInputDraft(value.name));
|
|
72
|
+
const [previousPrimaryColor, setPreviousPrimaryColor] = React.useState(value.primaryColor);
|
|
73
|
+
const [previousName, setPreviousName] = React.useState(value.name);
|
|
64
74
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}, [value.primaryColor]);
|
|
75
|
+
if (previousPrimaryColor !== value.primaryColor) {
|
|
76
|
+
setPreviousPrimaryColor(value.primaryColor);
|
|
77
|
+
setHexDraft(createInputDraft(value.primaryColor));
|
|
78
|
+
}
|
|
70
79
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
80
|
+
if (previousName !== value.name) {
|
|
81
|
+
setPreviousName(value.name);
|
|
82
|
+
setNameDraft(createInputDraft(value.name));
|
|
83
|
+
}
|
|
75
84
|
|
|
76
85
|
function handleNameChange(text: string) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
setNameError(undefined);
|
|
86
|
+
const error = text.trim().length === 0 ? NAME_ERROR_MESSAGE : undefined;
|
|
87
|
+
setNameDraft({ inputValue: text, error });
|
|
88
|
+
|
|
89
|
+
if (!error) {
|
|
82
90
|
onChange({ ...value, name: text });
|
|
83
91
|
}
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
function handleHexChange(text: string) {
|
|
87
|
-
// Ensure leading hash
|
|
88
95
|
const normalized = text.startsWith('#') ? text : `#${text}`;
|
|
89
|
-
|
|
96
|
+
const error = isValidHex(normalized) ? undefined : HEX_ERROR_MESSAGE;
|
|
97
|
+
setHexDraft({ inputValue: normalized, error });
|
|
90
98
|
|
|
91
|
-
if (
|
|
92
|
-
setHexError(undefined);
|
|
99
|
+
if (!error) {
|
|
93
100
|
onChange({ ...value, primaryColor: normalized });
|
|
94
|
-
} else {
|
|
95
|
-
setHexError(HEX_ERROR_MESSAGE);
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
function handleSubmit() {
|
|
100
|
-
const hasValidName =
|
|
101
|
-
const hasValidHex = isValidHex(
|
|
105
|
+
const hasValidName = nameDraft.inputValue.trim().length > 0;
|
|
106
|
+
const hasValidHex = isValidHex(hexDraft.inputValue);
|
|
102
107
|
|
|
103
108
|
if (!hasValidName) {
|
|
104
|
-
|
|
109
|
+
setNameDraft((current) => ({ ...current, error: NAME_ERROR_MESSAGE }));
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
if (!hasValidHex) {
|
|
108
|
-
|
|
113
|
+
setHexDraft((current) => ({ ...current, error: HEX_ERROR_MESSAGE }));
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
if (!hasValidName || !hasValidHex) {
|
|
@@ -114,8 +119,8 @@ function ThemeComposerInner({
|
|
|
114
119
|
|
|
115
120
|
onSubmit?.({
|
|
116
121
|
...value,
|
|
117
|
-
name:
|
|
118
|
-
primaryColor:
|
|
122
|
+
name: nameDraft.inputValue,
|
|
123
|
+
primaryColor: hexDraft.inputValue,
|
|
119
124
|
});
|
|
120
125
|
}
|
|
121
126
|
|
|
@@ -136,16 +141,16 @@ function ThemeComposerInner({
|
|
|
136
141
|
<Stack gap="xs">
|
|
137
142
|
<Text variant="label">Name</Text>
|
|
138
143
|
<Input
|
|
139
|
-
value={
|
|
144
|
+
value={nameDraft.inputValue}
|
|
140
145
|
onChangeText={handleNameChange}
|
|
141
146
|
placeholder="My theme"
|
|
142
147
|
autoCorrect={false}
|
|
143
|
-
invalid={
|
|
148
|
+
invalid={nameDraft.error !== undefined}
|
|
144
149
|
testID={testID ? `${testID}-name-input` : undefined}
|
|
145
150
|
/>
|
|
146
|
-
{
|
|
151
|
+
{nameDraft.error ? (
|
|
147
152
|
<Text color="danger" variant="bodySmall">
|
|
148
|
-
{
|
|
153
|
+
{nameDraft.error}
|
|
149
154
|
</Text>
|
|
150
155
|
) : null}
|
|
151
156
|
</Stack>
|
|
@@ -178,13 +183,13 @@ function ThemeComposerInner({
|
|
|
178
183
|
<Stack direction="row" gap="m" align="center">
|
|
179
184
|
<Box flex={1}>
|
|
180
185
|
<Input
|
|
181
|
-
value={
|
|
186
|
+
value={hexDraft.inputValue}
|
|
182
187
|
onChangeText={handleHexChange}
|
|
183
188
|
placeholder={HEX_INPUT_PLACEHOLDER}
|
|
184
189
|
autoCapitalize="none"
|
|
185
190
|
autoCorrect={false}
|
|
186
191
|
maxLength={7}
|
|
187
|
-
invalid={
|
|
192
|
+
invalid={hexDraft.error !== undefined}
|
|
188
193
|
testID={testID ? `${testID}-hex-input` : undefined}
|
|
189
194
|
/>
|
|
190
195
|
</Box>
|
|
@@ -194,15 +199,17 @@ function ThemeComposerInner({
|
|
|
194
199
|
height={36}
|
|
195
200
|
radius="m"
|
|
196
201
|
style={{
|
|
197
|
-
backgroundColor: isValidHex(
|
|
202
|
+
backgroundColor: isValidHex(hexDraft.inputValue)
|
|
203
|
+
? hexDraft.inputValue
|
|
204
|
+
: theme.colors.border,
|
|
198
205
|
borderWidth: 1,
|
|
199
206
|
borderColor: theme.colors.border,
|
|
200
207
|
}}
|
|
201
208
|
/>
|
|
202
209
|
</Stack>
|
|
203
|
-
{
|
|
210
|
+
{hexDraft.error ? (
|
|
204
211
|
<Text color="danger" variant="bodySmall">
|
|
205
|
-
{
|
|
212
|
+
{hexDraft.error}
|
|
206
213
|
</Text>
|
|
207
214
|
) : null}
|
|
208
215
|
</Stack>
|
|
@@ -310,6 +317,6 @@ function ThemeComposerInner({
|
|
|
310
317
|
/***
|
|
311
318
|
* UI for composing and applying a theme via structured controls.
|
|
312
319
|
*
|
|
313
|
-
|
|
320
|
+
*
|
|
314
321
|
*/
|
|
315
322
|
export const ThemeComposer = withZoraThemeScope(ThemeComposerInner);
|