@gnome-ui/react-native 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +579 -2
- 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/Box/Box.d.ts +87 -0
- package/dist/components/Box/index.d.ts +2 -0
- package/dist/components/Chip/Chip.d.ts +63 -0
- package/dist/components/Chip/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/Drawer/Drawer.d.ts +117 -0
- package/dist/components/Drawer/index.d.ts +2 -0
- package/dist/components/FileTypeIcon/FileTypeIcon.d.ts +45 -0
- package/dist/components/FileTypeIcon/fileType.d.ts +8 -0
- package/dist/components/FileTypeIcon/index.d.ts +3 -0
- package/dist/components/IconButton/IconButton.d.ts +34 -0
- package/dist/components/IconButton/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/PreferencesGroup/PreferencesGroup.d.ts +52 -0
- package/dist/components/PreferencesGroup/index.d.ts +2 -0
- package/dist/components/SegmentedBar/SegmentedBar.d.ts +64 -0
- package/dist/components/SegmentedBar/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 +2147 -577
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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,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,63 @@
|
|
|
1
|
+
import { IconDefinition } from '@gnome-ui/icons';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export interface ChipProps {
|
|
4
|
+
/** Text label displayed inside the chip. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Leading icon from `@gnome-ui/icons`. */
|
|
7
|
+
icon?: IconDefinition;
|
|
8
|
+
/**
|
|
9
|
+
* When provided, renders a remove (×) button and calls this handler.
|
|
10
|
+
* The chip root becomes a plain `View`; only the remove button is
|
|
11
|
+
* interactive.
|
|
12
|
+
*/
|
|
13
|
+
onRemove?: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* When true the chip renders as a toggle button.
|
|
16
|
+
* Use `selected` + `onToggle` to control its state.
|
|
17
|
+
*/
|
|
18
|
+
selectable?: boolean;
|
|
19
|
+
/** Active/selected state. Only relevant when `selectable` is true. */
|
|
20
|
+
selected?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Called when a selectable chip is pressed.
|
|
23
|
+
* Only relevant when `selectable` is true.
|
|
24
|
+
*/
|
|
25
|
+
onToggle?: () => void;
|
|
26
|
+
/** Disabled state — applies to both selectable chips and the remove button. */
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
style?: StyleProp<ViewStyle>;
|
|
29
|
+
testID?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compact pill-shaped label for tags, filters, and selection states.
|
|
33
|
+
* Mirrors `@gnome-ui/react`'s `Chip`.
|
|
34
|
+
*
|
|
35
|
+
* Three usage modes:
|
|
36
|
+
* - **Static** — just a visual label (no `onRemove`, no `selectable`).
|
|
37
|
+
* - **Removable** — add `onRemove` to show a × button.
|
|
38
|
+
* - **Selectable** — add `selectable` + `selected` + `onToggle` for toggle
|
|
39
|
+
* behavior. Same `isInteractive = selectable && !onRemove` precedence as
|
|
40
|
+
* the web version: passing both `selectable` and `onRemove` renders the
|
|
41
|
+
* remove button, not a toggle.
|
|
42
|
+
*
|
|
43
|
+
* Pair with `WrapBox` for multi-chip layouts.
|
|
44
|
+
*
|
|
45
|
+
* Rebuilt with `Pressable`/`View`/`Text` rather than ported from
|
|
46
|
+
* `@gnome-ui/react`'s `<button>`/`<span>`: the selected background/border
|
|
47
|
+
* tint (`color-mix(in srgb, accent 15%/50%, transparent)`) has no RN
|
|
48
|
+
* equivalent, resolved to a literal 8-digit `#RRGGBBAA` hex instead — the
|
|
49
|
+
* same `Highlight` precedent, since `accentBgColor` is always a plain
|
|
50
|
+
* 6-digit hex. The `:hover`/`:active` background transitions collapse into
|
|
51
|
+
* a single pressed-state overlay tinted by `theme.activeOverlay` (the same
|
|
52
|
+
* `ActionRow`/`Card` recipe), since touch has no hover. The leading icon
|
|
53
|
+
* and remove (×) icon don't recolor to match the selected accent text
|
|
54
|
+
* (`color: inherit` on the web) — RN's `Icon` has no `currentColor`
|
|
55
|
+
* equivalent and only accepts a fixed named-swatch palette, none of which
|
|
56
|
+
* tracks the app's configurable accent color, so both icons stay in the
|
|
57
|
+
* default foreground color; a decorative nicety dropped, not a behavior
|
|
58
|
+
* gap. `accessibilityRole="checkbox"` on the selectable form ports 1:1
|
|
59
|
+
* (the same `Checkbox` precedent).
|
|
60
|
+
*
|
|
61
|
+
* @see https://developer.gnome.org/hig/patterns/selection.html
|
|
62
|
+
*/
|
|
63
|
+
export declare const Chip: ({ label, icon, onRemove, selectable, selected, onToggle, disabled, style, testID, }: ChipProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -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,117 @@
|
|
|
1
|
+
import { IconDefinition } from '@gnome-ui/icons';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
4
|
+
export type DrawerSide = 'left' | 'right';
|
|
5
|
+
export type DrawerSize = 'classic' | 'wide';
|
|
6
|
+
export interface DrawerRailItem {
|
|
7
|
+
/** Stable unique identifier. */
|
|
8
|
+
id: string;
|
|
9
|
+
/** Icon shown for this rail entry. */
|
|
10
|
+
icon: IconDefinition;
|
|
11
|
+
/** Accessible name, also used as the tooltip. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Whether this entry represents the currently visible drawer/panel. */
|
|
14
|
+
active?: boolean;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
onPress: () => void;
|
|
17
|
+
}
|
|
18
|
+
export interface DrawerProps {
|
|
19
|
+
/** Whether the drawer is visible. */
|
|
20
|
+
open: boolean;
|
|
21
|
+
/** Edge that the drawer slides in from. Defaults to `"right"`. */
|
|
22
|
+
side?: DrawerSide;
|
|
23
|
+
/** Preset drawer width. Defaults to `"classic"`. */
|
|
24
|
+
size?: DrawerSize;
|
|
25
|
+
/** Optional drawer heading. */
|
|
26
|
+
title?: ReactNode;
|
|
27
|
+
/** Drawer content when a prop is preferred over `children`. */
|
|
28
|
+
content?: ReactNode;
|
|
29
|
+
/** Drawer content. Used when `content` is not provided. */
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
/** Called when the user dismisses the drawer with the Android back button or the backdrop. */
|
|
32
|
+
onClose?: () => void;
|
|
33
|
+
/** Whether pressing the backdrop closes the drawer. Defaults to `true`. */
|
|
34
|
+
closeOnBackdrop?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Narrow icon rail rendered on the drawer's inner edge (the edge facing
|
|
37
|
+
* the backdrop), for switching between related drawers or panels without
|
|
38
|
+
* closing the drawer. Purely presentational — pressing an entry only
|
|
39
|
+
* calls its `onPress`; the caller decides what happens (swap `content`,
|
|
40
|
+
* open a different drawer, etc).
|
|
41
|
+
*/
|
|
42
|
+
rail?: DrawerRailItem[];
|
|
43
|
+
style?: StyleProp<ViewStyle>;
|
|
44
|
+
/** Forwarded to the backdrop — useful for testing. */
|
|
45
|
+
testID?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Slide-in panel for supplementary content, anchored to the left or right
|
|
49
|
+
* edge. Mirrors `@gnome-ui/react`'s `Drawer`.
|
|
50
|
+
*
|
|
51
|
+
* Rebuilt with `View`/`Modal` rather than ported from the web version's
|
|
52
|
+
* `createPortal(document.body)` + manual focus trap + Escape listener:
|
|
53
|
+
* `Modal` already floats above everything with no portal target needed,
|
|
54
|
+
* and `BackHandler`'s `hardwareBackPress` is the Android analog of the
|
|
55
|
+
* Escape listener (the same `Dialog`/`BottomSheet` precedent). Focus
|
|
56
|
+
* trapping has no port — no DOM `Tab`/`document.activeElement` concept in
|
|
57
|
+
* RN's touch-first model.
|
|
58
|
+
*
|
|
59
|
+
* **Floats with a margin on every side, all four corners rounded** — the
|
|
60
|
+
* backdrop `Pressable` carries `padding: theme.space3` (matching the
|
|
61
|
+
* `@gnome-ui/react` source's own recent update to the same floating-card
|
|
62
|
+
* look, not a divergence), and the drawer itself gets a uniform
|
|
63
|
+
* `borderRadius` instead of the flat-edge-on-the-anchored-side look a
|
|
64
|
+
* flush-to-the-screen-edge panel would need. Positioning within that
|
|
65
|
+
* padded backdrop uses `justifyContent: 'flex-end'`/`'flex-start'` on the
|
|
66
|
+
* backdrop (not the web CSS's `margin-left/right: auto` on the drawer
|
|
67
|
+
* itself) — confirmed empirically (a throwaway build with saturated debug
|
|
68
|
+
* colors standing in for the real theme colors, screenshotted on-device)
|
|
69
|
+
* that `justifyContent` renders correctly while auto-margins were, at
|
|
70
|
+
* best, unverified for this RN/Yoga version; `BottomSheet` already proves
|
|
71
|
+
* the same `justifyContent: 'flex-end'` mechanism works on this exact
|
|
72
|
+
* setup, just on the vertical axis instead of horizontal. On a phone-width
|
|
73
|
+
* screen the `classic`/`wide` presets (420/640, sized for wider viewports)
|
|
74
|
+
* get capped to fill essentially the entire available width after the
|
|
75
|
+
* margin either way, so the anchored side becomes visually obvious mainly
|
|
76
|
+
* on tablets — the same responsive behavior the web version would show at
|
|
77
|
+
* an equally narrow browser width, not an RN-specific gap.
|
|
78
|
+
*
|
|
79
|
+
* **No drag-to-dismiss, unlike `BottomSheet`**: the web source only
|
|
80
|
+
* defines entrance keyframes for both the backdrop and the panel, so this
|
|
81
|
+
* follows `Dialog`'s simpler shape (a single `progress` `Animated.Value`
|
|
82
|
+
* replayed via `useEffect` keyed on `open`, no separate exit animation or
|
|
83
|
+
* lagging `visible` state) rather than `BottomSheet`'s
|
|
84
|
+
* `PanResponder`-plus-timed-exit machinery.
|
|
85
|
+
*
|
|
86
|
+
* **The slide distance needs no `onLayout` measurement**, unlike
|
|
87
|
+
* `BottomSheet`'s content-driven height: the drawer's width is a value
|
|
88
|
+
* this component already computes in JS (`size`'s preset, scaled down by
|
|
89
|
+
* `DrawerDepthContext` depth, capped by the available space after the
|
|
90
|
+
* backdrop's margin) — RN's `transform` has no percentage-of-self units
|
|
91
|
+
* (the same `BottomSheet`/`Avatar`/`Slider` pitfall), but since the exact
|
|
92
|
+
* pixel width is already known synchronously, `translateX` can animate
|
|
93
|
+
* from that known offset to `0` immediately, with no first-frame
|
|
94
|
+
* imprecision to accept.
|
|
95
|
+
*
|
|
96
|
+
* **`DrawerDepthContext` (nested-drawer width auto-scaling) ports 1:1** —
|
|
97
|
+
* pure React Context state, no DOM dependency at all. A `Drawer` opened
|
|
98
|
+
* from within another drawer's `content`/`children` is detected via
|
|
99
|
+
* context and scales its own preset width down (`0.85^depth`, floored at
|
|
100
|
+
* `DRAWER_MIN_WIDTH`) so stacked drawers read as a drill-in hierarchy
|
|
101
|
+
* instead of identical overlapping panels.
|
|
102
|
+
*
|
|
103
|
+
* **`rail` reuses the newly-added `IconButton`** (itself just `Button` +
|
|
104
|
+
* `Icon` + optional `Tooltip`, the same composition `@gnome-ui/react`'s own
|
|
105
|
+
* `IconButton` already is) — `aria-pressed` becomes
|
|
106
|
+
* `accessibilityState={{ selected: item.active }}`, the closest RN
|
|
107
|
+
* equivalent for a toggleable icon button with no dedicated visual
|
|
108
|
+
* "pressed" style on either platform's source.
|
|
109
|
+
*
|
|
110
|
+
* `backdrop-filter: blur(4px)` has no port — no native blur view
|
|
111
|
+
* dependency exists in this package, the same gap already dropped from
|
|
112
|
+
* `Sidebar`'s blurred `variant`/`BottomSheet`'s backdrop. `role="dialog"` +
|
|
113
|
+
* `accessibilityViewIsModal` port 1:1 from `Dialog`'s own precedent.
|
|
114
|
+
*
|
|
115
|
+
* @see https://developer.gnome.org/hig/patterns/containers.html
|
|
116
|
+
*/
|
|
117
|
+
export declare const Drawer: ({ open, side, size, title, content, children, onClose, closeOnBackdrop, rail, style, testID, }: DrawerProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
import { IconSize } from '../Icon';
|
|
3
|
+
export interface FileTypeIconProps {
|
|
4
|
+
/** File name (e.g. `"report.pdf"`) — resolves the icon from its extension. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/**
|
|
7
|
+
* MIME type (e.g. `"application/pdf"`, `"inode/directory"`).
|
|
8
|
+
* Takes precedence over `name` when both are provided.
|
|
9
|
+
*/
|
|
10
|
+
mimeType?: string;
|
|
11
|
+
/** Renders the folder icon regardless of `name`/`mimeType`. */
|
|
12
|
+
isFolder?: boolean;
|
|
13
|
+
/** Thumbnail image URL. When provided, renders the image instead of the resolved icon. */
|
|
14
|
+
thumbnail?: string;
|
|
15
|
+
/** Accessible label. Defaults to a generated description (e.g. `"PDF document"`). */
|
|
16
|
+
label?: string;
|
|
17
|
+
/** Icon size. Defaults to `"md"`. */
|
|
18
|
+
size?: IconSize;
|
|
19
|
+
style?: StyleProp<ViewStyle>;
|
|
20
|
+
testID?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Small icon — optionally a thumbnail — resolved from a file's MIME type
|
|
24
|
+
* or name extension. Useful for file-manager-style listings. Mirrors
|
|
25
|
+
* `@gnome-ui/react`'s `FileTypeIcon`.
|
|
26
|
+
*
|
|
27
|
+
* Falls back to the generic file icon (mirrors freedesktop's
|
|
28
|
+
* `text-x-generic`) when the type can't be resolved.
|
|
29
|
+
*
|
|
30
|
+
* `fileType.ts`'s category-resolution logic (MIME type / extension → one of
|
|
31
|
+
* 13 categories, plus the freedesktop icon and generated label per
|
|
32
|
+
* category) is pure, DOM-free TS — duplicated verbatim from
|
|
33
|
+
* `@gnome-ui/react` rather than imported cross-package, the same
|
|
34
|
+
* `isIconDefinition`/`Icon.tsx` precedent already established for
|
|
35
|
+
* DOM-independent logic that still isn't worth a shared package for one
|
|
36
|
+
* function's worth of code.
|
|
37
|
+
*
|
|
38
|
+
* `role="img"` + `accessibilityLabel` ports 1:1 (the same `Avatar`/
|
|
39
|
+
* `LevelBar` precedent for RN's newer web-aligned `Role` union). The
|
|
40
|
+
* thumbnail reuses `Avatar`'s own `Image`/`resizeMode="cover"` recipe,
|
|
41
|
+
* sized from `Icon`'s own `ICON_SIZE_MAP` so swapping between the resolved
|
|
42
|
+
* icon and a thumbnail never shifts layout — the same reasoning the web
|
|
43
|
+
* version's `.sm`/`.md`/`.lg` classes document.
|
|
44
|
+
*/
|
|
45
|
+
export declare const FileTypeIcon: ({ name, mimeType, isFolder, thumbnail, label, size, style, testID, }: FileTypeIconProps) => import("react/jsx-runtime").JSX.Element;
|