@ankhorage/surface 2.0.2 → 2.1.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 +692 -47
- package/dist/components/action-sheet/ActionSheet.d.ts +1 -1
- package/dist/components/action-sheet/ActionSheet.d.ts.map +1 -1
- package/dist/components/action-sheet/ActionSheet.js +5 -4
- package/dist/components/action-sheet/ActionSheet.js.map +1 -1
- package/dist/components/action-sheet/ActionSheetItem.d.ts +1 -1
- package/dist/components/action-sheet/ActionSheetItem.d.ts.map +1 -1
- package/dist/components/action-sheet/ActionSheetItem.js +2 -2
- package/dist/components/action-sheet/ActionSheetItem.js.map +1 -1
- package/dist/components/action-sheet/types.d.ts +3 -0
- package/dist/components/action-sheet/types.d.ts.map +1 -1
- package/dist/components/action-sheet/types.js.map +1 -1
- package/dist/components/drawer/Drawer.d.ts +1 -1
- package/dist/components/drawer/Drawer.d.ts.map +1 -1
- package/dist/components/drawer/Drawer.js +4 -3
- package/dist/components/drawer/Drawer.js.map +1 -1
- package/dist/components/drawer/types.d.ts +2 -0
- package/dist/components/drawer/types.d.ts.map +1 -1
- package/dist/components/drawer/types.js.map +1 -1
- 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 +10 -6
- package/dist/components/menu/Menu.js.map +1 -1
- package/dist/components/menu/types.d.ts +2 -0
- package/dist/components/menu/types.d.ts.map +1 -1
- package/dist/components/menu/types.js.map +1 -1
- package/dist/components/modal/Modal.d.ts +1 -1
- package/dist/components/modal/Modal.d.ts.map +1 -1
- package/dist/components/modal/Modal.js +4 -3
- package/dist/components/modal/Modal.js.map +1 -1
- package/dist/components/modal/types.d.ts +2 -0
- package/dist/components/modal/types.d.ts.map +1 -1
- package/dist/components/modal/types.js.map +1 -1
- package/dist/components/navigation/DrawerNavigation.d.ts +1 -1
- package/dist/components/navigation/DrawerNavigation.d.ts.map +1 -1
- package/dist/components/navigation/DrawerNavigation.js +2 -2
- package/dist/components/navigation/DrawerNavigation.js.map +1 -1
- package/dist/components/navigation/DrawerNavigationItem.d.ts +1 -1
- package/dist/components/navigation/DrawerNavigationItem.d.ts.map +1 -1
- package/dist/components/navigation/DrawerNavigationItem.js +2 -2
- package/dist/components/navigation/DrawerNavigationItem.js.map +1 -1
- package/dist/components/navigation/NavigationItem.d.ts +1 -1
- package/dist/components/navigation/NavigationItem.d.ts.map +1 -1
- package/dist/components/navigation/NavigationItem.js +2 -2
- package/dist/components/navigation/NavigationItem.js.map +1 -1
- package/dist/components/navigation/NavigationList.d.ts +1 -1
- package/dist/components/navigation/NavigationList.d.ts.map +1 -1
- package/dist/components/navigation/NavigationList.js +2 -2
- package/dist/components/navigation/NavigationList.js.map +1 -1
- package/dist/components/navigation/TabBar.d.ts +1 -1
- package/dist/components/navigation/TabBar.d.ts.map +1 -1
- package/dist/components/navigation/TabBar.js +2 -2
- package/dist/components/navigation/TabBar.js.map +1 -1
- package/dist/components/navigation/TabBarItem.d.ts +1 -1
- package/dist/components/navigation/TabBarItem.d.ts.map +1 -1
- package/dist/components/navigation/TabBarItem.js +2 -2
- package/dist/components/navigation/TabBarItem.js.map +1 -1
- package/dist/components/navigation/types.d.ts +7 -0
- package/dist/components/navigation/types.d.ts.map +1 -1
- package/dist/components/navigation/types.js.map +1 -1
- package/dist/components/tabs/Tab.d.ts +1 -1
- package/dist/components/tabs/Tab.d.ts.map +1 -1
- package/dist/components/tabs/Tab.js +9 -6
- package/dist/components/tabs/Tab.js.map +1 -1
- package/dist/components/tabs/types.d.ts +2 -0
- package/dist/components/tabs/types.d.ts.map +1 -1
- package/dist/components/tabs/types.js.map +1 -1
- package/dist/components/text-input/TextInput.d.ts +1 -1
- package/dist/components/text-input/TextInput.d.ts.map +1 -1
- package/dist/components/text-input/TextInput.js +8 -2
- package/dist/components/text-input/TextInput.js.map +1 -1
- package/dist/components/text-input/types.d.ts +2 -0
- package/dist/components/text-input/types.d.ts.map +1 -1
- package/dist/components/text-input/types.js.map +1 -1
- package/dist/components/toast/Toast.d.ts +1 -1
- package/dist/components/toast/Toast.d.ts.map +1 -1
- package/dist/components/toast/Toast.js +3 -2
- package/dist/components/toast/Toast.js.map +1 -1
- package/dist/components/toast/types.d.ts +2 -0
- package/dist/components/toast/types.d.ts.map +1 -1
- package/dist/components/toast/types.js.map +1 -1
- package/dist/components/tooltip/Tooltip.d.ts +1 -1
- package/dist/components/tooltip/Tooltip.d.ts.map +1 -1
- package/dist/components/tooltip/Tooltip.js +4 -3
- package/dist/components/tooltip/Tooltip.js.map +1 -1
- package/dist/components/tooltip/types.d.ts +2 -0
- package/dist/components/tooltip/types.d.ts.map +1 -1
- package/dist/components/tooltip/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interactionPolicy.d.ts +5 -0
- package/dist/interactionPolicy.d.ts.map +1 -0
- package/dist/interactionPolicy.js +2 -0
- package/dist/interactionPolicy.js.map +1 -0
- package/dist/primitives/button-base/ButtonBase.d.ts +1 -1
- package/dist/primitives/button-base/ButtonBase.d.ts.map +1 -1
- package/dist/primitives/button-base/ButtonBase.js +3 -2
- package/dist/primitives/button-base/ButtonBase.js.map +1 -1
- package/dist/primitives/button-base/types.d.ts +2 -1
- package/dist/primitives/button-base/types.d.ts.map +1 -1
- package/dist/primitives/button-base/types.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/action-sheet/ActionSheet.tsx +5 -2
- package/src/components/action-sheet/ActionSheetItem.tsx +2 -0
- package/src/components/action-sheet/types.ts +3 -0
- package/src/components/drawer/Drawer.tsx +4 -2
- package/src/components/drawer/types.ts +3 -0
- package/src/components/menu/Menu.tsx +18 -6
- package/src/components/menu/types.ts +3 -0
- package/src/components/modal/Modal.tsx +4 -2
- package/src/components/modal/types.ts +3 -0
- package/src/components/navigation/DrawerNavigation.tsx +8 -1
- package/src/components/navigation/DrawerNavigationItem.tsx +7 -1
- package/src/components/navigation/NavigationItem.tsx +7 -1
- package/src/components/navigation/NavigationList.tsx +8 -1
- package/src/components/navigation/TabBar.tsx +8 -2
- package/src/components/navigation/TabBarItem.tsx +2 -1
- package/src/components/navigation/policy.test.ts +96 -0
- package/src/components/navigation/types.ts +7 -0
- package/src/components/tabs/Tab.tsx +17 -6
- package/src/components/tabs/types.ts +3 -0
- package/src/components/text-input/TextInput.tsx +10 -2
- package/src/components/text-input/types.ts +2 -0
- package/src/components/toast/Toast.tsx +10 -2
- package/src/components/toast/types.ts +2 -0
- package/src/components/tooltip/Tooltip.tsx +7 -5
- package/src/components/tooltip/types.ts +3 -0
- package/src/index.test.ts +1 -0
- package/src/index.ts +1 -0
- package/src/interactionPolicy.test.ts +86 -0
- package/src/interactionPolicy.ts +5 -0
- package/src/primitives/button-base/ButtonBase.tsx +4 -2
- package/src/primitives/button-base/types.ts +3 -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
package/src/index.test.ts
CHANGED
|
@@ -19,6 +19,7 @@ const expectedRootExports = [
|
|
|
19
19
|
"export { Field } from './components/field';",
|
|
20
20
|
"export { HelperText } from './components/helper-text';",
|
|
21
21
|
"export { IconButton } from './components/icon-button';",
|
|
22
|
+
"export type { InteractionPolicy, InteractionPolicyProps } from './interactionPolicy';",
|
|
22
23
|
"export { Label } from './components/label';",
|
|
23
24
|
"export { ListItem } from './components/list-item';",
|
|
24
25
|
"export {\n DrawerNavigation,\n DrawerNavigationItem,\n NavigationItem,\n NavigationList,\n TabBar,\n TabBarItem,\n} from './components/navigation';",
|
package/src/index.ts
CHANGED
|
@@ -61,6 +61,7 @@ export { FontProvider, useFontContext } from './context/FontContext';
|
|
|
61
61
|
export type { I18nInstance, TranslationRuntime, Translator } from './context/TranslationContext';
|
|
62
62
|
export { TranslationProvider, useTranslationContext } from './context/TranslationContext';
|
|
63
63
|
export * from './core/responsive';
|
|
64
|
+
export type { InteractionPolicy, InteractionPolicyProps } from './interactionPolicy';
|
|
64
65
|
export * from './layout';
|
|
65
66
|
export type { ButtonBaseProps } from './primitives/button-base';
|
|
66
67
|
export { ButtonBase } from './primitives/button-base';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from 'bun:test';
|
|
4
|
+
|
|
5
|
+
const indexSource = readFileSync(new URL('./index.ts', import.meta.url), 'utf8');
|
|
6
|
+
|
|
7
|
+
describe('InteractionPolicy public export', () => {
|
|
8
|
+
it('exports InteractionPolicy from the public root', () => {
|
|
9
|
+
expect(indexSource).toContain(
|
|
10
|
+
"export type { InteractionPolicy, InteractionPolicyProps } from './interactionPolicy';",
|
|
11
|
+
);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('exports InteractionPolicyProps from the public root', () => {
|
|
15
|
+
expect(indexSource).toContain(
|
|
16
|
+
"export type { InteractionPolicy, InteractionPolicyProps } from './interactionPolicy';",
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('does not export InteractionPolicy from button-base', () => {
|
|
21
|
+
expect(indexSource).not.toContain(
|
|
22
|
+
"export type { InteractionPolicy, InteractionPolicyProps } from './primitives/button-base';",
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('InteractionPolicy type', () => {
|
|
28
|
+
it('only allows enabled', () => {
|
|
29
|
+
const policy = 'enabled' as const;
|
|
30
|
+
expect(policy).toBe('enabled');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('only allows passive', () => {
|
|
34
|
+
const policy = 'passive' as const;
|
|
35
|
+
expect(policy).toBe('passive');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('InteractionPolicyProps', () => {
|
|
40
|
+
it('allows omitted interactionPolicy', () => {
|
|
41
|
+
const props: { readonly interactionPolicy?: 'enabled' | 'passive' } = {};
|
|
42
|
+
expect(props.interactionPolicy).toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('allows explicit enabled', () => {
|
|
46
|
+
const props: { readonly interactionPolicy?: 'enabled' | 'passive' } = {
|
|
47
|
+
interactionPolicy: 'enabled',
|
|
48
|
+
};
|
|
49
|
+
expect(props.interactionPolicy).toBe('enabled');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('allows explicit passive', () => {
|
|
53
|
+
const props: { readonly interactionPolicy?: 'enabled' | 'passive' } = {
|
|
54
|
+
interactionPolicy: 'passive',
|
|
55
|
+
};
|
|
56
|
+
expect(props.interactionPolicy).toBe('passive');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('ButtonBaseProps includes interactionPolicy', () => {
|
|
61
|
+
it('has interactionPolicy in ButtonBaseProps', () => {
|
|
62
|
+
expect(indexSource).toContain(
|
|
63
|
+
"export type { ButtonBaseProps } from './primitives/button-base';",
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('Policy does not leak to native hosts', () => {
|
|
69
|
+
it('InteractionPolicy is not a native View prop', () => {
|
|
70
|
+
const viewProps: Record<string, unknown> = {
|
|
71
|
+
accessible: true,
|
|
72
|
+
accessibilityLabel: 'test',
|
|
73
|
+
};
|
|
74
|
+
expect(viewProps.interactionPolicy).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('InteractionPolicy is not a native TextInput prop', () => {
|
|
78
|
+
const textInputProps: Record<string, unknown> = {
|
|
79
|
+
editable: true,
|
|
80
|
+
onChangeText: (_text: string) => {
|
|
81
|
+
void _text;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
expect(textInputProps.interactionPolicy).toBeUndefined();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -28,6 +28,7 @@ function getInteractionState(
|
|
|
28
28
|
export function ButtonBase({
|
|
29
29
|
children,
|
|
30
30
|
disabled = false,
|
|
31
|
+
interactionPolicy = 'enabled',
|
|
31
32
|
onPress,
|
|
32
33
|
onLongPress,
|
|
33
34
|
accessibilityLabel,
|
|
@@ -43,6 +44,7 @@ export function ButtonBase({
|
|
|
43
44
|
const [focused, setFocused] = React.useState(false);
|
|
44
45
|
const isWeb = Platform.OS === 'web';
|
|
45
46
|
const resolvedBoxStyles = resolveBoxStyles(theme, breakpoint, props);
|
|
47
|
+
const passive = interactionPolicy === 'passive';
|
|
46
48
|
|
|
47
49
|
return (
|
|
48
50
|
<Pressable
|
|
@@ -58,8 +60,8 @@ export function ButtonBase({
|
|
|
58
60
|
}}
|
|
59
61
|
onHoverIn={isWeb ? () => setHovered(true) : undefined}
|
|
60
62
|
onHoverOut={isWeb ? () => setHovered(false) : undefined}
|
|
61
|
-
onLongPress={onLongPress}
|
|
62
|
-
onPress={onPress}
|
|
63
|
+
onLongPress={passive ? undefined : onLongPress}
|
|
64
|
+
onPress={passive ? undefined : onPress}
|
|
63
65
|
style={(pressableState) => {
|
|
64
66
|
const state = getInteractionState(pressableState, hovered, focused, disabled);
|
|
65
67
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import type { AccessibilityRole, AccessibilityState, GestureResponderEvent } from 'react-native';
|
|
3
3
|
|
|
4
|
+
import type { InteractionPolicyProps } from '../../interactionPolicy';
|
|
4
5
|
import type { InteractionState } from '../../internal/resolvers/resolveInteractiveState';
|
|
5
6
|
import type { BoxProps } from '../../layout';
|
|
6
7
|
|
|
7
|
-
export interface ButtonBaseProps
|
|
8
|
+
export interface ButtonBaseProps
|
|
9
|
+
extends Omit<BoxProps, 'children' | 'pointerEvents'>, InteractionPolicyProps {
|
|
8
10
|
children?: React.ReactNode | ((state: InteractionState) => React.ReactNode);
|
|
9
11
|
disabled?: boolean;
|
|
10
12
|
onPress?: ((event: GestureResponderEvent) => void) | undefined;
|
|
@@ -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 = {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ThemeConfig } from '@ankhorage/contracts';
|
|
2
|
+
import { describe, expect, it } from 'bun:test';
|
|
3
|
+
|
|
4
|
+
import { generatePalette } from './colorEngine';
|
|
5
|
+
|
|
6
|
+
const config: ThemeConfig = {
|
|
7
|
+
id: 'inverse-semantics-test',
|
|
8
|
+
name: 'Inverse semantics test',
|
|
9
|
+
light: {
|
|
10
|
+
primaryColor: '#3B82F6',
|
|
11
|
+
harmony: 'monochromatic',
|
|
12
|
+
},
|
|
13
|
+
dark: {
|
|
14
|
+
primaryColor: '#3B82F6',
|
|
15
|
+
harmony: 'monochromatic',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe('inverse semantics', () => {
|
|
20
|
+
it('provides a paired inverse surface and content color in light mode', () => {
|
|
21
|
+
const { semantics, swatches } = generatePalette(config, 'light');
|
|
22
|
+
|
|
23
|
+
expect(semantics.surface.inverse).toBe(swatches.neutral[900]);
|
|
24
|
+
expect(semantics.content.inverse).toBe(swatches.neutral[50]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('provides a paired inverse surface and content color in dark mode', () => {
|
|
28
|
+
const { semantics, swatches } = generatePalette(config, 'dark');
|
|
29
|
+
|
|
30
|
+
expect(semantics.surface.inverse).toBe(swatches.neutral[50]);
|
|
31
|
+
expect(semantics.content.inverse).toBe(swatches.neutral[900]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('resolves global inverse content from neutral semantics instead of brand foreground', () => {
|
|
35
|
+
const brightBrandConfig: ThemeConfig = {
|
|
36
|
+
...config,
|
|
37
|
+
light: {
|
|
38
|
+
...config.light,
|
|
39
|
+
primaryColor: '#FDE047',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const { semantics, swatches } = generatePalette(brightBrandConfig, 'light');
|
|
43
|
+
|
|
44
|
+
expect(semantics.surface.inverse).toBe(swatches.neutral[900]);
|
|
45
|
+
expect(semantics.content.inverse).toBe(swatches.neutral[50]);
|
|
46
|
+
expect(semantics.content.inverse).not.toBe(semantics.brand.onSolidText);
|
|
47
|
+
});
|
|
48
|
+
});
|