@ruby-native/react 0.10.14 → 0.10.20
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 +25 -0
- package/index.d.ts +180 -0
- package/index.js +166 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -39,18 +39,43 @@ Each component renders a hidden `data-native-*` signal element that the Ruby Nat
|
|
|
39
39
|
- `NativeNavbar` - native navigation bar with title and buttons
|
|
40
40
|
- `NativeButton` - native nav bar button (icon, title, href, or click target)
|
|
41
41
|
- `NativeMenuItem` - item inside a native menu
|
|
42
|
+
- `NativeShareButton` - native nav bar button that opens the share sheet
|
|
43
|
+
- `NativeShareMenuItem` - menu item that opens the share sheet
|
|
42
44
|
- `NativeSubmitButton` - native "Save" button that submits a form
|
|
45
|
+
- `NativeFab` - floating action button
|
|
43
46
|
- `NativeOverscroll` - per-page overscroll colors
|
|
44
47
|
- `NativeBadge` - set the badge count on a home screen or tab bar icon
|
|
48
|
+
- `NativeBackButton` - visible button that pops the native navigation stack
|
|
49
|
+
|
|
50
|
+
`NativeBackButton` renders a chevron unless you pass children, and forwards extra props to the underlying `<button>`. An `onClick` handler runs first and can call `preventDefault()` to cancel the back:
|
|
51
|
+
|
|
52
|
+
```jsx
|
|
53
|
+
<NativeBackButton className="mr-2" aria-label="Go back" />
|
|
54
|
+
```
|
|
45
55
|
|
|
46
56
|
## Helpers
|
|
47
57
|
|
|
58
|
+
- `nativePlatform()` - returns `"ios"`, `"android"`, or `null` on the web. The counterpart of the `native_platform` Rails helper.
|
|
48
59
|
- `nativeHaptic(feedback = "success", data = {})` - returns props to spread onto a clickable element so tapping it triggers native haptic feedback:
|
|
49
60
|
|
|
50
61
|
```jsx
|
|
51
62
|
<button {...nativeHaptic("success")}>Save</button>
|
|
52
63
|
```
|
|
53
64
|
|
|
65
|
+
## TypeScript
|
|
66
|
+
|
|
67
|
+
Type declarations ship with the package. There is nothing to install and no `@types` companion package. Importing a component gives you autocomplete and prop checking:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { NativeNavbar, NativeButton } from "@ruby-native/react"
|
|
71
|
+
|
|
72
|
+
<NativeNavbar title="Books" pullToRefresh={false}>
|
|
73
|
+
<NativeButton position="leading" icon="chevron.left" href="/books" />
|
|
74
|
+
</NativeNavbar>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The `NativeIcons`, `NativeButtonPosition`, and `NativeHapticFeedback` types are exported too.
|
|
78
|
+
|
|
54
79
|
## Docs
|
|
55
80
|
|
|
56
81
|
Full guides at [rubynative.com/docs](https://rubynative.com/docs).
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns "ios" or "android" inside a Ruby Native app, and null on the web.
|
|
3
|
+
* The counterpart of the `native_platform` Rails helper.
|
|
4
|
+
* @returns {"ios" | "android" | null}
|
|
5
|
+
*/
|
|
6
|
+
export function nativePlatform(): "ios" | "android" | null;
|
|
7
|
+
/**
|
|
8
|
+
* @param {object} props
|
|
9
|
+
* @param {boolean} [props.enabled]
|
|
10
|
+
* @returns {import("react").ReactElement | null}
|
|
11
|
+
*/
|
|
12
|
+
export function NativeTabs({ enabled }: {
|
|
13
|
+
enabled?: boolean | undefined;
|
|
14
|
+
}): import("react").ReactElement | null;
|
|
15
|
+
/**
|
|
16
|
+
* @returns {import("react").ReactElement}
|
|
17
|
+
*/
|
|
18
|
+
export function NativePush(): import("react").ReactElement;
|
|
19
|
+
/**
|
|
20
|
+
* @returns {import("react").ReactElement}
|
|
21
|
+
*/
|
|
22
|
+
export function NativeForm(): import("react").ReactElement;
|
|
23
|
+
/**
|
|
24
|
+
* @returns {import("react").ReactElement}
|
|
25
|
+
*/
|
|
26
|
+
export function NativeReview(): import("react").ReactElement;
|
|
27
|
+
/**
|
|
28
|
+
* @param {object} props
|
|
29
|
+
* @param {string} [props.title]
|
|
30
|
+
* @param {boolean} [props.pullToRefresh]
|
|
31
|
+
* @param {import("react").ReactNode} [props.children]
|
|
32
|
+
* @returns {import("react").ReactElement}
|
|
33
|
+
*/
|
|
34
|
+
export function NativeNavbar({ title, pullToRefresh, children }: {
|
|
35
|
+
title?: string | undefined;
|
|
36
|
+
pullToRefresh?: boolean | undefined;
|
|
37
|
+
children?: import("react").ReactNode;
|
|
38
|
+
}): import("react").ReactElement;
|
|
39
|
+
/**
|
|
40
|
+
* @param {object} props
|
|
41
|
+
* @param {NativeButtonPosition} [props.position]
|
|
42
|
+
* @param {string} [props.icon]
|
|
43
|
+
* @param {NativeIcons} [props.icons]
|
|
44
|
+
* @param {string} [props.title]
|
|
45
|
+
* @param {string} [props.href]
|
|
46
|
+
* @param {string} [props.click]
|
|
47
|
+
* @param {boolean} [props.selected]
|
|
48
|
+
* @param {import("react").ReactNode} [props.children]
|
|
49
|
+
* @returns {import("react").ReactElement}
|
|
50
|
+
*/
|
|
51
|
+
export function NativeButton({ position, icon, icons, title, href, click, selected, children }: {
|
|
52
|
+
position?: NativeButtonPosition | undefined;
|
|
53
|
+
icon?: string | undefined;
|
|
54
|
+
icons?: NativeIcons | undefined;
|
|
55
|
+
title?: string | undefined;
|
|
56
|
+
href?: string | undefined;
|
|
57
|
+
click?: string | undefined;
|
|
58
|
+
selected?: boolean | undefined;
|
|
59
|
+
children?: import("react").ReactNode;
|
|
60
|
+
}): import("react").ReactElement;
|
|
61
|
+
/**
|
|
62
|
+
* @param {object} props
|
|
63
|
+
* @param {string} [props.title]
|
|
64
|
+
* @param {string} [props.href]
|
|
65
|
+
* @param {string} [props.click]
|
|
66
|
+
* @param {string} [props.icon]
|
|
67
|
+
* @param {NativeIcons} [props.icons]
|
|
68
|
+
* @param {boolean} [props.selected]
|
|
69
|
+
* @returns {import("react").ReactElement}
|
|
70
|
+
*/
|
|
71
|
+
export function NativeMenuItem({ title, href, click, icon, icons, selected }: {
|
|
72
|
+
title?: string | undefined;
|
|
73
|
+
href?: string | undefined;
|
|
74
|
+
click?: string | undefined;
|
|
75
|
+
icon?: string | undefined;
|
|
76
|
+
icons?: NativeIcons | undefined;
|
|
77
|
+
selected?: boolean | undefined;
|
|
78
|
+
}): import("react").ReactElement;
|
|
79
|
+
/**
|
|
80
|
+
* @param {object} props
|
|
81
|
+
* @param {NativeButtonPosition} [props.position]
|
|
82
|
+
* @param {string} [props.title]
|
|
83
|
+
* @param {string} [props.icon]
|
|
84
|
+
* @param {NativeIcons} [props.icons]
|
|
85
|
+
* @param {string} [props.url]
|
|
86
|
+
* @returns {import("react").ReactElement}
|
|
87
|
+
*/
|
|
88
|
+
export function NativeShareButton({ position, title, icon, icons, url }: {
|
|
89
|
+
position?: NativeButtonPosition | undefined;
|
|
90
|
+
title?: string | undefined;
|
|
91
|
+
icon?: string | undefined;
|
|
92
|
+
icons?: NativeIcons | undefined;
|
|
93
|
+
url?: string | undefined;
|
|
94
|
+
}): import("react").ReactElement;
|
|
95
|
+
/**
|
|
96
|
+
* @param {object} props
|
|
97
|
+
* @param {string} [props.title]
|
|
98
|
+
* @param {string} [props.url]
|
|
99
|
+
* @param {string} [props.icon]
|
|
100
|
+
* @param {NativeIcons} [props.icons]
|
|
101
|
+
* @param {boolean} [props.selected]
|
|
102
|
+
* @returns {import("react").ReactElement}
|
|
103
|
+
*/
|
|
104
|
+
export function NativeShareMenuItem({ title, url, icon, icons, selected }: {
|
|
105
|
+
title?: string | undefined;
|
|
106
|
+
url?: string | undefined;
|
|
107
|
+
icon?: string | undefined;
|
|
108
|
+
icons?: NativeIcons | undefined;
|
|
109
|
+
selected?: boolean | undefined;
|
|
110
|
+
}): import("react").ReactElement;
|
|
111
|
+
/**
|
|
112
|
+
* @param {object} props
|
|
113
|
+
* @param {string} [props.icon]
|
|
114
|
+
* @param {NativeIcons} [props.icons]
|
|
115
|
+
* @param {string} [props.href]
|
|
116
|
+
* @param {string} [props.click]
|
|
117
|
+
* @returns {import("react").ReactElement}
|
|
118
|
+
*/
|
|
119
|
+
export function NativeFab({ icon, icons, href, click }: {
|
|
120
|
+
icon?: string | undefined;
|
|
121
|
+
icons?: NativeIcons | undefined;
|
|
122
|
+
href?: string | undefined;
|
|
123
|
+
click?: string | undefined;
|
|
124
|
+
}): import("react").ReactElement;
|
|
125
|
+
/**
|
|
126
|
+
* @param {object} props
|
|
127
|
+
* @param {string} props.top
|
|
128
|
+
* @param {string} [props.bottom]
|
|
129
|
+
* @returns {import("react").ReactElement}
|
|
130
|
+
*/
|
|
131
|
+
export function NativeOverscroll({ top, bottom }: {
|
|
132
|
+
top: string;
|
|
133
|
+
bottom?: string | undefined;
|
|
134
|
+
}): import("react").ReactElement;
|
|
135
|
+
/**
|
|
136
|
+
* @param {object} props
|
|
137
|
+
* @param {string} [props.title]
|
|
138
|
+
* @param {string} [props.click]
|
|
139
|
+
* @returns {import("react").ReactElement}
|
|
140
|
+
*/
|
|
141
|
+
export function NativeSubmitButton({ title, click }: {
|
|
142
|
+
title?: string | undefined;
|
|
143
|
+
click?: string | undefined;
|
|
144
|
+
}): import("react").ReactElement;
|
|
145
|
+
/**
|
|
146
|
+
* @param {object} props
|
|
147
|
+
* @param {number} [props.count] Sets both `home` and `tab` unless they are given explicitly.
|
|
148
|
+
* @param {number} [props.home]
|
|
149
|
+
* @param {number} [props.tab]
|
|
150
|
+
* @returns {import("react").ReactElement}
|
|
151
|
+
*/
|
|
152
|
+
export function NativeBadge({ count, home, tab }: {
|
|
153
|
+
count?: number | undefined;
|
|
154
|
+
home?: number | undefined;
|
|
155
|
+
tab?: number | undefined;
|
|
156
|
+
}): import("react").ReactElement;
|
|
157
|
+
/**
|
|
158
|
+
* A visible button that pops the native navigation stack on tap. Renders a
|
|
159
|
+
* chevron unless you pass children. Extra props are forwarded to the button.
|
|
160
|
+
* An `onClick` handler runs first and may `preventDefault()` to cancel the pop.
|
|
161
|
+
* The counterpart of the `native_back_button_tag` Rails helper.
|
|
162
|
+
* @param {import("react").ButtonHTMLAttributes<HTMLButtonElement>} props
|
|
163
|
+
* @returns {import("react").ReactElement}
|
|
164
|
+
*/
|
|
165
|
+
export function NativeBackButton({ children, className, onClick, ...rest }: import("react").ButtonHTMLAttributes<HTMLButtonElement>): import("react").ReactElement;
|
|
166
|
+
/**
|
|
167
|
+
* @param {NativeHapticFeedback} [feedback]
|
|
168
|
+
* @param {Record<string, unknown>} [data]
|
|
169
|
+
* @returns {Record<string, unknown>}
|
|
170
|
+
*/
|
|
171
|
+
export function nativeHaptic(feedback?: NativeHapticFeedback, data?: Record<string, unknown>): Record<string, unknown>;
|
|
172
|
+
/**
|
|
173
|
+
* Per-platform icon names. iOS uses SF Symbols, Android uses Material icons.
|
|
174
|
+
*/
|
|
175
|
+
export type NativeIcons = {
|
|
176
|
+
ios?: string | undefined;
|
|
177
|
+
android?: string | undefined;
|
|
178
|
+
};
|
|
179
|
+
export type NativeHapticFeedback = "success" | "warning" | "error" | "impact" | "selection";
|
|
180
|
+
export type NativeButtonPosition = "leading" | "trailing";
|
package/index.js
CHANGED
|
@@ -2,6 +2,21 @@ import { createElement } from "react"
|
|
|
2
2
|
|
|
3
3
|
import("@inertiajs/react").then(m => { window.__inertiaRouter = m.router }).catch(() => {})
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Per-platform icon names. iOS uses SF Symbols, Android uses Material icons.
|
|
7
|
+
* @typedef {object} NativeIcons
|
|
8
|
+
* @property {string} [ios]
|
|
9
|
+
* @property {string} [android]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {"success" | "warning" | "error" | "impact" | "selection"} NativeHapticFeedback
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {"leading" | "trailing"} NativeButtonPosition
|
|
18
|
+
*/
|
|
19
|
+
|
|
5
20
|
function rubyNativePlatform() {
|
|
6
21
|
if (typeof navigator === "undefined") return null
|
|
7
22
|
const ua = navigator.userAgent || ""
|
|
@@ -10,6 +25,11 @@ function rubyNativePlatform() {
|
|
|
10
25
|
return null
|
|
11
26
|
}
|
|
12
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} [icon]
|
|
30
|
+
* @param {NativeIcons} [icons]
|
|
31
|
+
* @returns {string | undefined}
|
|
32
|
+
*/
|
|
13
33
|
function resolveIcon(icon, icons) {
|
|
14
34
|
if (icons) {
|
|
15
35
|
const platform = rubyNativePlatform()
|
|
@@ -18,31 +38,89 @@ function resolveIcon(icon, icons) {
|
|
|
18
38
|
return icon
|
|
19
39
|
}
|
|
20
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Sends a message over the native bridge. Deliberately not exported: app code
|
|
43
|
+
* talks to the app through signal elements, not the raw message protocol.
|
|
44
|
+
* @param {Record<string, unknown>} message
|
|
45
|
+
* @returns {boolean}
|
|
46
|
+
*/
|
|
47
|
+
function postMessage(message) {
|
|
48
|
+
if (typeof window === "undefined") return false
|
|
49
|
+
const bridge = window.RubyNative
|
|
50
|
+
if (!bridge || typeof bridge.postMessage !== "function") return false
|
|
51
|
+
bridge.postMessage(message)
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Returns "ios" or "android" inside a Ruby Native app, and null on the web.
|
|
57
|
+
* The counterpart of the `native_platform` Rails helper.
|
|
58
|
+
* @returns {"ios" | "android" | null}
|
|
59
|
+
*/
|
|
60
|
+
export function nativePlatform() {
|
|
61
|
+
return rubyNativePlatform()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {object} props
|
|
66
|
+
* @param {boolean} [props.enabled]
|
|
67
|
+
* @returns {import("react").ReactElement | null}
|
|
68
|
+
*/
|
|
21
69
|
export function NativeTabs({ enabled = true }) {
|
|
22
70
|
if (!enabled) return null
|
|
23
71
|
return createElement("div", { "data-native-tabs": true, hidden: true })
|
|
24
72
|
}
|
|
25
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @returns {import("react").ReactElement}
|
|
76
|
+
*/
|
|
26
77
|
export function NativePush() {
|
|
27
78
|
return createElement("div", { "data-native-push": true, hidden: true })
|
|
28
79
|
}
|
|
29
80
|
|
|
81
|
+
/**
|
|
82
|
+
* @returns {import("react").ReactElement}
|
|
83
|
+
*/
|
|
30
84
|
export function NativeForm() {
|
|
31
85
|
return createElement("div", { "data-native-form": true, hidden: true })
|
|
32
86
|
}
|
|
33
87
|
|
|
88
|
+
/**
|
|
89
|
+
* @returns {import("react").ReactElement}
|
|
90
|
+
*/
|
|
34
91
|
export function NativeReview() {
|
|
35
92
|
return createElement("div", { "data-native-review": true, hidden: true })
|
|
36
93
|
}
|
|
37
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {object} props
|
|
97
|
+
* @param {string} [props.title]
|
|
98
|
+
* @param {boolean} [props.pullToRefresh]
|
|
99
|
+
* @param {import("react").ReactNode} [props.children]
|
|
100
|
+
* @returns {import("react").ReactElement}
|
|
101
|
+
*/
|
|
38
102
|
export function NativeNavbar({ title = "", pullToRefresh = true, children }) {
|
|
103
|
+
/** @type {Record<string, any>} */
|
|
39
104
|
const props = { "data-native-navbar": title, hidden: true }
|
|
40
105
|
if (!pullToRefresh) props["data-native-pull-to-refresh"] = "false"
|
|
41
106
|
return createElement("div", props, children)
|
|
42
107
|
}
|
|
43
108
|
|
|
109
|
+
/**
|
|
110
|
+
* @param {object} props
|
|
111
|
+
* @param {NativeButtonPosition} [props.position]
|
|
112
|
+
* @param {string} [props.icon]
|
|
113
|
+
* @param {NativeIcons} [props.icons]
|
|
114
|
+
* @param {string} [props.title]
|
|
115
|
+
* @param {string} [props.href]
|
|
116
|
+
* @param {string} [props.click]
|
|
117
|
+
* @param {boolean} [props.selected]
|
|
118
|
+
* @param {import("react").ReactNode} [props.children]
|
|
119
|
+
* @returns {import("react").ReactElement}
|
|
120
|
+
*/
|
|
44
121
|
export function NativeButton({ position = "trailing", icon, icons, title, href, click, selected, children }) {
|
|
45
122
|
const resolved = resolveIcon(icon, icons)
|
|
123
|
+
/** @type {Record<string, any>} */
|
|
46
124
|
const props = { "data-native-button": true }
|
|
47
125
|
if (resolved) props["data-native-icon"] = resolved
|
|
48
126
|
if (title) props["data-native-title"] = title
|
|
@@ -53,8 +131,19 @@ export function NativeButton({ position = "trailing", icon, icons, title, href,
|
|
|
53
131
|
return createElement("div", props, children)
|
|
54
132
|
}
|
|
55
133
|
|
|
134
|
+
/**
|
|
135
|
+
* @param {object} props
|
|
136
|
+
* @param {string} [props.title]
|
|
137
|
+
* @param {string} [props.href]
|
|
138
|
+
* @param {string} [props.click]
|
|
139
|
+
* @param {string} [props.icon]
|
|
140
|
+
* @param {NativeIcons} [props.icons]
|
|
141
|
+
* @param {boolean} [props.selected]
|
|
142
|
+
* @returns {import("react").ReactElement}
|
|
143
|
+
*/
|
|
56
144
|
export function NativeMenuItem({ title, href, click, icon, icons, selected }) {
|
|
57
145
|
const resolved = resolveIcon(icon, icons)
|
|
146
|
+
/** @type {Record<string, any>} */
|
|
58
147
|
const props = { "data-native-menu-item": true }
|
|
59
148
|
if (title) props["data-native-title"] = title
|
|
60
149
|
if (href) props["data-native-href"] = href
|
|
@@ -64,8 +153,18 @@ export function NativeMenuItem({ title, href, click, icon, icons, selected }) {
|
|
|
64
153
|
return createElement("div", props)
|
|
65
154
|
}
|
|
66
155
|
|
|
156
|
+
/**
|
|
157
|
+
* @param {object} props
|
|
158
|
+
* @param {NativeButtonPosition} [props.position]
|
|
159
|
+
* @param {string} [props.title]
|
|
160
|
+
* @param {string} [props.icon]
|
|
161
|
+
* @param {NativeIcons} [props.icons]
|
|
162
|
+
* @param {string} [props.url]
|
|
163
|
+
* @returns {import("react").ReactElement}
|
|
164
|
+
*/
|
|
67
165
|
export function NativeShareButton({ position = "trailing", title = "Share", icon = "square.and.arrow.up", icons, url }) {
|
|
68
166
|
const resolved = resolveIcon(icon, icons)
|
|
167
|
+
/** @type {Record<string, any>} */
|
|
69
168
|
const props = { "data-native-button": true, "data-native-share": "" }
|
|
70
169
|
if (title) props["data-native-title"] = title
|
|
71
170
|
if (resolved) props["data-native-icon"] = resolved
|
|
@@ -74,8 +173,18 @@ export function NativeShareButton({ position = "trailing", title = "Share", icon
|
|
|
74
173
|
return createElement("div", props)
|
|
75
174
|
}
|
|
76
175
|
|
|
176
|
+
/**
|
|
177
|
+
* @param {object} props
|
|
178
|
+
* @param {string} [props.title]
|
|
179
|
+
* @param {string} [props.url]
|
|
180
|
+
* @param {string} [props.icon]
|
|
181
|
+
* @param {NativeIcons} [props.icons]
|
|
182
|
+
* @param {boolean} [props.selected]
|
|
183
|
+
* @returns {import("react").ReactElement}
|
|
184
|
+
*/
|
|
77
185
|
export function NativeShareMenuItem({ title = "Share", url, icon = "square.and.arrow.up", icons, selected }) {
|
|
78
186
|
const resolved = resolveIcon(icon, icons)
|
|
187
|
+
/** @type {Record<string, any>} */
|
|
79
188
|
const props = { "data-native-menu-item": true, "data-native-share": "", "data-native-title": title }
|
|
80
189
|
if (url) props["data-native-share-url"] = url
|
|
81
190
|
if (resolved) props["data-native-icon"] = resolved
|
|
@@ -83,15 +192,30 @@ export function NativeShareMenuItem({ title = "Share", url, icon = "square.and.a
|
|
|
83
192
|
return createElement("div", props)
|
|
84
193
|
}
|
|
85
194
|
|
|
195
|
+
/**
|
|
196
|
+
* @param {object} props
|
|
197
|
+
* @param {string} [props.icon]
|
|
198
|
+
* @param {NativeIcons} [props.icons]
|
|
199
|
+
* @param {string} [props.href]
|
|
200
|
+
* @param {string} [props.click]
|
|
201
|
+
* @returns {import("react").ReactElement}
|
|
202
|
+
*/
|
|
86
203
|
export function NativeFab({ icon, icons, href, click }) {
|
|
87
204
|
const resolved = resolveIcon(icon, icons)
|
|
88
205
|
if (!resolved) throw new Error("NativeFab requires `icon` or `icons`")
|
|
206
|
+
/** @type {Record<string, any>} */
|
|
89
207
|
const props = { "data-native-fab": true, "data-native-icon": resolved, hidden: true }
|
|
90
208
|
if (href) props["data-native-href"] = href
|
|
91
209
|
if (click) props["data-native-click"] = click
|
|
92
210
|
return createElement("div", props)
|
|
93
211
|
}
|
|
94
212
|
|
|
213
|
+
/**
|
|
214
|
+
* @param {object} props
|
|
215
|
+
* @param {string} props.top
|
|
216
|
+
* @param {string} [props.bottom]
|
|
217
|
+
* @returns {import("react").ReactElement}
|
|
218
|
+
*/
|
|
95
219
|
export function NativeOverscroll({ top, bottom }) {
|
|
96
220
|
return createElement("div", {
|
|
97
221
|
"data-native-overscroll-top": top,
|
|
@@ -100,6 +224,12 @@ export function NativeOverscroll({ top, bottom }) {
|
|
|
100
224
|
})
|
|
101
225
|
}
|
|
102
226
|
|
|
227
|
+
/**
|
|
228
|
+
* @param {object} props
|
|
229
|
+
* @param {string} [props.title]
|
|
230
|
+
* @param {string} [props.click]
|
|
231
|
+
* @returns {import("react").ReactElement}
|
|
232
|
+
*/
|
|
103
233
|
export function NativeSubmitButton({ title = "Save", click = "[type='submit']" }) {
|
|
104
234
|
return createElement("div", {
|
|
105
235
|
"data-native-submit-button": true,
|
|
@@ -109,16 +239,51 @@ export function NativeSubmitButton({ title = "Save", click = "[type='submit']" }
|
|
|
109
239
|
})
|
|
110
240
|
}
|
|
111
241
|
|
|
242
|
+
/**
|
|
243
|
+
* @param {object} props
|
|
244
|
+
* @param {number} [props.count] Sets both `home` and `tab` unless they are given explicitly.
|
|
245
|
+
* @param {number} [props.home]
|
|
246
|
+
* @param {number} [props.tab]
|
|
247
|
+
* @returns {import("react").ReactElement}
|
|
248
|
+
*/
|
|
112
249
|
export function NativeBadge({ count, home, tab }) {
|
|
113
250
|
if (count != null && home == null) home = count
|
|
114
251
|
if (count != null && tab == null) tab = count
|
|
252
|
+
/** @type {Record<string, any>} */
|
|
115
253
|
const props = { "data-native-badge": "", hidden: true }
|
|
116
254
|
if (home != null) props["data-native-badge-home"] = home
|
|
117
255
|
if (tab != null) props["data-native-badge-tab"] = tab
|
|
118
256
|
return createElement("div", props)
|
|
119
257
|
}
|
|
120
258
|
|
|
259
|
+
/**
|
|
260
|
+
* A visible button that pops the native navigation stack on tap. Renders a
|
|
261
|
+
* chevron unless you pass children. Extra props are forwarded to the button.
|
|
262
|
+
* An `onClick` handler runs first and may `preventDefault()` to cancel the pop.
|
|
263
|
+
* The counterpart of the `native_back_button_tag` Rails helper.
|
|
264
|
+
* @param {import("react").ButtonHTMLAttributes<HTMLButtonElement>} props
|
|
265
|
+
* @returns {import("react").ReactElement}
|
|
266
|
+
*/
|
|
267
|
+
export function NativeBackButton({ children, className, onClick, ...rest }) {
|
|
268
|
+
const classes = ["native-back-button", className].filter(Boolean).join(" ")
|
|
269
|
+
/** @param {import("react").MouseEvent<HTMLButtonElement>} event */
|
|
270
|
+
const handleClick = event => {
|
|
271
|
+
if (onClick) onClick(event)
|
|
272
|
+
if (!event.defaultPrevented) postMessage({ action: "back" })
|
|
273
|
+
}
|
|
274
|
+
const content = children != null ? children : createElement(
|
|
275
|
+
"svg",
|
|
276
|
+
{ width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": true },
|
|
277
|
+
createElement("path", { d: "M15.75 19.5L8.25 12l7.5-7.5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
278
|
+
)
|
|
279
|
+
return createElement("button", { type: "button", ...rest, className: classes, onClick: handleClick }, content)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param {NativeHapticFeedback} [feedback]
|
|
284
|
+
* @param {Record<string, unknown>} [data]
|
|
285
|
+
* @returns {Record<string, unknown>}
|
|
286
|
+
*/
|
|
121
287
|
export function nativeHaptic(feedback = "success", data = {}) {
|
|
122
288
|
return { ...data, "data-native-haptic": feedback }
|
|
123
289
|
}
|
|
124
|
-
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruby-native/react",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.20",
|
|
4
4
|
"description": "React components for Ruby Native",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"files": ["index.js", "index.d.ts", "README.md"],
|
|
8
|
+
"scripts": {
|
|
9
|
+
"types": "tsc -p .",
|
|
10
|
+
"prepublishOnly": "npm run types"
|
|
11
|
+
},
|
|
7
12
|
"peerDependencies": {
|
|
8
13
|
"react": ">=18",
|
|
9
14
|
"@inertiajs/react": ">=2"
|
|
@@ -11,6 +16,10 @@
|
|
|
11
16
|
"peerDependenciesMeta": {
|
|
12
17
|
"@inertiajs/react": { "optional": true }
|
|
13
18
|
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/react": "^18.3.0",
|
|
21
|
+
"typescript": "^5.9.0"
|
|
22
|
+
},
|
|
14
23
|
"repository": {
|
|
15
24
|
"type": "git",
|
|
16
25
|
"url": "https://github.com/ruby-native/gem.git",
|