create-octane-xplat 0.7.3 → 0.10.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 +28 -8
- package/index.mjs +112 -14
- package/package.json +15 -8
- package/scaffold.mjs +531 -0
- package/template/.agents/skills/xplat/SKILL.md +109 -40
- package/template/.agents/skills/xplat/references/components.md +149 -96
- package/template/.agents/skills/xplat/references/navigation.md +81 -31
- package/template/.agents/skills/xplat/references/platform.md +74 -35
- package/template/.agents/skills/xplat/references/styling.md +80 -27
- package/template/AGENTS.md +1 -1
- package/template/package.json +11 -9
- package/template/pnpm-workspace.yaml +66 -33
- package/template/src/App.tsrx +17 -8
- package/template/src/process.d.ts +7 -0
- package/template/src/style.css +24 -6
- package/template/tsconfig.json +1 -1
- package/template/vite.config.native.mts +3 -2
- package/template/vite.config.ts +5 -3
- package/template/App_Resources/iOS/src/OctaneShaders.metal +0 -128
- package/template/App_Resources/iOS/src/ShaderEffectRegistration.swift +0 -31
- package/template/patches/@nativescript-community__octane@0.2.1.patch +0 -16
- package/template/patches/@nativescript-community__vite-octane@0.2.1.patch +0 -41
- package/template/patches/@nativescript__core@9.1.2.patch +0 -794
- package/template/patches/@nativescript__core@9.1.3-next.2.patch +0 -428
- package/template/patches/@nativescript__vite@8.0.11.patch +0 -569
- package/template/patches/@nativescript__vite@8.0.13.patch +0 -233
- package/template/patches/@tsrx__typescript-plugin@0.4.11.patch +0 -11
- package/template/patches/css@3.0.0.patch +0 -52
- package/template/patches/octane@0.6.3.patch +0 -1149
- /package/template/src/{index.ts → main.ts} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: xplat
|
|
3
|
-
description: Build and review code in an Octane
|
|
3
|
+
description: Build and review code in an Octane Xplat app — one TypeScript codebase targeting web (DOM) and iOS/Android (NativeScript). Use when writing components, styles, routes, platform leaves, or device-service calls, and when a feature works on one target but not another.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Octane
|
|
6
|
+
# Octane Xplat app
|
|
7
7
|
|
|
8
8
|
Every `src/*.tsrx` file compiles twice: once to the DOM renderer, once to
|
|
9
9
|
NativeScript views. The common failure mode is **silent divergence** — code
|
|
@@ -12,53 +12,122 @@ below prevent that; `pnpm lint` enforces most of them.
|
|
|
12
12
|
|
|
13
13
|
## Non-negotiables
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
15
|
+
**One element vocabulary per file.** Shared code renders only
|
|
16
|
+
`@octane-xplat/ui` components. Platform divergence lives in whole files
|
|
17
|
+
— `.web` for browser code, `.mobile` for shared iOS/Android variants, OS-specific suffixes, and the unsuffixed native default —
|
|
18
|
+
chosen by the bundler when something imports `./Foo`. Never branch on
|
|
19
|
+
platform inside JSX (`Platform.OS`, conditional imports, checks for browser globals). Platform-authentic widgets live behind
|
|
20
|
+
`@octane-xplat/ui/ios`, `/android`, and `/web` — importing a subpath
|
|
21
|
+
outside a matching suffix file fails the other platform's build (the
|
|
22
|
+
`xplat/platform-subpath-import` lint enforces it).
|
|
23
|
+
**No DOM globals in shared code.** `document`, `window`, `localStorage`,
|
|
24
|
+
DOM events — all web-only. Device capabilities (clipboard, storage,
|
|
25
|
+
permissions, connectivity) come from
|
|
26
|
+
`@octane-xplat/platform`; plugin-backed features use their leaf packages.
|
|
27
|
+
**Styles:** `className` for anything static; `style` objects only for
|
|
28
|
+
values that change while the app runs. Shared CSS must use the portable
|
|
29
|
+
subset — the native build warns on declarations it drops
|
|
30
|
+
(`position: fixed`, `margin: auto`, `box-shadow`, …).
|
|
31
|
+
**Hooks and JSX live in `.tsrx` (or `.tsx`).** Plain `.ts` is for
|
|
32
|
+
non-component code and may not import `.tsrx`. Import `.tsrx` files with
|
|
33
|
+
the explicit extension: `import { App } from './App.tsrx'`.
|
|
34
|
+
**Keep signal names suffixed with `$`.** Use `useSignal$` for state owned
|
|
35
|
+
by a component. Runtime signal imports let the compiler track reads.
|
|
36
|
+
|
|
37
|
+
```tsrx
|
|
38
|
+
import { useSignal$ } from 'octane/signals/client'
|
|
39
|
+
import { Pressable, Text } from '@octane-xplat/ui'
|
|
40
|
+
|
|
41
|
+
export function Counter() {
|
|
42
|
+
const count$ = useSignal$(0)
|
|
43
|
+
return <Pressable onPress={() => count$.set((value) => value + 1)}>
|
|
44
|
+
<Text>{count$.get()}</Text>
|
|
45
|
+
</Pressable>
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Use `signal$` for shared module state and `derived$` for a computed value.
|
|
50
|
+
Write from event handlers, not while rendering.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { signal$, derived$ } from 'octane/signals'
|
|
54
|
+
|
|
55
|
+
export const travelers$ = signal$(1)
|
|
56
|
+
export const seatsRemaining$ = derived$(() => 4 - travelers$.get())
|
|
57
|
+
export function addTraveler() {
|
|
58
|
+
travelers$.set((count) => count + 1)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`query$` loads asynchronous data. A screen-owned query keeps its selection
|
|
63
|
+
local to the screen. `.latest()` reads without suspending; use the data
|
|
64
|
+
guide's boundary pattern when reading with `.get()`.
|
|
65
|
+
|
|
66
|
+
```tsrx
|
|
67
|
+
import { query$ } from 'octane/signals'
|
|
68
|
+
import { Text } from '@octane-xplat/ui'
|
|
69
|
+
|
|
70
|
+
export function TripTitle(props: { id: string; loadTitle: (id: string) => Promise<string> }) {
|
|
71
|
+
const title$ = query$(() => props.id, (id) => props.loadTitle(id))
|
|
72
|
+
return <Text>{title$.latest() ?? 'Loading trip…'}</Text>
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**One `octane` per app.** Don't add a second renderer or duplicate the
|
|
77
|
+
package — two copies break the reconciler without a helpful error.
|
|
78
|
+
|
|
79
|
+
For component, routing, service, and styling usage, use the references below.
|
|
80
|
+
They show the supported names and matching code examples.
|
|
47
81
|
|
|
48
82
|
## Compiler rules that surprise
|
|
49
83
|
|
|
50
84
|
- `.tsrx` files can't declare `async` top-level functions — the compiler
|
|
51
85
|
treats them as async components and the native build fails.
|
|
52
|
-
- Literal `@{` in JSX text parses as a code block.
|
|
53
|
-
|
|
86
|
+
- Literal `@{` in JSX text parses as a code block. Render the sign separately:
|
|
87
|
+
|
|
88
|
+
```tsrx
|
|
89
|
+
import { Text } from '@octane-xplat/ui'
|
|
90
|
+
|
|
91
|
+
export function Handle() { return <Text>{'@'}{'traveler'}</Text> }
|
|
92
|
+
```
|
|
93
|
+
|
|
54
94
|
- Effect dependencies are inferred from closure reads. An effect that only
|
|
55
95
|
writes (refs, DOM) and never reads its driving prop won't re-run —
|
|
56
|
-
declare
|
|
57
|
-
|
|
58
|
-
|
|
96
|
+
declare dependencies explicitly when needed:
|
|
97
|
+
|
|
98
|
+
```tsrx
|
|
99
|
+
import { useLayoutEffect } from 'octane'
|
|
100
|
+
import { Text } from '@octane-xplat/ui'
|
|
101
|
+
|
|
102
|
+
export function Progress(props: { percent: number }) {
|
|
103
|
+
useLayoutEffect(() => { console.log('Progress changed') }, [props.percent])
|
|
104
|
+
return <Text>{props.percent}%</Text>
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- Input handlers must be idempotent: store the supplied value rather than
|
|
109
|
+
incrementing a counter for each input event. Web may dispatch more than once.
|
|
110
|
+
|
|
111
|
+
```tsrx
|
|
112
|
+
import { useState } from 'octane'
|
|
113
|
+
import { TextInput } from '@octane-xplat/ui'
|
|
114
|
+
|
|
115
|
+
export function NameField() {
|
|
116
|
+
const [name, setName] = useState('')
|
|
117
|
+
return <TextInput value={name} onChange={setName} accessibilityLabel="Name" />
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
59
121
|
- `.mobile.tsrx`/`.ios.tsrx`/`.android.tsrx` files and unsuffixed native-default files containing JSX must
|
|
60
122
|
start with `/** @jsxImportSource @nativescript-community/octane */` on
|
|
61
|
-
line 1 — nothing may precede it
|
|
123
|
+
line 1 — nothing may precede it:
|
|
124
|
+
|
|
125
|
+
```tsrx
|
|
126
|
+
/** @jsxImportSource @nativescript-community/octane */
|
|
127
|
+
import { Text } from '@octane-xplat/ui'
|
|
128
|
+
|
|
129
|
+
export function NativeGreeting() { return <Text>Hello</Text> }
|
|
130
|
+
```
|
|
62
131
|
|
|
63
132
|
## Verify
|
|
64
133
|
|
|
@@ -1,98 +1,151 @@
|
|
|
1
1
|
# Components — the vocabulary
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
3
|
+
Use shared components for screens that work across targets. Put these examples
|
|
4
|
+
in `.tsrx` files; style their classes in the app's CSS.
|
|
5
|
+
|
|
6
|
+
```tsrx
|
|
7
|
+
import { VStack, HStack, Text, Pressable } from '@octane-xplat/ui'
|
|
8
|
+
|
|
9
|
+
export function Welcome() {
|
|
10
|
+
return <VStack className="welcome">
|
|
11
|
+
<Text>Plan your next trip</Text>
|
|
12
|
+
<HStack><Pressable onPress={() => console.log('Start planning')}>
|
|
13
|
+
<Text>Start planning</Text>
|
|
14
|
+
</Pressable></HStack>
|
|
15
|
+
</VStack>
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Shared component inventory
|
|
20
|
+
|
|
21
|
+
| Need | Component(s) |
|
|
22
|
+
| --------------------- | ----------------------------------------------------------------------------- |
|
|
23
|
+
| Layout | `View`, `VStack`, `HStack`, `Grid`, `Stack`, `Absolute`, `Spacer`, `SafeArea` |
|
|
24
|
+
| Text | `Text`, `Heading`, `RichText`, `RichTextSpan` |
|
|
25
|
+
| Tap | `Pressable`, `Button`, `Link`, `NavLink` |
|
|
26
|
+
| Input | `TextInput`, `TextArea`, `Switch`, `Slider` |
|
|
27
|
+
| Scrolling | `ScrollableArea`, `VirtualList` |
|
|
28
|
+
| Media | `Image`, `Icon` |
|
|
29
|
+
| Feedback | `Spinner`, `Meter`, `showToast` |
|
|
30
|
+
| Overlays | `BottomSheet`, `Overlay`, `Popover`, `Drawer`, `HoverCard`, `Tooltip` |
|
|
31
|
+
| Shells | `Screen`, `Tabs` |
|
|
32
|
+
| Theme and measurement | `useColorScheme`, `useSafeAreaInsets`, `useMeasure` |
|
|
33
|
+
|
|
34
|
+
`ScrollableArea` scrolls a bounded set of children. For a long list, use
|
|
35
|
+
`VirtualList` so the app does not mount every row at once.
|
|
36
|
+
|
|
37
|
+
```tsrx
|
|
38
|
+
import { ScrollableArea, VirtualList, Text } from '@octane-xplat/ui'
|
|
39
|
+
|
|
40
|
+
const destinations = [{ id: 'oslo', name: 'Oslo' }, { id: 'rome', name: 'Rome' }]
|
|
41
|
+
|
|
42
|
+
export function Destinations() {
|
|
43
|
+
return <>
|
|
44
|
+
<ScrollableArea><Text>Choose your next destination</Text></ScrollableArea>
|
|
45
|
+
<VirtualList items={destinations} keyExtractor={(item) => item.id}
|
|
46
|
+
renderItem={(item) => <Text>{item.name}</Text>} />
|
|
47
|
+
</>
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Text fields keep the platform's keyboard, caret, selection, and input methods.
|
|
52
|
+
Use `onChange` to store the new value. `KeyboardAvoiding` helps keep the field
|
|
53
|
+
visible when the keyboard opens.
|
|
54
|
+
|
|
55
|
+
```tsrx
|
|
56
|
+
import { useState } from 'octane'
|
|
57
|
+
import { KeyboardAvoiding, TextInput } from '@octane-xplat/ui'
|
|
58
|
+
|
|
59
|
+
export function TravelerName() {
|
|
60
|
+
const [name, setName] = useState('')
|
|
61
|
+
return <KeyboardAvoiding>
|
|
62
|
+
<TextInput accessibilityLabel="Traveler name" placeholder="Your name"
|
|
63
|
+
value={name} onChange={setName} />
|
|
64
|
+
</KeyboardAvoiding>
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`TextArea` supports multiline text. Its submit shortcut is Cmd/Ctrl+Enter on
|
|
69
|
+
web; native submits with `returnKeyType="done"` or `"send"`.
|
|
70
|
+
|
|
71
|
+
```tsrx
|
|
72
|
+
import { useState } from 'octane'
|
|
73
|
+
import { TextArea } from '@octane-xplat/ui'
|
|
74
|
+
|
|
75
|
+
export function TravelNotes() {
|
|
76
|
+
const [notes, setNotes] = useState('')
|
|
77
|
+
return <TextArea value={notes} onChange={setNotes} returnKeyType="done"
|
|
78
|
+
onSubmit={() => console.log(notes)} accessibilityLabel="Travel notes" />
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`BottomSheet` opens above the current screen. Keep its open state in your
|
|
83
|
+
component and give it a label that explains its purpose.
|
|
84
|
+
|
|
85
|
+
```tsrx
|
|
86
|
+
import { useState } from 'octane'
|
|
87
|
+
import { BottomSheet, Pressable, Text } from '@octane-xplat/ui'
|
|
88
|
+
|
|
89
|
+
export function TripHelp() {
|
|
90
|
+
const [open, setOpen] = useState(false)
|
|
91
|
+
return <>
|
|
92
|
+
<Pressable onPress={() => setOpen(true)}><Text>Trip help</Text></Pressable>
|
|
93
|
+
<BottomSheet label="Trip help" isOpen={open} onOpenChange={setOpen}>
|
|
94
|
+
<Text>You can change your destination later.</Text>
|
|
95
|
+
</BottomSheet>
|
|
96
|
+
</>
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`RichTextSpan` gives individual text runs their own style or press handler.
|
|
101
|
+
Native uses formatted text runs for these spans.
|
|
102
|
+
|
|
103
|
+
```tsrx
|
|
104
|
+
import { RichText, RichTextSpan } from '@octane-xplat/ui'
|
|
105
|
+
|
|
106
|
+
export function TripStatus() {
|
|
107
|
+
return <RichText>
|
|
108
|
+
<RichTextSpan text="Your trip is " />
|
|
109
|
+
<RichTextSpan text="ready" className="status-ready"
|
|
110
|
+
onPress={() => console.log('Show trip')} />
|
|
111
|
+
</RichText>
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Platform widgets
|
|
116
|
+
|
|
117
|
+
Choose platform widgets when you want the OS's own controls. Import them only
|
|
118
|
+
in the matching platform file. This iOS example belongs in `Preference.ios.tsrx`:
|
|
119
|
+
|
|
120
|
+
```tsrx
|
|
121
|
+
/** @jsxImportSource @nativescript-community/octane */
|
|
122
|
+
import { useState } from 'octane'
|
|
123
|
+
import { UISwitch } from '@octane-xplat/ui/ios'
|
|
124
|
+
|
|
125
|
+
export function Preference() {
|
|
126
|
+
const [checked, setChecked] = useState(false)
|
|
127
|
+
return <UISwitch checked={checked} onCheckedChange={setChecked} />
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The Android equivalent belongs in `Preference.android.tsrx`:
|
|
132
|
+
|
|
133
|
+
```tsrx
|
|
134
|
+
/** @jsxImportSource @nativescript-community/octane */
|
|
135
|
+
import { useState } from 'octane'
|
|
136
|
+
import { MaterialSwitch } from '@octane-xplat/ui/android'
|
|
137
|
+
|
|
138
|
+
export function Preference() {
|
|
139
|
+
const [checked, setChecked] = useState(false)
|
|
140
|
+
return <MaterialSwitch checked={checked} onCheckedChange={setChecked} />
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
| Platform | Additional widget names |
|
|
145
|
+
| -------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
146
|
+
| iOS | `UISlider`, `UIActivityIndicatorView`, `UITableView`, `UITabBar`, `UIModal`, `SideDrawer`, `LiquidGlass`, `LiquidGlassContainer` |
|
|
147
|
+
| Android | `SeekBar`, `CircularProgressIndicator`, `RecyclerView`, `BottomNavigationView`, `MaterialDialog`, `DrawerLayout` |
|
|
148
|
+
|
|
149
|
+
Keep platform lists outside other scrolling containers. Use a layout container
|
|
150
|
+
around the list; see the [components guide](https://octane-xplat.goddardai.org/components)
|
|
151
|
+
for overlay, hover, tab, and accessibility examples and their platform limits.
|
|
@@ -1,33 +1,83 @@
|
|
|
1
1
|
# Navigation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
3
|
+
Register screens once during app startup. A route names a screen and a stack;
|
|
4
|
+
web turns it into a URL, while native presents the screen in the app's navigation.
|
|
5
|
+
Put components in `.tsrx` files.
|
|
6
|
+
|
|
7
|
+
```tsrx
|
|
8
|
+
import { defineRoutes, registerRoutes, Text } from '@octane-xplat/ui'
|
|
9
|
+
|
|
10
|
+
function Home() { return <Text>Home</Text> }
|
|
11
|
+
function Settings() { return <Text>Settings</Text> }
|
|
12
|
+
|
|
13
|
+
registerRoutes(defineRoutes([
|
|
14
|
+
{ path: '/', screen: Home },
|
|
15
|
+
{ path: '/settings', screen: Settings },
|
|
16
|
+
]))
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
After registering `settings`, use `NavLink` for a screen link or `pushRoute`
|
|
20
|
+
in an event handler. `Link` accepts a URL, including an external website.
|
|
21
|
+
|
|
22
|
+
```tsrx
|
|
23
|
+
import { HStack, Link, NavLink, Pressable, Text, pushRoute } from '@octane-xplat/ui'
|
|
24
|
+
|
|
25
|
+
export function SettingsLinks() {
|
|
26
|
+
const settings = { stack: 'root', name: 'settings', params: {} }
|
|
27
|
+
return <HStack>
|
|
28
|
+
<NavLink route={settings}><Text>Settings</Text></NavLink>
|
|
29
|
+
<Pressable onPress={() => pushRoute(settings)}><Text>Open settings</Text></Pressable>
|
|
30
|
+
<Link href="https://octane-xplat.goddardai.org"><Text>Documentation</Text></Link>
|
|
31
|
+
</HStack>
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `useRoute` to read the current screen and `useCanGoBack` to decide whether
|
|
36
|
+
to offer a back button. Pass the same stack name to `popRoute`.
|
|
37
|
+
|
|
38
|
+
```tsrx
|
|
39
|
+
import { Pressable, Text, useRoute, useCanGoBack, popRoute } from '@octane-xplat/ui'
|
|
40
|
+
|
|
41
|
+
export function NavigationStatus() {
|
|
42
|
+
const route = useRoute('root')
|
|
43
|
+
const canGoBack = useCanGoBack('root')
|
|
44
|
+
return <>
|
|
45
|
+
<Text>{route?.name ?? 'Home'}</Text>
|
|
46
|
+
<Pressable disabled={!canGoBack} onPress={() => popRoute('root')}>
|
|
47
|
+
<Text>Back</Text>
|
|
48
|
+
</Pressable>
|
|
49
|
+
</>
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Prefer string IDs in route params: generated route helpers use string params,
|
|
54
|
+
and IDs make URLs shareable. The low-level router JSON-encodes object params
|
|
55
|
+
on web with a warning; do not rely on it to carry application state. With a
|
|
56
|
+
registered `/projects/:id` screen, navigate using the project ID:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { pushRoute } from '@octane-xplat/ui'
|
|
60
|
+
|
|
61
|
+
pushRoute({ stack: 'root', name: 'projects/:id', params: { id: '42' } })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Named tab stacks keep each tab's navigation separate. `Tabs` renders the tab's
|
|
65
|
+
initial content; pushing a route into that stack opens it inside the tab pane.
|
|
66
|
+
The screen below assumes `settings` is already registered.
|
|
67
|
+
|
|
68
|
+
```tsrx
|
|
69
|
+
import { Tabs, Text, Pressable, pushRoute } from '@octane-xplat/ui'
|
|
70
|
+
|
|
71
|
+
export function AppTabs() {
|
|
72
|
+
return <Tabs tabs={[
|
|
73
|
+
{ title: 'Home', stack: 'home', render: () => <Text>Home</Text> },
|
|
74
|
+
{ title: 'Account', stack: 'account', render: () =>
|
|
75
|
+
<Pressable onPress={() => pushRoute({ stack: 'account', name: 'settings', params: {} })}>
|
|
76
|
+
<Text>Settings</Text>
|
|
77
|
+
</Pressable> },
|
|
78
|
+
]} />
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For loaders, deep links, route config, and generated route types, see the
|
|
83
|
+
[navigation guide](https://octane-xplat.goddardai.org/navigation).
|