@gjsify/react-native 0.48.0 → 0.50.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.
@@ -1,6 +1,6 @@
1
- import { type PropAnswer, type PropStatus } from './primitives/answers.js';
1
+ import { type PropAnswer, type PropStatus, type ValueVocabulary } from './primitives/answers.js';
2
2
  import { PRIMITIVE_NAMES } from './primitives/table.js';
3
- export type { PropAnswer, PropStatus };
3
+ export type { PropAnswer, PropStatus, ValueVocabulary };
4
4
  /**
5
5
  * Every primitive this layer answers for.
6
6
  *
@@ -68,11 +68,29 @@ export declare function acceptsPropValue(primitive: string, prop: string, value:
68
68
  * under Node type stripping is why) — so the formatter the throw uses lives in
69
69
  * `errors.ts` and both call it.
70
70
  *
71
- * A value merely ABSENT from a mapped prop's table is NOT answered here: that is the
72
- * "Known: …" a typo gets, it is built from the map at the moment of the throw, and this
73
- * surface is about the values the table refuses ON PURPOSE.
71
+ * TWO GRAINS OF REFUSAL, AND THE SECOND ONE WAS MISSING (#1648). A prop can refuse a
72
+ * value by NAME — `answer.refuses`, a deny-list with a reason per entry — and it can
73
+ * refuse one by OMISSION from the list its route maps: `pointerEvents` is
74
+ * `{ auto, none }`, `box-none` is a real React Native spelling with no GTK
75
+ * expression, and a render throws on it. Reading only the deny-list answered ACCEPTED
76
+ * for the second kind on every mapped property, every `announce` route and every
77
+ * accessible record — and a consumer deleted a working `box-none` mapping of their
78
+ * own because a green test said the layer had caught up.
79
+ *
80
+ * What is still NOT answered here is the TYPE grain: `coerce` refuses a non-boolean
81
+ * for `editable` and a non-function for `onPress`, and neither is a list this table
82
+ * can publish. `acceptsPropValue` is an oracle for the vocabulary, not for the type.
74
83
  */
75
84
  export declare function explainPropValue(primitive: string, prop: string, value: unknown, variant?: PropVariant): string | null;
85
+ /**
86
+ * The values this prop accepts by name, or `null` when the table enumerates none.
87
+ *
88
+ * The reader for {@link PropAnswer.allows}, beside `propRefusedValues` because they
89
+ * are the two halves of one question and a consumer holding a ledger of "which props
90
+ * has the layer caught up on" needs both: a deny-list says which spellings have a
91
+ * reason, an allow-list says which ones exist at all.
92
+ */
93
+ export declare function propAllowedValues(primitive: string, prop: string, variant?: PropVariant): readonly string[] | null;
76
94
  /** Every prop name this primitive's table row carries, sorted. Framework props are not among them. */
77
95
  export declare function propNames(primitive: string, variant?: PropVariant): readonly string[];
78
96
  /**
@@ -1,10 +1,24 @@
1
1
  import { type ReactElement, type ReactNode } from 'react';
2
- import type Adw from '@girs/adw-1';
2
+ import Adw from 'gi://Adw?version=1';
3
3
  import { type ScreenDeclaration } from './screens.js';
4
4
  /** What one tab can be told. */
5
5
  export interface TabScreenOptions {
6
6
  /** The switcher button's label. Defaults to the route name. */
7
7
  title?: string;
8
+ /**
9
+ * The switcher button's icon, as an icon-theme name (`go-home-symbolic`).
10
+ *
11
+ * NOT OPTIONAL DECORATION, which is why it is here rather than left to the
12
+ * application. `Adw.ViewSwitcher` reserves the icon whether or not one is set —
13
+ * MEASURED, the same five tabs measure 317/647 px with icons and without — and a
14
+ * page with no `icon-name` draws the icon theme's missing-image glyph in the space
15
+ * it reserved. So the choice was never "icons or no icons"; it was "your icon or a
16
+ * broken one".
17
+ *
18
+ * A name the theme does not carry draws that same glyph, and nothing says so.
19
+ * `Gtk.IconTheme.has_icon()` is the check worth running when picking one.
20
+ */
21
+ iconName?: string;
8
22
  }
