@scalewing/react-native 0.4.0 → 0.6.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 +28 -0
- package/README.md +19 -2
- package/dist/components/Accordion.d.ts +13 -0
- package/dist/components/Accordion.js +17 -0
- package/dist/components/Field.d.ts +10 -0
- package/dist/components/Field.js +30 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/map-accordion-style.d.ts +6 -0
- package/dist/map-accordion-style.js +39 -0
- package/dist/map-field-accessibility.d.ts +10 -0
- package/dist/map-field-accessibility.js +7 -0
- package/dist/map-field-style.d.ts +7 -0
- package/dist/map-field-style.js +22 -0
- package/dist/map-segmented-style.js +3 -3
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @scalewing/react-native
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ebbbd43: Add a controlled native Accordion with optional separate title navigation,
|
|
8
|
+
leading/metadata slots, expanded accessibility state, and theme-owned chrome.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- ebbbd43: Make native segmented selection visible with paired accent/onAccent colors
|
|
13
|
+
and a larger minimum touch height. Existing component props are unchanged.
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
- @scalewing/tokens@0.6.0
|
|
16
|
+
|
|
17
|
+
## 0.5.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- Keep public package versions matched for the GitHub `v0.5.0` release tag.
|
|
22
|
+
Native Field ships in this version.
|
|
23
|
+
- 7cf64df: Add an accessible token-styled native `Field` with label, hint,
|
|
24
|
+
error, focus, disabled, and standard React Native text-input behavior.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies
|
|
29
|
+
- @scalewing/tokens@0.5.0
|
|
30
|
+
|
|
3
31
|
## 0.4.0
|
|
4
32
|
|
|
5
33
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -2,19 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
React Native / Expo primitives that consume the same Scalewing tokens as the DOM package.
|
|
4
4
|
|
|
5
|
+
Native `Accordion` is controlled by `open` / `onOpenChange`. Provide a title,
|
|
6
|
+
localized disclosure `accessibilityLabel`, and content. Optional `leading`
|
|
7
|
+
and `metadata` slots support marks and counts. With `onTitlePress`, the title
|
|
8
|
+
is a separate action (named by `titleAccessibilityLabel` or `title`) and only
|
|
9
|
+
the trailing control toggles. Otherwise the entire header toggles. Collapsed
|
|
10
|
+
content unmounts and is absent from the accessibility tree.
|
|
11
|
+
|
|
5
12
|
```ts
|
|
6
|
-
import { Button, Card, SegmentedControl, Stack, TabBar, Text, ThemeProvider } from '@scalewing/react-native';
|
|
13
|
+
import { Button, Card, Field, SegmentedControl, Stack, TabBar, Text, ThemeProvider } from '@scalewing/react-native';
|
|
7
14
|
|
|
8
15
|
<ThemeProvider colorScheme="system" palette="cerulean">
|
|
9
16
|
<Card padding={4}>
|
|
10
17
|
<Stack gap={3}>
|
|
11
18
|
<Text variant="title">Kickoff</Text>
|
|
19
|
+
<Field label="Team name" onChangeText={() => undefined} value="Harbor United" />
|
|
12
20
|
<Button onPress={() => undefined}>Save</Button>
|
|
13
21
|
</Stack>
|
|
14
22
|
</Card>
|
|
15
23
|
</ThemeProvider>
|
|
16
24
|
```
|
|
17
25
|
|
|
18
|
-
There is no CSS class API. `padding={4}` is spacing step 4, the same step as
|
|
26
|
+
There is no CSS class API. `padding={4}` is spacing step 4, the same step as
|
|
27
|
+
`sw-padding-4` on web. Native `Field` renders a controlled `TextInput` with a
|
|
28
|
+
visible label and optional hint or error; it accepts standard text-input props
|
|
29
|
+
except styling and controlled semantics owned by the component. `colorScheme`
|
|
30
|
+
is `"light"`, `"dark"`, or `"system"`. Named palettes include both schemes;
|
|
31
|
+
`colors` may be `{ light, dark }`.
|
|
32
|
+
|
|
33
|
+
`SegmentedControl` uses the active palette's accent/onAccent pair for the
|
|
34
|
+
selected section, with a 44-point minimum touch height. Labels grow with
|
|
35
|
+
system text size. The native example demonstrates switching sections.
|
|
19
36
|
|
|
20
37
|
React and React Native are peer dependencies so the host Expo app owns the runtime. Rationale: duplicating React Native inside this package would fight Metro and Expo upgrades.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export type AccordionProps = {
|
|
3
|
+
accessibilityLabel: string;
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
leading?: ReactNode;
|
|
6
|
+
metadata?: ReactNode;
|
|
7
|
+
onOpenChange: (open: boolean) => void;
|
|
8
|
+
onTitlePress?: () => void;
|
|
9
|
+
open: boolean;
|
|
10
|
+
title: string;
|
|
11
|
+
titleAccessibilityLabel?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function Accordion({ accessibilityLabel, children, leading, metadata, onOpenChange, onTitlePress, open, title, titleAccessibilityLabel, }: AccordionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Pressable } from 'react-native';
|
|
3
|
+
import { mapAccordionChevronStyle, mapAccordionHeaderStyle, mapAccordionPressStyle, mapAccordionStyle, } from '../map-accordion-style.js';
|
|
4
|
+
import { useTheme } from '../theme/ThemeProvider.js';
|
|
5
|
+
import { Box } from './Box.js';
|
|
6
|
+
import { Text } from './Text.js';
|
|
7
|
+
export function Accordion({ accessibilityLabel, children, leading, metadata, onOpenChange, onTitlePress, open, title, titleAccessibilityLabel, }) {
|
|
8
|
+
const theme = useTheme();
|
|
9
|
+
const toggle = () => onOpenChange(!open);
|
|
10
|
+
const chevron = (_jsx(Box, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: mapAccordionChevronStyle(theme, open) }));
|
|
11
|
+
return (_jsxs(Box, { style: mapAccordionStyle(theme), children: [_jsxs(Box, { style: mapAccordionHeaderStyle(theme), children: [_jsxs(Pressable, { accessibilityRole: "button", accessibilityLabel: onTitlePress
|
|
12
|
+
? (titleAccessibilityLabel ?? title)
|
|
13
|
+
: accessibilityLabel, accessibilityState: onTitlePress ? undefined : { expanded: open }, onPress: onTitlePress ?? toggle, style: ({ pressed }) => [
|
|
14
|
+
mapAccordionPressStyle(theme, pressed),
|
|
15
|
+
{ flex: 1, minWidth: 0 },
|
|
16
|
+
], children: [leading, _jsx(Text, { variant: "label", style: { flex: 1, minWidth: 0 }, children: title }), !onTitlePress ? (_jsxs(_Fragment, { children: [metadata, chevron] })) : null] }), onTitlePress ? (_jsxs(Pressable, { accessibilityRole: "button", accessibilityLabel: accessibilityLabel, accessibilityState: { expanded: open }, onPress: toggle, style: ({ pressed }) => mapAccordionPressStyle(theme, pressed), children: [metadata, chevron] })) : null] }), open ? (_jsx(Box, { paddingX: 1, paddingBottom: 1, children: children })) : null] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type TextInputProps } from 'react-native';
|
|
2
|
+
export type FieldProps = Omit<TextInputProps, 'accessibilityLabel' | 'accessibilityState' | 'editable' | 'onChangeText' | 'style' | 'value'> & {
|
|
3
|
+
disabled?: boolean;
|
|
4
|
+
error?: string;
|
|
5
|
+
hint?: string;
|
|
6
|
+
label: string;
|
|
7
|
+
onChangeText: (value: string) => void;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function Field({ disabled, error, hint, label, onBlur, onChangeText, onFocus, value, ...inputProps }: FieldProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { TextInput } from 'react-native';
|
|
4
|
+
import { mapFieldAccessibility } from '../map-field-accessibility.js';
|
|
5
|
+
import { mapFieldInputStyle } from '../map-field-style.js';
|
|
6
|
+
import { useTheme } from '../theme/ThemeProvider.js';
|
|
7
|
+
import { Stack } from './Stack.js';
|
|
8
|
+
import { Text } from './Text.js';
|
|
9
|
+
export function Field({ disabled = false, error, hint, label, onBlur, onChangeText, onFocus, value, ...inputProps }) {
|
|
10
|
+
const theme = useTheme();
|
|
11
|
+
const [focused, setFocused] = useState(false);
|
|
12
|
+
const supportingText = error ?? hint;
|
|
13
|
+
return (_jsxs(Stack, { gap: 1, children: [_jsx(Text, { variant: "label", children: label }), _jsx(TextInput, { ...inputProps, ...mapFieldAccessibility({
|
|
14
|
+
accessibilityHint: inputProps.accessibilityHint,
|
|
15
|
+
disabled,
|
|
16
|
+
error,
|
|
17
|
+
hint,
|
|
18
|
+
label,
|
|
19
|
+
}), editable: !disabled, onBlur: (event) => {
|
|
20
|
+
setFocused(false);
|
|
21
|
+
onBlur?.(event);
|
|
22
|
+
}, onChangeText: onChangeText, onFocus: (event) => {
|
|
23
|
+
setFocused(true);
|
|
24
|
+
onFocus?.(event);
|
|
25
|
+
}, placeholderTextColor: theme.colors.muted, style: mapFieldInputStyle(theme, {
|
|
26
|
+
disabled,
|
|
27
|
+
focused,
|
|
28
|
+
invalid: Boolean(error),
|
|
29
|
+
}), value: value }), supportingText ? (_jsx(Text, { accessibilityLiveRegion: error ? 'polite' : 'none', color: error ? 'danger' : 'muted', variant: "caption", children: supportingText })) : null] }));
|
|
30
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Box, type BoxProps } from './components/Box.js';
|
|
2
2
|
export { Button, type ButtonProps, type ButtonSize, type ButtonVariant, } from './components/Button.js';
|
|
3
3
|
export { Card, type CardProps, type CardVariant } from './components/Card.js';
|
|
4
|
+
export { Field, type FieldProps } from './components/Field.js';
|
|
4
5
|
export { Inline, type InlineProps } from './components/Inline.js';
|
|
5
6
|
export { Stack, type StackProps } from './components/Stack.js';
|
|
6
7
|
export { type Align, type Justify } from './alignment.js';
|
|
@@ -9,3 +10,4 @@ export { TabBar, TabBarTrailing, type TabBarItem, type TabBarProps, type TabBarT
|
|
|
9
10
|
export { Table, TableBody, TableCell, TableHeader, TableRow, type TableBodyProps, type TableCellProps, type TableDensity, type TableHeaderProps, type TableProps, type TableRowProps, } from './components/Table.js';
|
|
10
11
|
export { Text, type TextProps } from './components/Text.js';
|
|
11
12
|
export { ThemeProvider, useTheme, type ThemeProviderProps, } from './theme/ThemeProvider.js';
|
|
13
|
+
export { Accordion, type AccordionProps } from './components/Accordion.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Box } from './components/Box.js';
|
|
2
2
|
export { Button, } from './components/Button.js';
|
|
3
3
|
export { Card } from './components/Card.js';
|
|
4
|
+
export { Field } from './components/Field.js';
|
|
4
5
|
export { Inline } from './components/Inline.js';
|
|
5
6
|
export { Stack } from './components/Stack.js';
|
|
6
7
|
export { SegmentedControl, } from './components/SegmentedControl.js';
|
|
@@ -8,3 +9,4 @@ export { TabBar, TabBarTrailing, } from './components/TabBar.js';
|
|
|
8
9
|
export { Table, TableBody, TableCell, TableHeader, TableRow, } from './components/Table.js';
|
|
9
10
|
export { Text } from './components/Text.js';
|
|
10
11
|
export { ThemeProvider, useTheme, } from './theme/ThemeProvider.js';
|
|
12
|
+
export { Accordion } from './components/Accordion.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Theme } from '@scalewing/tokens';
|
|
2
|
+
import { type ViewStyle } from 'react-native';
|
|
3
|
+
export declare function mapAccordionStyle(theme: Theme): ViewStyle;
|
|
4
|
+
export declare function mapAccordionHeaderStyle(theme: Theme): ViewStyle;
|
|
5
|
+
export declare function mapAccordionPressStyle(theme: Theme, pressed: boolean): ViewStyle;
|
|
6
|
+
export declare function mapAccordionChevronStyle(theme: Theme, open: boolean): ViewStyle;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { focusRing } from '@scalewing/tokens';
|
|
2
|
+
export function mapAccordionStyle(theme) {
|
|
3
|
+
return {
|
|
4
|
+
backgroundColor: theme.colors.surface,
|
|
5
|
+
borderColor: theme.colors.border,
|
|
6
|
+
borderWidth: 1,
|
|
7
|
+
borderRadius: theme.radius.lg,
|
|
8
|
+
overflow: 'hidden',
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function mapAccordionHeaderStyle(theme) {
|
|
12
|
+
return {
|
|
13
|
+
flexDirection: 'row',
|
|
14
|
+
alignItems: 'stretch',
|
|
15
|
+
backgroundColor: theme.glass.fill,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function mapAccordionPressStyle(theme, pressed) {
|
|
19
|
+
return {
|
|
20
|
+
alignItems: 'center',
|
|
21
|
+
flexDirection: 'row',
|
|
22
|
+
gap: theme.space[3],
|
|
23
|
+
minHeight: theme.control.md.minHeight + theme.space[3],
|
|
24
|
+
minWidth: theme.control.md.minHeight,
|
|
25
|
+
paddingHorizontal: theme.space[4],
|
|
26
|
+
paddingVertical: theme.space[3],
|
|
27
|
+
opacity: pressed ? theme.disabledOpacity : 1,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function mapAccordionChevronStyle(theme, open) {
|
|
31
|
+
return {
|
|
32
|
+
width: theme.space[2],
|
|
33
|
+
height: theme.space[2],
|
|
34
|
+
borderBottomWidth: focusRing.width,
|
|
35
|
+
borderRightWidth: focusRing.width,
|
|
36
|
+
borderColor: theme.colors.muted,
|
|
37
|
+
transform: [{ rotate: open ? '225deg' : '45deg' }],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type TextInputProps } from 'react-native';
|
|
2
|
+
type FieldAccessibilityProps = Pick<TextInputProps, 'accessibilityHint' | 'accessibilityLabel' | 'accessibilityState'>;
|
|
3
|
+
export declare function mapFieldAccessibility(options: {
|
|
4
|
+
accessibilityHint?: string;
|
|
5
|
+
disabled: boolean;
|
|
6
|
+
error?: string;
|
|
7
|
+
hint?: string;
|
|
8
|
+
label: string;
|
|
9
|
+
}): FieldAccessibilityProps;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function mapFieldInputStyle(theme, options) {
|
|
2
|
+
const type = theme.typography.body;
|
|
3
|
+
return {
|
|
4
|
+
backgroundColor: theme.colors.surface,
|
|
5
|
+
borderColor: options.invalid
|
|
6
|
+
? theme.colors.danger
|
|
7
|
+
: options.focused
|
|
8
|
+
? theme.colors.accent
|
|
9
|
+
: theme.colors.border,
|
|
10
|
+
borderRadius: theme.radius.lg,
|
|
11
|
+
borderWidth: 1,
|
|
12
|
+
color: theme.colors.text,
|
|
13
|
+
fontSize: type.fontSize,
|
|
14
|
+
fontWeight: String(type.fontWeight),
|
|
15
|
+
letterSpacing: type.letterSpacing,
|
|
16
|
+
lineHeight: type.lineHeight,
|
|
17
|
+
minHeight: theme.control.md.minHeight,
|
|
18
|
+
opacity: options.disabled ? theme.disabledOpacity : 1,
|
|
19
|
+
paddingHorizontal: theme.control.md.paddingInline,
|
|
20
|
+
paddingVertical: theme.space[2],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -13,14 +13,14 @@ export function mapSegmentedControlStyle(theme) {
|
|
|
13
13
|
export function mapSegmentedItemStyle(theme, selected) {
|
|
14
14
|
return {
|
|
15
15
|
alignItems: 'center',
|
|
16
|
-
backgroundColor: selected ? theme.colors.
|
|
16
|
+
backgroundColor: selected ? theme.colors.accent : 'transparent',
|
|
17
17
|
borderRadius: theme.radius.pill,
|
|
18
18
|
flex: 1,
|
|
19
19
|
justifyContent: 'center',
|
|
20
|
-
minHeight: theme.control.
|
|
20
|
+
minHeight: theme.control.md.minHeight,
|
|
21
21
|
paddingHorizontal: theme.control.xs.paddingInline,
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
export function segmentedItemColor(selected) {
|
|
25
|
-
return selected ? '
|
|
25
|
+
return selected ? 'onAccent' : 'muted';
|
|
26
26
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalewing/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Scalewing React Native layout primitives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"README.md"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@scalewing/tokens": "0.
|
|
31
|
+
"@scalewing/tokens": "0.6.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"react": "^19.0.0",
|
|
@@ -36,8 +36,10 @@
|
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/react": "^19.2.2",
|
|
39
|
+
"@types/react-test-renderer": "^19.3.0",
|
|
39
40
|
"react": "19.2.3",
|
|
40
|
-
"react-native": "0.86.3"
|
|
41
|
+
"react-native": "0.86.3",
|
|
42
|
+
"react-test-renderer": "19.2.3"
|
|
41
43
|
},
|
|
42
44
|
"publishConfig": {
|
|
43
45
|
"access": "public"
|