@hzblj/kara-components 0.10.0 → 0.12.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 +55 -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/box/box.js +3 -0
- package/lib/module/atoms/box/box.js.map +1 -1
- package/lib/module/atoms/box/index.js +0 -1
- package/lib/module/atoms/box/index.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/index.js +1 -0
- package/lib/module/atoms/index.js.map +1 -1
- package/lib/module/atoms/pressable/pressable.js +75 -27
- 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/rn-text/rn-text.js +66 -26
- package/lib/module/atoms/rn-text/rn-text.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 +22 -8
- package/lib/module/atoms/skeleton/skeleton.js.map +1 -1
- package/lib/module/atoms/stagger/index.js +5 -0
- package/lib/module/atoms/stagger/index.js.map +1 -0
- package/lib/module/atoms/stagger/stagger.js +102 -0
- package/lib/module/atoms/stagger/stagger.js.map +1 -0
- package/lib/module/atoms/stagger/stagger.types.js +4 -0
- package/lib/module/atoms/stagger/stagger.types.js.map +1 -0
- 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 +5 -6
- 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/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 +155 -18
- 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/box/box.d.ts +3 -0
- package/lib/typescript/atoms/box/index.d.ts +0 -1
- 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/index.d.ts +1 -0
- package/lib/typescript/atoms/pressable/pressable.types.d.ts +10 -9
- package/lib/typescript/atoms/progress/progress.types.d.ts +2 -0
- package/lib/typescript/atoms/rn-text/rn-text.types.d.ts +7 -4
- package/lib/typescript/atoms/skeleton/skeleton.types.d.ts +3 -2
- package/lib/typescript/atoms/stagger/index.d.ts +2 -0
- package/lib/typescript/atoms/stagger/stagger.d.ts +2 -0
- package/lib/typescript/atoms/stagger/stagger.types.d.ts +32 -0
- 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/organisms/fab-menu/fab-menu.types.d.ts +5 -0
- package/lib/typescript/organisms/snackbar/snackbar.types.d.ts +35 -4
- 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/box/box.tsx +3 -0
- package/src/atoms/box/index.ts +0 -1
- 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/index.ts +1 -0
- package/src/atoms/pressable/pressable.tsx +164 -100
- package/src/atoms/pressable/pressable.types.ts +32 -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/rn-text/rn-text.tsx +101 -32
- package/src/atoms/rn-text/rn-text.types.ts +7 -3
- 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 +20 -6
- package/src/atoms/skeleton/skeleton.types.ts +3 -2
- package/src/atoms/stagger/index.ts +2 -0
- package/src/atoms/stagger/stagger.tsx +98 -0
- package/src/atoms/stagger/stagger.types.ts +35 -0
- 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 +5 -4
- package/src/molecules/stepper/stepper.ios.tsx +8 -2
- 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 +125 -21
- package/src/organisms/snackbar/snackbar.types.ts +38 -4
- package/src/organisms/swipe-actions/swipe-actions.android.tsx +1 -1
- package/src/use-stack-modifiers.ts +20 -7
- package/lib/module/atoms/box/a-box.js +0 -6
- package/lib/module/atoms/box/a-box.js.map +0 -1
- package/lib/typescript/atoms/box/a-box.d.ts +0 -49
- package/src/atoms/box/a-box.ts +0 -5
|
@@ -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. */
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {isMotionComponent, type MotionPresetConfig, type SxEntryExit, type SxLayout} from '@hzblj/kara-sx'
|
|
2
|
+
import {duration} from '@hzblj/kara-theme'
|
|
3
|
+
import {Children, cloneElement, isValidElement, type Key, type ReactElement, useLayoutEffect, useRef} from 'react'
|
|
4
|
+
|
|
5
|
+
import {Box} from '../box'
|
|
6
|
+
import type {StaggerComponent, StaggerMotion, StaggerProps} from './stagger.types'
|
|
7
|
+
|
|
8
|
+
type MotionChildProps = {entering?: SxEntryExit; exiting?: SxEntryExit; layout?: SxLayout}
|
|
9
|
+
|
|
10
|
+
const asConfig = (motion: StaggerMotion): MotionPresetConfig => (typeof motion === 'string' ? {preset: motion} : motion)
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The stagger's preset, or the child's own with the delay added: a child that names its look keeps it and only takes
|
|
14
|
+
* its turn. Reanimated's own builder is the child's, untouched, since adding a delay to one mutates it.
|
|
15
|
+
*/
|
|
16
|
+
const withDelay = (own: SxEntryExit | undefined, fallback: MotionPresetConfig, delay: number): SxEntryExit => {
|
|
17
|
+
if (own === undefined) {
|
|
18
|
+
return {...fallback, delay: (fallback.delay ?? 0) + delay}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (typeof own === 'string') {
|
|
22
|
+
return {delay, preset: own}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return typeof own === 'object' && own !== null && 'preset' in own ? {...own, delay: (own.delay ?? 0) + delay} : own
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Stagger: StaggerComponent = ({
|
|
29
|
+
children,
|
|
30
|
+
entering = 'fadeUp',
|
|
31
|
+
exiting,
|
|
32
|
+
from = 'start',
|
|
33
|
+
interval = 'stagger',
|
|
34
|
+
layout,
|
|
35
|
+
testID,
|
|
36
|
+
...sx
|
|
37
|
+
}: StaggerProps) => {
|
|
38
|
+
// The children that have arrived already. Only the ones new in this render take turns, so a list that grows by a
|
|
39
|
+
// page staggers the page, and an item added on its own arrives at once instead of after everything above it.
|
|
40
|
+
const arrived = useRef(new Set<Key>())
|
|
41
|
+
const items = Children.toArray(children)
|
|
42
|
+
const turns = new Map<Key, number>()
|
|
43
|
+
|
|
44
|
+
for (const item of items) {
|
|
45
|
+
if (isValidElement(item) && item.key !== null && !arrived.current.has(item.key)) {
|
|
46
|
+
turns.set(item.key, turns.size)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Marked once committed: a render React throws away must not use up a child's turn.
|
|
51
|
+
useLayoutEffect(() => {
|
|
52
|
+
for (const key of turns.keys()) {
|
|
53
|
+
arrived.current.add(key)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const step = duration[interval]
|
|
58
|
+
const enter = asConfig(entering)
|
|
59
|
+
const leave = exiting === undefined ? undefined : asConfig(exiting)
|
|
60
|
+
|
|
61
|
+
const delayOf = (key: Key | null) => {
|
|
62
|
+
const turn = key === null ? undefined : turns.get(key)
|
|
63
|
+
|
|
64
|
+
if (turn === undefined) {
|
|
65
|
+
return 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (from === 'start' ? turn : turns.size - 1 - turn) * step
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
// With `layout` the container moves and resizes with its children, so what holds it can follow.
|
|
73
|
+
<Box layout={layout} testID={testID} {...sx}>
|
|
74
|
+
{items.map(item => {
|
|
75
|
+
if (!isValidElement<MotionChildProps>(item)) {
|
|
76
|
+
return item
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const delay = delayOf(item.key)
|
|
80
|
+
|
|
81
|
+
// A component that takes the motion props gets them directly, with no view added around it.
|
|
82
|
+
if (isMotionComponent(item.type)) {
|
|
83
|
+
return cloneElement(item as ReactElement<MotionChildProps>, {
|
|
84
|
+
entering: withDelay(item.props.entering, enter, delay),
|
|
85
|
+
exiting: item.props.exiting ?? leave,
|
|
86
|
+
layout: item.props.layout ?? layout,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<Box entering={withDelay(undefined, enter, delay)} exiting={leave} key={item.key} layout={layout}>
|
|
92
|
+
{item}
|
|
93
|
+
</Box>
|
|
94
|
+
)
|
|
95
|
+
})}
|
|
96
|
+
</Box>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type {MotionPreset, MotionPresetConfig, ResponsiveViewSx} from '@hzblj/kara-sx'
|
|
2
|
+
import type {DurationSize, SpringName} from '@hzblj/kara-theme'
|
|
3
|
+
import type {ReactNode} from 'react'
|
|
4
|
+
|
|
5
|
+
/** A preset by name, or one adjusted: `{preset: 'fadeUp', duration: 'slow'}`. Its `delay` is where the first one starts. */
|
|
6
|
+
export type StaggerMotion = MotionPreset | MotionPresetConfig
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A `Box` whose children arrive one after another: each takes `entering` a step later than the one before. Children
|
|
10
|
+
* that arrive together are staggered among themselves, the first ones as much as a later batch (the next page of a
|
|
11
|
+
* list), while a single child added later arrives at once. A `Box`, `RnText` or `Pressable` child takes the motion
|
|
12
|
+
* itself; any other child is wrapped in a `Box` that does. With motion reduced every child fades, still one after
|
|
13
|
+
* another. The React Native tier: it needs no host.
|
|
14
|
+
*/
|
|
15
|
+
export type StaggerProps = ResponsiveViewSx & {
|
|
16
|
+
/** What arrives. A child with an `entering` preset of its own keeps it and only takes the delay. */
|
|
17
|
+
children?: ReactNode
|
|
18
|
+
/** How each child arrives. `fadeUp` by default. */
|
|
19
|
+
entering?: StaggerMotion
|
|
20
|
+
/** How a child leaves when it is removed. They leave together, not one after another; none by default. */
|
|
21
|
+
exiting?: StaggerMotion
|
|
22
|
+
/** Which end arrives first. `start` by default. */
|
|
23
|
+
from?: 'end' | 'start'
|
|
24
|
+
/** The step between one child and the next, a duration token. `stagger` (40 ms) by default, `micro` for a slower one. */
|
|
25
|
+
interval?: DurationSize
|
|
26
|
+
/**
|
|
27
|
+
* Moves the other children into place when one arrives or leaves, and the stagger itself to its new size: `true` on
|
|
28
|
+
* the `smooth` spring, or a spring token. Off by default. Give what sits after it `layout` too, so it follows.
|
|
29
|
+
*/
|
|
30
|
+
layout?: boolean | SpringName
|
|
31
|
+
/** Test identifier, forwarded to the view. */
|
|
32
|
+
testID?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type StaggerComponent = (props: StaggerProps) => ReactNode
|
|
@@ -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}>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {ResponsiveSx} from '@hzblj/kara-sx'
|
|
1
|
+
import type {AccessibilityProps, ResponsiveSx} from '@hzblj/kara-sx'
|
|
2
2
|
import type {ColorToken, TextVariant} from '@hzblj/kara-theme'
|
|
3
3
|
import type {ReactNode} from 'react'
|
|
4
4
|
|
|
@@ -13,26 +13,27 @@ export type TextTransform = 'lowercase' | 'uppercase'
|
|
|
13
13
|
* the native `sx` surface too: padding, a frame (`wide` lines it up by `align`), a background, and the `ios` bucket's
|
|
14
14
|
* `contentTransition`, `animation` and `monospacedDigit` for a number that changes in place.
|
|
15
15
|
*/
|
|
16
|
-
export type TextProps = ResponsiveSx &
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
16
|
+
export type TextProps = ResponsiveSx &
|
|
17
|
+
AccessibilityProps & {
|
|
18
|
+
/**
|
|
19
|
+
* How its lines align. iOS reads `left` and `right` as leading and trailing, so they flip in a right-to-left
|
|
20
|
+
* layout; Android keeps them as they are.
|
|
21
|
+
*/
|
|
22
|
+
align?: TextAlign
|
|
23
|
+
/** The text. */
|
|
24
|
+
children?: ReactNode
|
|
25
|
+
/** The text's colour. `content.primary` by default. */
|
|
26
|
+
color?: ColorToken
|
|
27
|
+
/** An underline or a strikethrough. */
|
|
28
|
+
decoration?: TextDecoration
|
|
29
|
+
/** The most lines it wraps to. iOS truncates the last with an ellipsis; Android clips it. */
|
|
30
|
+
numberOfLines?: number
|
|
31
|
+
/** Test identifier, forwarded to the native view. */
|
|
32
|
+
testID?: string
|
|
33
|
+
/** Upper- or lower-cases the text. Android applies it only when `children` is a plain string. */
|
|
34
|
+
transform?: TextTransform
|
|
35
|
+
/** The typography variant. `body` by default. */
|
|
36
|
+
variant?: TextVariant
|
|
37
|
+
}
|
|
37
38
|
|
|
38
39
|
export type TextComponent = (props: TextProps) => ReactNode
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {VStack as SwiftUIVStack} from '@expo/ui/swift-ui'
|
|
2
2
|
import {onTapGesture} from '@expo/ui/swift-ui/modifiers'
|
|
3
|
-
import {type FrameAlignment, useSxModifiers} from '@hzblj/kara-sx'
|
|
3
|
+
import {type FrameAlignment, splitAccessibility, useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
|
|
4
4
|
import {spacing} from '@hzblj/kara-theme'
|
|
5
5
|
import {useMemo} from 'react'
|
|
6
6
|
import {useHostGuard} from '../../host-context'
|
|
@@ -18,13 +18,15 @@ export const VStack: VStackComponent = ({
|
|
|
18
18
|
onPress,
|
|
19
19
|
spacing: gap,
|
|
20
20
|
testID,
|
|
21
|
-
...
|
|
21
|
+
...props
|
|
22
22
|
}: VStackProps) => {
|
|
23
23
|
useHostGuard('VStack')
|
|
24
|
+
const [accessibility, sx] = splitAccessibility(props)
|
|
24
25
|
const sxModifiers = useSxModifiers(sx, ALIGNMENT[align])
|
|
26
|
+
const accessibilityModifiers = useAccessibilityModifiers(accessibility, onPress !== undefined)
|
|
25
27
|
const modifiers = useMemo(
|
|
26
|
-
() => (onPress ? [
|
|
27
|
-
[onPress, sxModifiers]
|
|
28
|
+
() => [...sxModifiers, ...(onPress ? [onTapGesture(onPress)] : []), ...accessibilityModifiers],
|
|
29
|
+
[accessibilityModifiers, onPress, sxModifiers]
|
|
28
30
|
)
|
|
29
31
|
|
|
30
32
|
return (
|
|
@@ -1,24 +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 VStackAlign = 'center' | 'end' | 'start'
|
|
6
6
|
|
|
7
7
|
/** Stacks its children top to bottom: SwiftUI `VStack` / Compose `Column`. */
|
|
8
|
-
export type VStackProps = ResponsiveSx &
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
8
|
+
export type VStackProps = ResponsiveSx &
|
|
9
|
+
AccessibilityProps & {
|
|
10
|
+
/** The children's horizontal alignment. `start` by default. */
|
|
11
|
+
align?: VStackAlign
|
|
12
|
+
/** Laid out top to bottom, `spacing` apart. */
|
|
13
|
+
children?: ReactNode
|
|
14
|
+
/**
|
|
15
|
+
* Makes the whole stack pressable. iOS adds a tap gesture with no press feedback; Android a `clickable` whose ripple
|
|
16
|
+
* fills the stack's shape. Screen readers then read the stack as one button.
|
|
17
|
+
*/
|
|
18
|
+
onPress?: () => void
|
|
19
|
+
/** The space between children. */
|
|
20
|
+
spacing?: SpaceSize
|
|
21
|
+
/** Test identifier, forwarded to the native view. */
|
|
22
|
+
testID?: string
|
|
23
|
+
}
|
|
23
24
|
|
|
24
25
|
export type VStackComponent = (props: VStackProps) => ReactNode
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {ZStack as SwiftUIZStack} from '@expo/ui/swift-ui'
|
|
2
2
|
import {onTapGesture} from '@expo/ui/swift-ui/modifiers'
|
|
3
|
-
import {type FrameAlignment, useSxModifiers} from '@hzblj/kara-sx'
|
|
3
|
+
import {type FrameAlignment, splitAccessibility, useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
|
|
4
4
|
import {useMemo} from 'react'
|
|
5
5
|
import {useHostGuard} from '../../host-context'
|
|
6
6
|
import type {ZStackAlign, ZStackComponent, ZStackProps} from './z-stack.types'
|
|
@@ -11,12 +11,14 @@ const ALIGNMENT = {
|
|
|
11
11
|
top: 'top',
|
|
12
12
|
} as const satisfies Record<ZStackAlign, FrameAlignment>
|
|
13
13
|
|
|
14
|
-
export const ZStack: ZStackComponent = ({align = 'center', children, onPress, testID, ...
|
|
14
|
+
export const ZStack: ZStackComponent = ({align = 'center', children, onPress, testID, ...props}: ZStackProps) => {
|
|
15
15
|
useHostGuard('ZStack')
|
|
16
|
+
const [accessibility, sx] = splitAccessibility(props)
|
|
16
17
|
const sxModifiers = useSxModifiers(sx, ALIGNMENT[align])
|
|
18
|
+
const accessibilityModifiers = useAccessibilityModifiers(accessibility, onPress !== undefined)
|
|
17
19
|
const modifiers = useMemo(
|
|
18
|
-
() => (onPress ? [
|
|
19
|
-
[onPress, sxModifiers]
|
|
20
|
+
() => [...sxModifiers, ...(onPress ? [onTapGesture(onPress)] : []), ...accessibilityModifiers],
|
|
21
|
+
[accessibilityModifiers, onPress, sxModifiers]
|
|
20
22
|
)
|
|
21
23
|
|
|
22
24
|
return (
|
|
@@ -1,21 +1,22 @@
|
|
|
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
|
export type ZStackAlign = 'bottom' | 'center' | 'top'
|
|
5
5
|
|
|
6
6
|
/** Layers its children on one another: SwiftUI `ZStack` / Compose `Box`. */
|
|
7
|
-
export type ZStackProps = ResponsiveSx &
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
export type ZStackProps = ResponsiveSx &
|
|
8
|
+
AccessibilityProps & {
|
|
9
|
+
/** Where the children sit vertically; they are always centred horizontally. `center` by default. */
|
|
10
|
+
align?: ZStackAlign
|
|
11
|
+
/** Drawn in order, the last on top. */
|
|
12
|
+
children?: ReactNode
|
|
13
|
+
/**
|
|
14
|
+
* Makes the whole stack pressable. iOS adds a tap gesture with no press feedback; Android a `clickable` whose ripple
|
|
15
|
+
* fills the stack's shape. Screen readers then read the stack as one button.
|
|
16
|
+
*/
|
|
17
|
+
onPress?: () => void
|
|
18
|
+
/** Test identifier, forwarded to the native view. */
|
|
19
|
+
testID?: string
|
|
20
|
+
}
|
|
20
21
|
|
|
21
22
|
export type ZStackComponent = (props: ZStackProps) => ReactNode
|
|
@@ -5,8 +5,20 @@ import {spacing, useAppTheme} from '@hzblj/kara-theme'
|
|
|
5
5
|
import type {GlyphComponent, GlyphProps} from './glyph.types'
|
|
6
6
|
|
|
7
7
|
/** The Material Symbol `@hzblj/kara-native` ships as a drawable — inside a Compose host. */
|
|
8
|
-
export const Glyph: GlyphComponent = ({
|
|
8
|
+
export const Glyph: GlyphComponent = ({
|
|
9
|
+
accessibilityLabel,
|
|
10
|
+
color = 'content.primary',
|
|
11
|
+
name,
|
|
12
|
+
size = 's24',
|
|
13
|
+
}: GlyphProps) => {
|
|
9
14
|
const theme = useAppTheme()
|
|
10
15
|
|
|
11
|
-
return
|
|
16
|
+
return (
|
|
17
|
+
<DrawableIcon
|
|
18
|
+
accessibilityLabel={accessibilityLabel}
|
|
19
|
+
iconColor={theme.color(color)}
|
|
20
|
+
iconSize={spacing[size]}
|
|
21
|
+
resourceName={GLYPHS[name].android}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
12
24
|
}
|
package/src/glyph/glyph.ios.tsx
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import {Image} from '@expo/ui/swift-ui'
|
|
2
|
-
import {accessibilityHidden} from '@expo/ui/swift-ui/modifiers'
|
|
2
|
+
import {accessibilityHidden, accessibilityLabel as labelModifier} from '@expo/ui/swift-ui/modifiers'
|
|
3
3
|
import {GLYPHS} from '@hzblj/kara-native'
|
|
4
4
|
import {spacing, useAppTheme} from '@hzblj/kara-theme'
|
|
5
|
+
import {useMemo} from 'react'
|
|
5
6
|
|
|
6
7
|
import type {GlyphComponent, GlyphProps} from './glyph.types'
|
|
7
8
|
|
|
8
9
|
const DECORATIVE = [accessibilityHidden(true)]
|
|
9
10
|
|
|
10
11
|
/** The SF Symbol the system already has — inside a SwiftUI host. */
|
|
11
|
-
export const Glyph: GlyphComponent = ({
|
|
12
|
+
export const Glyph: GlyphComponent = ({
|
|
13
|
+
accessibilityLabel,
|
|
14
|
+
color = 'content.primary',
|
|
15
|
+
name,
|
|
16
|
+
size = 's24',
|
|
17
|
+
}: GlyphProps) => {
|
|
12
18
|
const theme = useAppTheme()
|
|
19
|
+
const modifiers = useMemo(
|
|
20
|
+
() => (accessibilityLabel ? [labelModifier(accessibilityLabel)] : DECORATIVE),
|
|
21
|
+
[accessibilityLabel]
|
|
22
|
+
)
|
|
13
23
|
|
|
14
|
-
return <Image color={theme.color(color)} modifiers={
|
|
24
|
+
return <Image color={theme.color(color)} modifiers={modifiers} size={spacing[size]} systemName={GLYPHS[name].ios} />
|
|
15
25
|
}
|
package/src/glyph/glyph.types.ts
CHANGED
|
@@ -7,6 +7,8 @@ import type {ReactNode} from 'react'
|
|
|
7
7
|
* their own icons with `Icon` / `RnIcon`.
|
|
8
8
|
*/
|
|
9
9
|
export type GlyphProps = {
|
|
10
|
+
/** What a screen reader says. Unset, the glyph is decorative and hidden from it. */
|
|
11
|
+
accessibilityLabel?: string
|
|
10
12
|
color?: ColorToken
|
|
11
13
|
name: GlyphName
|
|
12
14
|
size?: SpaceSize
|
|
@@ -15,19 +15,25 @@ import {
|
|
|
15
15
|
graphicsLayer,
|
|
16
16
|
tween,
|
|
17
17
|
} from '@expo/ui/jetpack-compose/modifiers'
|
|
18
|
-
import {
|
|
18
|
+
import {useAccessibilityModifiers} from '@hzblj/kara-sx'
|
|
19
|
+
import {duration, useAppTheme, useReducedMotion} from '@hzblj/kara-theme'
|
|
19
20
|
import {useMemo} from 'react'
|
|
20
21
|
import {Glyph} from '../../glyph'
|
|
21
22
|
import {useHostGuard} from '../../host-context'
|
|
22
23
|
import type {CollapsibleComponent, CollapsibleProps} from './collapsible.types'
|
|
23
24
|
|
|
24
|
-
/** Material's standard easing, at the length a
|
|
25
|
-
const TURN = tween({durationMillis: duration.
|
|
25
|
+
/** Material's standard easing, at the length a chevron turning takes. */
|
|
26
|
+
const TURN = tween({durationMillis: duration.fast, easing: 'fastOutSlowIn'})
|
|
27
|
+
const SNAP = tween({durationMillis: duration.none})
|
|
26
28
|
|
|
27
29
|
/** Down from the row, fading in — Compose's default grows from a corner, sideways as well as down. */
|
|
28
30
|
const ENTER = EnterTransition.expandVertically().plus(EnterTransition.fadeIn())
|
|
29
31
|
const EXIT = ExitTransition.shrinkVertically().plus(ExitTransition.fadeOut())
|
|
30
32
|
|
|
33
|
+
/** With motion reduced (Compose follows the device's own setting, this `KaraProvider reduceMotion`): no growing. */
|
|
34
|
+
const FADE_IN = EnterTransition.fadeIn()
|
|
35
|
+
const FADE_OUT = ExitTransition.fadeOut()
|
|
36
|
+
|
|
31
37
|
const WIDE = [fillMaxWidth()]
|
|
32
38
|
|
|
33
39
|
/** Material's least touch target. */
|
|
@@ -41,11 +47,21 @@ const ROW_HEIGHT = 48
|
|
|
41
47
|
export const Collapsible: CollapsibleComponent = ({children, expanded, label, onExpandedChange}: CollapsibleProps) => {
|
|
42
48
|
useHostGuard('Collapsible')
|
|
43
49
|
const theme = useAppTheme()
|
|
50
|
+
const reduced = useReducedMotion()
|
|
51
|
+
const accessibilityModifiers = useAccessibilityModifiers({}, true)
|
|
44
52
|
const modifiers = useMemo(
|
|
45
|
-
() => [
|
|
46
|
-
|
|
53
|
+
() => [
|
|
54
|
+
fillMaxWidth(),
|
|
55
|
+
defaultMinSize({minHeight: ROW_HEIGHT}),
|
|
56
|
+
clickable(() => onExpandedChange(!expanded)),
|
|
57
|
+
...accessibilityModifiers,
|
|
58
|
+
],
|
|
59
|
+
[accessibilityModifiers, expanded, onExpandedChange]
|
|
60
|
+
)
|
|
61
|
+
const turn = useMemo(
|
|
62
|
+
() => [graphicsLayer({rotationZ: animated(expanded ? 90 : 0, reduced ? SNAP : TURN)})],
|
|
63
|
+
[expanded, reduced]
|
|
47
64
|
)
|
|
48
|
-
const turn = useMemo(() => [graphicsLayer({rotationZ: animated(expanded ? 90 : 0, TURN)})], [expanded])
|
|
49
65
|
|
|
50
66
|
return (
|
|
51
67
|
<Column modifiers={WIDE}>
|
|
@@ -57,7 +73,11 @@ export const Collapsible: CollapsibleComponent = ({children, expanded, label, on
|
|
|
57
73
|
<Glyph name="chevronRight" size="s20" />
|
|
58
74
|
</Box>
|
|
59
75
|
</Row>
|
|
60
|
-
<AnimatedVisibility
|
|
76
|
+
<AnimatedVisibility
|
|
77
|
+
enterTransition={reduced ? FADE_IN : ENTER}
|
|
78
|
+
exitTransition={reduced ? FADE_OUT : EXIT}
|
|
79
|
+
visible={expanded}
|
|
80
|
+
>
|
|
61
81
|
{children}
|
|
62
82
|
</AnimatedVisibility>
|
|
63
83
|
</Column>
|