@gnome-ui/react-native 1.6.0 → 1.8.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.
Files changed (43) hide show
  1. package/README.md +533 -4
  2. package/dist/components/Bin/Bin.d.ts +18 -0
  3. package/dist/components/Bin/index.d.ts +2 -0
  4. package/dist/components/Blockquote/Blockquote.d.ts +40 -0
  5. package/dist/components/Blockquote/index.d.ts +2 -0
  6. package/dist/components/BottomTabBar/BottomTabBar.d.ts +106 -0
  7. package/dist/components/BottomTabBar/index.d.ts +2 -0
  8. package/dist/components/ButtonRow/ButtonRow.d.ts +41 -0
  9. package/dist/components/ButtonRow/index.d.ts +2 -0
  10. package/dist/components/Callout/Callout.d.ts +36 -0
  11. package/dist/components/Callout/index.d.ts +2 -0
  12. package/dist/components/CheckRow/CheckRow.d.ts +47 -0
  13. package/dist/components/CheckRow/index.d.ts +2 -0
  14. package/dist/components/ColorPicker/ColorPicker.d.ts +81 -0
  15. package/dist/components/ColorPicker/ColorSwatch.d.ts +50 -0
  16. package/dist/components/ColorPicker/index.d.ts +4 -0
  17. package/dist/components/ComboRow/ComboRow.d.ts +77 -0
  18. package/dist/components/ComboRow/index.d.ts +2 -0
  19. package/dist/components/EntryRow/EntryRow.d.ts +77 -0
  20. package/dist/components/EntryRow/index.d.ts +2 -0
  21. package/dist/components/ExpanderRow/ExpanderRow.d.ts +58 -0
  22. package/dist/components/ExpanderRow/index.d.ts +2 -0
  23. package/dist/components/FieldGroup/FieldGroup.d.ts +52 -0
  24. package/dist/components/FieldGroup/index.d.ts +2 -0
  25. package/dist/components/FilterableMultiSelectDropdown/FilterableMultiSelectDropdown.d.ts +46 -0
  26. package/dist/components/FilterableMultiSelectDropdown/index.d.ts +2 -0
  27. package/dist/components/Icon/Icon.d.ts +11 -1
  28. package/dist/components/MultiSelectDropdown/MultiSelectDropdown.d.ts +55 -0
  29. package/dist/components/MultiSelectDropdown/index.d.ts +2 -0
  30. package/dist/components/PasswordEntryRow/PasswordEntryRow.d.ts +45 -0
  31. package/dist/components/PasswordEntryRow/index.d.ts +2 -0
  32. package/dist/components/PasswordField/PasswordField.d.ts +48 -0
  33. package/dist/components/PasswordField/index.d.ts +2 -0
  34. package/dist/components/RangeSlider/RangeSlider.d.ts +73 -0
  35. package/dist/components/RangeSlider/index.d.ts +2 -0
  36. package/dist/components/StatusBadge/StatusBadge.d.ts +47 -0
  37. package/dist/components/StatusBadge/index.d.ts +2 -0
  38. package/dist/index.cjs +1 -1
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.ts +17 -0
  41. package/dist/index.js +2454 -778
  42. package/dist/index.js.map +1 -1
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -24,10 +24,20 @@ React Native component library following the [GNOME Human Interface Guidelines](
24
24
  > `SegmentedBar`/`AvatarGroup`/`AvatarRotator`/`CoachMark`/`CoachMarkTour`
25
25
  > (Tier 20), `Chip` (Tier 7), `IconButton`/`Drawer` (Tier 8/Tier 20), and
26
26
  > `Clamp` (Tier 6), `Box` (Tier 20), `WrapBox`/`ToggleGroup` (Tier 7), and
27
- > `InlineViewSwitcher` (Tier 8), and `PreferencesGroup` (Tier 13) also
28
- > shipped. Component ports from
29
- > `@gnome-ui/react` continue tier by tier see this package's own
30
- > [ROADMAP.md](./ROADMAP.md) for full
27
+ > `InlineViewSwitcher` (Tier 8), `PreferencesGroup` (Tier 13), and
28
+ > `EntryRow`/`PasswordEntryRow`/`ComboRow` (Tier 12), `ColorPicker`
29
+ > (Tier 20), `Bin` (Tier 15), `Blockquote` (Tier 20), `ButtonRow`
30
+ > (Tier 8), `Callout` (Tier 20), `CheckRow` (Tier 12), `ExpanderRow`
31
+ > (Tier 8), `FieldGroup` (Tier 20), and `MultiSelectDropdown` (Tier 20)
32
+ > also shipped, along with `FilterableMultiSelectDropdown` — an original
33
+ > `@gnome-ui/react`-only component (not a GNOME HIG port) built once its
34
+ > prerequisite `MultiSelectDropdown` landed — and `PasswordField`/
35
+ > `RangeSlider`/`StatusBadge` (all Tier 20). `BottomTabBar` also shipped —
36
+ > a React Native-only original with no `@gnome-ui/react` source at all
37
+ > (desktop apps don't have a bottom tab bar pattern to mirror), built on
38
+ > explicit request for the iOS/Android fixed bottom-navigation shape.
39
+ > Component ports from `@gnome-ui/react` continue tier by tier — see this
40
+ > package's own [ROADMAP.md](./ROADMAP.md) for full
31
41
  > per-tier status against all 130 `@gnome-ui/react` components, and the
32
42
  > main [ROADMAP.md](../../ROADMAP.md) Priority 3 for the framework
33
43
  > expansion this package belongs to.
@@ -1868,6 +1878,525 @@ own title. `min-width: 0` on the header text has no port and needs none: it's
1868
1878
  the classic CSS flexbox override for a min-content floor Yoga doesn't apply
1869
1879
  in the first place.
1870
1880
 
1881
+ ### EntryRow
1882
+
1883
+ ```tsx
1884
+ import { EntryRow } from '@gnome-ui/react-native';
1885
+
1886
+ const [name, setName] = useState('');
1887
+
1888
+ <BoxedList>
1889
+ <EntryRow title="Display name" value={name} onValueChange={setName} />
1890
+ <EntryRow
1891
+ title="Email"
1892
+ value={email}
1893
+ onValueChange={setEmail}
1894
+ keyboardType="email-address"
1895
+ leading={<Icon icon={MailRead} />}
1896
+ trailing={<IconButton icon={Delete} label="Clear" onPress={() => setEmail('')} />}
1897
+ />
1898
+ </BoxedList>
1899
+ ```
1900
+
1901
+ Row with an inline text entry field — mirrors `AdwEntryRow` and
1902
+ `@gnome-ui/react`'s own `EntryRow`. The `title` rises above the input as a
1903
+ small label once the field is focused or has content, and stands in for the
1904
+ placeholder until then. Use it inside a `BoxedList` for settings that take
1905
+ free-form text. Controlled (`value`) and uncontrolled (`defaultValue`) modes
1906
+ both work, and every remaining `TextInput` prop passes through.
1907
+
1908
+ The float is one JS-driven `Animated.Value` (`useNativeDriver: false`):
1909
+ `fontSize` is part of the transition and can't be native-driven, and mixing a
1910
+ native with a JS value on one component throws — the same trade-off
1911
+ `Expander` and `InlineViewSwitcher` already accepted. `useReducedMotion()`
1912
+ snaps between the two states instead.
1913
+
1914
+ **The label's travel is measured, not hardcoded.** The web expresses the
1915
+ resting position as `top: 50%; transform: translateY(-50%)` and the floated
1916
+ one as `top: 6px`, but RN can't interpolate between a percentage and a fixed
1917
+ offset — so the field reports its own height through `onLayout` and the
1918
+ distance is derived from it, which also keeps the label centred if you make
1919
+ the row taller than the 56 dp minimum.
1920
+
1921
+ The `:focus` inset ring is dropped rather than approximated. `TextField`'s
1922
+ own precedent — recolor the border on focus — doesn't transfer, because an
1923
+ `EntryRow` has no border of its own: it's a row inside a `BoxedList`, and
1924
+ adding one would shift the list's geometry. On a touch device the state is
1925
+ already unmistakable: the label floats up, the text fades in, and the
1926
+ keyboard opens.
1927
+
1928
+ Two deliberate divergences from the web version. The visible label is hidden
1929
+ from assistive tech and the `title` becomes the input's `accessibilityLabel`
1930
+ — RN has no `<label htmlFor>`, so otherwise the label would be announced as
1931
+ loose text next to an unnamed field (pass `accessibilityLabel` to override).
1932
+ And `testID` lands on the row rather than the input, matching every other
1933
+ component in this package; reach the field itself by its accessible name.
1934
+
1935
+ ### PasswordEntryRow
1936
+
1937
+ ```tsx
1938
+ import { PasswordEntryRow } from '@gnome-ui/react-native';
1939
+
1940
+ <BoxedList>
1941
+ <PasswordEntryRow title="Password" value={password} onValueChange={setPassword} />
1942
+
1943
+ {/* Registration and change-password forms */}
1944
+ <PasswordEntryRow
1945
+ title="New password"
1946
+ value={next}
1947
+ onValueChange={setNext}
1948
+ autoComplete="new-password"
1949
+ />
1950
+ </BoxedList>
1951
+ ```
1952
+
1953
+ Password entry row with a built-in reveal/conceal toggle — mirrors
1954
+ `AdwPasswordEntryRow` and `@gnome-ui/react`'s own `PasswordEntryRow`. It's an
1955
+ `EntryRow` that masks its input and always carries a trailing button to show
1956
+ or hide what's been typed, so don't add your own through `trailing` — that
1957
+ slot is for anything that should sit *before* the reveal button.
1958
+
1959
+ `type={revealed ? 'text' : 'password'}` becomes RN's `secureTextEntry`, and
1960
+ `autoComplete` defaults to `"current-password"`, which is what lets password
1961
+ managers and the platform keyboard offer a saved credential.
1962
+
1963
+ The reveal control is the already-shipped `IconButton` rather than a
1964
+ hand-rolled pressable, which costs one visual detail: `IconButton` is
1965
+ circular (it's `Button` at `shape="circular"`) where the web's
1966
+ `.revealButton` is a 32 dp square with a 6 dp radius. A circular flat icon
1967
+ button is the idiomatic touch control and keeps the row consistent with every
1968
+ other icon action here. The CSS's resting `opacity: 0.55` is dropped too — it
1969
+ exists so the button can brighten on hover, and with no hover on a touch
1970
+ device a permanently dimmed control is just harder to see.
1971
+
1972
+ The web needs `e.stopPropagation()` so pressing the button doesn't also
1973
+ trigger the row's focus-the-input click. RN's responder system routes a touch
1974
+ to the innermost pressable, so there's nothing to stop.
1975
+
1976
+ ### ComboRow
1977
+
1978
+ ```tsx
1979
+ import { ComboRow } from '@gnome-ui/react-native';
1980
+
1981
+ <BoxedList>
1982
+ <ComboRow
1983
+ title="Language"
1984
+ subtitle="Used across the whole app"
1985
+ value={language}
1986
+ onValueChange={setLanguage}
1987
+ options={[
1988
+ { value: 'en', label: 'English' },
1989
+ { value: 'es', label: 'Español' },
1990
+ ]}
1991
+ />
1992
+ </BoxedList>
1993
+ ```
1994
+
1995
+ Settings row with an inline combo selector at the trailing edge — mirrors
1996
+ `AdwComboRow` and `@gnome-ui/react`'s own `ComboRow`. Use it inside a
1997
+ `BoxedList` for a setting that picks one of a set of options. Controlled
1998
+ (`value`) and uncontrolled (`defaultValue`) modes both work; the trigger
1999
+ falls back to `"—"` when nothing is selected.
2000
+
2001
+ **This is a composition of `ActionRow` + `Dropdown`, where the web version
2002
+ hand-rolls its own listbox inline** — around 200 lines re-implementing the
2003
+ trigger, the flip-up placement, outside-click dismissal, roving
2004
+ `aria-activedescendant` and the whole keyboard layer, none of which is
2005
+ meaningfully different from that package's own `Dropdown`. Nothing forced the
2006
+ duplication visually either: `.row` is `ActionRow`'s exact metrics and
2007
+ `.trigger` is `Dropdown`'s exact trigger. Composing the two already-shipped
2008
+ components means the flip-to-fit placement, the tap-outside dismissal and the
2009
+ `Modal`-based list all come along for free rather than being rebuilt.
2010
+
2011
+ `Dropdown` is controlled-only, so the uncontrolled state lives in `ComboRow`
2012
+ — same behaviour as the web version, one level up. The keyboard layer drops
2013
+ as it does everywhere else here.
2014
+
2015
+ ### ColorPicker / ColorSwatch
2016
+
2017
+ ```tsx
2018
+ import { ColorPicker, ColorSwatch, GNOME_PALETTE } from '@gnome-ui/react-native';
2019
+
2020
+ const [color, setColor] = useState('#3584e4');
2021
+
2022
+ <ColorPicker value={color} onChange={setColor} />
2023
+
2024
+ // Custom colors, with your own picker behind the "+"
2025
+ <ColorPicker
2026
+ value={color}
2027
+ onChange={setColor}
2028
+ allowCustom
2029
+ onRequestCustom={() => setPickerOpen(true)}
2030
+ />
2031
+ ```
2032
+
2033
+ Color palette picker following the Adwaita `GtkColorButton` + swatch pattern
2034
+ — mirrors `@gnome-ui/react`'s own `ColorPicker`. Renders a wrapping row of
2035
+ circular `ColorSwatch` items backed by a radio group, defaulting to
2036
+ `GNOME_PALETTE` (the 9 Adwaita named colors, the same set `Avatar` uses).
2037
+ `ColorSwatch` is exported for standalone use; sizes are **22 / 30 / 38 dp**.
2038
+
2039
+ **`allowCustom` is the one prop that changes meaning.** On the web it wires a
2040
+ hidden `<input type="color">` and the browser supplies the whole picker UI;
2041
+ RN has no such control, and an HSV picker is a component in its own right
2042
+ rather than a detail of this one. So the prop keeps its *visible* behaviour —
2043
+ the "+" button, and a `value` outside the palette shown as its own selected
2044
+ swatch — while the press is handed to a new **`onRequestCustom`** callback
2045
+ for your app to answer with whatever picker it has. The result round-trips
2046
+ through `value`/`onChange` exactly as before.
2047
+
2048
+ The web's three `box-shadow` rings collapse into real box-model pieces, since
2049
+ RN gives a `View` one border: the resting `inset 0 0 0 1px` hairline becomes
2050
+ `borderWidth: 1`, the selected `inset 0 0 0 2px rgb(255 255 255 / .9)`
2051
+ becomes a 2 dp white border, and the outer `0 0 0 2px var(--swatch-color)`
2052
+ becomes a wrapper painted in the swatch color. That wrapper is **always**
2053
+ rendered with the same 2 dp padding and only changes color: a box-shadow ring
2054
+ costs no layout space on the web while a real padded wrapper does, so
2055
+ reserving it unconditionally is what keeps the row from reflowing as the
2056
+ selection moves.
2057
+
2058
+ `filter: drop-shadow(...)` on the checkmark has no RN counterpart, so the
2059
+ path is drawn twice — a translucent black copy offset 1 dp down, then the
2060
+ white one on top. That's what the filter renders, and it's why it exists:
2061
+ without it the check disappears on the yellow swatch. The checkmark is
2062
+ hand-drawn with `react-native-svg` rather than taken from `@gnome-ui/icons`,
2063
+ mirroring the web version, which hand-draws it too — it's a stroked path, and
2064
+ `Icon`'s palette has no white to give it. The container is a `WrapBox`, and
2065
+ the "+" button's `border: 1.5px dashed` ports directly.
2066
+
2067
+ ### Bin
2068
+
2069
+ ```tsx
2070
+ import { Bin } from '@gnome-ui/react-native';
2071
+
2072
+ <Bin style={{ maxWidth: 480 }}>
2073
+ <ExpensiveChart data={series} />
2074
+ </Bin>
2075
+ ```
2076
+
2077
+ Single-child container with no visual styling — mirrors `AdwBin` and
2078
+ `@gnome-ui/react`'s own `Bin`. A transparent passthrough `View` forwarding
2079
+ every prop (and a ref to the underlying `View`) straight through, useful as
2080
+ a neutral base for custom components that need to apply layout or size
2081
+ constraints without introducing any chrome of their own. A plain RN `View`
2082
+ already has no default visual styling — no background, no border — so
2083
+ unlike the web port there's no CSS reset to strip; this is a pure
2084
+ passthrough.
2085
+
2086
+ ### Blockquote
2087
+
2088
+ ```tsx
2089
+ import { Blockquote } from '@gnome-ui/react-native';
2090
+
2091
+ <Blockquote variant="info" cite="Ada Lovelace, 1842">
2092
+ The Analytical Engine has no pretensions to originate anything.
2093
+ </Blockquote>
2094
+ ```
2095
+
2096
+ Pull-quote with a colored left border, mirroring `@gnome-ui/react`'s
2097
+ `Blockquote`. Five visual variants (`default`/`info`/`warning`/`error`/
2098
+ `success`) share the same severity scale as `Banner`/`Chip`, with an
2099
+ optional leading `icon` and a `cite` attribution line rendered below the
2100
+ quote. The web version's `color-mix(in srgb, <color> 8%/14%, transparent)`
2101
+ tinted backgrounds have no RN equivalent — resolved as an 8-digit
2102
+ `#RRGGBBAA` alpha suffix on the variant's own hex token instead, the same
2103
+ substitution `Chip`'s selected-state tint already established. `<blockquote>`/
2104
+ `<footer>`/`<cite>` have no RN element equivalent, so this renders as a
2105
+ plain `View` with no semantic role.
2106
+
2107
+ ### ButtonRow
2108
+
2109
+ ```tsx
2110
+ import { BoxedList, ButtonRow } from '@gnome-ui/react-native';
2111
+
2112
+ <BoxedList>
2113
+ <ButtonRow title="Save changes" variant="suggested" onPress={handleSave} />
2114
+ <ButtonRow title="Delete account" variant="destructive" onPress={handleDelete} />
2115
+ </BoxedList>
2116
+ ```
2117
+
2118
+ Full-width activatable row styled as a button, for use inside a `BoxedList`
2119
+ — mirrors `AdwButtonRow` and `@gnome-ui/react`'s own `ButtonRow`. Use when
2120
+ an entire list row should trigger a single action with a centered label;
2121
+ prefer `ActionRow` with `interactive` when the row also needs a
2122
+ title/subtitle layout. Rebuilt with `Pressable` rather than ported from the
2123
+ web `<button>` — the same pressed-state-overlay recipe `ActionRow`/`Card`
2124
+ already established (`theme.activeOverlay` stands in for the web's
2125
+ `:hover`/`:active` background transition). The title's color reuses `Text`'s
2126
+ own `TextColor` union (`"accent"`/`"destructive"` resolve to the exact same
2127
+ tokens the source CSS's `suggested`/`destructive` variants reference), and
2128
+ gets `flex: 1` + `textAlign: 'center'` — ported straight from the source
2129
+ CSS's `.title` — so the label stays centered even when only one of
2130
+ `leading`/`trailing` is present. The variant color does not propagate to
2131
+ `leading`/`trailing` icons (no `currentColor` equivalent in RN), the same
2132
+ dropped nicety `Chip` already accepted for its own icons.
2133
+
2134
+ ### Callout
2135
+
2136
+ ```tsx
2137
+ import { Callout } from '@gnome-ui/react-native';
2138
+
2139
+ <Callout variant="tip" dismissible onDismiss={() => setVisible(false)}>
2140
+ Press and hold a row to reveal more actions.
2141
+ </Callout>
2142
+ ```
2143
+
2144
+ Inline, dismissible admonition box for contextual help text within forms
2145
+ and cards — mirrors `@gnome-ui/react`'s `Callout`. Unlike `Banner` (a
2146
+ persistent, edge-to-edge strip at the top of a view) and `Toast` (a
2147
+ temporary notification), `Callout` is a contained, tinted box meant to sit
2148
+ inline alongside the content it annotates. Three variants (`info`/
2149
+ `warning`/`tip`) each pair a tinted background/border with a leading icon;
2150
+ `Icon`'s fixed named palette (`blue`/`yellow`/`green`) stands in for the
2151
+ web's arbitrary `color-mix()` icon tint — `blue3`/`green4` happen to match
2152
+ `accentBgColor`/`successBgColor` exactly in every theme this package
2153
+ ships, so it's not an approximation for `info`/`tip`. `role="note"` ports
2154
+ 1:1 from RN's web-aligned `Role` union (same as `Dialog`/`Tooltip`).
2155
+
2156
+ ### CheckRow
2157
+
2158
+ ```tsx
2159
+ import { BoxedList, CheckRow } from '@gnome-ui/react-native';
2160
+
2161
+ <BoxedList>
2162
+ <CheckRow
2163
+ title="Wi-Fi"
2164
+ subtitle="Home Network"
2165
+ checked={wifi}
2166
+ onCheckedChange={setWifi}
2167
+ />
2168
+ </BoxedList>
2169
+ ```
2170
+
2171
+ Activatable row with an integrated checkbox, for use inside a `BoxedList`
2172
+ — mirrors `@gnome-ui/react`'s `CheckRow`. The entire row is a single
2173
+ pressable; pressing anywhere toggles the checked state. Use for multi-select
2174
+ scenarios; prefer a single `Switch` row for a standalone on/off setting.
2175
+ Supports both controlled (`checked`) and uncontrolled (`defaultChecked`)
2176
+ modes, the same `isControlled`/internal-state-fallback shape already used
2177
+ by `Expander`/`ComboRow`/`Popover`. The checkbox visual reuses `Checkbox`'s
2178
+ exact border/background animation and checkmark-fade-in recipe, but as a
2179
+ plain non-interactive `View` rather than the real `Checkbox` component —
2180
+ nesting one `Pressable` inside another would create two overlapping tap
2181
+ targets. `aria-labelledby` has no RN equivalent, so `accessibilityLabel`
2182
+ combines the title and subtitle instead.
2183
+
2184
+ ### ExpanderRow
2185
+
2186
+ ```tsx
2187
+ import { ActionRow, BoxedList, ExpanderRow } from '@gnome-ui/react-native';
2188
+
2189
+ <BoxedList>
2190
+ <ExpanderRow title="Advanced" subtitle="Proxy, DNS, and MTU settings">
2191
+ <ActionRow title="Proxy" subtitle="Automatic" />
2192
+ <ActionRow title="DNS" subtitle="Automatic" />
2193
+ </ExpanderRow>
2194
+ </BoxedList>
2195
+ ```
2196
+
2197
+ Collapsible `ActionRow` that reveals nested rows on activation — mirrors
2198
+ `@gnome-ui/react`'s `ExpanderRow`. The header row toggles a smooth reveal
2199
+ animation exposing child rows; supports both controlled (`expanded`) and
2200
+ uncontrolled (`defaultExpanded`) modes. The reveal panel reuses the
2201
+ standalone `Expander`'s exact `Animated.View` height-driven recipe almost
2202
+ verbatim — only the chevron differs, using `PanDown` (rotating 0deg →
2203
+ 180deg, matching the source CSS's straight down-arrow) instead of
2204
+ `Expander`'s own `PanEnd` triangle. Nested children get a real `Separator`
2205
+ inserted before each one automatically. Use `trailing` for a value label
2206
+ or a `Switch`; stop event propagation inside interactive trailing widgets
2207
+ so the row's own toggle isn't triggered. Unlike `CheckRow`/`ButtonRow`,
2208
+ there is no `disabled` prop — the source component doesn't expose one.
2209
+
2210
+ ### FieldGroup
2211
+
2212
+ ```tsx
2213
+ import { FieldGroup, RadioButton } from '@gnome-ui/react-native';
2214
+
2215
+ <FieldGroup label="Notification method" helperText="Choose how you want to be notified.">
2216
+ <LabeledRadio label="Email" selected={method === 'email'} onSelect={() => setMethod('email')} />
2217
+ <LabeledRadio label="SMS" selected={method === 'sms'} onSelect={() => setMethod('sms')} />
2218
+ </FieldGroup>
2219
+ ```
2220
+
2221
+ Generic form-field grouping with a shared label, help text, and error
2222
+ message, for arbitrary fields outside a `BoxedList` — mirrors
2223
+ `@gnome-ui/react`'s `FieldGroup`. `PreferencesGroup` is scoped specifically
2224
+ to wrapping settings rows inside a `BoxedList`; use `FieldGroup` for a
2225
+ plain labeled grouping around any set of related form controls (e.g. a
2226
+ `RadioButton` group or several `Checkbox`es sharing one label and error),
2227
+ independent of the settings-page layout. `<fieldset>`/`<legend>` have no
2228
+ RN element equivalent — this renders as a `View` with `role="group"` and a
2229
+ themed `Text` label. The hint/error text reuses `TextField`'s exact
2230
+ `error ?? helperText` recipe. Unlike the web version's native
2231
+ `<fieldset disabled>`, `disabled` here only dims the group visually — RN
2232
+ has no equivalent of a fieldset automatically disabling every descendant
2233
+ control, so each child still needs disabling individually.
2234
+
2235
+ ### MultiSelectDropdown
2236
+
2237
+ ```tsx
2238
+ import { MultiSelectDropdown } from '@gnome-ui/react-native';
2239
+
2240
+ <MultiSelectDropdown
2241
+ options={[
2242
+ { value: 'wifi', label: 'Wi-Fi' },
2243
+ { value: 'bluetooth', label: 'Bluetooth' },
2244
+ ]}
2245
+ value={permissions}
2246
+ onChange={setPermissions}
2247
+ placeholder="Select permissions"
2248
+ />
2249
+ ```
2250
+
2251
+ Checkbox-list variant of `Dropdown` for selecting multiple values from a
2252
+ single trigger — mirrors `@gnome-ui/react`'s `MultiSelectDropdown`. Use it
2253
+ over `Dropdown`/`ComboRow` (single-select only) whenever more than one
2254
+ value can be chosen at once. Toggling an option keeps the panel open, so
2255
+ the user can pick several in a row; close it via a backdrop tap. Reuses
2256
+ `Dropdown`'s `Modal` + backdrop + independently-measured trigger-rect/
2257
+ panel-height positioning almost verbatim, and gives each option row a
2258
+ leading checkbox-square visual instead of `Dropdown`'s single trailing
2259
+ checkmark.
2260
+
2261
+ ### FilterableMultiSelectDropdown
2262
+
2263
+ ```tsx
2264
+ import { FilterableMultiSelectDropdown } from '@gnome-ui/react-native';
2265
+
2266
+ <FilterableMultiSelectDropdown
2267
+ options={languages}
2268
+ value={selected}
2269
+ onChange={setSelected}
2270
+ filterPlaceholder="Search languages…"
2271
+ />
2272
+ ```
2273
+
2274
+ `MultiSelectDropdown` plus a filter field for narrowing long option lists
2275
+ — mirrors `@gnome-ui/react`'s `FilterableMultiSelectDropdown`, an original
2276
+ component specific to that package (not a GNOME HIG port). Opening the
2277
+ panel auto-focuses a filter `TextInput` pinned above the list; typing
2278
+ narrows the options to those whose label or description contains the
2279
+ query (case-insensitive) — filtering only affects what's shown, values
2280
+ selected before a query hides their option stay selected. An empty
2281
+ filtered result shows a centered "No results" message. The web version's
2282
+ filter-field keyboard navigation (↑/↓ roving highlight, Home/End,
2283
+ Enter-to-toggle) has no RN port, the same "no keyboard focus to drive it"
2284
+ reasoning `Dropdown` already established — the `TextInput` and its
2285
+ software keyboard still work natively, only the roving-highlight layer on
2286
+ top is dropped.
2287
+
2288
+ ### PasswordField
2289
+
2290
+ ```tsx
2291
+ import { PasswordField } from '@gnome-ui/react-native';
2292
+
2293
+ <PasswordField
2294
+ label="Password"
2295
+ value={password}
2296
+ onChangeText={setPassword}
2297
+ helperText="At least 8 characters"
2298
+ autoComplete="new-password"
2299
+ />
2300
+ ```
2301
+
2302
+ Single-line password input with a peek toggle to reveal the value as plain
2303
+ text — mirrors `@gnome-ui/react`'s `PasswordField`. `TextField` plus the
2304
+ exact `secureTextEntry`/`IconButton` reveal recipe `PasswordEntryRow`
2305
+ already established, at `size="sm"` (28 dp circular), positioned
2306
+ absolutely at the input's trailing edge and vertically centered — the
2307
+ input gets extra trailing padding so typed text never runs under the
2308
+ button. Use this over `TextField` with a manually-set `secureTextEntry`
2309
+ (which has no reveal affordance). Pass `revealable={false}` to hide the
2310
+ toggle entirely (e.g. for a PIN field).
2311
+
2312
+ ### RangeSlider
2313
+
2314
+ ```tsx
2315
+ import { RangeSlider } from '@gnome-ui/react-native';
2316
+
2317
+ <RangeSlider
2318
+ value={priceRange}
2319
+ onChange={setPriceRange}
2320
+ minLabel="Minimum price"
2321
+ maxLabel="Maximum price"
2322
+ />
2323
+ ```
2324
+
2325
+ Dual-thumb slider for selecting a min/max range — mirrors
2326
+ `@gnome-ui/react`'s `RangeSlider`. Distinct from `Slider` (a single value)
2327
+ — use this for range filters (price, date range, age, etc.) where both
2328
+ bounds are adjustable. Reuses `Slider`'s exact `PanResponder`/`locationX`
2329
+ pixel-positioning technique: a single responder spans the whole track and,
2330
+ on touch-down, picks whichever thumb is nearer — that thumb then stays
2331
+ locked to the gesture for the rest of the drag, even if the touch drifts
2332
+ closer to the other thumb. Touching anywhere on the track immediately
2333
+ jumps the nearest thumb there, so there's no separate "drag the track"
2334
+ affordance to port. `minDistance` keeps the two thumbs from crossing or
2335
+ overlapping. The web's keyboard navigation has no RN port — replaced by
2336
+ two independent `accessibilityRole="adjustable"` elements (one per thumb),
2337
+ the same VoiceOver/TalkBack increment/decrement analog `Slider` already
2338
+ established.
2339
+
2340
+ ### StatusBadge
2341
+
2342
+ ```tsx
2343
+ import { StatusBadge } from '@gnome-ui/react-native';
2344
+
2345
+ <StatusBadge variant="success">published</StatusBadge>
2346
+ <StatusBadge variant="warning">beta</StatusBadge>
2347
+ <StatusBadge variant="new">new</StatusBadge>
2348
+ ```
2349
+
2350
+ Pill-shaped text label for entity status — published, beta, new, etc. —
2351
+ mirrors `@gnome-ui/react`'s `StatusBadge`. Unlike `Badge` (numeric counts,
2352
+ optional anchor/dot mode), this is a simpler sibling designed purely for
2353
+ short human-readable state labels: no anchor positioning, no dot mode, no
2354
+ counter. Six variants (`success`/`warning`/`error`/`new`/`accent`/
2355
+ `neutral`) reuse `Badge`'s exact color-mapping shape, plus a `new` (purple)
2356
+ variant `Badge` doesn't have.
2357
+
2358
+ ### BottomTabBar
2359
+
2360
+ ```tsx
2361
+ import { BottomTabBar } from '@gnome-ui/react-native';
2362
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
2363
+
2364
+ const insets = useSafeAreaInsets();
2365
+
2366
+ <BottomTabBar
2367
+ items={[
2368
+ { value: 'home', label: 'Home', icon: HomeOutline, activeIcon: HomeFilled },
2369
+ { value: 'search', label: 'Search', icon: Search },
2370
+ { value: 'profile', label: 'Profile', icon: Person, badge: true },
2371
+ ]}
2372
+ value={tab}
2373
+ onChange={setTab}
2374
+ bottomInset={insets.bottom}
2375
+ />
2376
+ ```
2377
+
2378
+ Fixed bottom navigation bar — the iOS/Android "tab bar" pattern (Music,
2379
+ Instagram, most system apps): a small, fixed set of top-level
2380
+ destinations, each an icon + label, always visible at the foot of the
2381
+ screen. No GNOME/libadwaita widget mirrors this, so it's an original
2382
+ component for this package, not a port.
2383
+
2384
+ Distinct from the existing `TabBar`/`TabItem` (an in-page, horizontally
2385
+ scrollable content switcher) — that one signals "active" purely via a
2386
+ background pill + bold weight + accent underline, never by tinting the
2387
+ icon/label themselves. A bottom tab bar's whole visual signature is the
2388
+ opposite: the active icon+label *are* the app's accent color, so this is
2389
+ the first consumer of `Icon`'s new `tintColor` prop (an arbitrary-fill
2390
+ override, since `color="blue"` always means a fixed swatch, never
2391
+ whatever accent the app actually configured via `GnomeProvider
2392
+ accentColor`). Pass `activeIcon` for the filled-vs-outline convention iOS/
2393
+ Android system tab bars both use; omit it to reuse `icon` for both states,
2394
+ tinted differently. `badge` reuses the real `Badge` component's `anchor`
2395
+ mode — `true` for a dot, a number for a count (capped at `"99+"`).
2396
+ `bottomInset` lets you thread in your own `useSafeAreaInsets().bottom` —
2397
+ this package takes no dependency on `react-native-safe-area-context`
2398
+ itself.
2399
+
1871
2400
  ## Installation
1872
2401
 
1873
2402
  ```bash
@@ -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,2 @@
1
+ export type { BinProps } from './Bin';
2
+ export { Bin } from './Bin';
@@ -0,0 +1,40 @@
1
+ import { ReactNode } from 'react';
2
+ import { StyleProp, ViewProps, ViewStyle } from 'react-native';
3
+ export type BlockquoteVariant = 'default' | 'info' | 'warning' | 'error' | 'success';
4
+ export interface BlockquoteProps extends Omit<ViewProps, 'style'> {
5
+ /**
6
+ * Visual emphasis style.
7
+ * - `"default"` (default) — neutral left border; use for general quotations.
8
+ * - `"info"` — accent blue; use for tips and noteworthy passages.
9
+ * - `"warning"` — yellow; use for cautionary content.
10
+ * - `"error"` — red; use for critical or deprecated content.
11
+ * - `"success"` — green; use for positive or approved content.
12
+ */
13
+ variant?: BlockquoteVariant;
14
+ /** Optional icon rendered to the left of the quoted text, e.g. an `<Icon>`. */
15
+ icon?: ReactNode;
16
+ /** Attribution rendered below the quote — author name, source title, etc. */
17
+ cite?: ReactNode;
18
+ /** The quoted content. */
19
+ children: ReactNode;
20
+ style?: StyleProp<ViewStyle>;
21
+ }
22
+ /**
23
+ * Styled pull-quote with a colored left border, mirroring `@gnome-ui/react`'s
24
+ * `Blockquote`. Five visual variants share the same severity scale as
25
+ * `Banner`/`Chip`.
26
+ *
27
+ * `<blockquote>`/`<footer>`/`<cite>` have no RN element equivalent — this
28
+ * renders as a plain `View` with no semantic role, the same "no landmark
29
+ * role available" gap `PathBar`'s dropped `<nav>` and `HeaderBar`'s dropped
30
+ * `<header>` already established; the quoted text and citation are plain
31
+ * `Text` reads with no assistive-tech grouping to port.
32
+ *
33
+ * @example
34
+ * <Blockquote variant="info" cite="Ada Lovelace, 1842">
35
+ * The Analytical Engine has no pretensions to originate anything.
36
+ * </Blockquote>
37
+ *
38
+ * @see https://developer.gnome.org/hig/
39
+ */
40
+ export declare const Blockquote: ({ variant, icon, cite, children, style, ...viewProps }: BlockquoteProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,2 @@
1
+ export type { BlockquoteProps, BlockquoteVariant } from './Blockquote';
2
+ export { Blockquote } from './Blockquote';