@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 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
- > Component ports from `@gnome-ui/react` continue tier by tiersee this
40
- > package's own [ROADMAP.md](./ROADMAP.md) for full
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,2 @@
1
+ export type { BreakpointBinProps, BreakpointBinState, BreakpointDefinition, } from './BreakpointBin';
2
+ export { BreakpointBin } from './BreakpointBin';
@@ -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,2 @@
1
+ export type { ButtonContentIconPosition, ButtonContentProps } from './ButtonContent';
2
+ export { ButtonContent } from './ButtonContent';
@@ -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;