@ankhorage/zora 25.8.16 → 25.8.18

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.
Files changed (195) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/capabilities/index.js +34 -59
  4. package/dist/capabilities/index.js.map +3 -3
  5. package/dist/features/date-picker/composition/DatePickerContent.d.ts.map +1 -1
  6. package/dist/features/date-picker/composition/DatePickerContent.js +1 -11
  7. package/dist/features/date-picker/composition/DatePickerContent.js.map +1 -1
  8. package/dist/features/date-picker/composition/DatePickerField.d.ts.map +1 -1
  9. package/dist/features/date-picker/composition/DatePickerField.js +2 -17
  10. package/dist/features/date-picker/composition/DatePickerField.js.map +1 -1
  11. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.js +7 -19
  12. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.js.map +1 -1
  13. package/dist/features/picker/composition/PickerField.d.ts +14 -0
  14. package/dist/features/picker/composition/PickerField.d.ts.map +1 -0
  15. package/dist/features/picker/composition/PickerField.js +21 -0
  16. package/dist/features/picker/composition/PickerField.js.map +1 -0
  17. package/dist/features/picker/composition/PickerHeader.d.ts +9 -0
  18. package/dist/features/picker/composition/PickerHeader.d.ts.map +1 -0
  19. package/dist/features/picker/composition/PickerHeader.js +14 -0
  20. package/dist/features/picker/composition/PickerHeader.js.map +1 -0
  21. package/dist/features/tabletop/meta.d.ts +46 -46
  22. package/dist/features/tabletop/meta.d.ts.map +1 -1
  23. package/dist/features/tabletop/meta.js +33 -58
  24. package/dist/features/tabletop/meta.js.map +1 -1
  25. package/dist/features/time-picker/composition/TimePickerContent.js +1 -12
  26. package/dist/features/time-picker/composition/TimePickerContent.js.map +1 -1
  27. package/dist/features/time-picker/composition/TimePickerField.d.ts.map +1 -1
  28. package/dist/features/time-picker/composition/TimePickerField.js +2 -17
  29. package/dist/features/time-picker/composition/TimePickerField.js.map +1 -1
  30. package/dist/features/typography/utils/headingThemeRecipeMeta.d.ts +5 -5
  31. package/dist/features/typography/utils/headingThemeRecipeMeta.d.ts.map +1 -1
  32. package/dist/features/typography/utils/headingThemeRecipeMeta.js +2 -19
  33. package/dist/features/typography/utils/headingThemeRecipeMeta.js.map +1 -1
  34. package/dist/features/typography/utils/resolveHeadingRecipe.d.ts.map +1 -1
  35. package/dist/features/typography/utils/resolveHeadingRecipe.js +11 -68
  36. package/dist/features/typography/utils/resolveHeadingRecipe.js.map +1 -1
  37. package/dist/features/typography/utils/resolveHeadingThemeRecipe.d.ts +4 -4
  38. package/dist/features/typography/utils/resolveHeadingThemeRecipe.d.ts.map +1 -1
  39. package/dist/features/typography/utils/resolveHeadingThemeRecipe.js +2 -33
  40. package/dist/features/typography/utils/resolveHeadingThemeRecipe.js.map +1 -1
  41. package/dist/features/typography/utils/resolveTextStyle.d.ts.map +1 -1
  42. package/dist/features/typography/utils/resolveTextStyle.js +11 -76
  43. package/dist/features/typography/utils/resolveTextStyle.js.map +1 -1
  44. package/dist/features/typography/utils/resolveTextThemeRecipe.d.ts +4 -4
  45. package/dist/features/typography/utils/resolveTextThemeRecipe.d.ts.map +1 -1
  46. package/dist/features/typography/utils/resolveTextThemeRecipe.js +2 -33
  47. package/dist/features/typography/utils/resolveTextThemeRecipe.js.map +1 -1
  48. package/dist/features/typography/utils/resolveTypographyStyleTokens.d.ts +21 -0
  49. package/dist/features/typography/utils/resolveTypographyStyleTokens.d.ts.map +1 -0
  50. package/dist/features/typography/utils/resolveTypographyStyleTokens.js +75 -0
  51. package/dist/features/typography/utils/resolveTypographyStyleTokens.js.map +1 -0
  52. package/dist/features/typography/utils/resolveTypographyThemeRecipeFields.d.ts +10 -0
  53. package/dist/features/typography/utils/resolveTypographyThemeRecipeFields.d.ts.map +1 -0
  54. package/dist/features/typography/utils/resolveTypographyThemeRecipeFields.js +19 -0
  55. package/dist/features/typography/utils/resolveTypographyThemeRecipeFields.js.map +1 -0
  56. package/dist/features/typography/utils/textThemeRecipeMeta.d.ts +6 -6
  57. package/dist/features/typography/utils/textThemeRecipeMeta.d.ts.map +1 -1
  58. package/dist/features/typography/utils/textThemeRecipeMeta.js +2 -19
  59. package/dist/features/typography/utils/textThemeRecipeMeta.js.map +1 -1
  60. package/dist/features/typography/utils/typographyThemeRecipeFields.d.ts +30 -0
  61. package/dist/features/typography/utils/typographyThemeRecipeFields.d.ts.map +1 -0
  62. package/dist/features/typography/utils/typographyThemeRecipeFields.js +23 -0
  63. package/dist/features/typography/utils/typographyThemeRecipeFields.js.map +1 -0
  64. package/dist/features/uploader/adapters/inbound/Uploader.d.ts.map +1 -1
  65. package/dist/features/uploader/adapters/inbound/Uploader.js +1 -16
  66. package/dist/features/uploader/adapters/inbound/Uploader.js.map +1 -1
  67. package/dist/features/uploader/application/use-cases/validateUploadAsset.d.ts.map +1 -1
  68. package/dist/features/uploader/application/use-cases/validateUploadAsset.js +1 -16
  69. package/dist/features/uploader/application/use-cases/validateUploadAsset.js.map +1 -1
  70. package/dist/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.d.ts.map +1 -1
  71. package/dist/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.js +3 -4
  72. package/dist/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.js.map +1 -1
  73. package/dist/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.d.ts +8 -0
  74. package/dist/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.d.ts.map +1 -0
  75. package/dist/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.js +10 -0
  76. package/dist/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.js.map +1 -0
  77. package/dist/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.d.ts.map +1 -1
  78. package/dist/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.js +4 -6
  79. package/dist/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.js.map +1 -1
  80. package/package.json +14 -4
  81. package/src/features/date-picker/composition/DatePickerContent.tsx +1 -22
  82. package/src/features/date-picker/composition/DatePickerField.tsx +2 -31
  83. package/src/features/matrix-grid/application/getMatrixGridNextFocusId.ts +11 -22
  84. package/src/features/picker/composition/PickerField.tsx +61 -0
  85. package/src/features/picker/composition/PickerHeader.tsx +25 -0
  86. package/src/features/tabletop/meta.ts +34 -58
  87. package/src/features/time-picker/composition/TimePickerContent.tsx +1 -23
  88. package/src/features/time-picker/composition/TimePickerField.tsx +2 -31
  89. package/src/features/typography/utils/headingThemeRecipeMeta.ts +2 -19
  90. package/src/features/typography/utils/resolveHeadingRecipe.ts +12 -88
  91. package/src/features/typography/utils/resolveHeadingThemeRecipe.ts +3 -39
  92. package/src/features/typography/utils/resolveTextStyle.ts +10 -98
  93. package/src/features/typography/utils/resolveTextThemeRecipe.ts +3 -39
  94. package/src/features/typography/utils/resolveTypographyStyleTokens.ts +113 -0
  95. package/src/features/typography/utils/resolveTypographyThemeRecipeFields.ts +27 -0
  96. package/src/features/typography/utils/textThemeRecipeMeta.ts +2 -19
  97. package/src/features/typography/utils/typographyThemeRecipeFields.ts +23 -0
  98. package/src/features/uploader/adapters/inbound/Uploader.tsx +1 -20
  99. package/src/features/uploader/application/use-cases/validateUploadAsset.test.ts +2 -2
  100. package/src/features/uploader/application/use-cases/validateUploadAsset.ts +2 -20
  101. package/src/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.ts +3 -6
  102. package/src/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.ts +18 -0
  103. package/src/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.ts +4 -8
  104. package/src/webPackageExports.test.ts +101 -0
  105. package/web-dist/chunks/{Accordion-rqr10gn4.js → Accordion-1495be5z.js} +2 -2
  106. package/web-dist/chunks/{Accordion-m2hcc8g4.js → Accordion-3qphhsb4.js} +1 -1
  107. package/web-dist/chunks/{Accordion-gn8ptzwb.js → Accordion-4x8gdqqs.js} +1 -1
  108. package/web-dist/chunks/{Accordion-9aczb0k2.js → Accordion-58bpmzdj.js} +2 -2
  109. package/web-dist/chunks/{Accordion-2jy53kzk.js → Accordion-69c4pfz2.js} +2 -2
  110. package/web-dist/chunks/{Accordion-rqhh2m5b.js → Accordion-7n93d7fa.js} +1 -1
  111. package/web-dist/chunks/{Accordion-m6mp8aea.js → Accordion-a42brn0p.js} +1 -1
  112. package/web-dist/chunks/{Accordion-g08740h4.js → Accordion-c912kzs0.js} +1 -1
  113. package/web-dist/chunks/{Accordion-tyh8jj9d.js → Accordion-cbbafww1.js} +1 -1
  114. package/web-dist/chunks/{Accordion-11ej93t5.js → Accordion-cjjg81ry.js} +2 -2
  115. package/web-dist/chunks/{Accordion-vkd002gw.js → Accordion-d0bebw9j.js} +3 -3
  116. package/web-dist/chunks/{Accordion-vjcd5kxc.js → Accordion-gntmfcmz.js} +1 -1
  117. package/web-dist/chunks/{Accordion-wqyrrptg.js → Accordion-jyemxzat.js} +1 -1
  118. package/web-dist/chunks/{Accordion-p7d5svfq.js → Accordion-kmwhjw1k.js} +1 -1
  119. package/web-dist/chunks/{Accordion-26prtnm7.js → Accordion-ptxa6trg.js} +1 -1
  120. package/web-dist/chunks/{Accordion-4yq92mxp.js → Accordion-ve81xazy.js} +1 -1
  121. package/web-dist/chunks/Accordion-vnb3vntm.js +79 -0
  122. package/web-dist/chunks/{Accordion-9rnsg9pk.js → Accordion-vqx3exsq.js} +129 -243
  123. package/web-dist/chunks/{Accordion-1hbt9k69.js → Accordion-w95t2pr3.js} +1 -1
  124. package/web-dist/chunks/{Accordion-g2aqhct0.js → Accordion-z5dpzw3g.js} +2 -2
  125. package/web-dist/chunks/{Accordion-a4pz3vjj.js → Accordion-zf1g5422.js} +1 -1
  126. package/web-dist/components/accordion/Accordion.js +2 -2
  127. package/web-dist/components/accordion-item/AccordionItem.js +2 -2
  128. package/web-dist/components/app-bar/AppBar.js +2 -2
  129. package/web-dist/components/app-header/AppHeader.js +2 -2
  130. package/web-dist/components/auth-screen/AuthScreen.js +4 -4
  131. package/web-dist/components/avatar/Avatar.js +2 -2
  132. package/web-dist/components/avatar-group/AvatarGroup.js +2 -2
  133. package/web-dist/components/barcode-scanner-view/BarcodeScannerView.js +4 -4
  134. package/web-dist/components/breadcrumbs/Breadcrumbs.js +1 -1
  135. package/web-dist/components/camera-permission-view/CameraPermissionView.js +4 -4
  136. package/web-dist/components/card/Card.js +3 -3
  137. package/web-dist/components/chat-list-item/ChatListItem.js +3 -3
  138. package/web-dist/components/checkbox/Checkbox.js +2 -2
  139. package/web-dist/components/checkbox-group/CheckboxGroup.js +2 -2
  140. package/web-dist/components/chip/Chip.js +2 -2
  141. package/web-dist/components/chip-group/ChipGroup.js +2 -2
  142. package/web-dist/components/collection-editor/CollectionEditor.js +3 -3
  143. package/web-dist/components/content-rail/ContentRail.js +1 -1
  144. package/web-dist/components/data-table/DataTable.js +7 -7
  145. package/web-dist/components/date-picker/DatePicker.js +12 -49
  146. package/web-dist/components/dialog/Dialog.js +2 -2
  147. package/web-dist/components/empty-state/EmptyState.js +4 -4
  148. package/web-dist/components/explorer/Explorer.js +2 -2
  149. package/web-dist/components/field/Field.js +2 -2
  150. package/web-dist/components/file-explorer/FileExplorer.js +2 -2
  151. package/web-dist/components/flat-list/FlatList.js +3 -3
  152. package/web-dist/components/forgot-password-form/ForgotPasswordForm.js +4 -4
  153. package/web-dist/components/form/Form.js +3 -3
  154. package/web-dist/components/form-actions/FormActions.js +3 -3
  155. package/web-dist/components/form-error/FormError.js +3 -3
  156. package/web-dist/components/heading/Heading.js +1 -1
  157. package/web-dist/components/hero/Hero.js +3 -3
  158. package/web-dist/components/list/List.js +3 -3
  159. package/web-dist/components/list-item/ListItem.js +3 -3
  160. package/web-dist/components/list-section/ListSection.js +3 -3
  161. package/web-dist/components/matrix-grid/MatrixGrid.js +5 -17
  162. package/web-dist/components/media-card/MediaCard.js +3 -3
  163. package/web-dist/components/media-explorer/MediaExplorer.js +2 -2
  164. package/web-dist/components/message-bubble/MessageBubble.js +3 -3
  165. package/web-dist/components/metric-card/MetricCard.js +3 -3
  166. package/web-dist/components/missing-element/MissingElement.js +1 -1
  167. package/web-dist/components/o-auth-provider-button/OAuthProviderButton.js +4 -4
  168. package/web-dist/components/o-auth-provider-list/OAuthProviderList.js +4 -4
  169. package/web-dist/components/otp-form/OtpForm.js +4 -4
  170. package/web-dist/components/pagination/Pagination.js +1 -1
  171. package/web-dist/components/palette-item/PaletteItem.js +3 -3
  172. package/web-dist/components/post-card/PostCard.js +3 -3
  173. package/web-dist/components/product-card/ProductCard.js +3 -3
  174. package/web-dist/components/progress/Progress.js +2 -2
  175. package/web-dist/components/progress-ring/ProgressRing.js +2 -2
  176. package/web-dist/components/radio/Radio.js +2 -2
  177. package/web-dist/components/radio-group/RadioGroup.js +2 -2
  178. package/web-dist/components/reader/Reader.js +4 -4
  179. package/web-dist/components/scan-overlay/ScanOverlay.js +4 -4
  180. package/web-dist/components/screen-section/ScreenSection.js +2 -2
  181. package/web-dist/components/section-header/SectionHeader.js +2 -2
  182. package/web-dist/components/section-list/SectionList.js +3 -3
  183. package/web-dist/components/sign-in-form/SignInForm.js +4 -4
  184. package/web-dist/components/sign-up-form/SignUpForm.js +4 -4
  185. package/web-dist/components/skeleton/Skeleton.js +6 -6
  186. package/web-dist/components/skeleton-card/SkeletonCard.js +6 -6
  187. package/web-dist/components/skeleton-list/SkeletonList.js +6 -6
  188. package/web-dist/components/skeleton-text/SkeletonText.js +6 -6
  189. package/web-dist/components/text/Text.js +1 -1
  190. package/web-dist/components/time-picker/TimePicker.js +12 -51
  191. package/web-dist/components/timeline/Timeline.js +1 -1
  192. package/web-dist/components/toolbar/Toolbar.js +3 -3
  193. package/web-dist/components/tree-item/TreeItem.js +3 -3
  194. package/web-dist/components/uploader/Uploader.js +21 -37
  195. package/web-dist/manifest.json +204 -202
