@shopify/shop-minis-react 0.0.0-snapshot.20251219164319 → 0.0.0-snapshot.20251222174301
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/dist/components/navigation/minis-router.js +8 -15
- package/dist/components/navigation/minis-router.js.map +1 -1
- package/dist/hooks/intents/useBarcodeScanner.js +10 -0
- package/dist/hooks/intents/useBarcodeScanner.js.map +1 -0
- package/dist/hooks/intents/useContentShare.js +10 -0
- package/dist/hooks/intents/useContentShare.js.map +1 -0
- package/dist/hooks/intents/useImageCapture.js +10 -0
- package/dist/hooks/intents/useImageCapture.js.map +1 -0
- package/dist/hooks/intents/useLocationSelection.js +10 -0
- package/dist/hooks/intents/useLocationSelection.js.map +1 -0
- package/dist/hooks/intents/usePaymentRequest.js +10 -0
- package/dist/hooks/intents/usePaymentRequest.js.map +1 -0
- package/dist/hooks/intents/useProductSelection.js +10 -0
- package/dist/hooks/intents/useProductSelection.js.map +1 -0
- package/dist/hooks/navigation/useNavigateWithTransition.js +6 -6
- package/dist/hooks/navigation/useNavigateWithTransition.js.map +1 -1
- package/dist/index.js +131 -118
- package/dist/index.js.map +1 -1
- package/dist/internal/useHandleIntent.js +26 -0
- package/dist/internal/useHandleIntent.js.map +1 -0
- package/dist/internal/useShopIntents.js +7 -0
- package/dist/internal/useShopIntents.js.map +1 -0
- package/dist/mocks.js +10 -11
- package/dist/mocks.js.map +1 -1
- package/dist/shop-minis-platform/src/intents/shared.js +19 -0
- package/dist/shop-minis-platform/src/intents/shared.js.map +1 -0
- package/package.json +2 -2
- package/src/components/navigation/minis-router.tsx +1 -9
- package/src/hooks/index.ts +3 -1
- package/src/hooks/intents/index.ts +42 -0
- package/src/hooks/intents/useBarcodeScanner.ts +43 -0
- package/src/hooks/intents/useContentShare.ts +47 -0
- package/src/hooks/intents/useImageCapture.ts +45 -0
- package/src/hooks/intents/useLocationSelection.ts +45 -0
- package/src/hooks/intents/usePaymentRequest.ts +47 -0
- package/src/hooks/intents/useProductSelection.ts +45 -0
- package/src/hooks/navigation/useNavigateWithTransition.test.ts +3 -17
- package/src/hooks/navigation/useNavigateWithTransition.ts +4 -9
- package/src/internal/useHandleIntent.ts +101 -0
- package/src/internal/useShopIntents.ts +13 -0
- package/src/mocks.ts +0 -1
- package/dist/hooks/events/useOnNavigateBack.js +0 -14
- package/dist/hooks/events/useOnNavigateBack.js.map +0 -1
- package/dist/internal/navigation-manager.js +0 -28
- package/dist/internal/navigation-manager.js.map +0 -1
- package/src/hooks/events/useOnNavigateBack.ts +0 -16
- package/src/internal/navigation-manager.tsx +0 -41
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useHandleIntent,
|
|
3
|
+
HandleIntentOptions,
|
|
4
|
+
} from '../../internal/useHandleIntent'
|
|
5
|
+
import {useShopIntents} from '../../internal/useShopIntents'
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
SelectProductIntentParams,
|
|
9
|
+
SelectProductIntentResult,
|
|
10
|
+
} from '@shopify/shop-minis-platform'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Hook to request user to select products
|
|
14
|
+
*
|
|
15
|
+
* This intent shows a product selection screen where the user can browse
|
|
16
|
+
* and select one or more products from the Shop catalog.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const selectProducts = useProductSelection({
|
|
20
|
+
* onCancel: () => console.log('User cancelled'),
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* const result = await selectProducts({
|
|
24
|
+
* multiSelect: true,
|
|
25
|
+
* maxSelections: 5,
|
|
26
|
+
* title: 'Choose your favorites'
|
|
27
|
+
* })
|
|
28
|
+
*
|
|
29
|
+
* if (result) {
|
|
30
|
+
* console.log('Selected products:', result.products)
|
|
31
|
+
* }
|
|
32
|
+
*/
|
|
33
|
+
export const useProductSelection = (
|
|
34
|
+
options?: HandleIntentOptions<SelectProductIntentResult>
|
|
35
|
+
) => {
|
|
36
|
+
const intents = useShopIntents()
|
|
37
|
+
return useHandleIntent(intents.selectProduct, options)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Type helper for the product selection function
|
|
42
|
+
*/
|
|
43
|
+
export type ProductSelectionFunction = (
|
|
44
|
+
params: SelectProductIntentParams
|
|
45
|
+
) => Promise<SelectProductIntentResult | void>
|
|
@@ -290,29 +290,15 @@ describe('useNavigateWithTransition', () => {
|
|
|
290
290
|
})
|
|
291
291
|
})
|
|
292
292
|
|
|
293
|
-
it('handles navigation to root path
|
|
294
|
-
// Mock history state with idx
|
|
295
|
-
const originalHistoryState = window.history.state
|
|
296
|
-
Object.defineProperty(window.history, 'state', {
|
|
297
|
-
value: {idx: 3},
|
|
298
|
-
writable: true,
|
|
299
|
-
configurable: true,
|
|
300
|
-
})
|
|
301
|
-
|
|
293
|
+
it('handles navigation to root path', async () => {
|
|
302
294
|
const {result} = renderHook(() => useNavigateWithTransition())
|
|
303
295
|
|
|
304
296
|
await act(async () => {
|
|
305
297
|
result.current('/')
|
|
306
298
|
})
|
|
307
299
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
// Restore original state
|
|
312
|
-
Object.defineProperty(window.history, 'state', {
|
|
313
|
-
value: originalHistoryState,
|
|
314
|
-
writable: true,
|
|
315
|
-
configurable: true,
|
|
300
|
+
expect(mockNavigate).toHaveBeenCalledWith('/', {
|
|
301
|
+
replace: false,
|
|
316
302
|
})
|
|
317
303
|
})
|
|
318
304
|
|
|
@@ -38,19 +38,14 @@ export function useNavigateWithTransition(): UseNavigateWithTransitionReturns {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const isSameRoute = to === location.pathname
|
|
41
|
-
const isNavigatingToHomeRoute = to === '/'
|
|
42
41
|
|
|
43
42
|
// Path navigation - with options
|
|
44
43
|
if (document.startViewTransition) {
|
|
45
44
|
const transition = document.startViewTransition(() => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
replace: isSameRoute,
|
|
51
|
-
...options,
|
|
52
|
-
})
|
|
53
|
-
}
|
|
45
|
+
navigate(to, {
|
|
46
|
+
replace: isSameRoute,
|
|
47
|
+
...options,
|
|
48
|
+
})
|
|
54
49
|
|
|
55
50
|
if (options?.preventScrollReset !== true) {
|
|
56
51
|
window.scrollTo(0, 0)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {useCallback} from 'react'
|
|
2
|
+
|
|
3
|
+
import {IntentResultCode} from '@shopify/shop-minis-platform'
|
|
4
|
+
|
|
5
|
+
import type {ShopIntentResult} from '@shopify/shop-minis-platform'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Options for handling intent results
|
|
9
|
+
*/
|
|
10
|
+
export interface HandleIntentOptions<T> {
|
|
11
|
+
/**
|
|
12
|
+
* Handler called when user cancels the intent
|
|
13
|
+
* If not provided, cancellation is treated as a no-op (returns undefined)
|
|
14
|
+
*/
|
|
15
|
+
onCancel?: () => T | void
|
|
16
|
+
/**
|
|
17
|
+
* Handler called when intent fails
|
|
18
|
+
* If not provided, throws the error
|
|
19
|
+
*/
|
|
20
|
+
onError?: (error: {message: string; code?: string}) => T | void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Wrapper to handle intent results
|
|
25
|
+
*
|
|
26
|
+
* Unlike actions which can reject, intents ALWAYS resolve with a result code.
|
|
27
|
+
* This hook provides a cleaner API for handling intent results:
|
|
28
|
+
*
|
|
29
|
+
* - SUCCESS: Returns the data
|
|
30
|
+
* - CANCELLED: Calls onCancel or returns undefined
|
|
31
|
+
* - FAILED: Calls onError or throws the error
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const selectProduct = useHandleIntent(intents.selectProduct, {
|
|
35
|
+
* onCancel: () => console.log('User cancelled'),
|
|
36
|
+
* onError: (error) => console.error('Selection failed:', error)
|
|
37
|
+
* })
|
|
38
|
+
*
|
|
39
|
+
* const result = await selectProduct({multiSelect: true})
|
|
40
|
+
* // result is the data, or undefined if cancelled
|
|
41
|
+
*/
|
|
42
|
+
export const useHandleIntent = <T, Args extends unknown[]>(
|
|
43
|
+
intent: (...args: Args) => Promise<ShopIntentResult<T>>,
|
|
44
|
+
options?: HandleIntentOptions<T>
|
|
45
|
+
) => {
|
|
46
|
+
return useCallback(
|
|
47
|
+
async (...args: Args): Promise<T | void> => {
|
|
48
|
+
const result = await intent(...args)
|
|
49
|
+
|
|
50
|
+
switch (result.resultCode) {
|
|
51
|
+
case IntentResultCode.SUCCESS:
|
|
52
|
+
return result.data!
|
|
53
|
+
|
|
54
|
+
case IntentResultCode.CANCELLED:
|
|
55
|
+
if (options?.onCancel) {
|
|
56
|
+
return options.onCancel()
|
|
57
|
+
}
|
|
58
|
+
return undefined
|
|
59
|
+
|
|
60
|
+
case IntentResultCode.FAILED:
|
|
61
|
+
if (options?.onError) {
|
|
62
|
+
return options.onError(result.error!)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw new Error(result.error?.message || 'Intent failed')
|
|
66
|
+
|
|
67
|
+
default: {
|
|
68
|
+
// Exhaustiveness check
|
|
69
|
+
const _exhaustive: never = result.resultCode
|
|
70
|
+
throw new Error(`Unhandled result code: ${_exhaustive}`)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
[intent, options]
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Alternative: Manual result handling without throwing
|
|
80
|
+
*
|
|
81
|
+
* Use this when you want to handle all result codes explicitly
|
|
82
|
+
* without throwing errors.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const selectProduct = useHandleIntentManual(intents.selectProduct)
|
|
86
|
+
*
|
|
87
|
+
* const result = await selectProduct({multiSelect: true})
|
|
88
|
+
* if (result.resultCode === 'SUCCESS') {
|
|
89
|
+
* console.log(result.data)
|
|
90
|
+
* }
|
|
91
|
+
*/
|
|
92
|
+
export const useHandleIntentManual = <T, Args extends unknown[]>(
|
|
93
|
+
intent: (...args: Args) => Promise<ShopIntentResult<T>>
|
|
94
|
+
) => {
|
|
95
|
+
return useCallback(
|
|
96
|
+
(...args: Args) => {
|
|
97
|
+
return intent(...args)
|
|
98
|
+
},
|
|
99
|
+
[intent]
|
|
100
|
+
)
|
|
101
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type {ShopIntents} from '@shopify/shop-minis-platform'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Internal hook to access Shop Intents
|
|
5
|
+
*
|
|
6
|
+
* This provides direct access to window.minisIntents
|
|
7
|
+
* Use specific intent hooks (like useProductSelection) instead of this hook directly
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export function useShopIntents(): ShopIntents {
|
|
12
|
+
return window.minisIntents
|
|
13
|
+
}
|
package/src/mocks.ts
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { useRef as i, useEffect as o } from "react";
|
|
2
|
-
function s(n) {
|
|
3
|
-
const e = i(n);
|
|
4
|
-
e.current = n, o(() => {
|
|
5
|
-
const t = window.minisEvents.on("NAVIGATE_BACK", () => {
|
|
6
|
-
e.current();
|
|
7
|
-
});
|
|
8
|
-
return () => window.minisEvents.off(t);
|
|
9
|
-
}, []);
|
|
10
|
-
}
|
|
11
|
-
export {
|
|
12
|
-
s as useOnNavigateBack
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=useOnNavigateBack.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useOnNavigateBack.js","sources":["../../../src/hooks/events/useOnNavigateBack.ts"],"sourcesContent":["import {useEffect, useRef} from 'react'\n\nexport function useOnNavigateBack(callback: () => void) {\n // Using a ref allows the callback to be updated without triggering a re-render\n // This makes the hook nicer to use because developers don't need useCallback\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useEffect(() => {\n const listenerId = window.minisEvents.on('NAVIGATE_BACK', () => {\n callbackRef.current()\n })\n\n return () => window.minisEvents.off(listenerId)\n }, [])\n}\n"],"names":["useOnNavigateBack","callback","callbackRef","useRef","useEffect","listenerId"],"mappings":";AAEO,SAASA,EAAkBC,GAAsB;AAGhD,QAAAC,IAAcC,EAAOF,CAAQ;AACnC,EAAAC,EAAY,UAAUD,GAEtBG,EAAU,MAAM;AACd,UAAMC,IAAa,OAAO,YAAY,GAAG,iBAAiB,MAAM;AAC9D,MAAAH,EAAY,QAAQ;AAAA,IAAA,CACrB;AAED,WAAO,MAAM,OAAO,YAAY,IAAIG,CAAU;AAAA,EAChD,GAAG,EAAE;AACP;"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { useEffect as e } from "react";
|
|
2
|
-
import { useLocation as a } from "../shop-minis-react/node_modules/.pnpm/react-router@7.7.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/react-router/dist/development/chunk-EF7DTUVF.js";
|
|
3
|
-
import { useOnNavigateBack as r } from "../hooks/events/useOnNavigateBack.js";
|
|
4
|
-
import { useNavigateWithTransition as s } from "../hooks/navigation/useNavigateWithTransition.js";
|
|
5
|
-
import { useShopActions as h } from "./useShopActions.js";
|
|
6
|
-
function u() {
|
|
7
|
-
const t = a(), i = s(), { reportNavigationState: o } = h();
|
|
8
|
-
return e(() => {
|
|
9
|
-
const n = {
|
|
10
|
-
location: {
|
|
11
|
-
pathname: t.pathname,
|
|
12
|
-
search: t.search,
|
|
13
|
-
hash: t.hash,
|
|
14
|
-
key: t.key,
|
|
15
|
-
state: t.state
|
|
16
|
-
},
|
|
17
|
-
historyLength: typeof window > "u" ? 0 : window.history.length,
|
|
18
|
-
historyIndex: typeof window > "u" || !window.history.state?.idx ? null : window.history.state.idx
|
|
19
|
-
};
|
|
20
|
-
o(n);
|
|
21
|
-
}, [t, o]), r(() => {
|
|
22
|
-
i(-1);
|
|
23
|
-
}), null;
|
|
24
|
-
}
|
|
25
|
-
export {
|
|
26
|
-
u as NavigationManager
|
|
27
|
-
};
|
|
28
|
-
//# sourceMappingURL=navigation-manager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-manager.js","sources":["../../src/internal/navigation-manager.tsx"],"sourcesContent":["import {useEffect} from 'react'\n\nimport {useLocation} from 'react-router'\n\nimport {useOnNavigateBack} from '../hooks/events/useOnNavigateBack'\nimport {useNavigateWithTransition} from '../hooks/navigation/useNavigateWithTransition'\n\nimport {useShopActions} from './useShopActions'\n\nexport function NavigationManager() {\n const location = useLocation()\n const navigate = useNavigateWithTransition()\n const {reportNavigationState} = useShopActions()\n\n // Report navigation state on location changes\n useEffect(() => {\n const navigationState = {\n location: {\n pathname: location.pathname,\n search: location.search,\n hash: location.hash,\n key: location.key,\n state: location.state,\n },\n historyLength: typeof window === 'undefined' ? 0 : window.history.length,\n historyIndex:\n typeof window === 'undefined' || !window.history.state?.idx\n ? null\n : window.history.state.idx,\n }\n\n reportNavigationState(navigationState)\n }, [location, reportNavigationState])\n\n // Handle native back button press\n useOnNavigateBack(() => {\n navigate(-1)\n })\n\n return null\n}\n"],"names":["NavigationManager","location","useLocation","navigate","useNavigateWithTransition","reportNavigationState","useShopActions","useEffect","navigationState","useOnNavigateBack"],"mappings":";;;;;AASO,SAASA,IAAoB;AAClC,QAAMC,IAAWC,EAAY,GACvBC,IAAWC,EAA0B,GACrC,EAAC,uBAAAC,EAAqB,IAAIC,EAAe;AAG/C,SAAAC,EAAU,MAAM;AACd,UAAMC,IAAkB;AAAA,MACtB,UAAU;AAAA,QACR,UAAUP,EAAS;AAAA,QACnB,QAAQA,EAAS;AAAA,QACjB,MAAMA,EAAS;AAAA,QACf,KAAKA,EAAS;AAAA,QACd,OAAOA,EAAS;AAAA,MAClB;AAAA,MACA,eAAe,OAAO,SAAW,MAAc,IAAI,OAAO,QAAQ;AAAA,MAClE,cACE,OAAO,SAAW,OAAe,CAAC,OAAO,QAAQ,OAAO,MACpD,OACA,OAAO,QAAQ,MAAM;AAAA,IAC7B;AAEA,IAAAI,EAAsBG,CAAe;AAAA,EAAA,GACpC,CAACP,GAAUI,CAAqB,CAAC,GAGpCI,EAAkB,MAAM;AACtB,IAAAN,EAAS,EAAE;AAAA,EAAA,CACZ,GAEM;AACT;"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import {useEffect, useRef} from 'react'
|
|
2
|
-
|
|
3
|
-
export function useOnNavigateBack(callback: () => void) {
|
|
4
|
-
// Using a ref allows the callback to be updated without triggering a re-render
|
|
5
|
-
// This makes the hook nicer to use because developers don't need useCallback
|
|
6
|
-
const callbackRef = useRef(callback)
|
|
7
|
-
callbackRef.current = callback
|
|
8
|
-
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
const listenerId = window.minisEvents.on('NAVIGATE_BACK', () => {
|
|
11
|
-
callbackRef.current()
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
return () => window.minisEvents.off(listenerId)
|
|
15
|
-
}, [])
|
|
16
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import {useEffect} from 'react'
|
|
2
|
-
|
|
3
|
-
import {useLocation} from 'react-router'
|
|
4
|
-
|
|
5
|
-
import {useOnNavigateBack} from '../hooks/events/useOnNavigateBack'
|
|
6
|
-
import {useNavigateWithTransition} from '../hooks/navigation/useNavigateWithTransition'
|
|
7
|
-
|
|
8
|
-
import {useShopActions} from './useShopActions'
|
|
9
|
-
|
|
10
|
-
export function NavigationManager() {
|
|
11
|
-
const location = useLocation()
|
|
12
|
-
const navigate = useNavigateWithTransition()
|
|
13
|
-
const {reportNavigationState} = useShopActions()
|
|
14
|
-
|
|
15
|
-
// Report navigation state on location changes
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
const navigationState = {
|
|
18
|
-
location: {
|
|
19
|
-
pathname: location.pathname,
|
|
20
|
-
search: location.search,
|
|
21
|
-
hash: location.hash,
|
|
22
|
-
key: location.key,
|
|
23
|
-
state: location.state,
|
|
24
|
-
},
|
|
25
|
-
historyLength: typeof window === 'undefined' ? 0 : window.history.length,
|
|
26
|
-
historyIndex:
|
|
27
|
-
typeof window === 'undefined' || !window.history.state?.idx
|
|
28
|
-
? null
|
|
29
|
-
: window.history.state.idx,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
reportNavigationState(navigationState)
|
|
33
|
-
}, [location, reportNavigationState])
|
|
34
|
-
|
|
35
|
-
// Handle native back button press
|
|
36
|
-
useOnNavigateBack(() => {
|
|
37
|
-
navigate(-1)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
return null
|
|
41
|
-
}
|