@object-ui/mobile 0.1.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/LICENSE +21 -0
- package/dist/MobileProvider.d.ts +33 -0
- package/dist/MobileProvider.d.ts.map +1 -0
- package/dist/MobileProvider.js +30 -0
- package/dist/ResponsiveContainer.d.ts +28 -0
- package/dist/ResponsiveContainer.d.ts.map +1 -0
- package/dist/ResponsiveContainer.js +23 -0
- package/dist/breakpoints.d.ts +22 -0
- package/dist/breakpoints.d.ts.map +1 -0
- package/dist/breakpoints.js +50 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/pwa.d.ts +14 -0
- package/dist/pwa.d.ts.map +1 -0
- package/dist/pwa.js +30 -0
- package/dist/serviceWorker.d.ts +25 -0
- package/dist/serviceWorker.d.ts.map +1 -0
- package/dist/serviceWorker.js +45 -0
- package/dist/useBreakpoint.d.ts +30 -0
- package/dist/useBreakpoint.d.ts.map +1 -0
- package/dist/useBreakpoint.js +43 -0
- package/dist/useGesture.d.ts +35 -0
- package/dist/useGesture.d.ts.map +1 -0
- package/dist/useGesture.js +117 -0
- package/dist/usePullToRefresh.d.ts +25 -0
- package/dist/usePullToRefresh.d.ts.map +1 -0
- package/dist/usePullToRefresh.js +65 -0
- package/dist/useResponsive.d.ts +19 -0
- package/dist/useResponsive.d.ts.map +1 -0
- package/dist/useResponsive.js +23 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ObjectQL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { PWAConfig, OfflineConfig } from '@object-ui/types';
|
|
10
|
+
import { type BreakpointState } from './useBreakpoint';
|
|
11
|
+
export interface MobileContextValue extends BreakpointState {
|
|
12
|
+
/** PWA configuration */
|
|
13
|
+
pwa?: PWAConfig;
|
|
14
|
+
/** Offline configuration */
|
|
15
|
+
offline?: OfflineConfig;
|
|
16
|
+
}
|
|
17
|
+
export interface MobileProviderProps {
|
|
18
|
+
/** PWA configuration */
|
|
19
|
+
pwa?: PWAConfig;
|
|
20
|
+
/** Offline configuration */
|
|
21
|
+
offline?: OfflineConfig;
|
|
22
|
+
/** Children */
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Provider that combines breakpoint detection with mobile configuration.
|
|
27
|
+
*/
|
|
28
|
+
export declare function MobileProvider({ pwa, offline, children }: MobileProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
/**
|
|
30
|
+
* Hook to access the mobile context.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useMobileContext(): MobileContextValue | null;
|
|
33
|
+
//# sourceMappingURL=MobileProvider.d.ts.map
|
|
@@ -0,0 +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,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEtE,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,wBAAwB;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAKD,MAAM,WAAW,mBAAmB;IAClC,wBAAwB;IACxB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,eAAe;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CAa7E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,GAAG,IAAI,CAE5D"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ObjectUI
|
|
4
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
10
|
+
import { useBreakpoint } from './useBreakpoint';
|
|
11
|
+
const MobileCtx = createContext(null);
|
|
12
|
+
MobileCtx.displayName = 'MobileContext';
|
|
13
|
+
/**
|
|
14
|
+
* Provider that combines breakpoint detection with mobile configuration.
|
|
15
|
+
*/
|
|
16
|
+
export function MobileProvider({ pwa, offline, children }) {
|
|
17
|
+
const breakpoint = useBreakpoint();
|
|
18
|
+
const value = useMemo(() => ({
|
|
19
|
+
...breakpoint,
|
|
20
|
+
pwa,
|
|
21
|
+
offline,
|
|
22
|
+
}), [breakpoint, pwa, offline]);
|
|
23
|
+
return _jsx(MobileCtx.Provider, { value: value, children: children });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Hook to access the mobile context.
|
|
27
|
+
*/
|
|
28
|
+
export function useMobileContext() {
|
|
29
|
+
return useContext(MobileCtx);
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { BreakpointName } from '@object-ui/types';
|
|
10
|
+
export interface ResponsiveContainerProps {
|
|
11
|
+
/** Minimum breakpoint to show content */
|
|
12
|
+
minBreakpoint?: BreakpointName;
|
|
13
|
+
/** Maximum breakpoint to show content */
|
|
14
|
+
maxBreakpoint?: BreakpointName;
|
|
15
|
+
/** Show only on specific breakpoints */
|
|
16
|
+
showOn?: BreakpointName[];
|
|
17
|
+
/** Hide on specific breakpoints */
|
|
18
|
+
hideOn?: BreakpointName[];
|
|
19
|
+
/** Fallback content when hidden */
|
|
20
|
+
fallback?: React.ReactNode;
|
|
21
|
+
/** Children */
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Container that conditionally renders children based on the current breakpoint.
|
|
26
|
+
*/
|
|
27
|
+
export declare function ResponsiveContainer({ minBreakpoint, maxBreakpoint, showOn, hideOn, fallback, children, }: ResponsiveContainerProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
//# sourceMappingURL=ResponsiveContainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResponsiveContainer.d.ts","sourceRoot":"","sources":["../src/ResponsiveContainer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,MAAM,WAAW,wBAAwB;IACvC,yCAAyC;IACzC,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,yCAAyC;IACzC,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,wCAAwC;IACxC,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;IAC1B,mCAAmC;IACnC,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;IAC1B,mCAAmC;IACnC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,eAAe;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,aAAa,EACb,MAAM,EACN,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE,wBAAwB,2CAoB1B"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useBreakpoint } from './useBreakpoint';
|
|
3
|
+
/**
|
|
4
|
+
* Container that conditionally renders children based on the current breakpoint.
|
|
5
|
+
*/
|
|
6
|
+
export function ResponsiveContainer({ minBreakpoint, maxBreakpoint, showOn, hideOn, fallback = null, children, }) {
|
|
7
|
+
const { breakpoint, isAbove, isBelow } = useBreakpoint();
|
|
8
|
+
// Check showOn/hideOn lists
|
|
9
|
+
if (showOn && !showOn.includes(breakpoint)) {
|
|
10
|
+
return _jsx(_Fragment, { children: fallback });
|
|
11
|
+
}
|
|
12
|
+
if (hideOn && hideOn.includes(breakpoint)) {
|
|
13
|
+
return _jsx(_Fragment, { children: fallback });
|
|
14
|
+
}
|
|
15
|
+
// Check min/max breakpoints
|
|
16
|
+
if (minBreakpoint && !isAbove(minBreakpoint)) {
|
|
17
|
+
return _jsx(_Fragment, { children: fallback });
|
|
18
|
+
}
|
|
19
|
+
if (maxBreakpoint && !isBelow(maxBreakpoint)) {
|
|
20
|
+
return _jsx(_Fragment, { children: fallback });
|
|
21
|
+
}
|
|
22
|
+
return _jsx(_Fragment, { children: children });
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { BreakpointName, ResponsiveValue } from '@object-ui/types';
|
|
9
|
+
/** Default breakpoint widths (Tailwind CSS compatible) */
|
|
10
|
+
export declare const BREAKPOINTS: Record<BreakpointName, number>;
|
|
11
|
+
/** Ordered breakpoint names from smallest to largest */
|
|
12
|
+
export declare const BREAKPOINT_ORDER: BreakpointName[];
|
|
13
|
+
/**
|
|
14
|
+
* Resolves a responsive value to the appropriate value for the current breakpoint.
|
|
15
|
+
* Falls back to the next smaller breakpoint value if the current one is not defined.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveResponsiveValue<T>(value: ResponsiveValue<T>, currentBreakpoint: BreakpointName): T | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Gets the current breakpoint name based on window width.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getCurrentBreakpoint(width: number): BreakpointName;
|
|
22
|
+
//# sourceMappingURL=breakpoints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"breakpoints.d.ts","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExE,0DAA0D;AAC1D,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAOtD,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAA0C,CAAC;AAExF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,iBAAiB,EAAE,cAAc,GAChC,CAAC,GAAG,SAAS,CAkBf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAQlE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/** Default breakpoint widths (Tailwind CSS compatible) */
|
|
9
|
+
export const BREAKPOINTS = {
|
|
10
|
+
xs: 0,
|
|
11
|
+
sm: 640,
|
|
12
|
+
md: 768,
|
|
13
|
+
lg: 1024,
|
|
14
|
+
xl: 1280,
|
|
15
|
+
'2xl': 1536,
|
|
16
|
+
};
|
|
17
|
+
/** Ordered breakpoint names from smallest to largest */
|
|
18
|
+
export const BREAKPOINT_ORDER = ['xs', 'sm', 'md', 'lg', 'xl', '2xl'];
|
|
19
|
+
/**
|
|
20
|
+
* Resolves a responsive value to the appropriate value for the current breakpoint.
|
|
21
|
+
* Falls back to the next smaller breakpoint value if the current one is not defined.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveResponsiveValue(value, currentBreakpoint) {
|
|
24
|
+
// If it's not an object, it's a direct value
|
|
25
|
+
if (typeof value !== 'object' || value === null) {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
const responsive = value;
|
|
29
|
+
const currentIndex = BREAKPOINT_ORDER.indexOf(currentBreakpoint);
|
|
30
|
+
// Walk backwards from current breakpoint to find a defined value
|
|
31
|
+
for (let i = currentIndex; i >= 0; i--) {
|
|
32
|
+
const bp = BREAKPOINT_ORDER[i];
|
|
33
|
+
if (bp in responsive) {
|
|
34
|
+
return responsive[bp];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Gets the current breakpoint name based on window width.
|
|
41
|
+
*/
|
|
42
|
+
export function getCurrentBreakpoint(width) {
|
|
43
|
+
for (let i = BREAKPOINT_ORDER.length - 1; i >= 0; i--) {
|
|
44
|
+
const bp = BREAKPOINT_ORDER[i];
|
|
45
|
+
if (width >= BREAKPOINTS[bp]) {
|
|
46
|
+
return bp;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return 'xs';
|
|
50
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @object-ui/mobile
|
|
10
|
+
*
|
|
11
|
+
* Mobile optimization for Object UI providing:
|
|
12
|
+
* - useBreakpoint / useResponsive hooks for responsive behavior
|
|
13
|
+
* - useGesture hook for touch gesture detection
|
|
14
|
+
* - PWA utilities (manifest generation, service worker registration)
|
|
15
|
+
* - Mobile-aware component wrappers
|
|
16
|
+
* - Pull-to-refresh and infinite scroll support
|
|
17
|
+
*
|
|
18
|
+
* @packageDocumentation
|
|
19
|
+
*/
|
|
20
|
+
export { useBreakpoint, type BreakpointState } from './useBreakpoint';
|
|
21
|
+
export { useResponsive } from './useResponsive';
|
|
22
|
+
export { useGesture, type UseGestureOptions } from './useGesture';
|
|
23
|
+
export { usePullToRefresh, type PullToRefreshOptions } from './usePullToRefresh';
|
|
24
|
+
export { MobileProvider, type MobileProviderProps } from './MobileProvider';
|
|
25
|
+
export { ResponsiveContainer, type ResponsiveContainerProps } from './ResponsiveContainer';
|
|
26
|
+
export { generatePWAManifest } from './pwa';
|
|
27
|
+
export { registerServiceWorker, type ServiceWorkerConfig } from './serviceWorker';
|
|
28
|
+
export { BREAKPOINTS, resolveResponsiveValue } from './breakpoints';
|
|
29
|
+
export type { BreakpointName, ResponsiveValue, ResponsiveConfig, MobileOverrides, PWAConfig, PWAIcon, CacheStrategy, OfflineConfig, OfflineRoute, GestureType, GestureConfig, GestureContext, MobileComponentConfig, } from '@object-ui/types';
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,iBAAiB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAGpE,YAAY,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,OAAO,EACP,aAAa,EACb,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,EACb,cAAc,EACd,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @object-ui/mobile
|
|
10
|
+
*
|
|
11
|
+
* Mobile optimization for Object UI providing:
|
|
12
|
+
* - useBreakpoint / useResponsive hooks for responsive behavior
|
|
13
|
+
* - useGesture hook for touch gesture detection
|
|
14
|
+
* - PWA utilities (manifest generation, service worker registration)
|
|
15
|
+
* - Mobile-aware component wrappers
|
|
16
|
+
* - Pull-to-refresh and infinite scroll support
|
|
17
|
+
*
|
|
18
|
+
* @packageDocumentation
|
|
19
|
+
*/
|
|
20
|
+
export { useBreakpoint } from './useBreakpoint';
|
|
21
|
+
export { useResponsive } from './useResponsive';
|
|
22
|
+
export { useGesture } from './useGesture';
|
|
23
|
+
export { usePullToRefresh } from './usePullToRefresh';
|
|
24
|
+
export { MobileProvider } from './MobileProvider';
|
|
25
|
+
export { ResponsiveContainer } from './ResponsiveContainer';
|
|
26
|
+
export { generatePWAManifest } from './pwa';
|
|
27
|
+
export { registerServiceWorker } from './serviceWorker';
|
|
28
|
+
export { BREAKPOINTS, resolveResponsiveValue } from './breakpoints';
|
package/dist/pwa.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { PWAConfig } from '@object-ui/types';
|
|
9
|
+
/**
|
|
10
|
+
* Generates a Web App Manifest object from PWAConfig.
|
|
11
|
+
* Can be serialized to JSON and served at /manifest.json.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generatePWAManifest(config: PWAConfig): Record<string, unknown>;
|
|
14
|
+
//# sourceMappingURL=pwa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../src/pwa.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkB9E"}
|
package/dist/pwa.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Generates a Web App Manifest object from PWAConfig.
|
|
10
|
+
* Can be serialized to JSON and served at /manifest.json.
|
|
11
|
+
*/
|
|
12
|
+
export function generatePWAManifest(config) {
|
|
13
|
+
return {
|
|
14
|
+
name: config.name,
|
|
15
|
+
short_name: config.shortName,
|
|
16
|
+
description: config.description,
|
|
17
|
+
theme_color: config.themeColor ?? '#3b82f6',
|
|
18
|
+
background_color: config.backgroundColor ?? '#ffffff',
|
|
19
|
+
display: config.display ?? 'standalone',
|
|
20
|
+
start_url: config.startUrl ?? '/',
|
|
21
|
+
scope: config.scope ?? '/',
|
|
22
|
+
orientation: config.orientation ?? 'any',
|
|
23
|
+
icons: config.icons?.map((icon) => ({
|
|
24
|
+
src: icon.src,
|
|
25
|
+
sizes: icon.sizes,
|
|
26
|
+
type: icon.type ?? 'image/png',
|
|
27
|
+
purpose: icon.purpose ?? 'any',
|
|
28
|
+
})) ?? [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
export interface ServiceWorkerConfig {
|
|
9
|
+
/** URL of the service worker script */
|
|
10
|
+
url?: string;
|
|
11
|
+
/** Registration scope */
|
|
12
|
+
scope?: string;
|
|
13
|
+
/** Callback on successful registration */
|
|
14
|
+
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
|
15
|
+
/** Callback on update available */
|
|
16
|
+
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
|
17
|
+
/** Callback on error */
|
|
18
|
+
onError?: (error: Error) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Registers a service worker for PWA support.
|
|
22
|
+
* Safe to call in non-browser environments (no-op).
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerServiceWorker(config?: ServiceWorkerConfig): Promise<ServiceWorkerRegistration | null>;
|
|
25
|
+
//# sourceMappingURL=serviceWorker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceWorker.d.ts","sourceRoot":"","sources":["../src/serviceWorker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,SAAS,CAAC,EAAE,CAAC,YAAY,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAC9D,mCAAmC;IACnC,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAC7D,wBAAwB;IACxB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,GAAE,mBAAwB,GAC/B,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAmC3C"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Registers a service worker for PWA support.
|
|
10
|
+
* Safe to call in non-browser environments (no-op).
|
|
11
|
+
*/
|
|
12
|
+
export async function registerServiceWorker(config = {}) {
|
|
13
|
+
if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const swUrl = config.url ?? '/service-worker.js';
|
|
17
|
+
try {
|
|
18
|
+
const registration = await navigator.serviceWorker.register(swUrl, {
|
|
19
|
+
scope: config.scope ?? '/',
|
|
20
|
+
});
|
|
21
|
+
registration.onupdatefound = () => {
|
|
22
|
+
const installingWorker = registration.installing;
|
|
23
|
+
if (!installingWorker)
|
|
24
|
+
return;
|
|
25
|
+
installingWorker.onstatechange = () => {
|
|
26
|
+
if (installingWorker.state === 'installed') {
|
|
27
|
+
if (navigator.serviceWorker.controller) {
|
|
28
|
+
// Update available
|
|
29
|
+
config.onUpdate?.(registration);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// First install
|
|
33
|
+
config.onSuccess?.(registration);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
return registration;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
42
|
+
config.onError?.(err);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { BreakpointName } from '@object-ui/types';
|
|
9
|
+
export interface BreakpointState {
|
|
10
|
+
/** Current breakpoint name */
|
|
11
|
+
breakpoint: BreakpointName;
|
|
12
|
+
/** Current window width */
|
|
13
|
+
width: number;
|
|
14
|
+
/** Whether the screen is mobile sized (< md) */
|
|
15
|
+
isMobile: boolean;
|
|
16
|
+
/** Whether the screen is tablet sized (md-lg) */
|
|
17
|
+
isTablet: boolean;
|
|
18
|
+
/** Whether the screen is desktop sized (>= lg) */
|
|
19
|
+
isDesktop: boolean;
|
|
20
|
+
/** Check if current breakpoint is at or above the given breakpoint */
|
|
21
|
+
isAbove: (bp: BreakpointName) => boolean;
|
|
22
|
+
/** Check if current breakpoint is at or below the given breakpoint */
|
|
23
|
+
isBelow: (bp: BreakpointName) => boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Hook that tracks the current responsive breakpoint.
|
|
27
|
+
* Updates on window resize with debouncing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function useBreakpoint(): BreakpointState;
|
|
30
|
+
//# sourceMappingURL=useBreakpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBreakpoint.d.ts","sourceRoot":"","sources":["../src/useBreakpoint.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,UAAU,EAAE,cAAc,CAAC;IAC3B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,QAAQ,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,QAAQ,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,SAAS,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,OAAO,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,OAAO,CAAC;IACzC,sEAAsE;IACtE,OAAO,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,OAAO,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,eAAe,CAmC/C"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useEffect } from 'react';
|
|
9
|
+
import { BREAKPOINTS, BREAKPOINT_ORDER, getCurrentBreakpoint } from './breakpoints';
|
|
10
|
+
/**
|
|
11
|
+
* Hook that tracks the current responsive breakpoint.
|
|
12
|
+
* Updates on window resize with debouncing.
|
|
13
|
+
*/
|
|
14
|
+
export function useBreakpoint() {
|
|
15
|
+
const [width, setWidth] = useState(() => typeof window !== 'undefined' ? window.innerWidth : 1024);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (typeof window === 'undefined')
|
|
18
|
+
return;
|
|
19
|
+
let timeoutId;
|
|
20
|
+
const handleResize = () => {
|
|
21
|
+
clearTimeout(timeoutId);
|
|
22
|
+
timeoutId = setTimeout(() => {
|
|
23
|
+
setWidth(window.innerWidth);
|
|
24
|
+
}, 100);
|
|
25
|
+
};
|
|
26
|
+
window.addEventListener('resize', handleResize);
|
|
27
|
+
return () => {
|
|
28
|
+
clearTimeout(timeoutId);
|
|
29
|
+
window.removeEventListener('resize', handleResize);
|
|
30
|
+
};
|
|
31
|
+
}, []);
|
|
32
|
+
const breakpoint = getCurrentBreakpoint(width);
|
|
33
|
+
const bpIndex = BREAKPOINT_ORDER.indexOf(breakpoint);
|
|
34
|
+
return {
|
|
35
|
+
breakpoint,
|
|
36
|
+
width,
|
|
37
|
+
isMobile: bpIndex < BREAKPOINT_ORDER.indexOf('md'),
|
|
38
|
+
isTablet: bpIndex >= BREAKPOINT_ORDER.indexOf('md') && bpIndex < BREAKPOINT_ORDER.indexOf('lg'),
|
|
39
|
+
isDesktop: bpIndex >= BREAKPOINT_ORDER.indexOf('lg'),
|
|
40
|
+
isAbove: (bp) => width >= BREAKPOINTS[bp],
|
|
41
|
+
isBelow: (bp) => width < BREAKPOINTS[bp],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { GestureType, GestureContext } from '@object-ui/types';
|
|
9
|
+
export interface UseGestureOptions {
|
|
10
|
+
/** Gesture type to detect */
|
|
11
|
+
type: GestureType;
|
|
12
|
+
/** Callback when gesture is detected */
|
|
13
|
+
onGesture: (context: GestureContext) => void;
|
|
14
|
+
/** Minimum distance for swipe detection (pixels) */
|
|
15
|
+
threshold?: number;
|
|
16
|
+
/** Duration for long-press detection (milliseconds) */
|
|
17
|
+
longPressDuration?: number;
|
|
18
|
+
/** Whether gesture detection is enabled */
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Hook for detecting touch gestures on an element.
|
|
23
|
+
* Returns a ref to attach to the target element.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* const gestureRef = useGesture({
|
|
28
|
+
* type: 'swipe-left',
|
|
29
|
+
* onGesture: (ctx) => console.log('Swiped left!', ctx),
|
|
30
|
+
* });
|
|
31
|
+
* return <div ref={gestureRef}>Swipe me</div>;
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function useGesture<T extends HTMLElement = HTMLElement>(options: UseGestureOptions): import("react").RefObject<T | null>;
|
|
35
|
+
//# sourceMappingURL=useGesture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useGesture.d.ts","sourceRoot":"","sources":["../src/useGesture.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,wCAAwC;IACxC,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC5D,OAAO,EAAE,iBAAiB,uCAkH3B"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect, useRef, useCallback } from 'react';
|
|
9
|
+
/**
|
|
10
|
+
* Hook for detecting touch gestures on an element.
|
|
11
|
+
* Returns a ref to attach to the target element.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const gestureRef = useGesture({
|
|
16
|
+
* type: 'swipe-left',
|
|
17
|
+
* onGesture: (ctx) => console.log('Swiped left!', ctx),
|
|
18
|
+
* });
|
|
19
|
+
* return <div ref={gestureRef}>Swipe me</div>;
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function useGesture(options) {
|
|
23
|
+
const ref = useRef(null);
|
|
24
|
+
const startRef = useRef(null);
|
|
25
|
+
const longPressTimerRef = useRef(undefined);
|
|
26
|
+
const { type, onGesture, threshold = 50, longPressDuration = 500, enabled = true } = options;
|
|
27
|
+
const handleTouchStart = useCallback((e) => {
|
|
28
|
+
if (!enabled)
|
|
29
|
+
return;
|
|
30
|
+
const touch = e.touches[0];
|
|
31
|
+
startRef.current = { x: touch.clientX, y: touch.clientY, time: Date.now() };
|
|
32
|
+
if (type === 'long-press') {
|
|
33
|
+
longPressTimerRef.current = setTimeout(() => {
|
|
34
|
+
if (startRef.current) {
|
|
35
|
+
onGesture({
|
|
36
|
+
type: 'long-press',
|
|
37
|
+
startPosition: { x: startRef.current.x, y: startRef.current.y },
|
|
38
|
+
endPosition: { x: startRef.current.x, y: startRef.current.y },
|
|
39
|
+
distance: 0,
|
|
40
|
+
duration: longPressDuration,
|
|
41
|
+
velocity: 0,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}, longPressDuration);
|
|
45
|
+
}
|
|
46
|
+
}, [type, onGesture, longPressDuration, enabled]);
|
|
47
|
+
const handleTouchEnd = useCallback((e) => {
|
|
48
|
+
if (!enabled || !startRef.current)
|
|
49
|
+
return;
|
|
50
|
+
clearTimeout(longPressTimerRef.current);
|
|
51
|
+
const touch = e.changedTouches[0];
|
|
52
|
+
const dx = touch.clientX - startRef.current.x;
|
|
53
|
+
const dy = touch.clientY - startRef.current.y;
|
|
54
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
55
|
+
const duration = Date.now() - startRef.current.time;
|
|
56
|
+
const velocity = distance / Math.max(duration, 1);
|
|
57
|
+
const context = {
|
|
58
|
+
type,
|
|
59
|
+
startPosition: { x: startRef.current.x, y: startRef.current.y },
|
|
60
|
+
endPosition: { x: touch.clientX, y: touch.clientY },
|
|
61
|
+
distance,
|
|
62
|
+
duration,
|
|
63
|
+
velocity,
|
|
64
|
+
};
|
|
65
|
+
// Detect gesture type
|
|
66
|
+
if (type === 'tap' && distance < 10 && duration < 300) {
|
|
67
|
+
onGesture(context);
|
|
68
|
+
}
|
|
69
|
+
else if (type === 'double-tap') {
|
|
70
|
+
// Double-tap handled separately (simplified)
|
|
71
|
+
if (distance < 10 && duration < 300) {
|
|
72
|
+
onGesture(context);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else if (distance >= threshold) {
|
|
76
|
+
const absX = Math.abs(dx);
|
|
77
|
+
const absY = Math.abs(dy);
|
|
78
|
+
let direction;
|
|
79
|
+
if (absX > absY) {
|
|
80
|
+
direction = dx > 0 ? 'right' : 'left';
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
direction = dy > 0 ? 'down' : 'up';
|
|
84
|
+
}
|
|
85
|
+
context.direction = direction;
|
|
86
|
+
if ((type === 'swipe-left' && direction === 'left') ||
|
|
87
|
+
(type === 'swipe-right' && direction === 'right') ||
|
|
88
|
+
(type === 'swipe-up' && direction === 'up') ||
|
|
89
|
+
(type === 'swipe-down' && direction === 'down') ||
|
|
90
|
+
type === 'pan') {
|
|
91
|
+
onGesture(context);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
startRef.current = null;
|
|
95
|
+
}, [type, onGesture, threshold, enabled]);
|
|
96
|
+
const handleTouchMove = useCallback(() => {
|
|
97
|
+
// Cancel long-press if finger moves
|
|
98
|
+
if (type === 'long-press') {
|
|
99
|
+
clearTimeout(longPressTimerRef.current);
|
|
100
|
+
}
|
|
101
|
+
}, [type]);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
const el = ref.current;
|
|
104
|
+
if (!el || !enabled)
|
|
105
|
+
return;
|
|
106
|
+
el.addEventListener('touchstart', handleTouchStart, { passive: true });
|
|
107
|
+
el.addEventListener('touchend', handleTouchEnd, { passive: true });
|
|
108
|
+
el.addEventListener('touchmove', handleTouchMove, { passive: true });
|
|
109
|
+
return () => {
|
|
110
|
+
el.removeEventListener('touchstart', handleTouchStart);
|
|
111
|
+
el.removeEventListener('touchend', handleTouchEnd);
|
|
112
|
+
el.removeEventListener('touchmove', handleTouchMove);
|
|
113
|
+
clearTimeout(longPressTimerRef.current);
|
|
114
|
+
};
|
|
115
|
+
}, [handleTouchStart, handleTouchEnd, handleTouchMove, enabled]);
|
|
116
|
+
return ref;
|
|
117
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
export interface PullToRefreshOptions {
|
|
9
|
+
/** Callback when pull-to-refresh is triggered */
|
|
10
|
+
onRefresh: () => Promise<void>;
|
|
11
|
+
/** Minimum pull distance to trigger refresh (pixels) */
|
|
12
|
+
threshold?: number;
|
|
13
|
+
/** Whether pull-to-refresh is enabled */
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Hook for implementing pull-to-refresh behavior.
|
|
18
|
+
* Returns a ref to attach to the scrollable container.
|
|
19
|
+
*/
|
|
20
|
+
export declare function usePullToRefresh<T extends HTMLElement = HTMLElement>(options: PullToRefreshOptions): {
|
|
21
|
+
ref: import("react").RefObject<T | null>;
|
|
22
|
+
isRefreshing: boolean;
|
|
23
|
+
pullDistance: number;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=usePullToRefresh.d.ts.map
|
|
@@ -0,0 +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;;;;EA6D9B"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
9
|
+
/**
|
|
10
|
+
* Hook for implementing pull-to-refresh behavior.
|
|
11
|
+
* Returns a ref to attach to the scrollable container.
|
|
12
|
+
*/
|
|
13
|
+
export function usePullToRefresh(options) {
|
|
14
|
+
const { onRefresh, threshold = 80, enabled = true } = options;
|
|
15
|
+
const ref = useRef(null);
|
|
16
|
+
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
17
|
+
const [pullDistance, setPullDistance] = useState(0);
|
|
18
|
+
const startYRef = useRef(0);
|
|
19
|
+
const handleTouchStart = useCallback((e) => {
|
|
20
|
+
if (!enabled || isRefreshing)
|
|
21
|
+
return;
|
|
22
|
+
const el = ref.current;
|
|
23
|
+
if (el && el.scrollTop === 0) {
|
|
24
|
+
startYRef.current = e.touches[0].clientY;
|
|
25
|
+
}
|
|
26
|
+
}, [enabled, isRefreshing]);
|
|
27
|
+
const handleTouchMove = useCallback((e) => {
|
|
28
|
+
if (!enabled || isRefreshing || !startYRef.current)
|
|
29
|
+
return;
|
|
30
|
+
const currentY = e.touches[0].clientY;
|
|
31
|
+
const diff = currentY - startYRef.current;
|
|
32
|
+
if (diff > 0) {
|
|
33
|
+
setPullDistance(Math.min(diff, threshold * 1.5));
|
|
34
|
+
}
|
|
35
|
+
}, [enabled, isRefreshing, threshold]);
|
|
36
|
+
const handleTouchEnd = useCallback(async () => {
|
|
37
|
+
if (!enabled || isRefreshing)
|
|
38
|
+
return;
|
|
39
|
+
if (pullDistance >= threshold) {
|
|
40
|
+
setIsRefreshing(true);
|
|
41
|
+
try {
|
|
42
|
+
await onRefresh();
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
setIsRefreshing(false);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
setPullDistance(0);
|
|
49
|
+
startYRef.current = 0;
|
|
50
|
+
}, [enabled, isRefreshing, pullDistance, threshold, onRefresh]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const el = ref.current;
|
|
53
|
+
if (!el || !enabled)
|
|
54
|
+
return;
|
|
55
|
+
el.addEventListener('touchstart', handleTouchStart, { passive: true });
|
|
56
|
+
el.addEventListener('touchmove', handleTouchMove, { passive: true });
|
|
57
|
+
el.addEventListener('touchend', handleTouchEnd, { passive: true });
|
|
58
|
+
return () => {
|
|
59
|
+
el.removeEventListener('touchstart', handleTouchStart);
|
|
60
|
+
el.removeEventListener('touchmove', handleTouchMove);
|
|
61
|
+
el.removeEventListener('touchend', handleTouchEnd);
|
|
62
|
+
};
|
|
63
|
+
}, [handleTouchStart, handleTouchMove, handleTouchEnd, enabled]);
|
|
64
|
+
return { ref, isRefreshing, pullDistance };
|
|
65
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import type { ResponsiveValue } from '@object-ui/types';
|
|
9
|
+
/**
|
|
10
|
+
* Hook that resolves a responsive value based on the current breakpoint.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const columns = useResponsive({ xs: 1, sm: 2, lg: 3 });
|
|
15
|
+
* // columns = 1 on mobile, 2 on sm/md, 3 on lg+
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function useResponsive<T>(value: ResponsiveValue<T>): T | undefined;
|
|
19
|
+
//# sourceMappingURL=useResponsive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useResponsive.d.ts","sourceRoot":"","sources":["../src/useResponsive.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAIxD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAOzE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { useMemo } from 'react';
|
|
9
|
+
import { useBreakpoint } from './useBreakpoint';
|
|
10
|
+
import { resolveResponsiveValue } from './breakpoints';
|
|
11
|
+
/**
|
|
12
|
+
* Hook that resolves a responsive value based on the current breakpoint.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const columns = useResponsive({ xs: 1, sm: 2, lg: 3 });
|
|
17
|
+
* // columns = 1 on mobile, 2 on sm/md, 3 on lg+
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function useResponsive(value) {
|
|
21
|
+
const { breakpoint } = useBreakpoint();
|
|
22
|
+
return useMemo(() => resolveResponsiveValue(value, breakpoint), [value, breakpoint]);
|
|
23
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@object-ui/mobile",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Mobile optimization for Object UI with responsive components, PWA support, and touch gesture handling.",
|
|
7
|
+
"homepage": "https://www.objectui.org",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/objectstack-ai/objectui.git",
|
|
11
|
+
"directory": "packages/mobile"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@object-ui/types": "0.5.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/react": "^19.2.13",
|
|
33
|
+
"react": "^19.1.0",
|
|
34
|
+
"typescript": "^5.9.3",
|
|
35
|
+
"vitest": "^4.0.18"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"clean": "rm -rf dist",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"type-check": "tsc --noEmit",
|
|
42
|
+
"lint": "eslint ."
|
|
43
|
+
}
|
|
44
|
+
}
|