@@ -1,9 +1,7 @@
1
- import { Field } from '@ankhorage/surface';
2
1
  import type React from 'react';
3
2
 
4
3
  import type { TimePickerProps } from '../../../types/time-picker';
5
- import { View } from '../../layout/public';
6
- import { Text } from '../../typography/public';
4
+ import { PickerField } from '../../picker/composition/PickerField';
7
5
 
8
6
  /*** Renders the shared TimePicker field frame around a platform-specific trigger. */
9
7
  export function TimePickerField({
@@ -15,18 +13,7 @@ export function TimePickerField({
15
13
  required,
16
14
  testID,
17
15
  }: TimePickerFieldProps) {
18
- return (
19
- <Field
20
- disabled={disabled}
21
- errorText={error}
22
- invalid={Boolean(error)}
23
- label={label ? renderLabel(label, description) : undefined}
24
- required={required}
25
- testID={testID}
26
- >
27
- {children}
28
- </Field>
29
- );
16
+ return <PickerField {...{ children, description, disabled, error, label, required, testID }} />;
30
17
  }
31
18
 
32
19
  interface TimePickerFieldProps extends Pick<
@@ -35,19 +22,3 @@ interface TimePickerFieldProps extends Pick<
35
22
  > {
36
23
  children: React.ReactNode;
37
24
  }
38
-
39
- /*** Renders the picker label and optional supporting description. */
40
- function renderLabel(label: React.ReactNode, description: React.ReactNode | undefined) {
41
- return (
42
- <View gap="xs">
43
- <Text variant="label" weight="semiBold">
44
- {label}
45
- </Text>
46
- {description ? (
47
- <Text emphasis="muted" variant="bodySmall">
48
- {description}
49
- </Text>
50
- ) : null}
51
- </View>
52
- );
53
- }
@@ -1,5 +1,5 @@
1
1
  import type { ZoraThemeRecipeMeta } from '../../../types/theme-recipe';
2
- import { ZORA_COLORS, ZORA_EMPHASES } from '../../theme/colorModel';
2
+ import { typographyThemeRecipeFields } from './typographyThemeRecipeFields';
3
3
 
4
4
  export const headingThemeRecipeMeta = {
5
5
  name: 'Heading',
@@ -11,23 +11,6 @@ export const headingThemeRecipeMeta = {
11
11
  label: 'Size',
12
12
  options: ['display', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
13
13
  },
14
- color: { type: 'choice', label: 'Color', options: ZORA_COLORS },
15
- emphasis: {
16
- type: 'choice',
17
- label: 'Emphasis',
18
- options: ZORA_EMPHASES,
19
- default: 'default',
20
- },
21
- align: {
22
- type: 'choice',
23
- label: 'Align',
24
- options: ['auto', 'left', 'right', 'center', 'justify'],
25
- },
26
- weight: {
27
- type: 'choice',
28
- label: 'Weight',
29
- options: ['regular', 'medium', 'semiBold', 'bold'],
30
- },
31
- italic: { type: 'boolean', label: 'Italic', default: false },
14
+ ...typographyThemeRecipeFields,
32
15
  },
33
16
  } as const satisfies ZoraThemeRecipeMeta;
@@ -1,4 +1,4 @@
1
- import type { FontWeight, RoleSemantics, SurfaceTheme } from '@ankhorage/surface';
1
+ import type { SurfaceTheme } from '@ankhorage/surface';
2
2
  import type { TextStyle } from 'react-native';
3
3
 
4
4
  import type {
@@ -10,6 +10,7 @@ import type {
10
10
  HeadingWeight,
11
11
  } from '../../../types/heading';
12
12
  import { resolveHeadingSizeFromLevel } from './resolveHeadingSizeFromLevel';
13
+ import { resolveTypographyStyleTokens } from './resolveTypographyStyleTokens';
13
14
  export function resolveHeadingRecipe(
14
15
  theme: SurfaceTheme,
15
16
  {
@@ -23,15 +24,21 @@ export function resolveHeadingRecipe(
23
24
  }: ResolveHeadingRecipeOptions,
24
25
  ): TextStyle {
25
26
  const recipe = resolveSizeRecipe(theme, size ?? resolveHeadingSizeFromLevel(level));
26
- const resolvedWeight = resolveWeight(theme, weight ?? recipe.weight);
27
+ const tokens = resolveTypographyStyleTokens({
28
+ theme,
29
+ color,
30
+ emphasis,
31
+ weight: weight ?? recipe.weight,
32
+ italic,
33
+ });
27
34
 
28
35
  return {
29
- color: resolveHeadingColor(theme, emphasis, color),
36
+ color: tokens.color,
30
37
  elevation: 0,
31
- fontFamily: resolveFontFamily({ theme, weight: resolvedWeight, italic }),
38
+ fontFamily: tokens.fontFamily,
32
39
  fontSize: recipe.fontSize,
33
40
  fontStyle: italic ? 'italic' : 'normal',
34
- fontWeight: resolvedWeight,
41
+ fontWeight: tokens.fontWeight,
35
42
  lineHeight: recipe.lineHeight,
36
43
  textAlign: align,
37
44
  };
@@ -89,86 +96,3 @@ function resolveSizeRecipe(theme: SurfaceTheme, size: HeadingSize): HeadingRecip
89
96
  weight: heading.weight,
90
97
  };
91
98
  }
92
-
93
- function resolveRoleSemantics(theme: SurfaceTheme, color: HeadingColor): RoleSemantics {
94
- switch (color) {
95
- case 'secondary':
96
- return theme.semantics.secondary;
97
- case 'tertiary':
98
- return theme.semantics.accent;
99
- case 'quaternary':
100
- return theme.semantics.highlight;
101
- case 'neutral':
102
- return theme.semantics.action.neutral;
103
- case 'error':
104
- return theme.semantics.error;
105
- case 'info':
106
- return theme.semantics.info;
107
- case 'danger':
108
- return theme.semantics.action.danger;
109
- case 'success':
110
- return theme.semantics.success;
111
- case 'warning':
112
- return theme.semantics.warning;
113
- case 'primary':
114
- default:
115
- return theme.semantics.action.primary;
116
- }
117
- }
118
-
119
- function resolveHeadingColor(
120
- theme: SurfaceTheme,
121
- emphasis: HeadingEmphasis,
122
- color?: HeadingColor,
123
- ): string {
124
- if (color) {
125
- const role = resolveRoleSemantics(theme, color);
126
- return emphasis === 'inverse' ? role.onSolidText : role.base;
127
- }
128
-
129
- switch (emphasis) {
130
- case 'muted':
131
- return theme.semantics.content.muted;
132
- case 'subtle':
133
- return theme.semantics.content.subtle;
134
- case 'inverse':
135
- return theme.semantics.content.inverse;
136
- case 'default':
137
- default:
138
- return theme.semantics.content.default;
139
- }
140
- }
141
-
142
- function resolveWeight(theme: SurfaceTheme, weight: HeadingWeight): FontWeight {
143
- switch (weight) {
144
- case 'medium':
145
- return theme.typography.weights.medium;
146
- case 'semiBold':
147
- return theme.typography.weights.semiBold;
148
- case 'bold':
149
- return theme.typography.weights.bold;
150
- case 'regular':
151
- default:
152
- return theme.typography.weights.regular;
153
- }
154
- }
155
-
156
- function resolveFontFamily({
157
- theme,
158
- weight,
159
- italic,
160
- }: {
161
- theme: SurfaceTheme;
162
- weight: FontWeight;
163
- italic: boolean;
164
- }): string | undefined {
165
- const fonts = italic ? theme.typography.fonts.italic : theme.typography.fonts.normal;
166
-
167
- for (const [fontWeight, fontFamily] of Object.entries(fonts)) {
168
- if (fontWeight === weight) {
169
- return fontFamily;
170
- }
171
- }
172
-
173
- return undefined;
174
- }
@@ -1,23 +1,14 @@
1
1
  import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
2
2
 
3
- import type {
4
- HeadingAlign,
5
- HeadingColor,
6
- HeadingEmphasis,
7
- HeadingSize,
8
- HeadingWeight,
9
- } from '../../../types/heading';
3
+ import type { HeadingSize } from '../../../types/heading';
4
+ import { resolveTypographyThemeRecipeFields } from './resolveTypographyThemeRecipeFields';
10
5
 
11
6
  export function resolveHeadingThemeRecipe(
12
7
  fields: Readonly<Record<string, ThemeRecipeOverrideValue>>,
13
8
  ) {
14
9
  return {
15
10
  size: readSize(fields.size),
16
- color: readColor(fields.color),
17
- emphasis: readEmphasis(fields.emphasis),
18
- align: readAlign(fields.align),
19
- weight: readWeight(fields.weight),
20
- italic: typeof fields.italic === 'boolean' ? fields.italic : undefined,
11
+ ...resolveTypographyThemeRecipeFields(fields),
21
12
  };
22
13
  }
23
14
 
@@ -26,30 +17,3 @@ function readSize(value: ThemeRecipeOverrideValue | undefined): HeadingSize | un
26
17
  if (value === 'h4' || value === 'h5' || value === 'h6') return value;
27
18
  return undefined;
28
19
  }
29
-
30
- function readColor(value: ThemeRecipeOverrideValue | undefined): HeadingColor | undefined {
31
- if (typeof value !== 'string') return undefined;
32
- if (value === 'primary' || value === 'secondary' || value === 'tertiary') return value;
33
- if (value === 'quaternary' || value === 'neutral' || value === 'danger') return value;
34
- if (value === 'success' || value === 'warning' || value === 'error' || value === 'info')
35
- return value;
36
- return undefined;
37
- }
38
-
39
- function readEmphasis(value: ThemeRecipeOverrideValue | undefined): HeadingEmphasis | undefined {
40
- if (value === 'default' || value === 'muted' || value === 'subtle' || value === 'inverse')
41
- return value;
42
- return undefined;
43
- }
44
-
45
- function readAlign(value: ThemeRecipeOverrideValue | undefined): HeadingAlign | undefined {
46
- if (value === 'auto' || value === 'left' || value === 'right') return value;
47
- if (value === 'center' || value === 'justify') return value;
48
- return undefined;
49
- }
50
-
51
- function readWeight(value: ThemeRecipeOverrideValue | undefined): HeadingWeight | undefined {
52
- if (value === 'regular' || value === 'medium' || value === 'semiBold' || value === 'bold')
53
- return value;
54
- return undefined;
55
- }
@@ -1,9 +1,7 @@
1
1
  import {
2
2
  type Breakpoint,
3
- type FontWeight,
4
3
  resolveResponsive,
5
4
  type Responsive,
6
- type RoleSemantics,
7
5
  type SurfaceTheme,
8
6
  } from '@ankhorage/surface';
9
7
  import type { TextStyle } from 'react-native';
@@ -15,6 +13,7 @@ import type {
15
13
  TextVariant,
16
14
  TextWeight,
17
15
  } from '../../../types/text';
16
+ import { resolveTypographyStyleTokens } from './resolveTypographyStyleTokens';
18
17
  /*** Resolves responsive text variants and theme semantics into a native text style. */
19
18
  export function resolveTextStyle({
20
19
  theme,
@@ -31,23 +30,21 @@ export function resolveTextStyle({
31
30
  const resolvedColor = resolveResponsive(color, breakpoint);
32
31
  const resolvedAlign = resolveResponsive(align, breakpoint);
33
32
  const recipe = resolveVariantRecipe(theme, resolvedVariant, breakpoint);
34
- const resolvedWeight = resolveWeight(
33
+ const tokens = resolveTypographyStyleTokens({
35
34
  theme,
36
- resolveResponsive(weight, breakpoint) ?? recipe.weight,
37
- );
35
+ color: resolvedColor,
36
+ emphasis: resolvedEmphasis,
37
+ weight: resolveResponsive(weight, breakpoint) ?? recipe.weight,
38
+ italic,
39
+ });
38
40
 
39
41
  return {
40
- color: resolveTextColor(theme, resolvedEmphasis, resolvedColor),
42
+ color: tokens.color,
41
43
  elevation: 0,
42
- fontFamily: resolveFontFamily({
43
- theme,
44
- variant: resolvedVariant,
45
- weight: resolvedWeight,
46
- italic,
47
- }),
44
+ fontFamily: resolvedVariant === 'code' ? 'monospace' : tokens.fontFamily,
48
45
  fontSize: recipe.fontSize,
49
46
  fontStyle: italic ? 'italic' : 'normal',
50
- fontWeight: resolvedWeight,
47
+ fontWeight: tokens.fontWeight,
51
48
  letterSpacing: recipe.letterSpacing,
52
49
  lineHeight: recipe.lineHeight,
53
50
  textAlign: resolvedAlign,
@@ -78,46 +75,6 @@ function isMediumBreakpointOrLarger(breakpoint: Breakpoint): boolean {
78
75
  return breakpoint === 'md' || breakpoint === 'lg' || breakpoint === 'xl';
79
76
  }
80
77
 
81
- function resolveWeight(theme: SurfaceTheme, weight: TextWeight): FontWeight {
82
- switch (weight) {
83
- case 'medium':
84
- return theme.typography.weights.medium;
85
- case 'semiBold':
86
- return theme.typography.weights.semiBold;
87
- case 'bold':
88
- return theme.typography.weights.bold;
89
- case 'regular':
90
- default:
91
- return theme.typography.weights.regular;
92
- }
93
- }
94
-
95
- function resolveFontFamily({
96
- theme,
97
- variant,
98
- weight,
99
- italic,
100
- }: {
101
- theme: SurfaceTheme;
102
- variant: TextVariant;
103
- weight: FontWeight;
104
- italic: boolean;
105
- }): string | undefined {
106
- if (variant === 'code') {
107
- return 'monospace';
108
- }
109
-
110
- const fonts = italic ? theme.typography.fonts.italic : theme.typography.fonts.normal;
111
-
112
- for (const [fontWeight, fontFamily] of Object.entries(fonts)) {
113
- if (fontWeight === weight) {
114
- return fontFamily;
115
- }
116
- }
117
-
118
- return undefined;
119
- }
120
-
121
78
  function resolveVariantRecipe(
122
79
  theme: SurfaceTheme,
123
80
  variant: TextVariant,
@@ -173,48 +130,3 @@ function resolveVariantRecipe(
173
130
  };
174
131
  }
175
132
  }
176
-
177
- function resolveRoleSemantics(theme: SurfaceTheme, color: TextColor): RoleSemantics {
178
- switch (color) {
179
- case 'secondary':
180
- return theme.semantics.secondary;
181
- case 'tertiary':
182
- return theme.semantics.accent;
183
- case 'quaternary':
184
- return theme.semantics.highlight;
185
- case 'neutral':
186
- return theme.semantics.action.neutral;
187
- case 'error':
188
- return theme.semantics.error;
189
- case 'info':
190
- return theme.semantics.info;
191
- case 'danger':
192
- return theme.semantics.action.danger;
193
- case 'success':
194
- return theme.semantics.success;
195
- case 'warning':
196
- return theme.semantics.warning;
197
- case 'primary':
198
- default:
199
- return theme.semantics.action.primary;
200
- }
201
- }
202
-
203
- function resolveTextColor(theme: SurfaceTheme, emphasis: TextEmphasis, color?: TextColor): string {
204
- if (color) {
205
- const role = resolveRoleSemantics(theme, color);
206
- return emphasis === 'inverse' ? role.onSolidText : role.base;
207
- }
208
-
209
- switch (emphasis) {
210
- case 'muted':
211
- return theme.semantics.content.muted;
212
- case 'subtle':
213
- return theme.semantics.content.subtle;
214
- case 'inverse':
215
- return theme.semantics.content.inverse;
216
- case 'default':
217
- default:
218
- return theme.semantics.content.default;
219
- }
220
- }
@@ -1,21 +1,12 @@
1
1
  import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
2
2
 
3
- import type {
4
- TextAlign,
5
- TextColor,
6
- TextEmphasis,
7
- TextVariant,
8
- TextWeight,
9
- } from '../../../types/text';
3
+ import type { TextVariant } from '../../../types/text';
4
+ import { resolveTypographyThemeRecipeFields } from './resolveTypographyThemeRecipeFields';
10
5
 
11
6
  export function resolveTextThemeRecipe(fields: Readonly<Record<string, ThemeRecipeOverrideValue>>) {
12
7
  return {
13
8
  variant: readVariant(fields.variant),
14
- color: readColor(fields.color),
15
- emphasis: readEmphasis(fields.emphasis),
16
- align: readAlign(fields.align),
17
- weight: readWeight(fields.weight),
18
- italic: typeof fields.italic === 'boolean' ? fields.italic : undefined,
9
+ ...resolveTypographyThemeRecipeFields(fields),
19
10
  };
20
11
  }
21
12
 
@@ -25,30 +16,3 @@ function readVariant(value: ThemeRecipeOverrideValue | undefined): TextVariant |
25
16
  if (value === 'label' || value === 'eyebrow' || value === 'code') return value;
26
17
  return undefined;
27
18
  }
28
-
29
- function readColor(value: ThemeRecipeOverrideValue | undefined): TextColor | undefined {
30
- if (typeof value !== 'string') return undefined;
31
- if (value === 'primary' || value === 'secondary' || value === 'tertiary') return value;
32
- if (value === 'quaternary' || value === 'neutral' || value === 'danger') return value;
33
- if (value === 'success' || value === 'warning' || value === 'error' || value === 'info')
34
- return value;
35
- return undefined;
36
- }
37
-
38
- function readEmphasis(value: ThemeRecipeOverrideValue | undefined): TextEmphasis | undefined {
39
- if (value === 'default' || value === 'muted' || value === 'subtle' || value === 'inverse')
40
- return value;
41
- return undefined;
42
- }
43
-
44
- function readAlign(value: ThemeRecipeOverrideValue | undefined): TextAlign | undefined {
45
- if (value === 'auto' || value === 'left' || value === 'right') return value;
46
- if (value === 'center' || value === 'justify') return value;
47
- return undefined;
48
- }
49
-
50
- function readWeight(value: ThemeRecipeOverrideValue | undefined): TextWeight | undefined {
51
- if (value === 'regular' || value === 'medium' || value === 'semiBold' || value === 'bold')
52
- return value;
53
- return undefined;
54
- }
@@ -0,0 +1,113 @@
1
+ import type { FontWeight, RoleSemantics, SurfaceTheme } from '@ankhorage/surface';
2
+
3
+ import type { ZoraColor, ZoraEmphasis } from '../../../types/theme';
4
+
5
+ /*** Resolves the Surface-derived style tokens shared by Heading and Text. */
6
+ export function resolveTypographyStyleTokens({
7
+ theme,
8
+ color,
9
+ emphasis,
10
+ weight,
11
+ italic,
12
+ }: ResolveTypographyStyleTokensInput): ResolveTypographyStyleTokensOutput {
13
+ const role = color ? resolveRoleSemantics(theme, color) : undefined;
14
+ const fontWeight = resolveWeight(theme, weight);
15
+
16
+ return {
17
+ color: role
18
+ ? emphasis === 'inverse'
19
+ ? role.onSolidText
20
+ : role.base
21
+ : resolveEmphasisColor(theme, emphasis),
22
+ fontFamily: resolveFontFamily(theme, fontWeight, italic),
23
+ fontWeight,
24
+ };
25
+ }
26
+
27
+ interface ResolveTypographyStyleTokensInput {
28
+ readonly theme: SurfaceTheme;
29
+ readonly color: TypographyColor | undefined;
30
+ readonly emphasis: TypographyEmphasis;
31
+ readonly weight: TypographyWeight;
32
+ readonly italic: boolean;
33
+ }
34
+
35
+ interface ResolveTypographyStyleTokensOutput {
36
+ readonly color: string;
37
+ readonly fontFamily: string | undefined;
38
+ readonly fontWeight: FontWeight;
39
+ }
40
+
41
+ type TypographyColor = ZoraColor;
42
+ type TypographyEmphasis = ZoraEmphasis;
43
+ type TypographyWeight = 'regular' | 'medium' | 'semiBold' | 'bold';
44
+
45
+ function resolveRoleSemantics(theme: SurfaceTheme, color: TypographyColor): RoleSemantics {
46
+ switch (color) {
47
+ case 'secondary':
48
+ return theme.semantics.secondary;
49
+ case 'tertiary':
50
+ return theme.semantics.accent;
51
+ case 'quaternary':
52
+ return theme.semantics.highlight;
53
+ case 'neutral':
54
+ return theme.semantics.action.neutral;
55
+ case 'error':
56
+ return theme.semantics.error;
57
+ case 'info':
58
+ return theme.semantics.info;
59
+ case 'danger':
60
+ return theme.semantics.action.danger;
61
+ case 'success':
62
+ return theme.semantics.success;
63
+ case 'warning':
64
+ return theme.semantics.warning;
65
+ case 'primary':
66
+ default:
67
+ return theme.semantics.action.primary;
68
+ }
69
+ }
70
+
71
+ function resolveEmphasisColor(theme: SurfaceTheme, emphasis: TypographyEmphasis): string {
72
+ switch (emphasis) {
73
+ case 'muted':
74
+ return theme.semantics.content.muted;
75
+ case 'subtle':
76
+ return theme.semantics.content.subtle;
77
+ case 'inverse':
78
+ return theme.semantics.content.inverse;
79
+ case 'default':
80
+ default:
81
+ return theme.semantics.content.default;
82
+ }
83
+ }
84
+
85
+ function resolveWeight(theme: SurfaceTheme, weight: TypographyWeight): FontWeight {
86
+ switch (weight) {
87
+ case 'medium':
88
+ return theme.typography.weights.medium;
89
+ case 'semiBold':
90
+ return theme.typography.weights.semiBold;
91
+ case 'bold':
92
+ return theme.typography.weights.bold;
93
+ case 'regular':
94
+ default:
95
+ return theme.typography.weights.regular;
96
+ }
97
+ }
98
+
99
+ function resolveFontFamily(
100
+ theme: SurfaceTheme,
101
+ weight: FontWeight,
102
+ italic: boolean,
103
+ ): string | undefined {
104
+ const fonts = italic ? theme.typography.fonts.italic : theme.typography.fonts.normal;
105
+
106
+ for (const [fontWeight, fontFamily] of Object.entries(fonts)) {
107
+ if (fontWeight === weight) {
108
+ return fontFamily;
109
+ }
110
+ }
111
+
112
+ return undefined;
113
+ }
@@ -0,0 +1,27 @@
1
+ import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
2
+
3
+ import { ZORA_COLORS, ZORA_EMPHASES } from '../../theme/colorModel';
4
+
5
+ const ALIGNMENTS = ['auto', 'left', 'right', 'center', 'justify'] as const;
6
+ const WEIGHTS = ['regular', 'medium', 'semiBold', 'bold'] as const;
7
+
8
+ /*** Resolves the common scalar presentation fields in Typography theme recipes. */
9
+ export function resolveTypographyThemeRecipeFields(
10
+ fields: Readonly<Record<string, ThemeRecipeOverrideValue>>,
11
+ ) {
12
+ return {
13
+ color: readChoice(fields.color, ZORA_COLORS),
14
+ emphasis: readChoice(fields.emphasis, ZORA_EMPHASES),
15
+ align: readChoice(fields.align, ALIGNMENTS),
16
+ weight: readChoice(fields.weight, WEIGHTS),
17
+ italic: typeof fields.italic === 'boolean' ? fields.italic : undefined,
18
+ };
19
+ }
20
+
21
+ function readChoice<T extends string>(
22
+ value: ThemeRecipeOverrideValue | undefined,
23
+ choices: readonly T[],
24
+ ): T | undefined {
25
+ if (typeof value !== 'string') return undefined;
26
+ return choices.find((choice) => choice === value);
27
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ZoraThemeRecipeMeta } from '../../../types/theme-recipe';
2
- import { ZORA_COLORS, ZORA_EMPHASES } from '../../theme/colorModel';
2
+ import { typographyThemeRecipeFields } from './typographyThemeRecipeFields';
3
3
 
4
4
  export const textThemeRecipeMeta = {
5
5
  name: 'Text',
@@ -12,23 +12,6 @@ export const textThemeRecipeMeta = {
12
12
  options: ['body', 'lead', 'bodySmall', 'caption', 'label', 'eyebrow', 'code'],
13
13
  default: 'body',
14
14
  },
15
- color: { type: 'choice', label: 'Color', options: ZORA_COLORS },
16
- emphasis: {
17
- type: 'choice',
18
- label: 'Emphasis',
19
- options: ZORA_EMPHASES,
20
- default: 'default',
21
- },
22
- align: {
23
- type: 'choice',
24
- label: 'Align',
25
- options: ['auto', 'left', 'right', 'center', 'justify'],
26
- },
27
- weight: {
28
- type: 'choice',
29
- label: 'Weight',
30
- options: ['regular', 'medium', 'semiBold', 'bold'],
31
- },
32
- italic: { type: 'boolean', label: 'Italic', default: false },
15
+ ...typographyThemeRecipeFields,
33
16
  },
34
17
  } as const satisfies ZoraThemeRecipeMeta;
@@ -0,0 +1,23 @@
1
+ import { ZORA_COLORS, ZORA_EMPHASES } from '../../theme/colorModel';
2
+
3
+ /*** Defines the common presentation fields used by Typography theme recipes. */
4
+ export const typographyThemeRecipeFields = {
5
+ color: { type: 'choice', label: 'Color', options: ZORA_COLORS },
6
+ emphasis: {
7
+ type: 'choice',
8
+ label: 'Emphasis',
9
+ options: ZORA_EMPHASES,
10
+ default: 'default',
11
+ },
12
+ align: {
13
+ type: 'choice',
14
+ label: 'Align',
15
+ options: ['auto', 'left', 'right', 'center', 'justify'],
16
+ },
17
+ weight: {
18
+ type: 'choice',
19
+ label: 'Weight',
20
+ options: ['regular', 'medium', 'semiBold', 'bold'],
21
+ },
22
+ italic: { type: 'boolean', label: 'Italic', default: false },
23
+ } as const;
@@ -1,3 +1,4 @@
1
+ import { formatBytes } from '@ankhorage/utility/number';
1
2
  import React from 'react';
2
3
 
3
4
  import type { UploadAsset, UploaderProps, UploadType } from '../../../../types/upload';
@@ -445,23 +446,3 @@ function formatUnknownError(error: unknown): string {
445
446
 
446
447
  return 'Something went wrong.';
447
448
  }
448
-
449
- /*** Formats an upload byte limit for concise helper text. */
450
- function formatBytes(value: number): string {
451
- if (!Number.isFinite(value) || value <= 0) {
452
- return '0 B';
453
- }
454
-
455
- const units = ['B', 'KB', 'MB', 'GB'] as const;
456
- let size = value;
457
- let unitIndex = 0;
458
-
459
- while (size >= 1024 && unitIndex < units.length - 1) {
460
- size /= 1024;
461
- unitIndex += 1;
462
- }
463
-
464
- const unit = units.at(unitIndex) ?? 'B';
465
- const rounded = unitIndex === 0 ? Math.round(size) : Math.round(size * 10) / 10;
466
- return `${rounded} ${unit}`;
467
- }