@hzblj/kara-components 0.10.0 → 0.11.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 +27 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.android.js +6 -1
- package/lib/module/atoms/activity-indicator/activity-indicator.android.js.map +1 -1
- package/lib/module/atoms/activity-indicator/activity-indicator.ios.js +2 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.ios.js.map +1 -1
- package/lib/module/atoms/avatar/avatar.js +5 -0
- package/lib/module/atoms/avatar/avatar.js.map +1 -1
- package/lib/module/atoms/button/button-tokens.js +2 -2
- package/lib/module/atoms/button/button-tokens.js.map +1 -1
- package/lib/module/atoms/button/button.android.js +6 -2
- package/lib/module/atoms/button/button.android.js.map +1 -1
- package/lib/module/atoms/button/button.ios.js +7 -2
- package/lib/module/atoms/button/button.ios.js.map +1 -1
- package/lib/module/atoms/card/card.android.js +5 -3
- package/lib/module/atoms/card/card.android.js.map +1 -1
- package/lib/module/atoms/card/card.ios.js +7 -3
- package/lib/module/atoms/card/card.ios.js.map +1 -1
- package/lib/module/atoms/checkbox/checkbox.android.js +1 -1
- package/lib/module/atoms/checkbox/checkbox.android.js.map +1 -1
- package/lib/module/atoms/fab/fab.android.js +9 -1
- package/lib/module/atoms/fab/fab.android.js.map +1 -1
- package/lib/module/atoms/fab/fab.ios.js +9 -3
- package/lib/module/atoms/fab/fab.ios.js.map +1 -1
- package/lib/module/atoms/glass-view/glass-view.android.js +1 -1
- package/lib/module/atoms/glass-view/glass-view.android.js.map +1 -1
- package/lib/module/atoms/h-stack/h-stack.ios.js +5 -3
- package/lib/module/atoms/h-stack/h-stack.ios.js.map +1 -1
- package/lib/module/atoms/icon-button/icon-button.android.js +9 -0
- package/lib/module/atoms/icon-button/icon-button.android.js.map +1 -1
- package/lib/module/atoms/icon-button/icon-button.ios.js +9 -2
- package/lib/module/atoms/icon-button/icon-button.ios.js.map +1 -1
- package/lib/module/atoms/image/image.js +4 -4
- package/lib/module/atoms/image/image.js.map +1 -1
- package/lib/module/atoms/pressable/pressable.js +26 -25
- package/lib/module/atoms/pressable/pressable.js.map +1 -1
- package/lib/module/atoms/progress/progress.android.js +7 -0
- package/lib/module/atoms/progress/progress.android.js.map +1 -1
- package/lib/module/atoms/progress/progress.ios.js +6 -1
- package/lib/module/atoms/progress/progress.ios.js.map +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.android.js +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.ios.js +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -1
- package/lib/module/atoms/skeleton/skeleton.js +20 -6
- package/lib/module/atoms/skeleton/skeleton.js.map +1 -1
- package/lib/module/atoms/surface/surface.android.js +5 -3
- package/lib/module/atoms/surface/surface.android.js.map +1 -1
- package/lib/module/atoms/surface/surface.ios.js +7 -3
- package/lib/module/atoms/surface/surface.ios.js.map +1 -1
- package/lib/module/atoms/switch/switch.android.js +1 -1
- package/lib/module/atoms/switch/switch.android.js.map +1 -1
- package/lib/module/atoms/text/text.android.js +5 -3
- package/lib/module/atoms/text/text.android.js.map +1 -1
- package/lib/module/atoms/text/text.ios.js +6 -4
- package/lib/module/atoms/text/text.ios.js.map +1 -1
- package/lib/module/atoms/v-stack/v-stack.ios.js +5 -3
- package/lib/module/atoms/v-stack/v-stack.ios.js.map +1 -1
- package/lib/module/atoms/z-stack/z-stack.ios.js +5 -3
- package/lib/module/atoms/z-stack/z-stack.ios.js.map +1 -1
- package/lib/module/glyph/glyph.android.js +2 -0
- package/lib/module/glyph/glyph.android.js.map +1 -1
- package/lib/module/glyph/glyph.ios.js +5 -2
- package/lib/module/glyph/glyph.ios.js.map +1 -1
- package/lib/module/molecules/collapsible/collapsible.android.js +18 -8
- package/lib/module/molecules/collapsible/collapsible.android.js.map +1 -1
- package/lib/module/molecules/labeled-content/labeled-content.js +4 -1
- package/lib/module/molecules/labeled-content/labeled-content.js.map +1 -1
- package/lib/module/molecules/link/link.js +4 -1
- package/lib/module/molecules/link/link.js.map +1 -1
- package/lib/module/molecules/list-item/list-item.js +4 -1
- package/lib/module/molecules/list-item/list-item.js.map +1 -1
- package/lib/module/molecules/navigation-bar/navigation-bar.android.js +1 -1
- package/lib/module/molecules/navigation-bar/navigation-bar.android.js.map +1 -1
- package/lib/module/molecules/navigation-bar/navigation-bar.ios.js +1 -1
- package/lib/module/molecules/navigation-bar/navigation-bar.ios.js.map +1 -1
- package/lib/module/molecules/rn-list-item/rn-list-item.js +10 -2
- package/lib/module/molecules/rn-list-item/rn-list-item.js.map +1 -1
- package/lib/module/molecules/scroll-edge-container/scroll-edge-container.android.js +3 -4
- package/lib/module/molecules/scroll-edge-container/scroll-edge-container.android.js.map +1 -1
- package/lib/module/molecules/stepper/stepper.ios.js +6 -2
- package/lib/module/molecules/stepper/stepper.ios.js.map +1 -1
- package/lib/module/motion.js +19 -0
- package/lib/module/motion.js.map +1 -0
- package/lib/module/organisms/fab-menu/fab-menu.android.js +8 -5
- package/lib/module/organisms/fab-menu/fab-menu.android.js.map +1 -1
- package/lib/module/organisms/fab-menu/fab-menu.ios.js +5 -2
- package/lib/module/organisms/fab-menu/fab-menu.ios.js.map +1 -1
- package/lib/module/organisms/snackbar/snackbar.js +142 -17
- package/lib/module/organisms/snackbar/snackbar.js.map +1 -1
- package/lib/module/organisms/swipe-actions/swipe-actions.android.js +1 -1
- package/lib/module/organisms/swipe-actions/swipe-actions.android.js.map +1 -1
- package/lib/module/use-stack-modifiers.js +10 -8
- package/lib/module/use-stack-modifiers.js.map +1 -1
- package/lib/typescript/atoms/activity-indicator/activity-indicator.types.d.ts +2 -0
- package/lib/typescript/atoms/avatar/avatar.types.d.ts +5 -0
- package/lib/typescript/atoms/button/button-tokens.d.ts +1 -1
- package/lib/typescript/atoms/button/button.types.d.ts +2 -0
- package/lib/typescript/atoms/card/card.types.d.ts +2 -2
- package/lib/typescript/atoms/fab/fab.types.d.ts +8 -1
- package/lib/typescript/atoms/h-stack/h-stack.types.d.ts +6 -3
- package/lib/typescript/atoms/icon-button/icon-button.types.d.ts +7 -0
- package/lib/typescript/atoms/image/image.d.ts +2 -2
- package/lib/typescript/atoms/pressable/pressable.types.d.ts +9 -9
- package/lib/typescript/atoms/progress/progress.types.d.ts +2 -0
- package/lib/typescript/atoms/skeleton/skeleton.types.d.ts +3 -2
- package/lib/typescript/atoms/surface/surface.types.d.ts +2 -2
- package/lib/typescript/atoms/text/text.types.d.ts +2 -2
- package/lib/typescript/atoms/v-stack/v-stack.types.d.ts +3 -3
- package/lib/typescript/atoms/z-stack/z-stack.types.d.ts +3 -3
- package/lib/typescript/glyph/glyph.types.d.ts +2 -0
- package/lib/typescript/molecules/labeled-content/labeled-content.types.d.ts +4 -2
- package/lib/typescript/molecules/link/link.types.d.ts +2 -1
- package/lib/typescript/molecules/list-item/list-item.types.d.ts +5 -3
- package/lib/typescript/molecules/stepper/stepper.ios.d.ts +1 -1
- package/lib/typescript/motion.d.ts +8 -0
- package/lib/typescript/organisms/fab-menu/fab-menu.types.d.ts +5 -0
- package/lib/typescript/organisms/snackbar/snackbar.types.d.ts +38 -5
- package/lib/typescript/use-stack-modifiers.d.ts +6 -5
- package/package.json +4 -4
- package/src/atoms/activity-indicator/activity-indicator.android.tsx +7 -1
- package/src/atoms/activity-indicator/activity-indicator.ios.tsx +9 -1
- package/src/atoms/activity-indicator/activity-indicator.types.ts +2 -0
- package/src/atoms/avatar/avatar.tsx +5 -1
- package/src/atoms/avatar/avatar.types.ts +5 -0
- package/src/atoms/button/button-tokens.ts +2 -2
- package/src/atoms/button/button.android.tsx +5 -3
- package/src/atoms/button/button.ios.tsx +10 -2
- package/src/atoms/button/button.types.ts +2 -0
- package/src/atoms/card/card.android.tsx +6 -4
- package/src/atoms/card/card.ios.tsx +7 -3
- package/src/atoms/card/card.types.ts +8 -7
- package/src/atoms/checkbox/checkbox.android.tsx +1 -1
- package/src/atoms/fab/fab.android.tsx +5 -3
- package/src/atoms/fab/fab.ios.tsx +8 -4
- package/src/atoms/fab/fab.types.ts +8 -1
- package/src/atoms/glass-view/glass-view.android.tsx +1 -1
- package/src/atoms/h-stack/h-stack.ios.tsx +6 -4
- package/src/atoms/h-stack/h-stack.types.ts +17 -13
- package/src/atoms/icon-button/icon-button.android.tsx +16 -3
- package/src/atoms/icon-button/icon-button.ios.tsx +13 -4
- package/src/atoms/icon-button/icon-button.types.ts +7 -0
- package/src/atoms/image/image.tsx +3 -3
- package/src/atoms/pressable/pressable.tsx +26 -18
- package/src/atoms/pressable/pressable.types.ts +30 -29
- package/src/atoms/progress/progress.android.tsx +5 -3
- package/src/atoms/progress/progress.ios.tsx +5 -2
- package/src/atoms/progress/progress.types.ts +2 -0
- package/src/atoms/segmented-control/segmented-control.android.tsx +1 -1
- package/src/atoms/segmented-control/segmented-control.ios.tsx +1 -1
- package/src/atoms/skeleton/skeleton.tsx +18 -4
- package/src/atoms/skeleton/skeleton.types.ts +3 -2
- package/src/atoms/surface/surface.android.tsx +6 -4
- package/src/atoms/surface/surface.ios.tsx +7 -3
- package/src/atoms/surface/surface.types.ts +10 -9
- package/src/atoms/switch/switch.android.tsx +1 -1
- package/src/atoms/text/text.android.tsx +6 -4
- package/src/atoms/text/text.ios.tsx +6 -4
- package/src/atoms/text/text.types.ts +23 -22
- package/src/atoms/v-stack/v-stack.ios.tsx +6 -4
- package/src/atoms/v-stack/v-stack.types.ts +17 -16
- package/src/atoms/z-stack/z-stack.ios.tsx +6 -4
- package/src/atoms/z-stack/z-stack.types.ts +15 -14
- package/src/glyph/glyph.android.tsx +14 -2
- package/src/glyph/glyph.ios.tsx +13 -3
- package/src/glyph/glyph.types.ts +2 -0
- package/src/molecules/collapsible/collapsible.android.tsx +27 -7
- package/src/molecules/labeled-content/labeled-content.tsx +12 -1
- package/src/molecules/labeled-content/labeled-content.types.ts +4 -2
- package/src/molecules/link/link.tsx +2 -1
- package/src/molecules/link/link.types.ts +2 -1
- package/src/molecules/list-item/list-item.tsx +20 -2
- package/src/molecules/list-item/list-item.types.ts +5 -3
- package/src/molecules/navigation-bar/navigation-bar.android.tsx +1 -1
- package/src/molecules/navigation-bar/navigation-bar.ios.tsx +1 -1
- package/src/molecules/rn-list-item/rn-list-item.tsx +18 -3
- package/src/molecules/scroll-edge-container/scroll-edge-container.android.tsx +3 -2
- package/src/molecules/stepper/stepper.ios.tsx +8 -2
- package/src/motion.ts +13 -0
- package/src/organisms/fab-menu/fab-menu.android.tsx +9 -5
- package/src/organisms/fab-menu/fab-menu.ios.tsx +6 -2
- package/src/organisms/fab-menu/fab-menu.types.ts +5 -0
- package/src/organisms/snackbar/snackbar.tsx +143 -19
- package/src/organisms/snackbar/snackbar.types.ts +41 -5
- package/src/organisms/swipe-actions/swipe-actions.android.tsx +1 -1
- package/src/use-stack-modifiers.ts +20 -7
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import type {ResponsiveSx} from '@hzblj/kara-sx'
|
|
1
|
+
import type {AccessibilityProps, ResponsiveSx} from '@hzblj/kara-sx'
|
|
2
2
|
import type {SpaceSize} from '@hzblj/kara-theme'
|
|
3
3
|
import type {ReactNode} from 'react'
|
|
4
4
|
|
|
5
5
|
export type HStackAlign = 'center' | 'end' | 'start'
|
|
6
6
|
|
|
7
7
|
/** A horizontal stack: SwiftUI `HStack` / Compose `Row`. */
|
|
8
|
-
export type HStackProps = ResponsiveSx &
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
export type HStackProps = ResponsiveSx &
|
|
9
|
+
AccessibilityProps & {
|
|
10
|
+
/** How the children line up vertically: `start` is the top, `end` the bottom. `start` by default. */
|
|
11
|
+
align?: HStackAlign
|
|
12
|
+
/** Native views, laid out from the leading edge. */
|
|
13
|
+
children?: ReactNode
|
|
14
|
+
/**
|
|
15
|
+
* Makes the stack pressable: a tap gesture on iOS, a `clickable` with Material's ripple on Android. Screen readers
|
|
16
|
+
* then read the stack as one button.
|
|
17
|
+
*/
|
|
18
|
+
onPress?: () => void
|
|
19
|
+
/** The gap between children. Unset, SwiftUI puts its default spacing between them and Compose none. */
|
|
20
|
+
spacing?: SpaceSize
|
|
21
|
+
/** Test identifier, forwarded to the native view. */
|
|
22
|
+
testID?: string
|
|
23
|
+
}
|
|
20
24
|
|
|
21
25
|
export type HStackComponent = (props: HStackProps) => ReactNode
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import {IconButton as ComposeIconButton} from '@expo/ui/jetpack-compose'
|
|
2
|
+
import {useAccessibilityModifiers} from '@hzblj/kara-sx'
|
|
2
3
|
|
|
3
4
|
import {useHostGuard} from '../../host-context'
|
|
4
5
|
import {Icon} from '../icon'
|
|
5
6
|
import type {IconButtonComponent, IconButtonProps} from './icon-button.types'
|
|
6
7
|
|
|
7
|
-
export const IconButton: IconButtonComponent = ({
|
|
8
|
+
export const IconButton: IconButtonComponent = ({
|
|
9
|
+
accessibilityHint,
|
|
10
|
+
accessibilityLabel,
|
|
11
|
+
disabled = false,
|
|
12
|
+
name,
|
|
13
|
+
onPress,
|
|
14
|
+
}: IconButtonProps) => {
|
|
8
15
|
useHostGuard('IconButton')
|
|
16
|
+
// The label rides on the icon, as Material's own icon buttons carry it; the hint labels the click.
|
|
17
|
+
const modifiers = useAccessibilityModifiers({accessibilityHint}, accessibilityHint !== undefined)
|
|
9
18
|
|
|
10
19
|
return (
|
|
11
|
-
<ComposeIconButton enabled={!disabled} onClick={onPress}>
|
|
12
|
-
<Icon
|
|
20
|
+
<ComposeIconButton enabled={!disabled} modifiers={modifiers} onClick={onPress}>
|
|
21
|
+
<Icon
|
|
22
|
+
accessibilityLabel={accessibilityLabel}
|
|
23
|
+
color={disabled ? 'btn.content.disabled' : 'content.primary'}
|
|
24
|
+
name={name}
|
|
25
|
+
/>
|
|
13
26
|
</ComposeIconButton>
|
|
14
27
|
)
|
|
15
28
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {Button} from '@expo/ui/swift-ui'
|
|
2
2
|
import {buttonStyle, contentShape, disabled as disabledModifier, shapes} from '@expo/ui/swift-ui/modifiers'
|
|
3
|
+
import {useAccessibilityModifiers} from '@hzblj/kara-sx'
|
|
3
4
|
import {useMemo} from 'react'
|
|
4
5
|
|
|
5
6
|
import {useHostGuard} from '../../host-context'
|
|
@@ -13,24 +14,32 @@ const TARGET = 44
|
|
|
13
14
|
/** The whole square takes the tap, not only the glyph's own pixels. */
|
|
14
15
|
const WHOLE_TARGET = {modifiers: [contentShape(shapes.circle())]}
|
|
15
16
|
|
|
16
|
-
export const IconButton: IconButtonComponent = ({
|
|
17
|
+
export const IconButton: IconButtonComponent = ({
|
|
18
|
+
accessibilityHint,
|
|
19
|
+
accessibilityLabel,
|
|
20
|
+
disabled = false,
|
|
21
|
+
name,
|
|
22
|
+
onPress,
|
|
23
|
+
testID,
|
|
24
|
+
}: IconButtonProps) => {
|
|
17
25
|
useHostGuard('IconButton')
|
|
26
|
+
const accessibilityModifiers = useAccessibilityModifiers({accessibilityHint})
|
|
18
27
|
|
|
19
28
|
const modifiers = useMemo(() => {
|
|
20
|
-
const list = [buttonStyle('plain')]
|
|
29
|
+
const list = [buttonStyle('plain'), ...accessibilityModifiers]
|
|
21
30
|
|
|
22
31
|
if (disabled) {
|
|
23
32
|
list.push(disabledModifier(true))
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
return list
|
|
27
|
-
}, [disabled])
|
|
36
|
+
}, [accessibilityModifiers, disabled])
|
|
28
37
|
|
|
29
38
|
return (
|
|
30
39
|
<Button modifiers={modifiers} onPress={onPress} testID={testID}>
|
|
31
40
|
<ZStack h={TARGET} ios={WHOLE_TARGET} w={TARGET}>
|
|
32
41
|
{/* SwiftUI dims a disabled button's label itself; a disabled colour on top of that dimmed it twice. */}
|
|
33
|
-
<Icon color="content.primary" name={name} />
|
|
42
|
+
<Icon accessibilityLabel={accessibilityLabel} color="content.primary" name={name} />
|
|
34
43
|
</ZStack>
|
|
35
44
|
</Button>
|
|
36
45
|
)
|
|
@@ -8,6 +8,13 @@ import type {IconName} from '../icon'
|
|
|
8
8
|
* itself the same way on both.
|
|
9
9
|
*/
|
|
10
10
|
export type IconButtonProps = {
|
|
11
|
+
/**
|
|
12
|
+
* What VoiceOver and TalkBack say for the button, since an icon has no words of its own: "Share", "Add to
|
|
13
|
+
* favourites". Required for that reason.
|
|
14
|
+
*/
|
|
15
|
+
accessibilityLabel: string
|
|
16
|
+
/** What pressing it does, when the label alone does not say. See `AccessibilityProps`. */
|
|
17
|
+
accessibilityHint?: string
|
|
11
18
|
/** Dims the icon (iOS's own disabled look, `btn.content.disabled` on Android) and stops the button responding. */
|
|
12
19
|
disabled?: boolean
|
|
13
20
|
/** The icon, one of the app's. */
|
|
@@ -3,12 +3,12 @@ import {duration} from '@hzblj/kara-theme'
|
|
|
3
3
|
import {Image as ExpoImage, type ImageProps as ExpoImageProps} from 'expo-image'
|
|
4
4
|
import type {Ref} from 'react'
|
|
5
5
|
|
|
6
|
-
const FADE = {duration: duration.
|
|
6
|
+
const FADE = {duration: duration.quick, timing: 'ease-out'} as const
|
|
7
7
|
|
|
8
8
|
const FadingImage = (props: ExpoImageProps & {ref?: Ref<ExpoImage>}) => <ExpoImage transition={FADE} {...props} />
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* `expo-image` with the `sx` props, the way `Box` is a `View` with them, and the design system's
|
|
12
|
-
* already applied, so a loaded image never pops in. An explicit `transition` wins, and so does an explicit `style`.
|
|
11
|
+
* `expo-image` with the `sx` props, the way `Box` is a `View` with them, and the design system's fade (`quick`,
|
|
12
|
+
* eased out) already applied, so a loaded image never pops in. An explicit `transition` wins, and so does an explicit `style`.
|
|
13
13
|
*/
|
|
14
14
|
export const Image = sx(FadingImage)
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import {useSplitSx} from '@hzblj/kara-sx'
|
|
2
|
-
import {
|
|
1
|
+
import {accessibilityProps, splitAccessibility, useSplitSx} from '@hzblj/kara-sx'
|
|
2
|
+
import {opacity, useAppTheme, useReducedMotion, withOpacity} from '@hzblj/kara-theme'
|
|
3
3
|
import {useCallback, useMemo} from 'react'
|
|
4
4
|
import {Platform, Pressable as RNPressable, type ViewStyle} from 'react-native'
|
|
5
|
-
import Animated, {useAnimatedStyle, useSharedValue,
|
|
5
|
+
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'
|
|
6
6
|
|
|
7
7
|
import {haptics} from '../../haptics'
|
|
8
|
+
import {timing} from '../../motion'
|
|
8
9
|
import {splitForRipple} from '../../ripple-clip'
|
|
9
10
|
import type {PressableComponent, PressableProps} from './pressable.types'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
/** The scale eases down and settles back a little slower, with no bounce: a press is not a toy. */
|
|
13
|
+
const SCALE_IN = timing('quick')
|
|
14
|
+
const SCALE_OUT = timing('fast')
|
|
12
15
|
|
|
13
|
-
/** A dim comes in
|
|
14
|
-
const DIM_IN =
|
|
15
|
-
const DIM_OUT =
|
|
16
|
+
/** A dim comes in at once and leaves a little slower, as UIKit's own highlight does. */
|
|
17
|
+
const DIM_IN = timing('micro', 'out')
|
|
18
|
+
const DIM_OUT = timing('quick', 'out')
|
|
16
19
|
|
|
17
20
|
const PLATFORM_EFFECT = Platform.OS === 'ios' ? 'scale' : 'none'
|
|
18
21
|
|
|
@@ -30,22 +33,27 @@ export const Pressable: PressableComponent = ({
|
|
|
30
33
|
testID,
|
|
31
34
|
...props
|
|
32
35
|
}: PressableProps) => {
|
|
33
|
-
const [
|
|
36
|
+
const [accessibility, ownProps] = splitAccessibility(props)
|
|
37
|
+
const [resolved, rest] = useSplitSx(ownProps)
|
|
38
|
+
const reduced = useReducedMotion()
|
|
39
|
+
// With motion reduced the press still shows, and nothing moves: a scale dims instead.
|
|
40
|
+
const effect = reduced && pressEffect === 'scale' ? 'opacity' : pressEffect
|
|
34
41
|
// 0 at rest, 1 held: one value drives whichever effect is picked, so a change of effect needs no second animation.
|
|
35
42
|
const pressed = useSharedValue(0)
|
|
36
43
|
const feedback = haptic === true ? 'light' : haptic || undefined
|
|
44
|
+
const a11y = accessibilityProps(accessibility, 'button')
|
|
37
45
|
const dimmed = opacity[pressedOpacity]
|
|
38
46
|
|
|
39
47
|
const theme = useAppTheme()
|
|
40
48
|
const isAndroid = Platform.OS === 'android'
|
|
41
49
|
|
|
42
50
|
const rStyle = useAnimatedStyle(() => {
|
|
43
|
-
if (
|
|
51
|
+
if (effect === 'scale') {
|
|
44
52
|
return {transform: [{scale: 1 - (1 - pressedScale) * pressed.value}]}
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
return
|
|
48
|
-
}, [dimmed,
|
|
55
|
+
return effect === 'opacity' ? {opacity: 1 - (1 - dimmed) * pressed.value} : {}
|
|
56
|
+
}, [dimmed, effect, pressedScale])
|
|
49
57
|
|
|
50
58
|
// Android's press is Material's: a ripple over the content (the pressed state layer, `onSurface` at 10 %) clipped to
|
|
51
59
|
// the shape by an outer view (`splitForRipple`). The effect, when there is one, moves that outer view, ripple and all.
|
|
@@ -63,20 +71,18 @@ export const Pressable: PressableComponent = ({
|
|
|
63
71
|
|
|
64
72
|
// The finger is down: the Taptic Engine has until the release to wake, so the haptic lands with the press.
|
|
65
73
|
const onPressIn = useCallback(() => {
|
|
66
|
-
if (
|
|
67
|
-
pressed.value =
|
|
68
|
-
} else if (pressEffect === 'opacity') {
|
|
69
|
-
pressed.value = withTiming(1, DIM_IN)
|
|
74
|
+
if (effect !== 'none') {
|
|
75
|
+
pressed.value = withTiming(1, effect === 'scale' ? SCALE_IN : DIM_IN)
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
if (feedback) {
|
|
73
79
|
haptics.prepare(feedback)
|
|
74
80
|
}
|
|
75
|
-
}, [
|
|
81
|
+
}, [effect, feedback, pressed])
|
|
76
82
|
|
|
77
83
|
const onPressOut = useCallback(() => {
|
|
78
|
-
pressed.value =
|
|
79
|
-
}, [
|
|
84
|
+
pressed.value = withTiming(0, effect === 'scale' ? SCALE_OUT : DIM_OUT)
|
|
85
|
+
}, [effect, pressed])
|
|
80
86
|
|
|
81
87
|
const onPressWithHaptic = useCallback(() => {
|
|
82
88
|
if (feedback) {
|
|
@@ -90,6 +96,7 @@ export const Pressable: PressableComponent = ({
|
|
|
90
96
|
return (
|
|
91
97
|
<Animated.View style={[androidStyle.outer, rStyle]}>
|
|
92
98
|
<RNPressable
|
|
99
|
+
{...a11y}
|
|
93
100
|
android_ripple={ripple}
|
|
94
101
|
disabled={disabled}
|
|
95
102
|
hitSlop={8}
|
|
@@ -108,6 +115,7 @@ export const Pressable: PressableComponent = ({
|
|
|
108
115
|
|
|
109
116
|
return (
|
|
110
117
|
<AnimatedPressable
|
|
118
|
+
{...a11y}
|
|
111
119
|
disabled={disabled}
|
|
112
120
|
hitSlop={8}
|
|
113
121
|
onPress={onPressWithHaptic}
|
|
@@ -1,42 +1,43 @@
|
|
|
1
|
-
import type {ResponsiveViewSx} from '@hzblj/kara-sx'
|
|
1
|
+
import type {AccessibilityProps, ResponsiveViewSx} from '@hzblj/kara-sx'
|
|
2
2
|
import type {OpacitySize} from '@hzblj/kara-theme'
|
|
3
3
|
import type {ReactNode} from 'react'
|
|
4
4
|
|
|
5
5
|
import type {Haptic} from '../../haptics'
|
|
6
6
|
|
|
7
|
-
/** How the content answers a press: it
|
|
7
|
+
/** How the content answers a press: it eases down, it dims, or it stays still. */
|
|
8
8
|
export type PressEffect = 'none' | 'opacity' | 'scale'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* React Native's `Pressable`, styled through `sx` like a `Box`, taking touches 8 points past its edges. Each platform
|
|
12
|
-
* presses its own way by default: iOS
|
|
12
|
+
* presses its own way by default: iOS eases the content down to 96%, Android draws Material's ripple over it,
|
|
13
13
|
* clipped to the pressable's shape. `pressEffect` picks the same answer on both, and Android keeps its ripple on top
|
|
14
|
-
* unless `ripple` is off. Give it the `borderRadius` of what it wraps (`full` around a `GlassView`).
|
|
15
|
-
* tier: it needs no host.
|
|
14
|
+
* unless `ripple` is off. Give it the `borderRadius` of what it wraps (`full` around a `GlassView`). Screen readers read
|
|
15
|
+
* it as a button unless `accessibilityRole` names another role. The React Native tier: it needs no host.
|
|
16
16
|
*/
|
|
17
|
-
export type PressableProps = ResponsiveViewSx &
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
17
|
+
export type PressableProps = ResponsiveViewSx &
|
|
18
|
+
AccessibilityProps & {
|
|
19
|
+
/** The pressable content. */
|
|
20
|
+
children?: ReactNode
|
|
21
|
+
/** Ignores presses: no `onPress`, no haptic, no press effect or ripple. */
|
|
22
|
+
disabled?: boolean
|
|
23
|
+
/** Played on press. `true` is a `light` impact; a name picks the haptic, e.g. `selection` for a choice. */
|
|
24
|
+
haptic?: boolean | Haptic
|
|
25
|
+
/** Called when the press is released, after the `haptic` plays. */
|
|
26
|
+
onPress?: () => void
|
|
27
|
+
/**
|
|
28
|
+
* How the content answers while it is held. `scale` eases it down to `pressedScale`, `opacity` dims it to
|
|
29
|
+
* `pressedOpacity`, `none` leaves it still. `scale` on iOS and `none` on Android by default, where the ripple is the
|
|
30
|
+
* press. With motion reduced, `scale` dims instead: the press still shows, and nothing moves.
|
|
31
|
+
*/
|
|
32
|
+
pressEffect?: PressEffect
|
|
33
|
+
/** What `opacity` dims to, an opacity token. `o60` by default. */
|
|
34
|
+
pressedOpacity?: OpacitySize
|
|
35
|
+
/** What `scale` eases down to. `0.96` by default: lower reads as a squash, not a press. */
|
|
36
|
+
pressedScale?: number
|
|
37
|
+
/** Android's ripple: Material's pressed state layer over the content, clipped to the shape. On by default. iOS has none. */
|
|
38
|
+
ripple?: boolean
|
|
39
|
+
/** Test identifier, forwarded to the view. */
|
|
40
|
+
testID?: string
|
|
41
|
+
}
|
|
41
42
|
|
|
42
43
|
export type PressableComponent = (props: PressableProps) => ReactNode
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import {CircularProgressIndicator, LinearProgressIndicator} from '@expo/ui/jetpack-compose'
|
|
2
|
+
import {useAccessibilityModifiers} from '@hzblj/kara-sx'
|
|
2
3
|
import {useAppTheme} from '@hzblj/kara-theme'
|
|
3
4
|
|
|
4
5
|
import {useHostGuard} from '../../host-context'
|
|
5
6
|
import type {ProgressComponent, ProgressProps} from './progress.types'
|
|
6
7
|
|
|
7
|
-
export const Progress: ProgressComponent = ({circular = false, value}: ProgressProps) => {
|
|
8
|
+
export const Progress: ProgressComponent = ({accessibilityLabel, circular = false, value}: ProgressProps) => {
|
|
8
9
|
useHostGuard('Progress')
|
|
9
10
|
const theme = useAppTheme()
|
|
10
11
|
const color = theme.color('fill.accent.primary')
|
|
12
|
+
const modifiers = useAccessibilityModifiers({accessibilityLabel})
|
|
11
13
|
|
|
12
14
|
if (circular) {
|
|
13
|
-
return <CircularProgressIndicator color={color} progress={value} />
|
|
15
|
+
return <CircularProgressIndicator color={color} modifiers={modifiers} progress={value} />
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
return <LinearProgressIndicator color={color} progress={value} />
|
|
18
|
+
return <LinearProgressIndicator color={color} modifiers={modifiers} progress={value} />
|
|
17
19
|
}
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import {Gauge, ProgressView} from '@expo/ui/swift-ui'
|
|
2
2
|
import {gaugeStyle, progressViewStyle, tint} from '@expo/ui/swift-ui/modifiers'
|
|
3
|
+
import {useAccessibilityModifiers} from '@hzblj/kara-sx'
|
|
3
4
|
import {useAppTheme} from '@hzblj/kara-theme'
|
|
4
5
|
import {useMemo} from 'react'
|
|
5
6
|
|
|
6
7
|
import {useHostGuard} from '../../host-context'
|
|
7
8
|
import type {ProgressComponent, ProgressProps} from './progress.types'
|
|
8
9
|
|
|
9
|
-
export const Progress: ProgressComponent = ({circular = false, value}: ProgressProps) => {
|
|
10
|
+
export const Progress: ProgressComponent = ({accessibilityLabel, circular = false, value}: ProgressProps) => {
|
|
10
11
|
useHostGuard('Progress')
|
|
11
12
|
const theme = useAppTheme()
|
|
13
|
+
const accessibilityModifiers = useAccessibilityModifiers({accessibilityLabel})
|
|
12
14
|
|
|
13
15
|
const ring = circular && value !== undefined
|
|
14
16
|
const modifiers = useMemo(
|
|
15
17
|
() => [
|
|
16
18
|
ring ? gaugeStyle('circularCapacity') : progressViewStyle(circular ? 'circular' : 'linear'),
|
|
17
19
|
tint(theme.color('fill.accent.primary')),
|
|
20
|
+
...accessibilityModifiers,
|
|
18
21
|
],
|
|
19
|
-
[circular, ring, theme]
|
|
22
|
+
[accessibilityModifiers, circular, ring, theme]
|
|
20
23
|
)
|
|
21
24
|
|
|
22
25
|
// SwiftUI's circular `ProgressView` ignores its value on iOS and spins; a ring that fills is a capacity `Gauge`.
|
|
@@ -5,6 +5,8 @@ import type {ReactNode} from 'react'
|
|
|
5
5
|
* value, which `ProgressView` cannot draw on iOS) / Compose's `LinearProgressIndicator` and `CircularProgressIndicator`.
|
|
6
6
|
*/
|
|
7
7
|
export type ProgressProps = {
|
|
8
|
+
/** What is in progress, for VoiceOver and TalkBack: "Uploading". They read how far along it is themselves. */
|
|
9
|
+
accessibilityLabel?: string
|
|
8
10
|
/** A circular indicator instead of a bar. Off by default. */
|
|
9
11
|
circular?: boolean
|
|
10
12
|
/** Between `0` and `1`. Leave it unset for an indeterminate indicator. */
|
|
@@ -29,7 +29,7 @@ export const SegmentedControl: SegmentedControlComponent = ({
|
|
|
29
29
|
() => ({
|
|
30
30
|
activeBorderColor: theme.color('fill.accent.primary'),
|
|
31
31
|
activeContainerColor: theme.color('fill.accent.primary'),
|
|
32
|
-
activeContentColor: theme.color('content.on.
|
|
32
|
+
activeContentColor: theme.color('content.on.accent'),
|
|
33
33
|
disabledActiveBorderColor: theme.color('fill.accent.disabled'),
|
|
34
34
|
disabledActiveContainerColor: theme.color('fill.accent.disabled'),
|
|
35
35
|
disabledActiveContentColor: theme.color('btn.content.disabled'),
|
|
@@ -33,7 +33,7 @@ export const SegmentedControl: SegmentedControlComponent = ({
|
|
|
33
33
|
return (
|
|
34
34
|
<NativeSegmentedControl
|
|
35
35
|
activePillColor={theme.color('fill.accent.primary')}
|
|
36
|
-
activeTextColor={theme.color('content.on.
|
|
36
|
+
activeTextColor={theme.color('content.on.accent')}
|
|
37
37
|
disabled={disabled}
|
|
38
38
|
fontFamily={face.fontFamily}
|
|
39
39
|
fontSize={face.fontSize}
|
|
@@ -1,21 +1,35 @@
|
|
|
1
|
-
import {duration, opacity} from '@hzblj/kara-theme'
|
|
1
|
+
import {duration, opacity, useReducedMotion} from '@hzblj/kara-theme'
|
|
2
2
|
import {useEffect} from 'react'
|
|
3
|
-
import {useAnimatedStyle, useSharedValue, withRepeat, withTiming} from 'react-native-reanimated'
|
|
3
|
+
import {cancelAnimation, useAnimatedStyle, useSharedValue, withRepeat, withTiming} from 'react-native-reanimated'
|
|
4
4
|
|
|
5
|
+
import {timing} from '../../motion'
|
|
5
6
|
import {ABox} from '../box'
|
|
6
7
|
import type {SkeletonComponent, SkeletonProps} from './skeleton.types'
|
|
7
8
|
|
|
9
|
+
/** A breath, not a transition, so it sits off the scale: `slow` twice each way, at constant speed. */
|
|
10
|
+
const PULSE = {...timing('slow', 'linear'), duration: duration.slow * 2}
|
|
11
|
+
|
|
8
12
|
export const Skeleton: SkeletonComponent = ({borderRadius = 'small', h, testID, w}: SkeletonProps) => {
|
|
13
|
+
const reduced = useReducedMotion()
|
|
9
14
|
const pulse = useSharedValue<number>(opacity.o40)
|
|
10
15
|
|
|
16
|
+
// With motion reduced it holds still, halfway between the two ends of the pulse.
|
|
11
17
|
useEffect(() => {
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
if (reduced) {
|
|
19
|
+
cancelAnimation(pulse)
|
|
20
|
+
pulse.value = opacity.o70
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pulse.value = opacity.o40
|
|
25
|
+
pulse.value = withRepeat(withTiming(opacity.o100, PULSE), -1, true)
|
|
26
|
+
}, [pulse, reduced])
|
|
14
27
|
|
|
15
28
|
const rStyle = useAnimatedStyle(() => ({opacity: pulse.value}), [])
|
|
16
29
|
|
|
17
30
|
return (
|
|
18
31
|
<ABox
|
|
32
|
+
aria-hidden
|
|
19
33
|
bg="fill.quaternary"
|
|
20
34
|
borderRadius={borderRadius}
|
|
21
35
|
h={h}
|
|
@@ -3,8 +3,9 @@ import type {RadiiSize} from '@hzblj/kara-theme'
|
|
|
3
3
|
import type {ReactNode} from 'react'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A loading placeholder that pulses in `fill.quaternary
|
|
7
|
-
* the
|
|
6
|
+
* A loading placeholder that pulses in `fill.quaternary`, and holds still with motion reduced. Hidden from screen
|
|
7
|
+
* readers: what is loading is for the screen to say. It is a React Native view, so it needs no host. Sized with the
|
|
8
|
+
* `sx` names: `w`, `h`, `borderRadius`.
|
|
8
9
|
*/
|
|
9
10
|
export type SkeletonProps = {
|
|
10
11
|
/** The corner radius. `small` by default. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Column, Surface as ComposeSurface, Shape} from '@expo/ui/jetpack-compose'
|
|
2
2
|
import {testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
|
|
3
|
-
import {useSxModifiers} from '@hzblj/kara-sx'
|
|
3
|
+
import {splitAccessibility, useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
|
|
4
4
|
import {radii, useAppTheme} from '@hzblj/kara-theme'
|
|
5
5
|
import {useMemo} from 'react'
|
|
6
6
|
import {useHostGuard} from '../../host-context'
|
|
@@ -24,15 +24,17 @@ export const Surface: SurfaceComponent = ({
|
|
|
24
24
|
px,
|
|
25
25
|
py,
|
|
26
26
|
testID,
|
|
27
|
-
...
|
|
27
|
+
...props
|
|
28
28
|
}: SurfaceProps) => {
|
|
29
29
|
useHostGuard('Surface')
|
|
30
|
+
const [accessibility, sx] = splitAccessibility(props)
|
|
30
31
|
|
|
31
32
|
const theme = useAppTheme()
|
|
32
33
|
const sxModifiers = useSxModifiers({...sx, bg: undefined})
|
|
34
|
+
const accessibilityModifiers = useAccessibilityModifiers(accessibility)
|
|
33
35
|
const modifiers = useMemo(
|
|
34
|
-
() => (testID ? [
|
|
35
|
-
[sxModifiers, testID]
|
|
36
|
+
() => [...sxModifiers, ...(testID ? [testIdModifier(testID)] : []), ...accessibilityModifiers],
|
|
37
|
+
[accessibilityModifiers, sxModifiers, testID]
|
|
36
38
|
)
|
|
37
39
|
const contentModifiers = useSxModifiers({p, pb, pl, pr, pt, px, py, wide: sx.wide})
|
|
38
40
|
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import {GlassEffectContainer, VStack} from '@expo/ui/swift-ui'
|
|
2
|
-
import {useSxModifiers} from '@hzblj/kara-sx'
|
|
2
|
+
import {splitAccessibility, useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
|
|
3
|
+
import {useMemo} from 'react'
|
|
3
4
|
import {useHostGuard} from '../../host-context'
|
|
4
5
|
import type {SurfaceComponent, SurfaceProps} from './surface.types'
|
|
5
6
|
|
|
6
|
-
export const Surface: SurfaceComponent = ({children, glass = false, testID, ...
|
|
7
|
+
export const Surface: SurfaceComponent = ({children, glass = false, testID, ...props}: SurfaceProps) => {
|
|
7
8
|
useHostGuard('Surface')
|
|
9
|
+
const [accessibility, sx] = splitAccessibility(props)
|
|
8
10
|
|
|
9
11
|
// `surface.primary` unless told otherwise, as Material's `Surface` is on Android — without it a plain one drew nothing.
|
|
10
|
-
const
|
|
12
|
+
const sxModifiers = useSxModifiers({bg: 'surface.primary', borderRadius: 'large', p: 's16', ...sx})
|
|
13
|
+
const accessibilityModifiers = useAccessibilityModifiers(accessibility)
|
|
14
|
+
const modifiers = useMemo(() => [...sxModifiers, ...accessibilityModifiers], [accessibilityModifiers, sxModifiers])
|
|
11
15
|
const content = (
|
|
12
16
|
<VStack alignment="leading" modifiers={modifiers} testID={testID}>
|
|
13
17
|
{children}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import type {ResponsiveSx} from '@hzblj/kara-sx'
|
|
1
|
+
import type {AccessibilityProps, ResponsiveSx} from '@hzblj/kara-sx'
|
|
2
2
|
import type {ReactNode} from 'react'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* A container on `surface.primary`, padded `s16` and rounded `large` unless told otherwise: a SwiftUI `VStack` on
|
|
6
6
|
* iOS, Material's `Surface` around a `Column` on Android.
|
|
7
7
|
*/
|
|
8
|
-
export type SurfaceProps = ResponsiveSx &
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
export type SurfaceProps = ResponsiveSx &
|
|
9
|
+
AccessibilityProps & {
|
|
10
|
+
/** Stacked vertically, from the leading edge. */
|
|
11
|
+
children?: ReactNode
|
|
12
|
+
/** iOS wraps the content in a `GlassEffectContainer`; Android raises it to a tonal elevation of `3`. */
|
|
13
|
+
glass?: boolean
|
|
14
|
+
/** Test identifier, forwarded to the native view. */
|
|
15
|
+
testID?: string
|
|
16
|
+
}
|
|
16
17
|
|
|
17
18
|
export type SurfaceComponent = (props: SurfaceProps) => ReactNode
|
|
@@ -34,7 +34,7 @@ export const Switch: SwitchComponent = ({disabled = false, label, onValueChange,
|
|
|
34
34
|
<ComposeSwitch
|
|
35
35
|
colors={{
|
|
36
36
|
checkedBorderColor: theme.color('fill.accent.primary'),
|
|
37
|
-
checkedThumbColor: theme.color('content.on.
|
|
37
|
+
checkedThumbColor: theme.color('content.on.accent'),
|
|
38
38
|
checkedTrackColor: theme.color('fill.accent.primary'),
|
|
39
39
|
}}
|
|
40
40
|
enabled={!disabled}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Text as ComposeText} from '@expo/ui/jetpack-compose'
|
|
2
2
|
import {testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
|
|
3
|
-
import {useSxModifiers} from '@hzblj/kara-sx'
|
|
3
|
+
import {splitAccessibility, useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
|
|
4
4
|
import {useAppTheme} from '@hzblj/kara-theme'
|
|
5
5
|
import {useMemo} from 'react'
|
|
6
6
|
|
|
@@ -33,15 +33,17 @@ export const Text: TextComponent = ({
|
|
|
33
33
|
testID,
|
|
34
34
|
transform,
|
|
35
35
|
variant = 'body',
|
|
36
|
-
...
|
|
36
|
+
...props
|
|
37
37
|
}: TextProps) => {
|
|
38
38
|
useHostGuard('Text')
|
|
39
39
|
const theme = useAppTheme()
|
|
40
40
|
const style = theme.typography[variant]
|
|
41
|
+
const [accessibility, sx] = splitAccessibility(props)
|
|
41
42
|
const sxModifiers = useSxModifiers(sx)
|
|
43
|
+
const accessibilityModifiers = useAccessibilityModifiers(accessibility)
|
|
42
44
|
const modifiers = useMemo(
|
|
43
|
-
() => (testID ? [
|
|
44
|
-
[sxModifiers, testID]
|
|
45
|
+
() => [...sxModifiers, ...(testID ? [testIdModifier(testID)] : []), ...accessibilityModifiers],
|
|
46
|
+
[accessibilityModifiers, sxModifiers, testID]
|
|
45
47
|
)
|
|
46
48
|
|
|
47
49
|
return (
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
underline,
|
|
11
11
|
} from '@expo/ui/swift-ui/modifiers'
|
|
12
12
|
import {scaledFont} from '@hzblj/kara-native/swift-ui'
|
|
13
|
-
import {type FrameAlignment, useSxModifiers} from '@hzblj/kara-sx'
|
|
13
|
+
import {type FrameAlignment, splitAccessibility, useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
|
|
14
14
|
import {useAppTheme} from '@hzblj/kara-theme'
|
|
15
15
|
import {useMemo} from 'react'
|
|
16
16
|
|
|
@@ -39,13 +39,15 @@ export const Text: TextComponent = ({
|
|
|
39
39
|
testID,
|
|
40
40
|
transform,
|
|
41
41
|
variant = 'body',
|
|
42
|
-
...
|
|
42
|
+
...props
|
|
43
43
|
}: TextProps) => {
|
|
44
44
|
useHostGuard('Text')
|
|
45
45
|
const theme = useAppTheme()
|
|
46
46
|
const style = theme.typography[variant]
|
|
47
47
|
// After the text's own modifiers: the font and colour belong to the glyphs, the padding and frame to the box around.
|
|
48
|
+
const [accessibility, sx] = splitAccessibility(props)
|
|
48
49
|
const sxModifiers = useSxModifiers(sx, FRAME_ALIGNMENT[align ?? 'left'])
|
|
50
|
+
const accessibilityModifiers = useAccessibilityModifiers(accessibility)
|
|
49
51
|
|
|
50
52
|
const modifiers = useMemo(() => {
|
|
51
53
|
const list: ModifierConfig[] = [
|
|
@@ -76,8 +78,8 @@ export const Text: TextComponent = ({
|
|
|
76
78
|
list.push(lineLimit(numberOfLines))
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
return [...list, ...sxModifiers]
|
|
80
|
-
}, [align, color, decoration, numberOfLines, style, sxModifiers, theme, transform])
|
|
81
|
+
return [...list, ...sxModifiers, ...accessibilityModifiers]
|
|
82
|
+
}, [accessibilityModifiers, align, color, decoration, numberOfLines, style, sxModifiers, theme, transform])
|
|
81
83
|
|
|
82
84
|
return (
|
|
83
85
|
<SwiftUIText modifiers={modifiers} testID={testID}>
|