@gjsify/adwaita-react-native 0.44.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 ADDED
@@ -0,0 +1,136 @@
1
+ # @gjsify/adwaita-react-native
2
+
3
+ Adwaita widgets with **one API surface and two implementations**: the real `Adw.*`
4
+ widget on GTK4, React Native primitives on a phone.
5
+
6
+ ```tsx
7
+ import { AdwClamp } from '@gjsify/adwaita-react-native';
8
+
9
+ <AdwClamp maximumSize={400}>{content}</AdwClamp>;
10
+ ```
11
+
12
+ On GTK that is `Adw.Clamp` through [`@gjsify/gtk-host`](../gtk-host). On Android and
13
+ iOS it is a `View` whose width and offset come from
14
+ [`@gjsify/adwaita-core`](../../web/adwaita-core)'s port of
15
+ `adw_clamp_layout_allocate` — libadwaita's own easing curve, not a `maxWidth`
16
+ approximation.
17
+
18
+ ## What this is, honestly
19
+
20
+ **A walking skeleton, not a widget set.** Two widgets — `AdwBin` and `AdwClamp` —
21
+ carrying the whole vertical: both platform halves, the resolution mechanism, the
22
+ manifest declaration, a gate, suites on both sides, and a photograph of the GTK half.
23
+ The remaining widgets are repetition against a boundary that has been measured.
24
+
25
+ **The promise is the Adwaita design language on React Native, not "your app runs".**
26
+ Unlike [`@gjsify/react-native`](../react-native) — the opposite direction, which was
27
+ measured against a real production Expo application — this package has no third-party
28
+ codebase behind it. What it has is a shared arithmetic core and two renderers held to
29
+ the same numbers.
30
+
31
+ ## Install
32
+
33
+ Not on npm yet: this is a new name, and the first publish plus its Trusted Publisher
34
+ bootstrap is a maintainer action deliberately not attempted from CI or a feature branch
35
+ ([the procedure](../../../docs/publishing.md)). Until then it is consumable from a
36
+ workspace checkout.
37
+
38
+ ```sh
39
+ npm install @gjsify/adwaita-react-native
40
+ ```
41
+
42
+ `react` (>= 19.2) is a peer dependency. `react-native` (>= 0.87) is an **optional** peer:
43
+ a GTK-only consumer does not need it installed.
44
+
45
+ ## Widgets
46
+
47
+ | widget | props | notes |
48
+ |---|---|---|
49
+ | `AdwBin` | `children` | One child, no layout of its own |
50
+ | `AdwClamp` | `children`, `maximumSize` (600), `tighteningThreshold` (400) | Constrain a child's width and centre it, on libadwaita's easing curve |
51
+
52
+ Props are libadwaita's own names, camelCased — `maximumSize` is `AdwClamp:maximum-size`,
53
+ not a React Native `maxWidth` — so the property you look up in libadwaita's documentation
54
+ is the property you write. Defaults are libadwaita's.
55
+
56
+ ## How the two halves are selected
57
+
58
+ Through the package's `exports` map and the `react-native` condition:
59
+
60
+ | condition | entry |
61
+ |---|---|
62
+ | `react-native` | `lib/esm/index.native.js` |
63
+ | `default` | `lib/esm/index.gtk.js` |
64
+ | `exports` ignored, `module` read | `lib/esm/index.js` — refuses, by name |
65
+
66
+ **A stock React Native 0.87 application needs no configuration.** `metro-config` enables
67
+ package exports and `@react-native/metro-config` supplies the `react-native` condition.
68
+
69
+ Two things follow that are easy to undo by accident, so both are held by
70
+ `scripts/check-adwaita-rn-platform-split.mjs` rather than by convention — its header
71
+ carries the resolver measurements behind them:
72
+
73
+ - **The condition ORDER is part of the map.** `exports` answers with the first match and
74
+ `default` matches everything, so moving `default` ahead of `react-native` hands the GTK
75
+ build to every phone.
76
+ - **Every barrel names its platform files literally.** Nothing relies on a resolver
77
+ picking a `.native` sibling: that mechanism is real, and right for an *application*,
78
+ but it does not carry a published library whose shipped imports carry a `.js` extension.
79
+
80
+ ### Consuming it on GTK
81
+
82
+ Nothing to configure on the phone. On GTK, point JSX at the host that has a GTK element
83
+ list, so a stray `<div>` is a type error instead of a tag that type-checks and renders
84
+ nothing:
85
+
86
+ ```json
87
+ {
88
+ "compilerOptions": {
89
+ "jsx": "react-jsx",
90
+ "jsxImportSource": "@gjsify/gtk-host/react"
91
+ }
92
+ }
93
+ ```
94
+
95
+ ## What is proven, and what is not
96
+
97
+ | claim | held by |
98
+ |---|---|
99
+ | both halves satisfy one declared surface | `tsc` — a platform module that renames, drops or invents a prop, or changes an arity, is a compile error |
100
+ | the `.native` half type-checks against **real** React Native | the `react-native` package's own types, never a subset |
101
+ | GTK: the widget is the real `Adw.*`, and it **renders** | the live GTK tree plus a GSK rasterisation, because GTK's failure mode is an empty window at exit 0 |
102
+ | React Native: which primitives, which props, which nesting | React's own reconciler over `react-test-renderer` |
103
+ | both halves agree on the number | two frames, one of them ON the easing curve: 1000 points at `maximumSize` 400 gives width 400 at offset 300, and 700 points at the default 600/400 gives **575 at offset 62**, where a `min()` would give 600 |
104
+ | both halves answer the same for a property value GObject cannot store | `normalizeClampSize` from `@gjsify/adwaita-core`, run by **both** halves — the same call `@gjsify/adwaita-web` and `@gjsify/adwaita-nativescript` make. `400.7` &rarr; 400, `NaN` &rarr; the default 600, `-5` &rarr; the range floor 0, asserted on both sides |
105
+
106
+ **Not proven: Yoga, and the device.** A `width` in a style object is an instruction to a
107
+ layout engine that no test here runs. The React Native half is type-checked and
108
+ tree-checked and has never been on a phone. That gap closes with an app on a real device,
109
+ and until then it is a gap, not a formality.
110
+
111
+ ## Named divergences
112
+
113
+ These are places where the two halves cannot be made identical, written down rather
114
+ than smoothed over.
115
+
116
+ - **`AdwClamp` ignores the child's intrinsic minimum on React Native.** libadwaita's
117
+ clamp is a two-pass measure-then-allocate, and a child minimum wider than the clamp
118
+ RAISES all three thresholds — which is how such a child still gets its minimum
119
+ instead of being cut off. React Native has no such pass: `onLayout` reports a size
120
+ after layout and never the child's intrinsic minimum. So `childMin`/`childNat` are
121
+ passed as 0, and a child wider than the clamp is compressed here and is not on GTK.
122
+ - **`AdwClamp`'s `small`/`medium`/`large` size class is not carried.** libadwaita stamps
123
+ it on the child as a style class; React Native has no class system to stamp it into,
124
+ and inventing a styling seam is a decision this slice does not make.
125
+ - **One unclamped frame.** Before the first `onLayout` there is no available width, so
126
+ the child renders full-width for one frame.
127
+ - **A second child is kept on React Native and dropped on GTK, silently.** `Adw.Bin` and
128
+ `Adw.Clamp` are one-child widgets, and gtk-host's `single` child policy fills that slot
129
+ with `set_child`, so a second child EVICTS the first — with no throw, no host error and
130
+ no GLib message. A `View` has no such limit and renders both. Both behaviours are pinned
131
+ by a test on each side, so neither can move alone. Refusing more than one child on both
132
+ halves would close it and is a surface decision this slice does not make.
133
+ - **`Adw.ClampScrollable` has no counterpart and will not get one.** It binds its four
134
+ scroll properties onto its child, which is a GTK adoption concern; on React Native
135
+ scrolling belongs to the `ScrollView`, not to the clamp. This is an asymmetry, not a
136
+ missing widget.
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
@@ -0,0 +1 @@
1
+ import{AdwBin as e}from"./widgets/bin.gtk.js";import{AdwClamp as t}from"./widgets/clamp.gtk.js";export{e as AdwBin,t as AdwClamp};
@@ -0,0 +1 @@
1
+ import{AdwBin as e}from"./widgets/bin.js";import{AdwClamp as t}from"./widgets/clamp.js";export{e as AdwBin,t as AdwClamp};
@@ -0,0 +1 @@
1
+ import{AdwBin as e}from"./widgets/bin.native.js";import{AdwClamp as t}from"./widgets/clamp.native.js";export{e as AdwBin,t as AdwClamp};
File without changes
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";const e=`@gjsify/adwaita-react-native`;function refuseBaseModule(t){throw Error(`${e}: <${t}> was loaded from the BASE module, which has no implementation. The two real ones are selected by the package's \`exports\` map — the \`react-native\` condition picks the React Native build, anything else picks the GTK build. Reaching this module means the tool that resolved ${e} ignored export conditions. For Metro, that is \`unstable_enablePackageExports\` (on by default since metro-config 0.83) plus \`unstable_conditionNames: ['react-native']\` (set by @react-native/metro-config); for anything else, import the platform module directly.`)}export{refuseBaseModule};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{createElement as e}from"react";const t=`RCTView`,View=n=>e(t,n);export{t as RCT_VIEW,View};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{jsx as e}from"@gjsify/gtk-host/react/jsx-runtime";function AdwBin({children:t}){return e(`adw-bin`,{children:t})}export{AdwBin};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{refuseBaseModule as e}from"../refuse.js";function AdwBin(t){return e(`AdwBin`)}export{AdwBin};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{View as e}from"react-native";import{jsx as t}from"react/jsx-runtime";function AdwBin({children:n}){return t(e,{children:n})}export{AdwBin};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{jsx as e}from"@gjsify/gtk-host/react/jsx-runtime";import{ADW_CLAMP_DEFAULTS as t,normalizeClampSize as n}from"@gjsify/adwaita-core";const authoredSize=(e,t)=>e===void 0?void 0:n(e,t);function AdwClamp({children:n,maximumSize:r,tighteningThreshold:i}){return e(`adw-clamp`,{"maximum-size":authoredSize(r,t.maximumSize),"tightening-threshold":authoredSize(i,t.tighteningThreshold),children:n})}export{AdwClamp};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{refuseBaseModule as e}from"../refuse.js";function AdwClamp(t){return e(`AdwClamp`)}export{AdwClamp};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{ADW_CLAMP_DEFAULTS as e,clampAllocate as t,normalizeClampSize as n}from"@gjsify/adwaita-core";import{View as r}from"react-native";import{jsx as i}from"react/jsx-runtime";import{useCallback as a,useState as o}from"react";function AdwClamp({children:s,maximumSize:c,tighteningThreshold:l}){let[u,d]=o(null),f=a(e=>{d(e.nativeEvent.layout.width)},[]),p=u===null?null:t(u,{maximumSize:n(c,e.maximumSize),tighteningThreshold:n(l,e.tighteningThreshold),childMin:0,childNat:0});return i(r,{onLayout:f,style:{alignSelf:`stretch`},children:i(r,{style:p===null?void 0:{width:p.childSize,marginStart:p.offset},children:s})})}export{AdwClamp};
@@ -0,0 +1,3 @@
1
+ export type { AdwBinProps, AdwClampProps, AdwWidgetProps } from './props.js';
2
+ export { AdwBin } from './widgets/bin.js';
3
+ export { AdwClamp } from './widgets/clamp.js';
@@ -0,0 +1,3 @@
1
+ export type { AdwBinProps, AdwClampProps, AdwWidgetProps } from './props.js';
2
+ export { AdwBin } from './widgets/bin.gtk.js';
3
+ export { AdwClamp } from './widgets/clamp.gtk.js';
@@ -0,0 +1,3 @@
1
+ export type { AdwBinProps, AdwClampProps, AdwWidgetProps } from './props.js';
2
+ export { AdwBin } from './widgets/bin.native.js';
3
+ export { AdwClamp } from './widgets/clamp.native.js';
@@ -0,0 +1,71 @@
1
+ import type * as BinBase from './widgets/bin.js';
2
+ import type * as BinGtk from './widgets/bin.gtk.js';
3
+ import type * as BinNative from './widgets/bin.native.js';
4
+ import type * as ClampBase from './widgets/clamp.js';
5
+ import type * as ClampGtk from './widgets/clamp.gtk.js';
6
+ import type * as ClampNative from './widgets/clamp.native.js';
7
+ /**
8
+ * `Assert<false>` is a constraint violation, and that is the whole trick.
9
+ *
10
+ * THE FIRST VERSION OF THIS FILE WAS INERT, and it failed in the way this repository
11
+ * pays for most often: a conditional type resolved to `never` exactly as designed, and
12
+ * the witness that was supposed to reject `never` used an `as` cast, which TypeScript
13
+ * permits from `{}`. Measured — making `AdwBin`'s React Native props require a field
14
+ * the base does not declare left the whole package at exit 0. So the assertion is a
15
+ * CONSTRAINT now: `Assert<T extends true>` cannot be satisfied by `false`, there is no
16
+ * cast in the expression, and the diagnostic names the alias, which names the widget
17
+ * and the platform.
18
+ *
19
+ * Exported: `testing/react-native.spec.ts` holds the double against React Native's own
20
+ * surface with the same trick, and a second copy of a one-line type is a second copy.
21
+ */
22
+ export type Assert<T extends true> = T;
23
+ /**
24
+ * Are `A` and `B` the same type, without a union distributing on the way in?
25
+ *
26
+ * The tuple wrappers are load-bearing: a bare `A extends B` distributes over a union,
27
+ * and the two things this file compares — key sets — are unions.
28
+ */
29
+ export type Identical<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;
30
+ /**
31
+ * Do `A` and `B` accept exactly the same property NAMES?
32
+ *
33
+ * The half of a props contract that structural assignability cannot express when every
34
+ * property is optional. Used here per widget, and by `testing/react-native.spec.ts`
35
+ * against React Native's own `View`.
36
+ */
37
+ export type SameKeys<A, B> = Identical<keyof A, keyof B>;
38
+ /** The props a component accepts, or `never` for anything that is not one. */
39
+ type PropsOf<Component> = Component extends (props: infer P) => unknown ? P : never;
40
+ /**
41
+ * Does `Platform` provide everything `Base` declares — the whole module surface by
42
+ * assignability, AND `Name`'s prop names exactly?
43
+ *
44
+ * The component is named rather than derived because a mapped type over `keyof Base`
45
+ * stays deferred when `Base` is a type parameter, and two deferred mapped types compare
46
+ * EQUAL: written that way the key-set half was inert, and the rename it was added for
47
+ * went on passing. A named component is also the better diagnostic, and `Name` is
48
+ * constrained to both modules, so a wrong one is an error here rather than a silent
49
+ * `never`.
50
+ *
51
+ * The assignability half stays: it is what catches a REQUIRED prop the base does not
52
+ * declare, a changed arity and a widened return, none of which a key set can see. A
53
+ * platform-only export beyond `Name` is deliberately fine — the surface promise is about
54
+ * the widgets, not about a helper one half happens to need.
55
+ */
56
+ type SatisfiesBase<Platform, Base, Name extends keyof Base & keyof Platform> = Platform extends Base ? SameKeys<PropsOf<Platform[Name]>, PropsOf<Base[Name]>> : false;
57
+ export type BinGtkSatisfiesBase = Assert<SatisfiesBase<typeof BinGtk, typeof BinBase, 'AdwBin'>>;
58
+ export type BinNativeSatisfiesBase = Assert<SatisfiesBase<typeof BinNative, typeof BinBase, 'AdwBin'>>;
59
+ export type ClampGtkSatisfiesBase = Assert<SatisfiesBase<typeof ClampGtk, typeof ClampBase, 'AdwClamp'>>;
60
+ export type ClampNativeSatisfiesBase = Assert<SatisfiesBase<typeof ClampNative, typeof ClampBase, 'AdwClamp'>>;
61
+ /**
62
+ * The names above, as data, so the RUNTIME half can assert the set is complete.
63
+ *
64
+ * A type alias that is deleted takes its check with it and leaves no trace; this list
65
+ * is what a test can count, and `check-adwaita-rn-platform-split.mjs` holds it against
66
+ * the widgets on disk. Two derivations of one fact, and the weaker one is the one that
67
+ * would otherwise wave a missing widget through.
68
+ */
69
+ export declare const PARITY_ASSERTIONS: readonly ["BinGtkSatisfiesBase", "BinNativeSatisfiesBase", "ClampGtkSatisfiesBase", "ClampNativeSatisfiesBase"];
70
+ declare const _default: () => Promise<void>;
71
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+ /** What every widget in this package accepts. */
3
+ export interface AdwWidgetProps {
4
+ children?: ReactNode;
5
+ }
6
+ /** `Adw.Bin` — a widget with one child and no layout of its own. */
7
+ export type AdwBinProps = AdwWidgetProps;
8
+ /**
9
+ * `Adw.Clamp` — constrain a child's size and centre it.
10
+ *
11
+ * The defaults named below are libadwaita's own; in code both halves read them from
12
+ * `@gjsify/adwaita-core`'s `ADW_CLAMP_DEFAULTS` rather than repeating them.
13
+ */
14
+ export interface AdwClampProps extends AdwWidgetProps {
15
+ /** `maximum-size` — how wide the child may get. Default 600. */
16
+ maximumSize?: number;
17
+ /** `tightening-threshold` — where the eased tightening starts. Default 400. */
18
+ tighteningThreshold?: number;
19
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Refuse, naming the component and how to reach a real one.
3
+ *
4
+ * Called from the component body rather than at module scope so that a bundler which
5
+ * merely REACHES the base module — a type-only import that survived, a barrel walked for
6
+ * its declarations — does not die at load. The failure is at the first render, which is
7
+ * still long before anything ships.
8
+ */
9
+ export declare function refuseBaseModule(component: string): never;
@@ -0,0 +1,18 @@
1
+ import type { View as RealView } from 'react-native';
2
+ /**
3
+ * The host element name `View` renders as.
4
+ *
5
+ * `RCTView` is React Native's own name for the platform view — the type a real
6
+ * `react-test-renderer` tree carries under the real runtime — so a test asserting on it
7
+ * reads the same as one written against the real thing.
8
+ */
9
+ export declare const RCT_VIEW = "RCTView";
10
+ /**
11
+ * `react-native`'s `View`, as a host component.
12
+ *
13
+ * The `typeof RealView` annotation IS the contract: a double that grew a prop React
14
+ * Native does not have, or lost one it does, is a compile error here rather than a green
15
+ * test against a private fiction. `props` is widened because `createElement` is typed
16
+ * against React's intrinsic elements and knows nothing about React Native's.
17
+ */
18
+ export declare const View: typeof RealView;
@@ -0,0 +1,33 @@
1
+ import type { View as RealView } from 'react-native';
2
+ import type { Assert, SameKeys } from '../parity.spec.js';
3
+ import { View } from './react-native.js';
4
+ /**
5
+ * The double accepts EXACTLY React Native's `View` props — same names, no more, no
6
+ * fewer.
7
+ *
8
+ * ON KEY SETS, AND WHY THE OBVIOUS VERSION DOES NOT WORK. The first attempt was
9
+ * `const CONTRACT: typeof RealView = View`, which reads like the whole contract and
10
+ * catches almost nothing: `ViewProps` is entirely OPTIONAL properties, and under
11
+ * structural typing any two all-optional object types are assignable to each other in
12
+ * both directions. Measured with that assertion ALONE in the file — replacing the
13
+ * annotation in `react-native.ts` with a hand-written interface carrying an invented
14
+ * `notOnTheRealOne?: number` produced no complaint from the contract at all. Comparing
15
+ * `keyof` is what bites, because a key set is a union of string literals and a union
16
+ * with an extra member is not assignable to one without it. Both directions were run
17
+ * against the version below: an invented prop and a dropped `onLayout` each fail on
18
+ * `DoublePropsMatchReactNative` and nowhere else. A third, added since: a prop whose
19
+ * TYPE changes while the key set does not (`onLayout?: string`) is invisible here and
20
+ * falls on `CONTRACT` below — the two assertions cover different halves and both are
21
+ * needed.
22
+ *
23
+ * The check is deliberately TAUTOLOGICAL while `react-native.ts` annotates `View` as
24
+ * `typeof RealView` — that is the point. It is the annotation this holds in place, and
25
+ * removing the annotation is the only way for the double to acquire a surface of its
26
+ * own.
27
+ *
28
+ * `SameKeys` comes from `parity.spec.ts`, which needs the same instrument for the same
29
+ * reason one level up: the package's own props are all-optional too.
30
+ */
31
+ export type DoublePropsMatchReactNative = Assert<SameKeys<Parameters<typeof View>[0], Parameters<typeof RealView>[0]>>;
32
+ declare const _default: () => Promise<void>;
33
+ export default _default;
@@ -0,0 +1,10 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { AdwBinProps } from '../props.js';
3
+ /**
4
+ * A widget with one child.
5
+ *
6
+ * The declaration here is the CONTRACT both platform modules are held to by
7
+ * `parity.spec.ts`, so a platform module that renames a prop or changes an arity is a
8
+ * type error rather than a divergence somebody finds on one of the two devices.
9
+ */
10
+ export declare function AdwBin(_props: AdwBinProps): ReactElement | null;
@@ -0,0 +1,5 @@
1
+ /** @jsxImportSource @gjsify/gtk-host/react */
2
+ import type { ReactElement } from 'react';
3
+ import type { AdwBinProps } from '../props.js';
4
+ /** {@link import('./bin.js').AdwBin} on GTK. */
5
+ export declare function AdwBin({ children }: AdwBinProps): ReactElement | null;
@@ -0,0 +1,5 @@
1
+ /** @jsxImportSource react */
2
+ import type { ReactElement } from 'react';
3
+ import type { AdwBinProps } from '../props.js';
4
+ /** {@link import('./bin.js').AdwBin} on React Native. */
5
+ export declare function AdwBin({ children }: AdwBinProps): ReactElement | null;
@@ -0,0 +1,3 @@
1
+ /** @jsxImportSource react */
2
+ declare const _default: () => Promise<void>;
3
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { AdwClampProps } from '../props.js';
3
+ /**
4
+ * Constrain a child's size and centre it.
5
+ *
6
+ * THE TWO IMPLEMENTATIONS SHARE THEIR ARITHMETIC AND NOTHING ELSE. On GTK the real
7
+ * `Adw.Clamp` computes the allocation in C; on React Native `@gjsify/adwaita-core`'s
8
+ * `clampAllocate` — a port of `adw_clamp_layout_allocate` — computes it in TypeScript.
9
+ * Both are libadwaita's easing curve, so the suites assert the same child size and the
10
+ * same centring offset on both sides rather than each side's own idea of "clamped".
11
+ */
12
+ export declare function AdwClamp(_props: AdwClampProps): ReactElement | null;
@@ -0,0 +1,5 @@
1
+ /** @jsxImportSource @gjsify/gtk-host/react */
2
+ import type { ReactElement } from 'react';
3
+ import type { AdwClampProps } from '../props.js';
4
+ /** {@link import('./clamp.js').AdwClamp} on GTK. */
5
+ export declare function AdwClamp({ children, maximumSize, tighteningThreshold }: AdwClampProps): ReactElement | null;
@@ -0,0 +1,3 @@
1
+ /** @jsxImportSource @gjsify/gtk-host/react */
2
+ declare const _default: () => Promise<void>;
3
+ export default _default;
@@ -0,0 +1,12 @@
1
+ /** @jsxImportSource react */
2
+ import { type ReactElement } from 'react';
3
+ import type { AdwClampProps } from '../props.js';
4
+ /**
5
+ * {@link import('./clamp.js').AdwClamp} on React Native.
6
+ *
7
+ * BEFORE THE FIRST LAYOUT there is no available width, so the child is rendered unclamped
8
+ * for exactly one frame. Rendering nothing instead would be worse than it sounds: the
9
+ * outer `View` is what `onLayout` fires on, so an empty first pass still costs the frame
10
+ * AND makes the child's own mount effects run a frame later than they do on GTK.
11
+ */
12
+ export declare function AdwClamp({ children, maximumSize, tighteningThreshold }: AdwClampProps): ReactElement | null;
@@ -0,0 +1,3 @@
1
+ /** @jsxImportSource react */
2
+ declare const _default: () => Promise<void>;
3
+ export default _default;
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@gjsify/adwaita-react-native",
3
+ "version": "0.44.0",
4
+ "description": "Adwaita widgets with one API surface and two implementations — the real Adw.* widget on GTK4, React Native primitives on a phone",
5
+ "type": "module",
6
+ "module": "lib/esm/index.js",
7
+ "types": "lib/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/types/index.d.ts",
11
+ "react-native": "./lib/esm/index.native.js",
12
+ "default": "./lib/esm/index.gtk.js"
13
+ },
14
+ "./widgets/bin": {
15
+ "types": "./lib/types/widgets/bin.d.ts",
16
+ "react-native": "./lib/esm/widgets/bin.native.js",
17
+ "default": "./lib/esm/widgets/bin.gtk.js"
18
+ },
19
+ "./widgets/clamp": {
20
+ "types": "./lib/types/widgets/clamp.d.ts",
21
+ "react-native": "./lib/esm/widgets/clamp.native.js",
22
+ "default": "./lib/esm/widgets/clamp.gtk.js"
23
+ },
24
+ "./testing/react-native": {
25
+ "types": "./lib/types/testing/react-native.d.ts",
26
+ "default": "./lib/esm/testing/react-native.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "lib",
31
+ "!lib/types/**/*.spec.d.ts"
32
+ ],
33
+ "scripts": {
34
+ "clear": "gjsify clear lib tmp tsconfig.tsbuildinfo test.gjs.mjs dist",
35
+ "check": "gjsify tsc --noEmit",
36
+ "build": "gjsify run build:gjsify && gjsify run build:types",
37
+ "build:gjsify": "gjsify build --library 'src/**/*.{ts,tsx,js}' --exclude 'src/**/*.spec.{mts,ts,tsx}' 'src/test.{mts,ts}' 'src/test.node.mts'",
38
+ "build:types": "gjsify tsc",
39
+ "build:test": "gjsify run build:test:gjs && gjsify run build:test:node",
40
+ "build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs --define 'process.env.NODE_ENV=\"production\"' --exclude-globals navigator",
41
+ "build:test:node": "gjsify build src/test.node.mts --app node --outfile dist/test.node.mjs --exclude-globals navigator --alias react-native=@gjsify/adwaita-react-native/testing/react-native",
42
+ "test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:node && gjsify run test:gjs",
43
+ "test:gjs": "gjsify run test.gjs.mjs",
44
+ "test:node": "node dist/test.node.mjs"
45
+ },
46
+ "keywords": [
47
+ "gjs",
48
+ "gtk",
49
+ "adwaita",
50
+ "react",
51
+ "react-native",
52
+ "design-system"
53
+ ],
54
+ "dependencies": {
55
+ "@gjsify/adwaita-core": "^0.44.0",
56
+ "@gjsify/gtk-host": "^0.44.0"
57
+ },
58
+ "peerDependencies": {
59
+ "react": "^19.2.0",
60
+ "react-native": ">=0.87 <1"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "react-native": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "@girs/adw-1": "^4.1.0",
69
+ "@girs/gjs": "^4.1.0",
70
+ "@girs/gtk-4.0": "^4.1.0",
71
+ "@gjsify/cli": "^0.44.0",
72
+ "@gjsify/unit": "^0.44.0",
73
+ "@types/node": "^25.9.2",
74
+ "@types/react": "^19.2.2",
75
+ "@types/react-test-renderer": "^19.1.0",
76
+ "react": "^19.2.0",
77
+ "react-native": ">=0.87 <1",
78
+ "react-test-renderer": "^19.2.8",
79
+ "typescript": "^6.0.3"
80
+ },
81
+ "gjsify": {
82
+ "runtimes": {
83
+ "gjs": "polyfill",
84
+ "node": "polyfill",
85
+ "browser": "none",
86
+ "nativescript": "none",
87
+ "react-native": "native"
88
+ },
89
+ "tier": 3
90
+ },
91
+ "license": "MIT",
92
+ "repository": {
93
+ "type": "git",
94
+ "url": "git+https://github.com/gjsify/gjsify.git",
95
+ "directory": "packages/framework/adwaita-react-native"
96
+ },
97
+ "bugs": {
98
+ "url": "https://github.com/gjsify/gjsify/issues"
99
+ },
100
+ "homepage": "https://github.com/gjsify/gjsify/tree/main/packages/framework/adwaita-react-native#readme"
101
+ }