@gnome-ui/react-native 1.5.0 → 1.7.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/README.md +647 -3
- package/dist/components/AvatarGroup/AvatarGroup.d.ts +47 -0
- package/dist/components/AvatarGroup/index.d.ts +2 -0
- package/dist/components/AvatarRotator/AvatarRotator.d.ts +65 -0
- package/dist/components/AvatarRotator/index.d.ts +2 -0
- package/dist/components/Bin/Bin.d.ts +18 -0
- package/dist/components/Bin/index.d.ts +2 -0
- package/dist/components/Box/Box.d.ts +87 -0
- package/dist/components/Box/index.d.ts +2 -0
- package/dist/components/Clamp/Clamp.d.ts +61 -0
- package/dist/components/Clamp/index.d.ts +2 -0
- package/dist/components/CoachMark/CoachMark.d.ts +92 -0
- package/dist/components/CoachMark/CoachMarkTour.d.ts +54 -0
- package/dist/components/CoachMark/coachMarkUtils.d.ts +42 -0
- package/dist/components/CoachMark/index.d.ts +5 -0
- package/dist/components/ColorPicker/ColorPicker.d.ts +81 -0
- package/dist/components/ColorPicker/ColorSwatch.d.ts +50 -0
- package/dist/components/ColorPicker/index.d.ts +4 -0
- package/dist/components/ComboRow/ComboRow.d.ts +77 -0
- package/dist/components/ComboRow/index.d.ts +2 -0
- package/dist/components/EntryRow/EntryRow.d.ts +77 -0
- package/dist/components/EntryRow/index.d.ts +2 -0
- package/dist/components/InlineViewSwitcher/InlineViewSwitcher.d.ts +93 -0
- package/dist/components/InlineViewSwitcher/InlineViewSwitcherItem.d.ts +25 -0
- package/dist/components/InlineViewSwitcher/index.d.ts +4 -0
- package/dist/components/InlineViewSwitcher/variants.d.ts +30 -0
- package/dist/components/PasswordEntryRow/PasswordEntryRow.d.ts +45 -0
- package/dist/components/PasswordEntryRow/index.d.ts +2 -0
- package/dist/components/PreferencesGroup/PreferencesGroup.d.ts +52 -0
- package/dist/components/PreferencesGroup/index.d.ts +2 -0
- package/dist/components/StatusPage/StatusPage.d.ts +79 -0
- package/dist/components/StatusPage/index.d.ts +2 -0
- package/dist/components/ToggleGroup/ToggleGroup.d.ts +70 -0
- package/dist/components/ToggleGroup/ToggleGroupItem.d.ts +49 -0
- package/dist/components/ToggleGroup/index.d.ts +4 -0
- package/dist/components/WrapBox/WrapBox.d.ts +67 -0
- package/dist/components/WrapBox/index.d.ts +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.js +2057 -645
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
import { AvatarColor, AvatarSize } from '../Avatar';
|
|
3
|
+
export interface AvatarGroupItem {
|
|
4
|
+
name?: string;
|
|
5
|
+
src?: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
color?: AvatarColor;
|
|
8
|
+
}
|
|
9
|
+
export interface AvatarGroupProps {
|
|
10
|
+
avatars: AvatarGroupItem[];
|
|
11
|
+
/** Max visible avatars before showing the overflow chip. Defaults to `5`. */
|
|
12
|
+
max?: number;
|
|
13
|
+
/** Size applied to all avatars and the overflow chip. Defaults to `"md"`. */
|
|
14
|
+
size?: AvatarSize;
|
|
15
|
+
/** Accessible label for the group. Auto-generated from names when omitted. */
|
|
16
|
+
accessibilityLabel?: string;
|
|
17
|
+
style?: StyleProp<ViewStyle>;
|
|
18
|
+
testID?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Overlapping stack of `Avatar`s with a "+N" overflow indicator. Mirrors
|
|
22
|
+
* `@gnome-ui/react`'s `AvatarGroup`.
|
|
23
|
+
*
|
|
24
|
+
* The web version's separating ring around each overlapping avatar is two
|
|
25
|
+
* layered `box-shadow`s (an inset 1px dark/light border plus an outset 2px
|
|
26
|
+
* window-colored ring) — RN can only give a `View` one border, so this
|
|
27
|
+
* keeps just the outer window-colored ring (`borderWidth: 2,
|
|
28
|
+
* borderColor: theme.windowBgColor`, overriding `Avatar`'s own subtle 1px
|
|
29
|
+
* ring via its `style` prop), since that's the ring doing the actual
|
|
30
|
+
* "stay visually distinct from the avatar behind you" work — the inner
|
|
31
|
+
* hairline is a decorative nicety, not a behavior gap. The overflow chip
|
|
32
|
+
* reuses `Avatar`'s own per-size box dimensions (`sm`/`md`/`lg`/`xl` →
|
|
33
|
+
* 24/32/48/64) so it lines up exactly with the avatars beside it.
|
|
34
|
+
*
|
|
35
|
+
* `role="group"` + `accessibilityLabel` ports 1:1 from RN's newer
|
|
36
|
+
* web-aligned `Role` union (the same `Avatar`/`Badge`/`LevelBar`
|
|
37
|
+
* precedent) — the label is auto-generated from `avatars[].name` (joined,
|
|
38
|
+
* plus "and N more" when overflowing) exactly like the web version, unless
|
|
39
|
+
* overridden. The overflow chip's `+N` text is hidden from the
|
|
40
|
+
* accessibility tree (`accessibilityElementsHidden`/
|
|
41
|
+
* `importantForAccessibility="no"`) in favor of the chip's own
|
|
42
|
+
* `accessibilityLabel="+N more"` — the same one-stop-not-two reasoning
|
|
43
|
+
* `SpinButton`/`Avatar` already established for a decorative inner glyph.
|
|
44
|
+
*
|
|
45
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html
|
|
46
|
+
*/
|
|
47
|
+
export declare const AvatarGroup: ({ avatars, max, size, accessibilityLabel, style, testID, }: AvatarGroupProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
import { AvatarColor, AvatarSize } from '../Avatar';
|
|
3
|
+
export interface AvatarRotatorProps {
|
|
4
|
+
/** Full name used for the accessible label and initials fallback. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** Image URLs to rotate through. */
|
|
7
|
+
avatars?: string[];
|
|
8
|
+
/** Accessible label. Defaults to `name`. */
|
|
9
|
+
alt?: string;
|
|
10
|
+
/** Size of the avatar. Defaults to `"md"`. */
|
|
11
|
+
size?: AvatarSize;
|
|
12
|
+
/** Fallback initials color when no avatar image is available. */
|
|
13
|
+
color?: AvatarColor;
|
|
14
|
+
/** Time between avatar changes in milliseconds. Defaults to `3000`. */
|
|
15
|
+
interval?: number;
|
|
16
|
+
/** Crossfade duration in milliseconds. Defaults to `240`. */
|
|
17
|
+
transitionDuration?: number;
|
|
18
|
+
/** Pause automatic rotation while pressed and held. Defaults to `true`. */
|
|
19
|
+
pauseOnPress?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Controlled active avatar index.
|
|
22
|
+
* When omitted the rotator manages index state internally.
|
|
23
|
+
*/
|
|
24
|
+
activeIndex?: number;
|
|
25
|
+
/** Initial active avatar index for uncontrolled usage. Defaults to `0`. */
|
|
26
|
+
defaultActiveIndex?: number;
|
|
27
|
+
/** Called when the active avatar changes. */
|
|
28
|
+
onIndexChange?: (index: number) => void;
|
|
29
|
+
style?: StyleProp<ViewStyle>;
|
|
30
|
+
testID?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Single avatar surface that crossfades through multiple image sources.
|
|
34
|
+
* Mirrors `@gnome-ui/react`'s `AvatarRotator`. Keeps `Avatar` focused on
|
|
35
|
+
* rendering one identity, while this component owns timing, crossfade
|
|
36
|
+
* animation, and pause behavior.
|
|
37
|
+
*
|
|
38
|
+
* Each source renders as its own absolutely-positioned `Avatar`, layered
|
|
39
|
+
* via `StyleSheet.absoluteFill` and crossfaded with `Animated.timing` on
|
|
40
|
+
* `useNativeDriver: true` — an exact reproduction of the web version's
|
|
41
|
+
* stacked-`.layer`-elements-with-opacity-transition technique, just with
|
|
42
|
+
* `RotatorLayer` (see above) owning each layer's own `Animated.Value`
|
|
43
|
+
* instead of a single shared CSS custom property driving them all.
|
|
44
|
+
*
|
|
45
|
+
* **`prefers-reduced-motion` stops the rotation outright, not just the
|
|
46
|
+
* fade** — ported exactly: the web source's own auto-advance `useEffect`
|
|
47
|
+
* bails out early when reduced motion is on, the same as when paused, so
|
|
48
|
+
* this isn't merely an instant-swap-instead-of-crossfade case like
|
|
49
|
+
* `ProgressBar`'s determinate transitions.
|
|
50
|
+
*
|
|
51
|
+
* The web version's `pauseOnHover` (mouseEnter/mouseLeave, focus/blur)
|
|
52
|
+
* becomes `pauseOnPress` (`onPressIn`/`onPressOut`) — the same touch
|
|
53
|
+
* substitution `Toast`'s own press-and-hold pause already established,
|
|
54
|
+
* kept as a real toggleable prop here (unlike `Toast`, where the web
|
|
55
|
+
* source bakes the behavior in without an escape hatch).
|
|
56
|
+
* `usePrefersReducedMotion` (the web version's `@gnome-ui/hooks` import)
|
|
57
|
+
* has no bearing here — this package's own `useReducedMotion()` from
|
|
58
|
+
* `GnomeProvider` is the correct, already-established source for this.
|
|
59
|
+
*
|
|
60
|
+
* `role="img"` + `accessibilityLabel` ports 1:1 from RN's newer
|
|
61
|
+
* web-aligned `Role` union (the same `Avatar`/`AvatarGroup` precedent).
|
|
62
|
+
*
|
|
63
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html
|
|
64
|
+
*/
|
|
65
|
+
export declare const AvatarRotator: ({ name, avatars, alt, size, color, interval, transitionDuration, pauseOnPress, activeIndex, defaultActiveIndex, onIndexChange, style, testID, }: AvatarRotatorProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { View, ViewProps } from 'react-native';
|
|
2
|
+
export type BinProps = ViewProps;
|
|
3
|
+
/**
|
|
4
|
+
* Single-child container with no visual styling.
|
|
5
|
+
*
|
|
6
|
+
* A transparent wrapper that forwards all `View` props (and a ref to the
|
|
7
|
+
* underlying `View`) straight through — useful as a neutral base for custom
|
|
8
|
+
* components that need to apply layout or size constraints without
|
|
9
|
+
* introducing any chrome of their own.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors `AdwBin` and `@gnome-ui/react`'s own `Bin`. A plain RN `View`
|
|
12
|
+
* already has no default visual styling (no background, no border), so
|
|
13
|
+
* unlike the web port there's no CSS reset to strip — this is a pure
|
|
14
|
+
* passthrough.
|
|
15
|
+
*
|
|
16
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Bin.html
|
|
17
|
+
*/
|
|
18
|
+
export declare const Bin: import('react').ForwardRefExoticComponent<ViewProps & import('react').RefAttributes<View>>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { StyleProp, View, ViewProps, ViewStyle } from 'react-native';
|
|
3
|
+
/** GNOME HIG standard spacing values (matches `GtkBox` spacing tokens). */
|
|
4
|
+
export type BoxSpacing = 3 | 6 | 12 | 18 | 24 | 32 | 48;
|
|
5
|
+
/** Alias of `BoxSpacing` for use as a padding scale. */
|
|
6
|
+
export type BoxPadding = BoxSpacing;
|
|
7
|
+
export type BoxOrientation = 'horizontal' | 'vertical';
|
|
8
|
+
export type BoxAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';
|
|
9
|
+
export type BoxJustify = 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly';
|
|
10
|
+
export interface BoxProps extends Omit<ViewProps, 'style'> {
|
|
11
|
+
/**
|
|
12
|
+
* Direction children are arranged.
|
|
13
|
+
* `"vertical"` → `flexDirection: 'column'` (default).
|
|
14
|
+
* `"horizontal"` → `flexDirection: 'row'`.
|
|
15
|
+
*/
|
|
16
|
+
orientation?: BoxOrientation;
|
|
17
|
+
/**
|
|
18
|
+
* Gap between children, in density-independent pixels.
|
|
19
|
+
* Accepts any of the GNOME HIG standard spacing values or any number.
|
|
20
|
+
* Defaults to `6` (the HIG "standard" inner spacing).
|
|
21
|
+
*/
|
|
22
|
+
spacing?: BoxSpacing | number;
|
|
23
|
+
/**
|
|
24
|
+
* Cross-axis alignment (`alignItems`).
|
|
25
|
+
* Defaults to `"stretch"` for vertical, `"center"` for horizontal.
|
|
26
|
+
*/
|
|
27
|
+
align?: BoxAlign;
|
|
28
|
+
/**
|
|
29
|
+
* Main-axis distribution (`justifyContent`).
|
|
30
|
+
* Defaults to `"start"`.
|
|
31
|
+
*/
|
|
32
|
+
justify?: BoxJustify;
|
|
33
|
+
/** Inner padding applied to all sides, in density-independent pixels. */
|
|
34
|
+
padding?: BoxPadding | number;
|
|
35
|
+
children?: ReactNode;
|
|
36
|
+
style?: StyleProp<ViewStyle>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fundamental flex layout primitive — the RN equivalent of `GtkBox`, and a
|
|
40
|
+
* 1:1 mirror of `@gnome-ui/react`'s own `Box`.
|
|
41
|
+
*
|
|
42
|
+
* Arranges children in a single row or column with consistent spacing
|
|
43
|
+
* following the GNOME Human Interface Guidelines spacing scale:
|
|
44
|
+
*
|
|
45
|
+
* | Token | dp | Use |
|
|
46
|
+
* |-------|----|-----|
|
|
47
|
+
* | tight | 3 | Dense UI, icon + label pairs |
|
|
48
|
+
* | standard | 6 | Default inner spacing |
|
|
49
|
+
* | medium | 12 | Between related groups |
|
|
50
|
+
* | large | 18 | Between loosely related sections |
|
|
51
|
+
* | section | 24 | Page-level section gaps |
|
|
52
|
+
* | loose | 32 | Large content separation |
|
|
53
|
+
* | jumbo | 48 | Hero / splash spacing |
|
|
54
|
+
*
|
|
55
|
+
* `BoxSpacing` keeps the web package's exact seven values rather than being
|
|
56
|
+
* remapped onto this package's own `theme.space1`–`space6` scale — the two
|
|
57
|
+
* overlap at 6/12/18/24/48 but not at 3 or 32/36, and `BoxSpacing` is a
|
|
58
|
+
* published type consumers may already be importing, so it ports verbatim.
|
|
59
|
+
*
|
|
60
|
+
* Two things the web version accepts don't survive the platform: `spacing`
|
|
61
|
+
* and `padding` are numbers only (RN's `gap`/`padding` take dp, not CSS
|
|
62
|
+
* strings like `"1rem"`), and `align`/`justify` — which the web passes
|
|
63
|
+
* straight through to CSS — are mapped from their bare `start`/`end`
|
|
64
|
+
* keywords onto Yoga's `flex-start`/`flex-end`. The prop values stay the
|
|
65
|
+
* web ones so the API reads identically across both packages; only the
|
|
66
|
+
* internal translation differs.
|
|
67
|
+
*
|
|
68
|
+
* `display: 'flex'` has no port and needs none — every RN `View` is already
|
|
69
|
+
* a flex container.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // Vertical section (heading + content)
|
|
73
|
+
* <Box spacing={12}>
|
|
74
|
+
* <Text variant="caption-heading" color="dim">Devices</Text>
|
|
75
|
+
* <BoxedList>…</BoxedList>
|
|
76
|
+
* </Box>
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Horizontal icon + label
|
|
80
|
+
* <Box orientation="horizontal" spacing={6} align="center">
|
|
81
|
+
* <Icon icon={Folder} />
|
|
82
|
+
* <Text>Documents</Text>
|
|
83
|
+
* </Box>
|
|
84
|
+
*
|
|
85
|
+
* @see https://developer.gnome.org/hig/guidelines/spacing.html
|
|
86
|
+
*/
|
|
87
|
+
export declare const Box: import('react').ForwardRefExoticComponent<BoxProps & import('react').RefAttributes<View>>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { StyleProp, View, ViewProps, ViewStyle } from 'react-native';
|
|
3
|
+
export interface ClampProps extends Omit<ViewProps, 'style'> {
|
|
4
|
+
/**
|
|
5
|
+
* Maximum content width in density-independent pixels.
|
|
6
|
+
* The container shrinks freely below this value.
|
|
7
|
+
* Defaults to `600` — the Adwaita recommended narrow-content width.
|
|
8
|
+
*/
|
|
9
|
+
maximumSize?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Fractional width (`0`–`1`) of the available space to use while that
|
|
12
|
+
* space is narrower than `maximumSize` — useful for keeping a
|
|
13
|
+
* comfortable margin on medium-width screens. Defaults to `1` (always
|
|
14
|
+
* fill the width).
|
|
15
|
+
*/
|
|
16
|
+
tighteningThreshold?: number;
|
|
17
|
+
children?: ReactNode;
|
|
18
|
+
style?: StyleProp<ViewStyle>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Constrains its children to a maximum width while letting them shrink
|
|
22
|
+
* freely, mirroring the Adwaita `AdwClamp` widget and `@gnome-ui/react`'s
|
|
23
|
+
* own `Clamp`.
|
|
24
|
+
*
|
|
25
|
+
* Use it on settings pages and forms so content never becomes too wide to
|
|
26
|
+
* read comfortably on a tablet or a landscape phone, while still filling
|
|
27
|
+
* the available width on a narrow one.
|
|
28
|
+
*
|
|
29
|
+
* The web version's `margin-inline: auto` centering becomes
|
|
30
|
+
* `alignSelf: 'center'` here rather than `marginHorizontal: 'auto'` —
|
|
31
|
+
* RN auto-margin support was left unverified for this Yoga version when
|
|
32
|
+
* `Drawer` needed the same trick, so this follows `Drawer`'s resolution of
|
|
33
|
+
* using flex alignment instead. The one consequence is that `Clamp`
|
|
34
|
+
* expects a column-direction parent (RN's default): `alignSelf` acts on
|
|
35
|
+
* the cross axis, so inside a `flexDirection: 'row'` parent it would
|
|
36
|
+
* centre vertically instead. Wrap it in a plain `View` there.
|
|
37
|
+
*
|
|
38
|
+
* `tighteningThreshold` is a real percentage width here, unlike in
|
|
39
|
+
* `@gnome-ui/react` where the prop is declared and documented but never
|
|
40
|
+
* reaches the DOM — implementing it exactly as that package documents it
|
|
41
|
+
* (a fraction of the available width, still capped by `maximumSize`)
|
|
42
|
+
* costs nothing on RN and avoids shipping a dead prop.
|
|
43
|
+
*
|
|
44
|
+
* Adds no padding of its own — wrap the content in its own padded
|
|
45
|
+
* container as needed.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // Settings page — content never wider than 600 dp
|
|
49
|
+
* <Clamp>
|
|
50
|
+
* <BoxedList>…</BoxedList>
|
|
51
|
+
* </Clamp>
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Leave a 10% margin while the screen is narrower than 480 dp
|
|
55
|
+
* <Clamp maximumSize={480} tighteningThreshold={0.9}>
|
|
56
|
+
* <Text>…</Text>
|
|
57
|
+
* </Clamp>
|
|
58
|
+
*
|
|
59
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Clamp.html
|
|
60
|
+
*/
|
|
61
|
+
export declare const Clamp: import('react').ForwardRefExoticComponent<ClampProps & import('react').RefAttributes<View>>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ReactNode, RefObject } from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle, View } from 'react-native';
|
|
3
|
+
import { CoachMarkPlacement } from './coachMarkUtils';
|
|
4
|
+
export interface CoachMarkAction {
|
|
5
|
+
label: string;
|
|
6
|
+
onPress: () => void;
|
|
7
|
+
}
|
|
8
|
+
export interface CoachMarkProps {
|
|
9
|
+
/** Whether the coach mark is shown. */
|
|
10
|
+
open: boolean;
|
|
11
|
+
/** The element to highlight and anchor to. */
|
|
12
|
+
targetRef: RefObject<View | null>;
|
|
13
|
+
/** Heading text. */
|
|
14
|
+
title?: ReactNode;
|
|
15
|
+
/** Body copy explaining the highlighted element. */
|
|
16
|
+
description?: ReactNode;
|
|
17
|
+
/** Preferred side of the target for the bubble. Flips to stay on-screen. Defaults to `'bottom'`. */
|
|
18
|
+
placement?: CoachMarkPlacement;
|
|
19
|
+
/** Dim the rest of the screen and cut a spotlight around the target. Defaults to `true`. */
|
|
20
|
+
spotlight?: boolean;
|
|
21
|
+
/** Extra px around the target inside the spotlight cutout. Defaults to `8`. */
|
|
22
|
+
spotlightPadding?: number;
|
|
23
|
+
/** Close when the dimmed backdrop is pressed. Defaults to `false` (guided). */
|
|
24
|
+
dismissOnBackdrop?: boolean;
|
|
25
|
+
/** 1-based index of this step within a tour, for the "X of N" counter. */
|
|
26
|
+
step?: number;
|
|
27
|
+
/** Total number of steps in the tour. */
|
|
28
|
+
stepCount?: number;
|
|
29
|
+
/** Primary (suggested) action, e.g. Next / Got it. */
|
|
30
|
+
primaryAction?: CoachMarkAction;
|
|
31
|
+
/** Secondary (flat) action, e.g. Back. */
|
|
32
|
+
secondaryAction?: CoachMarkAction;
|
|
33
|
+
/** Called when the Android back button is pressed, or the backdrop is dismissed. */
|
|
34
|
+
onDismiss?: () => void;
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
testID?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A single onboarding coach mark: it spotlights a target element and
|
|
40
|
+
* anchors a callout bubble (title, description, step counter, actions)
|
|
41
|
+
* beside it, guiding a user to one feature. Compose several with
|
|
42
|
+
* `CoachMarkTour`, or drive one directly with `open`. Mirrors
|
|
43
|
+
* `@gnome-ui/react`'s `CoachMark` — not a GNOME HIG widget, a pragmatic
|
|
44
|
+
* feature-discovery pattern.
|
|
45
|
+
*
|
|
46
|
+
* Rebuilt on RN's own `Modal` rather than the web version's DOM `Portal` —
|
|
47
|
+
* no `container` prop, the same `Overlay`/`Drawer` precedent for "no RN
|
|
48
|
+
* portal-target concept." Positions with the same two-pass viewport-aware
|
|
49
|
+
* flip as the web version (`coachMarkUtils.ts`, duplicated verbatim — pure
|
|
50
|
+
* math, no DOM), resolved from `targetRef.current?.measureInWindow(...)`
|
|
51
|
+
* and the bubble's own `onLayout` size, combined once both arrive — the
|
|
52
|
+
* same "resolve two independent async things, then combine" shape
|
|
53
|
+
* `Tooltip`/`Popover`/`Dropdown` already established. No focus trap (no
|
|
54
|
+
* DOM `Tab` concept in RN) and no scroll/resize re-positioning (RN has no
|
|
55
|
+
* global scroll event, the same `Tooltip` precedent for a transient
|
|
56
|
+
* floating element).
|
|
57
|
+
*
|
|
58
|
+
* **The spotlight cutout has no CSS `box-shadow: 0 0 0 100vmax` port** —
|
|
59
|
+
* that trick paints an opaque scrim everywhere *except* inside a rounded
|
|
60
|
+
* rect by using a huge spread shadow, which RN's `shadow*` props (real OS
|
|
61
|
+
* shadows, not a scrim generator) can't reproduce. Rebuilt as four plain
|
|
62
|
+
* `View` bands (top/bottom/left/right of the padded target rect) filling
|
|
63
|
+
* the screen minus a rectangular hole, plus a separate rounded
|
|
64
|
+
* `accentColor`-bordered ring `View` drawn on top at the target rect —
|
|
65
|
+
* visually equivalent (dims everything but the target, with an accent
|
|
66
|
+
* ring around it), just assembled from ordinary rects instead of one
|
|
67
|
+
* masked shape. The whole overlay (bands + ring) sits inside a single
|
|
68
|
+
* full-screen `Pressable`, so — matching the web version exactly — a tap
|
|
69
|
+
* anywhere within it (including visually "in the hole," since the web
|
|
70
|
+
* version's backdrop is one full-bleed element with the spotlight only
|
|
71
|
+
* painted on top, `pointer-events: none`) triggers `dismissOnBackdrop`,
|
|
72
|
+
* never the real content underneath.
|
|
73
|
+
*
|
|
74
|
+
* **`dismissOnBackdrop` has no effect when `spotlight` is `false`** —
|
|
75
|
+
* ported faithfully, not fixed: the web source only renders a backdrop
|
|
76
|
+
* element at all when `spotlight` is true, so with `spotlight={false}`
|
|
77
|
+
* there is nothing to press to dismiss via backdrop either way, on both
|
|
78
|
+
* platforms.
|
|
79
|
+
*
|
|
80
|
+
* The arrow reuses `Popover`/`Tooltip`'s transparent-border-triangle trick
|
|
81
|
+
* rather than the web CSS's rotated-45°-square, the same established RN
|
|
82
|
+
* substitution for every floating-bubble arrow in this package — offset
|
|
83
|
+
* along the bubble's edge by `arrowOffset` (from `computeBubblePosition`,
|
|
84
|
+
* unlike `Tooltip`/`Popover`'s simpler always-centered arrow).
|
|
85
|
+
*
|
|
86
|
+
* `role="dialog"` + `accessibilityViewIsModal` port 1:1 from `Dialog`'s
|
|
87
|
+
* own precedent. `BackHandler`'s `hardwareBackPress` is the Android analog
|
|
88
|
+
* of the web version's Escape listener.
|
|
89
|
+
*
|
|
90
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Window.html
|
|
91
|
+
*/
|
|
92
|
+
export declare const CoachMark: ({ open, targetRef, title, description, placement, spotlight, spotlightPadding, dismissOnBackdrop, step, stepCount, primaryAction, secondaryAction, onDismiss, style, testID, }: CoachMarkProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ReactNode, RefObject } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { CoachMarkPlacement } from './coachMarkUtils';
|
|
4
|
+
export interface CoachMarkStep {
|
|
5
|
+
/** The element this step highlights. */
|
|
6
|
+
targetRef: RefObject<View | null>;
|
|
7
|
+
/** Heading for the step. */
|
|
8
|
+
title?: ReactNode;
|
|
9
|
+
/** Body copy for the step. */
|
|
10
|
+
description?: ReactNode;
|
|
11
|
+
/** Preferred bubble side for this step. Defaults to the tour's placement. */
|
|
12
|
+
placement?: CoachMarkPlacement;
|
|
13
|
+
}
|
|
14
|
+
export interface CoachMarkTourProps {
|
|
15
|
+
/** Ordered steps of the tour. */
|
|
16
|
+
steps: CoachMarkStep[];
|
|
17
|
+
/** Whether the tour is running. */
|
|
18
|
+
open: boolean;
|
|
19
|
+
/** Step to start on when the tour opens. Defaults to `0`. */
|
|
20
|
+
startIndex?: number;
|
|
21
|
+
/** Called after the primary action on the final step. */
|
|
22
|
+
onFinish?: () => void;
|
|
23
|
+
/** Called when the user skips (Skip button or Android back) before finishing. */
|
|
24
|
+
onSkip?: () => void;
|
|
25
|
+
/** Called with the new index whenever the active step changes. */
|
|
26
|
+
onStepChange?: (index: number) => void;
|
|
27
|
+
/** Default preferred bubble side for steps that don't set their own. Defaults to `'bottom'`. */
|
|
28
|
+
placement?: CoachMarkPlacement;
|
|
29
|
+
/** Spotlight the target. Defaults to `true`. */
|
|
30
|
+
spotlight?: boolean;
|
|
31
|
+
/** Close the tour when the dimmed backdrop is pressed. Defaults to `false`. */
|
|
32
|
+
dismissOnBackdrop?: boolean;
|
|
33
|
+
/** Override the action-button labels (for i18n). */
|
|
34
|
+
labels?: Partial<{
|
|
35
|
+
next: string;
|
|
36
|
+
back: string;
|
|
37
|
+
skip: string;
|
|
38
|
+
finish: string;
|
|
39
|
+
}>;
|
|
40
|
+
testID?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Sequential onboarding tour built from `CoachMark` steps. Renders the
|
|
44
|
+
* mark for the active step, wires Next/Back/Skip/Done and the "X of N"
|
|
45
|
+
* counter, and advances through `steps` until finished or skipped.
|
|
46
|
+
* Mirrors `@gnome-ui/react`'s `CoachMarkTour` verbatim — pure state
|
|
47
|
+
* orchestration on top of `CoachMark`, nothing platform-specific to
|
|
48
|
+
* change.
|
|
49
|
+
*
|
|
50
|
+
* Uncontrolled step index: the tour tracks its own position and resets to
|
|
51
|
+
* `startIndex` each time it opens. Drive visibility with `open`; react to
|
|
52
|
+
* completion with `onFinish`/`onSkip`.
|
|
53
|
+
*/
|
|
54
|
+
export declare const CoachMarkTour: ({ steps, open, startIndex, onFinish, onSkip, onStepChange, placement, spotlight, dismissOnBackdrop, labels, testID, }: CoachMarkTourProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positioning helpers for `CoachMark`. Kept pure and measurement-free so
|
|
3
|
+
* they can be unit-tested directly; the component feeds them real rects
|
|
4
|
+
* (from `measureInWindow` instead of the web version's
|
|
5
|
+
* `getBoundingClientRect`, but the shape is identical either way).
|
|
6
|
+
*
|
|
7
|
+
* Duplicated verbatim from `@gnome-ui/react` rather than imported
|
|
8
|
+
* cross-package — the same `fileType.ts`/`Icon.tsx` precedent for
|
|
9
|
+
* DOM-free logic that isn't worth a shared package for one file's worth
|
|
10
|
+
* of code.
|
|
11
|
+
*
|
|
12
|
+
* The algorithm mirrors `Popover`'s viewport-aware flip: try the preferred
|
|
13
|
+
* side, then its opposite, then the rest, picking the first that fits;
|
|
14
|
+
* clamp the cross-axis and shift the arrow when nothing fits perfectly.
|
|
15
|
+
*/
|
|
16
|
+
export type CoachMarkPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
17
|
+
export interface Rect {
|
|
18
|
+
top: number;
|
|
19
|
+
left: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
export interface BubblePosition {
|
|
24
|
+
top: number;
|
|
25
|
+
left: number;
|
|
26
|
+
placement: CoachMarkPlacement;
|
|
27
|
+
/** Arrow centre offset in px from the near edge of the bubble. */
|
|
28
|
+
arrowOffset: number;
|
|
29
|
+
}
|
|
30
|
+
/** Grow a rect outward by `pad` on every side, e.g. the spotlight cutout. */
|
|
31
|
+
export declare const padRect: (rect: Rect, pad: number) => Rect;
|
|
32
|
+
/**
|
|
33
|
+
* Place the callout bubble around `target`, flipping to stay inside a
|
|
34
|
+
* `viewport` (width × height). `bubble` is the measured bubble size.
|
|
35
|
+
*/
|
|
36
|
+
export declare const computeBubblePosition: (target: Rect, bubble: {
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
}, viewport: {
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
}, preferred: CoachMarkPlacement) => BubblePosition;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { CoachMarkAction, CoachMarkProps } from './CoachMark';
|
|
2
|
+
export { CoachMark } from './CoachMark';
|
|
3
|
+
export type { CoachMarkStep, CoachMarkTourProps } from './CoachMarkTour';
|
|
4
|
+
export { CoachMarkTour } from './CoachMarkTour';
|
|
5
|
+
export type { CoachMarkPlacement } from './coachMarkUtils';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
import { ColorSwatchSize } from './ColorSwatch';
|
|
3
|
+
export interface ColorPickerColor {
|
|
4
|
+
/** Color value (hex recommended). */
|
|
5
|
+
value: string;
|
|
6
|
+
/** Human-readable name, used as the swatch's accessible label. */
|
|
7
|
+
label?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Default Adwaita-named palette (matches the `Avatar` color set). */
|
|
10
|
+
export declare const GNOME_PALETTE: ColorPickerColor[];
|
|
11
|
+
export interface ColorPickerProps {
|
|
12
|
+
/** Currently selected color value. */
|
|
13
|
+
value?: string;
|
|
14
|
+
/** Called when the user selects a color. */
|
|
15
|
+
onChange?: (value: string) => void;
|
|
16
|
+
/** Palette to display. Defaults to `GNOME_PALETTE` (the 9 Adwaita colors). */
|
|
17
|
+
colors?: ColorPickerColor[];
|
|
18
|
+
/**
|
|
19
|
+
* Show a "+" button after the palette, and render any `value` outside the
|
|
20
|
+
* palette as its own selected swatch. Pressing either calls
|
|
21
|
+
* `onRequestCustom` — RN has no `<input type="color">`, so the picker UI
|
|
22
|
+
* itself is the consuming app's to provide. Defaults to `false`.
|
|
23
|
+
*/
|
|
24
|
+
allowCustom?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Called when the "+" button (or the current custom swatch) is pressed.
|
|
27
|
+
* Open your own color picker here and feed the result back through
|
|
28
|
+
* `value`/`onChange`.
|
|
29
|
+
*/
|
|
30
|
+
onRequestCustom?: () => void;
|
|
31
|
+
/** Swatch size. Defaults to `"md"`. */
|
|
32
|
+
size?: ColorSwatchSize;
|
|
33
|
+
/** Accessible name for the group. Defaults to `"Color"`. */
|
|
34
|
+
accessibilityLabel?: string;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
style?: StyleProp<ViewStyle>;
|
|
37
|
+
testID?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Color palette picker following the Adwaita `GtkColorButton` + swatch
|
|
41
|
+
* pattern, mirroring `@gnome-ui/react`'s own `ColorPicker`. Renders a
|
|
42
|
+
* wrapping row of circular `ColorSwatch` items backed by a radio group.
|
|
43
|
+
*
|
|
44
|
+
* **`allowCustom` is the one prop that changes meaning.** On the web it
|
|
45
|
+
* wires a hidden `<input type="color">` and the browser supplies the whole
|
|
46
|
+
* picker UI; RN has no such control, and building an HSV picker would be a
|
|
47
|
+
* component in its own right rather than a detail of this one. So the prop
|
|
48
|
+
* keeps its *visible* behaviour — the "+" button, and a `value` outside the
|
|
49
|
+
* palette shown as its own selected swatch — while the press is handed to
|
|
50
|
+
* `onRequestCustom` for the app to answer with whatever picker it has. Round
|
|
51
|
+
* trips through `value`/`onChange` exactly as before.
|
|
52
|
+
*
|
|
53
|
+
* The container is a `WrapBox` (`display: flex; flex-wrap: wrap; gap: 8`
|
|
54
|
+
* with nothing else in `.picker`), and the "+" button's `border: 1.5px
|
|
55
|
+
* dashed` ports directly — `borderStyle: 'dashed'` is one of the few CSS
|
|
56
|
+
* border tricks RN does support. Its plus glyph comes from `@gnome-ui/icons`
|
|
57
|
+
* rather than the web's hand-drawn path, since `Add` is the same mark and
|
|
58
|
+
* already resolves to the foreground color `.customButton` asks for.
|
|
59
|
+
*
|
|
60
|
+
* As in `ToggleGroup`, the group takes `accessibilityRole="radiogroup"` but
|
|
61
|
+
* deliberately not `accessible`, which on iOS would collapse the swatches
|
|
62
|
+
* into one unreachable element. Arrow-key navigation and the roving
|
|
63
|
+
* `tabIndex` drop, as everywhere else here.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const [color, setColor] = useState('#3584e4');
|
|
67
|
+
*
|
|
68
|
+
* <ColorPicker value={color} onChange={setColor} />
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // Custom colors, with your own picker behind the "+"
|
|
72
|
+
* <ColorPicker
|
|
73
|
+
* value={color}
|
|
74
|
+
* onChange={setColor}
|
|
75
|
+
* allowCustom
|
|
76
|
+
* onRequestCustom={() => setPickerOpen(true)}
|
|
77
|
+
* />
|
|
78
|
+
*
|
|
79
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ColorButton.html
|
|
80
|
+
*/
|
|
81
|
+
export declare const ColorPicker: ({ value, onChange, colors, allowCustom, onRequestCustom, size, accessibilityLabel, disabled, style, testID, }: ColorPickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { PressableProps, StyleProp, View, ViewStyle } from 'react-native';
|
|
2
|
+
export type ColorSwatchSize = 'sm' | 'md' | 'lg';
|
|
3
|
+
/** `.swatch-sm` / `-md` / `-lg`. */
|
|
4
|
+
export declare const SWATCH_DIAMETER: Record<ColorSwatchSize, number>;
|
|
5
|
+
/**
|
|
6
|
+
* Width of the selected state's outer ring. Reserved as padding on every
|
|
7
|
+
* swatch, selected or not, so selecting one never reflows the row — the web
|
|
8
|
+
* gets this for free because `box-shadow` rings don't take up space.
|
|
9
|
+
*/
|
|
10
|
+
export declare const RING_WIDTH = 2;
|
|
11
|
+
export interface ColorSwatchProps extends Omit<PressableProps, 'children' | 'style' | 'onPress' | 'disabled'> {
|
|
12
|
+
/** Color value displayed as the swatch background. */
|
|
13
|
+
color: string;
|
|
14
|
+
/** Whether this swatch is the currently selected color. */
|
|
15
|
+
selected?: boolean;
|
|
16
|
+
/** Swatch diameter. Defaults to `"md"`. */
|
|
17
|
+
size?: ColorSwatchSize;
|
|
18
|
+
/** Called with `color` when the swatch is pressed. */
|
|
19
|
+
onSelect?: (color: string) => void;
|
|
20
|
+
/** Accessible name. Defaults to the color value. */
|
|
21
|
+
accessibilityLabel?: string;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
style?: StyleProp<ViewStyle>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Single circular color swatch. Usable standalone or composed inside
|
|
27
|
+
* `ColorPicker`, and shows a white checkmark when `selected`.
|
|
28
|
+
*
|
|
29
|
+
* The web's three `box-shadow` rings collapse into real box-model pieces,
|
|
30
|
+
* since RN gives a `View` exactly one border: the resting
|
|
31
|
+
* `inset 0 0 0 1px` hairline becomes `borderWidth: 1`, the selected state's
|
|
32
|
+
* `inset 0 0 0 2px rgb(255 255 255 / .9)` becomes a 2 dp white border, and
|
|
33
|
+
* the outer `0 0 0 2px var(--swatch-color)` becomes a wrapper painted in the
|
|
34
|
+
* swatch color. That wrapper is always rendered with the same 2 dp padding
|
|
35
|
+
* and only changes color, because a `box-shadow` ring costs no layout space
|
|
36
|
+
* on the web while a real padded wrapper does — reserving it unconditionally
|
|
37
|
+
* is what keeps the row from reflowing as the selection moves.
|
|
38
|
+
*
|
|
39
|
+
* The checkmark is hand-drawn with `react-native-svg` rather than taken from
|
|
40
|
+
* `@gnome-ui/icons`, mirroring the web version, which also hand-draws it:
|
|
41
|
+
* it's a stroked path, and `Icon`'s palette has no white to give it anyway.
|
|
42
|
+
* `filter: drop-shadow(...)` has no RN counterpart, so the path is drawn
|
|
43
|
+
* twice — a translucent black copy offset 1 dp down, then the white one on
|
|
44
|
+
* top — which is what that filter renders and is why it exists: without it
|
|
45
|
+
* the check disappears on a yellow swatch.
|
|
46
|
+
*
|
|
47
|
+
* `:hover { transform: scale(1.12) }` drops with hover; the selected
|
|
48
|
+
* `scale(1.05)` ports as-is.
|
|
49
|
+
*/
|
|
50
|
+
export declare const ColorSwatch: import('react').ForwardRefExoticComponent<ColorSwatchProps & import('react').RefAttributes<View>>;
|