@ankhorage/zora 2.11.0 → 2.13.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/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/dist/components/button/Button.d.ts.map +1 -1
- package/dist/components/button/Button.js +4 -2
- package/dist/components/button/Button.js.map +1 -1
- package/dist/components/button/meta.d.ts +3 -6
- package/dist/components/button/meta.d.ts.map +1 -1
- package/dist/components/button/meta.js +9 -15
- package/dist/components/button/meta.js.map +1 -1
- package/dist/components/button/resolveButtonThemeRecipe.d.ts +9 -0
- package/dist/components/button/resolveButtonThemeRecipe.d.ts.map +1 -0
- package/dist/components/button/resolveButtonThemeRecipe.js +30 -0
- package/dist/components/button/resolveButtonThemeRecipe.js.map +1 -0
- package/dist/components/button/themeRecipeMeta.d.ts +4 -10
- package/dist/components/button/themeRecipeMeta.d.ts.map +1 -1
- package/dist/components/button/themeRecipeMeta.js +5 -10
- package/dist/components/button/themeRecipeMeta.js.map +1 -1
- package/dist/components/card/Card.d.ts.map +1 -1
- package/dist/components/card/Card.js +8 -4
- package/dist/components/card/Card.js.map +1 -1
- package/dist/components/card/meta.d.ts +22 -3
- package/dist/components/card/meta.d.ts.map +1 -1
- package/dist/components/card/meta.js +14 -15
- package/dist/components/card/meta.js.map +1 -1
- package/dist/components/card/resolveCardThemeRecipe.d.ts +15 -0
- package/dist/components/card/resolveCardThemeRecipe.d.ts.map +1 -0
- package/dist/components/card/resolveCardThemeRecipe.js +22 -0
- package/dist/components/card/resolveCardThemeRecipe.js.map +1 -0
- package/dist/components/card/themeRecipeMeta.d.ts +1 -1
- package/dist/components/card/themeRecipeMeta.js +1 -1
- package/dist/components/card/themeRecipeMeta.js.map +1 -1
- package/dist/components/card/types.d.ts +2 -0
- package/dist/components/card/types.d.ts.map +1 -1
- package/dist/components/card/types.js.map +1 -1
- package/dist/components/heading/Heading.d.ts.map +1 -1
- package/dist/components/heading/Heading.js +14 -15
- package/dist/components/heading/Heading.js.map +1 -1
- package/dist/components/heading/meta.d.ts +6 -1
- package/dist/components/heading/meta.d.ts.map +1 -1
- package/dist/components/heading/meta.js +13 -22
- package/dist/components/heading/meta.js.map +1 -1
- package/dist/components/heading/resolveHeadingThemeRecipe.d.ts +11 -0
- package/dist/components/heading/resolveHeadingThemeRecipe.d.ts.map +1 -0
- package/dist/components/heading/resolveHeadingThemeRecipe.js +46 -0
- package/dist/components/heading/resolveHeadingThemeRecipe.js.map +1 -0
- package/dist/components/heading/themeRecipeMeta.d.ts +39 -0
- package/dist/components/heading/themeRecipeMeta.d.ts.map +1 -0
- package/dist/components/heading/themeRecipeMeta.js +32 -0
- package/dist/components/heading/themeRecipeMeta.js.map +1 -0
- package/dist/components/image/meta.d.ts +24 -3
- package/dist/components/image/meta.d.ts.map +1 -1
- package/dist/components/image/meta.js +18 -3
- package/dist/components/image/meta.js.map +1 -1
- package/dist/components/text/Text.d.ts +0 -1
- package/dist/components/text/Text.d.ts.map +1 -1
- package/dist/components/text/Text.js +14 -16
- package/dist/components/text/Text.js.map +1 -1
- package/dist/components/text/meta.d.ts +6 -5
- package/dist/components/text/meta.d.ts.map +1 -1
- package/dist/components/text/meta.js +13 -25
- package/dist/components/text/meta.js.map +1 -1
- package/dist/components/text/resolveTextThemeRecipe.d.ts +11 -0
- package/dist/components/text/resolveTextThemeRecipe.d.ts.map +1 -0
- package/dist/components/text/resolveTextThemeRecipe.js +46 -0
- package/dist/components/text/resolveTextThemeRecipe.js.map +1 -0
- package/dist/components/text/themeRecipeMeta.d.ts +15 -7
- package/dist/components/text/themeRecipeMeta.d.ts.map +1 -1
- package/dist/components/text/themeRecipeMeta.js +12 -11
- package/dist/components/text/themeRecipeMeta.js.map +1 -1
- package/dist/metadata/bindableComponentMeta.d.ts +22 -7
- package/dist/metadata/bindableComponentMeta.d.ts.map +1 -1
- package/dist/metadata/bindableComponentMeta.js +3 -13
- package/dist/metadata/bindableComponentMeta.js.map +1 -1
- package/dist/metadata/themeRecipeMeta.d.ts.map +1 -1
- package/dist/metadata/themeRecipeMeta.js +2 -0
- package/dist/metadata/themeRecipeMeta.js.map +1 -1
- package/dist/metadata/themeRecipeTypes.d.ts +3 -3
- package/dist/metadata/themeRecipeTypes.d.ts.map +1 -1
- package/dist/metadata/themeRecipeTypes.js.map +1 -1
- package/dist/metadata/types.d.ts +4 -2
- package/dist/metadata/types.d.ts.map +1 -1
- package/dist/metadata/types.js.map +1 -1
- package/dist/patterns/panel/Panel.d.ts +0 -2
- package/dist/patterns/panel/Panel.d.ts.map +1 -1
- package/dist/patterns/panel/Panel.js +6 -4
- package/dist/patterns/panel/Panel.js.map +1 -1
- package/dist/patterns/panel/meta.d.ts +22 -3
- package/dist/patterns/panel/meta.d.ts.map +1 -1
- package/dist/patterns/panel/meta.js +14 -15
- package/dist/patterns/panel/meta.js.map +1 -1
- package/dist/patterns/panel/themeRecipeMeta.d.ts +1 -1
- package/dist/patterns/panel/themeRecipeMeta.js +1 -1
- package/dist/patterns/panel/themeRecipeMeta.js.map +1 -1
- package/dist/patterns/panel/types.d.ts +2 -0
- package/dist/patterns/panel/types.d.ts.map +1 -1
- package/dist/patterns/panel/types.js.map +1 -1
- package/dist/theme/ZoraProvider.d.ts +4 -12
- package/dist/theme/ZoraProvider.d.ts.map +1 -1
- package/dist/theme/ZoraProvider.js +5 -14
- package/dist/theme/ZoraProvider.js.map +1 -1
- package/dist/theme/ZoraThemeRuntimeContext.d.ts +1 -2
- package/dist/theme/ZoraThemeRuntimeContext.d.ts.map +1 -1
- package/dist/theme/ZoraThemeRuntimeContext.js +0 -1
- package/dist/theme/ZoraThemeRuntimeContext.js.map +1 -1
- package/dist/theme/ZoraThemeScope.d.ts.map +1 -1
- package/dist/theme/ZoraThemeScope.js +4 -10
- package/dist/theme/ZoraThemeScope.js.map +1 -1
- package/dist/theme/resolveZoraThemeRecipe.d.ts +16 -0
- package/dist/theme/resolveZoraThemeRecipe.d.ts.map +1 -0
- package/dist/theme/resolveZoraThemeRecipe.js +56 -0
- package/dist/theme/resolveZoraThemeRecipe.js.map +1 -0
- package/dist/theme/useZoraTheme.d.ts +1 -1
- package/dist/theme/useZoraThemeRecipe.d.ts +2 -0
- package/dist/theme/useZoraThemeRecipe.d.ts.map +1 -0
- package/dist/theme/useZoraThemeRecipe.js +7 -0
- package/dist/theme/useZoraThemeRecipe.js.map +1 -0
- package/package.json +4 -4
- package/src/components/button/Button.tsx +4 -2
- package/src/components/button/meta.ts +10 -15
- package/src/components/button/resolveButtonThemeRecipe.test.ts +25 -0
- package/src/components/button/resolveButtonThemeRecipe.ts +41 -0
- package/src/components/button/themeRecipeMeta.ts +5 -10
- package/src/components/card/Card.tsx +13 -7
- package/src/components/card/meta.ts +15 -15
- package/src/components/card/resolveCardThemeRecipe.test.ts +28 -0
- package/src/components/card/resolveCardThemeRecipe.ts +33 -0
- package/src/components/card/themeRecipeMeta.ts +1 -1
- package/src/components/card/types.ts +2 -0
- package/src/components/contentFallback.test.ts +4 -4
- package/src/components/heading/Heading.tsx +18 -28
- package/src/components/heading/meta.ts +14 -22
- package/src/components/heading/resolveHeadingThemeRecipe.ts +55 -0
- package/src/components/heading/themeRecipeMeta.ts +33 -0
- package/src/components/image/meta.ts +18 -3
- package/src/components/text/Text.tsx +16 -25
- package/src/components/text/meta.ts +14 -25
- package/src/components/text/resolveTextThemeRecipe.ts +48 -0
- package/src/components/text/themeRecipeMeta.ts +12 -11
- package/src/metadata/authoringMeta.test.ts +18 -44
- package/src/metadata/bindableComponentMeta.test.ts +13 -0
- package/src/metadata/bindableComponentMeta.ts +4 -13
- package/src/metadata/componentMeta.test.ts +14 -0
- package/src/metadata/themeRecipeMeta.test.ts +41 -39
- package/src/metadata/themeRecipeMeta.ts +2 -0
- package/src/metadata/themeRecipeTypes.ts +3 -3
- package/src/metadata/types.ts +11 -24
- package/src/patterns/panel/Panel.tsx +14 -4
- package/src/patterns/panel/meta.ts +15 -15
- package/src/patterns/panel/themeRecipeMeta.ts +1 -1
- package/src/patterns/panel/types.ts +2 -0
- package/src/theme/ZoraProvider.tsx +10 -13
- package/src/theme/ZoraThemeRuntimeContext.tsx +1 -3
- package/src/theme/ZoraThemeScope.tsx +5 -25
- package/src/theme/resolveZoraThemeRecipe.test.ts +72 -0
- package/src/theme/resolveZoraThemeRecipe.ts +91 -0
- package/src/theme/themeConfigScope.test.ts +25 -0
- package/src/theme/useZoraThemeRecipe.ts +7 -0
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { ZORA_COLORS, ZORA_EMPHASES } from '../../internal/colorModel';
|
|
2
2
|
import type { ZoraComponentMeta } from '../../metadata';
|
|
3
3
|
|
|
4
|
+
const themeAuthoring = {
|
|
5
|
+
authority: 'theme',
|
|
6
|
+
scope: 'component',
|
|
7
|
+
allowInstanceOverride: true,
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
4
10
|
export const headingMeta = {
|
|
5
11
|
name: 'Heading',
|
|
6
12
|
category: 'component',
|
|
7
13
|
directManifestNode: true,
|
|
8
14
|
allowedChildren: [],
|
|
9
|
-
blueprint: {
|
|
10
|
-
|
|
11
|
-
defaultProps: {
|
|
12
|
-
text: 'Heading',
|
|
13
|
-
level: 2,
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
i18n: {
|
|
17
|
-
fields: [{ keyProp: 'i18nKey', defaultTextProp: 'text' }],
|
|
18
|
-
},
|
|
15
|
+
blueprint: { label: 'Heading', defaultProps: { text: 'Heading', level: 2 } },
|
|
16
|
+
i18n: { fields: [{ keyProp: 'i18nKey', defaultTextProp: 'text' }] },
|
|
19
17
|
props: {
|
|
20
18
|
text: {
|
|
21
19
|
type: 'string',
|
|
@@ -43,43 +41,37 @@ export const headingMeta = {
|
|
|
43
41
|
category: 'Typography',
|
|
44
42
|
label: 'Size',
|
|
45
43
|
enum: ['display', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
|
46
|
-
authoring:
|
|
44
|
+
authoring: themeAuthoring,
|
|
47
45
|
},
|
|
48
46
|
color: {
|
|
49
47
|
type: 'enum',
|
|
50
48
|
category: 'Style',
|
|
51
49
|
label: 'Color',
|
|
52
50
|
enum: ZORA_COLORS,
|
|
53
|
-
authoring:
|
|
51
|
+
authoring: themeAuthoring,
|
|
54
52
|
},
|
|
55
53
|
emphasis: {
|
|
56
54
|
type: 'enum',
|
|
57
55
|
category: 'Style',
|
|
58
56
|
label: 'Emphasis',
|
|
59
57
|
enum: ZORA_EMPHASES,
|
|
60
|
-
authoring:
|
|
58
|
+
authoring: themeAuthoring,
|
|
61
59
|
},
|
|
62
60
|
align: {
|
|
63
61
|
type: 'enum',
|
|
64
62
|
category: 'Layout',
|
|
65
63
|
label: 'Align',
|
|
66
64
|
enum: ['auto', 'left', 'right', 'center', 'justify'],
|
|
67
|
-
authoring:
|
|
65
|
+
authoring: themeAuthoring,
|
|
68
66
|
},
|
|
69
67
|
weight: {
|
|
70
68
|
type: 'enum',
|
|
71
69
|
category: 'Typography',
|
|
72
70
|
label: 'Weight',
|
|
73
71
|
enum: ['regular', 'medium', 'semiBold', 'bold'],
|
|
74
|
-
authoring:
|
|
75
|
-
},
|
|
76
|
-
italic: {
|
|
77
|
-
type: 'boolean',
|
|
78
|
-
category: 'Typography',
|
|
79
|
-
label: 'Italic',
|
|
80
|
-
default: false,
|
|
81
|
-
authoring: { authority: 'theme', scope: 'component' },
|
|
72
|
+
authoring: themeAuthoring,
|
|
82
73
|
},
|
|
74
|
+
italic: { type: 'boolean', category: 'Typography', label: 'Italic', authoring: themeAuthoring },
|
|
83
75
|
numberOfLines: {
|
|
84
76
|
type: 'number',
|
|
85
77
|
category: 'Layout',
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
HeadingAlign,
|
|
5
|
+
HeadingColor,
|
|
6
|
+
HeadingEmphasis,
|
|
7
|
+
HeadingSize,
|
|
8
|
+
HeadingWeight,
|
|
9
|
+
} from './types';
|
|
10
|
+
|
|
11
|
+
export function resolveHeadingThemeRecipe(
|
|
12
|
+
fields: Readonly<Record<string, ThemeRecipeOverrideValue>>,
|
|
13
|
+
) {
|
|
14
|
+
return {
|
|
15
|
+
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,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readSize(value: ThemeRecipeOverrideValue | undefined): HeadingSize | undefined {
|
|
25
|
+
if (value === 'display' || value === 'h1' || value === 'h2' || value === 'h3') return value;
|
|
26
|
+
if (value === 'h4' || value === 'h5' || value === 'h6') return value;
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ZORA_COLORS, ZORA_EMPHASES } from '../../internal/colorModel';
|
|
2
|
+
import type { ZoraThemeRecipeMeta } from '../../metadata/themeRecipeTypes';
|
|
3
|
+
|
|
4
|
+
export const headingThemeRecipeMeta = {
|
|
5
|
+
name: 'Heading',
|
|
6
|
+
kind: 'component',
|
|
7
|
+
description: 'Maps Heading presentation defaults without replacing semantic heading levels.',
|
|
8
|
+
fields: {
|
|
9
|
+
size: {
|
|
10
|
+
type: 'choice',
|
|
11
|
+
label: 'Size',
|
|
12
|
+
options: ['display', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
|
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 },
|
|
32
|
+
},
|
|
33
|
+
} as const satisfies ZoraThemeRecipeMeta;
|
|
@@ -3,8 +3,23 @@ import type { ZoraComponentMeta } from '../../metadata';
|
|
|
3
3
|
export const imageMeta = {
|
|
4
4
|
name: 'Image',
|
|
5
5
|
category: 'component',
|
|
6
|
-
|
|
6
|
+
description: 'Displays an image from the app media registry or a resolved runtime source.',
|
|
7
|
+
directManifestNode: true,
|
|
7
8
|
allowedChildren: [],
|
|
8
|
-
|
|
9
|
-
props: {
|
|
9
|
+
blueprint: { label: 'Image' },
|
|
10
|
+
props: {
|
|
11
|
+
source: {
|
|
12
|
+
type: 'media',
|
|
13
|
+
category: 'Content',
|
|
14
|
+
label: 'Source',
|
|
15
|
+
mediaKinds: ['image'],
|
|
16
|
+
authoring: { authority: 'instance' },
|
|
17
|
+
},
|
|
18
|
+
alt: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
category: 'Accessibility',
|
|
21
|
+
label: 'Alt text',
|
|
22
|
+
authoring: { authority: 'instance' },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
10
25
|
} as const satisfies ZoraComponentMeta;
|
|
@@ -3,8 +3,10 @@ import React from 'react';
|
|
|
3
3
|
import { Platform, Text as ReactNativeText, type TextStyle } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import { useZoraTheme } from '../../theme/useZoraTheme';
|
|
6
|
+
import { useZoraThemeRecipe } from '../../theme/useZoraThemeRecipe';
|
|
6
7
|
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
7
8
|
import { resolveTextStyle } from './resolveTextRecipe';
|
|
9
|
+
import { resolveTextThemeRecipe } from './resolveTextThemeRecipe';
|
|
8
10
|
import type { TextProps } from './types';
|
|
9
11
|
|
|
10
12
|
const textLayoutStyle = {
|
|
@@ -29,18 +31,9 @@ function resolveTextContent({
|
|
|
29
31
|
text: TextProps['text'];
|
|
30
32
|
i18nKey: TextProps['i18nKey'];
|
|
31
33
|
}): React.ReactNode {
|
|
32
|
-
if (children !== undefined)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (text !== undefined) {
|
|
37
|
-
return text;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (!i18nKey) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
34
|
+
if (children !== undefined) return children;
|
|
35
|
+
if (text !== undefined) return text;
|
|
36
|
+
if (i18nKey === undefined || i18nKey === '') return null;
|
|
44
37
|
return i18nKey;
|
|
45
38
|
}
|
|
46
39
|
|
|
@@ -50,12 +43,12 @@ function TextInner({
|
|
|
50
43
|
children,
|
|
51
44
|
text,
|
|
52
45
|
i18nKey,
|
|
53
|
-
variant
|
|
46
|
+
variant,
|
|
54
47
|
color,
|
|
55
|
-
emphasis
|
|
48
|
+
emphasis,
|
|
56
49
|
align,
|
|
57
50
|
weight,
|
|
58
|
-
italic
|
|
51
|
+
italic,
|
|
59
52
|
numberOfLines,
|
|
60
53
|
ellipsizeMode,
|
|
61
54
|
selectable,
|
|
@@ -69,22 +62,21 @@ function TextInner({
|
|
|
69
62
|
}: TextProps) {
|
|
70
63
|
const { theme } = useZoraTheme();
|
|
71
64
|
const { breakpoint } = useResponsiveRuntime();
|
|
65
|
+
const themeRecipe = resolveTextThemeRecipe(useZoraThemeRecipe('Text'));
|
|
72
66
|
const content = resolveTextContent({ children, text, i18nKey });
|
|
73
|
-
const resolvedVariant = resolveResponsive(variant, breakpoint) ?? 'body';
|
|
67
|
+
const resolvedVariant = resolveResponsive(variant ?? themeRecipe.variant, breakpoint) ?? 'body';
|
|
74
68
|
const resolvedStyle = resolveTextStyle({
|
|
75
69
|
theme,
|
|
76
70
|
breakpoint,
|
|
77
71
|
variant: resolvedVariant,
|
|
78
|
-
color,
|
|
79
|
-
emphasis,
|
|
80
|
-
align,
|
|
81
|
-
weight,
|
|
82
|
-
italic,
|
|
72
|
+
color: color ?? themeRecipe.color,
|
|
73
|
+
emphasis: emphasis ?? themeRecipe.emphasis,
|
|
74
|
+
align: align ?? themeRecipe.align,
|
|
75
|
+
weight: weight ?? themeRecipe.weight,
|
|
76
|
+
italic: italic ?? themeRecipe.italic ?? false,
|
|
83
77
|
});
|
|
84
78
|
|
|
85
|
-
if (content === null || content === undefined)
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
79
|
+
if (content === null || content === undefined) return null;
|
|
88
80
|
|
|
89
81
|
return (
|
|
90
82
|
<ReactNativeText
|
|
@@ -109,7 +101,6 @@ function TextInner({
|
|
|
109
101
|
* `Text` owns normal body, caption, label, code, and supporting-copy variants so
|
|
110
102
|
* consumers do not need to import lower-level Surface typography directly.
|
|
111
103
|
*
|
|
112
|
-
|
|
113
104
|
* @example Muted supporting copy
|
|
114
105
|
* ```tsx
|
|
115
106
|
* <Text variant="bodySmall" emphasis="muted">Updated just now</Text>
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import { ZORA_COLORS, ZORA_EMPHASES } from '../../internal/colorModel';
|
|
2
2
|
import type { ZoraComponentMeta } from '../../metadata';
|
|
3
3
|
|
|
4
|
+
const themeAuthoring = {
|
|
5
|
+
authority: 'theme',
|
|
6
|
+
scope: 'component',
|
|
7
|
+
allowInstanceOverride: true,
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
4
10
|
export const textMeta = {
|
|
5
11
|
name: 'Text',
|
|
6
12
|
category: 'component',
|
|
7
13
|
directManifestNode: true,
|
|
8
14
|
allowedChildren: [],
|
|
9
|
-
blueprint: {
|
|
10
|
-
|
|
11
|
-
defaultProps: {
|
|
12
|
-
text: 'Text',
|
|
13
|
-
emphasis: 'default',
|
|
14
|
-
variant: 'body',
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
i18n: {
|
|
18
|
-
fields: [{ keyProp: 'i18nKey', defaultTextProp: 'text' }],
|
|
19
|
-
},
|
|
15
|
+
blueprint: { label: 'Text', defaultProps: { text: 'Text' } },
|
|
16
|
+
i18n: { fields: [{ keyProp: 'i18nKey', defaultTextProp: 'text' }] },
|
|
20
17
|
props: {
|
|
21
18
|
text: {
|
|
22
19
|
type: 'string',
|
|
@@ -36,45 +33,37 @@ export const textMeta = {
|
|
|
36
33
|
category: 'Typography',
|
|
37
34
|
label: 'Variant',
|
|
38
35
|
enum: ['body', 'lead', 'bodySmall', 'caption', 'label', 'eyebrow', 'code'],
|
|
39
|
-
|
|
40
|
-
authoring: { authority: 'theme', scope: 'component' },
|
|
36
|
+
authoring: themeAuthoring,
|
|
41
37
|
},
|
|
42
38
|
color: {
|
|
43
39
|
type: 'enum',
|
|
44
40
|
category: 'Style',
|
|
45
41
|
label: 'Color',
|
|
46
42
|
enum: ZORA_COLORS,
|
|
47
|
-
authoring:
|
|
43
|
+
authoring: themeAuthoring,
|
|
48
44
|
},
|
|
49
45
|
emphasis: {
|
|
50
46
|
type: 'enum',
|
|
51
47
|
category: 'Style',
|
|
52
48
|
label: 'Emphasis',
|
|
53
49
|
enum: ZORA_EMPHASES,
|
|
54
|
-
|
|
55
|
-
authoring: { authority: 'theme', scope: 'component' },
|
|
50
|
+
authoring: themeAuthoring,
|
|
56
51
|
},
|
|
57
52
|
align: {
|
|
58
53
|
type: 'enum',
|
|
59
54
|
category: 'Layout',
|
|
60
55
|
label: 'Align',
|
|
61
56
|
enum: ['auto', 'left', 'right', 'center', 'justify'],
|
|
62
|
-
authoring:
|
|
57
|
+
authoring: themeAuthoring,
|
|
63
58
|
},
|
|
64
59
|
weight: {
|
|
65
60
|
type: 'enum',
|
|
66
61
|
category: 'Typography',
|
|
67
62
|
label: 'Weight',
|
|
68
63
|
enum: ['regular', 'medium', 'semiBold', 'bold'],
|
|
69
|
-
authoring:
|
|
70
|
-
},
|
|
71
|
-
italic: {
|
|
72
|
-
type: 'boolean',
|
|
73
|
-
category: 'Typography',
|
|
74
|
-
label: 'Italic',
|
|
75
|
-
default: false,
|
|
76
|
-
authoring: { authority: 'theme', scope: 'component' },
|
|
64
|
+
authoring: themeAuthoring,
|
|
77
65
|
},
|
|
66
|
+
italic: { type: 'boolean', category: 'Typography', label: 'Italic', authoring: themeAuthoring },
|
|
78
67
|
numberOfLines: {
|
|
79
68
|
type: 'number',
|
|
80
69
|
category: 'Layout',
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
|
|
2
|
+
|
|
3
|
+
import type { TextAlign, TextColor, TextEmphasis, TextVariant, TextWeight } from './types';
|
|
4
|
+
|
|
5
|
+
export function resolveTextThemeRecipe(fields: Readonly<Record<string, ThemeRecipeOverrideValue>>) {
|
|
6
|
+
return {
|
|
7
|
+
variant: readVariant(fields.variant),
|
|
8
|
+
color: readColor(fields.color),
|
|
9
|
+
emphasis: readEmphasis(fields.emphasis),
|
|
10
|
+
align: readAlign(fields.align),
|
|
11
|
+
weight: readWeight(fields.weight),
|
|
12
|
+
italic: typeof fields.italic === 'boolean' ? fields.italic : undefined,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readVariant(value: ThemeRecipeOverrideValue | undefined): TextVariant | undefined {
|
|
17
|
+
if (value === 'body' || value === 'lead' || value === 'bodySmall' || value === 'caption')
|
|
18
|
+
return value;
|
|
19
|
+
if (value === 'label' || value === 'eyebrow' || value === 'code') return value;
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readColor(value: ThemeRecipeOverrideValue | undefined): TextColor | undefined {
|
|
24
|
+
if (typeof value !== 'string') return undefined;
|
|
25
|
+
if (value === 'primary' || value === 'secondary' || value === 'tertiary') return value;
|
|
26
|
+
if (value === 'quaternary' || value === 'neutral' || value === 'danger') return value;
|
|
27
|
+
if (value === 'success' || value === 'warning' || value === 'error' || value === 'info')
|
|
28
|
+
return value;
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readEmphasis(value: ThemeRecipeOverrideValue | undefined): TextEmphasis | undefined {
|
|
33
|
+
if (value === 'default' || value === 'muted' || value === 'subtle' || value === 'inverse')
|
|
34
|
+
return value;
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readAlign(value: ThemeRecipeOverrideValue | undefined): TextAlign | undefined {
|
|
39
|
+
if (value === 'auto' || value === 'left' || value === 'right') return value;
|
|
40
|
+
if (value === 'center' || value === 'justify') return value;
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readWeight(value: ThemeRecipeOverrideValue | undefined): TextWeight | undefined {
|
|
45
|
+
if (value === 'regular' || value === 'medium' || value === 'semiBold' || value === 'bold')
|
|
46
|
+
return value;
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
@@ -1,33 +1,34 @@
|
|
|
1
|
+
import { ZORA_COLORS, ZORA_EMPHASES } from '../../internal/colorModel';
|
|
1
2
|
import type { ZoraThemeRecipeMeta } from '../../metadata/themeRecipeTypes';
|
|
2
3
|
|
|
3
4
|
export const textThemeRecipeMeta = {
|
|
4
5
|
name: 'Text',
|
|
5
6
|
kind: 'component',
|
|
6
|
-
description: 'Maps
|
|
7
|
+
description: 'Maps semantic Text presentation defaults into runtime props.',
|
|
7
8
|
fields: {
|
|
8
9
|
variant: {
|
|
9
|
-
type: '
|
|
10
|
-
tokenFamily: 'typography',
|
|
10
|
+
type: 'choice',
|
|
11
11
|
label: 'Typography variant',
|
|
12
|
+
options: ['body', 'lead', 'bodySmall', 'caption', 'label', 'eyebrow', 'code'],
|
|
12
13
|
default: 'body',
|
|
13
14
|
},
|
|
14
|
-
color: {
|
|
15
|
-
type: 'token',
|
|
16
|
-
tokenFamily: 'colors',
|
|
17
|
-
label: 'Color',
|
|
18
|
-
default: 'text',
|
|
19
|
-
},
|
|
15
|
+
color: { type: 'choice', label: 'Color', options: ZORA_COLORS },
|
|
20
16
|
emphasis: {
|
|
21
17
|
type: 'choice',
|
|
22
18
|
label: 'Emphasis',
|
|
23
|
-
options:
|
|
19
|
+
options: ZORA_EMPHASES,
|
|
24
20
|
default: 'default',
|
|
25
21
|
},
|
|
22
|
+
align: {
|
|
23
|
+
type: 'choice',
|
|
24
|
+
label: 'Align',
|
|
25
|
+
options: ['auto', 'left', 'right', 'center', 'justify'],
|
|
26
|
+
},
|
|
26
27
|
weight: {
|
|
27
28
|
type: 'choice',
|
|
28
29
|
label: 'Weight',
|
|
29
30
|
options: ['regular', 'medium', 'semiBold', 'bold'],
|
|
30
|
-
default: 'regular',
|
|
31
31
|
},
|
|
32
|
+
italic: { type: 'boolean', label: 'Italic', default: false },
|
|
32
33
|
},
|
|
33
34
|
} as const satisfies ZoraThemeRecipeMeta;
|
|
@@ -2,70 +2,44 @@ import { describe, expect, test } from 'bun:test';
|
|
|
2
2
|
|
|
3
3
|
import { ZORA_COMPONENT_META, type ZoraComponentPropAuthoring } from './index';
|
|
4
4
|
|
|
5
|
+
const componentMetaByName = new Map(Object.entries(ZORA_COMPONENT_META));
|
|
6
|
+
|
|
5
7
|
describe('ZORA component prop authoring authority', () => {
|
|
6
|
-
test('supports
|
|
8
|
+
test('supports instance and theme authority with explicit instance override capability', () => {
|
|
7
9
|
const values: readonly ZoraComponentPropAuthoring[] = [
|
|
8
10
|
{ authority: 'instance' },
|
|
9
11
|
{ authority: 'theme', scope: 'global' },
|
|
10
|
-
{ authority: 'theme', scope: 'component' },
|
|
11
|
-
{ authority: 'theme', scope: 'pattern' },
|
|
12
|
+
{ authority: 'theme', scope: 'component', allowInstanceOverride: true },
|
|
13
|
+
{ authority: 'theme', scope: 'pattern', allowInstanceOverride: true },
|
|
12
14
|
];
|
|
13
|
-
|
|
14
|
-
expect(values).toEqual([
|
|
15
|
-
{ authority: 'instance' },
|
|
16
|
-
{ authority: 'theme', scope: 'global' },
|
|
17
|
-
{ authority: 'theme', scope: 'component' },
|
|
18
|
-
{ authority: 'theme', scope: 'pattern' },
|
|
19
|
-
]);
|
|
15
|
+
expect(values).toHaveLength(4);
|
|
20
16
|
});
|
|
21
17
|
|
|
22
|
-
test('
|
|
23
|
-
expect(ZORA_COMPONENT_META.Button.props.children?.authoring).toEqual({
|
|
24
|
-
authority: 'instance',
|
|
25
|
-
});
|
|
18
|
+
test('keeps representative content and semantics instance-owned', () => {
|
|
19
|
+
expect(ZORA_COMPONENT_META.Button.props.children?.authoring).toEqual({ authority: 'instance' });
|
|
26
20
|
expect(ZORA_COMPONENT_META.Card.props.title?.authoring).toEqual({ authority: 'instance' });
|
|
27
|
-
expect(ZORA_COMPONENT_META.Heading.props.text?.authoring).toEqual({ authority: 'instance' });
|
|
28
21
|
expect(ZORA_COMPONENT_META.Heading.props.level?.authoring).toEqual({ authority: 'instance' });
|
|
29
22
|
expect(ZORA_COMPONENT_META.Text.props.text?.authoring).toEqual({ authority: 'instance' });
|
|
30
|
-
expect(ZORA_COMPONENT_META.Panel.props.description?.authoring).toEqual({
|
|
31
|
-
authority: 'instance',
|
|
32
|
-
});
|
|
33
23
|
});
|
|
34
24
|
|
|
35
|
-
test('
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
expect(ZORA_COMPONENT_META.Heading.props.size?.authoring).toEqual({
|
|
45
|
-
authority: 'theme',
|
|
46
|
-
scope: 'component',
|
|
47
|
-
});
|
|
48
|
-
expect(ZORA_COMPONENT_META.Text.props.variant?.authoring).toEqual({
|
|
49
|
-
authority: 'theme',
|
|
50
|
-
scope: 'component',
|
|
51
|
-
});
|
|
52
|
-
expect(ZORA_COMPONENT_META.Panel.props.compact?.authoring).toEqual({
|
|
53
|
-
authority: 'theme',
|
|
54
|
-
scope: 'pattern',
|
|
55
|
-
});
|
|
25
|
+
test('marks recipe props theme-owned while allowing explicit instance overrides', () => {
|
|
26
|
+
for (const name of ['Button', 'Card', 'Heading', 'Text', 'Panel']) {
|
|
27
|
+
const meta = componentMetaByName.get(name);
|
|
28
|
+
expect(meta, name).toBeDefined();
|
|
29
|
+
for (const prop of Object.values(meta?.props ?? {})) {
|
|
30
|
+
if (prop.authoring?.authority !== 'theme') continue;
|
|
31
|
+
expect(prop.authoring.allowInstanceOverride, name).toBe(true);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
56
34
|
});
|
|
57
35
|
|
|
58
|
-
test('keeps Heading blueprint content useful
|
|
36
|
+
test('keeps Heading blueprint semantic content useful', () => {
|
|
59
37
|
expect(ZORA_COMPONENT_META.Heading.blueprint?.defaultProps).toEqual({
|
|
60
38
|
text: 'Heading',
|
|
61
39
|
level: 2,
|
|
62
40
|
});
|
|
63
41
|
});
|
|
64
42
|
|
|
65
|
-
test('treats absent authoring metadata as not authorable', () => {
|
|
66
|
-
expect(ZORA_COMPONENT_META.Progress.props.value?.authoring).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
43
|
test('does not introduce a system authority', () => {
|
|
70
44
|
expect(JSON.stringify(ZORA_COMPONENT_META)).not.toContain('"authority":"system"');
|
|
71
45
|
});
|
|
@@ -2,6 +2,7 @@ import type { UiComponentMetaRegistry } from '@ankhorage/contracts';
|
|
|
2
2
|
import { describe, expect, it } from 'bun:test';
|
|
3
3
|
|
|
4
4
|
import { ZORA_BINDABLE_COMPONENT_META } from './bindableComponentMeta';
|
|
5
|
+
import { ZORA_COMPONENT_META } from './componentMeta';
|
|
5
6
|
|
|
6
7
|
describe('ZORA bindable component metadata', () => {
|
|
7
8
|
it('matches the shared UI metadata registry shape', () => {
|
|
@@ -15,4 +16,16 @@ describe('ZORA bindable component metadata', () => {
|
|
|
15
16
|
expect(registry.Image?.bindings?.props?.source?.value.type).toBe('imageAsset');
|
|
16
17
|
expect(registry.DataTable?.bindings?.props?.rows?.value.type).toBe('array');
|
|
17
18
|
});
|
|
19
|
+
|
|
20
|
+
it('shares canonical Image authoring metadata while preserving dynamic image bindings', () => {
|
|
21
|
+
const image = ZORA_BINDABLE_COMPONENT_META.Image;
|
|
22
|
+
|
|
23
|
+
expect(image.props.source).toMatchObject({
|
|
24
|
+
type: 'media',
|
|
25
|
+
mediaKinds: ['image'],
|
|
26
|
+
});
|
|
27
|
+
expect(image.props).toEqual(ZORA_COMPONENT_META.Image.props);
|
|
28
|
+
expect(image.directManifestNode).toBe(ZORA_COMPONENT_META.Image.directManifestNode);
|
|
29
|
+
expect(image.bindings.props.source.value.type).toBe('imageAsset');
|
|
30
|
+
});
|
|
18
31
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { UiComponentMetaRegistry } from '@ankhorage/contracts';
|
|
2
2
|
|
|
3
|
+
import { imageMeta } from '../components/image/meta';
|
|
4
|
+
|
|
3
5
|
export const ZORA_BINDABLE_COMPONENT_META = {
|
|
4
6
|
Text: {
|
|
5
7
|
name: 'Text',
|
|
@@ -107,16 +109,12 @@ export const ZORA_BINDABLE_COMPONENT_META = {
|
|
|
107
109
|
},
|
|
108
110
|
},
|
|
109
111
|
Image: {
|
|
110
|
-
|
|
111
|
-
category: 'component',
|
|
112
|
-
description: 'Displays an image asset or remote image source.',
|
|
113
|
-
directManifestNode: true,
|
|
114
|
-
allowedChildren: [],
|
|
112
|
+
...imageMeta,
|
|
115
113
|
bindings: {
|
|
116
114
|
props: {
|
|
117
115
|
source: {
|
|
118
116
|
label: 'Source',
|
|
119
|
-
description: '
|
|
117
|
+
description: 'Dynamic image source or asset descriptor.',
|
|
120
118
|
value: {
|
|
121
119
|
type: 'imageAsset',
|
|
122
120
|
fields: [
|
|
@@ -131,13 +129,6 @@ export const ZORA_BINDABLE_COMPONENT_META = {
|
|
|
131
129
|
},
|
|
132
130
|
},
|
|
133
131
|
},
|
|
134
|
-
props: {
|
|
135
|
-
source: {
|
|
136
|
-
type: 'imageAsset',
|
|
137
|
-
category: 'Content',
|
|
138
|
-
label: 'Source',
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
132
|
},
|
|
142
133
|
Input: {
|
|
143
134
|
name: 'Input',
|
|
@@ -124,6 +124,7 @@ describe('ZORA_COMPONENT_META invariants', () => {
|
|
|
124
124
|
'Textarea',
|
|
125
125
|
'Text',
|
|
126
126
|
'Heading',
|
|
127
|
+
'Image',
|
|
127
128
|
'Divider',
|
|
128
129
|
'ChatListItem',
|
|
129
130
|
'CameraPermissionView',
|
|
@@ -172,6 +173,19 @@ describe('ZORA_COMPONENT_META invariants', () => {
|
|
|
172
173
|
}
|
|
173
174
|
});
|
|
174
175
|
|
|
176
|
+
test('Image is a direct manifest leaf with canonical media authoring metadata', () => {
|
|
177
|
+
expect(ZORA_COMPONENT_META.Image.directManifestNode).toBe(true);
|
|
178
|
+
expect(ZORA_COMPONENT_META.Image.allowedChildren).toEqual([]);
|
|
179
|
+
expect(ZORA_COMPONENT_META.Image.props.source).toEqual({
|
|
180
|
+
type: 'media',
|
|
181
|
+
category: 'Content',
|
|
182
|
+
label: 'Source',
|
|
183
|
+
mediaKinds: ['image'],
|
|
184
|
+
authoring: { authority: 'instance' },
|
|
185
|
+
});
|
|
186
|
+
expect(ZORA_COMPONENT_META.Image.props.alt?.authoring).toEqual({ authority: 'instance' });
|
|
187
|
+
});
|
|
188
|
+
|
|
175
189
|
test('ThemeModeToggle is a direct manifest leaf with serializable defaults', () => {
|
|
176
190
|
expect(ZORA_COMPONENT_META.ThemeModeToggle.directManifestNode).toBe(true);
|
|
177
191
|
expect(ZORA_COMPONENT_META.ThemeModeToggle.allowedChildren).toEqual([]);
|