@hzblj/kara-components 0.19.0 → 0.20.1
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 +24 -0
- package/lib/module/atoms/segmented-control/segmented-control.android.js +13 -8
- package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.ios.js +10 -3
- package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -1
- package/lib/typescript/atoms/segmented-control/segmented-control.ios.d.ts +2 -1
- package/lib/typescript/atoms/segmented-control/segmented-control.types.d.ts +19 -1
- package/package.json +4 -4
- package/src/atoms/segmented-control/segmented-control.android.tsx +12 -8
- package/src/atoms/segmented-control/segmented-control.ios.tsx +9 -2
- package/src/atoms/segmented-control/segmented-control.types.ts +19 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @hzblj/kara-components
|
|
2
2
|
|
|
3
|
+
## 0.20.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#62](https://github.com/hzblj/kara-ui/pull/62) [`0c62a1f`](https://github.com/hzblj/kara-ui/commit/0c62a1f5950d679d2d87a0351081f78154c54704) Thanks [@hzblj](https://github.com/hzblj)! - A `SegmentedControl` with `bg` now shows exactly that colour on iOS. UIKit drew its own track over a grey system fill on top of kara's, whatever colour the control was given, so a white track showed grey inside a white rim. The control now hides those track images (alpha 0 after each of its layouts) while kara draws the track, and the Liquid Glass pill stays the system's.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`0c62a1f`](https://github.com/hzblj/kara-ui/commit/0c62a1f5950d679d2d87a0351081f78154c54704)]:
|
|
10
|
+
- @hzblj/kara-native@0.20.1
|
|
11
|
+
- @hzblj/kara-theme@0.20.1
|
|
12
|
+
- @hzblj/kara-sx@0.20.1
|
|
13
|
+
|
|
14
|
+
## 0.20.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- [#60](https://github.com/hzblj/kara-ui/pull/60) [`a93a723`](https://github.com/hzblj/kara-ui/commit/a93a72369a166f22d7aed19b99d5c889b61739aa) Thanks [@hzblj](https://github.com/hzblj)! - `SegmentedControl` takes its track and its labels through the `sx` names: `bg` colours the track, `p` is the room between the track's edge and the selected pill, and `variant` sets the labels in a typography variant, as `Text` does. On iOS a `bg` track is kara-native's own view, a capsule in exactly that colour under a clear `UISegmentedControl`, because UIKit renders a control's own background translucent; the pill stays the system's (Liquid Glass on iOS 26) and the track grows by `p`. Android fills the unselected segments with `bg`; Material draws no pill to inset. kara-native's `SegmentedControl` binding gains `trackPadding`, `fontWeight` (the system face's weight, so a regular variant is no longer drawn medium) and `letterSpacing`. On iOS 26 the titles under a held or dragged pill keep their colours instead of turning the system black.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`a93a723`](https://github.com/hzblj/kara-ui/commit/a93a72369a166f22d7aed19b99d5c889b61739aa)]:
|
|
23
|
+
- @hzblj/kara-native@0.20.0
|
|
24
|
+
- @hzblj/kara-sx@0.20.0
|
|
25
|
+
- @hzblj/kara-theme@0.20.0
|
|
26
|
+
|
|
3
27
|
## 0.19.0
|
|
4
28
|
|
|
5
29
|
### Minor Changes
|
|
@@ -24,10 +24,11 @@ const MATERIAL_HEIGHT = 40;
|
|
|
24
24
|
*/
|
|
25
25
|
const SegmentLabel = ({
|
|
26
26
|
iconSize,
|
|
27
|
-
segment
|
|
27
|
+
segment,
|
|
28
|
+
variant
|
|
28
29
|
}) => {
|
|
29
30
|
const theme = useAppTheme();
|
|
30
|
-
const face = theme.typography
|
|
31
|
+
const face = theme.typography[variant];
|
|
31
32
|
if (segment.image !== undefined) {
|
|
32
33
|
return /*#__PURE__*/_jsx(NativeImage, {
|
|
33
34
|
fontFamily: face.fontFamily,
|
|
@@ -59,16 +60,18 @@ const SegmentLabel = ({
|
|
|
59
60
|
* Material's single-choice segmented buttons, the selected one filled with the accent like a checked `Switch`. Its own
|
|
60
61
|
* host, themed like an `Island`, so it drops into React Native layout the way the iOS control does. Inside a `Screen`
|
|
61
62
|
* or `Island` it renders into that host instead (see `useInsideHost`). Material plays nothing on a new segment, so this
|
|
62
|
-
* plays `selection`.
|
|
63
|
+
* plays `selection`. `bg` fills the unselected segments; `p` has nothing to inset, as Material draws no pill.
|
|
63
64
|
*/
|
|
64
65
|
export const SegmentedControl = ({
|
|
66
|
+
bg,
|
|
65
67
|
disabled = false,
|
|
66
68
|
iconSize = 's20',
|
|
67
69
|
onChange,
|
|
68
70
|
selectedIndex,
|
|
69
71
|
spacing,
|
|
70
72
|
testID,
|
|
71
|
-
values
|
|
73
|
+
values,
|
|
74
|
+
variant = 'subheadlineMedium'
|
|
72
75
|
}) => {
|
|
73
76
|
const theme = useAppTheme();
|
|
74
77
|
const insideHost = useInsideHost();
|
|
@@ -81,9 +84,9 @@ export const SegmentedControl = ({
|
|
|
81
84
|
if (spacing === undefined) {
|
|
82
85
|
return [weight(1)];
|
|
83
86
|
}
|
|
84
|
-
const content = Math.max(theme.typography.
|
|
87
|
+
const content = Math.max(theme.typography[variant].lineHeight, iconPoints);
|
|
85
88
|
return [weight(1), height(Math.max(MATERIAL_HEIGHT, content + 2 * spacingTokens[spacing]))];
|
|
86
|
-
}, [iconPoints, spacing, theme]);
|
|
89
|
+
}, [iconPoints, spacing, theme, variant]);
|
|
87
90
|
|
|
88
91
|
// Every segment keeps the same outline, the selected one too: an accent border would run its fill out over the line
|
|
89
92
|
// the others draw inside, and make it look taller than its neighbours.
|
|
@@ -97,8 +100,9 @@ export const SegmentedControl = ({
|
|
|
97
100
|
disabledInactiveBorderColor: theme.color('border.tertiary'),
|
|
98
101
|
disabledInactiveContentColor: theme.color('btn.content.disabled'),
|
|
99
102
|
inactiveBorderColor: theme.color('border.secondary.base'),
|
|
103
|
+
inactiveContainerColor: bg === undefined ? undefined : theme.color(bg),
|
|
100
104
|
inactiveContentColor: theme.color('content.primary')
|
|
101
|
-
}), [theme]);
|
|
105
|
+
}), [bg, theme]);
|
|
102
106
|
const rowModifiers = useMemo(() => testID ? [fillMaxWidth(), testIdModifier(testID)] : [fillMaxWidth()], [testID]);
|
|
103
107
|
|
|
104
108
|
// Material reports a tap on the segment that is already selected too; iOS does not, and neither does this.
|
|
@@ -120,7 +124,8 @@ export const SegmentedControl = ({
|
|
|
120
124
|
children: /*#__PURE__*/_jsx(SegmentedButton.Label, {
|
|
121
125
|
children: /*#__PURE__*/_jsx(SegmentLabel, {
|
|
122
126
|
iconSize: iconPoints,
|
|
123
|
-
segment: segment
|
|
127
|
+
segment: segment,
|
|
128
|
+
variant: variant
|
|
124
129
|
})
|
|
125
130
|
})
|
|
126
131
|
}, index))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Host","SegmentedButton","SingleChoiceSegmentedButtonRow","Text","fillMaxWidth","height","testID","testIdModifier","weight","iconResourceName","haptics","DrawableIcon","NativeImage","spacing","spacingTokens","useAppTheme","useCallback","useMemo","useInsideHost","HStack","imageUri","segmentOf","jsx","_jsx","jsxs","_jsxs","MATCH_HEIGHT_ONLY","horizontal","vertical","MATERIAL_HEIGHT","SegmentLabel","iconSize","segment","theme","face","typography","
|
|
1
|
+
{"version":3,"names":["Host","SegmentedButton","SingleChoiceSegmentedButtonRow","Text","fillMaxWidth","height","testID","testIdModifier","weight","iconResourceName","haptics","DrawableIcon","NativeImage","spacing","spacingTokens","useAppTheme","useCallback","useMemo","useInsideHost","HStack","imageUri","segmentOf","jsx","_jsx","jsxs","_jsxs","MATCH_HEIGHT_ONLY","horizontal","vertical","MATERIAL_HEIGHT","SegmentLabel","iconSize","segment","variant","theme","face","typography","image","undefined","fontFamily","fontSize","fontWeight","String","imageHeight","label","source","style","children","icon","align","resourceName","SegmentedControl","bg","disabled","onChange","selectedIndex","values","insideHost","segments","map","iconPoints","segmentModifiers","content","Math","max","lineHeight","colors","activeBorderColor","color","activeContainerColor","activeContentColor","disabledActiveBorderColor","disabledActiveContainerColor","disabledActiveContentColor","disabledInactiveBorderColor","disabledInactiveContentColor","inactiveBorderColor","inactiveContainerColor","inactiveContentColor","rowModifiers","select","index","selection","row","modifiers","enabled","onClick","selected","Label","colorScheme","dark","matchContents","seedColor","materialSeed"],"sourceRoot":"../../../../src","sources":["atoms/segmented-control/segmented-control.android.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -19,20 +19,24 @@ const MATCH_HEIGHT_ONLY = {
|
|
|
19
19
|
* the width it is given and UIKit's height. Inside a `Screen` or `Island` it is carried into that host by `RNHostView`
|
|
20
20
|
* (see `useInsideHost`), the host's width and its own height. UIKit plays the control's own feedback. A segment shows a
|
|
21
21
|
* title or an image, never both, so an icon segment is drawn natively as one image of the icon and the label, in the
|
|
22
|
-
* label's colour; an image segment is the image alone, loaded and drawn by kara-native at `iconSize`.
|
|
22
|
+
* label's colour; an image segment is the image alone, loaded and drawn by kara-native at `iconSize`. With `bg` the
|
|
23
|
+
* track is kara-native's own view under a clear control, inset by `p`, so the pill stays the system's.
|
|
23
24
|
*/
|
|
24
25
|
export const SegmentedControl = ({
|
|
26
|
+
bg,
|
|
25
27
|
disabled = false,
|
|
26
28
|
iconSize = 's20',
|
|
27
29
|
onChange,
|
|
30
|
+
p,
|
|
28
31
|
selectedIndex,
|
|
29
32
|
spacing,
|
|
30
33
|
testID,
|
|
31
|
-
values
|
|
34
|
+
values,
|
|
35
|
+
variant = 'footnoteMedium'
|
|
32
36
|
}) => {
|
|
33
37
|
const theme = useAppTheme();
|
|
34
38
|
const insideHost = useInsideHost();
|
|
35
|
-
const face = theme.typography
|
|
39
|
+
const face = theme.typography[variant];
|
|
36
40
|
const segments = useMemo(() => values.map(segmentOf), [values]);
|
|
37
41
|
|
|
38
42
|
// The native control speaks in values; a segment's is its index, so two labels may read the same.
|
|
@@ -52,12 +56,15 @@ export const SegmentedControl = ({
|
|
|
52
56
|
disabled: disabled,
|
|
53
57
|
fontFamily: face.fontFamily,
|
|
54
58
|
fontSize: face.fontSize,
|
|
59
|
+
fontWeight: face.fontWeight,
|
|
55
60
|
iconSize: spacingTokens[iconSize],
|
|
56
61
|
inactiveTextColor: theme.color('content.primary'),
|
|
57
62
|
onValueChange: onValueChange,
|
|
58
63
|
options: options,
|
|
59
64
|
selectedValue: String(selectedIndex),
|
|
60
65
|
testID: testID,
|
|
66
|
+
trackColor: bg === undefined ? undefined : theme.color(bg),
|
|
67
|
+
trackPadding: p === undefined ? undefined : spacingTokens[p],
|
|
61
68
|
verticalPadding: spacing === undefined ? undefined : spacingTokens[spacing]
|
|
62
69
|
});
|
|
63
70
|
return insideHost ? /*#__PURE__*/_jsx(RNHostView, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["RNHostView","iconResourceName","SegmentedControl","NativeSegmentedControl","spacing","spacingTokens","useAppTheme","useCallback","useMemo","useInsideHost","imageUri","segmentOf","jsx","_jsx","MATCH_HEIGHT_ONLY","horizontal","vertical","disabled","iconSize","onChange","selectedIndex","testID","values","theme","insideHost","face","typography","
|
|
1
|
+
{"version":3,"names":["RNHostView","iconResourceName","SegmentedControl","NativeSegmentedControl","spacing","spacingTokens","useAppTheme","useCallback","useMemo","useInsideHost","imageUri","segmentOf","jsx","_jsx","MATCH_HEIGHT_ONLY","horizontal","vertical","bg","disabled","iconSize","onChange","p","selectedIndex","testID","values","variant","theme","insideHost","face","typography","segments","map","options","segment","index","icon","undefined","image","label","value","String","onValueChange","Number","control","activePillColor","color","activeTextColor","fontFamily","fontSize","fontWeight","inactiveTextColor","selectedValue","trackColor","trackPadding","verticalPadding","matchContents","children"],"sourceRoot":"../../../../src","sources":["atoms/segmented-control/segmented-control.ios.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -5,6 +5,7 @@ import type { SegmentedControlComponent } from './segmented-control.types';
|
|
|
5
5
|
* the width it is given and UIKit's height. Inside a `Screen` or `Island` it is carried into that host by `RNHostView`
|
|
6
6
|
* (see `useInsideHost`), the host's width and its own height. UIKit plays the control's own feedback. A segment shows a
|
|
7
7
|
* title or an image, never both, so an icon segment is drawn natively as one image of the icon and the label, in the
|
|
8
|
-
* label's colour; an image segment is the image alone, loaded and drawn by kara-native at `iconSize`.
|
|
8
|
+
* label's colour; an image segment is the image alone, loaded and drawn by kara-native at `iconSize`. With `bg` the
|
|
9
|
+
* track is kara-native's own view under a clear control, inset by `p`, so the pill stays the system's.
|
|
9
10
|
*/
|
|
10
11
|
export declare const SegmentedControl: SegmentedControlComponent;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SpaceSize } from '@hzblj/kara-theme';
|
|
1
|
+
import type { ColorToken, SpaceSize, TextVariant } from '@hzblj/kara-theme';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
3
|
import type { ImageSourcePropType } from 'react-native';
|
|
4
4
|
import type { IconName } from '../icon';
|
|
@@ -20,8 +20,16 @@ export type SegmentedControlSegment = {
|
|
|
20
20
|
* brings its own host. Inside one it joins that host, so it can share a stack with other native views. It takes the
|
|
21
21
|
* width it is given; the height is the platform's, or taller with `spacing`. A segment can lead with one of the app's
|
|
22
22
|
* icons or be an image, a logo, both loaded natively (SDWebImage on iOS, androidsvg on Android, as Footshop does).
|
|
23
|
+
* The track takes the `sx` names a component sized by props uses: `bg` for its colour, `p` for the room around the
|
|
24
|
+
* pill, and `variant` sets the labels like `Text`.
|
|
23
25
|
*/
|
|
24
26
|
export type SegmentedControlProps = {
|
|
27
|
+
/**
|
|
28
|
+
* The track's colour, a palette token. iOS draws it itself as a capsule under the control, in exactly this colour
|
|
29
|
+
* (UIKit renders a control's own background translucent); Android fills the unselected segments with it. The
|
|
30
|
+
* platform's own track when unset.
|
|
31
|
+
*/
|
|
32
|
+
bg?: ColorToken;
|
|
25
33
|
/** Stops the user changing the segment, and dims the control. */
|
|
26
34
|
disabled?: boolean;
|
|
27
35
|
/** The height of the segments' icons and images, a spacing token. `s20` by default. */
|
|
@@ -31,6 +39,11 @@ export type SegmentedControlProps = {
|
|
|
31
39
|
* Android plays `selection`; iOS's control plays its own feedback.
|
|
32
40
|
*/
|
|
33
41
|
onChange?: (index: number, value: string) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Room between the track's edge and the selected pill, a spacing token, with `bg`: the track grows by it and the pill
|
|
44
|
+
* keeps its height. iOS only, UIKit's own `s2` when unset; Material's segments have no pill to inset.
|
|
45
|
+
*/
|
|
46
|
+
p?: SpaceSize;
|
|
34
47
|
/** The selected segment. Controlled: pass back what `onChange` reports. */
|
|
35
48
|
selectedIndex: number;
|
|
36
49
|
/**
|
|
@@ -42,5 +55,10 @@ export type SegmentedControlProps = {
|
|
|
42
55
|
testID?: string;
|
|
43
56
|
/** The segments, in order: a label, or a segment with an icon or an image. */
|
|
44
57
|
values: (string | SegmentedControlSegment)[];
|
|
58
|
+
/**
|
|
59
|
+
* The labels' typography variant, the app's face if it has one. `footnoteMedium` on iOS and `subheadlineMedium` on
|
|
60
|
+
* Android by default, each platform's own segmented size. Not scaled with Dynamic Type on iOS.
|
|
61
|
+
*/
|
|
62
|
+
variant?: TextVariant;
|
|
45
63
|
};
|
|
46
64
|
export type SegmentedControlComponent = (props: SegmentedControlProps) => ReactNode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hzblj/kara-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"description": "The atoms, molecules, organisms and templates of @hzblj/kara-ui — real SwiftUI and Jetpack Compose views through @expo/ui.",
|
|
5
5
|
"author": "Jan Blazej",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@hzblj/expo-native-icons": "^0.1.0",
|
|
63
63
|
"@hzblj/expo-rich-haptics": "^0.1.0",
|
|
64
|
-
"@hzblj/kara-native": "^0.
|
|
65
|
-
"@hzblj/kara-sx": "^0.
|
|
66
|
-
"@hzblj/kara-theme": "^0.
|
|
64
|
+
"@hzblj/kara-native": "^0.20.1",
|
|
65
|
+
"@hzblj/kara-sx": "^0.20.1",
|
|
66
|
+
"@hzblj/kara-theme": "^0.20.1",
|
|
67
67
|
"sf-symbols-typescript": "^2.2.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
@@ -3,7 +3,7 @@ import {fillMaxWidth, height, testID as testIdModifier, weight} from '@expo/ui/j
|
|
|
3
3
|
import {iconResourceName} from '@hzblj/expo-native-icons'
|
|
4
4
|
import {haptics} from '@hzblj/expo-rich-haptics'
|
|
5
5
|
import {DrawableIcon, NativeImage} from '@hzblj/kara-native/jetpack-compose'
|
|
6
|
-
import {spacing as spacingTokens, useAppTheme} from '@hzblj/kara-theme'
|
|
6
|
+
import {spacing as spacingTokens, type TextVariant, useAppTheme} from '@hzblj/kara-theme'
|
|
7
7
|
import {useCallback, useMemo} from 'react'
|
|
8
8
|
|
|
9
9
|
import {useInsideHost} from '../../host-context'
|
|
@@ -19,15 +19,16 @@ const MATERIAL_HEIGHT = 40
|
|
|
19
19
|
type SegmentLabelProps = {
|
|
20
20
|
iconSize: number
|
|
21
21
|
segment: SegmentedControlSegment
|
|
22
|
+
variant: TextVariant
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* What a segment shows: its label, one of the app's icons before it, or an image loaded by kara-native. The icon and a
|
|
26
27
|
* label the image falls back to take the segment's content colour, so they follow the selection and `disabled`.
|
|
27
28
|
*/
|
|
28
|
-
const SegmentLabel = ({iconSize, segment}: SegmentLabelProps) => {
|
|
29
|
+
const SegmentLabel = ({iconSize, segment, variant}: SegmentLabelProps) => {
|
|
29
30
|
const theme = useAppTheme()
|
|
30
|
-
const face = theme.typography
|
|
31
|
+
const face = theme.typography[variant]
|
|
31
32
|
|
|
32
33
|
if (segment.image !== undefined) {
|
|
33
34
|
return (
|
|
@@ -60,9 +61,10 @@ const SegmentLabel = ({iconSize, segment}: SegmentLabelProps) => {
|
|
|
60
61
|
* Material's single-choice segmented buttons, the selected one filled with the accent like a checked `Switch`. Its own
|
|
61
62
|
* host, themed like an `Island`, so it drops into React Native layout the way the iOS control does. Inside a `Screen`
|
|
62
63
|
* or `Island` it renders into that host instead (see `useInsideHost`). Material plays nothing on a new segment, so this
|
|
63
|
-
* plays `selection`.
|
|
64
|
+
* plays `selection`. `bg` fills the unselected segments; `p` has nothing to inset, as Material draws no pill.
|
|
64
65
|
*/
|
|
65
66
|
export const SegmentedControl: SegmentedControlComponent = ({
|
|
67
|
+
bg,
|
|
66
68
|
disabled = false,
|
|
67
69
|
iconSize = 's20',
|
|
68
70
|
onChange,
|
|
@@ -70,6 +72,7 @@ export const SegmentedControl: SegmentedControlComponent = ({
|
|
|
70
72
|
spacing,
|
|
71
73
|
testID,
|
|
72
74
|
values,
|
|
75
|
+
variant = 'subheadlineMedium',
|
|
73
76
|
}: SegmentedControlProps) => {
|
|
74
77
|
const theme = useAppTheme()
|
|
75
78
|
const insideHost = useInsideHost()
|
|
@@ -83,9 +86,9 @@ export const SegmentedControl: SegmentedControlComponent = ({
|
|
|
83
86
|
return [weight(1)]
|
|
84
87
|
}
|
|
85
88
|
|
|
86
|
-
const content = Math.max(theme.typography.
|
|
89
|
+
const content = Math.max(theme.typography[variant].lineHeight, iconPoints)
|
|
87
90
|
return [weight(1), height(Math.max(MATERIAL_HEIGHT, content + 2 * spacingTokens[spacing]))]
|
|
88
|
-
}, [iconPoints, spacing, theme])
|
|
91
|
+
}, [iconPoints, spacing, theme, variant])
|
|
89
92
|
|
|
90
93
|
// Every segment keeps the same outline, the selected one too: an accent border would run its fill out over the line
|
|
91
94
|
// the others draw inside, and make it look taller than its neighbours.
|
|
@@ -100,9 +103,10 @@ export const SegmentedControl: SegmentedControlComponent = ({
|
|
|
100
103
|
disabledInactiveBorderColor: theme.color('border.tertiary'),
|
|
101
104
|
disabledInactiveContentColor: theme.color('btn.content.disabled'),
|
|
102
105
|
inactiveBorderColor: theme.color('border.secondary.base'),
|
|
106
|
+
inactiveContainerColor: bg === undefined ? undefined : theme.color(bg),
|
|
103
107
|
inactiveContentColor: theme.color('content.primary'),
|
|
104
108
|
}),
|
|
105
|
-
[theme]
|
|
109
|
+
[bg, theme]
|
|
106
110
|
)
|
|
107
111
|
|
|
108
112
|
const rowModifiers = useMemo(() => (testID ? [fillMaxWidth(), testIdModifier(testID)] : [fillMaxWidth()]), [testID])
|
|
@@ -132,7 +136,7 @@ export const SegmentedControl: SegmentedControlComponent = ({
|
|
|
132
136
|
selected={index === selectedIndex}
|
|
133
137
|
>
|
|
134
138
|
<SegmentedButton.Label>
|
|
135
|
-
<SegmentLabel iconSize={iconPoints} segment={segment} />
|
|
139
|
+
<SegmentLabel iconSize={iconPoints} segment={segment} variant={variant} />
|
|
136
140
|
</SegmentedButton.Label>
|
|
137
141
|
</SegmentedButton>
|
|
138
142
|
))}
|
|
@@ -16,20 +16,24 @@ const MATCH_HEIGHT_ONLY = {horizontal: false, vertical: true}
|
|
|
16
16
|
* the width it is given and UIKit's height. Inside a `Screen` or `Island` it is carried into that host by `RNHostView`
|
|
17
17
|
* (see `useInsideHost`), the host's width and its own height. UIKit plays the control's own feedback. A segment shows a
|
|
18
18
|
* title or an image, never both, so an icon segment is drawn natively as one image of the icon and the label, in the
|
|
19
|
-
* label's colour; an image segment is the image alone, loaded and drawn by kara-native at `iconSize`.
|
|
19
|
+
* label's colour; an image segment is the image alone, loaded and drawn by kara-native at `iconSize`. With `bg` the
|
|
20
|
+
* track is kara-native's own view under a clear control, inset by `p`, so the pill stays the system's.
|
|
20
21
|
*/
|
|
21
22
|
export const SegmentedControl: SegmentedControlComponent = ({
|
|
23
|
+
bg,
|
|
22
24
|
disabled = false,
|
|
23
25
|
iconSize = 's20',
|
|
24
26
|
onChange,
|
|
27
|
+
p,
|
|
25
28
|
selectedIndex,
|
|
26
29
|
spacing,
|
|
27
30
|
testID,
|
|
28
31
|
values,
|
|
32
|
+
variant = 'footnoteMedium',
|
|
29
33
|
}: SegmentedControlProps) => {
|
|
30
34
|
const theme = useAppTheme()
|
|
31
35
|
const insideHost = useInsideHost()
|
|
32
|
-
const face = theme.typography
|
|
36
|
+
const face = theme.typography[variant]
|
|
33
37
|
|
|
34
38
|
const segments = useMemo(() => values.map(segmentOf), [values])
|
|
35
39
|
|
|
@@ -60,12 +64,15 @@ export const SegmentedControl: SegmentedControlComponent = ({
|
|
|
60
64
|
disabled={disabled}
|
|
61
65
|
fontFamily={face.fontFamily}
|
|
62
66
|
fontSize={face.fontSize}
|
|
67
|
+
fontWeight={face.fontWeight}
|
|
63
68
|
iconSize={spacingTokens[iconSize]}
|
|
64
69
|
inactiveTextColor={theme.color('content.primary')}
|
|
65
70
|
onValueChange={onValueChange}
|
|
66
71
|
options={options}
|
|
67
72
|
selectedValue={String(selectedIndex)}
|
|
68
73
|
testID={testID}
|
|
74
|
+
trackColor={bg === undefined ? undefined : theme.color(bg)}
|
|
75
|
+
trackPadding={p === undefined ? undefined : spacingTokens[p]}
|
|
69
76
|
verticalPadding={spacing === undefined ? undefined : spacingTokens[spacing]}
|
|
70
77
|
/>
|
|
71
78
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {SpaceSize} from '@hzblj/kara-theme'
|
|
1
|
+
import type {ColorToken, SpaceSize, TextVariant} from '@hzblj/kara-theme'
|
|
2
2
|
import type {ReactNode} from 'react'
|
|
3
3
|
import type {ImageSourcePropType} from 'react-native'
|
|
4
4
|
|
|
@@ -23,8 +23,16 @@ export type SegmentedControlSegment = {
|
|
|
23
23
|
* brings its own host. Inside one it joins that host, so it can share a stack with other native views. It takes the
|
|
24
24
|
* width it is given; the height is the platform's, or taller with `spacing`. A segment can lead with one of the app's
|
|
25
25
|
* icons or be an image, a logo, both loaded natively (SDWebImage on iOS, androidsvg on Android, as Footshop does).
|
|
26
|
+
* The track takes the `sx` names a component sized by props uses: `bg` for its colour, `p` for the room around the
|
|
27
|
+
* pill, and `variant` sets the labels like `Text`.
|
|
26
28
|
*/
|
|
27
29
|
export type SegmentedControlProps = {
|
|
30
|
+
/**
|
|
31
|
+
* The track's colour, a palette token. iOS draws it itself as a capsule under the control, in exactly this colour
|
|
32
|
+
* (UIKit renders a control's own background translucent); Android fills the unselected segments with it. The
|
|
33
|
+
* platform's own track when unset.
|
|
34
|
+
*/
|
|
35
|
+
bg?: ColorToken
|
|
28
36
|
/** Stops the user changing the segment, and dims the control. */
|
|
29
37
|
disabled?: boolean
|
|
30
38
|
/** The height of the segments' icons and images, a spacing token. `s20` by default. */
|
|
@@ -34,6 +42,11 @@ export type SegmentedControlProps = {
|
|
|
34
42
|
* Android plays `selection`; iOS's control plays its own feedback.
|
|
35
43
|
*/
|
|
36
44
|
onChange?: (index: number, value: string) => void
|
|
45
|
+
/**
|
|
46
|
+
* Room between the track's edge and the selected pill, a spacing token, with `bg`: the track grows by it and the pill
|
|
47
|
+
* keeps its height. iOS only, UIKit's own `s2` when unset; Material's segments have no pill to inset.
|
|
48
|
+
*/
|
|
49
|
+
p?: SpaceSize
|
|
37
50
|
/** The selected segment. Controlled: pass back what `onChange` reports. */
|
|
38
51
|
selectedIndex: number
|
|
39
52
|
/**
|
|
@@ -45,6 +58,11 @@ export type SegmentedControlProps = {
|
|
|
45
58
|
testID?: string
|
|
46
59
|
/** The segments, in order: a label, or a segment with an icon or an image. */
|
|
47
60
|
values: (string | SegmentedControlSegment)[]
|
|
61
|
+
/**
|
|
62
|
+
* The labels' typography variant, the app's face if it has one. `footnoteMedium` on iOS and `subheadlineMedium` on
|
|
63
|
+
* Android by default, each platform's own segmented size. Not scaled with Dynamic Type on iOS.
|
|
64
|
+
*/
|
|
65
|
+
variant?: TextVariant
|
|
48
66
|
}
|
|
49
67
|
|
|
50
68
|
export type SegmentedControlComponent = (props: SegmentedControlProps) => ReactNode
|