@gnome-ui/react-native 1.5.0 → 1.7.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 +647 -3
- package/dist/components/AvatarGroup/AvatarGroup.d.ts +47 -0
- package/dist/components/AvatarGroup/index.d.ts +2 -0
- package/dist/components/AvatarRotator/AvatarRotator.d.ts +65 -0
- package/dist/components/AvatarRotator/index.d.ts +2 -0
- package/dist/components/Bin/Bin.d.ts +18 -0
- package/dist/components/Bin/index.d.ts +2 -0
- package/dist/components/Box/Box.d.ts +87 -0
- package/dist/components/Box/index.d.ts +2 -0
- package/dist/components/Clamp/Clamp.d.ts +61 -0
- package/dist/components/Clamp/index.d.ts +2 -0
- package/dist/components/CoachMark/CoachMark.d.ts +92 -0
- package/dist/components/CoachMark/CoachMarkTour.d.ts +54 -0
- package/dist/components/CoachMark/coachMarkUtils.d.ts +42 -0
- package/dist/components/CoachMark/index.d.ts +5 -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/InlineViewSwitcher/InlineViewSwitcher.d.ts +93 -0
- package/dist/components/InlineViewSwitcher/InlineViewSwitcherItem.d.ts +25 -0
- package/dist/components/InlineViewSwitcher/index.d.ts +4 -0
- package/dist/components/InlineViewSwitcher/variants.d.ts +30 -0
- package/dist/components/PasswordEntryRow/PasswordEntryRow.d.ts +45 -0
- package/dist/components/PasswordEntryRow/index.d.ts +2 -0
- package/dist/components/PreferencesGroup/PreferencesGroup.d.ts +52 -0
- package/dist/components/PreferencesGroup/index.d.ts +2 -0
- package/dist/components/StatusPage/StatusPage.d.ts +79 -0
- package/dist/components/StatusPage/index.d.ts +2 -0
- package/dist/components/ToggleGroup/ToggleGroup.d.ts +70 -0
- package/dist/components/ToggleGroup/ToggleGroupItem.d.ts +49 -0
- package/dist/components/ToggleGroup/index.d.ts +4 -0
- package/dist/components/WrapBox/WrapBox.d.ts +67 -0
- package/dist/components/WrapBox/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 +2057 -645
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,12 +17,16 @@ React Native component library following the [GNOME Human Interface Guidelines](
|
|
|
17
17
|
> `PathBar`) fully ported. Tier 4 Feedback: `Spinner`, `ProgressBar`,
|
|
18
18
|
> `Skeleton`, `Toast`/`Toaster`, `Banner`, `Dialog`, `Tooltip`, and
|
|
19
19
|
> `AnimatedIcon` (which brought a new `Icon` component along with it, as its
|
|
20
|
-
> own public component) shipped —
|
|
20
|
+
> own public component) and `StatusPage` shipped — Tier 4 complete. Tier 5
|
|
21
21
|
> Advanced Controls fully ported: `Dropdown`, `Slider`, `SpinButton`,
|
|
22
22
|
> `Avatar`, `Badge`, and `Popover`. Beyond Tier 5, `BottomSheet` (Tier 14)
|
|
23
23
|
> and `Overlay`/`LevelBar`/`Expander`/`Divider`/`Highlight`/`FileTypeIcon`/
|
|
24
|
-
> `SegmentedBar
|
|
25
|
-
> (Tier
|
|
24
|
+
> `SegmentedBar`/`AvatarGroup`/`AvatarRotator`/`CoachMark`/`CoachMarkTour`
|
|
25
|
+
> (Tier 20), `Chip` (Tier 7), `IconButton`/`Drawer` (Tier 8/Tier 20), and
|
|
26
|
+
> `Clamp` (Tier 6), `Box` (Tier 20), `WrapBox`/`ToggleGroup` (Tier 7), and
|
|
27
|
+
> `InlineViewSwitcher` (Tier 8), `PreferencesGroup` (Tier 13), and
|
|
28
|
+
> `EntryRow`/`PasswordEntryRow`/`ComboRow` (Tier 12), `ColorPicker`
|
|
29
|
+
> (Tier 20), and `Bin` (Tier 15) also shipped. Component ports from
|
|
26
30
|
> `@gnome-ui/react` continue tier by tier — see this package's own
|
|
27
31
|
> [ROADMAP.md](./ROADMAP.md) for full
|
|
28
32
|
> per-tier status against all 130 `@gnome-ui/react` components, and the
|
|
@@ -1430,6 +1434,646 @@ no exit keyframes at all, so this follows `Dialog`'s simpler animation
|
|
|
1430
1434
|
shape instead. `backdrop-filter: blur(4px)` has no port (no native blur
|
|
1431
1435
|
dependency in this package).
|
|
1432
1436
|
|
|
1437
|
+
### AvatarGroup
|
|
1438
|
+
|
|
1439
|
+
```tsx
|
|
1440
|
+
import { AvatarGroup } from '@gnome-ui/react-native';
|
|
1441
|
+
|
|
1442
|
+
<AvatarGroup
|
|
1443
|
+
avatars={[{ name: 'Alice Martin' }, { name: 'Bob Smith' }, { name: 'Carol White' }]}
|
|
1444
|
+
max={5}
|
|
1445
|
+
/>
|
|
1446
|
+
```
|
|
1447
|
+
|
|
1448
|
+
Overlapping stack of `Avatar`s with a "+N" overflow indicator. Mirrors
|
|
1449
|
+
`@gnome-ui/react`'s `AvatarGroup`. The web version's separating ring
|
|
1450
|
+
around each overlapping avatar is two layered `box-shadow`s (an inset 1px
|
|
1451
|
+
dark/light border plus an outset 2px window-colored ring) — RN can only
|
|
1452
|
+
give a `View` one border, so this keeps just the outer window-colored
|
|
1453
|
+
ring (overriding `Avatar`'s own subtle 1px ring via its `style` prop),
|
|
1454
|
+
since that's the ring doing the actual "stay visually distinct from the
|
|
1455
|
+
avatar behind you" work. The overflow chip reuses `Avatar`'s own per-size
|
|
1456
|
+
box dimensions so it lines up exactly with the avatars beside it.
|
|
1457
|
+
`role="group"` + an auto-generated `accessibilityLabel` (joined names,
|
|
1458
|
+
plus "and N more" when overflowing) port 1:1 from RN's newer web-aligned
|
|
1459
|
+
`Role` union.
|
|
1460
|
+
|
|
1461
|
+
### AvatarRotator
|
|
1462
|
+
|
|
1463
|
+
```tsx
|
|
1464
|
+
import { AvatarRotator } from '@gnome-ui/react-native';
|
|
1465
|
+
|
|
1466
|
+
<AvatarRotator name="Alice Martin" avatars={[url1, url2, url3]} />
|
|
1467
|
+
```
|
|
1468
|
+
|
|
1469
|
+
Single avatar surface that crossfades through multiple image sources.
|
|
1470
|
+
Mirrors `@gnome-ui/react`'s `AvatarRotator`. Keeps `Avatar` focused on
|
|
1471
|
+
rendering one identity, while this component owns timing, crossfade
|
|
1472
|
+
animation, and pause behavior.
|
|
1473
|
+
|
|
1474
|
+
Each source renders as its own absolutely-positioned `Avatar`, crossfaded
|
|
1475
|
+
with `Animated.timing` (`useNativeDriver: true`) — a `RotatorLayer`
|
|
1476
|
+
sub-component owns each layer's own `Animated.Value` rather than the
|
|
1477
|
+
parent tracking an array of them, the same "each item animates itself"
|
|
1478
|
+
shape `Toast`/`Toaster` already established for independently
|
|
1479
|
+
transitioning list items. `prefers-reduced-motion` stops the rotation
|
|
1480
|
+
outright, not just the fade — ported exactly from the web version's own
|
|
1481
|
+
auto-advance effect, which bails out early on both `isPaused` and reduced
|
|
1482
|
+
motion alike. `pauseOnHover` becomes `pauseOnPress`
|
|
1483
|
+
(`onPressIn`/`onPressOut`) — the same touch substitution `Toast`'s own
|
|
1484
|
+
press-and-hold pause already established, kept as a real toggleable prop
|
|
1485
|
+
here (defaults `true`).
|
|
1486
|
+
|
|
1487
|
+
### CoachMark / CoachMarkTour
|
|
1488
|
+
|
|
1489
|
+
```tsx
|
|
1490
|
+
import { CoachMark, CoachMarkTour } from '@gnome-ui/react-native';
|
|
1491
|
+
|
|
1492
|
+
<CoachMark
|
|
1493
|
+
open={open}
|
|
1494
|
+
targetRef={target}
|
|
1495
|
+
title="Sync your files"
|
|
1496
|
+
description="Press this to keep every device up to date."
|
|
1497
|
+
primaryAction={{ label: 'Got it', onPress: () => setOpen(false) }}
|
|
1498
|
+
onDismiss={() => setOpen(false)}
|
|
1499
|
+
/>
|
|
1500
|
+
|
|
1501
|
+
<CoachMarkTour
|
|
1502
|
+
open={running}
|
|
1503
|
+
steps={[
|
|
1504
|
+
{ targetRef: searchRef, title: 'Search', description: 'Find anything fast.' },
|
|
1505
|
+
{ targetRef: addRef, title: 'Add', description: 'Create a new item here.', placement: 'left' },
|
|
1506
|
+
]}
|
|
1507
|
+
onFinish={() => setRunning(false)}
|
|
1508
|
+
onSkip={() => setRunning(false)}
|
|
1509
|
+
/>
|
|
1510
|
+
```
|
|
1511
|
+
|
|
1512
|
+
Spotlights a target element and anchors a callout bubble (title,
|
|
1513
|
+
description, step counter, actions) beside it, guiding a user to one
|
|
1514
|
+
feature. Compose several with `CoachMarkTour`, or drive one directly with
|
|
1515
|
+
`open`. Mirrors `@gnome-ui/react`'s `CoachMark`/`CoachMarkTour` — not a
|
|
1516
|
+
GNOME HIG widget, a pragmatic feature-discovery pattern.
|
|
1517
|
+
|
|
1518
|
+
Positions with the same two-pass viewport-aware flip as the web version
|
|
1519
|
+
(`coachMarkUtils.ts`, duplicated verbatim — pure math, no DOM), resolved
|
|
1520
|
+
from `targetRef.current?.measureInWindow(...)` and the bubble's own
|
|
1521
|
+
`onLayout` size. **The measurement is deliberately delayed (a real
|
|
1522
|
+
`setTimeout`, not just one `requestAnimationFrame`)** — confirmed
|
|
1523
|
+
on-device that measuring too early catches a stale rect when the target
|
|
1524
|
+
sits below sibling content whose own size isn't final on the first commit
|
|
1525
|
+
(e.g. a multi-line description `Text` above it); a single rAF still
|
|
1526
|
+
landed before the follow-up layout pass accounted for it.
|
|
1527
|
+
|
|
1528
|
+
The spotlight cutout has no CSS `box-shadow: 0 0 0 100vmax` port — that
|
|
1529
|
+
trick paints an opaque scrim everywhere except inside a rounded rect via a
|
|
1530
|
+
huge spread shadow, which RN's real OS shadows can't reproduce. Rebuilt as
|
|
1531
|
+
four plain `View` bands around the padded target rect, plus a separate
|
|
1532
|
+
rounded `accentColor`-bordered ring on top — the whole overlay sits inside
|
|
1533
|
+
one full-screen `Pressable`, so a tap anywhere within it (including
|
|
1534
|
+
visually "in the hole") triggers `dismissOnBackdrop`, matching the web
|
|
1535
|
+
version exactly. `dismissOnBackdrop` has no effect when `spotlight` is
|
|
1536
|
+
`false` — ported faithfully, not fixed: the web source only renders a
|
|
1537
|
+
backdrop element at all when `spotlight` is true. The arrow reuses
|
|
1538
|
+
`Popover`/`Tooltip`'s transparent-border-triangle trick rather than the
|
|
1539
|
+
web CSS's rotated-45°-square, offset along the bubble's edge by
|
|
1540
|
+
`arrowOffset` from the position math (unlike `Tooltip`/`Popover`'s simpler
|
|
1541
|
+
always-centered arrow). No focus trap and no scroll/resize
|
|
1542
|
+
re-positioning, the same established gaps for a transient RN floating
|
|
1543
|
+
element. `CoachMarkTour` is pure state orchestration on top of
|
|
1544
|
+
`CoachMark`, ported verbatim.
|
|
1545
|
+
|
|
1546
|
+
### Clamp
|
|
1547
|
+
|
|
1548
|
+
```tsx
|
|
1549
|
+
import { Clamp } from '@gnome-ui/react-native';
|
|
1550
|
+
|
|
1551
|
+
<Clamp>
|
|
1552
|
+
<BoxedList>{/* … */}</BoxedList>
|
|
1553
|
+
</Clamp>
|
|
1554
|
+
|
|
1555
|
+
<Clamp maximumSize={480} tighteningThreshold={0.9}>
|
|
1556
|
+
<Text>Never wider than 480 dp, and never edge-to-edge below it</Text>
|
|
1557
|
+
</Clamp>
|
|
1558
|
+
```
|
|
1559
|
+
|
|
1560
|
+
Constrains its children to a maximum width while letting them shrink freely
|
|
1561
|
+
— mirrors `@gnome-ui/react`'s `Clamp` and the Adwaita `AdwClamp` widget. Use
|
|
1562
|
+
it on settings pages and forms so content never becomes too wide to read
|
|
1563
|
+
comfortably on a tablet or a landscape phone, while still filling the width
|
|
1564
|
+
on a narrow one. `maximumSize` defaults to **600** (the Adwaita recommended
|
|
1565
|
+
narrow-content width) and is in density-independent pixels, not CSS px.
|
|
1566
|
+
Adds no padding of its own.
|
|
1567
|
+
|
|
1568
|
+
The web version's `margin-inline: auto` centering becomes
|
|
1569
|
+
`alignSelf: 'center'` rather than `marginHorizontal: 'auto'` — RN
|
|
1570
|
+
auto-margin support was left unverified for this Yoga version back when
|
|
1571
|
+
`Drawer` needed the same trick, so this follows `Drawer`'s resolution of
|
|
1572
|
+
using flex alignment instead. The one consequence is that `Clamp` expects a
|
|
1573
|
+
column-direction parent (RN's default): `alignSelf` acts on the cross axis,
|
|
1574
|
+
so inside a `flexDirection: 'row'` parent it would centre vertically. Wrap
|
|
1575
|
+
it in a plain `View` there.
|
|
1576
|
+
|
|
1577
|
+
`tighteningThreshold` is a real percentage width here, unlike in
|
|
1578
|
+
`@gnome-ui/react`, where the prop is declared and documented but never
|
|
1579
|
+
reaches the DOM — implementing it exactly as that package documents it (a
|
|
1580
|
+
fraction of the available width, still capped by `maximumSize`) costs
|
|
1581
|
+
nothing on RN and avoids shipping a dead prop.
|
|
1582
|
+
|
|
1583
|
+
### Box
|
|
1584
|
+
|
|
1585
|
+
```tsx
|
|
1586
|
+
import { Box } from '@gnome-ui/react-native';
|
|
1587
|
+
|
|
1588
|
+
// Vertical section (heading + content)
|
|
1589
|
+
<Box spacing={12}>
|
|
1590
|
+
<Text variant="caption-heading" color="dim">Devices</Text>
|
|
1591
|
+
<BoxedList>{/* … */}</BoxedList>
|
|
1592
|
+
</Box>
|
|
1593
|
+
|
|
1594
|
+
// Horizontal icon + label
|
|
1595
|
+
<Box orientation="horizontal" spacing={6} align="center">
|
|
1596
|
+
<Icon icon={Folder} size="sm" />
|
|
1597
|
+
<Text>Documents</Text>
|
|
1598
|
+
</Box>
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
Fundamental flex layout primitive — the RN equivalent of `GtkBox`, and a 1:1
|
|
1602
|
+
mirror of `@gnome-ui/react`'s own `Box`. Arranges children in a row or
|
|
1603
|
+
column with consistent spacing from the GNOME HIG scale: **3** (tight) ·
|
|
1604
|
+
**6** (standard, the default) · **12** (medium) · **18** (large) · **24**
|
|
1605
|
+
(section) · **32** (loose) · **48** (jumbo), all in dp. `align` defaults to
|
|
1606
|
+
`"stretch"` when vertical and `"center"` when horizontal, `justify` to
|
|
1607
|
+
`"start"`.
|
|
1608
|
+
|
|
1609
|
+
`BoxSpacing` keeps the web package's exact seven values rather than being
|
|
1610
|
+
remapped onto this package's own `theme.space1`–`space6` scale — the two
|
|
1611
|
+
overlap at 6/12/18/24/48 but not at 3 or 32/36, and `BoxSpacing` is a
|
|
1612
|
+
published type consumers may already be importing, so it ports verbatim.
|
|
1613
|
+
|
|
1614
|
+
Two things the web version accepts don't survive the platform. `spacing`
|
|
1615
|
+
and `padding` are numbers only — RN's `gap`/`padding` take dp, not CSS
|
|
1616
|
+
strings like `"1rem"`. And `align`/`justify`, which the web hands straight
|
|
1617
|
+
to CSS, are mapped internally from their bare `start`/`end` keywords onto
|
|
1618
|
+
Yoga's `flex-start`/`flex-end`; the prop values stay the web ones, so the
|
|
1619
|
+
API reads identically across both packages. `display: 'flex'` needs no port
|
|
1620
|
+
at all — every RN `View` is already a flex container.
|
|
1621
|
+
|
|
1622
|
+
### WrapBox
|
|
1623
|
+
|
|
1624
|
+
```tsx
|
|
1625
|
+
import { WrapBox } from '@gnome-ui/react-native';
|
|
1626
|
+
|
|
1627
|
+
// Tag list
|
|
1628
|
+
<WrapBox>
|
|
1629
|
+
{tags.map((tag) => <Chip key={tag} label={tag} />)}
|
|
1630
|
+
</WrapBox>
|
|
1631
|
+
|
|
1632
|
+
// Tight between items, loose between lines
|
|
1633
|
+
<WrapBox childSpacing={6} lineSpacing={18} justify="center">
|
|
1634
|
+
{filters.map((filter) => <Chip key={filter} label={filter} />)}
|
|
1635
|
+
</WrapBox>
|
|
1636
|
+
```
|
|
1637
|
+
|
|
1638
|
+
Flexible wrapping layout container — children flow horizontally and wrap to
|
|
1639
|
+
new lines when they don't fit, like words in a paragraph, without locking
|
|
1640
|
+
them into a grid. Mirrors `AdwWrapBox` (libadwaita 1.7 / GNOME 48) and
|
|
1641
|
+
`@gnome-ui/react`'s own `WrapBox`. Pair with `Chip` for tag lists and filter
|
|
1642
|
+
rows.
|
|
1643
|
+
|
|
1644
|
+
`childSpacing` (default **6**) is the gap between items on a line;
|
|
1645
|
+
`lineSpacing` is the gap between lines and falls back to `childSpacing` when
|
|
1646
|
+
omitted — passing `0` really means zero, not "fall back". `justify` defaults
|
|
1647
|
+
to `"start"` and `align` to `"center"`; `wrapReverse` stacks lines bottom to
|
|
1648
|
+
top.
|
|
1649
|
+
|
|
1650
|
+
The web version ships its values as CSS custom properties consumed by a
|
|
1651
|
+
stylesheet (`--wrapbox-gap`, `--wrapbox-justify`, …) because a CSS module
|
|
1652
|
+
can't take runtime values any other way — RN has no such indirection, so
|
|
1653
|
+
they're written straight onto the style object. `flex-flow: row wrap`
|
|
1654
|
+
becomes `flexDirection: 'row'` + `flexWrap`, and the CSS shorthand
|
|
1655
|
+
`gap: <row> <column>` splits into RN's separate `rowGap`/`columnGap`; the
|
|
1656
|
+
single `gap` property would set both, which is precisely what this component
|
|
1657
|
+
has to be able to avoid. As in `Box`, the spacing props are numbers only
|
|
1658
|
+
(dp, not CSS strings) and `align`/`justify` keep the web's bare `start`/`end`
|
|
1659
|
+
keywords while mapping internally onto Yoga's `flex-start`/`flex-end`.
|
|
1660
|
+
|
|
1661
|
+
`alignContent: 'stretch'` is set explicitly even though neither package
|
|
1662
|
+
exposes an `alignContent` prop: **CSS defaults it to `stretch`, Yoga defaults
|
|
1663
|
+
it to `flex-start`**, so without it `align="stretch"` silently does nothing
|
|
1664
|
+
whenever the children have no cross-size of their own — the line collapses to
|
|
1665
|
+
zero height before `alignItems` gets to stretch anything into it. Caught
|
|
1666
|
+
on-device; it's a no-op in the ordinary case where the container hugs its
|
|
1667
|
+
content rather than having a fixed height.
|
|
1668
|
+
|
|
1669
|
+
### StatusPage
|
|
1670
|
+
|
|
1671
|
+
```tsx
|
|
1672
|
+
import { StatusPage } from '@gnome-ui/react-native';
|
|
1673
|
+
|
|
1674
|
+
<StatusPage
|
|
1675
|
+
icon={StarOutline}
|
|
1676
|
+
title="No favorites yet"
|
|
1677
|
+
description="Packages you star will show up here."
|
|
1678
|
+
>
|
|
1679
|
+
<Button variant="suggested" onPress={onAdd}>Add a package</Button>
|
|
1680
|
+
</StatusPage>
|
|
1681
|
+
|
|
1682
|
+
// For sidebars, popovers, and small panels
|
|
1683
|
+
<StatusPage compact icon={Search} title="No results" />
|
|
1684
|
+
```
|
|
1685
|
+
|
|
1686
|
+
Empty-state / status page following the Adwaita `AdwStatusPage` pattern —
|
|
1687
|
+
mirrors `@gnome-ui/react`'s `StatusPage`. Use it to fill a view with no
|
|
1688
|
+
content yet, an error state, or a completion confirmation. Always explain
|
|
1689
|
+
*why* the view is empty and *what the user can do* about it; don't use it
|
|
1690
|
+
for loading states, where `Spinner` or `ProgressBar` belong instead.
|
|
1691
|
+
`compact` scales padding, icon size, title variant, description
|
|
1692
|
+
variant/measure and both action-area gaps down together.
|
|
1693
|
+
|
|
1694
|
+
It centres its content on both axes, but — exactly as in the web version —
|
|
1695
|
+
the vertical centring only does anything once a parent gives it height: put
|
|
1696
|
+
it in a `flex: 1` container to fill the view.
|
|
1697
|
+
|
|
1698
|
+
The title renders through this package's `Text` at `variant="title-1"`
|
|
1699
|
+
(`"title-4"` when `compact`), so it also picks up `Text`'s automatic
|
|
1700
|
+
`header` accessibility role — a deliberate divergence from the web
|
|
1701
|
+
version's `<p class="title">`. That `<p>` exists because HTML forces you to
|
|
1702
|
+
pick a concrete `h1`–`h6` level for a component that can't know where it
|
|
1703
|
+
sits in the document outline; RN's `header` role carries no level, so the
|
|
1704
|
+
dilemma disappears. On a touch device the rotor is the only structural
|
|
1705
|
+
navigation a screen reader user has, which makes the role worth having.
|
|
1706
|
+
|
|
1707
|
+
`max-width: 36ch` on the description has no RN unit to port to. `ch` is the
|
|
1708
|
+
advance width of "0", ≈ 0.5em in the sans faces Adwaita uses, so the cap is
|
|
1709
|
+
resolved against the description's own font size — 288 dp at body size, 216
|
|
1710
|
+
dp at caption size — keeping the measure font-relative the way the CSS is,
|
|
1711
|
+
rather than freezing one pixel value that `compact` would get wrong. The
|
|
1712
|
+
icon is dimmed by its wrapper's opacity (0.55 light / 0.45 dark, the two
|
|
1713
|
+
values the web's own `prefers-color-scheme` block hardcodes) and hidden
|
|
1714
|
+
from assistive tech with the `accessibilityElementsHidden` +
|
|
1715
|
+
`importantForAccessibility="no"` pair used in place of `aria-hidden`. The
|
|
1716
|
+
action area is a `WrapBox` rather than a hand-rolled row — `.actions` is a
|
|
1717
|
+
centred wrapping flex row with a gap and nothing else.
|
|
1718
|
+
|
|
1719
|
+
### ToggleGroup / ToggleGroupItem
|
|
1720
|
+
|
|
1721
|
+
```tsx
|
|
1722
|
+
import { ToggleGroup, ToggleGroupItem } from '@gnome-ui/react-native';
|
|
1723
|
+
|
|
1724
|
+
const [align, setAlign] = useState('left');
|
|
1725
|
+
|
|
1726
|
+
<ToggleGroup value={align} onValueChange={setAlign} accessibilityLabel="Alignment">
|
|
1727
|
+
<ToggleGroupItem name="left" icon={FormatJustifyLeft} accessibilityLabel="Left" />
|
|
1728
|
+
<ToggleGroupItem name="center" icon={FormatJustifyCenter} accessibilityLabel="Center" />
|
|
1729
|
+
<ToggleGroupItem name="right" icon={FormatJustifyRight} accessibilityLabel="Right" />
|
|
1730
|
+
</ToggleGroup>
|
|
1731
|
+
|
|
1732
|
+
// Items can be icon-only, label-only, or icon + label
|
|
1733
|
+
<ToggleGroupItem name="grid" icon={Applications} label="Grid" />
|
|
1734
|
+
```
|
|
1735
|
+
|
|
1736
|
+
Mutually-exclusive group of toggle buttons for in-place option selection —
|
|
1737
|
+
mirrors `AdwToggleGroup` (libadwaita 1.7 / GNOME 48) and
|
|
1738
|
+
`@gnome-ui/react`'s own `ToggleGroup`. Use it for formatting controls,
|
|
1739
|
+
view-mode selectors and toolbar options, wherever a `ViewSwitcher` would be
|
|
1740
|
+
too heavy or doesn't belong in a `HeaderBar`. For icon-only items always
|
|
1741
|
+
pass an `accessibilityLabel`.
|
|
1742
|
+
|
|
1743
|
+
The context and its `value`/`onValueChange` shape port 1:1 — pure React. The
|
|
1744
|
+
keyboard layer doesn't: the web version owns an `onKeyDown` implementing
|
|
1745
|
+
← / → cycling and Home / End jumps over a roving `tabIndex`, none of which
|
|
1746
|
+
has a touch counterpart, so it drops per this package's standing convention
|
|
1747
|
+
(set by `ViewSwitcher` and `TabBar`). The `radiogroup`/`radio` + `checked`
|
|
1748
|
+
pairing that VoiceOver and TalkBack actually announce carries the semantics
|
|
1749
|
+
instead.
|
|
1750
|
+
|
|
1751
|
+
The group sets `accessibilityRole="radiogroup"` but deliberately **not**
|
|
1752
|
+
`accessible` — on iOS, `accessible` on a container collapses the whole
|
|
1753
|
+
subtree into a single accessibility element, which would make the individual
|
|
1754
|
+
toggles unreachable for VoiceOver. Without it the role still groups on
|
|
1755
|
+
Android while every item stays focusable on its own.
|
|
1756
|
+
|
|
1757
|
+
Three `color-mix(in srgb, accent N%, transparent)` values resolve to 8-digit
|
|
1758
|
+
`#RRGGBBAA` hexes off `theme.accentBgColor` (the `Chip` precedent for the
|
|
1759
|
+
same selected-tint problem), so the tint follows the app's configurable
|
|
1760
|
+
accent color. The CSS paints its active ring as an `inset` box-shadow, which
|
|
1761
|
+
RN has no equivalent for — it becomes a real `borderWidth: 1` that every
|
|
1762
|
+
item carries at all times (transparent when inactive) so selecting one never
|
|
1763
|
+
shifts the row's layout, the substitution `AvatarGroup` already made for its
|
|
1764
|
+
own ring. `box-shadow: var(--gnome-shadow-sm)` on the group is dropped
|
|
1765
|
+
rather than approximated: the theme generator keeps shadow tokens in `raw`
|
|
1766
|
+
only, and `Card` already established that a border carries the same
|
|
1767
|
+
separation here. `:hover` collapses away and `:active` maps to `Pressable`'s
|
|
1768
|
+
`pressed` using `theme.activeOverlay`, whose light/dark values match the
|
|
1769
|
+
CSS's own `:active` colors exactly.
|
|
1770
|
+
|
|
1771
|
+
The icon keeps the default foreground color instead of tracking the active
|
|
1772
|
+
accent text — `Icon` has no `currentColor` equivalent and its `color` prop
|
|
1773
|
+
is a fixed GNOME palette with no `accent` member, which couldn't follow a
|
|
1774
|
+
configurable accent anyway. Same call, same reason, as `Chip`.
|
|
1775
|
+
|
|
1776
|
+
### InlineViewSwitcher / InlineViewSwitcherItem
|
|
1777
|
+
|
|
1778
|
+
```tsx
|
|
1779
|
+
import { InlineViewSwitcher, InlineViewSwitcherItem } from '@gnome-ui/react-native';
|
|
1780
|
+
|
|
1781
|
+
const [view, setView] = useState('grid');
|
|
1782
|
+
|
|
1783
|
+
<InlineViewSwitcher value={view} onValueChange={setView} variant="pill">
|
|
1784
|
+
<InlineViewSwitcherItem name="grid" label="Grid" icon={Applications} />
|
|
1785
|
+
<InlineViewSwitcherItem name="list" label="List" icon={ViewSidebar} />
|
|
1786
|
+
</InlineViewSwitcher>
|
|
1787
|
+
|
|
1788
|
+
// Collapse to a BottomSheet picker when the items stop fitting
|
|
1789
|
+
<InlineViewSwitcher value={view} onValueChange={setView} overflow="menu">
|
|
1790
|
+
{/* … */}
|
|
1791
|
+
</InlineViewSwitcher>
|
|
1792
|
+
```
|
|
1793
|
+
|
|
1794
|
+
Compact inline view switcher for content areas, cards and toolbars —
|
|
1795
|
+
wherever `ViewSwitcher` (header-bar sized) would be too heavy. Mirrors
|
|
1796
|
+
`AdwInlineViewSwitcher` (libadwaita 1.7 / GNOME 48) and `@gnome-ui/react`'s
|
|
1797
|
+
own `InlineViewSwitcher`. Four variants — `default` (card surface + border),
|
|
1798
|
+
`flat` (indicator only), `round` (pill container, solid accent indicator),
|
|
1799
|
+
`pill` (segmented-control look, no accent) — and four overflow strategies:
|
|
1800
|
+
`wrap`, `scroll`, `compact`, `menu`.
|
|
1801
|
+
|
|
1802
|
+
Almost none of the *mechanism* ports, so this is a rebuild rather than a
|
|
1803
|
+
transliteration:
|
|
1804
|
+
|
|
1805
|
+
- **The sliding indicator** is measured, not laid out. The web reads the
|
|
1806
|
+
active button's `offsetLeft`/`offsetWidth`; here each item reports its own
|
|
1807
|
+
`onLayout` up through the context and the indicator animates `translateX` +
|
|
1808
|
+
`width` on **one JS-driven animation** (`useNativeDriver: false`). `width`
|
|
1809
|
+
can't be native-driven and mixing a native with a JS value on one component
|
|
1810
|
+
throws — the trade-off `Expander` already accepted for its animated height.
|
|
1811
|
+
`scaleX` would have been native-driveable but distorts the corner radii the
|
|
1812
|
+
variants are defined by. `useReducedMotion()` snaps it into place instead.
|
|
1813
|
+
- **Overflow detection** replaces `ResizeObserver` + `scrollWidth` vs
|
|
1814
|
+
`clientWidth` with the item measurements already being collected: their
|
|
1815
|
+
summed natural widths (RN leaves `flexShrink` at 0, so an overflowing row
|
|
1816
|
+
still reports each item at full width) against the row's own `onLayout`.
|
|
1817
|
+
The web's `naturalWidthRef` capture and 30 px hysteresis port verbatim —
|
|
1818
|
+
without them, collapsing the labels shrinks the content and immediately
|
|
1819
|
+
re-expands it.
|
|
1820
|
+
- **`overflow="scroll"`** becomes a horizontal `ScrollView` with the
|
|
1821
|
+
scrollbar hidden; `scroll-snap-align: start` has no RN style, but the
|
|
1822
|
+
measured item offsets feed `snapToOffsets`, which reproduces it exactly.
|
|
1823
|
+
- **`overflow="menu"`** reuses the already-shipped `BottomSheet`.
|
|
1824
|
+
|
|
1825
|
+
The ←/→/Home/End keyboard layer drops as everywhere else here, and — as in
|
|
1826
|
+
`ToggleGroup` — the group takes `accessibilityRole="radiogroup"` but
|
|
1827
|
+
deliberately not `accessible`, which on iOS would collapse the items into one
|
|
1828
|
+
unreachable element.
|
|
1829
|
+
|
|
1830
|
+
One divergence is a fix, not a port: the web applies its `.active` class to
|
|
1831
|
+
the menu trigger even though menu mode hides the indicator, which paints
|
|
1832
|
+
`round`'s trigger label in `accent-fg` (#fff) on a plain card — white on
|
|
1833
|
+
white. The RN trigger uses the idle color.
|
|
1834
|
+
|
|
1835
|
+
### PreferencesGroup
|
|
1836
|
+
|
|
1837
|
+
```tsx
|
|
1838
|
+
import { PreferencesGroup } from '@gnome-ui/react-native';
|
|
1839
|
+
|
|
1840
|
+
<PreferencesGroup
|
|
1841
|
+
title="Appearance"
|
|
1842
|
+
description="How the app looks on this device."
|
|
1843
|
+
headerSuffix={<Button variant="flat" onPress={reset}>Reset</Button>}
|
|
1844
|
+
>
|
|
1845
|
+
<BoxedList>{rows}</BoxedList>
|
|
1846
|
+
</PreferencesGroup>
|
|
1847
|
+
```
|
|
1848
|
+
|
|
1849
|
+
Titled section that wraps a `BoxedList` with an optional description —
|
|
1850
|
+
mirrors `AdwPreferencesGroup` and `@gnome-ui/react`'s own
|
|
1851
|
+
`PreferencesGroup`. Use it to group related settings under a named heading.
|
|
1852
|
+
It's purely a layout and labelling wrapper: it doesn't render the
|
|
1853
|
+
`BoxedList` itself, you pass one as `children`. All three header parts are
|
|
1854
|
+
optional; with none of them the header row is omitted entirely.
|
|
1855
|
+
|
|
1856
|
+
The web's empty `.content` wrapper looks like dead markup but is
|
|
1857
|
+
load-bearing, so it's kept. The group is a 12 dp-gap flex column — without
|
|
1858
|
+
that wrapper every child would become a flex item of the group and pick up a
|
|
1859
|
+
12 dp gap between the rows themselves, instead of one gap between the header
|
|
1860
|
+
and the content as a whole.
|
|
1861
|
+
|
|
1862
|
+
The title renders as `Text variant="body"` with an explicit semibold weight
|
|
1863
|
+
rather than `variant="heading"`, which is body-sized but **bold** and on the
|
|
1864
|
+
tighter heading line-height; the CSS `.title` is specifically semibold at the
|
|
1865
|
+
body line-height. It keeps the `header` accessibility role anyway (passed
|
|
1866
|
+
explicitly), since a settings-group heading is exactly the kind of landmark a
|
|
1867
|
+
screen reader rotor should list — the same call `StatusPage` makes for its
|
|
1868
|
+
own title. `min-width: 0` on the header text has no port and needs none: it's
|
|
1869
|
+
the classic CSS flexbox override for a min-content floor Yoga doesn't apply
|
|
1870
|
+
in the first place.
|
|
1871
|
+
|
|
1872
|
+
### EntryRow
|
|
1873
|
+
|
|
1874
|
+
```tsx
|
|
1875
|
+
import { EntryRow } from '@gnome-ui/react-native';
|
|
1876
|
+
|
|
1877
|
+
const [name, setName] = useState('');
|
|
1878
|
+
|
|
1879
|
+
<BoxedList>
|
|
1880
|
+
<EntryRow title="Display name" value={name} onValueChange={setName} />
|
|
1881
|
+
<EntryRow
|
|
1882
|
+
title="Email"
|
|
1883
|
+
value={email}
|
|
1884
|
+
onValueChange={setEmail}
|
|
1885
|
+
keyboardType="email-address"
|
|
1886
|
+
leading={<Icon icon={MailRead} />}
|
|
1887
|
+
trailing={<IconButton icon={Delete} label="Clear" onPress={() => setEmail('')} />}
|
|
1888
|
+
/>
|
|
1889
|
+
</BoxedList>
|
|
1890
|
+
```
|
|
1891
|
+
|
|
1892
|
+
Row with an inline text entry field — mirrors `AdwEntryRow` and
|
|
1893
|
+
`@gnome-ui/react`'s own `EntryRow`. The `title` rises above the input as a
|
|
1894
|
+
small label once the field is focused or has content, and stands in for the
|
|
1895
|
+
placeholder until then. Use it inside a `BoxedList` for settings that take
|
|
1896
|
+
free-form text. Controlled (`value`) and uncontrolled (`defaultValue`) modes
|
|
1897
|
+
both work, and every remaining `TextInput` prop passes through.
|
|
1898
|
+
|
|
1899
|
+
The float is one JS-driven `Animated.Value` (`useNativeDriver: false`):
|
|
1900
|
+
`fontSize` is part of the transition and can't be native-driven, and mixing a
|
|
1901
|
+
native with a JS value on one component throws — the same trade-off
|
|
1902
|
+
`Expander` and `InlineViewSwitcher` already accepted. `useReducedMotion()`
|
|
1903
|
+
snaps between the two states instead.
|
|
1904
|
+
|
|
1905
|
+
**The label's travel is measured, not hardcoded.** The web expresses the
|
|
1906
|
+
resting position as `top: 50%; transform: translateY(-50%)` and the floated
|
|
1907
|
+
one as `top: 6px`, but RN can't interpolate between a percentage and a fixed
|
|
1908
|
+
offset — so the field reports its own height through `onLayout` and the
|
|
1909
|
+
distance is derived from it, which also keeps the label centred if you make
|
|
1910
|
+
the row taller than the 56 dp minimum.
|
|
1911
|
+
|
|
1912
|
+
The `:focus` inset ring is dropped rather than approximated. `TextField`'s
|
|
1913
|
+
own precedent — recolor the border on focus — doesn't transfer, because an
|
|
1914
|
+
`EntryRow` has no border of its own: it's a row inside a `BoxedList`, and
|
|
1915
|
+
adding one would shift the list's geometry. On a touch device the state is
|
|
1916
|
+
already unmistakable: the label floats up, the text fades in, and the
|
|
1917
|
+
keyboard opens.
|
|
1918
|
+
|
|
1919
|
+
Two deliberate divergences from the web version. The visible label is hidden
|
|
1920
|
+
from assistive tech and the `title` becomes the input's `accessibilityLabel`
|
|
1921
|
+
— RN has no `<label htmlFor>`, so otherwise the label would be announced as
|
|
1922
|
+
loose text next to an unnamed field (pass `accessibilityLabel` to override).
|
|
1923
|
+
And `testID` lands on the row rather than the input, matching every other
|
|
1924
|
+
component in this package; reach the field itself by its accessible name.
|
|
1925
|
+
|
|
1926
|
+
### PasswordEntryRow
|
|
1927
|
+
|
|
1928
|
+
```tsx
|
|
1929
|
+
import { PasswordEntryRow } from '@gnome-ui/react-native';
|
|
1930
|
+
|
|
1931
|
+
<BoxedList>
|
|
1932
|
+
<PasswordEntryRow title="Password" value={password} onValueChange={setPassword} />
|
|
1933
|
+
|
|
1934
|
+
{/* Registration and change-password forms */}
|
|
1935
|
+
<PasswordEntryRow
|
|
1936
|
+
title="New password"
|
|
1937
|
+
value={next}
|
|
1938
|
+
onValueChange={setNext}
|
|
1939
|
+
autoComplete="new-password"
|
|
1940
|
+
/>
|
|
1941
|
+
</BoxedList>
|
|
1942
|
+
```
|
|
1943
|
+
|
|
1944
|
+
Password entry row with a built-in reveal/conceal toggle — mirrors
|
|
1945
|
+
`AdwPasswordEntryRow` and `@gnome-ui/react`'s own `PasswordEntryRow`. It's an
|
|
1946
|
+
`EntryRow` that masks its input and always carries a trailing button to show
|
|
1947
|
+
or hide what's been typed, so don't add your own through `trailing` — that
|
|
1948
|
+
slot is for anything that should sit *before* the reveal button.
|
|
1949
|
+
|
|
1950
|
+
`type={revealed ? 'text' : 'password'}` becomes RN's `secureTextEntry`, and
|
|
1951
|
+
`autoComplete` defaults to `"current-password"`, which is what lets password
|
|
1952
|
+
managers and the platform keyboard offer a saved credential.
|
|
1953
|
+
|
|
1954
|
+
The reveal control is the already-shipped `IconButton` rather than a
|
|
1955
|
+
hand-rolled pressable, which costs one visual detail: `IconButton` is
|
|
1956
|
+
circular (it's `Button` at `shape="circular"`) where the web's
|
|
1957
|
+
`.revealButton` is a 32 dp square with a 6 dp radius. A circular flat icon
|
|
1958
|
+
button is the idiomatic touch control and keeps the row consistent with every
|
|
1959
|
+
other icon action here. The CSS's resting `opacity: 0.55` is dropped too — it
|
|
1960
|
+
exists so the button can brighten on hover, and with no hover on a touch
|
|
1961
|
+
device a permanently dimmed control is just harder to see.
|
|
1962
|
+
|
|
1963
|
+
The web needs `e.stopPropagation()` so pressing the button doesn't also
|
|
1964
|
+
trigger the row's focus-the-input click. RN's responder system routes a touch
|
|
1965
|
+
to the innermost pressable, so there's nothing to stop.
|
|
1966
|
+
|
|
1967
|
+
### ComboRow
|
|
1968
|
+
|
|
1969
|
+
```tsx
|
|
1970
|
+
import { ComboRow } from '@gnome-ui/react-native';
|
|
1971
|
+
|
|
1972
|
+
<BoxedList>
|
|
1973
|
+
<ComboRow
|
|
1974
|
+
title="Language"
|
|
1975
|
+
subtitle="Used across the whole app"
|
|
1976
|
+
value={language}
|
|
1977
|
+
onValueChange={setLanguage}
|
|
1978
|
+
options={[
|
|
1979
|
+
{ value: 'en', label: 'English' },
|
|
1980
|
+
{ value: 'es', label: 'Español' },
|
|
1981
|
+
]}
|
|
1982
|
+
/>
|
|
1983
|
+
</BoxedList>
|
|
1984
|
+
```
|
|
1985
|
+
|
|
1986
|
+
Settings row with an inline combo selector at the trailing edge — mirrors
|
|
1987
|
+
`AdwComboRow` and `@gnome-ui/react`'s own `ComboRow`. Use it inside a
|
|
1988
|
+
`BoxedList` for a setting that picks one of a set of options. Controlled
|
|
1989
|
+
(`value`) and uncontrolled (`defaultValue`) modes both work; the trigger
|
|
1990
|
+
falls back to `"—"` when nothing is selected.
|
|
1991
|
+
|
|
1992
|
+
**This is a composition of `ActionRow` + `Dropdown`, where the web version
|
|
1993
|
+
hand-rolls its own listbox inline** — around 200 lines re-implementing the
|
|
1994
|
+
trigger, the flip-up placement, outside-click dismissal, roving
|
|
1995
|
+
`aria-activedescendant` and the whole keyboard layer, none of which is
|
|
1996
|
+
meaningfully different from that package's own `Dropdown`. Nothing forced the
|
|
1997
|
+
duplication visually either: `.row` is `ActionRow`'s exact metrics and
|
|
1998
|
+
`.trigger` is `Dropdown`'s exact trigger. Composing the two already-shipped
|
|
1999
|
+
components means the flip-to-fit placement, the tap-outside dismissal and the
|
|
2000
|
+
`Modal`-based list all come along for free rather than being rebuilt.
|
|
2001
|
+
|
|
2002
|
+
`Dropdown` is controlled-only, so the uncontrolled state lives in `ComboRow`
|
|
2003
|
+
— same behaviour as the web version, one level up. The keyboard layer drops
|
|
2004
|
+
as it does everywhere else here.
|
|
2005
|
+
|
|
2006
|
+
### ColorPicker / ColorSwatch
|
|
2007
|
+
|
|
2008
|
+
```tsx
|
|
2009
|
+
import { ColorPicker, ColorSwatch, GNOME_PALETTE } from '@gnome-ui/react-native';
|
|
2010
|
+
|
|
2011
|
+
const [color, setColor] = useState('#3584e4');
|
|
2012
|
+
|
|
2013
|
+
<ColorPicker value={color} onChange={setColor} />
|
|
2014
|
+
|
|
2015
|
+
// Custom colors, with your own picker behind the "+"
|
|
2016
|
+
<ColorPicker
|
|
2017
|
+
value={color}
|
|
2018
|
+
onChange={setColor}
|
|
2019
|
+
allowCustom
|
|
2020
|
+
onRequestCustom={() => setPickerOpen(true)}
|
|
2021
|
+
/>
|
|
2022
|
+
```
|
|
2023
|
+
|
|
2024
|
+
Color palette picker following the Adwaita `GtkColorButton` + swatch pattern
|
|
2025
|
+
— mirrors `@gnome-ui/react`'s own `ColorPicker`. Renders a wrapping row of
|
|
2026
|
+
circular `ColorSwatch` items backed by a radio group, defaulting to
|
|
2027
|
+
`GNOME_PALETTE` (the 9 Adwaita named colors, the same set `Avatar` uses).
|
|
2028
|
+
`ColorSwatch` is exported for standalone use; sizes are **22 / 30 / 38 dp**.
|
|
2029
|
+
|
|
2030
|
+
**`allowCustom` is the one prop that changes meaning.** On the web it wires a
|
|
2031
|
+
hidden `<input type="color">` and the browser supplies the whole picker UI;
|
|
2032
|
+
RN has no such control, and an HSV picker is a component in its own right
|
|
2033
|
+
rather than a detail of this one. So the prop keeps its *visible* behaviour —
|
|
2034
|
+
the "+" button, and a `value` outside the palette shown as its own selected
|
|
2035
|
+
swatch — while the press is handed to a new **`onRequestCustom`** callback
|
|
2036
|
+
for your app to answer with whatever picker it has. The result round-trips
|
|
2037
|
+
through `value`/`onChange` exactly as before.
|
|
2038
|
+
|
|
2039
|
+
The web's three `box-shadow` rings collapse into real box-model pieces, since
|
|
2040
|
+
RN gives a `View` one border: the resting `inset 0 0 0 1px` hairline becomes
|
|
2041
|
+
`borderWidth: 1`, the selected `inset 0 0 0 2px rgb(255 255 255 / .9)`
|
|
2042
|
+
becomes a 2 dp white border, and the outer `0 0 0 2px var(--swatch-color)`
|
|
2043
|
+
becomes a wrapper painted in the swatch color. That wrapper is **always**
|
|
2044
|
+
rendered with the same 2 dp padding and only changes color: a box-shadow ring
|
|
2045
|
+
costs no layout space on the web while a real padded wrapper does, so
|
|
2046
|
+
reserving it unconditionally is what keeps the row from reflowing as the
|
|
2047
|
+
selection moves.
|
|
2048
|
+
|
|
2049
|
+
`filter: drop-shadow(...)` on the checkmark has no RN counterpart, so the
|
|
2050
|
+
path is drawn twice — a translucent black copy offset 1 dp down, then the
|
|
2051
|
+
white one on top. That's what the filter renders, and it's why it exists:
|
|
2052
|
+
without it the check disappears on the yellow swatch. The checkmark is
|
|
2053
|
+
hand-drawn with `react-native-svg` rather than taken from `@gnome-ui/icons`,
|
|
2054
|
+
mirroring the web version, which hand-draws it too — it's a stroked path, and
|
|
2055
|
+
`Icon`'s palette has no white to give it. The container is a `WrapBox`, and
|
|
2056
|
+
the "+" button's `border: 1.5px dashed` ports directly.
|
|
2057
|
+
|
|
2058
|
+
### Bin
|
|
2059
|
+
|
|
2060
|
+
```tsx
|
|
2061
|
+
import { Bin } from '@gnome-ui/react-native';
|
|
2062
|
+
|
|
2063
|
+
<Bin style={{ maxWidth: 480 }}>
|
|
2064
|
+
<ExpensiveChart data={series} />
|
|
2065
|
+
</Bin>
|
|
2066
|
+
```
|
|
2067
|
+
|
|
2068
|
+
Single-child container with no visual styling — mirrors `AdwBin` and
|
|
2069
|
+
`@gnome-ui/react`'s own `Bin`. A transparent passthrough `View` forwarding
|
|
2070
|
+
every prop (and a ref to the underlying `View`) straight through, useful as
|
|
2071
|
+
a neutral base for custom components that need to apply layout or size
|
|
2072
|
+
constraints without introducing any chrome of their own. A plain RN `View`
|
|
2073
|
+
already has no default visual styling — no background, no border — so
|
|
2074
|
+
unlike the web port there's no CSS reset to strip; this is a pure
|
|
2075
|
+
passthrough.
|
|
2076
|
+
|
|
1433
2077
|
## Installation
|
|
1434
2078
|
|
|
1435
2079
|
```bash
|