@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.
- package/README.md +533 -4
- package/dist/components/Bin/Bin.d.ts +18 -0
- package/dist/components/Bin/index.d.ts +2 -0
- package/dist/components/Blockquote/Blockquote.d.ts +40 -0
- package/dist/components/Blockquote/index.d.ts +2 -0
- package/dist/components/BottomTabBar/BottomTabBar.d.ts +106 -0
- package/dist/components/BottomTabBar/index.d.ts +2 -0
- package/dist/components/ButtonRow/ButtonRow.d.ts +41 -0
- package/dist/components/ButtonRow/index.d.ts +2 -0
- package/dist/components/Callout/Callout.d.ts +36 -0
- package/dist/components/Callout/index.d.ts +2 -0
- package/dist/components/CheckRow/CheckRow.d.ts +47 -0
- package/dist/components/CheckRow/index.d.ts +2 -0
- package/dist/components/ColorPicker/ColorPicker.d.ts +81 -0
- package/dist/components/ColorPicker/ColorSwatch.d.ts +50 -0
- package/dist/components/ColorPicker/index.d.ts +4 -0
- package/dist/components/ComboRow/ComboRow.d.ts +77 -0
- package/dist/components/ComboRow/index.d.ts +2 -0
- package/dist/components/EntryRow/EntryRow.d.ts +77 -0
- package/dist/components/EntryRow/index.d.ts +2 -0
- package/dist/components/ExpanderRow/ExpanderRow.d.ts +58 -0
- package/dist/components/ExpanderRow/index.d.ts +2 -0
- package/dist/components/FieldGroup/FieldGroup.d.ts +52 -0
- package/dist/components/FieldGroup/index.d.ts +2 -0
- package/dist/components/FilterableMultiSelectDropdown/FilterableMultiSelectDropdown.d.ts +46 -0
- package/dist/components/FilterableMultiSelectDropdown/index.d.ts +2 -0
- package/dist/components/Icon/Icon.d.ts +11 -1
- package/dist/components/MultiSelectDropdown/MultiSelectDropdown.d.ts +55 -0
- package/dist/components/MultiSelectDropdown/index.d.ts +2 -0
- package/dist/components/PasswordEntryRow/PasswordEntryRow.d.ts +45 -0
- package/dist/components/PasswordEntryRow/index.d.ts +2 -0
- package/dist/components/PasswordField/PasswordField.d.ts +48 -0
- package/dist/components/PasswordField/index.d.ts +2 -0
- package/dist/components/RangeSlider/RangeSlider.d.ts +73 -0
- package/dist/components/RangeSlider/index.d.ts +2 -0
- package/dist/components/StatusBadge/StatusBadge.d.ts +47 -0
- package/dist/components/StatusBadge/index.d.ts +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/index.js +2454 -778
- package/dist/index.js.map +1 -1
- 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),
|
|
28
|
-
>
|
|
29
|
-
>
|
|
30
|
-
>
|
|
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,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;
|