@webround/commerce-react 0.0.0-stage → 0.3.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/dist/index.d.ts +23 -0
- package/dist/index.js +15 -0
- package/dist/site.d.ts +7 -0
- package/dist/site.js +41 -0
- package/dist/use-account.d.ts +12 -0
- package/dist/use-account.js +9 -0
- package/dist/use-addresses.d.ts +20 -0
- package/dist/use-addresses.js +20 -0
- package/dist/use-auth.d.ts +24 -0
- package/dist/use-auth.js +20 -0
- package/dist/use-cart.d.ts +27 -0
- package/dist/use-cart.js +23 -0
- package/dist/use-checkout.d.ts +37 -0
- package/dist/use-checkout.js +33 -0
- package/dist/use-customer-resource.d.ts +5 -0
- package/dist/use-customer-resource.js +9 -0
- package/dist/use-orders.d.ts +14 -0
- package/dist/use-orders.js +6 -0
- package/dist/use-resource.d.ts +25 -0
- package/dist/use-resource.js +60 -0
- package/dist/use-returns.d.ts +14 -0
- package/dist/use-returns.js +7 -0
- package/dist/use-reviews.d.ts +14 -0
- package/dist/use-reviews.js +10 -0
- package/dist/use-shop-address.d.ts +12 -0
- package/dist/use-shop-address.js +39 -0
- package/dist/use-shop.d.ts +34 -0
- package/dist/use-shop.js +31 -0
- package/dist/use-wishlist.d.ts +15 -0
- package/dist/use-wishlist.js +12 -0
- package/package.json +46 -5
- package/README.md +0 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { useResource } from './use-resource.js';
|
|
2
|
+
export type { UseResourceOptions, UseResourceResult } from './use-resource.js';
|
|
3
|
+
export { useShop } from './use-shop.js';
|
|
4
|
+
export type { UseShopOptions, UseShopResult } from './use-shop.js';
|
|
5
|
+
export { useShopAddress } from './use-shop-address.js';
|
|
6
|
+
export type { UseShopAddressOptions } from './use-shop-address.js';
|
|
7
|
+
export { authActions, useAuth } from './use-auth.js';
|
|
8
|
+
export type { AuthActions, UseAuthResult } from './use-auth.js';
|
|
9
|
+
export { cartActions, useCart } from './use-cart.js';
|
|
10
|
+
export type { CartActions, UseCartResult } from './use-cart.js';
|
|
11
|
+
export { useAccount } from './use-account.js';
|
|
12
|
+
export type { UseAccountResult } from './use-account.js';
|
|
13
|
+
export { checkoutActions, useCheckout } from './use-checkout.js';
|
|
14
|
+
export type { CheckoutActions, UseCheckoutOptions, UseCheckoutResult } from './use-checkout.js';
|
|
15
|
+
export { addressActions, useAddresses } from './use-addresses.js';
|
|
16
|
+
export type { AddressActions, UseAddressesResult } from './use-addresses.js';
|
|
17
|
+
export { useCustomerResource } from './use-customer-resource.js';
|
|
18
|
+
export { useOrder, useOrders, useSubscription, useSubscriptions } from './use-orders.js';
|
|
19
|
+
export { useGuestReturnSession, useReturnRequest, useReturnRequests } from './use-returns.js';
|
|
20
|
+
export { useWishlist } from './use-wishlist.js';
|
|
21
|
+
export type { UseWishlistResult } from './use-wishlist.js';
|
|
22
|
+
export { useMyReviews, useReviewAbility } from './use-reviews.js';
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// @webround/commerce-react: what a React component needs to use a store (React 19). The logic is `@webround/commerce`'s; this only binds it to components.
|
|
2
|
+
export { useResource } from './use-resource.js';
|
|
3
|
+
export { useShop } from './use-shop.js';
|
|
4
|
+
export { useShopAddress } from './use-shop-address.js';
|
|
5
|
+
export { authActions, useAuth } from './use-auth.js';
|
|
6
|
+
export { cartActions, useCart } from './use-cart.js';
|
|
7
|
+
export { useAccount } from './use-account.js';
|
|
8
|
+
export { checkoutActions, useCheckout } from './use-checkout.js';
|
|
9
|
+
export { addressActions, useAddresses } from './use-addresses.js';
|
|
10
|
+
export { useCustomerResource } from './use-customer-resource.js';
|
|
11
|
+
export { useOrder, useOrders, useSubscription, useSubscriptions } from './use-orders.js';
|
|
12
|
+
export { useGuestReturnSession, useReturnRequest, useReturnRequests } from './use-returns.js';
|
|
13
|
+
export { useWishlist } from './use-wishlist.js';
|
|
14
|
+
export { useMyReviews, useReviewAbility } from './use-reviews.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
package/dist/site.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type WrSiteEnv } from '@webround/core/site-env';
|
|
2
|
+
import { type Commerce, type CommerceConfig } from '@webround/commerce';
|
|
3
|
+
/** The store `env` describes. Throws, naming what is missing, when it does not say enough. */
|
|
4
|
+
export declare function commerceFromEnv(env: WrSiteEnv, options?: Omit<CommerceConfig, 'storeId' | 'commerceApiUrl' | 'catalogApiUrl' | 'checkoutApiUrl'>): Commerce;
|
|
5
|
+
/** This site's store: its session, its catalog, its cache. Made the first time one of them is read. */
|
|
6
|
+
export declare const commerce: Commerce;
|
|
7
|
+
//# sourceMappingURL=site.d.ts.map
|
package/dist/site.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { wrSiteEnv, WR_SITE_ENV_VARS } from '@webround/core/site-env';
|
|
2
|
+
import { createCommerce } from '@webround/commerce';
|
|
3
|
+
// `@webround/commerce-react/site`: the store of THIS site, made from what the site was built with (`VITE_WR_*`, set by the Worker that runs the draft; `wrSiteEnv` of @webround/core is the one place that reads them).
|
|
4
|
+
//
|
|
5
|
+
// import { commerce } from '@webround/commerce-react/site';
|
|
6
|
+
// const { actions } = useAuth(commerce.auth);
|
|
7
|
+
//
|
|
8
|
+
// Nothing is made until it is used: a page that never touches the store costs nothing, and a site built without the variables fails WHERE it uses the store, saying which one is missing, not when it loads.
|
|
9
|
+
/** The store `env` describes. Throws, naming what is missing, when it does not say enough. */
|
|
10
|
+
export function commerceFromEnv(env, options = {}) {
|
|
11
|
+
const { draftId, commerceApiUrl, catalogApiUrl, checkoutApiUrl } = env;
|
|
12
|
+
if (draftId === undefined || commerceApiUrl === undefined || catalogApiUrl === undefined) {
|
|
13
|
+
const missing = [
|
|
14
|
+
draftId === undefined ? WR_SITE_ENV_VARS.draftId : null,
|
|
15
|
+
commerceApiUrl === undefined ? WR_SITE_ENV_VARS.commerceApiUrl : null,
|
|
16
|
+
catalogApiUrl === undefined ? WR_SITE_ENV_VARS.catalogApiUrl : null,
|
|
17
|
+
].filter((name) => name !== null);
|
|
18
|
+
throw new Error(`@webround/commerce: this site was built without ${missing.join(', ')}, so it does not know which store to talk to.`);
|
|
19
|
+
}
|
|
20
|
+
// The checkout's address is not needed to run the store: a site without it fails where it uses the checkout, saying so
|
|
21
|
+
return createCommerce({ ...options, storeId: draftId, commerceApiUrl, catalogApiUrl, ...(checkoutApiUrl !== undefined ? { checkoutApiUrl } : {}) });
|
|
22
|
+
}
|
|
23
|
+
let made;
|
|
24
|
+
// This site prices and describes its cart with the checkout's preview (`commerce.checkout`): `/cart/total` is never asked
|
|
25
|
+
const store = () => (made ?? (made = commerceFromEnv(wrSiteEnv(), { cartPricing: false })));
|
|
26
|
+
/** This site's store: its session, its catalog, its cache. Made the first time one of them is read. */
|
|
27
|
+
export const commerce = {
|
|
28
|
+
get storeId() { return store().storeId; },
|
|
29
|
+
get auth() { return store().auth; },
|
|
30
|
+
get catalog() { return store().catalog; },
|
|
31
|
+
get cache() { return store().cache; },
|
|
32
|
+
get cart() { return store().cart; },
|
|
33
|
+
get checkout() { return store().checkout; },
|
|
34
|
+
get account() { return store().account; },
|
|
35
|
+
get addresses() { return store().addresses; },
|
|
36
|
+
get orders() { return store().orders; },
|
|
37
|
+
get returns() { return store().returns; },
|
|
38
|
+
get wishlist() { return store().wishlist; },
|
|
39
|
+
get reviews() { return store().reviews; },
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=site.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Account, type NewsletterStrategy } from '@webround/commerce';
|
|
2
|
+
import type { Auth } from '@webround/commerce';
|
|
3
|
+
export interface UseAccountResult {
|
|
4
|
+
/** What subscribing to, or cancelling, the newsletter will do now. */
|
|
5
|
+
newsletter: NewsletterStrategy;
|
|
6
|
+
actions: Account;
|
|
7
|
+
}
|
|
8
|
+
export declare function useAccount(commerce: {
|
|
9
|
+
account: Account;
|
|
10
|
+
auth: Pick<Auth, 'store'>;
|
|
11
|
+
}): UseAccountResult;
|
|
12
|
+
//# sourceMappingURL=use-account.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
import { newsletterStrategy } from '@webround/commerce';
|
|
3
|
+
export function useAccount(commerce) {
|
|
4
|
+
const { store } = commerce.auth;
|
|
5
|
+
const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);
|
|
6
|
+
const newsletter = useMemo(() => newsletterStrategy(state), [state.status, state.customer?.email, state.customer?.emailVerified]);
|
|
7
|
+
return { newsletter, actions: commerce.account };
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=use-account.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AddressBook, AddressBookState } from '@webround/commerce';
|
|
2
|
+
export interface AddressActions {
|
|
3
|
+
save: AddressBook['save'];
|
|
4
|
+
remember: AddressBook['remember'];
|
|
5
|
+
update: AddressBook['update'];
|
|
6
|
+
remove: AddressBook['remove'];
|
|
7
|
+
setDefault: AddressBook['setDefault'];
|
|
8
|
+
refresh: AddressBook['refresh'];
|
|
9
|
+
}
|
|
10
|
+
export interface UseAddressesResult {
|
|
11
|
+
state: AddressBookState;
|
|
12
|
+
addresses: AddressBookState['addresses'];
|
|
13
|
+
actions: AddressActions;
|
|
14
|
+
}
|
|
15
|
+
export declare function addressActions(book: AddressBook): AddressActions;
|
|
16
|
+
/** `commerce` is what `createCommerce` made (or `@webround/commerce-react/site`'s). Any number of components can use it: the book starts once. A server render says `unknown` and no addresses. */
|
|
17
|
+
export declare function useAddresses(commerce: {
|
|
18
|
+
addresses: AddressBook;
|
|
19
|
+
}): UseAddressesResult;
|
|
20
|
+
//# sourceMappingURL=use-addresses.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect, useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
export function addressActions(book) {
|
|
3
|
+
return {
|
|
4
|
+
save: (input) => book.save(input),
|
|
5
|
+
remember: (input) => book.remember(input),
|
|
6
|
+
update: (id, patch) => book.update(id, patch),
|
|
7
|
+
remove: (id) => book.remove(id),
|
|
8
|
+
setDefault: (id, kind) => book.setDefault(id, kind),
|
|
9
|
+
refresh: () => book.refresh(),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** `commerce` is what `createCommerce` made (or `@webround/commerce-react/site`'s). Any number of components can use it: the book starts once. A server render says `unknown` and no addresses. */
|
|
13
|
+
export function useAddresses(commerce) {
|
|
14
|
+
const { addresses: book } = commerce;
|
|
15
|
+
const state = useSyncExternalStore(book.subscribe, book.getState, book.getState);
|
|
16
|
+
const actions = useMemo(() => addressActions(book), [book]);
|
|
17
|
+
useEffect(() => { void book.start(); }, [book]);
|
|
18
|
+
return { state, addresses: state.addresses, actions };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=use-addresses.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Auth } from '@webround/commerce';
|
|
2
|
+
import type { AuthState, AuthStatus, AuthStore, Customer } from '@webround/commerce';
|
|
3
|
+
import type { TwoFactorChallenge } from '@webround/commerce';
|
|
4
|
+
/** What changes the session. A call that fails THROWS what the store said (a wrong password, a validation list in `issues`): it belongs to the form that made it. */
|
|
5
|
+
export interface AuthActions {
|
|
6
|
+
signup: AuthStore['signup'];
|
|
7
|
+
login: AuthStore['login'];
|
|
8
|
+
verifyTwoFactor: AuthStore['verifyTwoFactor'];
|
|
9
|
+
cancelChallenge: AuthStore['cancelChallenge'];
|
|
10
|
+
logout: AuthStore['logout'];
|
|
11
|
+
}
|
|
12
|
+
export interface UseAuthResult {
|
|
13
|
+
state: AuthState;
|
|
14
|
+
/** `unknown` until it is known whether a customer is kept (and on a server render: no window, nobody to take back). */
|
|
15
|
+
status: AuthStatus;
|
|
16
|
+
customer: Customer | null;
|
|
17
|
+
/** The code of the authenticator app is wanted (status `challenge`). */
|
|
18
|
+
challenge: TwoFactorChallenge | null;
|
|
19
|
+
actions: AuthActions;
|
|
20
|
+
}
|
|
21
|
+
export declare function authActions(store: AuthStore): AuthActions;
|
|
22
|
+
/** `auth` is what `createAuth` made. Any number of components can use it: the customer is taken back once. */
|
|
23
|
+
export declare function useAuth(auth: Pick<Auth, 'store'>): UseAuthResult;
|
|
24
|
+
//# sourceMappingURL=use-auth.d.ts.map
|
package/dist/use-auth.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect, useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
export function authActions(store) {
|
|
3
|
+
return {
|
|
4
|
+
signup: (input, options) => store.signup(input, options),
|
|
5
|
+
login: (input) => store.login(input),
|
|
6
|
+
verifyTwoFactor: (code) => store.verifyTwoFactor(code),
|
|
7
|
+
cancelChallenge: () => store.cancelChallenge(),
|
|
8
|
+
logout: () => store.logout(),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** `auth` is what `createAuth` made. Any number of components can use it: the customer is taken back once. */
|
|
12
|
+
export function useAuth(auth) {
|
|
13
|
+
const { store } = auth;
|
|
14
|
+
const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);
|
|
15
|
+
const actions = useMemo(() => authActions(store), [store]);
|
|
16
|
+
// Not in render: a server render asks nothing. `restore` is one call for whoever asks while it is under way, and nothing once it is known
|
|
17
|
+
useEffect(() => { void store.restore(); }, [store]);
|
|
18
|
+
return { state, status: state.status, customer: state.customer, challenge: state.challenge, actions };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=use-auth.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CartState, CartStore } from '@webround/commerce';
|
|
2
|
+
export interface CartActions {
|
|
3
|
+
add: CartStore['add'];
|
|
4
|
+
setQuantity: CartStore['setQuantity'];
|
|
5
|
+
remove: CartStore['remove'];
|
|
6
|
+
/** Empties the cart (a guest's and a customer's). */
|
|
7
|
+
clear: CartStore['clear'];
|
|
8
|
+
refresh: CartStore['refresh'];
|
|
9
|
+
}
|
|
10
|
+
export interface UseCartResult {
|
|
11
|
+
state: CartState;
|
|
12
|
+
/** How many items are in the cart: known before the store has priced anything. */
|
|
13
|
+
count: number;
|
|
14
|
+
/** What the store says the cart costs (null until it said); `state.isStale` when it is one change behind. */
|
|
15
|
+
priced: CartState['priced'];
|
|
16
|
+
actions: CartActions;
|
|
17
|
+
}
|
|
18
|
+
export declare function cartActions(store: CartStore): CartActions;
|
|
19
|
+
/**
|
|
20
|
+
* To step a quantity (a "+" button), read it from `state.lines`, not from `priced`: `lines` is what the cart is NOW (a change shows in it at once), `priced` is the store's answer, which can be one change behind
|
|
21
|
+
* (`state.isStale`), so three quick clicks on a "+" that reads `priced` ask for the same quantity three times.
|
|
22
|
+
*/
|
|
23
|
+
/** `commerce` is what `createCommerce` made (or `@webround/commerce-react/site`'s). Any number of components can use it: the cart starts once. A server render says `unknown` and zero items, as the first render in the browser does. */
|
|
24
|
+
export declare function useCart(commerce: {
|
|
25
|
+
cart: CartStore;
|
|
26
|
+
}): UseCartResult;
|
|
27
|
+
//# sourceMappingURL=use-cart.d.ts.map
|
package/dist/use-cart.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect, useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
export function cartActions(store) {
|
|
3
|
+
return {
|
|
4
|
+
add: (line) => store.add(line),
|
|
5
|
+
setQuantity: (variantId, quantity) => store.setQuantity(variantId, quantity),
|
|
6
|
+
remove: (variantId) => store.remove(variantId),
|
|
7
|
+
clear: () => store.clear(),
|
|
8
|
+
refresh: () => store.refresh(),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* To step a quantity (a "+" button), read it from `state.lines`, not from `priced`: `lines` is what the cart is NOW (a change shows in it at once), `priced` is the store's answer, which can be one change behind
|
|
13
|
+
* (`state.isStale`), so three quick clicks on a "+" that reads `priced` ask for the same quantity three times.
|
|
14
|
+
*/
|
|
15
|
+
/** `commerce` is what `createCommerce` made (or `@webround/commerce-react/site`'s). Any number of components can use it: the cart starts once. A server render says `unknown` and zero items, as the first render in the browser does. */
|
|
16
|
+
export function useCart(commerce) {
|
|
17
|
+
const { cart } = commerce;
|
|
18
|
+
const state = useSyncExternalStore(cart.subscribe, cart.getState, cart.getState);
|
|
19
|
+
const actions = useMemo(() => cartActions(cart), [cart]);
|
|
20
|
+
useEffect(() => { void cart.start(); }, [cart]);
|
|
21
|
+
return { state, count: state.count, priced: state.priced, actions };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=use-cart.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CheckoutState, CheckoutStore } from '@webround/commerce';
|
|
2
|
+
export interface CheckoutActions {
|
|
3
|
+
setDeliveryMethod: CheckoutStore['setDeliveryMethod'];
|
|
4
|
+
setShippingAddress: CheckoutStore['setShippingAddress'];
|
|
5
|
+
setBillingAddress: CheckoutStore['setBillingAddress'];
|
|
6
|
+
selectShippingMethod: CheckoutStore['selectShippingMethod'];
|
|
7
|
+
setPromotionCodes: CheckoutStore['setPromotionCodes'];
|
|
8
|
+
setAdditionalData: CheckoutStore['setAdditionalData'];
|
|
9
|
+
setEmail: CheckoutStore['setEmail'];
|
|
10
|
+
setInvoice: CheckoutStore['setInvoice'];
|
|
11
|
+
/** Asks the preview now (what it asked automatically is asked again). Throws what the store refuses. */
|
|
12
|
+
quote: CheckoutStore['quote'];
|
|
13
|
+
/** Starts the payment; give the customer the `url` it answers (`location.assign(url)`). Throws what the store refuses. */
|
|
14
|
+
pay: CheckoutStore['pay'];
|
|
15
|
+
reset: CheckoutStore['reset'];
|
|
16
|
+
/** The payment is done (the success page): empties the cart and forgets the checkout. */
|
|
17
|
+
complete: CheckoutStore['complete'];
|
|
18
|
+
}
|
|
19
|
+
export interface UseCheckoutOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Ask the preview by itself when the cart or what the customer chose changes (default: yes). Not for an empty cart, and not again for what already failed (ask again with `actions.quote`). A refusal is not thrown
|
|
22
|
+
* here: it is in `state.error`.
|
|
23
|
+
*/
|
|
24
|
+
autoQuote?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface UseCheckoutResult {
|
|
27
|
+
state: CheckoutState;
|
|
28
|
+
/** What the store says it costs (null until it said); `state.isStale` when it is the answer to something the customer has since changed. */
|
|
29
|
+
quote: CheckoutState['quote'];
|
|
30
|
+
actions: CheckoutActions;
|
|
31
|
+
}
|
|
32
|
+
export declare function checkoutActions(store: CheckoutStore): CheckoutActions;
|
|
33
|
+
/** `commerce` is what `createCommerce` made (or `@webround/commerce-react/site`'s). Reading `commerce.checkout` needs the checkout API's address. */
|
|
34
|
+
export declare function useCheckout(commerce: {
|
|
35
|
+
checkout: CheckoutStore;
|
|
36
|
+
}, options?: UseCheckoutOptions): UseCheckoutResult;
|
|
37
|
+
//# sourceMappingURL=use-checkout.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useEffect, useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
export function checkoutActions(store) {
|
|
3
|
+
return {
|
|
4
|
+
setDeliveryMethod: (variantId, method) => store.setDeliveryMethod(variantId, method),
|
|
5
|
+
setShippingAddress: (address) => store.setShippingAddress(address),
|
|
6
|
+
setBillingAddress: (address) => store.setBillingAddress(address),
|
|
7
|
+
selectShippingMethod: (id) => store.selectShippingMethod(id),
|
|
8
|
+
setPromotionCodes: (codes) => store.setPromotionCodes(codes),
|
|
9
|
+
setAdditionalData: (data) => store.setAdditionalData(data),
|
|
10
|
+
setEmail: (email) => store.setEmail(email),
|
|
11
|
+
setInvoice: (invoice) => store.setInvoice(invoice),
|
|
12
|
+
quote: (options) => store.quote(options),
|
|
13
|
+
pay: (returnUrls) => store.pay(returnUrls),
|
|
14
|
+
reset: () => store.reset(),
|
|
15
|
+
complete: () => store.complete(),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** `commerce` is what `createCommerce` made (or `@webround/commerce-react/site`'s). Reading `commerce.checkout` needs the checkout API's address. */
|
|
19
|
+
export function useCheckout(commerce, options = {}) {
|
|
20
|
+
const { checkout } = commerce;
|
|
21
|
+
const autoQuote = options.autoQuote ?? true;
|
|
22
|
+
const state = useSyncExternalStore(checkout.subscribe, checkout.getState, checkout.getState);
|
|
23
|
+
const actions = useMemo(() => checkoutActions(checkout), [checkout]);
|
|
24
|
+
useEffect(() => { checkout.start(); }, [checkout]);
|
|
25
|
+
// Which questions were asked is the store's to remember (one for all the components): a refusal is not asked again by itself, and the same question is never asked twice
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!autoQuote)
|
|
28
|
+
return;
|
|
29
|
+
checkout.ensureQuote().catch(() => { });
|
|
30
|
+
}, [checkout, autoQuote, state.requestKey, state.lineCount]);
|
|
31
|
+
return { state, quote: state.quote, actions };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=use-checkout.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Auth } from '@webround/commerce';
|
|
2
|
+
import type { Resource } from '@webround/commerce';
|
|
3
|
+
import { type UseResourceOptions, type UseResourceResult } from './use-resource.js';
|
|
4
|
+
export declare function useCustomerResource<T>(auth: Pick<Auth, 'store'>, make: () => Resource<T> | null, options?: UseResourceOptions): UseResourceResult<T>;
|
|
5
|
+
//# sourceMappingURL=use-customer-resource.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useSyncExternalStore } from 'react';
|
|
2
|
+
import { useResource } from './use-resource.js';
|
|
3
|
+
// A resource that is about the logged-in customer, shown by a component: the same as `useResource`, plus that the component follows who is logged in. Nobody logged in is no resource (`idle`), and logging in or out draws
|
|
4
|
+
// the component again with the right one (the cache forgets what was the customer's when they go).
|
|
5
|
+
export function useCustomerResource(auth, make, options) {
|
|
6
|
+
useSyncExternalStore(auth.store.subscribe, auth.store.getState, auth.store.getState);
|
|
7
|
+
return useResource(make(), options);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=use-customer-resource.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Auth } from '@webround/commerce';
|
|
2
|
+
import type { Orders } from '@webround/commerce';
|
|
3
|
+
import type { Order, OrderPage, PageQuery, Subscription, SubscriptionPage } from '@webround/commerce';
|
|
4
|
+
import type { UseResourceOptions, UseResourceResult } from './use-resource.js';
|
|
5
|
+
type Source = {
|
|
6
|
+
auth: Pick<Auth, 'store'>;
|
|
7
|
+
orders: Orders;
|
|
8
|
+
};
|
|
9
|
+
export declare const useOrders: (commerce: Source, query?: PageQuery, options?: UseResourceOptions) => UseResourceResult<OrderPage>;
|
|
10
|
+
export declare const useOrder: (commerce: Source, id: string | null | undefined) => UseResourceResult<Order>;
|
|
11
|
+
export declare const useSubscriptions: (commerce: Source, query?: PageQuery, options?: UseResourceOptions) => UseResourceResult<SubscriptionPage>;
|
|
12
|
+
export declare const useSubscription: (commerce: Source, id: string | null | undefined) => UseResourceResult<Subscription>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=use-orders.d.ts.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { useCustomerResource } from './use-customer-resource.js';
|
|
2
|
+
export const useOrders = (commerce, query, options = { keepPrevious: true }) => useCustomerResource(commerce.auth, () => commerce.orders.list(query), options);
|
|
3
|
+
export const useOrder = (commerce, id) => useCustomerResource(commerce.auth, () => (id ? commerce.orders.get(id) : null));
|
|
4
|
+
export const useSubscriptions = (commerce, query, options = { keepPrevious: true }) => useCustomerResource(commerce.auth, () => commerce.orders.subscriptions.list(query), options);
|
|
5
|
+
export const useSubscription = (commerce, id) => useCustomerResource(commerce.auth, () => (id ? commerce.orders.subscriptions.get(id) : null));
|
|
6
|
+
//# sourceMappingURL=use-orders.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Resource } from '@webround/commerce';
|
|
2
|
+
export interface UseResourceOptions {
|
|
3
|
+
/**
|
|
4
|
+
* When the question changes (another page of the results, another search), keep showing the answer of the one before until the new one comes, instead of nothing: `data` is that
|
|
5
|
+
* answer and `isStale` says it is not the one asked for. Off by default: what is shown is always the answer to what is asked.
|
|
6
|
+
*/
|
|
7
|
+
keepPrevious?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface UseResourceResult<T> {
|
|
10
|
+
/** `idle`: nothing is asked (no resource was given). `pending`: the answer is being waited for. `ready`: it is there. `error`: it could not be had. */
|
|
11
|
+
status: 'idle' | 'pending' | 'ready' | 'error';
|
|
12
|
+
/** The answer to what is asked, or (with `keepPrevious`, while the new one is awaited) the answer to the question before; undefined when there is none. */
|
|
13
|
+
data: T | undefined;
|
|
14
|
+
/** Why it failed (a `CommerceError` for what the store did, or a network error): set with `status: 'error'`, undefined otherwise. */
|
|
15
|
+
error: unknown;
|
|
16
|
+
/** The answer to what is asked is being waited for. */
|
|
17
|
+
isFetching: boolean;
|
|
18
|
+
/** `data` is the answer to an earlier question (`keepPrevious`), not to the one asked now. */
|
|
19
|
+
isStale: boolean;
|
|
20
|
+
/** Asks again after an error. It does nothing in any other state: what is there is not asked for again (to forget it, invalidate it in the catalog). */
|
|
21
|
+
retry: () => void;
|
|
22
|
+
}
|
|
23
|
+
/** A resource asked for by a component: `null`/`undefined` is "not asked yet" (a slug that is not known, a search that is not made). */
|
|
24
|
+
export declare function useResource<T>(resource: Resource<T> | null | undefined, options?: UseResourceOptions): UseResourceResult<T>;
|
|
25
|
+
//# sourceMappingURL=use-resource.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
2
|
+
const NOTHING = { status: 'idle' };
|
|
3
|
+
const noSubscription = () => () => undefined;
|
|
4
|
+
/** A resource asked for by a component: `null`/`undefined` is "not asked yet" (a slug that is not known, a search that is not made). */
|
|
5
|
+
export function useResource(resource, options = {}) {
|
|
6
|
+
const keepPrevious = options.keepPrevious === true;
|
|
7
|
+
const key = resource?.key;
|
|
8
|
+
// A resource is made again at every render, and the same key is the same question: only a new key is a new one (what is subscribed to must not change on every render)
|
|
9
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
10
|
+
const asked = useMemo(() => resource ?? null, [key]);
|
|
11
|
+
const subscribe = useCallback((listener) => (asked ? asked.subscribe(listener) : noSubscription()), [asked]);
|
|
12
|
+
const read = useCallback(() => (asked ? asked.snapshot() : NOTHING), [asked]);
|
|
13
|
+
// The server has the same state as the client: what the cache holds when it renders (a prerender fills it before)
|
|
14
|
+
const snapshot = useSyncExternalStore(subscribe, read, read);
|
|
15
|
+
// Asks when the question is there, and gives it up when it is gone: a question nobody waits for any more is not asked (the cache sees to that)
|
|
16
|
+
const waiting = useRef(null);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!asked)
|
|
19
|
+
return;
|
|
20
|
+
const controller = (waiting.current = new AbortController());
|
|
21
|
+
if (asked.snapshot().status !== 'ready')
|
|
22
|
+
asked.load({ signal: controller.signal }).catch(() => undefined); // the failure is the state of the key, read above
|
|
23
|
+
return () => { controller.abort(); if (waiting.current === controller)
|
|
24
|
+
waiting.current = null; };
|
|
25
|
+
}, [asked]);
|
|
26
|
+
// What is watched was forgotten (it was invalidated in the catalog): it is asked again, so that what is on the screen is never "nothing" for good
|
|
27
|
+
const forgotten = asked !== null && snapshot.status === 'idle';
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (forgotten && waiting.current && asked && asked.snapshot().status === 'idle')
|
|
30
|
+
asked.load({ signal: waiting.current.signal }).catch(() => undefined);
|
|
31
|
+
}, [forgotten, asked]);
|
|
32
|
+
const retrying = useRef(null);
|
|
33
|
+
useEffect(() => () => { retrying.current?.abort(); retrying.current = null; }, [asked]);
|
|
34
|
+
const retry = useCallback(() => {
|
|
35
|
+
if (!asked || asked.snapshot().status !== 'error')
|
|
36
|
+
return;
|
|
37
|
+
retrying.current?.abort();
|
|
38
|
+
const controller = (retrying.current = new AbortController());
|
|
39
|
+
asked.load({ signal: controller.signal }).catch(() => undefined);
|
|
40
|
+
}, [asked]);
|
|
41
|
+
// The last answer that was shown, for `keepPrevious`: kept once it is on the screen
|
|
42
|
+
const shown = useRef(undefined);
|
|
43
|
+
const hasShown = useRef(false);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (snapshot.status === 'ready') {
|
|
46
|
+
shown.current = snapshot.data;
|
|
47
|
+
hasShown.current = true;
|
|
48
|
+
}
|
|
49
|
+
}, [snapshot]);
|
|
50
|
+
if (!asked)
|
|
51
|
+
return { status: 'idle', data: undefined, error: undefined, isFetching: false, isStale: false, retry };
|
|
52
|
+
if (snapshot.status === 'ready')
|
|
53
|
+
return { status: 'ready', data: snapshot.data, error: undefined, isFetching: false, isStale: false, retry };
|
|
54
|
+
if (snapshot.status === 'error')
|
|
55
|
+
return { status: 'error', data: undefined, error: snapshot.error, isFetching: false, isStale: false, retry };
|
|
56
|
+
// `idle` here is a question that is about to be asked (the effect has not run yet): it is awaited, there is no frame in which it is "nothing"
|
|
57
|
+
const stale = keepPrevious && hasShown.current;
|
|
58
|
+
return { status: 'pending', data: stale ? shown.current : undefined, error: undefined, isFetching: true, isStale: stale, retry };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=use-resource.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Auth } from '@webround/commerce';
|
|
2
|
+
import type { Returns } from '@webround/commerce';
|
|
3
|
+
import type { GuestReturnSession, PageQuery, ReturnRequest, ReturnRequestPage } from '@webround/commerce';
|
|
4
|
+
import { type UseResourceOptions, type UseResourceResult } from './use-resource.js';
|
|
5
|
+
type Source = {
|
|
6
|
+
auth: Pick<Auth, 'store'>;
|
|
7
|
+
returns: Returns;
|
|
8
|
+
};
|
|
9
|
+
export declare const useReturnRequests: (commerce: Source, query?: PageQuery, options?: UseResourceOptions) => UseResourceResult<ReturnRequestPage>;
|
|
10
|
+
export declare const useReturnRequest: (commerce: Source, id: string | null | undefined) => UseResourceResult<ReturnRequest>;
|
|
11
|
+
/** The order a guest's link opens (the `token` of the page's address; none yet is no question). */
|
|
12
|
+
export declare const useGuestReturnSession: (commerce: Pick<Source, "returns">, token: string | null | undefined) => UseResourceResult<GuestReturnSession>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=use-returns.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { useCustomerResource } from './use-customer-resource.js';
|
|
2
|
+
import { useResource } from './use-resource.js';
|
|
3
|
+
export const useReturnRequests = (commerce, query, options = { keepPrevious: true }) => useCustomerResource(commerce.auth, () => commerce.returns.list(query), options);
|
|
4
|
+
export const useReturnRequest = (commerce, id) => useCustomerResource(commerce.auth, () => (id ? commerce.returns.get(id) : null));
|
|
5
|
+
/** The order a guest's link opens (the `token` of the page's address; none yet is no question). */
|
|
6
|
+
export const useGuestReturnSession = (commerce, token) => useResource(token ? commerce.returns.guest.session(token) : null);
|
|
7
|
+
//# sourceMappingURL=use-returns.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Auth } from '@webround/commerce';
|
|
2
|
+
import { type ReviewAbility } from '@webround/commerce';
|
|
3
|
+
import type { Reviews } from '@webround/commerce';
|
|
4
|
+
import type { MyReviewPage, MyReviewsQuery } from '@webround/commerce';
|
|
5
|
+
import type { UseResourceOptions, UseResourceResult } from './use-resource.js';
|
|
6
|
+
type Source = {
|
|
7
|
+
auth: Pick<Auth, 'store'>;
|
|
8
|
+
reviews: Reviews;
|
|
9
|
+
};
|
|
10
|
+
export declare const useMyReviews: (commerce: Source, query?: MyReviewsQuery, options?: UseResourceOptions) => UseResourceResult<MyReviewPage>;
|
|
11
|
+
/** Who can write a review now: a guest has to log in, a customer whose address is not verified has to verify it, one whose is can. It follows the session. */
|
|
12
|
+
export declare function useReviewAbility(commerce: Pick<Source, 'auth'>): ReviewAbility;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=use-reviews.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useSyncExternalStore } from 'react';
|
|
2
|
+
import { reviewAbility } from '@webround/commerce';
|
|
3
|
+
import { useCustomerResource } from './use-customer-resource.js';
|
|
4
|
+
export const useMyReviews = (commerce, query, options = { keepPrevious: true }) => useCustomerResource(commerce.auth, () => commerce.reviews.mine(query), options);
|
|
5
|
+
/** Who can write a review now: a guest has to log in, a customer whose address is not verified has to verify it, one whose is can. It follows the session. */
|
|
6
|
+
export function useReviewAbility(commerce) {
|
|
7
|
+
const state = useSyncExternalStore(commerce.auth.store.subscribe, commerce.auth.store.getState, commerce.auth.store.getState);
|
|
8
|
+
return reviewAbility(state);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=use-reviews.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AddressOptions } from '@webround/commerce';
|
|
2
|
+
import type { ShopStore } from '@webround/commerce';
|
|
3
|
+
export interface UseShopAddressOptions extends AddressOptions {
|
|
4
|
+
/** The query string of the address NOW, as the router gives it (`location.search`). */
|
|
5
|
+
search: string;
|
|
6
|
+
/** Goes to this query string, on the same page, adding a history entry (`navigate({ search })` of react-router). */
|
|
7
|
+
navigate: (search: string) => void;
|
|
8
|
+
/** The collection the page is for (from its route): it is not in the query. */
|
|
9
|
+
collectionId?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function useShopAddress(options: UseShopAddressOptions): ShopStore;
|
|
12
|
+
//# sourceMappingURL=use-shop-address.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import { createAddressShopStore } from '@webround/commerce';
|
|
3
|
+
export function useShopAddress(options) {
|
|
4
|
+
const { search, collectionId, limit } = options;
|
|
5
|
+
// What the port calls is always the latest the site gave, without the store being made again at every render
|
|
6
|
+
const navigate = useRef(options.navigate);
|
|
7
|
+
navigate.current = options.navigate;
|
|
8
|
+
const listeners = useRef(new Set());
|
|
9
|
+
// What the address says now: what the router said last, or what was written since (until the router says it too)
|
|
10
|
+
const shown = useRef(search);
|
|
11
|
+
const routerSaid = useRef(search);
|
|
12
|
+
// The router says something else than before (the back button, a link): THAT is the address, already in this render (no frame with the old one). Only a change of what the router says counts:
|
|
13
|
+
// a render that comes between a write and the router's answer must not take the address back.
|
|
14
|
+
if (routerSaid.current !== search) {
|
|
15
|
+
routerSaid.current = search;
|
|
16
|
+
shown.current = search;
|
|
17
|
+
}
|
|
18
|
+
const port = useMemo(() => ({
|
|
19
|
+
read: () => shown.current,
|
|
20
|
+
write(next) {
|
|
21
|
+
shown.current = next;
|
|
22
|
+
for (const listener of [...listeners.current])
|
|
23
|
+
listener();
|
|
24
|
+
navigate.current(next);
|
|
25
|
+
},
|
|
26
|
+
subscribe(listener) {
|
|
27
|
+
listeners.current.add(listener);
|
|
28
|
+
return () => { listeners.current.delete(listener); };
|
|
29
|
+
},
|
|
30
|
+
}), []);
|
|
31
|
+
// Whoever listens (another component with the same store) is told that the address may have changed: if what the shop says is the same, it is the same state and nothing is drawn again
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
for (const listener of [...listeners.current])
|
|
34
|
+
listener();
|
|
35
|
+
}, [search]);
|
|
36
|
+
// A new store when the page's collection or the shop's limit change: it is the same address read with other defaults
|
|
37
|
+
return useMemo(() => createAddressShopStore(port, { collectionId, limit }), [port, collectionId, limit]);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=use-shop-address.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CachedCatalog } from '@webround/commerce';
|
|
2
|
+
import type { ProductPage, ProductSearch, StoreDataResponse } from '@webround/commerce';
|
|
3
|
+
import { type SortOption } from '@webround/commerce';
|
|
4
|
+
import type { ShopState } from '@webround/commerce';
|
|
5
|
+
import { type ShopActions, type ShopStore } from '@webround/commerce';
|
|
6
|
+
import type { SearchContext } from '@webround/commerce';
|
|
7
|
+
import { type UseResourceResult } from './use-resource.js';
|
|
8
|
+
export interface UseShopOptions {
|
|
9
|
+
catalog: CachedCatalog;
|
|
10
|
+
/** What the question needs besides what the customer chose: the currency, the language, the cadence, what the products are loaded with. */
|
|
11
|
+
context: SearchContext;
|
|
12
|
+
/** The state the shop starts in (only when the hook keeps the state itself: it is not read again). */
|
|
13
|
+
initial?: ShopState;
|
|
14
|
+
/** Where the state lives, when it is not in memory in the hook (an address bar, a store the page owns). */
|
|
15
|
+
store?: ShopStore;
|
|
16
|
+
/** While a new question is awaited, keep showing the answer to the one before (marked `isStale`), so a listing does not empty at every click. On by default. */
|
|
17
|
+
keepPrevious?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface UseShopResult {
|
|
20
|
+
/** What the customer chose. */
|
|
21
|
+
state: ShopState;
|
|
22
|
+
/** What changes it. */
|
|
23
|
+
actions: ShopActions;
|
|
24
|
+
/** The store's own data (its tags, collections, filters, currencies): what the controls are drawn from. */
|
|
25
|
+
storeData: UseResourceResult<StoreDataResponse>;
|
|
26
|
+
/** The orders that can be offered now (with the store's data in hand, and a text or not); none until it is. */
|
|
27
|
+
sortOptions: SortOption[];
|
|
28
|
+
/** The question the catalog is being asked now, or null while the store's data is awaited. */
|
|
29
|
+
question: ProductSearch | null;
|
|
30
|
+
/** The products. While the store's data is awaited this is `pending` (or its error, with a `retry` that asks for the store again). */
|
|
31
|
+
results: UseResourceResult<ProductPage>;
|
|
32
|
+
}
|
|
33
|
+
export declare function useShop(options: UseShopOptions): UseShopResult;
|
|
34
|
+
//# sourceMappingURL=use-shop.d.ts.map
|
package/dist/use-shop.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
2
|
+
import { canonicalJson } from '@webround/commerce';
|
|
3
|
+
import { shopQuestion } from '@webround/commerce';
|
|
4
|
+
import { sortOptionsOf } from '@webround/commerce';
|
|
5
|
+
import { createShopStore, shopActions } from '@webround/commerce';
|
|
6
|
+
import { useResource } from './use-resource.js';
|
|
7
|
+
export function useShop(options) {
|
|
8
|
+
const { catalog, context, keepPrevious = true } = options;
|
|
9
|
+
// The state: in memory, in the hook, unless a store was given
|
|
10
|
+
const own = useRef(null);
|
|
11
|
+
if (!options.store && !own.current)
|
|
12
|
+
own.current = createShopStore(options.initial);
|
|
13
|
+
const store = options.store ?? own.current;
|
|
14
|
+
const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);
|
|
15
|
+
const actions = useMemo(() => shopActions(store), [store]);
|
|
16
|
+
const storeData = useResource(catalog.resource.storeData.get(context.langCode === undefined ? undefined : { langCode: context.langCode }));
|
|
17
|
+
const data = storeData.data?.data;
|
|
18
|
+
// A new `context` object at every render is the same context: only what is in it makes a new question
|
|
19
|
+
const contextKey = canonicalJson(context);
|
|
20
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
21
|
+
const asked = useMemo(() => shopQuestion(catalog, data, state, context), [catalog, data, state, contextKey]);
|
|
22
|
+
const products = useResource(asked?.resource ?? null, { keepPrevious });
|
|
23
|
+
const sortOptions = useMemo(() => (data ? sortOptionsOf(data) : []), [data]);
|
|
24
|
+
// With no question yet the products are waiting for the store's data (or failed with it): say that, so the screen has one thing to look at
|
|
25
|
+
const retry = useCallback(() => storeData.retry(), [storeData]);
|
|
26
|
+
const results = asked
|
|
27
|
+
? products
|
|
28
|
+
: { status: storeData.status === 'error' ? 'error' : 'pending', data: undefined, error: storeData.error, isFetching: storeData.status !== 'error', isStale: false, retry };
|
|
29
|
+
return { state, actions, storeData, sortOptions, question: asked?.question ?? null, results };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=use-shop.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Wishlist, type WishlistState } from '@webround/commerce';
|
|
2
|
+
export interface UseWishlistResult {
|
|
3
|
+
state: WishlistState;
|
|
4
|
+
count: number;
|
|
5
|
+
/** Whether a variant is in the wishlist. */
|
|
6
|
+
has(variantId: string): boolean;
|
|
7
|
+
/** The switch: adds a variant that is not in the wishlist, removes one that is. Throws what the store refuses; `LOGIN_REQUIRED` for a guest. */
|
|
8
|
+
toggle: Wishlist['toggle'];
|
|
9
|
+
refresh: Wishlist['refresh'];
|
|
10
|
+
}
|
|
11
|
+
/** `commerce` is what `createCommerce` made (or `@webround/commerce-react/site`'s). Any number of components can use it (a heart on each product): the wishlist starts once. A server render says `unknown` and nothing in it. */
|
|
12
|
+
export declare function useWishlist(commerce: {
|
|
13
|
+
wishlist: Wishlist;
|
|
14
|
+
}): UseWishlistResult;
|
|
15
|
+
//# sourceMappingURL=use-wishlist.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
import { isInWishlist } from '@webround/commerce';
|
|
3
|
+
/** `commerce` is what `createCommerce` made (or `@webround/commerce-react/site`'s). Any number of components can use it (a heart on each product): the wishlist starts once. A server render says `unknown` and nothing in it. */
|
|
4
|
+
export function useWishlist(commerce) {
|
|
5
|
+
const { wishlist } = commerce;
|
|
6
|
+
const state = useSyncExternalStore(wishlist.subscribe, wishlist.getState, wishlist.getState);
|
|
7
|
+
useEffect(() => { void wishlist.start(); }, [wishlist]);
|
|
8
|
+
const has = useCallback((variantId) => isInWishlist(state, variantId), [state.variantIds]);
|
|
9
|
+
const actions = useMemo(() => ({ toggle: (variantId) => wishlist.toggle(variantId), refresh: () => wishlist.refresh() }), [wishlist]);
|
|
10
|
+
return { state, count: state.count, has, toggle: actions.toggle, refresh: actions.refresh };
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=use-wishlist.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@webround/commerce-react",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"development": "./src/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"./site": {
|
|
15
|
+
"import": "./dist/site.js",
|
|
16
|
+
"types": "./dist/site.d.ts",
|
|
17
|
+
"development": "./src/site.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"!dist/**/*.map"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"dev": "tsc --watch",
|
|
27
|
+
"test": "node --test test/*.test.ts",
|
|
28
|
+
"typecheck": "tsc --noEmit -p tsconfig.test.json"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@webround/commerce": "^0.3.0",
|
|
32
|
+
"@webround/core": "^0.3.0",
|
|
33
|
+
"react": ">=19"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/jsdom": "^30.0.0",
|
|
37
|
+
"@types/react": "^19.3.0",
|
|
38
|
+
"@types/react-dom": "^19.3.0",
|
|
39
|
+
"jsdom": "^26.1.0",
|
|
40
|
+
"react": "^19.3.0",
|
|
41
|
+
"react-dom": "^19.3.0",
|
|
42
|
+
"typescript": "^5.5.0"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/README.md
DELETED