@gjsify/react-native 0.47.0 → 0.49.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/PROPS.md +319 -92
- package/README.md +112 -0
- package/SUPPORT.md +14 -9
- package/lib/esm/accessibility.js +1 -0
- package/lib/esm/apis/alert.js +1 -1
- package/lib/esm/app-registry.js +1 -1
- package/lib/esm/child-facts.js +1 -0
- package/lib/esm/components.js +1 -1
- package/lib/esm/generated/own-exports.js +1 -1
- package/lib/esm/generated/unsupported-react-native.js +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/esm/lists/controller.js +1 -1
- package/lib/esm/primitives/accessibility.js +1 -0
- package/lib/esm/primitives/answers.js +1 -1
- package/lib/esm/primitives/defaults.js +1 -1
- package/lib/esm/primitives/errors.js +1 -1
- package/lib/esm/primitives/index.js +1 -1
- package/lib/esm/primitives/resolve.js +1 -1
- package/lib/esm/primitives/style.js +1 -1
- package/lib/esm/primitives/table.js +1 -1
- package/lib/esm/prop-table.js +1 -1
- package/lib/esm/router/chrome.js +1 -0
- package/lib/esm/router/navigation.js +1 -1
- package/lib/esm/router/per-route-cache.js +1 -0
- package/lib/esm/router/stack.js +1 -1
- package/lib/esm/router/tabs.js +1 -1
- package/lib/esm/solid/index.js +1 -1
- package/lib/esm/support-table.js +1 -1
- package/lib/esm/window-chrome.js +1 -0
- package/lib/types/accessibility.d.ts +10 -0
- package/lib/types/app-registry.d.ts +95 -5
- package/lib/types/app-registry.spec.d.ts +2 -0
- package/lib/types/child-facts.d.ts +45 -0
- package/lib/types/child-facts.spec.d.ts +2 -0
- package/lib/types/components.d.ts +43 -1
- package/lib/types/generated/unsupported-react-native.d.ts +8 -4
- package/lib/types/index.d.ts +3 -3
- package/lib/types/primitives/accessibility.d.ts +112 -0
- package/lib/types/primitives/answers.d.ts +18 -0
- package/lib/types/primitives/errors.d.ts +9 -0
- package/lib/types/primitives/resolve.d.ts +10 -0
- package/lib/types/primitives/style.d.ts +23 -0
- package/lib/types/primitives/table.d.ts +13 -1
- package/lib/types/primitives/text-metrics.spec.d.ts +2 -0
- package/lib/types/prop-table.d.ts +29 -0
- package/lib/types/router/chrome.d.ts +106 -0
- package/lib/types/router/errors.d.ts +3 -1
- package/lib/types/router/per-route-cache.d.ts +21 -0
- package/lib/types/router/stack.d.ts +6 -1
- package/lib/types/router/tabs.d.ts +47 -2
- package/lib/types/solid/index.d.ts +2 -1
- package/lib/types/support-table.d.ts +1 -1
- package/lib/types/window-chrome.d.ts +39 -0
- package/package.json +7 -7
- package/react-native-surface.json +18 -9
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type ReactElement, type ReactNode, type Ref } from 'react';
|
|
2
|
+
import type { AccessibilityProps } from './primitives/accessibility.js';
|
|
2
3
|
import type { ClassNameInput } from './primitives/classes.js';
|
|
3
4
|
import { type TextInputHandle } from './primitives/handles.js';
|
|
4
5
|
import { type ChildContext, type PrimitivePlan, type WidgetNode } from './primitives/resolve.js';
|
|
5
6
|
import { type StyleInput } from './primitives/style.js';
|
|
6
7
|
import type { StyleTokens } from '@gjsify/gtk-host/style';
|
|
7
8
|
/** What every primitive accepts on top of its own props. */
|
|
8
|
-
export interface CommonProps {
|
|
9
|
+
export interface CommonProps extends AccessibilityProps {
|
|
9
10
|
className?: ClassNameInput;
|
|
10
11
|
style?: StyleInput;
|
|
11
12
|
children?: ReactNode;
|
|
@@ -101,6 +102,15 @@ export interface ScrollViewProps extends CommonProps {
|
|
|
101
102
|
/** NativeWind's spelling for the inner node's class list. See `ContentSpec.classNameProp`. */
|
|
102
103
|
contentContainerClassName?: ClassNameInput;
|
|
103
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* How many scrollers are currently watching their viewport's width.
|
|
107
|
+
*
|
|
108
|
+
* A column has nothing to invalidate when that width changes — its height is not a
|
|
109
|
+
* function of it — and a column that subscribed anyway would render identically to
|
|
110
|
+
* one that did not, queueing a resize per window resize with nothing to show for it.
|
|
111
|
+
* Only a count tells them apart.
|
|
112
|
+
*/
|
|
113
|
+
export declare const railWatchCount: () => number;
|
|
104
114
|
export declare function ScrollView(props: ScrollViewProps): ReactElement;
|
|
105
115
|
export interface ActivityIndicatorProps extends CommonProps {
|
|
106
116
|
animating?: boolean;
|
|
@@ -231,6 +241,38 @@ export declare function Button(props: ButtonProps): ReactElement;
|
|
|
231
241
|
* lost a screen.
|
|
232
242
|
*/
|
|
233
243
|
export declare function SafeAreaView(props: ViewProps): ReactElement;
|
|
244
|
+
export interface ModalProps extends CommonProps {
|
|
245
|
+
/** Default `true`, as React Native's is. `false` renders nothing at all. */
|
|
246
|
+
visible?: boolean;
|
|
247
|
+
/**
|
|
248
|
+
* The user asked for this modal to go away — `Adw.Dialog::close-attempt`.
|
|
249
|
+
*
|
|
250
|
+
* REQUIRED IN PRACTICE, exactly as React Native documents it for Android and
|
|
251
|
+
* tvOS, and here for the same reason made explicit: the dialog is built with
|
|
252
|
+
* `can-close: false`, so Escape, the close control and a click on the backdrop
|
|
253
|
+
* all arrive HERE and none of them takes the sheet down. Setting `visible` to
|
|
254
|
+
* false does. Without this prop a modal cannot be dismissed by the user, which
|
|
255
|
+
* is React Native's behaviour and not a limitation of this layer.
|
|
256
|
+
*/
|
|
257
|
+
onRequestClose?: () => void;
|
|
258
|
+
/** The modal is on screen — `Gtk.Widget::map`, which fires once. */
|
|
259
|
+
onShow?: () => void;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* A portal: the element's host node is not its parent node (ADR 0045).
|
|
263
|
+
*
|
|
264
|
+
* The whole component is `visible`. Everything else — presenting the dialog against
|
|
265
|
+
* the parent's toplevel, waiting for one when the tree is still detached, taking the
|
|
266
|
+
* sheet back down on unmount — is the host's placement axis, which is why this is
|
|
267
|
+
* eight lines and why a Vue or Solid binding gets the same behaviour for free.
|
|
268
|
+
*
|
|
269
|
+
* THE HOOKS RUN EITHER WAY, and that is the one thing this file could get wrong: an
|
|
270
|
+
* early return before `usePlan` would change the component's hook count the first
|
|
271
|
+
* time `visible` flipped, which React refuses outright. So the plan is always built
|
|
272
|
+
* and only the RENDER is conditional — and an unrendered element is one React
|
|
273
|
+
* deletes, which reaches `destroy` in the host and closes the dialog.
|
|
274
|
+
*/
|
|
275
|
+
export declare function Modal(props: ModalProps): ReactElement | null;
|
|
234
276
|
export interface KeyboardAvoidingViewProps extends ViewProps {
|
|
235
277
|
/** A declared no-op: there is no on-screen keyboard to move out of the way of. */
|
|
236
278
|
behavior?: 'height' | 'position' | 'padding';
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
export declare const Modal: never;
|
|
2
1
|
export declare const RefreshControl: never;
|
|
3
2
|
export declare const AppState: never;
|
|
4
3
|
export declare const PixelRatio: never;
|
|
5
4
|
export declare const PlatformColor: never;
|
|
6
5
|
export declare const AccessibilityInfo: never;
|
|
7
6
|
export declare const LayoutAnimation: never;
|
|
8
|
-
export declare const InteractionManager: never;
|
|
9
7
|
export declare const useAnimatedValue: never;
|
|
10
8
|
export declare const useAnimatedValueXY: never;
|
|
11
9
|
export declare const useAnimatedColor: never;
|
|
@@ -48,10 +46,16 @@ export declare const UIManager: never;
|
|
|
48
46
|
export declare const findNodeHandle: never;
|
|
49
47
|
export declare const registerCallableModule: never;
|
|
50
48
|
export declare const Networking: never;
|
|
51
|
-
export declare const NativeDialogManagerAndroid: never;
|
|
52
49
|
export declare const ProgressBarAndroid: never;
|
|
53
|
-
export declare const Touchable: never;
|
|
54
50
|
export declare const NativeEventEmitter: never;
|
|
55
51
|
export declare const DeviceEventEmitter: never;
|
|
56
52
|
export declare const NativeAppEventEmitter: never;
|
|
57
53
|
export declare const RootTagContext: never;
|
|
54
|
+
export declare const AssetRegistry: never;
|
|
55
|
+
export declare const unstable_VirtualArray: never;
|
|
56
|
+
export declare const unstable_createVirtualCollectionView: never;
|
|
57
|
+
export declare const unstable_VirtualColumn: never;
|
|
58
|
+
export declare const unstable_VirtualColumnGenerator: never;
|
|
59
|
+
export declare const unstable_VirtualRow: never;
|
|
60
|
+
export declare const unstable_getScrollParent: never;
|
|
61
|
+
export declare const unstable_DEFAULT_INITIAL_NUM_TO_RENDER: never;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { AppRegistry, registerRootComponent } from './app-registry.js';
|
|
1
|
+
export { AppRegistry, lastWindowChromeProblems, registerRootComponent } from './app-registry.js';
|
|
2
2
|
export type { ComponentProvider, RunApplicationOptions } from './app-registry.js';
|
|
3
3
|
export { EventEmitter } from './event-emitter.js';
|
|
4
4
|
export type { EventSubscription } from './event-emitter.js';
|
|
5
|
-
export { ActivityIndicator, Button, Image, ImageBackground, KeyboardAvoidingView, Pressable, SafeAreaView, ScrollView, StatusBar, Switch, Text, TextInput, TouchableHighlight, TouchableOpacity, TouchableWithoutFeedback, View, } from './components.js';
|
|
6
|
-
export type { ActivityIndicatorProps, ButtonProps, CommonProps, ImageBackgroundProps, ImageProps, ImageURISource, KeyboardAvoidingViewProps, PressableProps, PressableState, ScrollViewProps, StatusBarProps, SwitchProps, TextInputProps, TextProps, TouchableHighlightProps, TouchableProps, TouchableWithoutFeedbackProps, ViewProps, } from './components.js';
|
|
5
|
+
export { ActivityIndicator, Button, Image, ImageBackground, KeyboardAvoidingView, Modal, Pressable, SafeAreaView, ScrollView, StatusBar, Switch, Text, TextInput, TouchableHighlight, TouchableOpacity, TouchableWithoutFeedback, View, } from './components.js';
|
|
6
|
+
export type { ActivityIndicatorProps, ButtonProps, CommonProps, ImageBackgroundProps, ImageProps, ImageURISource, KeyboardAvoidingViewProps, ModalProps, PressableProps, PressableState, ScrollViewProps, StatusBarProps, SwitchProps, TextInputProps, TextProps, TouchableHighlightProps, TouchableProps, TouchableWithoutFeedbackProps, ViewProps, } from './components.js';
|
|
7
7
|
export { FlatList, SectionList, VirtualizedList, VirtualizedSectionList } from './lists/components.js';
|
|
8
8
|
export type { FlatListProps, ListRenderItemInfo, ListSeparators, ListSlot, SectionListProps, SectionListSection, VirtualizedListProps, } from './lists/components.js';
|
|
9
9
|
export { Alert, Appearance, Dimensions, Keyboard, Linking, Platform, Share } from './apis/index.js';
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One GTK accessible attribute, and the GValue GTK reads its value out of.
|
|
3
|
+
*
|
|
4
|
+
* `set` picks the call — `Gtk.Accessible.update_property()` or `update_state()`.
|
|
5
|
+
* They are two enums (`Gtk.AccessibleProperty`, `Gtk.AccessibleState`) with
|
|
6
|
+
* overlapping nicks, so which SET an attribute belongs to is not derivable from
|
|
7
|
+
* its name and is declared here.
|
|
8
|
+
*/
|
|
9
|
+
export interface AccessibleAttribute {
|
|
10
|
+
readonly set: 'property' | 'state';
|
|
11
|
+
/**
|
|
12
|
+
* The enum member's nick — `label`, `help-text`, `checked`, `disabled`.
|
|
13
|
+
*
|
|
14
|
+
* A nick rather than the enum member, for the reason every other route holds
|
|
15
|
+
* strings: nothing under `primitives/` imports `gi://`. The call layer resolves
|
|
16
|
+
* it the way `@gjsify/gtk-host`'s `props.ts` resolves any enum nick, by
|
|
17
|
+
* upper-casing and swapping `-` for `_`.
|
|
18
|
+
*/
|
|
19
|
+
readonly name: string;
|
|
20
|
+
/** Which GValue GTK reads it out of — see this module's header; every one is measured. */
|
|
21
|
+
readonly as: 'string' | 'boolean' | 'tristate';
|
|
22
|
+
}
|
|
23
|
+
/** A prop whose own value is one accessible attribute — `accessibilityLabel`. */
|
|
24
|
+
export interface AccessibleValueRoute {
|
|
25
|
+
readonly to: 'accessible';
|
|
26
|
+
readonly from: 'value';
|
|
27
|
+
readonly attribute: AccessibleAttribute;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A prop whose value is a RECORD of accessible attributes — `accessibilityState`.
|
|
31
|
+
*
|
|
32
|
+
* React Native has exactly one of these and its five keys all land, which is the
|
|
33
|
+
* happier half of a mapping that is otherwise full of refusals: `disabled` and
|
|
34
|
+
* `busy` are GTK booleans, and `selected`, `checked` and `expanded` are GTK
|
|
35
|
+
* TRISTATES — so `checked: 'mixed'`, the one genuinely three-valued thing in
|
|
36
|
+
* React Native's accessibility surface, has an exact GTK member
|
|
37
|
+
* (`Gtk.AccessibleTristate.MIXED`) rather than an approximation.
|
|
38
|
+
*
|
|
39
|
+
* `refuses` is empty today and is kept because the next key React Native adds is
|
|
40
|
+
* more likely to have no GTK attribute than to have one, and a key refused BY NAME
|
|
41
|
+
* with a reason is the answer this table gives everywhere else.
|
|
42
|
+
*/
|
|
43
|
+
export interface AccessibleMembersRoute {
|
|
44
|
+
readonly to: 'accessible';
|
|
45
|
+
readonly from: 'members';
|
|
46
|
+
readonly members: Readonly<Record<string, AccessibleAttribute>>;
|
|
47
|
+
/** Keys this layer recognises and cannot answer, each with what to do instead. */
|
|
48
|
+
readonly refuses: Readonly<Record<string, string>>;
|
|
49
|
+
}
|
|
50
|
+
export type AccessibleRoute = AccessibleValueRoute | AccessibleMembersRoute;
|
|
51
|
+
/**
|
|
52
|
+
* One accessible attribute write, decided here and CALLED one layer up.
|
|
53
|
+
*
|
|
54
|
+
* `value` is already the shape the call layer puts in a GValue: a string for
|
|
55
|
+
* `string`, a boolean for `boolean`, and 0/1/2 for `tristate` — the numeric
|
|
56
|
+
* `Gtk.AccessibleTristate` member, resolved here so the call layer holds no
|
|
57
|
+
* vocabulary of its own.
|
|
58
|
+
*/
|
|
59
|
+
export interface ResolvedAccessible {
|
|
60
|
+
/** The React Native prop that asked for it, for a refusal's benefit. */
|
|
61
|
+
readonly prop: string;
|
|
62
|
+
readonly set: 'property' | 'state';
|
|
63
|
+
readonly name: string;
|
|
64
|
+
readonly value: string | boolean | number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The accessibility props every primitive accepts, declared ONCE for both L3s.
|
|
68
|
+
*
|
|
69
|
+
* Here rather than in either binding because the two `CommonProps` are
|
|
70
|
+
* hand-maintained and this set is identical in both — the second copy is where a
|
|
71
|
+
* prop that exists in React and not in Solid comes from, and `PROPS.md` would keep
|
|
72
|
+
* claiming both answered it. L2 owns the vocabulary; a binding extends it.
|
|
73
|
+
*/
|
|
74
|
+
export interface AccessibilityProps {
|
|
75
|
+
/**
|
|
76
|
+
* Recognised and without effect — every GTK widget is already in the
|
|
77
|
+
* accessibility tree. The table's row carries the whole reason and names the
|
|
78
|
+
* prop that takes an element OUT of it (`accessibilityRole="none"`).
|
|
79
|
+
*/
|
|
80
|
+
accessible?: boolean;
|
|
81
|
+
/** The accessible NAME — `Gtk.AccessibleProperty.LABEL`, what a screen reader says on focus. */
|
|
82
|
+
accessibilityLabel?: string;
|
|
83
|
+
/**
|
|
84
|
+
* The accessible ROLE — the GObject property `Gtk.Accessible:accessible-role`.
|
|
85
|
+
*
|
|
86
|
+
* `string` rather than React Native's own union, because the mapping is the
|
|
87
|
+
* table's and an unmapped value is a named refusal at render rather than a type
|
|
88
|
+
* error: React Native's own type ends in `| string`, so a union here would
|
|
89
|
+
* reject nothing the table accepts and accept nothing extra.
|
|
90
|
+
*/
|
|
91
|
+
accessibilityRole?: string;
|
|
92
|
+
/** What acting on this element does — `Gtk.AccessibleProperty.HELP_TEXT`. */
|
|
93
|
+
accessibilityHint?: string;
|
|
94
|
+
/** The accessible STATES — `Gtk.Accessible.update_state()`; `checked` is a real tri-state. */
|
|
95
|
+
accessibilityState?: {
|
|
96
|
+
disabled?: boolean;
|
|
97
|
+
busy?: boolean;
|
|
98
|
+
checked?: boolean | 'mixed';
|
|
99
|
+
selected?: boolean;
|
|
100
|
+
expanded?: boolean;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A route plus the authored value → the attribute writes it stands for.
|
|
105
|
+
*
|
|
106
|
+
* Throws nothing itself: it reports a bad value as a `problem` so the caller raises
|
|
107
|
+
* the one `PrimitiveError` this layer raises, with the primitive's own name in it.
|
|
108
|
+
*/
|
|
109
|
+
export declare function resolveAccessible(route: AccessibleRoute, prop: string, value: unknown): {
|
|
110
|
+
entries: readonly ResolvedAccessible[];
|
|
111
|
+
problem: string | null;
|
|
112
|
+
};
|
|
@@ -21,6 +21,8 @@ export type PropStatus =
|
|
|
21
21
|
| 'gesture'
|
|
22
22
|
/** Calls `Gtk.Accessible.announce()` when a signal reports the content changed. */
|
|
23
23
|
| 'announcement'
|
|
24
|
+
/** Writes a `Gtk.Accessible` property or state through `update_property()`/`update_state()`. */
|
|
25
|
+
| 'accessible'
|
|
24
26
|
/** Recognised and deliberately without effect on a desktop window. */
|
|
25
27
|
| 'ignored'
|
|
26
28
|
/** Refused by name, with a reason. Throws a `PrimitiveError` when rendered. */
|
|
@@ -44,6 +46,22 @@ export interface PropAnswer {
|
|
|
44
46
|
readonly why: string;
|
|
45
47
|
/** What it reaches on the GTK side: property names, a signal, or nothing. */
|
|
46
48
|
readonly gtk: readonly string[];
|
|
49
|
+
/**
|
|
50
|
+
* The VALUES this prop refuses by name, each with its own reason (#1555).
|
|
51
|
+
*
|
|
52
|
+
* A field rather than a fold into {@link why}, because the prop is ACCEPTED and
|
|
53
|
+
* only some of its values are not: `accessibilityRole` answers most role names and
|
|
54
|
+
* refuses a handful, and a row that said `property | — ` could not tell a reader
|
|
55
|
+
* which — `propRefusedValues` is the reader that can. Keyed by the VALUE for a
|
|
56
|
+
* mapped property route and by the KEY for an accessible record
|
|
57
|
+
* (`accessibilityState`), which is the same question at the two grains the table
|
|
58
|
+
* has.
|
|
59
|
+
*
|
|
60
|
+
* The reason is the table's own string, not a sentence built here: `prop-table.ts`
|
|
61
|
+
* wraps it in the subject the resolver throws, so the static answer and the render
|
|
62
|
+
* stay one string (ADR 0039 § 2).
|
|
63
|
+
*/
|
|
64
|
+
readonly refuses: Readonly<Record<string, string>>;
|
|
47
65
|
}
|
|
48
66
|
/** Does this status let a render proceed? `ignored` counts — it is a declared no-op, not a refusal. */
|
|
49
67
|
export declare const isAccepted: (status: PropStatus) => boolean;
|
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
* both the throw and the static answer are built from it.
|
|
8
8
|
*/
|
|
9
9
|
export declare const primitiveErrorMessage: (primitive: string, subject: string, detail: string) => string;
|
|
10
|
+
/**
|
|
11
|
+
* How a VALUE reads inside a subject — `prop "accessibilityRole" = "keyboardkey"`.
|
|
12
|
+
*
|
|
13
|
+
* Here rather than in `resolve.ts`, where it started, because the per-value refusals
|
|
14
|
+
* are answerable statically now (ADR 0039 § Amendment, #1555) and `prop-table.ts` has
|
|
15
|
+
* to build the same subject the throw does. Two formatters that happen to agree are
|
|
16
|
+
* not one string.
|
|
17
|
+
*/
|
|
18
|
+
export declare const describeValue: (value: unknown) => string;
|
|
10
19
|
/** A primitive, prop or combination this layer cannot answer for, and why. */
|
|
11
20
|
export declare class PrimitiveError extends Error {
|
|
12
21
|
readonly name = "PrimitiveError";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LayoutIntent, StyleTokens } from '@gjsify/gtk-host/style';
|
|
2
|
+
import { type ResolvedAccessible } from './accessibility.js';
|
|
2
3
|
import type { ClassNameInput } from './classes.js';
|
|
3
4
|
import { type ChildContext, type ChildFacts, type Orientation, type WidgetFacts } from './intents.js';
|
|
4
5
|
import { type ClassNameSink, type StyleInput } from './style.js';
|
|
@@ -77,6 +78,7 @@ export interface ResolvedAnnouncement {
|
|
|
77
78
|
/** The `Gtk.AccessibleAnnouncementPriority` nick. */
|
|
78
79
|
readonly priority: 'low' | 'medium' | 'high';
|
|
79
80
|
}
|
|
81
|
+
export type { ResolvedAccessible } from './accessibility.js';
|
|
80
82
|
export interface PrimitivePlan {
|
|
81
83
|
readonly primitive: string;
|
|
82
84
|
/** The node the PARENT adopts. */
|
|
@@ -100,6 +102,14 @@ export interface PrimitivePlan {
|
|
|
100
102
|
readonly gestures: readonly ResolvedGesture[];
|
|
101
103
|
/** Live regions this element declares. At most one, and empty for every primitive but `Text`. */
|
|
102
104
|
readonly announcements: readonly ResolvedAnnouncement[];
|
|
105
|
+
/**
|
|
106
|
+
* Accessible attributes to write through `Gtk.Accessible.update_property()` and
|
|
107
|
+
* `update_state()`, decided here and CALLED one layer up — the same split
|
|
108
|
+
* {@link ResolvedFile} and {@link ResolvedAnnouncement} have, and for the same
|
|
109
|
+
* reason: the call needs `gi://Gtk` enum members and a `GObject.Value`, and
|
|
110
|
+
* nothing under `primitives/` imports `gi://`.
|
|
111
|
+
*/
|
|
112
|
+
readonly accessibility: readonly ResolvedAccessible[];
|
|
103
113
|
/** What a `ref` on this element receives, or null for the widget itself. */
|
|
104
114
|
readonly handle: HandleKind | null;
|
|
105
115
|
/** Where a text child goes, or null when text under this primitive is refused. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { partition, type Partitioned, type StyleProps } from '@gjsify/gtk-host/style';
|
|
2
2
|
import type { StyleTokens } from '@gjsify/gtk-host/style';
|
|
3
|
+
import { type AnimatedValueLike } from '../animated/brand.js';
|
|
3
4
|
import { type ClassGroups, type ClassNameInput } from './classes.js';
|
|
4
5
|
/**
|
|
5
6
|
* A React Native `style` prop.
|
|
@@ -21,6 +22,28 @@ interface StyleInputArray extends ReadonlyArray<StyleInput> {
|
|
|
21
22
|
* exactly like a style that was never written.
|
|
22
23
|
*/
|
|
23
24
|
export declare function flattenStyle(style: StyleInput): StyleObject;
|
|
25
|
+
/**
|
|
26
|
+
* A style → its plain entries and its `Animated.Value`s, split before the partition.
|
|
27
|
+
*
|
|
28
|
+
* L2's and not `components.ts`', because TWO readers need this split and one of them
|
|
29
|
+
* is a PARENT. `Animated.View` needs the animated half to bind; a parent counting its
|
|
30
|
+
* absolutely positioned children needs to be able to READ an
|
|
31
|
+
* `<Animated.View className="absolute" style={{ opacity: value }}>` at all, and
|
|
32
|
+
* `normalise` refuses a non-scalar style value by design (`scalarOnly`). So the raw
|
|
33
|
+
* style handed to `declaresAbsolute` threw where the parent wanted an answer, the
|
|
34
|
+
* throw was swallowed as "not absolute", and the child then got the refusal that names
|
|
35
|
+
* the PARENT for something the parent could not see — #1451, and the reason two
|
|
36
|
+
* finished features (a 160 ms fade and `absolute`) did not compose.
|
|
37
|
+
*
|
|
38
|
+
* Dropping the animated half hides nothing. An `Animated.Value` under a plain `<View>`
|
|
39
|
+
* is still refused BY NAME when that element resolves its own style, which is the
|
|
40
|
+
* element that authored it — the parent's read is a question about somebody else's
|
|
41
|
+
* props and never the place that judges them.
|
|
42
|
+
*/
|
|
43
|
+
export declare function splitAnimatedStyle(style: StyleInput): {
|
|
44
|
+
readonly plain: StyleObject;
|
|
45
|
+
readonly animated: Readonly<Record<string, AnimatedValueLike>>;
|
|
46
|
+
};
|
|
24
47
|
/** What one element authored, before any of it has been interpreted. */
|
|
25
48
|
export interface StyleAuthored {
|
|
26
49
|
readonly className?: ClassNameInput;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AccessibleRoute } from './accessibility.js';
|
|
1
2
|
import type { Orientation, WidgetFacts } from './intents.js';
|
|
2
3
|
/** How a prop's value becomes a GTK value. */
|
|
3
4
|
export type Coercion =
|
|
@@ -29,6 +30,17 @@ export interface PropertyRoute {
|
|
|
29
30
|
readonly names: readonly string[];
|
|
30
31
|
readonly as: Coercion;
|
|
31
32
|
readonly map?: Readonly<Record<string, string | number | boolean>>;
|
|
33
|
+
/**
|
|
34
|
+
* Values this layer recognises and refuses, each with what to do instead.
|
|
35
|
+
*
|
|
36
|
+
* A value simply ABSENT from {@link map} already gets a named refusal listing
|
|
37
|
+
* the known ones, and that is the right answer for a typo. It is the wrong
|
|
38
|
+
* answer for a real React Native spelling with no GTK member:
|
|
39
|
+
* `accessibilityRole="keyboardkey"` is not a typo, and "Known: adjustable,
|
|
40
|
+
* alert, …" tells its author nothing they can act on. A key here answers by
|
|
41
|
+
* name instead, in the voice every {@link RefusedRoute} uses.
|
|
42
|
+
*/
|
|
43
|
+
readonly refuses?: Readonly<Record<string, string>>;
|
|
32
44
|
/** Properties that come with this prop being SET at all, whatever its value. */
|
|
33
45
|
readonly also?: Readonly<Record<string, unknown>>;
|
|
34
46
|
}
|
|
@@ -144,7 +156,7 @@ export interface GestureRoute {
|
|
|
144
156
|
/** The `Gtk.GestureClick` signal — `released` for a completed press. */
|
|
145
157
|
readonly signal: string;
|
|
146
158
|
}
|
|
147
|
-
export type PropRoute = PropertyRoute | StylePropertyRoute | EventRoute | RefusedRoute | IgnoredRoute | FileRoute | GestureRoute | AnnounceRoute;
|
|
159
|
+
export type PropRoute = PropertyRoute | StylePropertyRoute | EventRoute | RefusedRoute | IgnoredRoute | FileRoute | GestureRoute | AnnounceRoute | AccessibleRoute;
|
|
148
160
|
/**
|
|
149
161
|
* The imperative handle a `ref` on this primitive receives, instead of the widget.
|
|
150
162
|
*
|
|
@@ -44,6 +44,35 @@ export declare function acceptsProp(primitive: string, prop: string, variant?: P
|
|
|
44
44
|
* a consumer's test makes and the message is what it prints when it fails.
|
|
45
45
|
*/
|
|
46
46
|
export declare function explainProp(primitive: string, prop: string, variant?: PropVariant): string | null;
|
|
47
|
+
/**
|
|
48
|
+
* The values (or record keys) this prop refuses by name, each with its reason.
|
|
49
|
+
*
|
|
50
|
+
* Empty for most props. `accessibilityRole` is the one that made this necessary: it is
|
|
51
|
+
* an ACCEPTED property route and a handful of React Native's role names have no GTK
|
|
52
|
+
* member, each answered with its own sentence — and until #1555 not one of them was
|
|
53
|
+
* reachable except by rendering it and catching the throw.
|
|
54
|
+
*/
|
|
55
|
+
export declare function propRefusedValues(primitive: string, prop: string, variant?: PropVariant): readonly string[];
|
|
56
|
+
/**
|
|
57
|
+
* Would `<Primitive prop={value}>` render?
|
|
58
|
+
*
|
|
59
|
+
* `false` when the PROP is refused and when this particular VALUE is, which are two
|
|
60
|
+
* different fixes and therefore two different sentences — see {@link explainPropValue}.
|
|
61
|
+
*/
|
|
62
|
+
export declare function acceptsPropValue(primitive: string, prop: string, value: unknown, variant?: PropVariant): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* The sentence a render would print for this prop with THIS value.
|
|
65
|
+
*
|
|
66
|
+
* `null` when it renders. The subject is built here rather than in `answers.ts`, which
|
|
67
|
+
* may hold no relative value import (ADR 0039 § 2, and the generator that imports it
|
|
68
|
+
* under Node type stripping is why) — so the formatter the throw uses lives in
|
|
69
|
+
* `errors.ts` and both call it.
|
|
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.
|
|
74
|
+
*/
|
|
75
|
+
export declare function explainPropValue(primitive: string, prop: string, value: unknown, variant?: PropVariant): string | null;
|
|
47
76
|
/** Every prop name this primitive's table row carries, sorted. Framework props are not among them. */
|
|
48
77
|
export declare function propNames(primitive: string, variant?: PropVariant): readonly string[];
|
|
49
78
|
/**
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
+
/** Where an inner level puts what it wants the window's one header bar to show. */
|
|
3
|
+
export interface ChromeSlot {
|
|
4
|
+
/**
|
|
5
|
+
* Set the owning header bar's title widget, or `null` to give it back.
|
|
6
|
+
*
|
|
7
|
+
* The element needs `slot: 'title'` — it is rendered as a child of the owner's
|
|
8
|
+
* `AdwHeaderBar`, and that is `Adw.HeaderBar.set_title_widget`.
|
|
9
|
+
*/
|
|
10
|
+
setTitleWidget(element: ReactElement | null): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* What a navigator finds at the level it was rendered.
|
|
14
|
+
*
|
|
15
|
+
* There is deliberately no "am I the owner" field: ownership is only ever the answer
|
|
16
|
+
* to "who claims the window's bar", `useChrome` does that itself, and a navigator that
|
|
17
|
+
* read it would be tempted to decide the window controls by it — which is the mistake
|
|
18
|
+
* the header records.
|
|
19
|
+
*/
|
|
20
|
+
export interface Chrome {
|
|
21
|
+
/** A header bar above already carries the window controls, so this level's must not. */
|
|
22
|
+
readonly decorated: boolean;
|
|
23
|
+
/** The enclosing header bar's title slot, or `null` when there is no bar above. */
|
|
24
|
+
readonly titleSlot: ChromeSlot | null;
|
|
25
|
+
}
|
|
26
|
+
/** What a level publishes to the subtree below it. */
|
|
27
|
+
interface ChromeLevel {
|
|
28
|
+
/** A navigator above already claimed the window's header bar. */
|
|
29
|
+
readonly taken: boolean;
|
|
30
|
+
/** A header bar between the window and here carries the window controls. */
|
|
31
|
+
readonly decorated: boolean;
|
|
32
|
+
/** That level's header bar title slot, when the level has a bar at all. */
|
|
33
|
+
readonly titleSlot: ChromeSlot | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Publish a level to everything a navigator renders below it.
|
|
37
|
+
*
|
|
38
|
+
* EVERY navigator has to do this for every screen it renders, including the ones with
|
|
39
|
+
* no bar to contribute to (`titleSlot: null`): without it a nested navigator reads the
|
|
40
|
+
* default and believes it is outermost, and its claim on the window's header bar is
|
|
41
|
+
* then the second one — a refusal, at mount, in an application that was only nesting
|
|
42
|
+
* two navigators.
|
|
43
|
+
*/
|
|
44
|
+
export declare const provideChromeLevel: (level: ChromeLevel, children: ReactNode) => ReactElement;
|
|
45
|
+
/**
|
|
46
|
+
* The level below a header bar: whatever else is true, the controls are accounted for.
|
|
47
|
+
*
|
|
48
|
+
* `decorated: true` unconditionally, and that is arithmetic rather than a choice — the
|
|
49
|
+
* bar either carried the controls (because nothing above it had them) or something
|
|
50
|
+
* above it did.
|
|
51
|
+
*/
|
|
52
|
+
export declare const underHeaderBar: (titleSlot: ChromeSlot | null) => ChromeLevel;
|
|
53
|
+
/** The level below a navigator level that rendered no bar: only ownership changed. */
|
|
54
|
+
export declare const withoutHeaderBar: (decorated: boolean) => ChromeLevel;
|
|
55
|
+
/**
|
|
56
|
+
* What this navigator finds, and the window claim that goes with being outermost.
|
|
57
|
+
*
|
|
58
|
+
* `rendersChrome` is the navigator's own answer to "am I putting a header bar ON SCREEN
|
|
59
|
+
* right now", and the claim depends on it. A `<Tabs headerShown={false}>`, or a stack
|
|
60
|
+
* showing a `headerShown: false` screen, renders none, and taking the window's away
|
|
61
|
+
* would leave the window with NO chrome — an Adwaita window carries no titlebar of its
|
|
62
|
+
* own, so that is a window that cannot be closed. Which is also why `decorated` is TRUE
|
|
63
|
+
* in that case: the window's own bar is still there, carrying the controls, and a bar
|
|
64
|
+
* further down must not carry a second set.
|
|
65
|
+
*
|
|
66
|
+
* ON SCREEN, not "somewhere in this navigator": only mapped bars draw, so a claim held
|
|
67
|
+
* for a bar on a page further down the stack is a claim against nothing. What that
|
|
68
|
+
* measured, and which pages count, is in `stack.ts` at the call.
|
|
69
|
+
*
|
|
70
|
+
* The claim is a layout EFFECT with an undo, so a navigator that unmounts gives the
|
|
71
|
+
* window its bar back — which is what makes a re-render, a route change and a
|
|
72
|
+
* hot-reloaded root layout safe rather than one-shot.
|
|
73
|
+
*/
|
|
74
|
+
export declare function useChrome(navigator: string, rendersChrome: boolean): Chrome;
|
|
75
|
+
/** One title slot per header bar, plus whatever has been contributed to each. */
|
|
76
|
+
export interface TitleSlots {
|
|
77
|
+
/** The widget contributed for this key, or `null` while nothing has been. */
|
|
78
|
+
titleWidgetFor(key: string): ReactElement | null;
|
|
79
|
+
/** The slot for this key, with an identity that survives every re-render. */
|
|
80
|
+
slotFor(key: string): ChromeSlot;
|
|
81
|
+
/** Drop the slots of every bar this navigator no longer renders. */
|
|
82
|
+
retain(keys: ReadonlySet<string>): void;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The contributions a navigator's header bars hold, keyed by bar.
|
|
86
|
+
*
|
|
87
|
+
* The slots are CACHED by key for the reason `usePageBindings` records one file over:
|
|
88
|
+
* a fresh object per render would make the context value change on every commit and
|
|
89
|
+
* re-run every consumer's effect, which for this consumer means clearing and
|
|
90
|
+
* re-setting the contribution in a loop.
|
|
91
|
+
*
|
|
92
|
+
* Cached and therefore PRUNED, through the one lifecycle `per-route-cache.ts` owns:
|
|
93
|
+
* the key is a route key, of which React Navigation mints a fresh one per pushed
|
|
94
|
+
* instance, so an unpruned map grows with the navigation history (#1547). `held` and
|
|
95
|
+
* `widgets` need no prune of their own — a contributor's effect cleanup clears both on
|
|
96
|
+
* the way out — and `retain` is the caller's, because only the navigator knows which
|
|
97
|
+
* bars it still renders.
|
|
98
|
+
*
|
|
99
|
+
* A SECOND live contribution to one bar REFUSES. The cleanup in a contributor's
|
|
100
|
+
* effect clears the slot before it re-runs, so the same contributor re-setting is
|
|
101
|
+
* never the occupied case — an occupied slot means two navigators are pointing at one
|
|
102
|
+
* title, and a silent overwrite would show one of them and lose the other with no
|
|
103
|
+
* message anywhere.
|
|
104
|
+
*/
|
|
105
|
+
export declare function useTitleSlots(navigator: string): TitleSlots;
|
|
106
|
+
export {};
|
|
@@ -25,7 +25,9 @@ export type RouterErrorCode =
|
|
|
25
25
|
/** An `options` key the navigator has no GTK answer for. */
|
|
26
26
|
| 'unknown-screen-option'
|
|
27
27
|
/** A navigator rendered outside the router, so it has no route node to build from. */
|
|
28
|
-
| 'no-route-node'
|
|
28
|
+
| 'no-route-node'
|
|
29
|
+
/** Two navigators inside one screen both contributing to its header bar's title. */
|
|
30
|
+
| 'chrome-taken';
|
|
29
31
|
/** A routing input, file name or call this layer cannot answer for, and why. */
|
|
30
32
|
export declare class RouterError extends Error {
|
|
31
33
|
readonly name = "RouterError";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** How many per-route cache entries the process is holding right now. */
|
|
2
|
+
export declare const perRouteCacheEntries: () => number;
|
|
3
|
+
/** A `Map` keyed by route key that is pruned to the pages a navigator still renders. */
|
|
4
|
+
export declare class PerRouteCache<T> {
|
|
5
|
+
private readonly held;
|
|
6
|
+
/** The value for `key`, made once and kept — the identity every caller depends on. */
|
|
7
|
+
getOrCreate(key: string, make: () => T): T;
|
|
8
|
+
/** Replace what is held for `key`. For values a render recomputes, like screen options. */
|
|
9
|
+
set(key: string, value: T): void;
|
|
10
|
+
get(key: string): T | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Drop every key that is not in `keys`. The whole lifecycle, in one call.
|
|
13
|
+
*
|
|
14
|
+
* Deleting the key the loop is standing on is defined behaviour for a `Map`
|
|
15
|
+
* iterator — no snapshot is needed, and taking one would only hide that.
|
|
16
|
+
*/
|
|
17
|
+
retain(keys: ReadonlySet<string>): void;
|
|
18
|
+
/** Drop everything. What an unmounting navigator owes the count. */
|
|
19
|
+
clear(): void;
|
|
20
|
+
get size(): number;
|
|
21
|
+
}
|
|
@@ -5,7 +5,12 @@ import { type ScreenDeclaration } from './screens.js';
|
|
|
5
5
|
export interface StackScreenOptions {
|
|
6
6
|
/** The page title, shown by the page's own header bar. Defaults to the route name. */
|
|
7
7
|
title?: string;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* `false` renders the screen with no `Adw.HeaderBar`, and so with no back button.
|
|
10
|
+
*
|
|
11
|
+
* It also leaves the level below with no title slot to contribute to, so a
|
|
12
|
+
* navigator inside this screen renders its own bar — see `chrome.ts`.
|
|
13
|
+
*/
|
|
9
14
|
headerShown?: boolean;
|
|
10
15
|
/** `none` turns the transition off for this screen only. */
|
|
11
16
|
animation?: 'default' | 'none';
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import { type ReactElement, type ReactNode } from 'react';
|
|
2
|
-
import
|
|
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.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createElement, mount } from '@gjsify/gtk-host/solid';
|
|
2
|
+
import type { AccessibilityProps } from '../primitives/accessibility.js';
|
|
2
3
|
import type { ClassNameInput } from '../primitives/classes.js';
|
|
3
4
|
import { type TextInputHandle } from '../primitives/handles.js';
|
|
4
5
|
import type { StyleInput } from '../primitives/style.js';
|
|
@@ -14,7 +15,7 @@ type HostNode = ReturnType<typeof createElement>;
|
|
|
14
15
|
export type PrimitiveChild = HostNode | string | number | boolean | null | undefined;
|
|
15
16
|
export type PrimitiveChildren = PrimitiveChild | readonly PrimitiveChildren[];
|
|
16
17
|
/** What every primitive accepts on top of its own props. */
|
|
17
|
-
export interface CommonProps {
|
|
18
|
+
export interface CommonProps extends AccessibilityProps {
|
|
18
19
|
className?: ClassNameInput;
|
|
19
20
|
style?: StyleInput;
|
|
20
21
|
children?: PrimitiveChildren;
|