@ankhorage/surface 2.0.1 → 2.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 +12 -0
- package/README.md +692 -47
- package/dist/components/menu/Menu.d.ts +1 -1
- package/dist/components/menu/Menu.d.ts.map +1 -1
- package/dist/components/menu/Menu.js +50 -32
- package/dist/components/menu/Menu.js.map +1 -1
- package/dist/components/menu/index.d.ts +1 -1
- package/dist/components/menu/index.d.ts.map +1 -1
- package/dist/components/menu/index.js.map +1 -1
- package/dist/components/menu/navigation.d.ts +1 -1
- package/dist/components/menu/navigation.d.ts.map +1 -1
- package/dist/components/menu/navigation.js.map +1 -1
- package/dist/components/menu/types.d.ts +11 -5
- package/dist/components/menu/types.d.ts.map +1 -1
- package/dist/components/menu/types.js.map +1 -1
- package/dist/components/tooltip/Tooltip.js +1 -1
- package/dist/components/tooltip/Tooltip.js.map +1 -1
- package/dist/examples/DocsExamples.d.ts +3 -2
- package/dist/examples/DocsExamples.d.ts.map +1 -1
- package/dist/examples/DocsExamples.js +61 -37
- package/dist/examples/DocsExamples.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/theme/colorEngine.d.ts.map +1 -1
- package/dist/theme/colorEngine.js +4 -1
- package/dist/theme/colorEngine.js.map +1 -1
- package/dist/theme/types.d.ts +1 -0
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js.map +1 -1
- package/package.json +5 -2
- package/src/components/menu/Menu.tsx +71 -36
- package/src/components/menu/index.ts +1 -1
- package/src/components/menu/navigation.ts +1 -1
- package/src/components/menu/types.ts +12 -5
- package/src/components/tooltip/Tooltip.tsx +1 -1
- package/src/examples/DocsExamples.tsx +74 -60
- package/src/index.ts +1 -1
- package/src/theme/colorEngine.test.ts +3 -2
- package/src/theme/colorEngine.ts +4 -2
- package/src/theme/inverseSemantics.test.ts +48 -0
- package/src/theme/types.ts +1 -0
|
@@ -85,16 +85,19 @@ export function generatePalette(config, mode = 'light') {
|
|
|
85
85
|
// Keep destructive action and feedback error aligned until a dedicated error status swatch is introduced.
|
|
86
86
|
const error = danger;
|
|
87
87
|
const info = buildRoleSemantics(secondarySwatch, isDark);
|
|
88
|
+
const inverseSurface = isDark ? neutralSwatch[50] : neutralSwatch[900];
|
|
89
|
+
const inverseOnSurface = isDark ? neutralSwatch[900] : neutralSwatch[50];
|
|
88
90
|
const surfaceSemantics = {
|
|
89
91
|
default: neutral.surface,
|
|
90
92
|
subtle: neutral.bgSubtle,
|
|
91
93
|
raised: neutral.surface,
|
|
94
|
+
inverse: inverseSurface,
|
|
92
95
|
};
|
|
93
96
|
const content = {
|
|
94
97
|
default: neutral.text,
|
|
95
98
|
muted: neutral.textMuted,
|
|
96
99
|
subtle: neutral.textSubtle,
|
|
97
|
-
inverse:
|
|
100
|
+
inverse: inverseOnSurface,
|
|
98
101
|
};
|
|
99
102
|
const border = {
|
|
100
103
|
default: neutral.border,
|
|
@@ -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 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 // Keep destructive action and feedback error aligned until a dedicated error status swatch is introduced.\n const error = danger;\n const info = buildRoleSemantics(secondarySwatch, 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 tertiary: tertiarySwatch[500],\n quaternary: quaternarySwatch[500],\n background: neutral.bg,\n surface: neutral.surface,\n text: neutral.text,\n textSecondary: neutral.textMuted,\n border: neutral.border,\n error: error.base,\n success: success.base,\n warning: warning.base,\n info: info.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 error,\n info,\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 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 // Keep destructive action and feedback error aligned until a dedicated error status swatch is introduced.\n const error = danger;\n const info = buildRoleSemantics(secondarySwatch, isDark);\n const inverseSurface = isDark ? neutralSwatch[50] : neutralSwatch[900];\n const inverseOnSurface = isDark ? neutralSwatch[900] : neutralSwatch[50];\n\n const surfaceSemantics: SurfaceSemantics = {\n default: neutral.surface,\n subtle: neutral.bgSubtle,\n raised: neutral.surface,\n inverse: inverseSurface,\n };\n\n const content: ContentSemantics = {\n default: neutral.text,\n muted: neutral.textMuted,\n subtle: neutral.textSubtle,\n inverse: inverseOnSurface,\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 tertiary: tertiarySwatch[500],\n quaternary: quaternarySwatch[500],\n background: neutral.bg,\n surface: neutral.surface,\n text: neutral.text,\n textSecondary: neutral.textMuted,\n border: neutral.border,\n error: error.base,\n success: success.base,\n warning: warning.base,\n info: info.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 error,\n info,\n surface: surfaceSemantics,\n content,\n border,\n action,\n },\n };\n}\n"]}
|
package/dist/theme/types.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,YAAY,EACV,YAAY,EACZ,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,QAAQ,EACR,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"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"}
|
package/dist/theme/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { GeneratedThemeSwatches } from '@ankhorage/color-theory';\nimport type { ThemeConfig as ContractsThemeConfig } from '@ankhorage/contracts';\n\nexport type {\n ColorHarmony,\n ColorSwatch,\n ColorSwatchStep,\n GeneratedColorRole,\n GeneratedThemeModeColors,\n GeneratedThemeSwatches,\n HexColor,\n SemanticColorReferenceMap,\n SemanticColorToken,\n} from '@ankhorage/color-theory';\nexport type { ThemeConfig, ThemeModeConfig } from '@ankhorage/contracts';\n\nexport interface NeutralSemantics {\n bg: string;\n bgSubtle: string;\n surface: string;\n surfaceHover: string;\n surfaceActive: string;\n border: string;\n borderStrong: string;\n divider: string;\n text: string;\n textMuted: string;\n textSubtle: string;\n}\n\nexport interface RoleSemantics {\n base: string;\n hover: string;\n strong: string;\n softBg: string;\n softHover: string;\n softActive: string;\n outline: string;\n onSolidText: string;\n}\n\nexport interface SurfaceSemantics {\n default: string;\n subtle: string;\n raised: string;\n}\n\nexport interface ContentSemantics {\n default: string;\n muted: string;\n subtle: string;\n inverse: string;\n}\n\nexport interface BorderSemantics {\n default: string;\n strong: string;\n focus: string;\n}\n\nexport interface ActionSemantics {\n primary: RoleSemantics;\n neutral: RoleSemantics;\n danger: RoleSemantics;\n}\n\nexport interface ThemeSemantics {\n neutral: NeutralSemantics;\n brand: RoleSemantics;\n secondary: RoleSemantics;\n accent: RoleSemantics;\n highlight: RoleSemantics;\n danger: RoleSemantics;\n success: RoleSemantics;\n warning: RoleSemantics;\n error: RoleSemantics;\n info: RoleSemantics;\n surface: SurfaceSemantics;\n content: ContentSemantics;\n border: BorderSemantics;\n action: ActionSemantics;\n}\n\nexport type FontWeight =\n | '100'\n | '200'\n | '300'\n | '400'\n | '500'\n | '600'\n | '700'\n | '800'\n | '900'\n | 'bold'\n | 'normal';\n\nexport interface ThemeTokens {\n colors: {\n primary: string;\n secondary: string;\n accent: string;\n highlight: string;\n tertiary: string;\n quaternary: string;\n background: string;\n surface: string;\n text: string;\n textSecondary: string;\n border: string;\n error: string;\n success: string;\n warning: string;\n info: string;\n [key: string]: string;\n };\n swatches: GeneratedThemeSwatches;\n semantics: ThemeSemantics;\n spacing: {\n none: 0;\n xs: number;\n s: number;\n m: number;\n l: number;\n xl: number;\n xxl: number;\n [key: string]: number;\n };\n radii: {\n none: 0;\n s: number;\n m: number;\n l: number;\n full: number;\n };\n typography: {\n headings: Record<\n 1 | 2 | 3 | 4 | 5 | 6,\n {\n size: number;\n lineHeight: number;\n weight: 'regular' | 'medium' | 'semiBold' | 'bold';\n }\n >;\n sizes: {\n xs: number;\n s: number;\n m: number;\n l: number;\n xl: number;\n xxl: number;\n '3xl': number;\n h1: number;\n h2: number;\n h3: number;\n h4: number;\n h5: number;\n h6: number;\n [key: string]: number;\n };\n weights: {\n thin: FontWeight;\n extraLight: FontWeight;\n light: FontWeight;\n regular: FontWeight;\n medium: FontWeight;\n semiBold: FontWeight;\n bold: FontWeight;\n extraBold: FontWeight;\n black: FontWeight;\n };\n fonts: {\n /** Map of weight -> fontFamily name for normal style */\n normal: Record<FontWeight, string | undefined>;\n /** Map of weight -> fontFamily name for italic style */\n italic: Record<FontWeight, string | undefined>;\n };\n };\n shadows: {\n soft: number;\n medium: number;\n hard: number;\n [key: string]: number;\n };\n}\n\nexport interface SurfaceTheme extends ThemeTokens {\n config: ContractsThemeConfig;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { GeneratedThemeSwatches } from '@ankhorage/color-theory';\nimport type { ThemeConfig as ContractsThemeConfig } from '@ankhorage/contracts';\n\nexport type {\n ColorHarmony,\n ColorSwatch,\n ColorSwatchStep,\n GeneratedColorRole,\n GeneratedThemeModeColors,\n GeneratedThemeSwatches,\n HexColor,\n SemanticColorReferenceMap,\n SemanticColorToken,\n} from '@ankhorage/color-theory';\nexport type { ThemeConfig, ThemeModeConfig } from '@ankhorage/contracts';\n\nexport interface NeutralSemantics {\n bg: string;\n bgSubtle: string;\n surface: string;\n surfaceHover: string;\n surfaceActive: string;\n border: string;\n borderStrong: string;\n divider: string;\n text: string;\n textMuted: string;\n textSubtle: string;\n}\n\nexport interface RoleSemantics {\n base: string;\n hover: string;\n strong: string;\n softBg: string;\n softHover: string;\n softActive: string;\n outline: string;\n onSolidText: string;\n}\n\nexport interface SurfaceSemantics {\n default: string;\n subtle: string;\n raised: string;\n inverse: string;\n}\n\nexport interface ContentSemantics {\n default: string;\n muted: string;\n subtle: string;\n inverse: string;\n}\n\nexport interface BorderSemantics {\n default: string;\n strong: string;\n focus: string;\n}\n\nexport interface ActionSemantics {\n primary: RoleSemantics;\n neutral: RoleSemantics;\n danger: RoleSemantics;\n}\n\nexport interface ThemeSemantics {\n neutral: NeutralSemantics;\n brand: RoleSemantics;\n secondary: RoleSemantics;\n accent: RoleSemantics;\n highlight: RoleSemantics;\n danger: RoleSemantics;\n success: RoleSemantics;\n warning: RoleSemantics;\n error: RoleSemantics;\n info: RoleSemantics;\n surface: SurfaceSemantics;\n content: ContentSemantics;\n border: BorderSemantics;\n action: ActionSemantics;\n}\n\nexport type FontWeight =\n | '100'\n | '200'\n | '300'\n | '400'\n | '500'\n | '600'\n | '700'\n | '800'\n | '900'\n | 'bold'\n | 'normal';\n\nexport interface ThemeTokens {\n colors: {\n primary: string;\n secondary: string;\n accent: string;\n highlight: string;\n tertiary: string;\n quaternary: string;\n background: string;\n surface: string;\n text: string;\n textSecondary: string;\n border: string;\n error: string;\n success: string;\n warning: string;\n info: string;\n [key: string]: string;\n };\n swatches: GeneratedThemeSwatches;\n semantics: ThemeSemantics;\n spacing: {\n none: 0;\n xs: number;\n s: number;\n m: number;\n l: number;\n xl: number;\n xxl: number;\n [key: string]: number;\n };\n radii: {\n none: 0;\n s: number;\n m: number;\n l: number;\n full: number;\n };\n typography: {\n headings: Record<\n 1 | 2 | 3 | 4 | 5 | 6,\n {\n size: number;\n lineHeight: number;\n weight: 'regular' | 'medium' | 'semiBold' | 'bold';\n }\n >;\n sizes: {\n xs: number;\n s: number;\n m: number;\n l: number;\n xl: number;\n xxl: number;\n '3xl': number;\n h1: number;\n h2: number;\n h3: number;\n h4: number;\n h5: number;\n h6: number;\n [key: string]: number;\n };\n weights: {\n thin: FontWeight;\n extraLight: FontWeight;\n light: FontWeight;\n regular: FontWeight;\n medium: FontWeight;\n semiBold: FontWeight;\n bold: FontWeight;\n extraBold: FontWeight;\n black: FontWeight;\n };\n fonts: {\n /** Map of weight -> fontFamily name for normal style */\n normal: Record<FontWeight, string | undefined>;\n /** Map of weight -> fontFamily name for italic style */\n italic: Record<FontWeight, string | undefined>;\n };\n };\n shadows: {\n soft: number;\n medium: number;\n hard: number;\n [key: string]: number;\n };\n}\n\nexport interface SurfaceTheme extends ThemeTokens {\n config: ContractsThemeConfig;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/surface",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.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": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@ankhorage/color-theory": "^0.0.7",
|
|
46
|
-
"@ankhorage/contracts": "^1.
|
|
46
|
+
"@ankhorage/contracts": "^2.1.0"
|
|
47
47
|
},
|
|
48
48
|
"files": [
|
|
49
49
|
"dist",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"build": "rm -rf dist tsconfig.tsbuildinfo && bun x tsc -p tsconfig.json",
|
|
70
70
|
"changeset": "changeset",
|
|
71
71
|
"changeset:status": "changeset status --since=origin/main",
|
|
72
|
+
"docs": "paradox",
|
|
72
73
|
"knip": "ankhorage-knip",
|
|
73
74
|
"lint": "ankhorage-eslint . --max-warnings=0",
|
|
74
75
|
"lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
|
|
@@ -76,10 +77,12 @@
|
|
|
76
77
|
"format:check": "ankhorage-prettier --check .",
|
|
77
78
|
"prepack": "bun run build",
|
|
78
79
|
"test": "bun test src",
|
|
80
|
+
"typecheck": "bun x tsc --noEmit -p tsconfig.json",
|
|
79
81
|
"version-packages": "changeset version"
|
|
80
82
|
},
|
|
81
83
|
"devDependencies": {
|
|
82
84
|
"@ankhorage/devtools": "^1.0.6",
|
|
85
|
+
"@ankhorage/paradox": "^0.1.7",
|
|
83
86
|
"@changesets/cli": "^2.31.0",
|
|
84
87
|
"@expo/vector-icons": "^15.1.1",
|
|
85
88
|
"@types/bun": "^1.3.13",
|
|
@@ -5,12 +5,12 @@ import { FocusScope } from '../../internal/focus/FocusScope';
|
|
|
5
5
|
import { useFocusManager } from '../../internal/focus/useFocusManager';
|
|
6
6
|
import { Portal } from '../../internal/overlay/Portal';
|
|
7
7
|
import { resolveOverlayAnimation } from '../../internal/resolvers';
|
|
8
|
-
import { Box, Surface } from '../../layout';
|
|
8
|
+
import { Box, Inline, Stack, Surface } from '../../layout';
|
|
9
9
|
import { ButtonBase } from '../../primitives/button-base';
|
|
10
10
|
import { Text } from '../../primitives/text';
|
|
11
11
|
import { useTheme } from '../../theme/ThemeContext';
|
|
12
12
|
import { resolveNextMenuIndex } from './navigation';
|
|
13
|
-
import type { MenuProps } from './types';
|
|
13
|
+
import type { MenuAction, MenuProps } from './types';
|
|
14
14
|
|
|
15
15
|
interface MeasurableNode {
|
|
16
16
|
measureInWindow?: (
|
|
@@ -25,7 +25,35 @@ function measureNode(node: unknown, callback: (layout: LayoutRectangle) => void)
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
function renderActionContent(action: MenuAction, active: boolean) {
|
|
29
|
+
const titleColor =
|
|
30
|
+
action.intent === 'danger' ? 'danger' : active || action.selected ? 'neutral' : undefined;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Inline align="center" gap="s" justify="space-between" wrap="nowrap">
|
|
34
|
+
{action.leading ? <Box>{action.leading}</Box> : null}
|
|
35
|
+
<Box flex={1}>
|
|
36
|
+
<Stack gap="xxs">
|
|
37
|
+
<Text
|
|
38
|
+
color={titleColor}
|
|
39
|
+
variant="bodySmall"
|
|
40
|
+
weight={action.selected ? 'semiBold' : 'medium'}
|
|
41
|
+
>
|
|
42
|
+
{action.title}
|
|
43
|
+
</Text>
|
|
44
|
+
{action.description ? (
|
|
45
|
+
<Text emphasis="muted" variant="caption">
|
|
46
|
+
{action.description}
|
|
47
|
+
</Text>
|
|
48
|
+
) : null}
|
|
49
|
+
</Stack>
|
|
50
|
+
</Box>
|
|
51
|
+
{action.trailing ? <Box>{action.trailing}</Box> : null}
|
|
52
|
+
</Inline>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function Menu({ trigger, actions, dismiss, closeOnSelect = true, testID }: MenuProps) {
|
|
29
57
|
const { theme } = useTheme();
|
|
30
58
|
const { bindKeydown } = useFocusManager();
|
|
31
59
|
const animation = resolveOverlayAnimation('menu');
|
|
@@ -36,15 +64,34 @@ export function Menu({ trigger, items, onDismiss, closeOnSelect = true, testID }
|
|
|
36
64
|
|
|
37
65
|
const closeMenu = React.useCallback(() => {
|
|
38
66
|
setOpen(false);
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
if (dismiss) {
|
|
68
|
+
dismiss();
|
|
69
|
+
}
|
|
70
|
+
}, [dismiss]);
|
|
71
|
+
|
|
72
|
+
const activateAction = React.useCallback(
|
|
73
|
+
(action: MenuAction) => {
|
|
74
|
+
if (action.disabled) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (action.activate) {
|
|
79
|
+
action.activate();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (closeOnSelect) {
|
|
83
|
+
closeMenu();
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
[closeMenu, closeOnSelect],
|
|
87
|
+
);
|
|
41
88
|
|
|
42
89
|
const openMenu = React.useCallback(() => {
|
|
43
90
|
measureNode(anchorRef.current, setLayout);
|
|
44
|
-
const firstEnabledIndex =
|
|
91
|
+
const firstEnabledIndex = actions.findIndex((action) => !action.disabled);
|
|
45
92
|
setActiveIndex(firstEnabledIndex === -1 ? 0 : firstEnabledIndex);
|
|
46
93
|
setOpen(true);
|
|
47
|
-
}, [
|
|
94
|
+
}, [actions]);
|
|
48
95
|
|
|
49
96
|
React.useEffect(() => {
|
|
50
97
|
if (!open) {
|
|
@@ -55,17 +102,14 @@ export function Menu({ trigger, items, onDismiss, closeOnSelect = true, testID }
|
|
|
55
102
|
const { key } = event;
|
|
56
103
|
if (key === 'ArrowDown' || key === 'ArrowUp' || key === 'Home' || key === 'End') {
|
|
57
104
|
event.preventDefault();
|
|
58
|
-
setActiveIndex((current) => resolveNextMenuIndex(
|
|
105
|
+
setActiveIndex((current) => resolveNextMenuIndex(actions, current, key));
|
|
59
106
|
}
|
|
60
107
|
|
|
61
108
|
if (event.key === 'Enter') {
|
|
62
109
|
event.preventDefault();
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
if (closeOnSelect) {
|
|
67
|
-
closeMenu();
|
|
68
|
-
}
|
|
110
|
+
const activeAction = actions[activeIndex];
|
|
111
|
+
if (activeAction) {
|
|
112
|
+
activateAction(activeAction);
|
|
69
113
|
}
|
|
70
114
|
}
|
|
71
115
|
|
|
@@ -74,7 +118,7 @@ export function Menu({ trigger, items, onDismiss, closeOnSelect = true, testID }
|
|
|
74
118
|
closeMenu();
|
|
75
119
|
}
|
|
76
120
|
});
|
|
77
|
-
}, [
|
|
121
|
+
}, [activateAction, actions, activeIndex, bindKeydown, closeMenu, open]);
|
|
78
122
|
|
|
79
123
|
return (
|
|
80
124
|
<View collapsable={false} ref={anchorRef}>
|
|
@@ -117,7 +161,7 @@ export function Menu({ trigger, items, onDismiss, closeOnSelect = true, testID }
|
|
|
117
161
|
accessibilityRole="menu"
|
|
118
162
|
p="xs"
|
|
119
163
|
style={{
|
|
120
|
-
minWidth: Math.max(layout?.width ?? 0,
|
|
164
|
+
minWidth: Math.max(layout?.width ?? 0, 220),
|
|
121
165
|
shadowOpacity: 0.12,
|
|
122
166
|
shadowRadius: 12,
|
|
123
167
|
shadowOffset: { width: 0, height: 6 },
|
|
@@ -125,40 +169,31 @@ export function Menu({ trigger, items, onDismiss, closeOnSelect = true, testID }
|
|
|
125
169
|
testID={testID}
|
|
126
170
|
variant="raised"
|
|
127
171
|
>
|
|
128
|
-
{
|
|
129
|
-
const
|
|
172
|
+
{actions.map((action, index) => {
|
|
173
|
+
const active = index === activeIndex;
|
|
174
|
+
const selected = action.selected ?? active;
|
|
130
175
|
|
|
131
176
|
return (
|
|
132
177
|
<Pressable
|
|
133
178
|
accessibilityRole="menuitem"
|
|
134
|
-
accessibilityState={{ disabled:
|
|
135
|
-
disabled={
|
|
136
|
-
key={
|
|
137
|
-
onPress={() =>
|
|
138
|
-
if (item.disabled) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
item.onPress?.();
|
|
143
|
-
if (closeOnSelect) {
|
|
144
|
-
closeMenu();
|
|
145
|
-
}
|
|
146
|
-
}}
|
|
179
|
+
accessibilityState={{ disabled: action.disabled, selected }}
|
|
180
|
+
disabled={action.disabled}
|
|
181
|
+
key={action.id}
|
|
182
|
+
onPress={() => activateAction(action)}
|
|
147
183
|
>
|
|
148
184
|
<Box
|
|
149
185
|
px="m"
|
|
150
186
|
py="s"
|
|
187
|
+
radius="s"
|
|
151
188
|
style={{
|
|
152
189
|
backgroundColor: selected
|
|
153
190
|
? theme.semantics.action.neutral.softBg
|
|
154
191
|
: 'transparent',
|
|
155
|
-
opacity:
|
|
192
|
+
opacity: action.disabled ? 0.56 : 1,
|
|
156
193
|
}}
|
|
157
|
-
testID={testID ? `${testID}-item-${
|
|
194
|
+
testID={testID ? `${testID}-item-${action.id}` : undefined}
|
|
158
195
|
>
|
|
159
|
-
|
|
160
|
-
{item.label}
|
|
161
|
-
</Text>
|
|
196
|
+
{renderActionContent(action, active)}
|
|
162
197
|
</Box>
|
|
163
198
|
</Pressable>
|
|
164
199
|
);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Menu } from './Menu';
|
|
2
|
-
export type { MenuItem, MenuProps } from './types';
|
|
2
|
+
export type { MenuAction, MenuActionIntent, MenuAction as MenuItem, MenuProps } from './types';
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export type MenuActionIntent = 'default' | 'danger';
|
|
4
|
+
|
|
5
|
+
export interface MenuAction {
|
|
4
6
|
id: string;
|
|
5
|
-
|
|
7
|
+
title: React.ReactNode;
|
|
8
|
+
description?: React.ReactNode;
|
|
9
|
+
leading?: React.ReactNode;
|
|
10
|
+
trailing?: React.ReactNode;
|
|
11
|
+
intent?: MenuActionIntent;
|
|
6
12
|
disabled?: boolean;
|
|
7
|
-
|
|
13
|
+
selected?: boolean;
|
|
14
|
+
activate?: () => void;
|
|
8
15
|
}
|
|
9
16
|
|
|
10
17
|
export interface MenuProps {
|
|
11
18
|
trigger?: React.ReactNode;
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
actions: readonly MenuAction[];
|
|
20
|
+
dismiss?: () => void;
|
|
14
21
|
closeOnSelect?: boolean;
|
|
15
22
|
testID?: string;
|
|
16
23
|
}
|
|
@@ -1,77 +1,91 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
TabPanel,
|
|
20
|
-
Tabs,
|
|
21
|
-
Text,
|
|
22
|
-
Textarea,
|
|
23
|
-
TextInput,
|
|
24
|
-
ThemeProvider,
|
|
25
|
-
} from '../index';
|
|
3
|
+
import { Button } from '../components/button';
|
|
4
|
+
import { Card } from '../components/card';
|
|
5
|
+
import { Checkbox } from '../components/checkbox';
|
|
6
|
+
import { Drawer } from '../components/drawer';
|
|
7
|
+
import { Field } from '../components/field';
|
|
8
|
+
import { HelperText } from '../components/helper-text';
|
|
9
|
+
import { Menu } from '../components/menu';
|
|
10
|
+
import { Modal } from '../components/modal';
|
|
11
|
+
import { Tab, TabList, TabPanel, Tabs } from '../components/tabs';
|
|
12
|
+
import { TextInput } from '../components/text-input';
|
|
13
|
+
import { ToastProvider, useToast } from '../components/toast';
|
|
14
|
+
import { Tooltip } from '../components/tooltip';
|
|
15
|
+
import { Box, Stack } from '../layout';
|
|
16
|
+
import { Text } from '../primitives/text';
|
|
17
|
+
import { ThemeProvider } from '../theme';
|
|
18
|
+
import type { ThemeConfig } from '../theme/types';
|
|
26
19
|
|
|
27
|
-
const docsThemeConfig = {
|
|
28
|
-
id: 'docs
|
|
29
|
-
name: 'Docs
|
|
30
|
-
light: {
|
|
31
|
-
|
|
32
|
-
primaryColor: '#2563eb',
|
|
33
|
-
},
|
|
34
|
-
dark: {
|
|
35
|
-
harmony: 'monochromatic' as const,
|
|
36
|
-
primaryColor: '#2563eb',
|
|
37
|
-
},
|
|
20
|
+
const docsThemeConfig: ThemeConfig = {
|
|
21
|
+
id: 'docs',
|
|
22
|
+
name: 'Docs',
|
|
23
|
+
light: { primaryColor: '#2563EB', harmony: 'analogous' },
|
|
24
|
+
dark: { primaryColor: '#60A5FA', harmony: 'analogous' },
|
|
38
25
|
};
|
|
39
26
|
|
|
40
|
-
export function
|
|
27
|
+
export function FormExample() {
|
|
41
28
|
return (
|
|
42
29
|
<ThemeProvider initialConfig={docsThemeConfig}>
|
|
43
30
|
<Stack gap="m" p="l">
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
<Field helperText="Use your work email." label="Email" required>
|
|
32
|
+
<TextInput placeholder="you@example.com" />
|
|
33
|
+
</Field>
|
|
34
|
+
<Checkbox checked>Accept terms</Checkbox>
|
|
35
|
+
<HelperText emphasis="muted">Looks good.</HelperText>
|
|
49
36
|
</Stack>
|
|
50
37
|
</ThemeProvider>
|
|
51
38
|
);
|
|
52
39
|
}
|
|
53
40
|
|
|
54
|
-
|
|
41
|
+
function ToastButton() {
|
|
42
|
+
const { showToast } = useToast();
|
|
43
|
+
return (
|
|
44
|
+
<Button
|
|
45
|
+
onPress={() =>
|
|
46
|
+
showToast({ description: 'Your changes were saved.', status: 'success', title: 'Saved' })
|
|
47
|
+
}
|
|
48
|
+
>
|
|
49
|
+
Show toast
|
|
50
|
+
</Button>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function FeedbackExample() {
|
|
55
55
|
return (
|
|
56
56
|
<ThemeProvider initialConfig={docsThemeConfig}>
|
|
57
|
-
<
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
</Field>
|
|
64
|
-
<Stack gap="s">
|
|
65
|
-
<Label required>Preferences</Label>
|
|
66
|
-
<Checkbox defaultChecked>Weekly updates</Checkbox>
|
|
67
|
-
<Radio>Product announcements</Radio>
|
|
68
|
-
<Switch readOnly>Read-only setting</Switch>
|
|
69
|
-
<HelperText color="error">Invalid fields should reuse the same error color.</HelperText>
|
|
57
|
+
<ToastProvider>
|
|
58
|
+
<Stack gap="m" p="l">
|
|
59
|
+
<Tooltip content="Helpful contextual information">
|
|
60
|
+
<Text>Hover for tooltip</Text>
|
|
61
|
+
</Tooltip>
|
|
62
|
+
<ToastButton />
|
|
70
63
|
</Stack>
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
</ToastProvider>
|
|
65
|
+
</ThemeProvider>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function OverlayExample() {
|
|
70
|
+
const [modalVisible, setModalVisible] = React.useState(false);
|
|
71
|
+
const [drawerVisible, setDrawerVisible] = React.useState(false);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<ThemeProvider initialConfig={docsThemeConfig}>
|
|
75
|
+
<Stack gap="m" p="l">
|
|
76
|
+
<Button onPress={() => setModalVisible(true)}>Open modal</Button>
|
|
77
|
+
<Button onPress={() => setDrawerVisible(true)}>Open drawer</Button>
|
|
78
|
+
<Modal onDismiss={() => setModalVisible(false)} visible={modalVisible}>
|
|
79
|
+
<Card>
|
|
80
|
+
<Stack gap="s">
|
|
81
|
+
<Text variant="label" weight="semiBold">
|
|
82
|
+
Modal title
|
|
83
|
+
</Text>
|
|
84
|
+
<Text>Modal content</Text>
|
|
85
|
+
</Stack>
|
|
86
|
+
</Card>
|
|
73
87
|
</Modal>
|
|
74
|
-
<Drawer
|
|
88
|
+
<Drawer onDismiss={() => setDrawerVisible(false)} visible={drawerVisible}>
|
|
75
89
|
<Text>Drawer content</Text>
|
|
76
90
|
</Drawer>
|
|
77
91
|
</Stack>
|
|
@@ -99,9 +113,9 @@ export function NavigationExample() {
|
|
|
99
113
|
</TabPanel>
|
|
100
114
|
</Tabs>
|
|
101
115
|
<Menu
|
|
102
|
-
|
|
103
|
-
{ id: 'edit',
|
|
104
|
-
{ disabled: true, id: 'archive',
|
|
116
|
+
actions={[
|
|
117
|
+
{ id: 'edit', title: 'Edit' },
|
|
118
|
+
{ disabled: true, id: 'archive', title: 'Archive' },
|
|
105
119
|
]}
|
|
106
120
|
trigger={<Text>Open menu</Text>}
|
|
107
121
|
/>
|
package/src/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type { LabelProps } from './components/label';
|
|
|
20
20
|
export { Label } from './components/label';
|
|
21
21
|
export type { ListItemProps } from './components/list-item';
|
|
22
22
|
export { ListItem } from './components/list-item';
|
|
23
|
-
export type { MenuItem, MenuProps } from './components/menu';
|
|
23
|
+
export type { MenuAction, MenuActionIntent, MenuItem, MenuProps } from './components/menu';
|
|
24
24
|
export { Menu } from './components/menu';
|
|
25
25
|
export type { ModalProps } from './components/modal';
|
|
26
26
|
export { Modal } from './components/modal';
|
|
@@ -67,10 +67,11 @@ describe('colorEngine', () => {
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
it('generates a valid palette for dark mode', () => {
|
|
70
|
-
const { colors, semantics } = generatePalette(mockConfig, 'dark');
|
|
70
|
+
const { colors, swatches, semantics } = generatePalette(mockConfig, 'dark');
|
|
71
71
|
|
|
72
72
|
expect(colors.background).toBe(semantics.neutral.bg);
|
|
73
|
-
expect(semantics.
|
|
73
|
+
expect(semantics.surface.inverse).toBe(swatches.neutral[50]);
|
|
74
|
+
expect(semantics.content.inverse).toBe(swatches.neutral[900]);
|
|
74
75
|
});
|
|
75
76
|
|
|
76
77
|
it('uses the color-theory default semantic status seeds', () => {
|
package/src/theme/colorEngine.ts
CHANGED
|
@@ -120,7 +120,6 @@ export function generatePalette(
|
|
|
120
120
|
parseHexColorOrThrow(modeConfig.primaryColor);
|
|
121
121
|
|
|
122
122
|
const generated = generateThemeModeColors(modeConfig);
|
|
123
|
-
|
|
124
123
|
const { swatches } = generated;
|
|
125
124
|
const neutralSwatch = swatches.neutral;
|
|
126
125
|
|
|
@@ -140,18 +139,21 @@ export function generatePalette(
|
|
|
140
139
|
// Keep destructive action and feedback error aligned until a dedicated error status swatch is introduced.
|
|
141
140
|
const error = danger;
|
|
142
141
|
const info = buildRoleSemantics(secondarySwatch, isDark);
|
|
142
|
+
const inverseSurface = isDark ? neutralSwatch[50] : neutralSwatch[900];
|
|
143
|
+
const inverseOnSurface = isDark ? neutralSwatch[900] : neutralSwatch[50];
|
|
143
144
|
|
|
144
145
|
const surfaceSemantics: SurfaceSemantics = {
|
|
145
146
|
default: neutral.surface,
|
|
146
147
|
subtle: neutral.bgSubtle,
|
|
147
148
|
raised: neutral.surface,
|
|
149
|
+
inverse: inverseSurface,
|
|
148
150
|
};
|
|
149
151
|
|
|
150
152
|
const content: ContentSemantics = {
|
|
151
153
|
default: neutral.text,
|
|
152
154
|
muted: neutral.textMuted,
|
|
153
155
|
subtle: neutral.textSubtle,
|
|
154
|
-
inverse:
|
|
156
|
+
inverse: inverseOnSurface,
|
|
155
157
|
};
|
|
156
158
|
|
|
157
159
|
const border: BorderSemantics = {
|