@gnome-ui/react-native 1.3.0 → 1.5.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 +463 -4
- package/dist/components/Avatar/Avatar.d.ts +53 -0
- package/dist/components/Avatar/index.d.ts +2 -0
- package/dist/components/Badge/Badge.d.ts +48 -0
- package/dist/components/Badge/index.d.ts +2 -0
- package/dist/components/BottomSheet/BottomSheet.d.ts +82 -0
- package/dist/components/BottomSheet/index.d.ts +2 -0
- package/dist/components/Chip/Chip.d.ts +63 -0
- package/dist/components/Chip/index.d.ts +2 -0
- package/dist/components/Divider/Divider.d.ts +36 -0
- package/dist/components/Divider/index.d.ts +2 -0
- package/dist/components/Drawer/Drawer.d.ts +117 -0
- package/dist/components/Drawer/index.d.ts +2 -0
- package/dist/components/Expander/Expander.d.ts +64 -0
- package/dist/components/Expander/index.d.ts +2 -0
- package/dist/components/FileTypeIcon/FileTypeIcon.d.ts +45 -0
- package/dist/components/FileTypeIcon/fileType.d.ts +8 -0
- package/dist/components/FileTypeIcon/index.d.ts +3 -0
- package/dist/components/Highlight/Highlight.d.ts +42 -0
- package/dist/components/Highlight/index.d.ts +2 -0
- package/dist/components/IconButton/IconButton.d.ts +34 -0
- package/dist/components/IconButton/index.d.ts +2 -0
- package/dist/components/LevelBar/LevelBar.d.ts +72 -0
- package/dist/components/LevelBar/index.d.ts +2 -0
- package/dist/components/Overlay/Overlay.d.ts +41 -0
- package/dist/components/Overlay/index.d.ts +2 -0
- package/dist/components/Popover/Popover.d.ts +91 -0
- package/dist/components/Popover/index.d.ts +2 -0
- package/dist/components/SegmentedBar/SegmentedBar.d.ts +64 -0
- package/dist/components/SegmentedBar/index.d.ts +2 -0
- package/dist/components/SpinButton/SpinButton.d.ts +57 -0
- package/dist/components/SpinButton/index.d.ts +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.js +2089 -662
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,10 +18,16 @@ React Native component library following the [GNOME Human Interface Guidelines](
|
|
|
18
18
|
> `Skeleton`, `Toast`/`Toaster`, `Banner`, `Dialog`, `Tooltip`, and
|
|
19
19
|
> `AnimatedIcon` (which brought a new `Icon` component along with it, as its
|
|
20
20
|
> own public component) shipped — `Status Page` skipped for now. Tier 5
|
|
21
|
-
> Advanced Controls
|
|
22
|
-
> `
|
|
23
|
-
>
|
|
24
|
-
>
|
|
21
|
+
> Advanced Controls fully ported: `Dropdown`, `Slider`, `SpinButton`,
|
|
22
|
+
> `Avatar`, `Badge`, and `Popover`. Beyond Tier 5, `BottomSheet` (Tier 14)
|
|
23
|
+
> and `Overlay`/`LevelBar`/`Expander`/`Divider`/`Highlight`/`FileTypeIcon`/
|
|
24
|
+
> `SegmentedBar` (Tier 20), `Chip` (Tier 7), and `IconButton`/`Drawer`
|
|
25
|
+
> (Tier 8/Tier 20) also shipped. Component ports from
|
|
26
|
+
> `@gnome-ui/react` continue tier by tier — see this package's own
|
|
27
|
+
> [ROADMAP.md](./ROADMAP.md) for full
|
|
28
|
+
> per-tier status against all 130 `@gnome-ui/react` components, and the
|
|
29
|
+
> main [ROADMAP.md](../../ROADMAP.md) Priority 3 for the framework
|
|
30
|
+
> expansion this package belongs to.
|
|
25
31
|
|
|
26
32
|
## How it works
|
|
27
33
|
|
|
@@ -971,6 +977,459 @@ width isn't a known constant: a zero-width `View` with
|
|
|
971
977
|
`Text` child around that point regardless of how wide the label renders,
|
|
972
978
|
with no measurement needed.
|
|
973
979
|
|
|
980
|
+
### SpinButton
|
|
981
|
+
|
|
982
|
+
```tsx
|
|
983
|
+
import { SpinButton } from '@gnome-ui/react-native';
|
|
984
|
+
|
|
985
|
+
<SpinButton value={quantity} onChange={setQuantity} min={0} max={10} accessibilityLabel="Quantity" />;
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
Numeric −/+ stepper following the Adwaita `GtkSpinButton` pattern, mirroring
|
|
989
|
+
`@gnome-ui/react`'s `SpinButton`. The `min`/`max`/`step`/`decimals`/`wrap`/
|
|
990
|
+
`format` clamp-and-format math ports verbatim (pure JS, no DOM involved).
|
|
991
|
+
|
|
992
|
+
The primary interaction is tapping the visible −/+ buttons, same as a
|
|
993
|
+
sighted mouse user on the web version. The web version's keyboard
|
|
994
|
+
interaction (↑/↓ one step, Page Up/Down ten steps, Home/End to bounds) has
|
|
995
|
+
no RN equivalent — a touch-first device has no keyboard to drive it, the
|
|
996
|
+
same reasoning `Slider` already applied. Rather than dropping value
|
|
997
|
+
adjustment accessibility entirely, single-step increment/decrement reuses
|
|
998
|
+
`Slider`'s exact `accessibilityRole="adjustable"` +
|
|
999
|
+
`onAccessibilityAction`/`accessibilityActions` recipe (VoiceOver's
|
|
1000
|
+
swipe-up/down, TalkBack's local-context menu) — the bigger Page Up/Down and
|
|
1001
|
+
Home/End jumps have no equivalent screen-reader gesture on either platform,
|
|
1002
|
+
so those alone are dropped, same as `Slider`. The visible −/+ buttons and
|
|
1003
|
+
value text are hidden from the accessibility tree
|
|
1004
|
+
(`accessibilityElementsHidden`/`importantForAccessibility="no"`, mirroring
|
|
1005
|
+
the web version's `aria-hidden`/`tabIndex={-1}` on both `<button>`s and the
|
|
1006
|
+
value `<span>`) so a screen reader user gets one adjustable stop, not three.
|
|
1007
|
+
|
|
1008
|
+
### Avatar
|
|
1009
|
+
|
|
1010
|
+
```tsx
|
|
1011
|
+
import { Avatar } from '@gnome-ui/react-native';
|
|
1012
|
+
|
|
1013
|
+
<Avatar name="Grace Hopper" size="lg" />
|
|
1014
|
+
<Avatar src="https://example.com/alice.jpg" alt="Alice's profile photo" />;
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
Circular avatar with image or initials fallback, mirroring `@gnome-ui/react`'s
|
|
1018
|
+
`Avatar`. The color-hash and initials-extraction math ports verbatim (pure
|
|
1019
|
+
JS, no DOM involved).
|
|
1020
|
+
|
|
1021
|
+
The outer container carries `role="img"` + `accessibilityLabel` — RN's newer
|
|
1022
|
+
web-aligned `Role` union has an `"img"` value, a direct 1:1 port of the web
|
|
1023
|
+
version's `role="img"`, no substitution needed (same as `ProgressBar`'s
|
|
1024
|
+
`role="progressbar"`). The image/initials underneath are hidden from the
|
|
1025
|
+
accessibility tree, mirroring the web version's `aria-hidden` on both, so a
|
|
1026
|
+
screen reader gets one stop, not two — same reasoning as `SpinButton`'s
|
|
1027
|
+
hidden −/+ buttons.
|
|
1028
|
+
|
|
1029
|
+
The web CSS's `box-shadow: inset 0 0 0 1px …` ring becomes a real 1px
|
|
1030
|
+
`borderWidth`/`borderColor` here (RN has no inset shadow) — the same
|
|
1031
|
+
substitution `Slider`'s thumb border already used for a ring effect.
|
|
1032
|
+
|
|
1033
|
+
### Badge
|
|
1034
|
+
|
|
1035
|
+
```tsx
|
|
1036
|
+
import { Avatar, Badge } from '@gnome-ui/react-native';
|
|
1037
|
+
|
|
1038
|
+
<Badge variant="error" anchor={<Avatar name="Alice Bob" />}>3</Badge>
|
|
1039
|
+
<Badge dot variant="success" />;
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
Counter or status indicator, optionally overlaid on another element,
|
|
1043
|
+
mirroring `@gnome-ui/react`'s `Badge`. `children` renders as a themed `Text`
|
|
1044
|
+
label when it's a string or number (the common case — counts and short
|
|
1045
|
+
text); any other node renders as-is, the same convention `Button`'s
|
|
1046
|
+
`children` already established.
|
|
1047
|
+
|
|
1048
|
+
The web CSS's `box-shadow: 0 0 0 2px var(--gnome-window-bg-color)` ring
|
|
1049
|
+
(always present, separating the badge from whatever's behind it) has no RN
|
|
1050
|
+
equivalent that avoids affecting layout — RN's `border*` shrinks the content
|
|
1051
|
+
box instead of drawing outside it. Reproduced instead with an outer wrapping
|
|
1052
|
+
`View` (2px padding, `theme.windowBgColor` background, pill radius) around
|
|
1053
|
+
the actual colored badge, so the ring appears to spread outward exactly like
|
|
1054
|
+
the web version's non-blurred shadow, without eating into the badge's own
|
|
1055
|
+
text padding.
|
|
1056
|
+
|
|
1057
|
+
### Popover
|
|
1058
|
+
|
|
1059
|
+
```tsx
|
|
1060
|
+
import { Button, Popover, Text } from '@gnome-ui/react-native';
|
|
1061
|
+
|
|
1062
|
+
<Popover content={<Text>Rich content here</Text>}>
|
|
1063
|
+
<Button>Open</Button>
|
|
1064
|
+
</Popover>;
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
Floating panel anchored to a trigger element, following the Adwaita
|
|
1068
|
+
`GtkPopover` pattern, mirroring `@gnome-ui/react`'s `Popover`. Unlike
|
|
1069
|
+
`Tooltip`, it can hold rich interactive content (buttons, links, forms).
|
|
1070
|
+
|
|
1071
|
+
Reuses this package's own established pieces rather than re-deriving them:
|
|
1072
|
+
`Tooltip`'s `cloneElement`-onto-an-arbitrary-trigger architecture and
|
|
1073
|
+
4-placement fallback-cascade positioning (no arrow-offset-shift-when-clamped
|
|
1074
|
+
— same simplification `Tooltip` already accepted), and `Dropdown`'s
|
|
1075
|
+
toggle-on-press + full-screen backdrop `Pressable` that closes on an outside
|
|
1076
|
+
tap plus reduced-motion fade-in.
|
|
1077
|
+
|
|
1078
|
+
**Deliberate divergence from `Dropdown`'s backdrop structure**: `Dropdown`
|
|
1079
|
+
nests its panel directly inside the backdrop `Pressable` and gets away with
|
|
1080
|
+
it because almost every pixel of its panel is itself a `Pressable` option
|
|
1081
|
+
row, which claims the touch responder before it can bubble to the backdrop.
|
|
1082
|
+
A popover's `content` is arbitrary — likely to have inert padding/whitespace
|
|
1083
|
+
with no `Pressable` of its own — so nesting the same way would let a tap on
|
|
1084
|
+
inert panel space fall through to the backdrop and close the popover, unlike
|
|
1085
|
+
the web version's `.contains()` check (which never closes on *any* tap
|
|
1086
|
+
inside the panel). Fixed with `onStartShouldSetResponder={() => true}` on
|
|
1087
|
+
the panel itself: it claims the touch responder for any touch RN's
|
|
1088
|
+
negotiation hasn't already given to a deeper `Pressable` inside `content`,
|
|
1089
|
+
without making the panel itself behave like a button.
|
|
1090
|
+
|
|
1091
|
+
`BackHandler`'s `hardwareBackPress` (wired the same way `Dialog` already
|
|
1092
|
+
does) is the Android analog of the web version's document-level Escape
|
|
1093
|
+
listener. Focus-trapping and focus-restore-on-close have no port — no DOM
|
|
1094
|
+
`document.activeElement`/`querySelector` equivalent exists in RN, the same
|
|
1095
|
+
gap already present in `Dialog`/`Tooltip`/`Dropdown`.
|
|
1096
|
+
|
|
1097
|
+
The web version's rotated-square-with-matching-background arrow is replaced
|
|
1098
|
+
with `Tooltip`'s simpler transparent-border-triangle technique — the same
|
|
1099
|
+
visual affordance, a much simpler RN-native primitive.
|
|
1100
|
+
|
|
1101
|
+
### BottomSheet
|
|
1102
|
+
|
|
1103
|
+
```tsx
|
|
1104
|
+
import { BottomSheet, Button } from '@gnome-ui/react-native';
|
|
1105
|
+
|
|
1106
|
+
<Button onPress={() => setOpen(true)}>Open</Button>
|
|
1107
|
+
<BottomSheet open={open} title="Options" onClose={() => setOpen(false)}>
|
|
1108
|
+
<Text>Rich content here</Text>
|
|
1109
|
+
</BottomSheet>;
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
Slide-up panel that overlays content from the bottom edge, mirroring
|
|
1113
|
+
`AdwBottomSheet` (libadwaita 1.6+) and `@gnome-ui/react`'s `BottomSheet`.
|
|
1114
|
+
Reuses `Dialog`'s backdrop-opacity-on-an-`AnimatedPressable` +
|
|
1115
|
+
no-op-`Pressable`-around-the-card recipe, and `BackHandler`'s
|
|
1116
|
+
`hardwareBackPress` as the Android analog of the web version's Escape
|
|
1117
|
+
listener.
|
|
1118
|
+
|
|
1119
|
+
**Real drag-to-dismiss**, not a fixed-panel simplification: `PanResponder`
|
|
1120
|
+
(the same core API `Slider` already proved handles a threshold gesture)
|
|
1121
|
+
drives a single `Animated.Value` shared with the entrance/exit animation —
|
|
1122
|
+
dragging the handle bar past 150 px (same constant as the web version)
|
|
1123
|
+
requests a close; releasing short of that springs back to `0`. A real
|
|
1124
|
+
slide-up needs the sheet's own height first (RN's `transform` has no
|
|
1125
|
+
percentage-of-self units, the same `Slider`/`Avatar` pitfall) — the sheet
|
|
1126
|
+
renders once off-screen, measured via `onLayout`, before animating in.
|
|
1127
|
+
|
|
1128
|
+
**A real, timed exit animation, unlike `Dialog`**: `Dialog`'s web source has
|
|
1129
|
+
no exit keyframes at all, but `BottomSheet`'s does — ported with a local
|
|
1130
|
+
`visible` state that lags one animation behind the `open` prop, flipping to
|
|
1131
|
+
`false` only in the exit `Animated.timing`'s own completion callback.
|
|
1132
|
+
|
|
1133
|
+
The web version's `backdrop-filter: blur(4px)` has no port (no native blur
|
|
1134
|
+
view dependency, same reasoning that dropped `Sidebar`'s blurred variant),
|
|
1135
|
+
and `useBodyScrollLock` needs no RN equivalent (`Modal` already blocks all
|
|
1136
|
+
background interaction). `children`, when a plain string, is wrapped in
|
|
1137
|
+
`Text` before rendering — RN throws if a raw string is a `View`'s child,
|
|
1138
|
+
unlike the web version's plain `<div>{children}</div>`.
|
|
1139
|
+
|
|
1140
|
+
### Overlay
|
|
1141
|
+
|
|
1142
|
+
```tsx
|
|
1143
|
+
import { Overlay, Button } from '@gnome-ui/react-native';
|
|
1144
|
+
|
|
1145
|
+
<Button onPress={() => setOpen(true)}>Open</Button>
|
|
1146
|
+
<Overlay open={open} onDismiss={() => setOpen(false)}>
|
|
1147
|
+
<YourOwnCard />
|
|
1148
|
+
</Overlay>;
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
Standalone backdrop/scrim layer with a fade transition and
|
|
1152
|
+
press-to-dismiss — the shared building block behind `Dialog`, `Dropdown`,
|
|
1153
|
+
`Popover`, and `BottomSheet`'s own backdrops, extracted here for building
|
|
1154
|
+
custom overlay UI, mirroring `@gnome-ui/react`'s `Overlay`.
|
|
1155
|
+
|
|
1156
|
+
Deliberately minimal, same as the web version: no focus trap, no
|
|
1157
|
+
`BackHandler`/Escape handling, no `role` — use `Dialog`/`Popover`/
|
|
1158
|
+
`BottomSheet` directly when you need those. Reuses `Dialog`'s exact
|
|
1159
|
+
backdrop recipe (`AnimatedPressable` + a no-op `Pressable` wrapping
|
|
1160
|
+
`children`, so a tap on your own content never bubbles to the backdrop and
|
|
1161
|
+
dismisses it) and `BottomSheet`'s real, timed exit animation technique — a
|
|
1162
|
+
local `visible` state that lags the `open` prop by one `Animated.timing`,
|
|
1163
|
+
flipping to `false` only in that animation's own completion callback.
|
|
1164
|
+
|
|
1165
|
+
**Not retrofitted into `Dialog`/`Dropdown`/`Popover`/`BottomSheet`** — each
|
|
1166
|
+
already ships and is fully tested with its own inline copy of this same
|
|
1167
|
+
backdrop pattern (with small per-component differences: `Popover` claims
|
|
1168
|
+
the touch responder differently than the no-op-`Pressable` wrapper the
|
|
1169
|
+
others use). Extracting `Overlay` as a new standalone primitive was the
|
|
1170
|
+
scoped ask; retrofitting four already-shipped components to share it is a
|
|
1171
|
+
separate, riskier refactor this turn didn't take on.
|
|
1172
|
+
|
|
1173
|
+
### LevelBar
|
|
1174
|
+
|
|
1175
|
+
```tsx
|
|
1176
|
+
import { LevelBar } from '@gnome-ui/react-native';
|
|
1177
|
+
|
|
1178
|
+
<LevelBar value={0.15} low={0.25} high={0.75} accessibilityLabel="Battery" />
|
|
1179
|
+
<LevelBar value={0.6} discrete numBlocks={5} accessibilityLabel="Signal strength" />;
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
Discrete level indicator with color-coded low/high offset zones, mirroring
|
|
1183
|
+
`GtkLevelBar` and `@gnome-ui/react`'s `LevelBar`. Use for a gauge/
|
|
1184
|
+
measurement display (disk usage, battery, signal strength) — not for task
|
|
1185
|
+
progress (`ProgressBar`) or a proportional category breakdown
|
|
1186
|
+
(`SegmentedBar`).
|
|
1187
|
+
|
|
1188
|
+
The continuous fill reuses `ProgressBar`'s exact animation technique rather
|
|
1189
|
+
than animating `width` directly: a fixed `width: '100%'` fill with
|
|
1190
|
+
`transformOrigin: 'left'` and an animated `transform: [{ scaleX }]`, so the
|
|
1191
|
+
whole thing runs on `useNativeDriver: true` — a JS-driven `width` animation
|
|
1192
|
+
schedules its next frame via a plain `setTimeout` that routinely fires
|
|
1193
|
+
after a test's `render()` returns but before unmount, producing a spurious
|
|
1194
|
+
"update not wrapped in act()" warning, the same reasoning `ProgressBar`'s
|
|
1195
|
+
own docstring documents. `useReducedMotion()` mirrors `ProgressBar`'s
|
|
1196
|
+
determinate behavior (duration drops to `0`, an immediate jump).
|
|
1197
|
+
|
|
1198
|
+
Discrete mode's per-block color transition has no port — a value change is
|
|
1199
|
+
a plain, unanimated color swap per block, a decorative nicety rather than a
|
|
1200
|
+
behavior gap. `role="meter"` ports 1:1 from RN's newer web-aligned `Role`
|
|
1201
|
+
union (unlike `AccessibilityRole`, which has no `"meter"` value at all).
|
|
1202
|
+
|
|
1203
|
+
### Expander
|
|
1204
|
+
|
|
1205
|
+
```tsx
|
|
1206
|
+
import { Expander } from '@gnome-ui/react-native';
|
|
1207
|
+
|
|
1208
|
+
<Expander label="Show advanced options">
|
|
1209
|
+
<TextField label="Custom endpoint" />
|
|
1210
|
+
</Expander>
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
Standalone disclosure triangle + collapsible content, mirroring `GtkExpander`
|
|
1214
|
+
and `@gnome-ui/react`'s `Expander`. A bare, unstyled counterpart to
|
|
1215
|
+
`ExpanderRow`; use it outside a settings-row context (e.g. "Show advanced
|
|
1216
|
+
options" in a form, or "Show details" under an error message).
|
|
1217
|
+
|
|
1218
|
+
The web version clips the panel with a CSS grid-height animation and rides
|
|
1219
|
+
the content's `padding-top` on a second, separate transition, so a collapsed
|
|
1220
|
+
expander doesn't reserve blank space for hidden padding. RN has no CSS grid
|
|
1221
|
+
to lean on, so the panel is a single `Animated.View` whose numeric `height`
|
|
1222
|
+
is driven directly (`useNativeDriver: false`, the same accepted trade-off
|
|
1223
|
+
`Checkbox`/`RadioButton`/`Switch`/`AnimatedIcon` already make for
|
|
1224
|
+
non-transform properties) — since the content's own `onLayout` measurement
|
|
1225
|
+
already includes its `paddingTop`, one animated height reproduces the web
|
|
1226
|
+
version's two-transition result. Content stays mounted while collapsed
|
|
1227
|
+
(`accessibilityElementsHidden`/`importantForAccessibility="no"`, the same
|
|
1228
|
+
substitution for the web's `inert` used elsewhere in this package), and on
|
|
1229
|
+
first mount with `defaultExpanded` the panel briefly renders at its natural,
|
|
1230
|
+
unmeasured height so the initial reveal doesn't pop once layout resolves.
|
|
1231
|
+
|
|
1232
|
+
The chevron is `PanEnd` (GNOME's own `pan-end-symbolic` disclosure triangle)
|
|
1233
|
+
rotating 0deg → 90deg on an `Animated.Value`, the same `interpolate`-to-
|
|
1234
|
+
`rotate` recipe `Spinner` uses for its own spin.
|
|
1235
|
+
|
|
1236
|
+
### Divider
|
|
1237
|
+
|
|
1238
|
+
```tsx
|
|
1239
|
+
import { Divider } from '@gnome-ui/react-native';
|
|
1240
|
+
|
|
1241
|
+
<Divider>OR</Divider>
|
|
1242
|
+
<Divider>Continue with</Divider>
|
|
1243
|
+
<Divider />
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
Horizontal rule with an optional centered label — the common auth/login-form
|
|
1247
|
+
pattern ("Sign in" / **OR** / "Continue with Google"). Mirrors
|
|
1248
|
+
`@gnome-ui/react`'s `Divider`. For a bare dividing line with no label, use
|
|
1249
|
+
`Separator` instead — it also supports a vertical orientation, which
|
|
1250
|
+
`Divider` does not.
|
|
1251
|
+
|
|
1252
|
+
`role="separator"` ports 1:1 from RN's newer web-aligned `Role` union (the
|
|
1253
|
+
same one `Avatar`/`Badge`/`LevelBar` already reach for) — unlike
|
|
1254
|
+
`Separator`'s own `accessible={false}`, since a labelled `Divider` ("OR") is
|
|
1255
|
+
exactly the kind of content a screen reader user needs read aloud, rather
|
|
1256
|
+
than a purely decorative line. The label reuses `Text`'s
|
|
1257
|
+
`variant="caption" color="dim"` verbatim, which already resolves to the same
|
|
1258
|
+
font-size/weight/dim-opacity the web version's `.label` class hard-codes.
|
|
1259
|
+
|
|
1260
|
+
### Highlight
|
|
1261
|
+
|
|
1262
|
+
```tsx
|
|
1263
|
+
import { Highlight } from '@gnome-ui/react-native';
|
|
1264
|
+
|
|
1265
|
+
<Highlight text="Preferences for accessibility" query="access" />
|
|
1266
|
+
<Highlight text="The quick brown fox" query={['quick', 'fox']} />
|
|
1267
|
+
```
|
|
1268
|
+
|
|
1269
|
+
Wraps every occurrence of `query` within `text` in a highlighted inline run
|
|
1270
|
+
— mirrors `@gnome-ui/react`'s `Highlight`, which wraps matches in a `<mark>`.
|
|
1271
|
+
Pairs with `SearchBar`'s suggestion list and any filterable list to show
|
|
1272
|
+
users which part of a result matched what they typed.
|
|
1273
|
+
|
|
1274
|
+
The outer span is the themed `Text` component (so callers get the same
|
|
1275
|
+
`variant`/`color` API as everywhere else), but each matched run is a plain,
|
|
1276
|
+
unthemed RN `Text` carrying only the highlight's own overrides — RN's `Text`
|
|
1277
|
+
is the one primitive that inherits ambient `fontSize`/`color`/`fontFamily`
|
|
1278
|
+
from a parent `Text` when nested, the same way the web version's `<mark>`
|
|
1279
|
+
inherits from its surrounding text and only overrides
|
|
1280
|
+
`background-color`/`font-weight`. Reaching for the themed `Text` for the
|
|
1281
|
+
marked runs too would reset them to its own default `variant="body"` sizing
|
|
1282
|
+
instead of inheriting whatever variant the caller chose for the whole
|
|
1283
|
+
string.
|
|
1284
|
+
|
|
1285
|
+
The web version's translucent `color-mix(in srgb, accent 30%, transparent)`
|
|
1286
|
+
background has no RN equivalent (`color-mix` is CSS-only) — resolved to a
|
|
1287
|
+
literal 8-digit `#RRGGBBAA` hex instead, since `accentBgColor` is always a
|
|
1288
|
+
plain 6-digit hex across all four theme variants. `border-radius` on the
|
|
1289
|
+
`<mark>` has no reliable port either: RN only paints `backgroundColor` on an
|
|
1290
|
+
inline (nested) `Text` run, not `borderRadius` — a decorative nicety
|
|
1291
|
+
dropped, not a behavior gap. `prefers-contrast: more`'s solid-background/
|
|
1292
|
+
white-text swap ports via `useResolvedContrast()`, the same hook `Button`
|
|
1293
|
+
already uses for its own high-contrast branching.
|
|
1294
|
+
|
|
1295
|
+
### FileTypeIcon
|
|
1296
|
+
|
|
1297
|
+
```tsx
|
|
1298
|
+
import { FileTypeIcon } from '@gnome-ui/react-native';
|
|
1299
|
+
|
|
1300
|
+
<FileTypeIcon name="report.pdf" />
|
|
1301
|
+
<FileTypeIcon mimeType="image/png" />
|
|
1302
|
+
<FileTypeIcon name="cover.jpg" thumbnail={thumbnailUrl} />
|
|
1303
|
+
<FileTypeIcon isFolder />
|
|
1304
|
+
```
|
|
1305
|
+
|
|
1306
|
+
Small icon — optionally a thumbnail — resolved from a file's MIME type or
|
|
1307
|
+
name extension. Useful for file-manager-style listings. Mirrors
|
|
1308
|
+
`@gnome-ui/react`'s `FileTypeIcon`, falling back to the generic file icon
|
|
1309
|
+
(freedesktop's `text-x-generic`) when the type can't be resolved.
|
|
1310
|
+
|
|
1311
|
+
`fileType.ts`'s category-resolution logic (MIME type / extension → one of
|
|
1312
|
+
13 categories, plus the freedesktop icon and generated label per category)
|
|
1313
|
+
is pure, DOM-free TS — duplicated verbatim from `@gnome-ui/react` rather
|
|
1314
|
+
than imported cross-package, the same `Icon.tsx` precedent already
|
|
1315
|
+
established for logic that isn't worth a shared package for one file's
|
|
1316
|
+
worth of code. `role="img"` + `accessibilityLabel` ports 1:1, and the
|
|
1317
|
+
thumbnail reuses `Avatar`'s own `Image`/`resizeMode="cover"` recipe, sized
|
|
1318
|
+
from `Icon`'s own size map so swapping between the resolved icon and a
|
|
1319
|
+
thumbnail never shifts layout.
|
|
1320
|
+
|
|
1321
|
+
### Chip
|
|
1322
|
+
|
|
1323
|
+
```tsx
|
|
1324
|
+
import { Chip } from '@gnome-ui/react-native';
|
|
1325
|
+
|
|
1326
|
+
<Chip label="React" />
|
|
1327
|
+
<Chip label="React" onRemove={() => {}} />
|
|
1328
|
+
<Chip label="React" selectable selected={selected} onToggle={() => setSelected((s) => !s)} />
|
|
1329
|
+
```
|
|
1330
|
+
|
|
1331
|
+
Compact pill-shaped label for tags, filters, and selection states. Mirrors
|
|
1332
|
+
`@gnome-ui/react`'s `Chip`. Three usage modes: **static** (just a visual
|
|
1333
|
+
label), **removable** (add `onRemove` for a × button), and **selectable**
|
|
1334
|
+
(add `selectable` + `selected` + `onToggle` for toggle behavior — same
|
|
1335
|
+
`isInteractive = selectable && !onRemove` precedence as the web version,
|
|
1336
|
+
so passing both renders the remove button, not a toggle). Pair with
|
|
1337
|
+
`WrapBox` for multi-chip layouts.
|
|
1338
|
+
|
|
1339
|
+
The selected background/border tint
|
|
1340
|
+
(`color-mix(in srgb, accent 15%/50%, transparent)`) resolves to a literal
|
|
1341
|
+
8-digit `#RRGGBBAA` hex, the same `Highlight` precedent. The web version's
|
|
1342
|
+
`:hover`/`:active` background transitions collapse into a single
|
|
1343
|
+
pressed-state overlay tinted by `theme.activeOverlay` (the same
|
|
1344
|
+
`ActionRow`/`Card` recipe), since touch has no hover. The leading icon and
|
|
1345
|
+
remove (×) icon stay in the default foreground color rather than tracking
|
|
1346
|
+
the selected accent text (`color: inherit` on the web) — RN's `Icon` has
|
|
1347
|
+
no `currentColor` equivalent and only accepts a fixed named-swatch
|
|
1348
|
+
palette, none of which tracks the app's configurable accent color, so
|
|
1349
|
+
this is a decorative nicety dropped, not a behavior gap.
|
|
1350
|
+
`accessibilityRole="checkbox"` on the selectable form ports 1:1, the same
|
|
1351
|
+
`Checkbox` precedent.
|
|
1352
|
+
|
|
1353
|
+
### SegmentedBar
|
|
1354
|
+
|
|
1355
|
+
```tsx
|
|
1356
|
+
import { SegmentedBar } from '@gnome-ui/react-native';
|
|
1357
|
+
|
|
1358
|
+
<SegmentedBar
|
|
1359
|
+
values={[
|
|
1360
|
+
{ label: 'TypeScript', value: 60, color: '#3178c6' },
|
|
1361
|
+
{ label: 'JavaScript', value: 30, color: '#f7df1e' },
|
|
1362
|
+
{ label: 'CSS', value: 10, color: '#563d7c' },
|
|
1363
|
+
]}
|
|
1364
|
+
/>
|
|
1365
|
+
```
|
|
1366
|
+
|
|
1367
|
+
Horizontal bar split into proportional segments, one per category. Mirrors
|
|
1368
|
+
`@gnome-ui/react`'s `SegmentedBar`. Typical use case: repository language
|
|
1369
|
+
distribution. Values are normalized proportionally when they don't sum to
|
|
1370
|
+
100.
|
|
1371
|
+
|
|
1372
|
+
The web version's hover interaction (dim every segment but the one under
|
|
1373
|
+
the pointer, brighten that one via `filter: brightness()`) is rebuilt for
|
|
1374
|
+
touch rather than dropped: each segment is a `Pressable`, and touching one
|
|
1375
|
+
dims the rest immediately via `onPressIn`/`onPressOut` — deliberately not
|
|
1376
|
+
gated behind `Tooltip`'s own long-press delay, since this feedback is the
|
|
1377
|
+
RN analog of a `Pressable`'s own instant `pressed` state, not the "peek"
|
|
1378
|
+
affordance a tooltip reveal is. Each segment is also wrapped in `Tooltip`
|
|
1379
|
+
(`placement="top"`, `delay={200}`, ported 1:1) for the label/percentage
|
|
1380
|
+
readout — `Tooltip` clones its own handlers onto the child while still
|
|
1381
|
+
calling the child's original ones, so the dim/highlight and the tooltip
|
|
1382
|
+
compose cleanly on the same `Pressable`. `filter: brightness(1.15)` on the
|
|
1383
|
+
actively-touched segment has no RN equivalent — dropped as a decorative
|
|
1384
|
+
nicety, since the touched segment already reads as highlighted by
|
|
1385
|
+
contrast once every other segment dims to 35% opacity.
|
|
1386
|
+
|
|
1387
|
+
### IconButton
|
|
1388
|
+
|
|
1389
|
+
```tsx
|
|
1390
|
+
import { IconButton } from '@gnome-ui/react-native';
|
|
1391
|
+
import { Search } from '@gnome-ui/icons';
|
|
1392
|
+
|
|
1393
|
+
<IconButton icon={Search} label="Search" />
|
|
1394
|
+
<IconButton icon={Search} label="Search" tooltip="Search files" />
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
Icon-only action button composed from `Button`, `Icon`, and optionally
|
|
1398
|
+
`Tooltip` — mirrors `@gnome-ui/react`'s `IconButton`, itself already just a
|
|
1399
|
+
thin composition of those same three pieces. `label` is required since the
|
|
1400
|
+
button has no visible text. Built as a genuine prerequisite for `Drawer`'s
|
|
1401
|
+
`rail`, not scope creep — every piece it composes already existed.
|
|
1402
|
+
|
|
1403
|
+
### Drawer
|
|
1404
|
+
|
|
1405
|
+
```tsx
|
|
1406
|
+
import { Drawer } from '@gnome-ui/react-native';
|
|
1407
|
+
|
|
1408
|
+
<Drawer open={open} title="Details" onClose={() => setOpen(false)}>
|
|
1409
|
+
<Text>Drawer content can be any React node passed as children.</Text>
|
|
1410
|
+
</Drawer>
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
Slide-in panel for supplementary content, anchored to the left or right
|
|
1414
|
+
edge. Mirrors `@gnome-ui/react`'s `Drawer`. Supports a `rail` (an
|
|
1415
|
+
`IconButton` strip on the drawer's inner edge for switching panels without
|
|
1416
|
+
closing it) and nested-drawer width auto-scaling via context — a `Drawer`
|
|
1417
|
+
opened from within another drawer's content automatically renders
|
|
1418
|
+
narrower (`0.85^depth`, floored at 240px), so stacked drawers read as a
|
|
1419
|
+
drill-in hierarchy.
|
|
1420
|
+
|
|
1421
|
+
Floats with a margin on every side and all four corners rounded, matching
|
|
1422
|
+
`@gnome-ui/react`'s own recent CSS update to the same look — positioned
|
|
1423
|
+
within the padded backdrop via `justifyContent` rather than the web CSS's
|
|
1424
|
+
`margin: auto` on the drawer itself, since RN auto-margin support was
|
|
1425
|
+
unverified for this Yoga version (confirmed correct with an on-device
|
|
1426
|
+
debug-color check before trusting it; `BottomSheet` already proves the
|
|
1427
|
+
same `justifyContent: 'flex-end'` mechanism on its own vertical axis).
|
|
1428
|
+
Unlike `BottomSheet`, there's no drag-to-dismiss — the web source defines
|
|
1429
|
+
no exit keyframes at all, so this follows `Dialog`'s simpler animation
|
|
1430
|
+
shape instead. `backdrop-filter: blur(4px)` has no port (no native blur
|
|
1431
|
+
dependency in this package).
|
|
1432
|
+
|
|
974
1433
|
## Installation
|
|
975
1434
|
|
|
976
1435
|
```bash
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
export type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
3
|
+
/**
|
|
4
|
+
* Named color palette for the initials fallback.
|
|
5
|
+
* Mirrors libadwaita's avatar color set.
|
|
6
|
+
*/
|
|
7
|
+
export type AvatarColor = 'blue' | 'green' | 'yellow' | 'orange' | 'red' | 'purple' | 'brown' | 'teal' | 'slate';
|
|
8
|
+
export interface AvatarProps {
|
|
9
|
+
/**
|
|
10
|
+
* Full name used to generate initials and — when `color` is omitted —
|
|
11
|
+
* to deterministically pick a background color.
|
|
12
|
+
*/
|
|
13
|
+
name?: string;
|
|
14
|
+
/** Image URL. When provided the initials fallback is hidden. */
|
|
15
|
+
src?: string;
|
|
16
|
+
/** Accessible label. Defaults to `name`, then `"Avatar"`. */
|
|
17
|
+
alt?: string;
|
|
18
|
+
/** Size of the avatar. Defaults to `"md"`. */
|
|
19
|
+
size?: AvatarSize;
|
|
20
|
+
/**
|
|
21
|
+
* Override the auto-derived background color for the initials fallback.
|
|
22
|
+
* When omitted a color is derived from `name` via a stable hash.
|
|
23
|
+
*/
|
|
24
|
+
color?: AvatarColor;
|
|
25
|
+
style?: StyleProp<ViewStyle>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Circular avatar with image or initials fallback.
|
|
29
|
+
*
|
|
30
|
+
* Follows the Adwaita `AdwAvatar` pattern — deterministic color from name,
|
|
31
|
+
* up to two initials when no image is supplied. Rebuilt with `View`/`Image`/
|
|
32
|
+
* `Text` rather than ported from `@gnome-ui/react`'s DOM-based JSX, but
|
|
33
|
+
* mirrors its prop API and color-hash/initials math (pure JS, ported
|
|
34
|
+
* verbatim).
|
|
35
|
+
*
|
|
36
|
+
* The outer container carries `role="img"` + `accessibilityLabel` — RN's
|
|
37
|
+
* newer web-aligned `Role` union has an `"img"` value, a direct 1:1 port of
|
|
38
|
+
* the web version's `role="img"`, no substitution needed (same as
|
|
39
|
+
* `ProgressBar`'s `role="progressbar"`). The image/initials underneath are
|
|
40
|
+
* hidden from the accessibility tree
|
|
41
|
+
* (`accessibilityElementsHidden`/`importantForAccessibility="no"`, mirroring
|
|
42
|
+
* the web version's `aria-hidden` on both), so a screen reader gets one
|
|
43
|
+
* stop, not two — same reasoning as `SpinButton`'s hidden −/+ buttons.
|
|
44
|
+
*
|
|
45
|
+
* The web CSS's `box-shadow: inset 0 0 0 1px …` ring (drawn on top, doesn't
|
|
46
|
+
* affect layout) becomes a real 1px `borderWidth`/`borderColor` here (RN has
|
|
47
|
+
* no inset shadow) — the same substitution `Slider`'s thumb border already
|
|
48
|
+
* used for a ring effect, at the cost of a negligible 1px content-box inset
|
|
49
|
+
* the web version doesn't have.
|
|
50
|
+
*
|
|
51
|
+
* @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Avatar.html
|
|
52
|
+
*/
|
|
53
|
+
export declare const Avatar: ({ name, src, alt, size, color, style }: AvatarProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export type BadgeVariant = 'accent' | 'success' | 'warning' | 'error' | 'neutral';
|
|
4
|
+
export interface BadgeProps {
|
|
5
|
+
/**
|
|
6
|
+
* Visual style. Defaults to `"accent"`.
|
|
7
|
+
* - `accent` — blue, for counts and highlights.
|
|
8
|
+
* - `success` — green, for positive status.
|
|
9
|
+
* - `warning` — yellow, for cautionary status.
|
|
10
|
+
* - `error` — red, for failures or urgent counts.
|
|
11
|
+
* - `neutral` — gray, for inactive or secondary counts.
|
|
12
|
+
*/
|
|
13
|
+
variant?: BadgeVariant;
|
|
14
|
+
/**
|
|
15
|
+
* When true, renders a small dot with no label — used for unread/online indicators.
|
|
16
|
+
* The `children` are ignored in dot mode.
|
|
17
|
+
*/
|
|
18
|
+
dot?: boolean;
|
|
19
|
+
/** Number or short text to display. Keep to 1–3 characters. String/number render as a themed label; other nodes render as-is. */
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* When provided, the badge is positioned over this child element at the
|
|
23
|
+
* top-right corner.
|
|
24
|
+
*/
|
|
25
|
+
anchor?: ReactNode;
|
|
26
|
+
style?: StyleProp<ViewStyle>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Counter or status indicator, optionally overlaid on another element.
|
|
30
|
+
*
|
|
31
|
+
* Rebuilt with `View`/`Text` rather than ported from `@gnome-ui/react`'s
|
|
32
|
+
* DOM-based JSX, but mirrors its prop API. `children` renders as a themed
|
|
33
|
+
* `Text` label when it's a string or number (the common case — counts and
|
|
34
|
+
* short text); any other node renders as-is, the same convention `Button`'s
|
|
35
|
+
* `children` already established.
|
|
36
|
+
*
|
|
37
|
+
* The web CSS's `box-shadow: 0 0 0 2px var(--gnome-window-bg-color)` ring
|
|
38
|
+
* (always present, separating the badge from whatever's behind it) has no
|
|
39
|
+
* RN equivalent that avoids affecting layout — RN's `border*` shrinks the
|
|
40
|
+
* content box instead of drawing outside it. Reproduced instead with an
|
|
41
|
+
* outer wrapping `View` (2px padding, `theme.windowBgColor` background,
|
|
42
|
+
* pill radius) around the actual colored badge, so the ring appears to
|
|
43
|
+
* spread outward exactly like the web version's non-blurred shadow, without
|
|
44
|
+
* eating into the badge's own text padding.
|
|
45
|
+
*
|
|
46
|
+
* @see https://developer.gnome.org/hig/patterns/feedback/badges.html
|
|
47
|
+
*/
|
|
48
|
+
export declare const Badge: ({ variant, dot, children, anchor, style }: BadgeProps) => import("react/jsx-runtime").JSX.Element;
|