9
23
  /**
10
24
  * Put the stack on the focused page, or leave it alone because that page is not there yet.
@@ -53,8 +67,39 @@ export interface TabsProps {
53
67
  children?: ReactNode;
54
68
  /** Options for every tab, before its own. */
55
69
  screenOptions?: TabScreenOptions;
56
- /** `false` drops the header bar, and with it the switcher. */
70
+ /** `false` drops the header bar, and with it the switcher and the bottom bar. */
57
71
  headerShown?: boolean;
72
+ /**
73
+ * A persistent bar between the content and the switcher bar — a now-playing strip,
74
+ * a selection toolbar, an offline notice.
75
+ *
76
+ * ## Why a prop rather than a child
77
+ *
78
+ * `<Tabs>` reads its children as `<Tabs.Screen>` DECLARATIONS and refuses anything
79
+ * else by name, so there is no child position to put this in. And an app cannot
80
+ * wrap `<Tabs>` from outside either: the switcher is created with `slot: 'title'`,
81
+ * which resolves against the PARENT, so a box placed between a tab layout and the
82
+ * header bar takes the switcher's slot away and the router refuses it. A consumer
83
+ * hit exactly that and recorded "no mini player" as a limitation for weeks.
84
+ *
85
+ * ## Where it lands, measured
86
+ *
87
+ * `Adw.ToolbarView`'s `bottom` slot is an ADDER, so it takes this and the switcher
88
+ * bar together, and the order decides the stacking. MEASURED on libadwaita 1.9.3 in a
89
+ * 480x320 window: with two bottom bars added in sequence, the FIRST sits closer to
90
+ * the content — content at y=0 h=214, first-added at y=217, second-added at y=257.
91
+ * So this is rendered BEFORE the switcher bar, which puts it above it, which is
92
+ * where a phone puts a now-playing strip relative to its tab bar.
93
+ *
94
+ * Wrapped in a `Gtk.Box` rather than slotted directly, so that a caller can hand
95
+ * over any node — including a fragment or a component whose root element it does
96
+ * not control — without having to know that a slot name is involved.
97
+ *
98
+ * NOT RENDERED under `headerShown: false`, which drops the whole toolbar view and
99
+ * has nowhere to put it. Named here rather than worked around, because a navigator
100
+ * that asked for no chrome getting a bar anyway would be the greater surprise.
101
+ */
102
+ bottomBar?: ReactNode;
58
103
  }
59
104
  /**
60
105
  * `expo-router`'s `<Tabs>`: its tabs are the FILES this `_layout` owns.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/react-native",
3
- "version": "0.48.0",
3
+ "version": "0.50.0",
4
4
  "description": "The React Native view vocabulary rendered onto GTK4/Adwaita, over @gjsify/gtk-host",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -120,8 +120,8 @@
120
120
  "react-native"
121
121
  ],
122
122
  "dependencies": {
123
- "@gjsify/adwaita-app": "^0.48.0",
124
- "@gjsify/gtk-host": "^0.48.0"
123
+ "@gjsify/adwaita-app": "^0.50.0",
124
+ "@gjsify/gtk-host": "^0.50.0"
125
125
  },
126
126
  "peerDependencies": {
127
127
  "@react-navigation/core": "^7.21.13",
@@ -148,13 +148,13 @@
148
148
  }
149
149
  },
150
150
  "devDependencies": {
151
- "@girs/adw-1": "4.6.0",
152
- "@girs/gdk-4.0": "4.6.0",
153
- "@girs/gjs": "4.6.0",
154
- "@girs/gtk-4.0": "4.6.0",
155
- "@gjsify/cli": "^0.48.0",
156
- "@gjsify/rolldown-plugin-gjsify": "^0.48.0",
157
- "@gjsify/unit": "^0.48.0",
151
+ "@girs/adw-1": "5.0.0",
152
+ "@girs/gdk-4.0": "5.0.0",
153
+ "@girs/gjs": "5.0.0",
154
+ "@girs/gtk-4.0": "5.0.0",
155
+ "@gjsify/cli": "^0.50.0",
156
+ "@gjsify/rolldown-plugin-gjsify": "^0.50.0",
157
+ "@gjsify/unit": "^0.50.0",
158
158
  "@react-navigation/core": "^7.21.13",
159
159
  "@react-navigation/routers": "^7.6.4",
160
160
  "@types/node": "^25.9.2",