@yoamigo.com/core 0.4.7 → 1.0.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/cart-storage-DFdGPcwm.d.ts +176 -0
- package/dist/index.d.ts +1198 -39
- package/dist/index.js +2410 -468
- package/dist/lib.d.ts +93 -2
- package/dist/lib.js +337 -4
- package/dist/prod.d.ts +2 -2
- package/dist/prod.js +74 -1
- package/dist/router.d.ts +80 -2
- package/dist/router.js +74 -1
- package/package.json +1 -1
- package/dist/builder-selection-CYP91nRu.d.ts +0 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
|
+
import { C as ChangeSource, Q as QueryFilter, b as CollectionRecord, o as CartItem, E as EditMode } from './cart-storage-DFdGPcwm.js';
|
|
2
|
+
export { a as CollectionClient, d as CollectionClientConfig, t as ContentStoreContextType, v as ContentStoreMode, p as ContentStoreProvider, L as ListOptions, c as ListResponse, S as SingleResponse, f as clearSessionId, g as getCollectionClient, n as getLocalCartItemCount, k as getLocalCartItems, e as getSessionId, i as initBuilderSelection, r as resetCollectionClient, q as useContentStore } from './cart-storage-DFdGPcwm.js';
|
|
3
|
+
export { D as BackgroundConfig, F as BackgroundImageConfig, C as ContentStoreProviderProd, q as EmbedFieldValue, r as EmbedType, I as ImageFieldValue, f as MarkdownText, g as MarkdownTextProps, O as OverlayConfig, P as PageInfo, c as StaticImage, d as StaticImageProps, M as StaticText, S as StaticTextProps, V as VideoFieldValue, y as YaContainer, B as YaContainerProps, m as YaEmbed, o as YaEmbedProps, Y as YaImage, h as YaImageProps, w as YaLink, x as YaLinkProps, j as YaVideo, l as YaVideoProps, b as background, e as embed, i as image, z as parseBackgroundConfig, p as parseEmbedUrl, A as serializeBackgroundConfig, n as serializeEmbedValue, s as serializeImageValue, k as serializeVideoValue, t as text, u as useContentStoreProd, v as video } from './content-helpers-DOUKazMz.js';
|
|
1
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
5
|
import React$1, { ReactNode } from 'react';
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export { Route, Switch, useParams } from 'wouter';
|
|
6
|
+
export { Link, LinkProps, NavigateFunction, RouteDefinition, Router, RouterProps, ScrollRestoration, createRouteDefinition, extractRouteParams, filePathToRoutePath, generatePath, sortRoutesBySpecificity, useNavigate } from './router.js';
|
|
7
|
+
export { Route, Switch, useLocation, useParams } from 'wouter';
|
|
6
8
|
export { A as AssetResolverFn, C as ContentRegistry, c as contentRegistry, a as getAllContent, g as getContent, h as hasContent, r as registerContent, b as resolveAssetUrl, s as setAssetResolver } from './asset-resolver-BnIvDkVv.js';
|
|
7
|
-
export { i as initBuilderSelection } from './builder-selection-CYP91nRu.js';
|
|
8
|
-
|
|
9
|
-
type EditMode = 'read-only' | 'inline-edit';
|
|
10
|
-
type ChangeSource = 'user' | 'ai' | 'initial';
|
|
11
|
-
interface PageInfo {
|
|
12
|
-
path: string;
|
|
13
|
-
label: string;
|
|
14
|
-
}
|
|
15
|
-
interface ActiveFieldCallbacks {
|
|
16
|
-
close: () => void;
|
|
17
|
-
}
|
|
18
|
-
interface ContentStore {
|
|
19
|
-
getValue: (fieldId: string) => string;
|
|
20
|
-
setValue: (fieldId: string, value: string, source?: ChangeSource) => void;
|
|
21
|
-
getChangeSource: (fieldId: string) => ChangeSource;
|
|
22
|
-
clearChangeSource: (fieldId: string) => void;
|
|
23
|
-
mode: EditMode;
|
|
24
|
-
setMode: (mode: EditMode) => void;
|
|
25
|
-
subscribe: (listener: () => void) => () => void;
|
|
26
|
-
saveToWorker?: (fieldId: string, value: string) => Promise<void>;
|
|
27
|
-
activeFieldId: string | null;
|
|
28
|
-
setActiveField: (fieldId: string, callbacks: ActiveFieldCallbacks) => void;
|
|
29
|
-
clearActiveField: () => void;
|
|
30
|
-
getPages: () => PageInfo[];
|
|
31
|
-
}
|
|
32
|
-
type ContentStoreContextType = ContentStore;
|
|
33
|
-
type ContentStoreMode = EditMode;
|
|
34
|
-
declare function useContentStore(): ContentStoreContextType;
|
|
35
|
-
interface ContentStoreProviderProps {
|
|
36
|
-
children: ReactNode;
|
|
37
|
-
initialContent?: Record<string, string>;
|
|
38
|
-
initialMode?: EditMode;
|
|
39
|
-
pages?: PageInfo[];
|
|
40
|
-
}
|
|
41
|
-
declare function ContentStoreProvider({ children, initialContent, initialMode, pages, }: ContentStoreProviderProps): react_jsx_runtime.JSX.Element;
|
|
42
9
|
|
|
43
10
|
/** Common HTML elements that YaText can render as */
|
|
44
11
|
type YaTextElement = 'span' | 'div' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label' | 'strong' | 'em';
|
|
@@ -126,6 +93,435 @@ interface TooltipProps {
|
|
|
126
93
|
}
|
|
127
94
|
declare function Tooltip({ content, children, position, delay, }: TooltipProps): react_jsx_runtime.JSX.Element;
|
|
128
95
|
|
|
96
|
+
interface CollectionContentContextValue {
|
|
97
|
+
/** Get a value from the collection record */
|
|
98
|
+
getValue: (fieldId: string) => string;
|
|
99
|
+
/** Set a value (triggers local update + backend save). Only available in edit mode. */
|
|
100
|
+
setValue?: (fieldId: string, value: string, source?: ChangeSource) => void;
|
|
101
|
+
/** Save to backend (creates/updates StagedAppRecord). Only available in edit mode. */
|
|
102
|
+
saveToBackend?: (fieldId: string, value: string) => Promise<void>;
|
|
103
|
+
/** Get the change source for animation tracking */
|
|
104
|
+
getChangeSource?: (fieldId: string) => ChangeSource;
|
|
105
|
+
/** Clear change source after animation completes */
|
|
106
|
+
clearChangeSource?: (fieldId: string) => void;
|
|
107
|
+
/** Whether this content is from a collection */
|
|
108
|
+
isCollectionContent: true;
|
|
109
|
+
/** The prefix used for field IDs */
|
|
110
|
+
prefix: string;
|
|
111
|
+
/** The record ID (for UPDATE) or undefined (for CREATE) */
|
|
112
|
+
recordId?: string;
|
|
113
|
+
/** Collection slug for API calls */
|
|
114
|
+
collectionSlug?: string;
|
|
115
|
+
/** App ID for API calls */
|
|
116
|
+
appId?: string;
|
|
117
|
+
/** Whether editing is enabled */
|
|
118
|
+
isEditable: boolean;
|
|
119
|
+
}
|
|
120
|
+
interface CollectionContentProviderProps {
|
|
121
|
+
/** The collection record data (the `data` field from CollectionRecord) */
|
|
122
|
+
record: Record<string, unknown>;
|
|
123
|
+
/** Prefix for field IDs (e.g., "product" makes "product.name") */
|
|
124
|
+
prefix: string;
|
|
125
|
+
/** Child components */
|
|
126
|
+
children: ReactNode;
|
|
127
|
+
/** The AppRecord ID (required for UPDATE, optional for CREATE) */
|
|
128
|
+
recordId?: string;
|
|
129
|
+
/** Collection slug for API calls */
|
|
130
|
+
collectionSlug?: string;
|
|
131
|
+
/** App ID for API calls */
|
|
132
|
+
appId?: string;
|
|
133
|
+
/** Whether this is a new record or existing */
|
|
134
|
+
changeType?: 'CREATE' | 'UPDATE';
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Hook to get collection content context.
|
|
138
|
+
* Returns null if not inside a CollectionContentProvider.
|
|
139
|
+
*/
|
|
140
|
+
declare function useCollectionContent(): CollectionContentContextValue | null;
|
|
141
|
+
/**
|
|
142
|
+
* Provider that makes collection record data available to YaText, YaImage, etc.
|
|
143
|
+
*
|
|
144
|
+
* Fields are accessed using dot notation with the prefix:
|
|
145
|
+
* - `prefix="product"` + field `name` → `fieldId="product.name"`
|
|
146
|
+
* - Nested: `prefix="product"` + field `image.src` → `fieldId="product.image.src"`
|
|
147
|
+
*
|
|
148
|
+
* When edit props are provided, inline edits are saved to StagedAppRecord.
|
|
149
|
+
*/
|
|
150
|
+
declare function CollectionContentProvider({ record, prefix, children, recordId, collectionSlug, appId, changeType: _changeType, }: CollectionContentProviderProps): react_jsx_runtime.JSX.Element;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Collection Hooks
|
|
154
|
+
*
|
|
155
|
+
* React hooks for fetching collection data from the API.
|
|
156
|
+
* Used in templates for dynamic pages powered by collection records.
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
interface UseCollectionListOptions {
|
|
160
|
+
/** Collection slug (e.g., 'products', 'posts') */
|
|
161
|
+
collection: string;
|
|
162
|
+
/** Optional filters to apply */
|
|
163
|
+
filters?: QueryFilter[];
|
|
164
|
+
/** Page number (1-indexed for convenience) */
|
|
165
|
+
page?: number;
|
|
166
|
+
/** Number of items per page */
|
|
167
|
+
pageSize?: number;
|
|
168
|
+
/** Field to order by */
|
|
169
|
+
orderBy?: string;
|
|
170
|
+
/** Order direction */
|
|
171
|
+
orderDir?: 'asc' | 'desc';
|
|
172
|
+
/** Whether to fetch (useful for conditional fetching) */
|
|
173
|
+
enabled?: boolean;
|
|
174
|
+
}
|
|
175
|
+
interface UseCollectionListResult<T = Record<string, unknown>> {
|
|
176
|
+
/** The fetched records, or null if not yet loaded */
|
|
177
|
+
data: CollectionRecord<T>[] | null;
|
|
178
|
+
/** Whether the initial fetch is in progress */
|
|
179
|
+
isLoading: boolean;
|
|
180
|
+
/** Error message if fetch failed */
|
|
181
|
+
error: string | null;
|
|
182
|
+
/** Total count of records matching the query */
|
|
183
|
+
totalCount: number;
|
|
184
|
+
/** Whether there are more pages */
|
|
185
|
+
hasMore: boolean;
|
|
186
|
+
/** Current page number */
|
|
187
|
+
currentPage: number;
|
|
188
|
+
/** Total number of pages */
|
|
189
|
+
totalPages: number;
|
|
190
|
+
/** Refetch the data */
|
|
191
|
+
refetch: () => void;
|
|
192
|
+
/** Go to a specific page */
|
|
193
|
+
goToPage: (page: number) => void;
|
|
194
|
+
}
|
|
195
|
+
interface UseCollectionRecordOptions {
|
|
196
|
+
/** Collection slug (e.g., 'products', 'posts') */
|
|
197
|
+
collection: string;
|
|
198
|
+
/** Field name that contains the slug (e.g., 'slug', 'handle') */
|
|
199
|
+
slugField: string;
|
|
200
|
+
/** Slug value to look up */
|
|
201
|
+
slugValue: string;
|
|
202
|
+
/** Whether to fetch (useful for conditional fetching) */
|
|
203
|
+
enabled?: boolean;
|
|
204
|
+
}
|
|
205
|
+
interface UseCollectionRecordResult<T = Record<string, unknown>> {
|
|
206
|
+
/** The fetched record, or null if not yet loaded */
|
|
207
|
+
data: CollectionRecord<T> | null;
|
|
208
|
+
/** Whether the fetch is in progress */
|
|
209
|
+
isLoading: boolean;
|
|
210
|
+
/** Error message if fetch failed */
|
|
211
|
+
error: string | null;
|
|
212
|
+
/** Whether the record was not found (404) */
|
|
213
|
+
notFound: boolean;
|
|
214
|
+
/** Refetch the data */
|
|
215
|
+
refetch: () => void;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Hook for fetching a list of collection records with pagination.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```tsx
|
|
222
|
+
* const { data: products, isLoading, goToPage } = useCollectionList({
|
|
223
|
+
* collection: 'products',
|
|
224
|
+
* pageSize: 12,
|
|
225
|
+
* orderBy: 'createdAt',
|
|
226
|
+
* orderDir: 'desc'
|
|
227
|
+
* })
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
declare function useCollectionList<T = Record<string, unknown>>(options: UseCollectionListOptions): UseCollectionListResult<T>;
|
|
231
|
+
/**
|
|
232
|
+
* Hook for fetching a single collection record by slug.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```tsx
|
|
236
|
+
* const { slug } = useParams()
|
|
237
|
+
* const { data: product, isLoading, notFound } = useCollectionRecord({
|
|
238
|
+
* collection: 'products',
|
|
239
|
+
* slugField: 'slug',
|
|
240
|
+
* slugValue: slug!
|
|
241
|
+
* })
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
declare function useCollectionRecord<T = Record<string, unknown>>(options: UseCollectionRecordOptions): UseCollectionRecordResult<T>;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* CollectionPage Components
|
|
248
|
+
*
|
|
249
|
+
* Higher-level wrapper components for common collection page patterns.
|
|
250
|
+
* These simplify creating list and detail pages powered by collection data.
|
|
251
|
+
*
|
|
252
|
+
* @example List Page
|
|
253
|
+
* ```tsx
|
|
254
|
+
* <CollectionListPage
|
|
255
|
+
* collection="products"
|
|
256
|
+
* pageSize={12}
|
|
257
|
+
* render={({ data, isLoading }) => (
|
|
258
|
+
* isLoading ? <Skeleton /> : <ProductGrid products={data} />
|
|
259
|
+
* )}
|
|
260
|
+
* />
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* @example Detail Page
|
|
264
|
+
* ```tsx
|
|
265
|
+
* <CollectionDetailPage
|
|
266
|
+
* collection="products"
|
|
267
|
+
* slugField="slug"
|
|
268
|
+
* render={({ data, isLoading, notFound }) => {
|
|
269
|
+
* if (isLoading) return <Skeleton />
|
|
270
|
+
* if (notFound) return <NotFound />
|
|
271
|
+
* return <ProductDetail product={data} />
|
|
272
|
+
* }}
|
|
273
|
+
* />
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
|
|
277
|
+
interface CollectionListPageProps<T = Record<string, unknown>> {
|
|
278
|
+
/** Collection slug (e.g., 'products', 'posts') */
|
|
279
|
+
collection: string;
|
|
280
|
+
/** Number of items per page */
|
|
281
|
+
pageSize?: number;
|
|
282
|
+
/** Initial page number */
|
|
283
|
+
page?: number;
|
|
284
|
+
/** Filters to apply */
|
|
285
|
+
filters?: QueryFilter[];
|
|
286
|
+
/** Field to order by */
|
|
287
|
+
orderBy?: string;
|
|
288
|
+
/** Order direction */
|
|
289
|
+
orderDir?: 'asc' | 'desc';
|
|
290
|
+
/** Whether to fetch data */
|
|
291
|
+
enabled?: boolean;
|
|
292
|
+
/** Render function that receives the list result */
|
|
293
|
+
render: (result: UseCollectionListResult<T>) => ReactNode;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Wrapper component for collection list pages.
|
|
297
|
+
*
|
|
298
|
+
* Handles data fetching and provides the result to a render function,
|
|
299
|
+
* giving you full control over the UI.
|
|
300
|
+
*/
|
|
301
|
+
declare function CollectionListPage<T = Record<string, unknown>>({ collection, pageSize, page, filters, orderBy, orderDir, enabled, render, }: CollectionListPageProps<T>): ReactNode;
|
|
302
|
+
interface CollectionDetailPageProps<T = Record<string, unknown>> {
|
|
303
|
+
/** Collection slug (e.g., 'products', 'posts') */
|
|
304
|
+
collection: string;
|
|
305
|
+
/** Field name that contains the slug (e.g., 'slug', 'handle') */
|
|
306
|
+
slugField?: string;
|
|
307
|
+
/** URL parameter name for the slug (default: 'slug') */
|
|
308
|
+
slugParam?: string;
|
|
309
|
+
/** Override slug value (instead of reading from URL params) */
|
|
310
|
+
slug?: string;
|
|
311
|
+
/** Whether to fetch data */
|
|
312
|
+
enabled?: boolean;
|
|
313
|
+
/** Render function that receives the record result */
|
|
314
|
+
render: (result: CollectionDetailRenderProps<T>) => ReactNode;
|
|
315
|
+
}
|
|
316
|
+
interface CollectionDetailRenderProps<T = Record<string, unknown>> extends UseCollectionRecordResult<T> {
|
|
317
|
+
/** The slug value from URL params */
|
|
318
|
+
slug: string;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Wrapper component for collection detail pages.
|
|
322
|
+
*
|
|
323
|
+
* Automatically reads the slug from URL params (default: `:slug`)
|
|
324
|
+
* and fetches the matching record.
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```tsx
|
|
328
|
+
* // For route /products/:slug
|
|
329
|
+
* <CollectionDetailPage
|
|
330
|
+
* collection="products"
|
|
331
|
+
* render={({ data, isLoading, notFound }) => {
|
|
332
|
+
* if (isLoading) return <Skeleton />
|
|
333
|
+
* if (notFound) return <NotFound />
|
|
334
|
+
* return <ProductDetail product={data} />
|
|
335
|
+
* }}
|
|
336
|
+
* />
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
declare function CollectionDetailPage<T = Record<string, unknown>>({ collection, slugField, slugParam, slug: slugOverride, enabled, render, }: CollectionDetailPageProps<T>): ReactNode;
|
|
340
|
+
/**
|
|
341
|
+
* Type helper for creating typed collection page components.
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```tsx
|
|
345
|
+
* interface Product {
|
|
346
|
+
* slug: string
|
|
347
|
+
* name: string
|
|
348
|
+
* price: number
|
|
349
|
+
* }
|
|
350
|
+
*
|
|
351
|
+
* // Products list page with type safety
|
|
352
|
+
* function ProductsPage() {
|
|
353
|
+
* return (
|
|
354
|
+
* <CollectionListPage<Product>
|
|
355
|
+
* collection="products"
|
|
356
|
+
* render={({ data }) => (
|
|
357
|
+
* data?.map(p => <div key={p.id}>{p.data.name}</div>)
|
|
358
|
+
* )}
|
|
359
|
+
* />
|
|
360
|
+
* )
|
|
361
|
+
* }
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
type CollectionData<T> = CollectionRecord<T>;
|
|
365
|
+
/**
|
|
366
|
+
* Type helper for list page data
|
|
367
|
+
*/
|
|
368
|
+
type CollectionListData<T> = CollectionRecord<T>[];
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* CollectionList
|
|
372
|
+
*
|
|
373
|
+
* Renders a filtered list of collection items with editing support.
|
|
374
|
+
* Each item is wrapped in CollectionContentProvider so YaText/YaImage
|
|
375
|
+
* components can read and edit collection fields.
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* ```tsx
|
|
379
|
+
* <CollectionList
|
|
380
|
+
* collection="products"
|
|
381
|
+
* prefix="product"
|
|
382
|
+
* filters={[{ field: 'category', operator: 'eq', value: 'guitars' }]}
|
|
383
|
+
* pageSize={6}
|
|
384
|
+
* skeleton={({ index }) => <ProductCardSkeleton key={index} />}
|
|
385
|
+
* empty={<p>No products found</p>}
|
|
386
|
+
* wrapper={({ children }) => (
|
|
387
|
+
* <div className="grid grid-cols-3 gap-4">{children}</div>
|
|
388
|
+
* )}
|
|
389
|
+
* >
|
|
390
|
+
* {({ record, fieldPrefix }) => (
|
|
391
|
+
* <div key={record.id}>
|
|
392
|
+
* <YaImage fieldId={`${fieldPrefix}.image`} />
|
|
393
|
+
* <YaText fieldId={`${fieldPrefix}.name`} />
|
|
394
|
+
* </div>
|
|
395
|
+
* )}
|
|
396
|
+
* </CollectionList>
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
|
|
400
|
+
interface CollectionListRenderProps<T = Record<string, unknown>> {
|
|
401
|
+
/** The collection record */
|
|
402
|
+
record: CollectionRecord<T>;
|
|
403
|
+
/** Index of this item in the list */
|
|
404
|
+
index: number;
|
|
405
|
+
/** The unique fieldId prefix for this item (e.g., "product.abc123") */
|
|
406
|
+
fieldPrefix: string;
|
|
407
|
+
}
|
|
408
|
+
interface CollectionListProps<T = Record<string, unknown>> extends Omit<UseCollectionListOptions, 'enabled'> {
|
|
409
|
+
/** Base prefix for fieldIds. Each item gets `{prefix}.{recordId}` */
|
|
410
|
+
prefix: string;
|
|
411
|
+
/** Render function for each item */
|
|
412
|
+
children: (props: CollectionListRenderProps<T>) => ReactNode;
|
|
413
|
+
/** Render function for skeleton items during loading (REQUIRED for zero layout shift) */
|
|
414
|
+
skeleton: (props: {
|
|
415
|
+
index: number;
|
|
416
|
+
}) => ReactNode;
|
|
417
|
+
/** Empty state render (when no items match) */
|
|
418
|
+
empty?: ReactNode;
|
|
419
|
+
/** Error state render */
|
|
420
|
+
error?: (error: string) => ReactNode;
|
|
421
|
+
/** App ID for editing (auto-detected in builder context) */
|
|
422
|
+
appId?: string;
|
|
423
|
+
/** Whether to enable fetching (default: true) */
|
|
424
|
+
enabled?: boolean;
|
|
425
|
+
/** Render pagination controls */
|
|
426
|
+
pagination?: (props: CollectionListPaginationProps) => ReactNode;
|
|
427
|
+
/** Wrapper element/component for the list items */
|
|
428
|
+
wrapper?: (props: {
|
|
429
|
+
children: ReactNode;
|
|
430
|
+
}) => ReactNode;
|
|
431
|
+
}
|
|
432
|
+
interface CollectionListPaginationProps {
|
|
433
|
+
currentPage: number;
|
|
434
|
+
totalPages: number;
|
|
435
|
+
totalCount: number;
|
|
436
|
+
hasMore: boolean;
|
|
437
|
+
goToPage: (page: number) => void;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Renders a filtered collection list with editing support.
|
|
441
|
+
*
|
|
442
|
+
* Each record is wrapped in CollectionContentProvider with a unique prefix
|
|
443
|
+
* (`{prefix}.{recordId}`) so that multiple items can be rendered and edited
|
|
444
|
+
* on the same page without fieldId collisions.
|
|
445
|
+
*/
|
|
446
|
+
declare function CollectionList<T = Record<string, unknown>>({ collection, prefix, filters, page, pageSize, orderBy, orderDir, children, skeleton, empty, error, appId, enabled, pagination, wrapper: Wrapper, }: CollectionListProps<T>): ReactNode;
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* CollectionItem
|
|
450
|
+
*
|
|
451
|
+
* Renders a single collection item with editing support.
|
|
452
|
+
* The item is wrapped in CollectionContentProvider so YaText/YaImage
|
|
453
|
+
* components can read and edit collection fields.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* ```tsx
|
|
457
|
+
* // By slug with required skeleton for zero layout shift
|
|
458
|
+
* <CollectionItem
|
|
459
|
+
* collection="products"
|
|
460
|
+
* slugField="slug"
|
|
461
|
+
* slugValue="featured-guitar"
|
|
462
|
+
* prefix="featured"
|
|
463
|
+
* skeleton={<FeaturedProductSkeleton />}
|
|
464
|
+
* >
|
|
465
|
+
* {({ record, notFound }) => (
|
|
466
|
+
* notFound ? <NotFound /> :
|
|
467
|
+
* <>
|
|
468
|
+
* <YaImage fieldId="featured.image" />
|
|
469
|
+
* <YaText fieldId="featured.name" />
|
|
470
|
+
* </>
|
|
471
|
+
* )}
|
|
472
|
+
* </CollectionItem>
|
|
473
|
+
*
|
|
474
|
+
* // By ID (uses slug lookup with id field)
|
|
475
|
+
* <CollectionItem
|
|
476
|
+
* collection="products"
|
|
477
|
+
* slugField="id"
|
|
478
|
+
* slugValue={productId}
|
|
479
|
+
* prefix="product"
|
|
480
|
+
* skeleton={<ProductCardSkeleton />}
|
|
481
|
+
* >
|
|
482
|
+
* {({ record }) => <ProductCard />}
|
|
483
|
+
* </CollectionItem>
|
|
484
|
+
* ```
|
|
485
|
+
*/
|
|
486
|
+
|
|
487
|
+
interface CollectionItemRenderProps<T = Record<string, unknown>> {
|
|
488
|
+
/** The fetched record, or null if not loaded/found */
|
|
489
|
+
record: CollectionRecord<T> | null;
|
|
490
|
+
/** Whether the fetch is in progress */
|
|
491
|
+
isLoading: boolean;
|
|
492
|
+
/** Whether the record was not found (404) */
|
|
493
|
+
notFound: boolean;
|
|
494
|
+
/** Error message if fetch failed */
|
|
495
|
+
error: string | null;
|
|
496
|
+
/** Refetch the record */
|
|
497
|
+
refetch: () => void;
|
|
498
|
+
}
|
|
499
|
+
interface CollectionItemProps<T = Record<string, unknown>> {
|
|
500
|
+
/** Collection slug (e.g., 'products', 'posts') */
|
|
501
|
+
collection: string;
|
|
502
|
+
/** Prefix for fieldIds (e.g., "featured" makes "featured.name") */
|
|
503
|
+
prefix: string;
|
|
504
|
+
/** Field name to use for lookup (e.g., 'slug', 'handle', 'id') */
|
|
505
|
+
slugField?: string;
|
|
506
|
+
/** Value to look up */
|
|
507
|
+
slugValue: string;
|
|
508
|
+
/** Render function */
|
|
509
|
+
children: (props: CollectionItemRenderProps<T>) => ReactNode;
|
|
510
|
+
/** Skeleton to show during loading (REQUIRED for zero layout shift) */
|
|
511
|
+
skeleton: ReactNode;
|
|
512
|
+
/** App ID for editing (auto-detected in builder context) */
|
|
513
|
+
appId?: string;
|
|
514
|
+
/** Whether to enable fetching (default: true) */
|
|
515
|
+
enabled?: boolean;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Renders a single collection item with editing support.
|
|
519
|
+
*
|
|
520
|
+
* The record is wrapped in CollectionContentProvider with the specified prefix
|
|
521
|
+
* so YaText/YaImage can read fields like `{prefix}.name`, `{prefix}.image`, etc.
|
|
522
|
+
*/
|
|
523
|
+
declare function CollectionItem<T = Record<string, unknown>>({ collection, prefix, slugField, slugValue, children, skeleton, appId, enabled, }: CollectionItemProps<T>): ReactNode;
|
|
524
|
+
|
|
129
525
|
/**
|
|
130
526
|
* Animation state for a single field
|
|
131
527
|
*/
|
|
@@ -201,6 +597,108 @@ interface AIEditProviderProps {
|
|
|
201
597
|
*/
|
|
202
598
|
declare function AIEditProvider({ children, staggerDelay }: AIEditProviderProps): react_jsx_runtime.JSX.Element;
|
|
203
599
|
|
|
600
|
+
/**
|
|
601
|
+
* useCart Hook
|
|
602
|
+
*
|
|
603
|
+
* React hook for managing shopping cart state.
|
|
604
|
+
* Supports both logged-in users (API persistence) and guests (localStorage fallback).
|
|
605
|
+
*/
|
|
606
|
+
|
|
607
|
+
interface UseCartOptions {
|
|
608
|
+
/** App ID to scope the cart */
|
|
609
|
+
appId?: string;
|
|
610
|
+
/** App user token for authenticated users */
|
|
611
|
+
userToken?: string | null;
|
|
612
|
+
/** API base URL */
|
|
613
|
+
apiUrl?: string;
|
|
614
|
+
}
|
|
615
|
+
interface UseCartResult {
|
|
616
|
+
/** Cart items */
|
|
617
|
+
items: CartItem[];
|
|
618
|
+
/** Total number of items (sum of quantities) */
|
|
619
|
+
itemCount: number;
|
|
620
|
+
/** Whether the cart is loading */
|
|
621
|
+
isLoading: boolean;
|
|
622
|
+
/** Error message if any */
|
|
623
|
+
error: string | null;
|
|
624
|
+
/** Add an item to the cart */
|
|
625
|
+
addItem: (productId: string, collectionSlug: string, quantity?: number, variantId?: string, data?: Record<string, unknown>) => Promise<void>;
|
|
626
|
+
/** Update item quantity (0 removes the item) */
|
|
627
|
+
updateQuantity: (itemId: string, quantity: number) => Promise<void>;
|
|
628
|
+
/** Remove an item from the cart */
|
|
629
|
+
removeItem: (itemId: string) => Promise<void>;
|
|
630
|
+
/** Clear all items from the cart */
|
|
631
|
+
clearCart: () => Promise<void>;
|
|
632
|
+
/** Merge guest cart into user cart after login */
|
|
633
|
+
mergeGuestCart: () => Promise<void>;
|
|
634
|
+
/** Refresh cart from server */
|
|
635
|
+
refresh: () => Promise<void>;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Hook for managing shopping cart.
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* ```tsx
|
|
642
|
+
* const {
|
|
643
|
+
* items,
|
|
644
|
+
* itemCount,
|
|
645
|
+
* addItem,
|
|
646
|
+
* removeItem,
|
|
647
|
+
* clearCart,
|
|
648
|
+
* } = useCart({ userToken: authToken })
|
|
649
|
+
*
|
|
650
|
+
* // Add item
|
|
651
|
+
* await addItem('product-123', 'products', 2)
|
|
652
|
+
*
|
|
653
|
+
* // Remove item
|
|
654
|
+
* await removeItem('item-456')
|
|
655
|
+
* ```
|
|
656
|
+
*/
|
|
657
|
+
declare function useCart(options?: UseCartOptions): UseCartResult;
|
|
658
|
+
|
|
659
|
+
interface CartProviderProps extends UseCartOptions {
|
|
660
|
+
/** Child components */
|
|
661
|
+
children: ReactNode;
|
|
662
|
+
/** Callback when user token changes (for cart merge) */
|
|
663
|
+
onUserTokenChange?: (hasToken: boolean) => void;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Cart provider component.
|
|
667
|
+
*
|
|
668
|
+
* @example
|
|
669
|
+
* ```tsx
|
|
670
|
+
* // In your app root
|
|
671
|
+
* function App() {
|
|
672
|
+
* const [userToken, setUserToken] = useState<string | null>(null)
|
|
673
|
+
*
|
|
674
|
+
* return (
|
|
675
|
+
* <CartProvider userToken={userToken}>
|
|
676
|
+
* <MyApp />
|
|
677
|
+
* </CartProvider>
|
|
678
|
+
* )
|
|
679
|
+
* }
|
|
680
|
+
*
|
|
681
|
+
* // In any child component
|
|
682
|
+
* function CartIcon() {
|
|
683
|
+
* const { itemCount } = useCartContext()
|
|
684
|
+
* return <Badge count={itemCount}>Cart</Badge>
|
|
685
|
+
* }
|
|
686
|
+
* ```
|
|
687
|
+
*/
|
|
688
|
+
declare function CartProvider({ children, userToken, onUserTokenChange, ...options }: CartProviderProps): react_jsx_runtime.JSX.Element;
|
|
689
|
+
/**
|
|
690
|
+
* Hook to access cart context.
|
|
691
|
+
* Must be used within a CartProvider.
|
|
692
|
+
*
|
|
693
|
+
* @throws Error if used outside CartProvider
|
|
694
|
+
*/
|
|
695
|
+
declare function useCartContext(): UseCartResult;
|
|
696
|
+
/**
|
|
697
|
+
* Hook to access cart context with optional null return.
|
|
698
|
+
* Useful when cart might not be available.
|
|
699
|
+
*/
|
|
700
|
+
declare function useOptionalCartContext(): UseCartResult | null;
|
|
701
|
+
|
|
204
702
|
/**
|
|
205
703
|
* Text Diffing Utilities for AI Edit Animations
|
|
206
704
|
*
|
|
@@ -572,4 +1070,665 @@ interface UseSafeTriangleReturn<T extends HTMLElement, U extends HTMLElement> {
|
|
|
572
1070
|
*/
|
|
573
1071
|
declare function useSafeTriangle<T extends HTMLElement = HTMLElement, U extends HTMLElement = HTMLDivElement>(options?: UseSafeTriangleOptions): UseSafeTriangleReturn<T, U>;
|
|
574
1072
|
|
|
575
|
-
|
|
1073
|
+
/**
|
|
1074
|
+
* useContent - Unified Content Access Hook
|
|
1075
|
+
*
|
|
1076
|
+
* Provides a single API for Ya* components to access content regardless
|
|
1077
|
+
* of whether it comes from static content (ContentStoreProvider) or
|
|
1078
|
+
* collection data (CollectionContentProvider).
|
|
1079
|
+
*
|
|
1080
|
+
* @example
|
|
1081
|
+
* ```tsx
|
|
1082
|
+
* // Simple text field
|
|
1083
|
+
* const content = useContent<string>(fieldId)
|
|
1084
|
+
* const text = content.value
|
|
1085
|
+
*
|
|
1086
|
+
* // Typed image field
|
|
1087
|
+
* const content = useContent<ImageField>(fieldId)
|
|
1088
|
+
* const { src, alt } = content.value
|
|
1089
|
+
*
|
|
1090
|
+
* // Compound fields (like YaLink)
|
|
1091
|
+
* const textContent = useContent<string>(`${fieldId}.text`)
|
|
1092
|
+
* const hrefContent = useContent<string>(`${fieldId}.href`)
|
|
1093
|
+
* ```
|
|
1094
|
+
*/
|
|
1095
|
+
|
|
1096
|
+
interface ContentHandle<T> {
|
|
1097
|
+
/** Parsed value (for object types like ImageField) */
|
|
1098
|
+
value: T;
|
|
1099
|
+
/** Get raw string value */
|
|
1100
|
+
get(): string;
|
|
1101
|
+
/** Set value with optional change source for animations */
|
|
1102
|
+
set(value: T | string, source?: ChangeSource): void;
|
|
1103
|
+
/** Persist to backend (static or collection) */
|
|
1104
|
+
save(): Promise<void>;
|
|
1105
|
+
/** Current change source (for animations: 'user' | 'ai' | 'initial') */
|
|
1106
|
+
changeSource: ChangeSource | undefined;
|
|
1107
|
+
/** Clear change source after animation completes */
|
|
1108
|
+
clearChangeSource(): void;
|
|
1109
|
+
/** Current edit mode: 'read-only' | 'inline-edit' */
|
|
1110
|
+
mode: EditMode;
|
|
1111
|
+
/** Is this field in a collection context? */
|
|
1112
|
+
isCollectionField: boolean;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Unified content access hook.
|
|
1116
|
+
*
|
|
1117
|
+
* Automatically routes to the correct provider based on fieldId:
|
|
1118
|
+
* - If inside CollectionContentProvider and fieldId starts with prefix, uses collection context
|
|
1119
|
+
* - Otherwise uses ContentStoreProvider
|
|
1120
|
+
*
|
|
1121
|
+
* @param fieldId - The field identifier (e.g., "hero.title" or "product.name")
|
|
1122
|
+
* @returns ContentHandle<T> with value, get, set, save, and animation helpers
|
|
1123
|
+
*/
|
|
1124
|
+
declare function useContent<T = string>(fieldId: string): ContentHandle<T>;
|
|
1125
|
+
|
|
1126
|
+
/** Simple text field - just a string */
|
|
1127
|
+
type TextField = string;
|
|
1128
|
+
/** Image field with metadata */
|
|
1129
|
+
interface ImageField {
|
|
1130
|
+
/** Image source URL */
|
|
1131
|
+
src: string;
|
|
1132
|
+
/** Alt text for accessibility */
|
|
1133
|
+
alt?: string;
|
|
1134
|
+
/** CSS object-fit */
|
|
1135
|
+
objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
|
|
1136
|
+
/** CSS object-position (e.g., "50% 50%", "top left") */
|
|
1137
|
+
objectPosition?: string;
|
|
1138
|
+
}
|
|
1139
|
+
/** Link field with text and destination */
|
|
1140
|
+
interface LinkField {
|
|
1141
|
+
/** Display text */
|
|
1142
|
+
text: string;
|
|
1143
|
+
/** URL or route path */
|
|
1144
|
+
href: string;
|
|
1145
|
+
}
|
|
1146
|
+
/** Video field with playback options */
|
|
1147
|
+
interface VideoField {
|
|
1148
|
+
/** Video source type */
|
|
1149
|
+
type: 'upload' | 'youtube' | 'vimeo';
|
|
1150
|
+
/** Video URL (for upload) or video ID (for embeds) */
|
|
1151
|
+
src: string;
|
|
1152
|
+
/** Poster image URL */
|
|
1153
|
+
poster?: string;
|
|
1154
|
+
/** Autoplay video (requires muted for browser policy) */
|
|
1155
|
+
autoplay?: boolean;
|
|
1156
|
+
/** Mute video audio */
|
|
1157
|
+
muted?: boolean;
|
|
1158
|
+
/** Loop video playback */
|
|
1159
|
+
loop?: boolean;
|
|
1160
|
+
/** Show video controls */
|
|
1161
|
+
controls?: boolean;
|
|
1162
|
+
/** Play inline on mobile (prevent fullscreen hijack) */
|
|
1163
|
+
playsinline?: boolean;
|
|
1164
|
+
/** Preload strategy */
|
|
1165
|
+
preload?: 'none' | 'metadata' | 'auto';
|
|
1166
|
+
/** CSS object-fit */
|
|
1167
|
+
objectFit?: 'cover' | 'contain' | 'fill';
|
|
1168
|
+
/** CSS aspect-ratio (e.g., "16/9") */
|
|
1169
|
+
aspectRatio?: string;
|
|
1170
|
+
}
|
|
1171
|
+
/** Embed field for iframes (YouTube, Vimeo, Spotify, etc.) */
|
|
1172
|
+
interface EmbedField {
|
|
1173
|
+
/** Embed type */
|
|
1174
|
+
type: 'youtube' | 'vimeo' | 'spotify' | 'soundcloud' | 'custom';
|
|
1175
|
+
/** Embed URL or ID */
|
|
1176
|
+
src: string;
|
|
1177
|
+
/** CSS aspect-ratio (e.g., "16/9") */
|
|
1178
|
+
aspectRatio?: string;
|
|
1179
|
+
/** Fixed height in pixels (overrides aspectRatio) */
|
|
1180
|
+
height?: number;
|
|
1181
|
+
}
|
|
1182
|
+
/** Container/section field for background styling */
|
|
1183
|
+
interface ContainerField {
|
|
1184
|
+
/** Background color */
|
|
1185
|
+
backgroundColor?: string;
|
|
1186
|
+
/** Background image URL */
|
|
1187
|
+
backgroundImage?: string;
|
|
1188
|
+
/** Background position */
|
|
1189
|
+
backgroundPosition?: string;
|
|
1190
|
+
/** Background size */
|
|
1191
|
+
backgroundSize?: 'cover' | 'contain' | 'auto';
|
|
1192
|
+
}
|
|
1193
|
+
/** All field types union */
|
|
1194
|
+
type AnyField = TextField | ImageField | LinkField | VideoField | EmbedField | ContainerField;
|
|
1195
|
+
/** Parse raw string value into typed field */
|
|
1196
|
+
declare function parseFieldValue<T>(raw: string | undefined): T;
|
|
1197
|
+
/** Stringify field value for storage */
|
|
1198
|
+
declare function stringifyFieldValue<T>(value: T): string;
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* useCheckout Hook
|
|
1202
|
+
*
|
|
1203
|
+
* React hook for initiating Stripe Checkout sessions.
|
|
1204
|
+
*/
|
|
1205
|
+
interface UseCheckoutOptions {
|
|
1206
|
+
/** App ID to scope the checkout */
|
|
1207
|
+
appId?: string;
|
|
1208
|
+
/** App user token for authenticated users */
|
|
1209
|
+
userToken?: string | null;
|
|
1210
|
+
/** API base URL */
|
|
1211
|
+
apiUrl?: string;
|
|
1212
|
+
}
|
|
1213
|
+
interface CheckoutOptions {
|
|
1214
|
+
/** URL to redirect to on successful payment */
|
|
1215
|
+
successUrl: string;
|
|
1216
|
+
/** URL to redirect to if user cancels checkout */
|
|
1217
|
+
cancelUrl: string;
|
|
1218
|
+
/** Customer email (optional, auto-filled if user is logged in) */
|
|
1219
|
+
customerEmail?: string;
|
|
1220
|
+
/** Additional metadata to attach to the order */
|
|
1221
|
+
metadata?: Record<string, string>;
|
|
1222
|
+
}
|
|
1223
|
+
interface CheckoutResult {
|
|
1224
|
+
/** Stripe Checkout Session ID */
|
|
1225
|
+
sessionId: string;
|
|
1226
|
+
/** URL to redirect to for payment */
|
|
1227
|
+
url: string;
|
|
1228
|
+
/** Order ID in our database */
|
|
1229
|
+
orderId: string;
|
|
1230
|
+
/** Human-readable order number */
|
|
1231
|
+
orderNumber: string;
|
|
1232
|
+
}
|
|
1233
|
+
interface UseCheckoutResult {
|
|
1234
|
+
/** Initiate checkout and get the checkout URL */
|
|
1235
|
+
initiateCheckout: (options: CheckoutOptions) => Promise<CheckoutResult>;
|
|
1236
|
+
/** Whether checkout is being initiated */
|
|
1237
|
+
isLoading: boolean;
|
|
1238
|
+
/** Error message if checkout failed */
|
|
1239
|
+
error: string | null;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Hook for initiating Stripe Checkout.
|
|
1243
|
+
*
|
|
1244
|
+
* @example
|
|
1245
|
+
* ```tsx
|
|
1246
|
+
* const { initiateCheckout, isLoading, error } = useCheckout({ userToken: authToken })
|
|
1247
|
+
*
|
|
1248
|
+
* const handleCheckout = async () => {
|
|
1249
|
+
* try {
|
|
1250
|
+
* const result = await initiateCheckout({
|
|
1251
|
+
* successUrl: `${window.location.origin}/checkout/success`,
|
|
1252
|
+
* cancelUrl: `${window.location.origin}/cart`,
|
|
1253
|
+
* })
|
|
1254
|
+
*
|
|
1255
|
+
* // Redirect to Stripe Checkout
|
|
1256
|
+
* window.location.href = result.url
|
|
1257
|
+
* } catch (err) {
|
|
1258
|
+
* console.error('Checkout failed:', err)
|
|
1259
|
+
* }
|
|
1260
|
+
* }
|
|
1261
|
+
* ```
|
|
1262
|
+
*/
|
|
1263
|
+
declare function useCheckout(options?: UseCheckoutOptions): UseCheckoutResult;
|
|
1264
|
+
interface CheckoutSessionStatus {
|
|
1265
|
+
sessionId: string;
|
|
1266
|
+
status: 'open' | 'complete' | 'expired';
|
|
1267
|
+
paymentStatus: 'no_payment_required' | 'paid' | 'unpaid';
|
|
1268
|
+
customerEmail: string | null;
|
|
1269
|
+
amountTotal: number | null;
|
|
1270
|
+
currency: string | null;
|
|
1271
|
+
order: {
|
|
1272
|
+
id: string;
|
|
1273
|
+
orderNumber: string;
|
|
1274
|
+
status: string;
|
|
1275
|
+
} | null;
|
|
1276
|
+
}
|
|
1277
|
+
interface UseCheckoutStatusOptions extends UseCheckoutOptions {
|
|
1278
|
+
/** Stripe Checkout Session ID to check */
|
|
1279
|
+
sessionId: string;
|
|
1280
|
+
/** Whether to poll for status updates */
|
|
1281
|
+
poll?: boolean;
|
|
1282
|
+
/** Polling interval in milliseconds (default: 2000) */
|
|
1283
|
+
pollInterval?: number;
|
|
1284
|
+
}
|
|
1285
|
+
interface UseCheckoutStatusResult {
|
|
1286
|
+
/** Checkout session status */
|
|
1287
|
+
status: CheckoutSessionStatus | null;
|
|
1288
|
+
/** Whether status is loading */
|
|
1289
|
+
isLoading: boolean;
|
|
1290
|
+
/** Error message if any */
|
|
1291
|
+
error: string | null;
|
|
1292
|
+
/** Refresh status */
|
|
1293
|
+
refresh: () => Promise<void>;
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Hook for checking Stripe Checkout session status.
|
|
1297
|
+
*
|
|
1298
|
+
* @example
|
|
1299
|
+
* ```tsx
|
|
1300
|
+
* // On success page, check if payment completed
|
|
1301
|
+
* const searchParams = new URLSearchParams(window.location.search)
|
|
1302
|
+
* const sessionId = searchParams.get('session_id')
|
|
1303
|
+
*
|
|
1304
|
+
* const { status, isLoading } = useCheckoutStatus({
|
|
1305
|
+
* sessionId: sessionId!,
|
|
1306
|
+
* poll: true,
|
|
1307
|
+
* })
|
|
1308
|
+
*
|
|
1309
|
+
* if (status?.paymentStatus === 'paid') {
|
|
1310
|
+
* // Show success message
|
|
1311
|
+
* }
|
|
1312
|
+
* ```
|
|
1313
|
+
*/
|
|
1314
|
+
declare function useCheckoutStatus(options: UseCheckoutStatusOptions): UseCheckoutStatusResult;
|
|
1315
|
+
|
|
1316
|
+
/**
|
|
1317
|
+
* useProducts Hook
|
|
1318
|
+
*
|
|
1319
|
+
* Thin wrapper around useCollectionList for fetching products.
|
|
1320
|
+
* Provides product-specific filtering and types.
|
|
1321
|
+
*/
|
|
1322
|
+
|
|
1323
|
+
interface Product {
|
|
1324
|
+
/** Product name */
|
|
1325
|
+
name: string;
|
|
1326
|
+
/** URL-friendly slug */
|
|
1327
|
+
slug?: string;
|
|
1328
|
+
/** Price in cents */
|
|
1329
|
+
price: number;
|
|
1330
|
+
/** Product type */
|
|
1331
|
+
productType: 'one_time' | 'subscription';
|
|
1332
|
+
/** Product status */
|
|
1333
|
+
status: 'draft' | 'active' | 'archived';
|
|
1334
|
+
/** Product images */
|
|
1335
|
+
images?: string[];
|
|
1336
|
+
/** Short description */
|
|
1337
|
+
description?: string;
|
|
1338
|
+
/** Full description/details */
|
|
1339
|
+
details?: string;
|
|
1340
|
+
/** Product category */
|
|
1341
|
+
category?: string;
|
|
1342
|
+
/** Stripe integration */
|
|
1343
|
+
stripe?: {
|
|
1344
|
+
productId?: string;
|
|
1345
|
+
priceId?: string;
|
|
1346
|
+
};
|
|
1347
|
+
/** Subscription details (for subscription products) */
|
|
1348
|
+
subscription?: {
|
|
1349
|
+
interval: 'month' | 'year';
|
|
1350
|
+
intervalCount: number;
|
|
1351
|
+
trialDays?: number;
|
|
1352
|
+
};
|
|
1353
|
+
/** Inventory tracking */
|
|
1354
|
+
inventory?: {
|
|
1355
|
+
trackQuantity: boolean;
|
|
1356
|
+
quantity: number;
|
|
1357
|
+
};
|
|
1358
|
+
/** Any additional custom fields */
|
|
1359
|
+
[key: string]: unknown;
|
|
1360
|
+
}
|
|
1361
|
+
interface UseProductsOptions {
|
|
1362
|
+
/** Collection slug where products are stored (default: 'products') */
|
|
1363
|
+
collection?: string;
|
|
1364
|
+
/** Only fetch active products (default: true) */
|
|
1365
|
+
activeOnly?: boolean;
|
|
1366
|
+
/** Filter by category */
|
|
1367
|
+
category?: string;
|
|
1368
|
+
/** Filter by product type */
|
|
1369
|
+
productType?: 'one_time' | 'subscription';
|
|
1370
|
+
/** Additional filters */
|
|
1371
|
+
filters?: QueryFilter[];
|
|
1372
|
+
/** Page number */
|
|
1373
|
+
page?: number;
|
|
1374
|
+
/** Items per page */
|
|
1375
|
+
pageSize?: number;
|
|
1376
|
+
/** Field to sort by */
|
|
1377
|
+
orderBy?: string;
|
|
1378
|
+
/** Sort direction */
|
|
1379
|
+
orderDir?: 'asc' | 'desc';
|
|
1380
|
+
/** Whether to fetch */
|
|
1381
|
+
enabled?: boolean;
|
|
1382
|
+
}
|
|
1383
|
+
interface UseProductsResult extends Omit<UseCollectionListResult<Product>, 'data'> {
|
|
1384
|
+
/** The fetched products */
|
|
1385
|
+
products: CollectionRecord<Product>[] | null;
|
|
1386
|
+
}
|
|
1387
|
+
interface UseProductOptions {
|
|
1388
|
+
/** Collection slug where products are stored (default: 'products') */
|
|
1389
|
+
collection?: string;
|
|
1390
|
+
/** Product slug to fetch */
|
|
1391
|
+
slug: string;
|
|
1392
|
+
/** Whether to fetch */
|
|
1393
|
+
enabled?: boolean;
|
|
1394
|
+
}
|
|
1395
|
+
interface UseProductResult {
|
|
1396
|
+
/** The fetched product */
|
|
1397
|
+
product: CollectionRecord<Product> | null;
|
|
1398
|
+
/** Whether the fetch is in progress */
|
|
1399
|
+
isLoading: boolean;
|
|
1400
|
+
/** Error message if fetch failed */
|
|
1401
|
+
error: string | null;
|
|
1402
|
+
/** Whether the product was not found */
|
|
1403
|
+
notFound: boolean;
|
|
1404
|
+
/** Refetch the product */
|
|
1405
|
+
refetch: () => void;
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Hook for fetching a list of products from a collection.
|
|
1409
|
+
*
|
|
1410
|
+
* @example
|
|
1411
|
+
* ```tsx
|
|
1412
|
+
* // Fetch all active products
|
|
1413
|
+
* const { products, isLoading, goToPage } = useProducts({
|
|
1414
|
+
* pageSize: 12,
|
|
1415
|
+
* })
|
|
1416
|
+
*
|
|
1417
|
+
* // Fetch products by category
|
|
1418
|
+
* const { products } = useProducts({
|
|
1419
|
+
* category: 'merch',
|
|
1420
|
+
* activeOnly: true,
|
|
1421
|
+
* })
|
|
1422
|
+
*
|
|
1423
|
+
* // Fetch subscription products only
|
|
1424
|
+
* const { products } = useProducts({
|
|
1425
|
+
* productType: 'subscription',
|
|
1426
|
+
* })
|
|
1427
|
+
* ```
|
|
1428
|
+
*/
|
|
1429
|
+
declare function useProducts(options?: UseProductsOptions): UseProductsResult;
|
|
1430
|
+
/**
|
|
1431
|
+
* Hook for fetching a single product by slug.
|
|
1432
|
+
*
|
|
1433
|
+
* @example
|
|
1434
|
+
* ```tsx
|
|
1435
|
+
* const { slug } = useParams()
|
|
1436
|
+
* const { product, isLoading, notFound } = useProduct({ slug: slug! })
|
|
1437
|
+
*
|
|
1438
|
+
* if (notFound) {
|
|
1439
|
+
* return <NotFoundPage />
|
|
1440
|
+
* }
|
|
1441
|
+
*
|
|
1442
|
+
* if (product) {
|
|
1443
|
+
* return <ProductDetails product={product.data} />
|
|
1444
|
+
* }
|
|
1445
|
+
* ```
|
|
1446
|
+
*/
|
|
1447
|
+
declare function useProduct(options: UseProductOptions): UseProductResult;
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* Subscription Types
|
|
1451
|
+
*
|
|
1452
|
+
* Type definitions for subscription management in yoamigo-core.
|
|
1453
|
+
*/
|
|
1454
|
+
type SubscriptionStatusType = 'active' | 'trialing' | 'past_due' | 'canceled' | 'incomplete' | 'incomplete_expired' | 'unpaid' | 'paused';
|
|
1455
|
+
interface SubscriptionProduct {
|
|
1456
|
+
id: string;
|
|
1457
|
+
name: string;
|
|
1458
|
+
description?: string;
|
|
1459
|
+
}
|
|
1460
|
+
interface SubscriptionPrice {
|
|
1461
|
+
id: string;
|
|
1462
|
+
unitAmount: number;
|
|
1463
|
+
currency: string;
|
|
1464
|
+
interval: 'day' | 'week' | 'month' | 'year';
|
|
1465
|
+
intervalCount: number;
|
|
1466
|
+
}
|
|
1467
|
+
interface SubscriptionDetails {
|
|
1468
|
+
id: string;
|
|
1469
|
+
status: SubscriptionStatusType;
|
|
1470
|
+
currentPeriodStart: string;
|
|
1471
|
+
currentPeriodEnd: string;
|
|
1472
|
+
cancelAtPeriodEnd: boolean;
|
|
1473
|
+
canceledAt?: string;
|
|
1474
|
+
endedAt?: string;
|
|
1475
|
+
trialStart?: string;
|
|
1476
|
+
trialEnd?: string;
|
|
1477
|
+
product: SubscriptionProduct;
|
|
1478
|
+
price: SubscriptionPrice;
|
|
1479
|
+
}
|
|
1480
|
+
interface SubscribeParams {
|
|
1481
|
+
/** Tier slug to subscribe to (e.g., "pro", "business") */
|
|
1482
|
+
tierSlug: string;
|
|
1483
|
+
/** URL to redirect to on successful subscription */
|
|
1484
|
+
successUrl: string;
|
|
1485
|
+
/** URL to redirect to if user cancels */
|
|
1486
|
+
cancelUrl: string;
|
|
1487
|
+
/** Quantity of the subscription (default: 1) */
|
|
1488
|
+
quantity?: number;
|
|
1489
|
+
/** Number of trial days (overrides price default if set) */
|
|
1490
|
+
trialDays?: number;
|
|
1491
|
+
/** Customer email (auto-filled if authenticated) */
|
|
1492
|
+
customerEmail?: string;
|
|
1493
|
+
/** Additional metadata */
|
|
1494
|
+
metadata?: Record<string, string>;
|
|
1495
|
+
}
|
|
1496
|
+
interface SubscribeResult {
|
|
1497
|
+
success: boolean;
|
|
1498
|
+
checkoutUrl?: string;
|
|
1499
|
+
sessionId?: string;
|
|
1500
|
+
error?: string;
|
|
1501
|
+
}
|
|
1502
|
+
interface CustomerPortalParams {
|
|
1503
|
+
/** URL to return to after portal session */
|
|
1504
|
+
returnUrl: string;
|
|
1505
|
+
}
|
|
1506
|
+
interface CustomerPortalResult {
|
|
1507
|
+
success: boolean;
|
|
1508
|
+
portalUrl?: string;
|
|
1509
|
+
error?: string;
|
|
1510
|
+
}
|
|
1511
|
+
interface SubscriptionStatusResult {
|
|
1512
|
+
success: boolean;
|
|
1513
|
+
status: SubscriptionStatusType | 'none';
|
|
1514
|
+
subscriptions?: SubscriptionDetails[];
|
|
1515
|
+
error?: string;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* useSubscription Hook
|
|
1520
|
+
*
|
|
1521
|
+
* React hook for creating Stripe subscription checkout sessions.
|
|
1522
|
+
* Used by template websites to allow customers to subscribe to recurring products.
|
|
1523
|
+
*
|
|
1524
|
+
* Uses tier-based subscriptions - users select tiers (e.g., "pro", "business"),
|
|
1525
|
+
* not arbitrary price IDs. The API resolves tiers to the correct Stripe price
|
|
1526
|
+
* based on the environment (staging vs production).
|
|
1527
|
+
*/
|
|
1528
|
+
|
|
1529
|
+
interface UseSubscriptionOptions {
|
|
1530
|
+
/** App ID to scope the subscription */
|
|
1531
|
+
appId?: string;
|
|
1532
|
+
/** App user token for authenticated users */
|
|
1533
|
+
userToken?: string | null;
|
|
1534
|
+
/** API base URL */
|
|
1535
|
+
apiUrl?: string;
|
|
1536
|
+
}
|
|
1537
|
+
interface UseSubscriptionResult {
|
|
1538
|
+
/** Create a subscription checkout session and get the checkout URL */
|
|
1539
|
+
subscribe: (params: SubscribeParams) => Promise<SubscribeResult>;
|
|
1540
|
+
/** Whether subscription checkout is being created */
|
|
1541
|
+
isLoading: boolean;
|
|
1542
|
+
/** Error message if subscription creation failed */
|
|
1543
|
+
error: string | null;
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Hook for creating subscription checkout sessions using tier-based pricing.
|
|
1547
|
+
*
|
|
1548
|
+
* @example
|
|
1549
|
+
* ```tsx
|
|
1550
|
+
* const { subscribe, isLoading, error } = useSubscription({ userToken: authToken })
|
|
1551
|
+
*
|
|
1552
|
+
* const handleSubscribe = async (tierSlug: string) => {
|
|
1553
|
+
* const result = await subscribe({
|
|
1554
|
+
* tierSlug, // e.g., "pro", "business"
|
|
1555
|
+
* // URLs are site-specific - use whatever routes your template defines
|
|
1556
|
+
* successUrl: `${window.location.origin}/your-success-page`,
|
|
1557
|
+
* cancelUrl: `${window.location.origin}/your-pricing-page`,
|
|
1558
|
+
* })
|
|
1559
|
+
*
|
|
1560
|
+
* if (result.success && result.checkoutUrl) {
|
|
1561
|
+
* window.location.href = result.checkoutUrl
|
|
1562
|
+
* }
|
|
1563
|
+
* }
|
|
1564
|
+
* ```
|
|
1565
|
+
*/
|
|
1566
|
+
declare function useSubscription(options?: UseSubscriptionOptions): UseSubscriptionResult;
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* useSubscriptionTiers Hook
|
|
1570
|
+
*
|
|
1571
|
+
* React hook for fetching available subscription tiers for an app.
|
|
1572
|
+
* Tiers are returned pre-sorted by displayOrder for easy UI rendering.
|
|
1573
|
+
*/
|
|
1574
|
+
interface SubscriptionTier {
|
|
1575
|
+
/** Unique identifier for the tier (e.g., "pro", "business") */
|
|
1576
|
+
slug: string;
|
|
1577
|
+
/** Display name (e.g., "Pro Plan") */
|
|
1578
|
+
name: string;
|
|
1579
|
+
/** Optional description */
|
|
1580
|
+
description: string | null;
|
|
1581
|
+
/** Price in cents (e.g., 1999 = $19.99) */
|
|
1582
|
+
priceCents: number;
|
|
1583
|
+
/** Billing interval: "month" or "year" */
|
|
1584
|
+
billingInterval: 'month' | 'year';
|
|
1585
|
+
/** Interval count (e.g., 1 for monthly, 12 for yearly) */
|
|
1586
|
+
billingIntervalCount: number;
|
|
1587
|
+
/** Currency code (e.g., "usd") */
|
|
1588
|
+
currency: string;
|
|
1589
|
+
/** List of feature descriptions */
|
|
1590
|
+
features: string[];
|
|
1591
|
+
/** Whether to show "Most Popular" or similar badge */
|
|
1592
|
+
highlighted: boolean;
|
|
1593
|
+
/** Sort order for UI display */
|
|
1594
|
+
displayOrder: number;
|
|
1595
|
+
}
|
|
1596
|
+
interface UseSubscriptionTiersOptions {
|
|
1597
|
+
/** App ID to fetch tiers for */
|
|
1598
|
+
appId?: string;
|
|
1599
|
+
/** API base URL */
|
|
1600
|
+
apiUrl?: string;
|
|
1601
|
+
/** Whether to fetch tiers immediately on mount (default: true) */
|
|
1602
|
+
fetchOnMount?: boolean;
|
|
1603
|
+
}
|
|
1604
|
+
interface UseSubscriptionTiersResult {
|
|
1605
|
+
/** Available subscription tiers (sorted by displayOrder) */
|
|
1606
|
+
tiers: SubscriptionTier[];
|
|
1607
|
+
/** Whether tiers are being loaded */
|
|
1608
|
+
isLoading: boolean;
|
|
1609
|
+
/** Error message if loading failed */
|
|
1610
|
+
error: string | null;
|
|
1611
|
+
/** Manually refresh the tiers */
|
|
1612
|
+
refresh: () => Promise<void>;
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Hook for fetching available subscription tiers.
|
|
1616
|
+
*
|
|
1617
|
+
* @example
|
|
1618
|
+
* ```tsx
|
|
1619
|
+
* const { tiers, isLoading, error } = useSubscriptionTiers()
|
|
1620
|
+
*
|
|
1621
|
+
* if (isLoading) return <Loading />
|
|
1622
|
+
* if (error) return <Error message={error} />
|
|
1623
|
+
*
|
|
1624
|
+
* return (
|
|
1625
|
+
* <div className="pricing-grid">
|
|
1626
|
+
* {tiers.map(tier => (
|
|
1627
|
+
* <PricingCard
|
|
1628
|
+
* key={tier.slug}
|
|
1629
|
+
* tier={tier}
|
|
1630
|
+
* onSelect={() => handleSubscribe(tier.slug)}
|
|
1631
|
+
* />
|
|
1632
|
+
* ))}
|
|
1633
|
+
* </div>
|
|
1634
|
+
* )
|
|
1635
|
+
* ```
|
|
1636
|
+
*/
|
|
1637
|
+
declare function useSubscriptionTiers(options?: UseSubscriptionTiersOptions): UseSubscriptionTiersResult;
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* useCustomerPortal Hook
|
|
1641
|
+
*
|
|
1642
|
+
* React hook for creating Stripe Customer Portal sessions.
|
|
1643
|
+
* Allows customers to manage their subscriptions (update payment method,
|
|
1644
|
+
* cancel subscription, view billing history, etc.).
|
|
1645
|
+
*/
|
|
1646
|
+
|
|
1647
|
+
interface UseCustomerPortalOptions {
|
|
1648
|
+
/** App ID to scope the portal */
|
|
1649
|
+
appId?: string;
|
|
1650
|
+
/** App user token for authenticated users (required for portal) */
|
|
1651
|
+
userToken: string;
|
|
1652
|
+
/** API base URL */
|
|
1653
|
+
apiUrl?: string;
|
|
1654
|
+
}
|
|
1655
|
+
interface UseCustomerPortalResult {
|
|
1656
|
+
/** Open the customer portal and get the portal URL */
|
|
1657
|
+
openPortal: (params: CustomerPortalParams) => Promise<CustomerPortalResult>;
|
|
1658
|
+
/** Whether portal session is being created */
|
|
1659
|
+
isLoading: boolean;
|
|
1660
|
+
/** Error message if portal creation failed */
|
|
1661
|
+
error: string | null;
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Hook for creating Stripe Customer Portal sessions.
|
|
1665
|
+
*
|
|
1666
|
+
* @example
|
|
1667
|
+
* ```tsx
|
|
1668
|
+
* const { openPortal, isLoading, error } = useCustomerPortal({ userToken: authToken })
|
|
1669
|
+
*
|
|
1670
|
+
* const handleManageSubscription = async () => {
|
|
1671
|
+
* const result = await openPortal({
|
|
1672
|
+
* returnUrl: `${window.location.origin}/account`,
|
|
1673
|
+
* })
|
|
1674
|
+
*
|
|
1675
|
+
* if (result.success && result.portalUrl) {
|
|
1676
|
+
* window.location.href = result.portalUrl
|
|
1677
|
+
* }
|
|
1678
|
+
* }
|
|
1679
|
+
* ```
|
|
1680
|
+
*/
|
|
1681
|
+
declare function useCustomerPortal(options: UseCustomerPortalOptions): UseCustomerPortalResult;
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* useSubscriptionStatus Hook
|
|
1685
|
+
*
|
|
1686
|
+
* React hook for fetching and polling the current user's subscription status.
|
|
1687
|
+
* Provides information about active subscriptions, trial periods, and billing dates.
|
|
1688
|
+
*/
|
|
1689
|
+
|
|
1690
|
+
interface UseSubscriptionStatusOptions {
|
|
1691
|
+
/** App ID to scope the subscription status */
|
|
1692
|
+
appId?: string;
|
|
1693
|
+
/** App user token for authenticated users (required) */
|
|
1694
|
+
userToken: string;
|
|
1695
|
+
/** API base URL */
|
|
1696
|
+
apiUrl?: string;
|
|
1697
|
+
/** Whether to automatically fetch on mount (default: true) */
|
|
1698
|
+
autoFetch?: boolean;
|
|
1699
|
+
/** Poll interval in milliseconds (0 to disable polling, default: 0) */
|
|
1700
|
+
pollInterval?: number;
|
|
1701
|
+
}
|
|
1702
|
+
interface UseSubscriptionStatusResult {
|
|
1703
|
+
/** Aggregated subscription status ('active' if any subscription is active) */
|
|
1704
|
+
status: SubscriptionStatusType | 'none' | null;
|
|
1705
|
+
/** All subscriptions for the user */
|
|
1706
|
+
subscriptions: SubscriptionDetails[];
|
|
1707
|
+
/** Whether status is loading */
|
|
1708
|
+
isLoading: boolean;
|
|
1709
|
+
/** Error message if any */
|
|
1710
|
+
error: string | null;
|
|
1711
|
+
/** Manually refresh the subscription status */
|
|
1712
|
+
refresh: () => Promise<void>;
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Hook for checking the current user's subscription status.
|
|
1716
|
+
*
|
|
1717
|
+
* @example
|
|
1718
|
+
* ```tsx
|
|
1719
|
+
* const { status, subscriptions, isLoading, refresh } = useSubscriptionStatus({
|
|
1720
|
+
* userToken: authToken,
|
|
1721
|
+
* })
|
|
1722
|
+
*
|
|
1723
|
+
* if (status === 'active') {
|
|
1724
|
+
* // Show premium content
|
|
1725
|
+
* } else if (status === 'trialing') {
|
|
1726
|
+
* // Show trial banner with days remaining
|
|
1727
|
+
* } else {
|
|
1728
|
+
* // Show upgrade prompt
|
|
1729
|
+
* }
|
|
1730
|
+
* ```
|
|
1731
|
+
*/
|
|
1732
|
+
declare function useSubscriptionStatus(options: UseSubscriptionStatusOptions): UseSubscriptionStatusResult;
|
|
1733
|
+
|
|
1734
|
+
export { type AIEditContextValue, AIEditProvider, type AnimatedTextOptions, type AnimatedTextResult, type AnimationConfig, type AnimationMetadata, type AnimationOptions, type AnimationPhase, type AnimationResult, type AnimationState, type AnimationStrategy, type AnyField, CartItem, CartProvider, type CartProviderProps, type CheckoutOptions, type CheckoutResult, type CheckoutSessionStatus, type CollectionContentContextValue, CollectionContentProvider, type CollectionContentProviderProps, type CollectionData, CollectionDetailPage, type CollectionDetailPageProps, type CollectionDetailRenderProps, CollectionItem, type CollectionItemProps, type CollectionItemRenderProps, CollectionList, type CollectionListData, CollectionListPage, type CollectionListPageProps, type CollectionListPaginationProps, type CollectionListProps, type CollectionListRenderProps, CollectionRecord, type ContainerField, type ContentHandle, type CustomerPortalParams, type CustomerPortalResult, type EmbedField, type ImageField, type ImageValue, type LinkField, type LinkValue, type Product, QueryFilter, SafeHtml, type SafeHtmlProps, SafeTriangleBelow, type SubscribeParams, type SubscribeResult, type SubscriptionDetails, type SubscriptionPrice, type SubscriptionProduct, type SubscriptionStatusResult, type SubscriptionStatusType, type SubscriptionTier, type TextAnimationMetadata, type TextDiff, type TextField, Tooltip, type TooltipProps, type UseCartOptions, type UseCartResult, type UseCheckoutOptions, type UseCheckoutResult, type UseCheckoutStatusOptions, type UseCheckoutStatusResult, type UseCollectionListOptions, type UseCollectionListResult, type UseCollectionRecordOptions, type UseCollectionRecordResult, type UseCustomerPortalOptions, type UseCustomerPortalResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseSubscriptionOptions, type UseSubscriptionResult, type UseSubscriptionStatusOptions, type UseSubscriptionStatusResult, type UseSubscriptionTiersOptions, type UseSubscriptionTiersResult, type VideoField, YaText, type YaTextProps, buildIntermediateText, calculateAnimationTiming, computeTextDiff, containsHtml, getTextCursorPosition, imageCrossfadeStrategy, linkTransitionStrategy, parseFieldValue, stringifyFieldValue, stripHtml, textTypingStrategy, useAIEditAnimation, useAIEditContext, useAIEditContextOptional, useAnimatedText, useCart, useCartContext, useCheckout, useCheckoutStatus, useCollectionContent, useCollectionList, useCollectionRecord, useContent, useCustomerPortal, useOptionalCartContext, useProduct, useProducts, useSafeTriangle, useSubscription, useSubscriptionStatus, useSubscriptionTiers };
|