@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
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ZORA_THEME_RECIPE_META } from '../metadata/themeRecipeMeta';
|
|
2
|
+
const recipeMetaByName = new Map(Object.entries(ZORA_THEME_RECIPE_META));
|
|
3
|
+
export function resolveZoraThemeRecipe(theme, recipeName) {
|
|
4
|
+
const meta = recipeMetaByName.get(recipeName);
|
|
5
|
+
if (!meta)
|
|
6
|
+
throw new RangeError(`Unknown ZORA theme recipe: ${recipeName}.`);
|
|
7
|
+
const recipes = meta.kind === 'component' ? theme.config.recipes?.components : theme.config.recipes?.patterns;
|
|
8
|
+
const overrides = new Map(Object.entries(recipes ?? {})).get(recipeName);
|
|
9
|
+
const overrideByField = new Map(Object.entries(overrides ?? {}));
|
|
10
|
+
const resolved = [];
|
|
11
|
+
for (const [fieldName, fieldMeta] of Object.entries(meta.fields)) {
|
|
12
|
+
const override = overrideByField.get(fieldName);
|
|
13
|
+
if (override !== undefined) {
|
|
14
|
+
validateFieldValue(theme, recipeName, fieldName, fieldMeta, override);
|
|
15
|
+
resolved.push([fieldName, override]);
|
|
16
|
+
}
|
|
17
|
+
else if (fieldMeta.default !== undefined) {
|
|
18
|
+
resolved.push([fieldName, fieldMeta.default]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return Object.fromEntries(resolved);
|
|
22
|
+
}
|
|
23
|
+
function validateFieldValue(theme, recipeName, fieldName, meta, value) {
|
|
24
|
+
if (meta.type === 'boolean') {
|
|
25
|
+
if (typeof value !== 'boolean') {
|
|
26
|
+
throw new TypeError(`Theme recipe ${recipeName}.${fieldName} must be boolean.`);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value !== 'string') {
|
|
31
|
+
throw new TypeError(`Theme recipe ${recipeName}.${fieldName} must be a string.`);
|
|
32
|
+
}
|
|
33
|
+
if (meta.type === 'choice' && !meta.options.includes(value)) {
|
|
34
|
+
throw new RangeError(`Invalid theme recipe choice for ${recipeName}.${fieldName}: ${value}.`);
|
|
35
|
+
}
|
|
36
|
+
if (meta.type === 'token' && !hasThemeToken(theme, meta.tokenFamily, value)) {
|
|
37
|
+
throw new RangeError(`Unknown ${meta.tokenFamily} token for ${recipeName}.${fieldName}: ${value}.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function hasThemeToken(theme, family, token) {
|
|
41
|
+
if (family === 'colors')
|
|
42
|
+
return hasOwn(theme.colors, token);
|
|
43
|
+
if (family === 'spacing')
|
|
44
|
+
return hasOwn(theme.spacing, token);
|
|
45
|
+
if (family === 'radii')
|
|
46
|
+
return hasOwn(theme.radii, token);
|
|
47
|
+
if (family === 'shadows')
|
|
48
|
+
return hasOwn(theme.shadows, token);
|
|
49
|
+
return (hasOwn(theme.typography.sizes, token) ||
|
|
50
|
+
hasOwn(theme.typography.weights, token) ||
|
|
51
|
+
hasOwn(theme.typography.headings, token));
|
|
52
|
+
}
|
|
53
|
+
function hasOwn(value, key) {
|
|
54
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=resolveZoraThemeRecipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveZoraThemeRecipe.js","sourceRoot":"","sources":["../../src/theme/resolveZoraThemeRecipe.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThemeConfig, ThemeRecipeOverrideValue } from '@ankhorage/contracts';\n\nimport { ZORA_THEME_RECIPE_META } from '../metadata/themeRecipeMeta';\nimport type { ZoraThemeRecipeFieldMeta, ZoraThemeTokenFamily } from '../metadata/themeRecipeTypes';\n\ninterface ZoraThemeRecipeRuntimeTheme {\n readonly config: ThemeConfig;\n readonly colors: object;\n readonly spacing: object;\n readonly radii: object;\n readonly shadows: object;\n readonly typography: {\n readonly sizes: object;\n readonly weights: object;\n readonly headings: object;\n };\n}\n\nconst recipeMetaByName = new Map(Object.entries(ZORA_THEME_RECIPE_META));\n\nexport function resolveZoraThemeRecipe(\n theme: ZoraThemeRecipeRuntimeTheme,\n recipeName: string,\n): Readonly<Record<string, ThemeRecipeOverrideValue>> {\n const meta = recipeMetaByName.get(recipeName);\n if (!meta) throw new RangeError(`Unknown ZORA theme recipe: ${recipeName}.`);\n\n const recipes =\n meta.kind === 'component' ? theme.config.recipes?.components : theme.config.recipes?.patterns;\n const overrides = new Map(Object.entries(recipes ?? {})).get(recipeName);\n const overrideByField = new Map(Object.entries(overrides ?? {}));\n const resolved: [string, ThemeRecipeOverrideValue][] = [];\n\n for (const [fieldName, fieldMeta] of Object.entries(meta.fields)) {\n const override = overrideByField.get(fieldName);\n if (override !== undefined) {\n validateFieldValue(theme, recipeName, fieldName, fieldMeta, override);\n resolved.push([fieldName, override]);\n } else if (fieldMeta.default !== undefined) {\n resolved.push([fieldName, fieldMeta.default]);\n }\n }\n\n return Object.fromEntries(resolved);\n}\n\nfunction validateFieldValue(\n theme: ZoraThemeRecipeRuntimeTheme,\n recipeName: string,\n fieldName: string,\n meta: ZoraThemeRecipeFieldMeta,\n value: ThemeRecipeOverrideValue,\n): void {\n if (meta.type === 'boolean') {\n if (typeof value !== 'boolean') {\n throw new TypeError(`Theme recipe ${recipeName}.${fieldName} must be boolean.`);\n }\n return;\n }\n if (typeof value !== 'string') {\n throw new TypeError(`Theme recipe ${recipeName}.${fieldName} must be a string.`);\n }\n if (meta.type === 'choice' && !meta.options.includes(value)) {\n throw new RangeError(`Invalid theme recipe choice for ${recipeName}.${fieldName}: ${value}.`);\n }\n if (meta.type === 'token' && !hasThemeToken(theme, meta.tokenFamily, value)) {\n throw new RangeError(\n `Unknown ${meta.tokenFamily} token for ${recipeName}.${fieldName}: ${value}.`,\n );\n }\n}\n\nfunction hasThemeToken(\n theme: ZoraThemeRecipeRuntimeTheme,\n family: ZoraThemeTokenFamily,\n token: string,\n): boolean {\n if (family === 'colors') return hasOwn(theme.colors, token);\n if (family === 'spacing') return hasOwn(theme.spacing, token);\n if (family === 'radii') return hasOwn(theme.radii, token);\n if (family === 'shadows') return hasOwn(theme.shadows, token);\n return (\n hasOwn(theme.typography.sizes, token) ||\n hasOwn(theme.typography.weights, token) ||\n hasOwn(theme.typography.headings, token)\n );\n}\n\nfunction hasOwn(value: object, key: PropertyKey): boolean {\n return Object.prototype.hasOwnProperty.call(value, key);\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare function useZoraTheme(): {
|
|
2
2
|
theme: import("@ankhorage/surface").SurfaceTheme;
|
|
3
3
|
mode: "light" | "dark";
|
|
4
|
-
setThemeConfig: (config: Partial<import("@ankhorage/
|
|
4
|
+
setThemeConfig: (config: Partial<import("@ankhorage/surface").ThemeConfig>) => void;
|
|
5
5
|
setMode: (mode: "light" | "dark") => void;
|
|
6
6
|
_hasProvider?: boolean;
|
|
7
7
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useZoraThemeRecipe.d.ts","sourceRoot":"","sources":["../../src/theme/useZoraThemeRecipe.ts"],"names":[],"mappings":"AAGA,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,qFAGpD"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { resolveZoraThemeRecipe } from './resolveZoraThemeRecipe';
|
|
2
|
+
import { useZoraTheme } from './useZoraTheme';
|
|
3
|
+
export function useZoraThemeRecipe(recipeName) {
|
|
4
|
+
const { theme } = useZoraTheme();
|
|
5
|
+
return resolveZoraThemeRecipe(theme, recipeName);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=useZoraThemeRecipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useZoraThemeRecipe.js","sourceRoot":"","sources":["../../src/theme/useZoraThemeRecipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,MAAM,EAAE,KAAK,EAAE,GAAG,YAAY,EAAE,CAAC;IACjC,OAAO,sBAAsB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACnD,CAAC","sourcesContent":["import { resolveZoraThemeRecipe } from './resolveZoraThemeRecipe';\nimport { useZoraTheme } from './useZoraTheme';\n\nexport function useZoraThemeRecipe(recipeName: string) {\n const { theme } = useZoraTheme();\n return resolveZoraThemeRecipe(theme, recipeName);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/zora",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.13.0",
|
|
5
5
|
"description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/zora#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@ankhorage/color-theory": "^0.0.8",
|
|
45
|
-
"@ankhorage/contracts": "^
|
|
46
|
-
"@ankhorage/surface": "^2.
|
|
45
|
+
"@ankhorage/contracts": "^7.6.0",
|
|
46
|
+
"@ankhorage/surface": "^2.2.0"
|
|
47
47
|
},
|
|
48
48
|
"files": [
|
|
49
49
|
"dist",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@ankhorage/devtools": "^1.3.4",
|
|
93
|
-
"@ankhorage/paradox": "^0.1.
|
|
93
|
+
"@ankhorage/paradox": "^0.1.21",
|
|
94
94
|
"@changesets/cli": "^2.31.0",
|
|
95
95
|
"@expo/vector-icons": "^15.1.1",
|
|
96
96
|
"@react-native-picker/picker": "^2.11.4",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Button as SurfaceButton } from '@ankhorage/surface';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { useZoraThemeRecipe } from '../../theme/useZoraThemeRecipe';
|
|
5
5
|
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
6
|
+
import { resolveButtonThemeRecipe } from './resolveButtonThemeRecipe';
|
|
6
7
|
import type { ButtonProps } from './types';
|
|
7
8
|
|
|
8
9
|
function ButtonInner({
|
|
@@ -14,7 +15,8 @@ function ButtonInner({
|
|
|
14
15
|
interactionPolicy,
|
|
15
16
|
...props
|
|
16
17
|
}: ButtonProps) {
|
|
17
|
-
const
|
|
18
|
+
const themeFields = useZoraThemeRecipe('Button');
|
|
19
|
+
const recipe = resolveButtonThemeRecipe({ color, variant, size, themeFields });
|
|
18
20
|
|
|
19
21
|
return (
|
|
20
22
|
<SurfaceButton
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { ZORA_COLORS } 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 buttonMeta = {
|
|
5
11
|
name: 'Button',
|
|
6
12
|
category: 'component',
|
|
7
13
|
directManifestNode: true,
|
|
8
14
|
allowedChildren: [],
|
|
9
|
-
blueprint: {
|
|
10
|
-
label: 'Button',
|
|
11
|
-
defaultProps: {
|
|
12
|
-
children: 'Continue',
|
|
13
|
-
color: 'primary',
|
|
14
|
-
variant: 'solid',
|
|
15
|
-
size: 'm',
|
|
16
|
-
},
|
|
17
|
-
},
|
|
15
|
+
blueprint: { label: 'Button', defaultProps: { children: 'Continue' } },
|
|
18
16
|
events: {
|
|
19
17
|
press: {
|
|
20
18
|
label: 'Press',
|
|
@@ -36,24 +34,21 @@ export const buttonMeta = {
|
|
|
36
34
|
category: 'Style',
|
|
37
35
|
label: 'Color',
|
|
38
36
|
enum: [...ZORA_COLORS],
|
|
39
|
-
|
|
40
|
-
authoring: { authority: 'theme', scope: 'component' },
|
|
37
|
+
authoring: themeAuthoring,
|
|
41
38
|
},
|
|
42
39
|
variant: {
|
|
43
40
|
type: 'enum',
|
|
44
41
|
category: 'Style',
|
|
45
42
|
label: 'Variant',
|
|
46
43
|
enum: ['solid', 'outline', 'ghost', 'soft'],
|
|
47
|
-
|
|
48
|
-
authoring: { authority: 'theme', scope: 'component' },
|
|
44
|
+
authoring: themeAuthoring,
|
|
49
45
|
},
|
|
50
46
|
size: {
|
|
51
47
|
type: 'enum',
|
|
52
48
|
category: 'Style',
|
|
53
49
|
label: 'Size',
|
|
54
50
|
enum: ['s', 'm', 'l'],
|
|
55
|
-
|
|
56
|
-
authoring: { authority: 'theme', scope: 'component' },
|
|
51
|
+
authoring: themeAuthoring,
|
|
57
52
|
},
|
|
58
53
|
},
|
|
59
54
|
} as const satisfies ZoraComponentMeta;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { resolveButtonThemeRecipe } from './resolveButtonThemeRecipe';
|
|
4
|
+
|
|
5
|
+
describe('resolveButtonThemeRecipe', () => {
|
|
6
|
+
test('uses theme defaults without changing the existing hard default', () => {
|
|
7
|
+
expect(
|
|
8
|
+
resolveButtonThemeRecipe({ themeFields: { color: 'primary', variant: 'solid', size: 'l' } }),
|
|
9
|
+
).toEqual({
|
|
10
|
+
color: 'primary',
|
|
11
|
+
variant: 'solid',
|
|
12
|
+
size: 'l',
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('lets explicit instance props override theme recipe values', () => {
|
|
17
|
+
expect(
|
|
18
|
+
resolveButtonThemeRecipe({
|
|
19
|
+
color: 'danger',
|
|
20
|
+
size: 's',
|
|
21
|
+
themeFields: { color: 'primary', variant: 'soft', size: 'l' },
|
|
22
|
+
}),
|
|
23
|
+
).toEqual({ color: 'danger', variant: 'soft', size: 's' });
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
resolveButtonRecipe,
|
|
5
|
+
type ZoraButtonVariant,
|
|
6
|
+
type ZoraColor,
|
|
7
|
+
type ZoraControlSize,
|
|
8
|
+
} from '../../internal/recipes';
|
|
9
|
+
|
|
10
|
+
export function resolveButtonThemeRecipe(input: {
|
|
11
|
+
readonly color?: ZoraColor;
|
|
12
|
+
readonly variant?: ZoraButtonVariant;
|
|
13
|
+
readonly size?: ZoraControlSize;
|
|
14
|
+
readonly themeFields: Readonly<Record<string, ThemeRecipeOverrideValue>>;
|
|
15
|
+
}) {
|
|
16
|
+
return resolveButtonRecipe({
|
|
17
|
+
color: input.color ?? readColor(input.themeFields.color),
|
|
18
|
+
variant: input.variant ?? readVariant(input.themeFields.variant),
|
|
19
|
+
size: input.size ?? readSize(input.themeFields.size),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readColor(value: ThemeRecipeOverrideValue | undefined): ZoraColor | 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 readVariant(value: ThemeRecipeOverrideValue | undefined): ZoraButtonVariant | undefined {
|
|
33
|
+
if (value === 'solid' || value === 'outline' || value === 'ghost' || value === 'soft')
|
|
34
|
+
return value;
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readSize(value: ThemeRecipeOverrideValue | undefined): ZoraControlSize | undefined {
|
|
39
|
+
if (value === 's' || value === 'm' || value === 'l') return value;
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import { ZORA_COLORS } from '../../internal/colorModel';
|
|
1
2
|
import type { ZoraThemeRecipeMeta } from '../../metadata/themeRecipeTypes';
|
|
2
3
|
|
|
3
4
|
export const buttonThemeRecipeMeta = {
|
|
4
5
|
name: 'Button',
|
|
5
6
|
kind: 'component',
|
|
6
|
-
description: 'Maps shared
|
|
7
|
+
description: 'Maps shared semantic choices into Button presentation defaults.',
|
|
7
8
|
fields: {
|
|
8
9
|
color: {
|
|
9
|
-
type: '
|
|
10
|
-
tokenFamily: 'colors',
|
|
10
|
+
type: 'choice',
|
|
11
11
|
label: 'Color',
|
|
12
|
+
options: ZORA_COLORS,
|
|
12
13
|
default: 'primary',
|
|
13
14
|
},
|
|
14
15
|
variant: {
|
|
@@ -22,13 +23,7 @@ export const buttonThemeRecipeMeta = {
|
|
|
22
23
|
label: 'Size',
|
|
23
24
|
description: 'A Button-specific semantic scale, not a universal pixel size.',
|
|
24
25
|
options: ['s', 'm', 'l'],
|
|
25
|
-
default: '
|
|
26
|
-
},
|
|
27
|
-
radius: {
|
|
28
|
-
type: 'token',
|
|
29
|
-
tokenFamily: 'radii',
|
|
30
|
-
label: 'Corner radius',
|
|
31
|
-
default: 'm',
|
|
26
|
+
default: 'l',
|
|
32
27
|
},
|
|
33
28
|
},
|
|
34
29
|
} as const satisfies ZoraThemeRecipeMeta;
|
|
@@ -3,9 +3,11 @@ import React from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { Box, Stack } from '../../foundation';
|
|
5
5
|
import { resolveCardVariant } from '../../internal/recipes';
|
|
6
|
+
import { useZoraThemeRecipe } from '../../theme/useZoraThemeRecipe';
|
|
6
7
|
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
7
8
|
import { Heading } from '../heading';
|
|
8
9
|
import { Text } from '../text';
|
|
10
|
+
import { resolveCardThemeRecipe } from './resolveCardThemeRecipe';
|
|
9
11
|
import type { CardProps } from './types';
|
|
10
12
|
|
|
11
13
|
function CardInner({
|
|
@@ -17,15 +19,19 @@ function CardInner({
|
|
|
17
19
|
eyebrow,
|
|
18
20
|
actions,
|
|
19
21
|
footer,
|
|
20
|
-
tone
|
|
21
|
-
compact
|
|
22
|
+
tone,
|
|
23
|
+
compact,
|
|
24
|
+
padding,
|
|
25
|
+
radius,
|
|
22
26
|
onPress,
|
|
23
27
|
interactionPolicy,
|
|
24
28
|
...props
|
|
25
29
|
}: CardProps) {
|
|
30
|
+
const themeFields = useZoraThemeRecipe('Card');
|
|
31
|
+
const recipe = resolveCardThemeRecipe({ tone, compact, padding, radius, themeFields });
|
|
26
32
|
const hasHeader = [eyebrow, title, description, actions].some((item) => item != null);
|
|
27
33
|
const hasFooter = footer !== undefined;
|
|
28
|
-
const gap = compact ? 's' : 'm';
|
|
34
|
+
const gap = recipe.compact ? 's' : 'm';
|
|
29
35
|
const isInteractive = Boolean(onPress) && !actions;
|
|
30
36
|
const passive = interactionPolicy === 'passive';
|
|
31
37
|
|
|
@@ -33,9 +39,9 @@ function CardInner({
|
|
|
33
39
|
<SurfaceCard
|
|
34
40
|
{...props}
|
|
35
41
|
onPress={isInteractive && !passive ? onPress : undefined}
|
|
36
|
-
p={
|
|
37
|
-
radius=
|
|
38
|
-
variant={resolveCardVariant(tone)}
|
|
42
|
+
p={recipe.padding}
|
|
43
|
+
radius={recipe.radius}
|
|
44
|
+
variant={resolveCardVariant(recipe.tone)}
|
|
39
45
|
>
|
|
40
46
|
<Stack gap={gap}>
|
|
41
47
|
{hasHeader ? (
|
|
@@ -52,7 +58,7 @@ function CardInner({
|
|
|
52
58
|
{eyebrow}
|
|
53
59
|
</Text>
|
|
54
60
|
) : null}
|
|
55
|
-
{title ? <Heading level={compact ? 4 : 3}>{title}</Heading> : null}
|
|
61
|
+
{title ? <Heading level={recipe.compact ? 4 : 3}>{title}</Heading> : null}
|
|
56
62
|
{description ? (
|
|
57
63
|
<Text emphasis="muted" variant="bodySmall">
|
|
58
64
|
{description}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { ZoraComponentMeta } from '../../metadata';
|
|
2
2
|
import { CONTAINER_ALLOWED_CHILDREN } from '../../metadata/allowedChildren';
|
|
3
3
|
|
|
4
|
+
const themeAuthoring = {
|
|
5
|
+
authority: 'theme',
|
|
6
|
+
scope: 'component',
|
|
7
|
+
allowInstanceOverride: true,
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
4
10
|
export const cardMeta = {
|
|
5
11
|
name: 'Card',
|
|
6
12
|
category: 'component',
|
|
7
13
|
directManifestNode: true,
|
|
8
14
|
allowedChildren: [...CONTAINER_ALLOWED_CHILDREN],
|
|
9
|
-
blueprint: {
|
|
10
|
-
label: 'Card',
|
|
11
|
-
defaultProps: {
|
|
12
|
-
title: 'Card',
|
|
13
|
-
tone: 'default',
|
|
14
|
-
},
|
|
15
|
-
},
|
|
15
|
+
blueprint: { label: 'Card', defaultProps: { title: 'Card' } },
|
|
16
16
|
props: {
|
|
17
17
|
title: {
|
|
18
18
|
type: 'string',
|
|
@@ -37,15 +37,15 @@ export const cardMeta = {
|
|
|
37
37
|
category: 'Style',
|
|
38
38
|
label: 'Tone',
|
|
39
39
|
enum: ['default', 'subtle', 'outline'],
|
|
40
|
-
|
|
41
|
-
authoring: { authority: 'theme', scope: 'component' },
|
|
40
|
+
authoring: themeAuthoring,
|
|
42
41
|
},
|
|
43
|
-
compact: {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
compact: { type: 'boolean', category: 'Layout', label: 'Compact', authoring: themeAuthoring },
|
|
43
|
+
padding: { type: 'spacing', category: 'Layout', label: 'Padding', authoring: themeAuthoring },
|
|
44
|
+
radius: {
|
|
45
|
+
type: 'radius',
|
|
46
|
+
category: 'Style',
|
|
47
|
+
label: 'Corner radius',
|
|
48
|
+
authoring: themeAuthoring,
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
51
|
} as const satisfies ZoraComponentMeta;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { resolveCardThemeRecipe } from './resolveCardThemeRecipe';
|
|
4
|
+
|
|
5
|
+
describe('resolveCardThemeRecipe', () => {
|
|
6
|
+
test('preserves compact-aware padding when theme padding is unset', () => {
|
|
7
|
+
expect(
|
|
8
|
+
resolveCardThemeRecipe({ themeFields: { tone: 'default', compact: true, radius: 'l' } }),
|
|
9
|
+
).toEqual({
|
|
10
|
+
tone: 'default',
|
|
11
|
+
compact: true,
|
|
12
|
+
padding: 'm',
|
|
13
|
+
radius: 'l',
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('lets explicit instance props win over theme values', () => {
|
|
18
|
+
expect(
|
|
19
|
+
resolveCardThemeRecipe({
|
|
20
|
+
tone: 'outline',
|
|
21
|
+
compact: false,
|
|
22
|
+
padding: 'hero',
|
|
23
|
+
radius: 'pill',
|
|
24
|
+
themeFields: { tone: 'subtle', compact: true, padding: 'm', radius: 'l' },
|
|
25
|
+
}),
|
|
26
|
+
).toEqual({ tone: 'outline', compact: false, padding: 'hero', radius: 'pill' });
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
|
|
2
|
+
|
|
3
|
+
import type { ZoraCardTone } from '../../internal/recipes';
|
|
4
|
+
|
|
5
|
+
export function resolveCardThemeRecipe(input: {
|
|
6
|
+
readonly tone?: ZoraCardTone;
|
|
7
|
+
readonly compact?: boolean;
|
|
8
|
+
readonly padding?: string;
|
|
9
|
+
readonly radius?: string;
|
|
10
|
+
readonly themeFields: Readonly<Record<string, ThemeRecipeOverrideValue>>;
|
|
11
|
+
}) {
|
|
12
|
+
const tone = input.tone ?? readTone(input.themeFields.tone) ?? 'default';
|
|
13
|
+
const compact = input.compact ?? readBoolean(input.themeFields.compact) ?? false;
|
|
14
|
+
return {
|
|
15
|
+
tone,
|
|
16
|
+
compact,
|
|
17
|
+
padding: input.padding ?? readString(input.themeFields.padding) ?? (compact ? 'm' : 'l'),
|
|
18
|
+
radius: input.radius ?? readString(input.themeFields.radius) ?? 'l',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readTone(value: ThemeRecipeOverrideValue | undefined): ZoraCardTone | undefined {
|
|
23
|
+
if (value === 'default' || value === 'subtle' || value === 'outline') return value;
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readBoolean(value: ThemeRecipeOverrideValue | undefined): boolean | undefined {
|
|
28
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readString(value: ThemeRecipeOverrideValue | undefined): string | undefined {
|
|
32
|
+
return typeof value === 'string' ? value : undefined;
|
|
33
|
+
}
|
|
@@ -9,15 +9,15 @@ const headingSource = readFileSync(join(import.meta.dir, 'heading', 'Heading.tsx
|
|
|
9
9
|
describe('content fallback ownership', () => {
|
|
10
10
|
it('keeps Text free of translation context coupling', () => {
|
|
11
11
|
expect(textSource).not.toMatch(/useTranslationContext/);
|
|
12
|
-
expect(textSource).toMatch(/if \(children !== undefined\)
|
|
13
|
-
expect(textSource).toMatch(/if \(text !== undefined\)
|
|
12
|
+
expect(textSource).toMatch(/if \(children !== undefined\) return children;/);
|
|
13
|
+
expect(textSource).toMatch(/if \(text !== undefined\) return text;/);
|
|
14
14
|
expect(textSource).toMatch(/return i18nKey;/);
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
it('keeps Heading free of translation context coupling', () => {
|
|
18
18
|
expect(headingSource).not.toMatch(/useTranslationContext/);
|
|
19
|
-
expect(headingSource).toMatch(/if \(children !== undefined\)
|
|
20
|
-
expect(headingSource).toMatch(/if \(text !== undefined\)
|
|
19
|
+
expect(headingSource).toMatch(/if \(children !== undefined\) return children;/);
|
|
20
|
+
expect(headingSource).toMatch(/if \(text !== undefined\) return text;/);
|
|
21
21
|
expect(headingSource).toMatch(/return i18nKey;/);
|
|
22
22
|
});
|
|
23
23
|
});
|
|
@@ -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 { resolveHeadingRecipe, resolveHeadingSizeFromLevel } from './resolveHeadingRecipe';
|
|
9
|
+
import { resolveHeadingThemeRecipe } from './resolveHeadingThemeRecipe';
|
|
8
10
|
import type { HeadingProps } from './types';
|
|
9
11
|
|
|
10
12
|
const headingLayoutStyle = {
|
|
@@ -24,23 +26,10 @@ function resolveHeadingContent({
|
|
|
24
26
|
children,
|
|
25
27
|
text,
|
|
26
28
|
i18nKey,
|
|
27
|
-
}: {
|
|
28
|
-
children
|
|
29
|
-
text
|
|
30
|
-
i18nKey
|
|
31
|
-
}): React.ReactNode {
|
|
32
|
-
if (children !== undefined) {
|
|
33
|
-
return children;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (text !== undefined) {
|
|
37
|
-
return text;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (!i18nKey) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
29
|
+
}: Pick<HeadingProps, 'children' | 'text' | 'i18nKey'>) {
|
|
30
|
+
if (children !== undefined) return children;
|
|
31
|
+
if (text !== undefined) return text;
|
|
32
|
+
if (i18nKey === undefined || i18nKey === '') return null;
|
|
44
33
|
return i18nKey;
|
|
45
34
|
}
|
|
46
35
|
|
|
@@ -53,10 +42,10 @@ function HeadingInner({
|
|
|
53
42
|
level = 2,
|
|
54
43
|
size,
|
|
55
44
|
color,
|
|
56
|
-
emphasis
|
|
45
|
+
emphasis,
|
|
57
46
|
align,
|
|
58
47
|
weight,
|
|
59
|
-
italic
|
|
48
|
+
italic,
|
|
60
49
|
numberOfLines,
|
|
61
50
|
ellipsizeMode,
|
|
62
51
|
selectable,
|
|
@@ -70,16 +59,17 @@ function HeadingInner({
|
|
|
70
59
|
}: HeadingProps) {
|
|
71
60
|
const { theme } = useZoraTheme();
|
|
72
61
|
const { breakpoint } = useResponsiveRuntime();
|
|
62
|
+
const themeRecipe = resolveHeadingThemeRecipe(useZoraThemeRecipe('Heading'));
|
|
73
63
|
const content = resolveHeadingContent({ children, text, i18nKey });
|
|
74
|
-
const resolvedSize =
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
|
|
64
|
+
const resolvedSize =
|
|
65
|
+
resolveResponsive(size ?? themeRecipe.size, breakpoint) ?? resolveHeadingSizeFromLevel(level);
|
|
66
|
+
const resolvedColor = resolveResponsive(color ?? themeRecipe.color, breakpoint);
|
|
67
|
+
const resolvedEmphasis =
|
|
68
|
+
resolveResponsive(emphasis ?? themeRecipe.emphasis, breakpoint) ?? 'default';
|
|
69
|
+
const resolvedAlign = resolveResponsive(align ?? themeRecipe.align, breakpoint);
|
|
70
|
+
const resolvedWeight = resolveResponsive(weight ?? themeRecipe.weight, breakpoint);
|
|
79
71
|
|
|
80
|
-
if (content === null
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
72
|
+
if (content === null) return null;
|
|
83
73
|
|
|
84
74
|
return (
|
|
85
75
|
<ReactNativeText
|
|
@@ -95,7 +85,7 @@ function HeadingInner({
|
|
|
95
85
|
headingLayoutStyle,
|
|
96
86
|
resolveHeadingRecipe(theme, {
|
|
97
87
|
align: resolvedAlign,
|
|
98
|
-
italic,
|
|
88
|
+
italic: italic ?? themeRecipe.italic ?? false,
|
|
99
89
|
level,
|
|
100
90
|
size: resolvedSize,
|
|
101
91
|
color: resolvedColor,
|