@gnome-ui/react-native 1.8.0 → 1.9.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 +99 -2
- package/dist/components/BreakpointBin/BreakpointBin.d.ts +65 -0
- package/dist/components/BreakpointBin/index.d.ts +2 -0
- package/dist/components/ButtonContent/ButtonContent.d.ts +47 -0
- package/dist/components/ButtonContent/index.d.ts +2 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useBreakpoint.d.ts +79 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1006 -917
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,8 +36,14 @@ React Native component library following the [GNOME Human Interface Guidelines](
|
|
|
36
36
|
> a React Native-only original with no `@gnome-ui/react` source at all
|
|
37
37
|
> (desktop apps don't have a bottom tab bar pattern to mirror), built on
|
|
38
38
|
> explicit request for the iOS/Android fixed bottom-navigation shape.
|
|
39
|
-
>
|
|
40
|
-
> package's
|
|
39
|
+
> `useBreakpoint` and `BreakpointBin` (both Tier 6) also shipped — the
|
|
40
|
+
> package's first adaptive-layout primitives, built on `useWindowDimensions`
|
|
41
|
+
> and `onLayout` respectively since there's no CSS media query/container
|
|
42
|
+
> query to lean on. `ButtonContent` (Tier 15) also shipped — an icon+label
|
|
43
|
+
> layout helper mostly redundant with `Button`'s own `leadingIcon`/
|
|
44
|
+
> `trailingIcon`, kept for composing the same spacing outside `Button`
|
|
45
|
+
> itself. Component ports from `@gnome-ui/react` continue tier by
|
|
46
|
+
> tier — see this package's own [ROADMAP.md](./ROADMAP.md) for full
|
|
41
47
|
> per-tier status against all 130 `@gnome-ui/react` components, and the
|
|
42
48
|
> main [ROADMAP.md](../../ROADMAP.md) Priority 3 for the framework
|
|
43
49
|
> expansion this package belongs to.
|
|
@@ -497,6 +503,10 @@ button is the RN-idiomatic stand-in. The clear (×) button appears whenever
|
|
|
497
503
|
glyphs (`🔍`/`×`) rather than `@gnome-ui/icons`, matching every other
|
|
498
504
|
no-SVG-dependency component in this package.
|
|
499
505
|
|
|
506
|
+
`inline` drops the header-bar background/border so the bar blends into any
|
|
507
|
+
surface — a card, a plain content area, a custom container — instead of
|
|
508
|
+
looking like it belongs to a `HeaderBar`.
|
|
509
|
+
|
|
500
510
|
Dropped relative to `@gnome-ui/react`'s `SearchBar`: the `suggestions` /
|
|
501
511
|
`onSuggestionSelect` / `loadingSuggestions` / `renderSuggestion` /
|
|
502
512
|
`suggestionsLabel` autocomplete popover — it depends on a portal +
|
|
@@ -2104,6 +2114,37 @@ substitution `Chip`'s selected-state tint already established. `<blockquote>`/
|
|
|
2104
2114
|
`<footer>`/`<cite>` have no RN element equivalent, so this renders as a
|
|
2105
2115
|
plain `View` with no semantic role.
|
|
2106
2116
|
|
|
2117
|
+
### ButtonContent
|
|
2118
|
+
|
|
2119
|
+
```tsx
|
|
2120
|
+
import { ButtonContent, Icon } from '@gnome-ui/react-native';
|
|
2121
|
+
import { Save } from '@gnome-ui/icons';
|
|
2122
|
+
|
|
2123
|
+
<Pressable onPress={save}>
|
|
2124
|
+
<ButtonContent icon={<Icon icon={Save} size="sm" />} label="Save" color="accent" />
|
|
2125
|
+
</Pressable>;
|
|
2126
|
+
```
|
|
2127
|
+
|
|
2128
|
+
Icon + label layout helper mirroring `AdwButtonContent` — the same 6 dp
|
|
2129
|
+
gap, vertically-centered row every button in this package already
|
|
2130
|
+
produces internally via `leadingIcon`/`trailingIcon`. **Mostly redundant
|
|
2131
|
+
with `Button`'s own `leadingIcon`/`trailingIcon` props** for anything that
|
|
2132
|
+
actually is a `Button` — those already lay the icon and a themed,
|
|
2133
|
+
variant-colored label out identically, with no separate color prop
|
|
2134
|
+
needed. Reach for `ButtonContent` when composing icon+label content for
|
|
2135
|
+
something that *isn't* this package's `Button` instead: a bespoke
|
|
2136
|
+
`Pressable`, a custom card action, anywhere the same Adwaita spacing
|
|
2137
|
+
convention is wanted outside `Button` itself.
|
|
2138
|
+
|
|
2139
|
+
Unlike the web version, there's no `currentColor` to inherit the
|
|
2140
|
+
surrounding button's text color from, so a `color` prop (the same
|
|
2141
|
+
`TextColor` union `Text`/`ButtonRow` already use) needs to be passed
|
|
2142
|
+
explicitly to match — e.g. `color="accent"` beside a `suggested`-style
|
|
2143
|
+
action, `color="destructive"` beside a destructive one. The icon slot is
|
|
2144
|
+
marked `accessibilityElementsHidden`/`importantForAccessibility="no-hide-
|
|
2145
|
+
descendants"`, the RN equivalent of the web version's `aria-hidden="true"`
|
|
2146
|
+
on its icon `<span>`.
|
|
2147
|
+
|
|
2107
2148
|
### ButtonRow
|
|
2108
2149
|
|
|
2109
2150
|
```tsx
|
|
@@ -2397,6 +2438,62 @@ mode — `true` for a dot, a number for a count (capped at `"99+"`).
|
|
|
2397
2438
|
this package takes no dependency on `react-native-safe-area-context`
|
|
2398
2439
|
itself.
|
|
2399
2440
|
|
|
2441
|
+
### useBreakpoint
|
|
2442
|
+
|
|
2443
|
+
```tsx
|
|
2444
|
+
import { useBreakpoint } from '@gnome-ui/react-native';
|
|
2445
|
+
|
|
2446
|
+
const { isNarrow, isMedium, width } = useBreakpoint();
|
|
2447
|
+
|
|
2448
|
+
return isNarrow ? <CompactLayout /> : <RegularLayout />;
|
|
2449
|
+
```
|
|
2450
|
+
|
|
2451
|
+
Tracks the window width against the same GNOME/libadwaita canonical
|
|
2452
|
+
breakpoints as `@gnome-ui/react`'s hook of the same name — `narrow` (≤ 400
|
|
2453
|
+
dp, split views collapse), `medium` (≤ 550 dp, `ViewSwitcher` moves to a
|
|
2454
|
+
bottom bar), `wide` (≤ 860 dp, outer pane of a nested split view
|
|
2455
|
+
collapses). Built on `useWindowDimensions` rather than `Dimensions.get` +
|
|
2456
|
+
a manual listener, since it already re-renders subscribers on every
|
|
2457
|
+
rotation/resize — there's no CSS media query to lean on here, unlike the
|
|
2458
|
+
web version's `window.innerWidth` + `resize` listener.
|
|
2459
|
+
|
|
2460
|
+
Also exports `bucketForWidth`/`resolveResponsive`/`ResponsiveValue` — the
|
|
2461
|
+
same small pure-function toolkit the web hook exports, for picking a
|
|
2462
|
+
value that varies by breakpoint (`{ base: 3, wide: 2, narrow: 1 }`-shaped
|
|
2463
|
+
maps). These aren't wired into any component's props yet; they're
|
|
2464
|
+
exported now so a future adaptive component (`Sidebar`'s `mode` prop,
|
|
2465
|
+
`NavigationSplitView`, …) doesn't have to redefine the bucket-fallback
|
|
2466
|
+
logic from scratch.
|
|
2467
|
+
|
|
2468
|
+
### BreakpointBin
|
|
2469
|
+
|
|
2470
|
+
```tsx
|
|
2471
|
+
import { BreakpointBin } from '@gnome-ui/react-native';
|
|
2472
|
+
|
|
2473
|
+
<BreakpointBin breakpoints={[{ name: 'compact', maxWidth: 400 }]}>
|
|
2474
|
+
{({ activeBreakpoint }) =>
|
|
2475
|
+
activeBreakpoint === 'compact' ? <CompactCard /> : <WideCard />
|
|
2476
|
+
}
|
|
2477
|
+
</BreakpointBin>;
|
|
2478
|
+
```
|
|
2479
|
+
|
|
2480
|
+
The per-*component* (container-query) sibling of `useBreakpoint` — reacts
|
|
2481
|
+
to **its own width**, not the window, so the same component can render
|
|
2482
|
+
differently depending on how much space its parent gives it, regardless
|
|
2483
|
+
of device size. Two `BreakpointBin`s with identical `breakpoints` can be
|
|
2484
|
+
in different states side by side.
|
|
2485
|
+
|
|
2486
|
+
`@gnome-ui/react`'s version watches itself with `ResizeObserver`; RN has
|
|
2487
|
+
no such API, so this measures via `onLayout` instead — fired on mount and
|
|
2488
|
+
again on every subsequent resize of the wrapping `View` (a parent's flex
|
|
2489
|
+
layout reflowing, a device rotation, an ancestor `BreakpointBin` flipping
|
|
2490
|
+
column↔row). The active breakpoint is the smallest `maxWidth` ≥ the
|
|
2491
|
+
current width, declaration order doesn't matter (sorted internally), and
|
|
2492
|
+
`activeBreakpoint` is `null` when the container is wider than every
|
|
2493
|
+
threshold. Unlike the web version there's no `data-breakpoint` attribute
|
|
2494
|
+
to expose for CSS targeting (RN has no attribute selectors) — branch on
|
|
2495
|
+
`activeBreakpoint` directly inside the render prop instead.
|
|
2496
|
+
|
|
2400
2497
|
## Installation
|
|
2401
2498
|
|
|
2402
2499
|
```bash
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { StyleProp, ViewProps, ViewStyle } from 'react-native';
|
|
3
|
+
export interface BreakpointDefinition {
|
|
4
|
+
/** Identifier for this breakpoint, e.g. `"compact"` or `"narrow"`. */
|
|
5
|
+
name: string;
|
|
6
|
+
/**
|
|
7
|
+
* Container width threshold in dp.
|
|
8
|
+
* This breakpoint becomes active when the container width is ≤ this value.
|
|
9
|
+
*/
|
|
10
|
+
maxWidth: number;
|
|
11
|
+
}
|
|
12
|
+
export interface BreakpointBinState {
|
|
13
|
+
/**
|
|
14
|
+
* Name of the currently active breakpoint, or `null` when the container
|
|
15
|
+
* is wider than all defined breakpoints.
|
|
16
|
+
*/
|
|
17
|
+
activeBreakpoint: string | null;
|
|
18
|
+
/** Current container width in dp (from `onLayout`). */
|
|
19
|
+
width: number;
|
|
20
|
+
}
|
|
21
|
+
export interface BreakpointBinProps extends Omit<ViewProps, 'children' | 'style'> {
|
|
22
|
+
/**
|
|
23
|
+
* Breakpoint definitions. The active breakpoint is the smallest
|
|
24
|
+
* `maxWidth` ≥ the container's current width — declaration order doesn't
|
|
25
|
+
* matter, they're sorted internally.
|
|
26
|
+
*/
|
|
27
|
+
breakpoints: BreakpointDefinition[];
|
|
28
|
+
/**
|
|
29
|
+
* Render prop that receives the current breakpoint state.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <BreakpointBin breakpoints={[{ name: "compact", maxWidth: 400 }]}>
|
|
34
|
+
* {({ activeBreakpoint }) =>
|
|
35
|
+
* activeBreakpoint === "compact" ? <CompactLayout /> : <WideLayout />
|
|
36
|
+
* }
|
|
37
|
+
* </BreakpointBin>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
children: (state: BreakpointBinState) => ReactNode;
|
|
41
|
+
style?: StyleProp<ViewStyle>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Container that fires layout changes when **its own width** crosses
|
|
45
|
+
* defined thresholds — the CSS container-query equivalent of
|
|
46
|
+
* `AdwBreakpointBin` (libadwaita 1.9 / GNOME 50), and the per-*component*
|
|
47
|
+
* sibling of `useBreakpoint` (which watches the window instead).
|
|
48
|
+
*
|
|
49
|
+
* `@gnome-ui/react`'s version watches its own width via `ResizeObserver`;
|
|
50
|
+
* RN has no such API, so this measures the same thing via `onLayout` —
|
|
51
|
+
* fired on mount and again on every subsequent resize of the `View` itself
|
|
52
|
+
* (e.g. the parent's flex layout reflowing, or a device rotation changing
|
|
53
|
+
* how much space this container is given). Composable the same way: the
|
|
54
|
+
* same component can render differently depending on how much space its
|
|
55
|
+
* parent gives it, regardless of the window size — two `BreakpointBin`s
|
|
56
|
+
* side by side with identical `breakpoints` can be in different states.
|
|
57
|
+
*
|
|
58
|
+
* Unlike the web version, there's no `data-breakpoint` attribute to expose
|
|
59
|
+
* for CSS targeting (RN has no attribute selectors) — branch on
|
|
60
|
+
* `activeBreakpoint` directly inside the render prop instead, exactly like
|
|
61
|
+
* every story below already does.
|
|
62
|
+
*
|
|
63
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.BreakpointBin.html
|
|
64
|
+
*/
|
|
65
|
+
export declare const BreakpointBin: ({ breakpoints, children, style, onLayout, ...viewProps }: BreakpointBinProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { StyleProp, ViewProps, ViewStyle } from 'react-native';
|
|
3
|
+
import { TextColor } from '../Text';
|
|
4
|
+
export type ButtonContentIconPosition = 'start' | 'end';
|
|
5
|
+
export interface ButtonContentProps extends Omit<ViewProps, 'style'> {
|
|
6
|
+
/** Icon placed next to the label. Rendered as-is — size/color it yourself. */
|
|
7
|
+
icon?: ReactNode;
|
|
8
|
+
/** Text label. */
|
|
9
|
+
label: string;
|
|
10
|
+
/**
|
|
11
|
+
* Position of the icon relative to the label.
|
|
12
|
+
* @default 'start'
|
|
13
|
+
*/
|
|
14
|
+
iconPosition?: ButtonContentIconPosition;
|
|
15
|
+
/**
|
|
16
|
+
* Label color. RN has no `currentColor` equivalent, so unlike the web
|
|
17
|
+
* version (which inherits the parent button's text color via CSS), this
|
|
18
|
+
* needs to be told explicitly which color to match — e.g. `"accent"`
|
|
19
|
+
* when placed inside a `suggested` `Button`. Defaults to `"default"`.
|
|
20
|
+
*/
|
|
21
|
+
color?: TextColor;
|
|
22
|
+
style?: StyleProp<ViewStyle>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Icon + label layout helper for buttons that contain both an icon and
|
|
26
|
+
* text — the same 6 dp gap / vertically-centered row every button in this
|
|
27
|
+
* package already produces internally. Mirrors `AdwButtonContent`.
|
|
28
|
+
*
|
|
29
|
+
* **Mostly redundant with `Button`'s own `leadingIcon`/`trailingIcon`
|
|
30
|
+
* props** — pass those instead for anything that's actually a `Button`,
|
|
31
|
+
* they already lay the icon and (themed, variant-colored) label out
|
|
32
|
+
* identically and need no separate color prop. Reach for `ButtonContent`
|
|
33
|
+
* when composing icon+label content for something that *isn't* this
|
|
34
|
+
* package's `Button` — a bespoke `Pressable`, a `ButtonRow` title slot, a
|
|
35
|
+
* `Chip`'s custom content — anywhere the exact same Adwaita icon/label
|
|
36
|
+
* spacing convention is wanted outside `Button` itself.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <Pressable onPress={save}>
|
|
41
|
+
* <ButtonContent icon={<Icon icon={DocumentSave} />} label="Save" />
|
|
42
|
+
* </Pressable>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.ButtonContent.html
|
|
46
|
+
*/
|
|
47
|
+
export declare const ButtonContent: ({ icon, label, iconPosition, color, style, ...viewProps }: ButtonContentProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useBreakpoint';
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GNOME / libadwaita canonical breakpoints (in dp, assuming 1 sp = 1 dp at 1× density).
|
|
3
|
+
*
|
|
4
|
+
* | Name | Max width | Pattern triggered |
|
|
5
|
+
* |----------|-----------|--------------------|
|
|
6
|
+
* | `narrow` | ≤ 400 dp | Collapse split views; sidebar becomes overlay |
|
|
7
|
+
* | `medium` | ≤ 550 dp | Move ViewSwitcher to a bottom bar |
|
|
8
|
+
* | `wide` | ≤ 860 dp | Collapse outer pane in nested split views |
|
|
9
|
+
*
|
|
10
|
+
* Identical thresholds to `@gnome-ui/react`'s `useBreakpoint` — only the
|
|
11
|
+
* measurement source differs (`useWindowDimensions` instead of
|
|
12
|
+
* `window.innerWidth`/a `resize` listener).
|
|
13
|
+
*
|
|
14
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Breakpoint.html
|
|
15
|
+
*/
|
|
16
|
+
export declare const GNOME_BREAKPOINTS: {
|
|
17
|
+
/** ≤ 400 dp — split views collapse to single pane */
|
|
18
|
+
readonly narrow: 400;
|
|
19
|
+
/** ≤ 550 dp — ViewSwitcher moves to bottom bar */
|
|
20
|
+
readonly medium: 550;
|
|
21
|
+
/** ≤ 860 dp — outer pane of nested split views collapses */
|
|
22
|
+
readonly wide: 860;
|
|
23
|
+
};
|
|
24
|
+
export type GnomeBreakpointName = keyof typeof GNOME_BREAKPOINTS;
|
|
25
|
+
export interface BreakpointState {
|
|
26
|
+
/** Width ≤ 400 dp — split views are collapsed. */
|
|
27
|
+
isNarrow: boolean;
|
|
28
|
+
/** Width ≤ 550 dp — medium or narrower. */
|
|
29
|
+
isMedium: boolean;
|
|
30
|
+
/** Width ≤ 860 dp — wide or narrower. */
|
|
31
|
+
isWide: boolean;
|
|
32
|
+
/** Current window width in dp. */
|
|
33
|
+
width: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Tracks the window width against GNOME / libadwaita breakpoints.
|
|
37
|
+
*
|
|
38
|
+
* Built on `useWindowDimensions` (not `Dimensions.get` + a manual listener),
|
|
39
|
+
* since it already re-renders its subscribers on every rotation/resize —
|
|
40
|
+
* there is no CSS media query to lean on here, unlike the web version.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const { isNarrow, isMedium } = useBreakpoint();
|
|
44
|
+
* // isNarrow → true when the window is ≤ 400 dp wide (split views should collapse)
|
|
45
|
+
* // isMedium → true when ≤ 550 dp (use a bottom ViewSwitcher instead)
|
|
46
|
+
*/
|
|
47
|
+
export declare function useBreakpoint(): BreakpointState;
|
|
48
|
+
/** The widest bucket, above every breakpoint, is `base`. */
|
|
49
|
+
export type GnomeBreakpointBucket = GnomeBreakpointName | 'base';
|
|
50
|
+
/**
|
|
51
|
+
* A value that may vary by breakpoint: either the value itself, or a map of
|
|
52
|
+
* breakpoint names to values.
|
|
53
|
+
*
|
|
54
|
+
* The buckets are max-widths, so the map reads like stacked `max-width`
|
|
55
|
+
* media queries — `base` is the widest, and the narrowest matching entry
|
|
56
|
+
* wins:
|
|
57
|
+
*
|
|
58
|
+
* ```ts
|
|
59
|
+
* { base: 3, wide: 2, narrow: 1 }
|
|
60
|
+
* // ≤ 400 dp → 1 | ≤ 860 dp → 2 | wider → 3
|
|
61
|
+
* // (550 dp matches `wide`, since no `medium` entry is given)
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export type ResponsiveValue<T> = T | ({
|
|
65
|
+
base?: T;
|
|
66
|
+
} & Partial<Record<GnomeBreakpointName, T>>);
|
|
67
|
+
/**
|
|
68
|
+
* Bucket a width falls into. The same thresholds serve `useBreakpoint` and
|
|
69
|
+
* `BreakpointBin` — a breakpoint applies to whatever it's measured against,
|
|
70
|
+
* window or container alike.
|
|
71
|
+
*
|
|
72
|
+
* A width of `0` means "not measured yet" and reports `base`, so the first
|
|
73
|
+
* render matches the widest layout rather than flashing the narrowest one.
|
|
74
|
+
*/
|
|
75
|
+
export declare function bucketForWidth(width: number): GnomeBreakpointBucket;
|
|
76
|
+
/** True when `value` is a breakpoint map rather than a plain value. */
|
|
77
|
+
export declare function isResponsiveMap<T>(value: ResponsiveValue<T> | undefined): boolean;
|
|
78
|
+
/** Pick the entry for `bucket`, falling back outwards to the wider ones. */
|
|
79
|
+
export declare function resolveResponsive<T>(value: ResponsiveValue<T> | undefined, bucket: GnomeBreakpointBucket, fallback: T): T;
|