@object-ui/mobile 17.5.0 → 17.7.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/CHANGELOG.md +852 -0
- package/README.md +183 -34
- package/dist/MobileProvider.d.ts +1 -1
- package/dist/MobileProvider.d.ts.map +1 -1
- package/dist/MobileProvider.js +1 -1
- package/dist/ResponsiveContainer.js +1 -1
- package/dist/createOfflineDataSource.d.ts +1 -1
- package/dist/createOfflineDataSource.d.ts.map +1 -1
- package/dist/createOfflineDataSource.js +1 -1
- package/dist/index.d.ts +16 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -16
- package/dist/useBreakpoint.js +1 -1
- package/dist/useOfflineSync.d.ts +2 -2
- package/dist/useOfflineSync.d.ts.map +1 -1
- package/dist/usePullToRefresh.d.ts.map +1 -1
- package/dist/usePullToRefresh.js +96 -11
- package/dist/useResponsive.js +2 -2
- package/dist/useSpecGesture.d.ts +12 -6
- package/dist/useSpecGesture.d.ts.map +1 -1
- package/dist/useSpecGesture.js +37 -21
- package/package.json +6 -6
- package/dist/useResponsiveConfig.d.ts +0 -71
- package/dist/useResponsiveConfig.d.ts.map +0 -1
- package/dist/useResponsiveConfig.js +0 -41
package/README.md
CHANGED
|
@@ -25,18 +25,22 @@ npm install @object-ui/mobile
|
|
|
25
25
|
## Quick Start
|
|
26
26
|
|
|
27
27
|
```tsx
|
|
28
|
-
import { MobileProvider, useBreakpoint
|
|
28
|
+
import { MobileProvider, useBreakpoint } from '@object-ui/mobile';
|
|
29
29
|
|
|
30
|
-
function
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
function MobileNav() {
|
|
31
|
+
return <nav>Mobile navigation</nav>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function DesktopSidebar() {
|
|
35
|
+
return <aside>Desktop sidebar</aside>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function MainContent() {
|
|
39
|
+
return <main>Main content</main>;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
function ResponsiveApp() {
|
|
39
|
-
const { isMobile,
|
|
43
|
+
const { isMobile, isDesktop } = useBreakpoint();
|
|
40
44
|
|
|
41
45
|
return (
|
|
42
46
|
<div>
|
|
@@ -46,84 +50,229 @@ function ResponsiveApp() {
|
|
|
46
50
|
</div>
|
|
47
51
|
);
|
|
48
52
|
}
|
|
53
|
+
|
|
54
|
+
export function App() {
|
|
55
|
+
return (
|
|
56
|
+
<MobileProvider>
|
|
57
|
+
<ResponsiveApp />
|
|
58
|
+
</MobileProvider>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
49
61
|
```
|
|
50
62
|
|
|
51
63
|
## API
|
|
52
64
|
|
|
53
65
|
### MobileProvider
|
|
54
66
|
|
|
55
|
-
Wraps your application with mobile context:
|
|
67
|
+
Wraps your application with mobile context. Both props are optional: `pwa` takes a
|
|
68
|
+
`PWAConfig`, `offline` takes a `PWAOfflineConfig`.
|
|
56
69
|
|
|
57
70
|
```tsx
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
import { MobileProvider } from '@object-ui/mobile';
|
|
72
|
+
|
|
73
|
+
function App() {
|
|
74
|
+
return <p>Your application</p>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function Root() {
|
|
78
|
+
return (
|
|
79
|
+
<MobileProvider>
|
|
80
|
+
<App />
|
|
81
|
+
</MobileProvider>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
61
84
|
```
|
|
62
85
|
|
|
63
86
|
### useBreakpoint
|
|
64
87
|
|
|
65
|
-
Hook for detecting the current breakpoint
|
|
88
|
+
Hook for detecting the current breakpoint. The current breakpoint name is
|
|
89
|
+
`breakpoint` — one of `xs`, `sm`, `md`, `lg`, `xl`, `2xl`:
|
|
66
90
|
|
|
67
91
|
```tsx
|
|
68
|
-
|
|
92
|
+
import { useBreakpoint } from '@object-ui/mobile';
|
|
93
|
+
|
|
94
|
+
export function BreakpointBadge() {
|
|
95
|
+
const { isMobile, isTablet, isDesktop, breakpoint, width } = useBreakpoint();
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<span>
|
|
99
|
+
{breakpoint} at {width}px — mobile {String(isMobile)}, tablet {String(isTablet)}, desktop{' '}
|
|
100
|
+
{String(isDesktop)}
|
|
101
|
+
</span>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
69
104
|
```
|
|
70
105
|
|
|
106
|
+
`isAbove(bp)` and `isBelow(bp)` are also returned, for comparisons against a named
|
|
107
|
+
breakpoint.
|
|
108
|
+
|
|
71
109
|
### useResponsive
|
|
72
110
|
|
|
73
|
-
Hook for responsive values based on screen size
|
|
111
|
+
Hook for responsive values based on screen size. The keys are breakpoint names; a
|
|
112
|
+
breakpoint with no entry falls back to the next smaller one that has one:
|
|
74
113
|
|
|
75
114
|
```tsx
|
|
76
|
-
|
|
115
|
+
import { useResponsive } from '@object-ui/mobile';
|
|
116
|
+
|
|
117
|
+
export function ResponsiveGrid() {
|
|
118
|
+
const columns = useResponsive({ xs: 1, md: 2, lg: 4 });
|
|
119
|
+
|
|
120
|
+
return <div data-columns={columns}>{columns} column(s)</div>;
|
|
121
|
+
}
|
|
77
122
|
```
|
|
78
123
|
|
|
79
124
|
### useGesture / useSpecGesture
|
|
80
125
|
|
|
81
|
-
|
|
126
|
+
`useGesture` detects one gesture from Object UI's direction-fused vocabulary
|
|
127
|
+
(`tap`, `double-tap`, `long-press`, `swipe-left`, `swipe-right`, `swipe-up`,
|
|
128
|
+
`swipe-down`, `pinch`, `rotate`, `pan`) per call, and returns a ref to attach:
|
|
82
129
|
|
|
83
130
|
```tsx
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
131
|
+
import { useGesture } from '@object-ui/mobile';
|
|
132
|
+
|
|
133
|
+
function navigateNext() {}
|
|
134
|
+
function navigateBack() {}
|
|
135
|
+
|
|
136
|
+
export function SwipeArea() {
|
|
137
|
+
const nextRef = useGesture<HTMLDivElement>({
|
|
138
|
+
type: 'swipe-left',
|
|
139
|
+
onGesture: () => navigateNext(),
|
|
140
|
+
});
|
|
141
|
+
const backRef = useGesture<HTMLDivElement>({
|
|
142
|
+
type: 'swipe-right',
|
|
143
|
+
onGesture: () => navigateBack(),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div>
|
|
148
|
+
<div ref={nextRef}>Swipe left for the next record</div>
|
|
149
|
+
<div ref={backRef}>Swipe right to go back</div>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`useSpecGesture` takes the declarative `SpecGestureConfig` tuning shape instead, and
|
|
156
|
+
dispatches to per-gesture callbacks:
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { useSpecGesture } from '@object-ui/mobile';
|
|
160
|
+
|
|
161
|
+
function handleZoom(scale: number) {
|
|
162
|
+
return scale;
|
|
163
|
+
}
|
|
89
164
|
|
|
90
|
-
|
|
165
|
+
export function PinchArea() {
|
|
166
|
+
const ref = useSpecGesture<HTMLDivElement>({
|
|
167
|
+
config: { type: 'pinch', enabled: true, pinch: { minScale: 0.5, maxScale: 3 } },
|
|
168
|
+
onPinch: (scale) => handleZoom(scale),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return <div ref={ref}>Pinchable content</div>;
|
|
172
|
+
}
|
|
91
173
|
```
|
|
92
174
|
|
|
93
175
|
### usePullToRefresh
|
|
94
176
|
|
|
95
|
-
Hook for pull-to-refresh behavior
|
|
177
|
+
Hook for pull-to-refresh behavior. Attach the returned `ref` to the scrollable
|
|
178
|
+
container:
|
|
96
179
|
|
|
97
180
|
```tsx
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
181
|
+
import { usePullToRefresh } from '@object-ui/mobile';
|
|
182
|
+
|
|
183
|
+
async function fetchData(): Promise<void> {}
|
|
184
|
+
|
|
185
|
+
export function Feed() {
|
|
186
|
+
const { ref, isRefreshing, pullDistance } = usePullToRefresh<HTMLDivElement>({
|
|
187
|
+
onRefresh: async () => await fetchData(),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<div ref={ref} data-pull-distance={pullDistance}>
|
|
192
|
+
{isRefreshing ? 'Refreshing…' : 'Pull to refresh'}
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
101
196
|
```
|
|
102
197
|
|
|
198
|
+
The element does not have to exist on the first render. A component that shows
|
|
199
|
+
a loading screen first and attaches `ref` once its data arrives works, and so
|
|
200
|
+
does one that later swaps in a new element. The hook binds its touch listeners
|
|
201
|
+
to whatever element `ref` points at after each render.
|
|
202
|
+
|
|
203
|
+
Pull hosts can nest, for example a list view that renders a grid view inside
|
|
204
|
+
it. One pull belongs to the outermost armed host: an inner host lets the
|
|
205
|
+
gesture go when an armed pull host sits above it in the DOM, so a single pull
|
|
206
|
+
draws one indicator and runs one `onRefresh`.
|
|
207
|
+
|
|
103
208
|
### useTouchTarget
|
|
104
209
|
|
|
105
|
-
Hook for ensuring minimum touch target sizes
|
|
210
|
+
Hook for ensuring minimum touch target sizes. It returns the `style` and `className`
|
|
211
|
+
to spread onto the element; the defaults follow WCAG 2.5.5 (44×44 CSS pixels):
|
|
106
212
|
|
|
107
213
|
```tsx
|
|
108
|
-
|
|
109
|
-
|
|
214
|
+
import { useTouchTarget } from '@object-ui/mobile';
|
|
215
|
+
|
|
216
|
+
export function TapButton() {
|
|
217
|
+
const { style, className } = useTouchTarget({
|
|
218
|
+
config: { minWidth: 44, minHeight: 44 },
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<button style={style} className={className}>
|
|
223
|
+
Tap me
|
|
224
|
+
</button>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
110
227
|
```
|
|
111
228
|
|
|
112
229
|
### ResponsiveContainer
|
|
113
230
|
|
|
114
|
-
Renders children based on breakpoint
|
|
231
|
+
Renders children based on breakpoint. Pick the range with `minBreakpoint` /
|
|
232
|
+
`maxBreakpoint`, or name the breakpoints outright with `showOn` / `hideOn`:
|
|
115
233
|
|
|
116
234
|
```tsx
|
|
117
|
-
|
|
235
|
+
import { ResponsiveContainer } from '@object-ui/mobile';
|
|
236
|
+
|
|
237
|
+
function MobileView() {
|
|
238
|
+
return <p>Compact layout</p>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function DesktopView() {
|
|
242
|
+
return <p>Full layout</p>;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function BreakpointSwitch() {
|
|
246
|
+
return (
|
|
247
|
+
<div>
|
|
248
|
+
<ResponsiveContainer maxBreakpoint="md">
|
|
249
|
+
<MobileView />
|
|
250
|
+
</ResponsiveContainer>
|
|
251
|
+
<ResponsiveContainer minBreakpoint="lg">
|
|
252
|
+
<DesktopView />
|
|
253
|
+
</ResponsiveContainer>
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
118
257
|
```
|
|
119
258
|
|
|
120
259
|
### PWA Utilities
|
|
121
260
|
|
|
261
|
+
`PWAConfig` requires `enabled`, `name` and `shortName`. `registerServiceWorker` takes
|
|
262
|
+
the script `url` and `scope` plus lifecycle callbacks — the caching strategies live in
|
|
263
|
+
the generated worker (`getServiceWorkerSource`), not in this call:
|
|
264
|
+
|
|
122
265
|
```tsx
|
|
123
266
|
import { generatePWAManifest, registerServiceWorker } from '@object-ui/mobile';
|
|
124
267
|
|
|
125
|
-
const manifest = generatePWAManifest({
|
|
126
|
-
|
|
268
|
+
export const manifest = generatePWAManifest({
|
|
269
|
+
enabled: true,
|
|
270
|
+
name: 'My App',
|
|
271
|
+
shortName: 'My App',
|
|
272
|
+
themeColor: '#000',
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
void registerServiceWorker({ url: '/service-worker.js' });
|
|
127
276
|
```
|
|
128
277
|
|
|
129
278
|
## Links
|
package/dist/MobileProvider.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import type { PWAConfig, PWAOfflineConfig } from '@object-ui/types';
|
|
10
|
-
import { type BreakpointState } from './useBreakpoint';
|
|
10
|
+
import { type BreakpointState } from './useBreakpoint.js';
|
|
11
11
|
export interface MobileContextValue extends BreakpointState {
|
|
12
12
|
/** PWA configuration */
|
|
13
13
|
pwa?: PWAConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MobileProvider.d.ts","sourceRoot":"","sources":["../src/MobileProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA6C,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"MobileProvider.d.ts","sourceRoot":"","sources":["../src/MobileProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA6C,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEzE,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,wBAAwB;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAKD,MAAM,WAAW,mBAAmB;IAClC,wBAAwB;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,eAAe;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,mBAAmB,qBAa7E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,GAAG,IAAI,CAE5D"}
|
package/dist/MobileProvider.js
CHANGED
|
@@ -7,7 +7,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
7
7
|
* LICENSE file in the root directory of this source tree.
|
|
8
8
|
*/
|
|
9
9
|
import { createContext, useContext, useMemo } from 'react';
|
|
10
|
-
import { useBreakpoint } from './useBreakpoint';
|
|
10
|
+
import { useBreakpoint } from './useBreakpoint.js';
|
|
11
11
|
const MobileCtx = createContext(null);
|
|
12
12
|
MobileCtx.displayName = 'MobileContext';
|
|
13
13
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useBreakpoint } from './useBreakpoint';
|
|
2
|
+
import { useBreakpoint } from './useBreakpoint.js';
|
|
3
3
|
/**
|
|
4
4
|
* Container that conditionally renders children based on the current breakpoint.
|
|
5
5
|
*/
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Reads (`find` / `findOne`) are pass-through; the optional `cache` hook
|
|
11
11
|
* lets callers opt into local caching but is not built in here.
|
|
12
12
|
*/
|
|
13
|
-
import { type OfflineQueueBackend, type OfflineOperation } from './offlineQueue';
|
|
13
|
+
import { type OfflineQueueBackend, type OfflineOperation } from './offlineQueue.js';
|
|
14
14
|
/** Minimal DataSource shape we care about. */
|
|
15
15
|
export interface QueueableDataSource {
|
|
16
16
|
find?: (object: string, query?: any) => Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createOfflineDataSource.d.ts","sourceRoot":"","sources":["../src/createOfflineDataSource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"createOfflineDataSource.d.ts","sourceRoot":"","sources":["../src/createOfflineDataSource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAgB,MAAM,mBAAmB,CAAC;AAElG,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACrD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7E,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACxD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7E,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/D,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,0FAA0F;IAC1F,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IAC3C,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;IACzB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC7C;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,GAAG,MAAM,GAAG;QAAE,OAAO,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;CAC1G;AAiBD,gEAAgE;AAChE,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,6FAA6F;IAC7F,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,uDAAuD;IACvD,MAAM,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,kDAAkD;IAClD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,0DAA0D;IAC1D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,8BAA8B;IAC9B,OAAO,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,wBAAwB,GAChC,iBAAiB,CA6GnB"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Reads (`find` / `findOne`) are pass-through; the optional `cache` hook
|
|
11
11
|
* lets callers opt into local caching but is not built in here.
|
|
12
12
|
*/
|
|
13
|
-
import { generateOpId } from './offlineQueue';
|
|
13
|
+
import { generateOpId } from './offlineQueue.js';
|
|
14
14
|
const defaultIsNetworkError = (err) => {
|
|
15
15
|
if (!err)
|
|
16
16
|
return false;
|
package/dist/index.d.ts
CHANGED
|
@@ -17,21 +17,20 @@
|
|
|
17
17
|
*
|
|
18
18
|
* @packageDocumentation
|
|
19
19
|
*/
|
|
20
|
-
export { useBreakpoint, type BreakpointState } from './useBreakpoint';
|
|
21
|
-
export { useResponsive } from './useResponsive';
|
|
22
|
-
export {
|
|
23
|
-
export {
|
|
24
|
-
export {
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
29
|
-
export {
|
|
30
|
-
export {
|
|
31
|
-
export {
|
|
32
|
-
export {
|
|
33
|
-
export {
|
|
34
|
-
export {
|
|
35
|
-
export {
|
|
36
|
-
export type { BreakpointName, ResponsiveValue, MobileResponsiveConfig, MobileOverrides, PWAConfig, PWAIcon, FetchCacheStrategy, PWAOfflineConfig, OfflineRoute, GestureType, GestureConfig, GestureContext, MobileComponentConfig, SpecGestureConfig, SwipeGestureConfig, PinchGestureConfig, LongPressGestureConfig, TouchInteraction, TouchTargetConfig, } from '@object-ui/types';
|
|
20
|
+
export { useBreakpoint, type BreakpointState } from './useBreakpoint.js';
|
|
21
|
+
export { useResponsive } from './useResponsive.js';
|
|
22
|
+
export { useGesture, type UseGestureOptions } from './useGesture.js';
|
|
23
|
+
export { useSpecGesture, type UseSpecGestureOptions } from './useSpecGesture.js';
|
|
24
|
+
export { useTouchTarget, type UseTouchTargetOptions, type TouchTargetResult } from './useTouchTarget.js';
|
|
25
|
+
export { usePullToRefresh, type PullToRefreshOptions } from './usePullToRefresh.js';
|
|
26
|
+
export { MobileProvider, type MobileProviderProps } from './MobileProvider.js';
|
|
27
|
+
export { ResponsiveContainer, type ResponsiveContainerProps } from './ResponsiveContainer.js';
|
|
28
|
+
export { generatePWAManifest } from './pwa.js';
|
|
29
|
+
export { registerServiceWorker, type ServiceWorkerConfig } from './serviceWorker.js';
|
|
30
|
+
export { createOfflineQueue, IndexedDbOfflineQueue, MemoryOfflineQueue, generateOpId, type OfflineOperation, type OfflineQueueBackend, } from './offlineQueue.js';
|
|
31
|
+
export { createOfflineDataSource, type OfflineDataSource, type OfflineDataSourceOptions, type QueueableDataSource, } from './createOfflineDataSource.js';
|
|
32
|
+
export { useOfflineSync, type OfflineSyncState } from './useOfflineSync.js';
|
|
33
|
+
export { getServiceWorkerSource, requestBackgroundSync, type ServiceWorkerSourceOptions, } from './serviceWorkerSource.js';
|
|
34
|
+
export { BREAKPOINTS, resolveResponsiveValue } from './breakpoints.js';
|
|
35
|
+
export type { BreakpointName, ResponsiveValue, PWAConfig, PWAIcon, FetchCacheStrategy, PWAOfflineConfig, OfflineRoute, GestureType, GestureContext, SpecGestureConfig, SwipeGestureConfig, PinchGestureConfig, LongPressGestureConfig, TouchInteraction, TouchTargetConfig, } from '@object-ui/types';
|
|
37
36
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAQnD,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAC9F,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,GACzB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAGvE,YAAY,EACV,cAAc,EACd,eAAe,EAMf,SAAS,EACT,OAAO,EACP,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,19 +17,25 @@
|
|
|
17
17
|
*
|
|
18
18
|
* @packageDocumentation
|
|
19
19
|
*/
|
|
20
|
-
export { useBreakpoint } from './useBreakpoint';
|
|
21
|
-
export { useResponsive } from './useResponsive';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export {
|
|
30
|
-
export {
|
|
31
|
-
export {
|
|
32
|
-
export {
|
|
33
|
-
export {
|
|
34
|
-
export {
|
|
35
|
-
export {
|
|
20
|
+
export { useBreakpoint } from './useBreakpoint.js';
|
|
21
|
+
export { useResponsive } from './useResponsive.js';
|
|
22
|
+
// `useResponsiveConfig` REMOVED (objectui#7580, maintainer ruling 2026-09-04).
|
|
23
|
+
// It read `@objectstack/spec`'s `ResponsiveConfigSchema`, which objectstack#11027
|
|
24
|
+
// retired; it was measured at zero callers on objectui#4773. Its exported
|
|
25
|
+
// `SpecResponsiveConfig` / `ResolvedResponsiveState` went with it — both named
|
|
26
|
+
// the retired schema's shape and neither had a reader. No behaviour is retired:
|
|
27
|
+
// the live per-breakpoint readers are `useBreakpoint` and
|
|
28
|
+
// `ResponsiveContainer` below, which are untouched.
|
|
29
|
+
export { useGesture } from './useGesture.js';
|
|
30
|
+
export { useSpecGesture } from './useSpecGesture.js';
|
|
31
|
+
export { useTouchTarget } from './useTouchTarget.js';
|
|
32
|
+
export { usePullToRefresh } from './usePullToRefresh.js';
|
|
33
|
+
export { MobileProvider } from './MobileProvider.js';
|
|
34
|
+
export { ResponsiveContainer } from './ResponsiveContainer.js';
|
|
35
|
+
export { generatePWAManifest } from './pwa.js';
|
|
36
|
+
export { registerServiceWorker } from './serviceWorker.js';
|
|
37
|
+
export { createOfflineQueue, IndexedDbOfflineQueue, MemoryOfflineQueue, generateOpId, } from './offlineQueue.js';
|
|
38
|
+
export { createOfflineDataSource, } from './createOfflineDataSource.js';
|
|
39
|
+
export { useOfflineSync } from './useOfflineSync.js';
|
|
40
|
+
export { getServiceWorkerSource, requestBackgroundSync, } from './serviceWorkerSource.js';
|
|
41
|
+
export { BREAKPOINTS, resolveResponsiveValue } from './breakpoints.js';
|
package/dist/useBreakpoint.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
import { useState, useEffect } from 'react';
|
|
9
|
-
import { BREAKPOINTS, BREAKPOINT_ORDER, getCurrentBreakpoint } from './breakpoints';
|
|
9
|
+
import { BREAKPOINTS, BREAKPOINT_ORDER, getCurrentBreakpoint } from './breakpoints.js';
|
|
10
10
|
/**
|
|
11
11
|
* Hook that tracks the current responsive breakpoint.
|
|
12
12
|
* Updates on window resize with debouncing.
|
package/dist/useOfflineSync.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* React hook that subscribes to an `OfflineDataSource`, exposing the current
|
|
6
6
|
* online/offline status and the pending queue for status-bar / banner UIs.
|
|
7
7
|
*/
|
|
8
|
-
import type { OfflineDataSource } from './createOfflineDataSource';
|
|
9
|
-
import type { OfflineOperation } from './offlineQueue';
|
|
8
|
+
import type { OfflineDataSource } from './createOfflineDataSource.js';
|
|
9
|
+
import type { OfflineOperation } from './offlineQueue.js';
|
|
10
10
|
export interface OfflineSyncState {
|
|
11
11
|
/** True if the browser reports `navigator.onLine`. */
|
|
12
12
|
isOnline: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOfflineSync.d.ts","sourceRoot":"","sources":["../src/useOfflineSync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"useOfflineSync.d.ts","sourceRoot":"","sources":["../src/useOfflineSync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,QAAQ,EAAE,OAAO,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,wCAAwC;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,iCAAiC;IACjC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,uBAAuB;IACvB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,GAAG,gBAAgB,CA+C1E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePullToRefresh.d.ts","sourceRoot":"","sources":["../src/usePullToRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAClE,OAAO,EAAE,oBAAoB;;;;
|
|
1
|
+
{"version":3,"file":"usePullToRefresh.d.ts","sourceRoot":"","sources":["../src/usePullToRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAClE,OAAO,EAAE,oBAAoB;;;;EAoG9B"}
|
package/dist/usePullToRefresh.js
CHANGED
|
@@ -50,18 +50,103 @@ export function usePullToRefresh(options) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}, [enabled, isRefreshing, pullDistance, threshold, onRefresh]);
|
|
53
|
+
/**
|
|
54
|
+
* The listeners follow the ELEMENT, not the mount (objectui#10105).
|
|
55
|
+
*
|
|
56
|
+
* `ref` is an object ref, so nothing tells this hook when a consumer
|
|
57
|
+
* attaches it. The binding used to live in an effect keyed on the handlers
|
|
58
|
+
* and `enabled`, which read `ref.current` once and then waited for one of
|
|
59
|
+
* those to change. A consumer whose first commit is a loading screen has
|
|
60
|
+
* `ref.current === null` at that moment. When the real host appears later,
|
|
61
|
+
* none of those keys has changed, so the effect never ran again and the
|
|
62
|
+
* gesture was dead. The consumers that do this, and the ones that do not,
|
|
63
|
+
* were measured on objectui#10105; the list is not repeated here because it
|
|
64
|
+
* would go stale. The same happened when a refresh put the loading screen
|
|
65
|
+
* back and the host came back as a NEW element: the listeners stayed on the
|
|
66
|
+
* old, detached one.
|
|
67
|
+
*
|
|
68
|
+
* So the binding effect below has no dependency list and runs after every
|
|
69
|
+
* commit. It re-binds only when the element it SHOULD be bound to
|
|
70
|
+
* (`ref.current`, or `null` while disabled) differs from the one it IS bound
|
|
71
|
+
* to. That is a DOM-identity comparison, never a comparison of memoised
|
|
72
|
+
* identities (AGENTS.md §5 #10). The native listeners stay the same for the
|
|
73
|
+
* life of a binding and call the latest handlers through `handlersRef`, so a
|
|
74
|
+
* state change such as each `pullDistance` step during a gesture no longer
|
|
75
|
+
* re-binds anything.
|
|
76
|
+
*/
|
|
77
|
+
const handlersRef = useRef({ handleTouchStart, handleTouchMove, handleTouchEnd });
|
|
53
78
|
useEffect(() => {
|
|
54
|
-
|
|
55
|
-
|
|
79
|
+
handlersRef.current = { handleTouchStart, handleTouchMove, handleTouchEnd };
|
|
80
|
+
});
|
|
81
|
+
const bindingRef = useRef(null);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const target = enabled ? ref.current : null;
|
|
84
|
+
const bound = bindingRef.current;
|
|
85
|
+
if ((bound ? bound.el : null) === target)
|
|
56
86
|
return;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
if (bound)
|
|
88
|
+
unbindPullListeners(bound);
|
|
89
|
+
bindingRef.current = target ? bindPullListeners(target, handlersRef) : null;
|
|
90
|
+
});
|
|
91
|
+
// Unmount. The per-commit effect above returns no cleanup on purpose, since
|
|
92
|
+
// a cleanup there would re-bind on every commit. This is where the last
|
|
93
|
+
// binding is released. It releases the exact listeners that were added,
|
|
94
|
+
// which are recorded on the binding, so nothing depends on an identity
|
|
95
|
+
// staying stable.
|
|
96
|
+
useEffect(() => () => {
|
|
97
|
+
const bound = bindingRef.current;
|
|
98
|
+
if (bound)
|
|
99
|
+
unbindPullListeners(bound);
|
|
100
|
+
bindingRef.current = null;
|
|
101
|
+
}, []);
|
|
66
102
|
return { ref, isRefreshing, pullDistance };
|
|
67
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* One gesture, one owner: the OUTERMOST armed pull host (objectui#10105).
|
|
106
|
+
*
|
|
107
|
+
* Pull hosts nest. `ListView` has one, and so does the view it renders inside
|
|
108
|
+
* it. A touch on the inner host bubbles to the outer one, so without a rule
|
|
109
|
+
* both hooks drew an indicator and both ran their refresh for the same pull.
|
|
110
|
+
* The outer one is the owner: its single refetch reloads the rows the inner
|
|
111
|
+
* view was handed, and an inner view fed its rows by a parent has no refresh of
|
|
112
|
+
* its own to run.
|
|
113
|
+
*
|
|
114
|
+
* While bound, the hook marks its element with this attribute. It is a DOM
|
|
115
|
+
* attribute and not module state so that two copies of this package in one
|
|
116
|
+
* page still see each other. At `touchstart` an inner host looks for a marked
|
|
117
|
+
* ANCESTOR, and if one is armed it lets the gesture go: it never records a
|
|
118
|
+
* start point, so its move and end handlers do nothing for this gesture. The
|
|
119
|
+
* native listeners and the DOM ancestry are the same relation (the outer
|
|
120
|
+
* listener receives this event because it is an ancestor), which is why the
|
|
121
|
+
* check reads the DOM and not the React tree.
|
|
122
|
+
*/
|
|
123
|
+
const PULL_HOST_ATTRIBUTE = 'data-pull-to-refresh-host';
|
|
124
|
+
function armedPullHostAbove(el) {
|
|
125
|
+
const parent = el.parentElement;
|
|
126
|
+
return parent !== null && parent.closest(`[${PULL_HOST_ATTRIBUTE}]`) !== null;
|
|
127
|
+
}
|
|
128
|
+
function bindPullListeners(el, handlers) {
|
|
129
|
+
const binding = {
|
|
130
|
+
el,
|
|
131
|
+
start: (e) => {
|
|
132
|
+
if (armedPullHostAbove(el))
|
|
133
|
+
return;
|
|
134
|
+
handlers.current.handleTouchStart(e);
|
|
135
|
+
},
|
|
136
|
+
move: (e) => handlers.current.handleTouchMove(e),
|
|
137
|
+
end: () => {
|
|
138
|
+
void handlers.current.handleTouchEnd();
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
el.setAttribute(PULL_HOST_ATTRIBUTE, '');
|
|
142
|
+
el.addEventListener('touchstart', binding.start, { passive: true });
|
|
143
|
+
el.addEventListener('touchmove', binding.move, { passive: true });
|
|
144
|
+
el.addEventListener('touchend', binding.end, { passive: true });
|
|
145
|
+
return binding;
|
|
146
|
+
}
|
|
147
|
+
function unbindPullListeners(binding) {
|
|
148
|
+
binding.el.removeAttribute(PULL_HOST_ATTRIBUTE);
|
|
149
|
+
binding.el.removeEventListener('touchstart', binding.start);
|
|
150
|
+
binding.el.removeEventListener('touchmove', binding.move);
|
|
151
|
+
binding.el.removeEventListener('touchend', binding.end);
|
|
152
|
+
}
|
package/dist/useResponsive.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
import { useMemo } from 'react';
|
|
9
|
-
import { useBreakpoint } from './useBreakpoint';
|
|
10
|
-
import { resolveResponsiveValue } from './breakpoints';
|
|
9
|
+
import { useBreakpoint } from './useBreakpoint.js';
|
|
10
|
+
import { resolveResponsiveValue } from './breakpoints.js';
|
|
11
11
|
/**
|
|
12
12
|
* Hook that resolves a responsive value based on the current breakpoint.
|
|
13
13
|
*
|