@ankhorage/surface 1.0.1 → 1.0.3
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 +14 -0
- package/dist/primitives/icon/resolveExpoIconComponent.d.ts.map +1 -1
- package/dist/primitives/icon/resolveExpoIconComponent.js +11 -1
- package/dist/primitives/icon/resolveExpoIconComponent.js.map +1 -1
- package/dist/theme/colorEngine.d.ts.map +1 -1
- package/dist/theme/colorEngine.js +5 -11
- package/dist/theme/colorEngine.js.map +1 -1
- package/package.json +2 -2
- package/src/primitives/icon/resolveExpoIconComponent.test.ts +16 -0
- package/src/primitives/icon/resolveExpoIconComponent.ts +14 -1
- package/src/theme/colorEngine.test.ts +13 -0
- package/src/theme/colorEngine.ts +5 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @ankhorage/surface
|
|
2
2
|
|
|
3
|
+
## 1.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c8c99de: Support canonical icon provider aliases for Expo vector icons.
|
|
8
|
+
|
|
9
|
+
Surface now resolves provider strings such as `material-community` and `material-community-icons` to Expo's `MaterialCommunityIcons` export, so serialized route icons can use stable provider identifiers without falling back to Ionicons.
|
|
10
|
+
|
|
11
|
+
## 1.0.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- de83ae5: Consume `@ankhorage/color-theory` semantic status helpers for danger/success/warning roles.
|
|
16
|
+
|
|
3
17
|
## 1.0.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveExpoIconComponent.d.ts","sourceRoot":"","sources":["../../../src/primitives/icon/resolveExpoIconComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,WAAW,CAAC;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"resolveExpoIconComponent.d.ts","sourceRoot":"","sources":["../../../src/primitives/icon/resolveExpoIconComponent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,WAAW,CAAC;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAMH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAQ5E"}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import * as ExpoIcons from '@expo/vector-icons';
|
|
2
2
|
import {} from 'react-native';
|
|
3
|
+
const EXPO_ICON_PROVIDER_ALIASES = {
|
|
4
|
+
'material-community': 'MaterialCommunityIcons',
|
|
5
|
+
};
|
|
3
6
|
export function resolveExpoIconComponent(provider) {
|
|
4
|
-
const
|
|
7
|
+
const normalizedProvider = resolveExpoIconProviderName(provider);
|
|
8
|
+
const candidate = ExpoIcons[normalizedProvider];
|
|
5
9
|
if (typeof candidate === 'function') {
|
|
6
10
|
return candidate;
|
|
7
11
|
}
|
|
8
12
|
return ExpoIcons.Ionicons;
|
|
9
13
|
}
|
|
14
|
+
function resolveExpoIconProviderName(provider) {
|
|
15
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
16
|
+
return normalizedProvider in EXPO_ICON_PROVIDER_ALIASES
|
|
17
|
+
? EXPO_ICON_PROVIDER_ALIASES[normalizedProvider]
|
|
18
|
+
: provider;
|
|
19
|
+
}
|
|
10
20
|
//# sourceMappingURL=resolveExpoIconComponent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveExpoIconComponent.js","sourceRoot":"","sources":["../../../src/primitives/icon/resolveExpoIconComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAAkC,MAAM,cAAc,CAAC;AAU9D,MAAM,UAAU,wBAAwB,CAAC,QAAgB;IACvD,MAAM,SAAS,GAAI,SAAqC,CAAC,
|
|
1
|
+
{"version":3,"file":"resolveExpoIconComponent.js","sourceRoot":"","sources":["../../../src/primitives/icon/resolveExpoIconComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAAkC,MAAM,cAAc,CAAC;AAU9D,MAAM,0BAA0B,GAAG;IACjC,oBAAoB,EAAE,wBAAwB;CACtC,CAAC;AAEX,MAAM,UAAU,wBAAwB,CAAC,QAAgB;IACvD,MAAM,kBAAkB,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IACjE,MAAM,SAAS,GAAI,SAAqC,CAAC,kBAAkB,CAAC,CAAC;IAC7E,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,SAA8B,CAAC;IACxC,CAAC;IAED,OAAO,SAAS,CAAC,QAA6B,CAAC;AACjD,CAAC;AAED,SAAS,2BAA2B,CAAC,QAAgB;IACnD,MAAM,kBAAkB,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEzD,OAAO,kBAAkB,IAAI,0BAA0B;QACrD,CAAC,CAAC,0BAA0B,CAAC,kBAA6D,CAAC;QAC3F,CAAC,CAAC,QAAQ,CAAC;AACf,CAAC","sourcesContent":["import * as ExpoIcons from '@expo/vector-icons';\nimport type React from 'react';\nimport { type StyleProp, type TextStyle } from 'react-native';\n\nexport type ExpoIconComponent = React.ElementType<{\n color?: string;\n name?: string;\n size?: number;\n style?: StyleProp<TextStyle>;\n testID?: string;\n}>;\n\nconst EXPO_ICON_PROVIDER_ALIASES = {\n 'material-community': 'MaterialCommunityIcons',\n} as const;\n\nexport function resolveExpoIconComponent(provider: string): ExpoIconComponent {\n const normalizedProvider = resolveExpoIconProviderName(provider);\n const candidate = (ExpoIcons as Record<string, unknown>)[normalizedProvider];\n if (typeof candidate === 'function') {\n return candidate as ExpoIconComponent;\n }\n\n return ExpoIcons.Ionicons as ExpoIconComponent;\n}\n\nfunction resolveExpoIconProviderName(provider: string): string {\n const normalizedProvider = provider.trim().toLowerCase();\n\n return normalizedProvider in EXPO_ICON_PROVIDER_ALIASES\n ? EXPO_ICON_PROVIDER_ALIASES[normalizedProvider as keyof typeof EXPO_ICON_PROVIDER_ALIASES]\n : provider;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colorEngine.d.ts","sourceRoot":"","sources":["../../src/theme/colorEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,wBAAwB,EACxB,sBAAsB,EACtB,QAAQ,EACR,yBAAyB,EACzB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAOjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAOV,cAAc,EACf,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"colorEngine.d.ts","sourceRoot":"","sources":["../../src/theme/colorEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,wBAAwB,EACxB,sBAAsB,EACtB,QAAQ,EACR,yBAAyB,EACzB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAOjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAOV,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AAEzE,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,wBAAwB,EACnC,UAAU,EAAE,yBAAyB,GACpC,qBAAqB,CAUvB;AA8DD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE,OAAO,GAAG,MAAgB,GAC/B;IACD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,SAAS,EAAE,cAAc,CAAC;CAC3B,CAoFA"}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Fixed hex values for semantic status colors (not theme-generated)
|
|
3
|
-
const DANGER_HEX = parseHexColorOrThrow('#ef4444');
|
|
4
|
-
const SUCCESS_HEX = parseHexColorOrThrow('#22c55e');
|
|
5
|
-
const WARNING_HEX = parseHexColorOrThrow('#f59e0b');
|
|
1
|
+
import { createDefaultSemanticStatusSwatches, generateThemeModeColors, getReadableForeground, parseHexColorOrThrow, } from '@ankhorage/color-theory';
|
|
6
2
|
export function resolveSemanticColors(generated, references) {
|
|
7
3
|
return Object.fromEntries(Object.entries(references).map(([token, ref]) => {
|
|
8
4
|
const swatch = generated.swatches[ref.role];
|
|
@@ -82,12 +78,10 @@ export function generatePalette(config, mode = 'light') {
|
|
|
82
78
|
const secondarySwatch = swatches.secondary ?? swatches.primary;
|
|
83
79
|
const tertiarySwatch = swatches.tertiary ?? swatches.primary;
|
|
84
80
|
const quaternarySwatch = swatches.quaternary ?? swatches.primary;
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const success = buildRoleSemantics(successSwatch, isDark);
|
|
90
|
-
const warning = buildRoleSemantics(warningSwatch, isDark);
|
|
81
|
+
const semanticStatusSwatches = createDefaultSemanticStatusSwatches().swatches;
|
|
82
|
+
const danger = buildRoleSemantics(semanticStatusSwatches.danger, isDark);
|
|
83
|
+
const success = buildRoleSemantics(semanticStatusSwatches.success, isDark);
|
|
84
|
+
const warning = buildRoleSemantics(semanticStatusSwatches.warning, isDark);
|
|
91
85
|
const surfaceSemantics = {
|
|
92
86
|
default: neutral.surface,
|
|
93
87
|
subtle: neutral.bgSubtle,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colorEngine.js","sourceRoot":"","sources":["../../src/theme/colorEngine.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ColorSwatch,\n GeneratedThemeModeColors,\n GeneratedThemeSwatches,\n HexColor,\n SemanticColorReferenceMap,\n SemanticColorToken,\n} from '@ankhorage/color-theory';\nimport {\n generateColorSwatch,\n generateThemeModeColors,\n getReadableForeground,\n parseHexColorOrThrow,\n} from '@ankhorage/color-theory';\nimport type { ThemeConfig } from '@ankhorage/contracts';\n\nimport type {\n ActionSemantics,\n BorderSemantics,\n ContentSemantics,\n NeutralSemantics,\n RoleSemantics,\n SurfaceSemantics,\n ThemeSemantics,\n} from './types';\n\n// Fixed hex values for semantic status colors (not theme-generated)\nconst DANGER_HEX = parseHexColorOrThrow('#ef4444');\nconst SUCCESS_HEX = parseHexColorOrThrow('#22c55e');\nconst WARNING_HEX = parseHexColorOrThrow('#f59e0b');\n\n/**\n * Surface semantic resolver: maps color-theory SemanticColorToken references\n * to hex values from the generated swatches.\n */\nexport type SurfaceSemanticColors = Record<SemanticColorToken, HexColor>;\n\nexport function resolveSemanticColors(\n generated: GeneratedThemeModeColors,\n references: SemanticColorReferenceMap,\n): SurfaceSemanticColors {\n return Object.fromEntries(\n Object.entries(references).map(([token, ref]) => {\n const swatch = generated.swatches[ref.role];\n if (!swatch) {\n throw new Error(`Missing swatch for role '${ref.role}' (token: '${token}')`);\n }\n return [token, swatch[ref.step]];\n }),\n ) as SurfaceSemanticColors;\n}\n\nfunction buildNeutralSemantics(neutralSwatch: ColorSwatch, isDark: boolean): NeutralSemantics {\n if (isDark) {\n return {\n bg: neutralSwatch[950],\n bgSubtle: neutralSwatch[900],\n surface: neutralSwatch[900],\n surfaceHover: neutralSwatch[800],\n surfaceActive: neutralSwatch[700],\n border: neutralSwatch[800],\n borderStrong: neutralSwatch[600],\n divider: neutralSwatch[800],\n text: neutralSwatch[50],\n textMuted: neutralSwatch[200],\n textSubtle: neutralSwatch[300],\n };\n }\n return {\n bg: neutralSwatch[50],\n bgSubtle: neutralSwatch[100],\n surface: neutralSwatch[100],\n surfaceHover: neutralSwatch[200],\n surfaceActive: neutralSwatch[300],\n border: neutralSwatch[200],\n borderStrong: neutralSwatch[300],\n divider: neutralSwatch[200],\n text: neutralSwatch[900],\n textMuted: neutralSwatch[700],\n textSubtle: neutralSwatch[600],\n };\n}\n\nfunction buildRoleSemantics(swatch: ColorSwatch, isDark: boolean): RoleSemantics {\n const base = swatch[500];\n const { foreground: onSolidText } = getReadableForeground(base);\n\n if (isDark) {\n return {\n base,\n hover: swatch[400],\n strong: swatch[300],\n softBg: swatch[900],\n softHover: swatch[800],\n softActive: swatch[700],\n outline: swatch[500],\n onSolidText,\n };\n }\n\n return {\n base,\n hover: swatch[600],\n strong: swatch[700],\n softBg: swatch[100],\n softHover: swatch[200],\n softActive: swatch[300],\n outline: swatch[400],\n onSolidText,\n };\n}\n\nexport function generatePalette(\n config: ThemeConfig,\n mode: 'light' | 'dark' = 'light',\n): {\n colors: Record<string, string>;\n swatches: GeneratedThemeSwatches;\n semantics: ThemeSemantics;\n} {\n const modeConfig = mode === 'dark' ? config.dark : config.light;\n const isDark = mode === 'dark';\n\n // Throws deterministically on invalid primary color\n parseHexColorOrThrow(modeConfig.primaryColor);\n\n const generated = generateThemeModeColors(modeConfig);\n\n const { swatches } = generated;\n const neutralSwatch = swatches.neutral;\n\n const neutral = buildNeutralSemantics(neutralSwatch, isDark);\n const brand = buildRoleSemantics(swatches.primary, isDark);\n\n // Fallback to primary swatch for missing ordinal roles\n const secondarySwatch = swatches.secondary ?? swatches.primary;\n const tertiarySwatch = swatches.tertiary ?? swatches.primary;\n const quaternarySwatch = swatches.quaternary ?? swatches.primary;\n\n const dangerSwatch = generateColorSwatch(DANGER_HEX).swatch;\n const successSwatch = generateColorSwatch(SUCCESS_HEX).swatch;\n const warningSwatch = generateColorSwatch(WARNING_HEX).swatch;\n\n const danger = buildRoleSemantics(dangerSwatch, isDark);\n const success = buildRoleSemantics(successSwatch, isDark);\n const warning = buildRoleSemantics(warningSwatch, isDark);\n\n const surfaceSemantics: SurfaceSemantics = {\n default: neutral.surface,\n subtle: neutral.bgSubtle,\n raised: neutral.surface,\n };\n\n const content: ContentSemantics = {\n default: neutral.text,\n muted: neutral.textMuted,\n subtle: neutral.textSubtle,\n inverse: brand.onSolidText,\n };\n\n const border: BorderSemantics = {\n default: neutral.border,\n strong: neutral.borderStrong,\n focus: brand.outline,\n };\n\n const action: ActionSemantics = {\n primary: brand,\n neutral: buildRoleSemantics(neutralSwatch, isDark),\n danger,\n };\n\n const colors: Record<string, string> = {\n primary: brand.base,\n secondary: secondarySwatch[500],\n accent: tertiarySwatch[500],\n highlight: quaternarySwatch[500],\n background: neutral.bg,\n surface: neutral.surface,\n text: neutral.text,\n textSecondary: neutral.textMuted,\n border: neutral.border,\n error: danger.base,\n success: success.base,\n warning: warning.base,\n };\n\n return {\n colors,\n swatches,\n semantics: {\n neutral,\n brand,\n secondary: buildRoleSemantics(secondarySwatch, isDark),\n accent: buildRoleSemantics(tertiarySwatch, isDark),\n highlight: buildRoleSemantics(quaternarySwatch, isDark),\n danger,\n success,\n warning,\n surface: surfaceSemantics,\n content,\n border,\n action,\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"colorEngine.js","sourceRoot":"","sources":["../../src/theme/colorEngine.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ColorSwatch,\n GeneratedThemeModeColors,\n GeneratedThemeSwatches,\n HexColor,\n SemanticColorReferenceMap,\n SemanticColorToken,\n} from '@ankhorage/color-theory';\nimport {\n createDefaultSemanticStatusSwatches,\n generateThemeModeColors,\n getReadableForeground,\n parseHexColorOrThrow,\n} from '@ankhorage/color-theory';\nimport type { ThemeConfig } from '@ankhorage/contracts';\n\nimport type {\n ActionSemantics,\n BorderSemantics,\n ContentSemantics,\n NeutralSemantics,\n RoleSemantics,\n SurfaceSemantics,\n ThemeSemantics,\n} from './types';\n\n/**\n * Surface semantic resolver: maps color-theory SemanticColorToken references\n * to hex values from the generated swatches.\n */\nexport type SurfaceSemanticColors = Record<SemanticColorToken, HexColor>;\n\nexport function resolveSemanticColors(\n generated: GeneratedThemeModeColors,\n references: SemanticColorReferenceMap,\n): SurfaceSemanticColors {\n return Object.fromEntries(\n Object.entries(references).map(([token, ref]) => {\n const swatch = generated.swatches[ref.role];\n if (!swatch) {\n throw new Error(`Missing swatch for role '${ref.role}' (token: '${token}')`);\n }\n return [token, swatch[ref.step]];\n }),\n ) as SurfaceSemanticColors;\n}\n\nfunction buildNeutralSemantics(neutralSwatch: ColorSwatch, isDark: boolean): NeutralSemantics {\n if (isDark) {\n return {\n bg: neutralSwatch[950],\n bgSubtle: neutralSwatch[900],\n surface: neutralSwatch[900],\n surfaceHover: neutralSwatch[800],\n surfaceActive: neutralSwatch[700],\n border: neutralSwatch[800],\n borderStrong: neutralSwatch[600],\n divider: neutralSwatch[800],\n text: neutralSwatch[50],\n textMuted: neutralSwatch[200],\n textSubtle: neutralSwatch[300],\n };\n }\n return {\n bg: neutralSwatch[50],\n bgSubtle: neutralSwatch[100],\n surface: neutralSwatch[100],\n surfaceHover: neutralSwatch[200],\n surfaceActive: neutralSwatch[300],\n border: neutralSwatch[200],\n borderStrong: neutralSwatch[300],\n divider: neutralSwatch[200],\n text: neutralSwatch[900],\n textMuted: neutralSwatch[700],\n textSubtle: neutralSwatch[600],\n };\n}\n\nfunction buildRoleSemantics(swatch: ColorSwatch, isDark: boolean): RoleSemantics {\n const base = swatch[500];\n const { foreground: onSolidText } = getReadableForeground(base);\n\n if (isDark) {\n return {\n base,\n hover: swatch[400],\n strong: swatch[300],\n softBg: swatch[900],\n softHover: swatch[800],\n softActive: swatch[700],\n outline: swatch[500],\n onSolidText,\n };\n }\n\n return {\n base,\n hover: swatch[600],\n strong: swatch[700],\n softBg: swatch[100],\n softHover: swatch[200],\n softActive: swatch[300],\n outline: swatch[400],\n onSolidText,\n };\n}\n\nexport function generatePalette(\n config: ThemeConfig,\n mode: 'light' | 'dark' = 'light',\n): {\n colors: Record<string, string>;\n swatches: GeneratedThemeSwatches;\n semantics: ThemeSemantics;\n} {\n const modeConfig = mode === 'dark' ? config.dark : config.light;\n const isDark = mode === 'dark';\n\n // Throws deterministically on invalid primary color\n parseHexColorOrThrow(modeConfig.primaryColor);\n\n const generated = generateThemeModeColors(modeConfig);\n\n const { swatches } = generated;\n const neutralSwatch = swatches.neutral;\n\n const neutral = buildNeutralSemantics(neutralSwatch, isDark);\n const brand = buildRoleSemantics(swatches.primary, isDark);\n\n // Fallback to primary swatch for missing ordinal roles\n const secondarySwatch = swatches.secondary ?? swatches.primary;\n const tertiarySwatch = swatches.tertiary ?? swatches.primary;\n const quaternarySwatch = swatches.quaternary ?? swatches.primary;\n\n const semanticStatusSwatches = createDefaultSemanticStatusSwatches().swatches;\n\n const danger = buildRoleSemantics(semanticStatusSwatches.danger, isDark);\n const success = buildRoleSemantics(semanticStatusSwatches.success, isDark);\n const warning = buildRoleSemantics(semanticStatusSwatches.warning, isDark);\n\n const surfaceSemantics: SurfaceSemantics = {\n default: neutral.surface,\n subtle: neutral.bgSubtle,\n raised: neutral.surface,\n };\n\n const content: ContentSemantics = {\n default: neutral.text,\n muted: neutral.textMuted,\n subtle: neutral.textSubtle,\n inverse: brand.onSolidText,\n };\n\n const border: BorderSemantics = {\n default: neutral.border,\n strong: neutral.borderStrong,\n focus: brand.outline,\n };\n\n const action: ActionSemantics = {\n primary: brand,\n neutral: buildRoleSemantics(neutralSwatch, isDark),\n danger,\n };\n\n const colors: Record<string, string> = {\n primary: brand.base,\n secondary: secondarySwatch[500],\n accent: tertiarySwatch[500],\n highlight: quaternarySwatch[500],\n background: neutral.bg,\n surface: neutral.surface,\n text: neutral.text,\n textSecondary: neutral.textMuted,\n border: neutral.border,\n error: danger.base,\n success: success.base,\n warning: warning.base,\n };\n\n return {\n colors,\n swatches,\n semantics: {\n neutral,\n brand,\n secondary: buildRoleSemantics(secondarySwatch, isDark),\n accent: buildRoleSemantics(tertiarySwatch, isDark),\n highlight: buildRoleSemantics(quaternarySwatch, isDark),\n danger,\n success,\n warning,\n surface: surfaceSemantics,\n content,\n border,\n action,\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/surface",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"description": "Stable UI foundation for React Native and React Native Web.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/surface#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@ankhorage/color-theory": "^0.0.
|
|
44
|
+
"@ankhorage/color-theory": "^0.0.4",
|
|
45
45
|
"@ankhorage/contracts": "^1.1.0"
|
|
46
46
|
},
|
|
47
47
|
"files": [
|
|
@@ -2,11 +2,13 @@ import { describe, expect, mock, test } from 'bun:test';
|
|
|
2
2
|
|
|
3
3
|
const Ionicons = () => null;
|
|
4
4
|
const MaterialIcons = () => null;
|
|
5
|
+
const MaterialCommunityIcons = () => null;
|
|
5
6
|
|
|
6
7
|
describe('resolveExpoIconComponent', () => {
|
|
7
8
|
test('returns the requested Expo icon family when it exists', async () => {
|
|
8
9
|
await mock.module('@expo/vector-icons', () => ({
|
|
9
10
|
Ionicons,
|
|
11
|
+
MaterialCommunityIcons,
|
|
10
12
|
MaterialIcons,
|
|
11
13
|
}));
|
|
12
14
|
|
|
@@ -14,11 +16,25 @@ describe('resolveExpoIconComponent', () => {
|
|
|
14
16
|
|
|
15
17
|
expect(resolveExpoIconComponent('Ionicons')).toBe(Ionicons);
|
|
16
18
|
expect(resolveExpoIconComponent('MaterialIcons')).toBe(MaterialIcons);
|
|
19
|
+
expect(resolveExpoIconComponent('MaterialCommunityIcons')).toBe(MaterialCommunityIcons);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('resolves the canonical material-community provider id', async () => {
|
|
23
|
+
await mock.module('@expo/vector-icons', () => ({
|
|
24
|
+
Ionicons,
|
|
25
|
+
MaterialCommunityIcons,
|
|
26
|
+
MaterialIcons,
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
const { resolveExpoIconComponent } = await import('./resolveExpoIconComponent');
|
|
30
|
+
|
|
31
|
+
expect(resolveExpoIconComponent('material-community')).toBe(MaterialCommunityIcons);
|
|
17
32
|
});
|
|
18
33
|
|
|
19
34
|
test('falls back to Ionicons when the provider is unknown', async () => {
|
|
20
35
|
await mock.module('@expo/vector-icons', () => ({
|
|
21
36
|
Ionicons,
|
|
37
|
+
MaterialCommunityIcons,
|
|
22
38
|
MaterialIcons,
|
|
23
39
|
}));
|
|
24
40
|
|
|
@@ -10,11 +10,24 @@ export type ExpoIconComponent = React.ElementType<{
|
|
|
10
10
|
testID?: string;
|
|
11
11
|
}>;
|
|
12
12
|
|
|
13
|
+
const EXPO_ICON_PROVIDER_ALIASES = {
|
|
14
|
+
'material-community': 'MaterialCommunityIcons',
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
13
17
|
export function resolveExpoIconComponent(provider: string): ExpoIconComponent {
|
|
14
|
-
const
|
|
18
|
+
const normalizedProvider = resolveExpoIconProviderName(provider);
|
|
19
|
+
const candidate = (ExpoIcons as Record<string, unknown>)[normalizedProvider];
|
|
15
20
|
if (typeof candidate === 'function') {
|
|
16
21
|
return candidate as ExpoIconComponent;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
return ExpoIcons.Ionicons as ExpoIconComponent;
|
|
20
25
|
}
|
|
26
|
+
|
|
27
|
+
function resolveExpoIconProviderName(provider: string): string {
|
|
28
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
29
|
+
|
|
30
|
+
return normalizedProvider in EXPO_ICON_PROVIDER_ALIASES
|
|
31
|
+
? EXPO_ICON_PROVIDER_ALIASES[normalizedProvider as keyof typeof EXPO_ICON_PROVIDER_ALIASES]
|
|
32
|
+
: provider;
|
|
33
|
+
}
|
|
@@ -71,6 +71,19 @@ describe('colorEngine', () => {
|
|
|
71
71
|
expect(semantics.content.inverse).toBe(semantics.brand.onSolidText);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
it('uses the color-theory default semantic status seeds', () => {
|
|
75
|
+
const light = generatePalette(mockConfig, 'light');
|
|
76
|
+
const dark = generatePalette(mockConfig, 'dark');
|
|
77
|
+
|
|
78
|
+
expect(light.semantics.danger.base).toBe('#ef4444');
|
|
79
|
+
expect(light.semantics.success.base).toBe('#22c55e');
|
|
80
|
+
expect(light.semantics.warning.base).toBe('#f59e0b');
|
|
81
|
+
|
|
82
|
+
expect(dark.semantics.danger.base).toBe('#ef4444');
|
|
83
|
+
expect(dark.semantics.success.base).toBe('#22c55e');
|
|
84
|
+
expect(dark.semantics.warning.base).toBe('#f59e0b');
|
|
85
|
+
});
|
|
86
|
+
|
|
74
87
|
it('uses mode-aware role semantics for dark mode soft states', () => {
|
|
75
88
|
const light = generatePalette(mockConfig, 'light');
|
|
76
89
|
const dark = generatePalette(mockConfig, 'dark');
|
package/src/theme/colorEngine.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
SemanticColorToken,
|
|
8
8
|
} from '@ankhorage/color-theory';
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
createDefaultSemanticStatusSwatches,
|
|
11
11
|
generateThemeModeColors,
|
|
12
12
|
getReadableForeground,
|
|
13
13
|
parseHexColorOrThrow,
|
|
@@ -24,11 +24,6 @@ import type {
|
|
|
24
24
|
ThemeSemantics,
|
|
25
25
|
} from './types';
|
|
26
26
|
|
|
27
|
-
// Fixed hex values for semantic status colors (not theme-generated)
|
|
28
|
-
const DANGER_HEX = parseHexColorOrThrow('#ef4444');
|
|
29
|
-
const SUCCESS_HEX = parseHexColorOrThrow('#22c55e');
|
|
30
|
-
const WARNING_HEX = parseHexColorOrThrow('#f59e0b');
|
|
31
|
-
|
|
32
27
|
/**
|
|
33
28
|
* Surface semantic resolver: maps color-theory SemanticColorToken references
|
|
34
29
|
* to hex values from the generated swatches.
|
|
@@ -137,13 +132,11 @@ export function generatePalette(
|
|
|
137
132
|
const tertiarySwatch = swatches.tertiary ?? swatches.primary;
|
|
138
133
|
const quaternarySwatch = swatches.quaternary ?? swatches.primary;
|
|
139
134
|
|
|
140
|
-
const
|
|
141
|
-
const successSwatch = generateColorSwatch(SUCCESS_HEX).swatch;
|
|
142
|
-
const warningSwatch = generateColorSwatch(WARNING_HEX).swatch;
|
|
135
|
+
const semanticStatusSwatches = createDefaultSemanticStatusSwatches().swatches;
|
|
143
136
|
|
|
144
|
-
const danger = buildRoleSemantics(
|
|
145
|
-
const success = buildRoleSemantics(
|
|
146
|
-
const warning = buildRoleSemantics(
|
|
137
|
+
const danger = buildRoleSemantics(semanticStatusSwatches.danger, isDark);
|
|
138
|
+
const success = buildRoleSemantics(semanticStatusSwatches.success, isDark);
|
|
139
|
+
const warning = buildRoleSemantics(semanticStatusSwatches.warning, isDark);
|
|
147
140
|
|
|
148
141
|
const surfaceSemantics: SurfaceSemantics = {
|
|
149
142
|
default: neutral.surface,
|