picked 0.0.101-next.2 → 0.0.101
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/README.md +27 -12
- package/docs/explanation/conditions-and-the-logic-engine.md +94 -0
- package/docs/explanation/content-references.md +99 -0
- package/docs/explanation/feature-flag-allocation.md +218 -0
- package/docs/explanation/how-picked-works.md +124 -0
- package/docs/explanation/how-pickedql-works.md +76 -0
- package/docs/explanation/pages-and-flows.md +64 -0
- package/docs/explanation/the-content-model.md +87 -0
- package/docs/explanation/the-two-apis.md +68 -0
- package/docs/explanation/why-server-side-rendering.md +76 -0
- package/docs/explanation/workspaces-and-versions.md +71 -0
- package/docs/how-to/add-a-step-field-or-action.md +56 -0
- package/docs/how-to/analytics/index.md +324 -0
- package/docs/how-to/archive-or-delete-a-workspace.md +59 -0
- package/docs/how-to/authenticate-the-cli.md +101 -0
- package/docs/how-to/authenticate-with-a-magic-link.md +56 -0
- package/docs/how-to/authenticate-with-a-password.md +68 -0
- package/docs/how-to/authenticate-with-an-api-key.md +60 -0
- package/docs/how-to/authenticate-with-oauth.md +49 -0
- package/docs/how-to/build-a-flow.md +106 -0
- package/docs/how-to/call-an-external-api-from-a-workflow.md +116 -0
- package/docs/how-to/connect-the-mcp-server.md +46 -0
- package/docs/how-to/create-a-content-entry.md +58 -0
- package/docs/how-to/create-a-project.md +40 -0
- package/docs/how-to/create-an-experience.md +51 -0
- package/docs/how-to/create-and-revoke-an-api-key.md +54 -0
- package/docs/how-to/debug-with-the-chrome-extension.md +57 -0
- package/docs/how-to/define-a-page-layout.md +54 -0
- package/docs/how-to/fill-and-publish-a-page.md +82 -0
- package/docs/how-to/install-the-gtm-template.md +84 -0
- package/docs/how-to/install-the-sdk.md +93 -0
- package/docs/how-to/install-the-vite-plugin.md +69 -0
- package/docs/how-to/invite-and-remove-members.md +45 -0
- package/docs/how-to/point-one-entry-at-another.md +75 -0
- package/docs/how-to/preview-a-workspace.md +39 -0
- package/docs/how-to/publish-a-workspace.md +42 -0
- package/docs/how-to/read-a-feature-flag.md +113 -0
- package/docs/how-to/read-a-referenced-entry.md +109 -0
- package/docs/how-to/register-a-component.md +55 -0
- package/docs/how-to/register-an-action-handler.md +85 -0
- package/docs/how-to/register-an-api-that-uses-oauth2.md +107 -0
- package/docs/how-to/render-a-step-form.md +131 -0
- package/docs/how-to/render-an-unpublished-preview.md +41 -0
- package/docs/how-to/retry-a-failed-call-with-a-loop.md +81 -0
- package/docs/how-to/reuse-a-section.md +64 -0
- package/docs/how-to/reuse-a-step-field-or-action.md +39 -0
- package/docs/how-to/roll-back-a-published-version.md +39 -0
- package/docs/how-to/run-an-ab-test.md +59 -0
- package/docs/how-to/run-code-on-a-form-submission.md +125 -0
- package/docs/how-to/server-render-a-page.md +81 -0
- package/docs/how-to/set-a-content-security-policy.md +32 -0
- package/docs/how-to/set-up-mfa.md +126 -0
- package/docs/how-to/switch-workspaces.md +122 -0
- package/docs/how-to/upload-and-use-an-asset.mdx +87 -0
- package/docs/how-to/validate-a-field.md +52 -0
- package/docs/how-to/wait-for-an-action-response.md +182 -0
- package/docs/how-to/write-a-condition.md +57 -0
- package/docs/how-to/write-a-predicate.md +60 -0
- package/docs/index.md +59 -0
- package/docs/reference/dashboard/actions.mdx +65 -0
- package/docs/reference/dashboard/api-keys.mdx +47 -0
- package/docs/reference/dashboard/conditions.mdx +51 -0
- package/docs/reference/dashboard/content/asset-library.mdx +74 -0
- package/docs/reference/dashboard/content/blocks.mdx +66 -0
- package/docs/reference/dashboard/content/content-entries.mdx +109 -0
- package/docs/reference/dashboard/content/models.mdx +119 -0
- package/docs/reference/dashboard/content/pages.mdx +72 -0
- package/docs/reference/dashboard/experiences.mdx +61 -0
- package/docs/reference/dashboard/external-apis.md +182 -0
- package/docs/reference/dashboard/features.mdx +165 -0
- package/docs/reference/dashboard/fields.mdx +120 -0
- package/docs/reference/dashboard/flow-builder/canvas.mdx +58 -0
- package/docs/reference/dashboard/flow-builder/condition-builder.md +69 -0
- package/docs/reference/dashboard/flow-builder/keyboard-shortcuts.md +72 -0
- package/docs/reference/dashboard/flow-builder/nodes.md +74 -0
- package/docs/reference/dashboard/labels.md +321 -0
- package/docs/reference/dashboard/lead-search.md +235 -0
- package/docs/reference/dashboard/projects.mdx +53 -0
- package/docs/reference/dashboard/reports.mdx +53 -0
- package/docs/reference/dashboard/steps.mdx +47 -0
- package/docs/reference/dashboard/team-management.md +38 -0
- package/docs/reference/dashboard/workflow-runs.md +157 -0
- package/docs/reference/dashboard/workflow-templates.md +182 -0
- package/docs/reference/dashboard/workflow-test-runs.md +85 -0
- package/docs/reference/dashboard/workspace-versioning.mdx +42 -0
- package/docs/reference/dashboard/workspaces.mdx +91 -0
- package/docs/reference/delivery-api/experience-content.md +152 -0
- package/docs/reference/delivery-api/index.md +45 -0
- package/docs/reference/delivery-api/preview-caching-crawling.md +79 -0
- package/docs/reference/delivery-api/project-config.md +65 -0
- package/docs/reference/delivery-api/workspace-schema.md +107 -0
- package/docs/reference/glossary.md +201 -0
- package/docs/reference/management-api/authentication.md +87 -0
- package/docs/reference/management-api/authorization-and-scopes.md +291 -0
- package/docs/reference/management-api/error-handling.md +232 -0
- package/docs/reference/management-api/pagination.md +192 -0
- package/docs/reference/management-api/schema/action.md +261 -0
- package/docs/reference/management-api/schema/analytics-session.md +363 -0
- package/docs/reference/management-api/schema/analytics.md +73 -0
- package/docs/reference/management-api/schema/api-key.md +170 -0
- package/docs/reference/management-api/schema/auth.md +1177 -0
- package/docs/reference/management-api/schema/cms-fields.md +954 -0
- package/docs/reference/management-api/schema/cms-models.md +1155 -0
- package/docs/reference/management-api/schema/common.md +362 -0
- package/docs/reference/management-api/schema/condition.md +254 -0
- package/docs/reference/management-api/schema/design-system.md +199 -0
- package/docs/reference/management-api/schema/experience.md +1098 -0
- package/docs/reference/management-api/schema/external-api.md +637 -0
- package/docs/reference/management-api/schema/feature.md +350 -0
- package/docs/reference/management-api/schema/feedback.md +103 -0
- package/docs/reference/management-api/schema/field.md +400 -0
- package/docs/reference/management-api/schema/index.md +243 -0
- package/docs/reference/management-api/schema/integration.md +272 -0
- package/docs/reference/management-api/schema/label.md +349 -0
- package/docs/reference/management-api/schema/lead.md +340 -0
- package/docs/reference/management-api/schema/mcp-server.md +299 -0
- package/docs/reference/management-api/schema/metric.md +223 -0
- package/docs/reference/management-api/schema/operation.md +96 -0
- package/docs/reference/management-api/schema/organization.md +749 -0
- package/docs/reference/management-api/schema/project.md +607 -0
- package/docs/reference/management-api/schema/report.md +243 -0
- package/docs/reference/management-api/schema/saved-view.md +288 -0
- package/docs/reference/management-api/schema/shopify.md +795 -0
- package/docs/reference/management-api/schema/step.md +273 -0
- package/docs/reference/management-api/schema/tenant-branding.md +185 -0
- package/docs/reference/management-api/schema/workbench.md +246 -0
- package/docs/reference/management-api/schema/workflow-run.md +263 -0
- package/docs/reference/management-api/schema/workflow.md +1110 -0
- package/docs/reference/management-api/schema/workspace-version.md +823 -0
- package/docs/reference/management-api/schema/workspace.md +917 -0
- package/docs/reference/pickedql/data-sources.md +174 -0
- package/docs/reference/pickedql/dimensions.md +103 -0
- package/docs/reference/pickedql/filtering.md +64 -0
- package/docs/reference/pickedql/index.md +18 -0
- package/docs/reference/pickedql/schema.md +172 -0
- package/docs/reference/pickedql/virtual-functions.md +248 -0
- package/docs/reference/sdk/actions.md +154 -0
- package/docs/reference/sdk/analytics-events.md +453 -0
- package/docs/reference/sdk/conditions.md +147 -0
- package/docs/reference/sdk/content-security-policy.md +21 -0
- package/docs/reference/sdk/content.md +253 -0
- package/docs/reference/sdk/experiences.md +51 -0
- package/docs/reference/sdk/features.md +256 -0
- package/docs/reference/sdk/fields.md +255 -0
- package/docs/reference/sdk/index.md +68 -0
- package/docs/reference/sdk/plugins/create-experience.md +123 -0
- package/docs/reference/sdk/plugins/dynamic-experiences.md +74 -0
- package/docs/reference/sdk/plugins/fingerprintjs.md +39 -0
- package/docs/reference/sdk/plugins/hotjar.md +60 -0
- package/docs/reference/sdk/plugins/index.md +33 -0
- package/docs/reference/sdk/plugins/sentry.md +51 -0
- package/docs/reference/sdk/plugins/shopify.md +109 -0
- package/docs/reference/sdk/plugins/window-object.md +25 -0
- package/docs/reference/sdk/predicates.md +43 -0
- package/docs/reference/sdk/react.md +61 -0
- package/docs/reference/sdk/ssr/index.md +33 -0
- package/docs/reference/sdk/ssr/rendering-and-hydration.md +103 -0
- package/docs/reference/sdk/ssr/serialize.md +80 -0
- package/docs/reference/sdk/ssr/slots-and-placements.md +113 -0
- package/docs/reference/sdk/steps.md +86 -0
- package/docs/reference/sdk/svelte.md +74 -0
- package/docs/reference/sdk/type-generation.md +265 -0
- package/docs/reference/tools/admin-api-client.md +89 -0
- package/docs/reference/tools/chrome-extension.md +16 -0
- package/docs/reference/tools/cli.md +319 -0
- package/docs/reference/tools/delivery-api-client.md +60 -0
- package/docs/reference/tools/image-optimisation-service.md +104 -0
- package/docs/reference/tools/mcp-run.md +121 -0
- package/docs/reference/tools/mcp.md +391 -0
- package/docs/reference/tools/vite-plugin.md +163 -0
- package/docs/tutorials/first-api-call.md +253 -0
- package/docs/tutorials/first-experience-in-the-dashboard.mdx +160 -0
- package/docs/tutorials/first-experience.mdx +254 -0
- package/docs/tutorials/first-server-rendered-page.mdx +186 -0
- package/package.json +8 -7
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Shopify
|
|
7
|
+
|
|
8
|
+
`@picked/shopify` connects the Picked client to a Shopify storefront. It adds Shopify
|
|
9
|
+
predicates, writes Picked data to the cart, and gives helpers for the Shopify storefront
|
|
10
|
+
APIs.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { createClient } from 'picked'
|
|
14
|
+
import { shopifyPlugin } from '@picked/shopify/plugins/shopify'
|
|
15
|
+
|
|
16
|
+
const client = createClient({
|
|
17
|
+
projectId: 'your-project-id',
|
|
18
|
+
plugins: [shopifyPlugin()],
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The plugin has no options. The helpers read `window.Shopify` and `window.pickedConfig`.
|
|
23
|
+
The Picked Shopify app puts `window.pickedConfig` on the storefront.
|
|
24
|
+
|
|
25
|
+
## Predicates
|
|
26
|
+
|
|
27
|
+
The plugin registers these predicates. Each name starts with `shopify-`.
|
|
28
|
+
|
|
29
|
+
| Predicate | Returns |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `shopify-is-homepage` | `true` on `/`. |
|
|
32
|
+
| `shopify-is-product-page` | `true` when `pickedConfig.product` has a handle. |
|
|
33
|
+
| `shopify-get-product-handle` | The handle of `pickedConfig.product`, or `false`. |
|
|
34
|
+
| `shopify-is-collection-page` | `true` when `pickedConfig.collection` has a handle. |
|
|
35
|
+
| `shopify-get-collection-handle` | The handle of `pickedConfig.collection`, or `false`. |
|
|
36
|
+
| `shopify-is-page` | `true` on `/pages/<handle>`. |
|
|
37
|
+
| `shopify-get-page-handle` | The page handle from the path. |
|
|
38
|
+
| `shopify-is-blog-page` | `true` on `/blogs/<handle>`. |
|
|
39
|
+
| `shopify-get-blog-handle` | The blog handle from the path. |
|
|
40
|
+
| `shopify-is-article-page` | `true` on `/blogs/<blog>/<article>`. |
|
|
41
|
+
| `shopify-get-article-handle` | The article handle from the path. |
|
|
42
|
+
| `shopify-is-app-proxy-page` | `true` on `/a/`, `/apps/`, `/community/` and `/tools/` paths. |
|
|
43
|
+
| `shopify-get-app-proxy-handle` | The first path segment after the app proxy prefix. |
|
|
44
|
+
| `shopify-is-account-page` | `true` on `/account/<path>`. |
|
|
45
|
+
| `shopify-is-login-page` | `true` on `/account/login`. |
|
|
46
|
+
| `shopify-is-register-page` | `true` on `/account/register`. |
|
|
47
|
+
| `shopify-is-cart-page` | `true` on `/cart/<path>`. |
|
|
48
|
+
| `shopify-is-checkout-page` | `true` on `/checkout/<path>`. |
|
|
49
|
+
|
|
50
|
+
See [Predicates](/reference/sdk/predicates) for how a condition reads a predicate.
|
|
51
|
+
|
|
52
|
+
## Cart attributes
|
|
53
|
+
|
|
54
|
+
In a browser with `window.Shopify.analytics`, the plugin writes these cart attributes
|
|
55
|
+
through the AJAX API of the Shopify cart:
|
|
56
|
+
|
|
57
|
+
| Attribute | Value |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `picked.id` | The session ID. |
|
|
60
|
+
| `picked.is_user` | `'true'` or `'false'`. |
|
|
61
|
+
| `picked.features.<feature ID>` | The bucket of each resolved feature. |
|
|
62
|
+
|
|
63
|
+
The plugin writes the attributes again on the `initialize`, `session`, `user_decide`,
|
|
64
|
+
`feature_decide` and `experience_initialize` events. It then reads the cart back after 2
|
|
65
|
+
seconds. If a different app changed an attribute, the plugin writes it again, up to 3
|
|
66
|
+
times.
|
|
67
|
+
|
|
68
|
+
On the `session` event, the plugin also publishes `picked:session` with `{ id }` through
|
|
69
|
+
`Shopify.analytics.publish`. A Shopify web pixel can subscribe to this event.
|
|
70
|
+
|
|
71
|
+
## Storefront data
|
|
72
|
+
|
|
73
|
+
`@picked/shopify/plugins/shopify` also gives readers for `window.pickedConfig`. Each
|
|
74
|
+
returns `undefined` outside a browser.
|
|
75
|
+
|
|
76
|
+
| Function | Returns |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `getPickedConfig()` | The full `window.pickedConfig` object. |
|
|
79
|
+
| `getPickedConfigCustomer()` | The customer that is logged in. |
|
|
80
|
+
| `getPickedConfigProduct()` | The product of the current page. |
|
|
81
|
+
| `getPickedConfigCollection()` | The collection of the current page. |
|
|
82
|
+
| `getPickedConfigExperiments()` | The experiments of the Picked Shopify app. |
|
|
83
|
+
| `getPickedConfigFeatureExperiments()` | Each enabled experiment as a value for the `features` option of `createClient()`. |
|
|
84
|
+
| `getPickedConfigStorefrontApiToken()` | The public Storefront API token. |
|
|
85
|
+
| `getShopify()` | The `window.Shopify` object. |
|
|
86
|
+
|
|
87
|
+
## Storefront APIs
|
|
88
|
+
|
|
89
|
+
| Import | Functions |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `@picked/shopify/ajax-api` | `getProduct`, `getProductRecommendations`, `getPredictiveSearch`, `getCart`, `addToCart`, `updateCart`, `changeCart`, `clearCart`, `getShippingRates` |
|
|
92
|
+
| `@picked/shopify/json-api` | `getCollection`, `getCollectionProducts` |
|
|
93
|
+
| `@picked/shopify/storefront-api` | `createStorefrontApiClient`, `getStorefrontApiClient`, `cartDiscountCodesUpdate` |
|
|
94
|
+
| `@picked/shopify/picked-app` | `getProductMetadata`, `getCollectionMetadata` |
|
|
95
|
+
| `@picked/shopify/utils` | `getRequest`, `postRequest` |
|
|
96
|
+
|
|
97
|
+
Each request goes to the storefront origin, below `Shopify.routes.root`. A response that
|
|
98
|
+
is not `ok` throws an `Error`. The message of the error is the status text.
|
|
99
|
+
|
|
100
|
+
`createStorefrontApiClient()` needs the Storefront API token in `window.pickedConfig`. It
|
|
101
|
+
throws an `Error` when the token is not there.
|
|
102
|
+
|
|
103
|
+
The helpers of `@picked/shopify/picked-app` read metafields through the
|
|
104
|
+
`/apps/picked` app proxy of the Picked Shopify app.
|
|
105
|
+
|
|
106
|
+
## Related
|
|
107
|
+
|
|
108
|
+
- [Plugins](/reference/sdk/plugins)
|
|
109
|
+
- [Predicates](/reference/sdk/predicates)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Window Object
|
|
7
|
+
|
|
8
|
+
A plugin to add the Picked client to the window object
|
|
9
|
+
|
|
10
|
+
## Setup Plugin
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { createClient } from 'picked'
|
|
14
|
+
import { windowObjectPlugin } from '@picked/client/plugins/window-object'
|
|
15
|
+
|
|
16
|
+
const client = createClient({
|
|
17
|
+
projectId: 'your-project-id',
|
|
18
|
+
plugins: [windowObjectPlugin()],
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Related
|
|
23
|
+
|
|
24
|
+
- [Plugins](/reference/sdk/plugins)
|
|
25
|
+
- [How to install the SDK](/how-to/install-the-sdk)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Predicates
|
|
7
|
+
|
|
8
|
+
A predicate holds reusable condition logic that reads the current state of your
|
|
9
|
+
application. Each predicate is a pure function. It takes a state snapshot, and that
|
|
10
|
+
snapshot holds the responses, the fields, the browser context, and optional
|
|
11
|
+
arguments.
|
|
12
|
+
|
|
13
|
+
## Use the state snapshot
|
|
14
|
+
|
|
15
|
+
Each predicate takes a `StateSnapshot` object with these properties:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
interface StateSnapshot {
|
|
19
|
+
client: Picked // The Picked client.
|
|
20
|
+
responses?: Array<{
|
|
21
|
+
// Every response from the user.
|
|
22
|
+
fieldId: string
|
|
23
|
+
value: FieldValue | undefined
|
|
24
|
+
}>
|
|
25
|
+
args?: unknown[]
|
|
26
|
+
fields?: Field[] // Every registered field.
|
|
27
|
+
location?: Location // The browser location: URL, pathname, search, and more.
|
|
28
|
+
document?: Document // The browser document object.
|
|
29
|
+
sessionStorage?: Storage // The browser sessionStorage API.
|
|
30
|
+
localStorage?: Storage // The browser localStorage API.
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
:::warning
|
|
35
|
+
In server-side rendering, and in any environment that is not a browser, `location`,
|
|
36
|
+
`document`, `sessionStorage`, and `localStorage` are `undefined`.
|
|
37
|
+
:::
|
|
38
|
+
|
|
39
|
+
## Related
|
|
40
|
+
|
|
41
|
+
- [How to write a predicate](/how-to/write-a-predicate)
|
|
42
|
+
- [About conditions and the logic engine](/explanation/conditions-and-the-logic-engine) —
|
|
43
|
+
why predicates exist.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# React
|
|
7
|
+
|
|
8
|
+
`@picked/react` lets the Picked client render React components, and gives hooks and
|
|
9
|
+
components to read the client from React.
|
|
10
|
+
|
|
11
|
+
| Peer dependency | Version |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `react`, `react-dom` | `^18.3.0` or `^19.0.0` |
|
|
14
|
+
| `@picked/client` | The version of your Picked SDK |
|
|
15
|
+
| `effector` | `^23.4.4` |
|
|
16
|
+
|
|
17
|
+
## Entry points
|
|
18
|
+
|
|
19
|
+
| Import | Exports |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `@picked/react` | `adaptReactComponent`, `isValidAdaptedComponent`, `PickedProvider`, `ExperienceSlot`, `HtmlRender` |
|
|
22
|
+
| `@picked/react/plugins/framework` | `reactFrameworkPlugin` |
|
|
23
|
+
| `@picked/react/hooks` | `usePicked`, `useExperience`, `useStep`, `useStepContent`, `useStepLayoutContent`, `useSetStepContext`, `useFeature`, `useFeatureResult`, `useFeatureEnabled`, `useAction`, `useActionResult`, `useContent`, `useReference`, `useReferences`, `useStoreGlobal`, `useSessionStorage`, `useLocalStorage`, the field hooks, and the context providers |
|
|
24
|
+
| `@picked/react/router` | `StepRouter`, `Step` |
|
|
25
|
+
| `@picked/react/form` | `useForm` |
|
|
26
|
+
| `@picked/react/server` | `renderer`, which renders a React component to HTML on the server |
|
|
27
|
+
|
|
28
|
+
## Framework plugin
|
|
29
|
+
|
|
30
|
+
`reactFrameworkPlugin` registers React as a framework of the client. The client then
|
|
31
|
+
renders each React component in a shadow root.
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { createClient } from 'picked'
|
|
35
|
+
import { reactFrameworkPlugin } from '@picked/react/plugins/framework'
|
|
36
|
+
|
|
37
|
+
const client = createClient({
|
|
38
|
+
projectId: 'your-project-id',
|
|
39
|
+
plugins: [reactFrameworkPlugin({ components: {} })],
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Option | Type | Default | Description |
|
|
44
|
+
| --- | --- | --- | --- |
|
|
45
|
+
| `components` | `{ experience?: { component, style? } }` | required | `experience` renders each experience that has no component of its own. |
|
|
46
|
+
| `stylesheets` | `string[]` | none | The URLs of stylesheets to add to each shadow root. |
|
|
47
|
+
| `css` | `string[]` | none | CSS text to add to each shadow root. |
|
|
48
|
+
|
|
49
|
+
## Components
|
|
50
|
+
|
|
51
|
+
`adaptReactComponent(component)` makes a React component that the client can render. It
|
|
52
|
+
takes a component, a dynamic import, or a lazy loader. See
|
|
53
|
+
[How to register a component](/how-to/register-a-component).
|
|
54
|
+
|
|
55
|
+
## Related
|
|
56
|
+
|
|
57
|
+
- [How to install the SDK](/how-to/install-the-sdk)
|
|
58
|
+
- [Steps](/reference/sdk/steps)
|
|
59
|
+
- [Features](/reference/sdk/features)
|
|
60
|
+
- [Content](/reference/sdk/content)
|
|
61
|
+
- [Rendering and hydration](/reference/sdk/ssr/rendering-and-hydration)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Server-Side Rendering
|
|
7
|
+
|
|
8
|
+
Render a Picked Page on your server so that search engines can read it. Then hydrate
|
|
9
|
+
the page on the client. This page shows the whole shape. The pages after it cover
|
|
10
|
+
each part.
|
|
11
|
+
|
|
12
|
+
## The server entry point
|
|
13
|
+
|
|
14
|
+
The server helpers live in a dedicated entry point, so a browser bundle never holds
|
|
15
|
+
them:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { serialize, registerProjectConfig } from '@picked/client/server'
|
|
19
|
+
import type { PickedSnapshot } from '@picked/client/server'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Go to [Server Snapshot](/reference/sdk/ssr/serialize) for the `serialize()` reference. Go to
|
|
23
|
+
[Rendering and Hydration](/reference/sdk/ssr/rendering-and-hydration) to connect React and
|
|
24
|
+
Svelte. Go to [Slots and Placements](/reference/sdk/ssr/slots-and-placements) for how a slot
|
|
25
|
+
chooses an experience. If the SDK is new to you, read
|
|
26
|
+
[Installation](/reference/sdk) first.
|
|
27
|
+
|
|
28
|
+
## Related
|
|
29
|
+
|
|
30
|
+
- [About server-side rendering](/explanation/why-server-side-rendering) — why the snapshot
|
|
31
|
+
exists.
|
|
32
|
+
- [How to server-render a page](/how-to/server-render-a-page)
|
|
33
|
+
- [Server snapshot](/reference/sdk/ssr/serialize)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rendering and Hydration
|
|
7
|
+
|
|
8
|
+
Seed a Picked client from the server snapshot, render your slots, and hydrate. This
|
|
9
|
+
page covers React and Svelte.
|
|
10
|
+
|
|
11
|
+
## React: `PickedProvider`
|
|
12
|
+
|
|
13
|
+
`PickedProvider` gives a client to each component below it. Its props are a union of
|
|
14
|
+
two modes:
|
|
15
|
+
|
|
16
|
+
| Props | Mode | Description |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `{ options, snapshot? }` | Create | The provider calls `createClient(options)` and hydrates the client from the snapshot. |
|
|
19
|
+
| `{ client, snapshot? }` | Adopt | The provider uses a client that is there already, such as a global one for the whole application. It can also seed that client from the snapshot. |
|
|
20
|
+
|
|
21
|
+
The provider seeds the client at once, so the server and the first paint on the
|
|
22
|
+
client give the same markup. In the browser the provider calls `client.start()` from
|
|
23
|
+
an effect. `start()` is idempotent, so it changes nothing on a client that already
|
|
24
|
+
started.
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { PickedProvider, ExperienceSlot } from '@picked/react'
|
|
28
|
+
|
|
29
|
+
<PickedProvider options={options} snapshot={snapshot}>
|
|
30
|
+
<ExperienceSlot name="page" />
|
|
31
|
+
</PickedProvider>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Svelte: `setPickedContext`
|
|
35
|
+
|
|
36
|
+
`setPickedContext` does the same work in the Svelte style. Call it one time at the
|
|
37
|
+
top of the `<script>` block of a component. It takes the same two modes,
|
|
38
|
+
`{ options, snapshot? }` or `{ client, snapshot? }`, and it returns the client.
|
|
39
|
+
|
|
40
|
+
```svelte
|
|
41
|
+
<script lang="ts">
|
|
42
|
+
import { ExperienceSlot, setPickedContext } from '@picked/svelte'
|
|
43
|
+
|
|
44
|
+
setPickedContext({ options, snapshot })
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<ExperienceSlot name="page" />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Render the page component
|
|
51
|
+
|
|
52
|
+
The SDK does not connect content to a component. You own the component that a Page
|
|
53
|
+
renders. Register it under the name that the Page uses. The Page Layout in the dashboard
|
|
54
|
+
points at that same name. The component reads the resolved entry data
|
|
55
|
+
through a content hook: `useStepContent()` in React, and `getStepContent()` in
|
|
56
|
+
Svelte.
|
|
57
|
+
|
|
58
|
+
:::code-group
|
|
59
|
+
|
|
60
|
+
```tsx [React]
|
|
61
|
+
import { useStepContent } from '@picked/react/hooks'
|
|
62
|
+
|
|
63
|
+
export function CMSPage() {
|
|
64
|
+
const content = useStepContent()
|
|
65
|
+
if (!content) {
|
|
66
|
+
return <p>Loading page content…</p>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const title = typeof content.title === 'string' ? content.title : ''
|
|
70
|
+
return (
|
|
71
|
+
<article>
|
|
72
|
+
<h1>{title}</h1>
|
|
73
|
+
</article>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```svelte [Svelte]
|
|
79
|
+
<script lang="ts">
|
|
80
|
+
import { getStepContent } from '@picked/svelte/hooks'
|
|
81
|
+
|
|
82
|
+
const content = getStepContent()
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
{#if $content}
|
|
86
|
+
<article>
|
|
87
|
+
<h1>{typeof $content.title === 'string' ? $content.title : ''}</h1>
|
|
88
|
+
</article>
|
|
89
|
+
{:else}
|
|
90
|
+
<p>Loading page content…</p>
|
|
91
|
+
{/if}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
:::
|
|
95
|
+
|
|
96
|
+
`useStepContent()` returns the `data` object of the entry directly. It returns `null`
|
|
97
|
+
while no content is registered. `getStepContent()` returns a store, and you read it
|
|
98
|
+
with `$content`.
|
|
99
|
+
|
|
100
|
+
## Related
|
|
101
|
+
|
|
102
|
+
- [How to server-render a page](/how-to/server-render-a-page) — the full example.
|
|
103
|
+
- [Slots and placements](/reference/sdk/ssr/slots-and-placements)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Server Snapshot
|
|
7
|
+
|
|
8
|
+
`serialize()` finds which experiences match a URL and reads their content on the
|
|
9
|
+
server. It returns a snapshot as plain JSON, and you pass that snapshot to the
|
|
10
|
+
client.
|
|
11
|
+
|
|
12
|
+
## The `serialize(client, params)` function
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { createClient } from 'picked'
|
|
16
|
+
import { serialize } from '@picked/client/server'
|
|
17
|
+
|
|
18
|
+
const client = createClient(options)
|
|
19
|
+
|
|
20
|
+
const snapshot = await serialize(client, {
|
|
21
|
+
projectConfig,
|
|
22
|
+
attributes: { location: { pathname: '/promo' } },
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`serialize()` does this. It registers the project configuration on the client at once. It
|
|
27
|
+
evaluates the condition of each experience against `attributes.location`. It reads
|
|
28
|
+
the content for each experience whose steps point at layout content. It removes the
|
|
29
|
+
duplicate content blobs, which it finds by their IDs. It then returns a `PickedSnapshot`.
|
|
30
|
+
|
|
31
|
+
The content that it reads holds the entries of the steps. It also holds the entries that
|
|
32
|
+
a [reference field](/reference/dashboard/content/models#reference-fields) reaches.
|
|
33
|
+
|
|
34
|
+
Give `serialize()` one client for each request. It does not support two calls at the
|
|
35
|
+
same time on one client.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
serialize(client: Picked, params: SerializeParams): Promise<PickedSnapshot>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Parameters
|
|
42
|
+
|
|
43
|
+
| Parameter | Type | Default | Description |
|
|
44
|
+
| --- | --- | --- | --- |
|
|
45
|
+
| `projectConfig` | `ProjectConfig` | required | The published configuration, or the preview configuration, to register and choose against. |
|
|
46
|
+
| `attributes.location` | `Partial<SerializeLocation>` | none | The URL fields that go into the selection context, such as `pathname`. |
|
|
47
|
+
| `getContent` | `SerializeGetContent` | runtime API | Your own function to read content, in place of the runtime API. |
|
|
48
|
+
| `previewToken` | `string` | none | The SDK sends this with the content request, so that preview content resolves during a preview render. |
|
|
49
|
+
| `timeoutMs` | `number` | `10000` | The limit for each content request. After the limit, the SDK skips that blob and continues. |
|
|
50
|
+
|
|
51
|
+
`SerializeLocation` carries the URL fields `href`, `pathname`, `search`, `hostname`,
|
|
52
|
+
`origin`, `protocol`, `port`, `host`, and `hash`. Pass only the fields that your
|
|
53
|
+
conditions read.
|
|
54
|
+
|
|
55
|
+
## The snapshot
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
interface PickedSnapshot {
|
|
59
|
+
versionId: string | null
|
|
60
|
+
projectConfig: ProjectConfig
|
|
61
|
+
content: ContentBlob[]
|
|
62
|
+
conditionResults: Record<string, boolean>
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
| Field | Description |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `versionId` | The project version that the snapshot was chosen against. It is `null` when the version is not known, for example in a preview. |
|
|
69
|
+
| `projectConfig` | The options that the server chose with. They travel to the client, so that the client renders against exactly the same options. |
|
|
70
|
+
| `content` | The content blobs for the chosen experiences, with the duplicates removed. It holds the entries of the steps, and every entry that a reference field reaches. The server render and the first paint in the browser therefore resolve the same pointers. |
|
|
71
|
+
| `conditionResults` | The condition results that the server kept, under a key made from the rule hash. The client then reuses the choice of the server. |
|
|
72
|
+
|
|
73
|
+
The snapshot is plain JSON. Write it into your HTML response with `JSON.stringify`
|
|
74
|
+
and read it back on the client.
|
|
75
|
+
|
|
76
|
+
## Related
|
|
77
|
+
|
|
78
|
+
- [How to server-render a page](/how-to/server-render-a-page)
|
|
79
|
+
- [How to render an unpublished preview](/how-to/render-an-unpublished-preview)
|
|
80
|
+
- [Content](/reference/sdk/content) — how to read a content record in a component.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Slots and Placements
|
|
7
|
+
|
|
8
|
+
A slot is a named mount point. `<ExperienceSlot name="page" />` renders every
|
|
9
|
+
experience that points at that slot and whose condition matches.
|
|
10
|
+
|
|
11
|
+
## What a slot is
|
|
12
|
+
|
|
13
|
+
The `name` of a slot points at the `.picked-slot-<name>` selector. So
|
|
14
|
+
`<ExperienceSlot name="page" />` renders every experience with the target selector
|
|
15
|
+
`.picked-slot-page`, when the condition of that experience is active.
|
|
16
|
+
|
|
17
|
+
The slot reads the active state of each experience from the seeded snapshot. It
|
|
18
|
+
therefore renders at once, both in server-side rendering and at the first paint of
|
|
19
|
+
the hydration.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<ExperienceSlot name="page" />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
| Prop | Type | Default | Description |
|
|
28
|
+
| --- | --- | --- | --- |
|
|
29
|
+
| `name` | `string` | — | The name of the slot. It points at the `.picked-slot-<name>` selector. |
|
|
30
|
+
| `open` | `'rule' \| 'rendered'` | `'rule'` | What an experience does when its `initializeOpen` is empty. `'rule'` keeps it closed. `'rendered'` opens it. A boolean or a rule in `initializeOpen` has the same result with both values. |
|
|
31
|
+
| `wrap` | `boolean` | `false` | Puts the output of each experience in a wrapper element. See [The wrapper](#the-wrapper). |
|
|
32
|
+
|
|
33
|
+
The slot reads `open` when it mounts an experience.
|
|
34
|
+
|
|
35
|
+
## Point an experience at a slot
|
|
36
|
+
|
|
37
|
+
Set the target selector of the experience to `.picked-slot-<name>`. An experience
|
|
38
|
+
that points at `.picked-slot-page` renders into `<ExperienceSlot name="page" />`. The
|
|
39
|
+
name of the slot and the end of the selector must match.
|
|
40
|
+
|
|
41
|
+
## Placement order
|
|
42
|
+
|
|
43
|
+
When more than one experience points at the same slot, they render in this placement
|
|
44
|
+
order:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
replace -> before -> prepend -> append -> after
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`replace` wins. An active experience with the `replace` placement renders alone, and
|
|
51
|
+
it replaces every other match in the slot. In every other case each active experience
|
|
52
|
+
renders, in placement order.
|
|
53
|
+
|
|
54
|
+
## More than one experience in one slot
|
|
55
|
+
|
|
56
|
+
The slot collects each experience that points at its selector. It drops each one
|
|
57
|
+
whose condition is not active. It then sorts the rest into placement order. When a `replace`
|
|
58
|
+
experience is among the active ones, only that experience renders. With no active
|
|
59
|
+
`replace`, the slot renders every active experience in the order `before`, `prepend`,
|
|
60
|
+
`append`, `after`.
|
|
61
|
+
|
|
62
|
+
One Page can therefore take a whole slot with `replace`, while another slot holds
|
|
63
|
+
several experiences that use the other placements. A condition decides which
|
|
64
|
+
experiences are active. See [Conditions](/reference/dashboard/conditions).
|
|
65
|
+
|
|
66
|
+
## Lifecycle events
|
|
67
|
+
|
|
68
|
+
The slot sends these events on the event bus of the client for each experience that it
|
|
69
|
+
renders. It sends them only in the browser, and only after the client is ready. The server
|
|
70
|
+
render sends no events.
|
|
71
|
+
|
|
72
|
+
| Bus event | When it fires | Analytics event |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `experience_initialize` | The slot mounts the experience. | `experience_load` |
|
|
75
|
+
| `experience_open` | The experience opens. `open` and `initializeOpen` decide this. | `app_open` |
|
|
76
|
+
| `step_enter` | The flow of an open experience arrives at a step. | `step_enter` |
|
|
77
|
+
| `experience_unmount` | The slot stops rendering the experience. | None |
|
|
78
|
+
|
|
79
|
+
When the slot stops rendering an experience, the open state of that experience does not
|
|
80
|
+
change. The slot therefore sends no `experience_close`, and the analytics plugin sends no
|
|
81
|
+
`app_close`. After that, the experience sends no events, and its flow stops.
|
|
82
|
+
|
|
83
|
+
Each time that the slot mounts an experience, the experience starts closed, and `open` and
|
|
84
|
+
`initializeOpen` decide again if it opens. This is also true when the slot mounts the same
|
|
85
|
+
experience again, for example after a React `<Activity>` shows it again.
|
|
86
|
+
|
|
87
|
+
The flow of an experience continues from its history. When the history of an experience
|
|
88
|
+
already ends at a step, the experience opens at that step and the slot sends no
|
|
89
|
+
`step_enter` for it.
|
|
90
|
+
|
|
91
|
+
Each analytics event has the page URL at the time that the event fires.
|
|
92
|
+
|
|
93
|
+
## The wrapper
|
|
94
|
+
|
|
95
|
+
With `wrap`, the slot puts the output of each experience in one element:
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<div data-picked-experience-id="<experience id>" style="display: contents">
|
|
99
|
+
<!-- the experience -->
|
|
100
|
+
</div>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The element has no box of its own, so the layout does not change. The element is in the
|
|
104
|
+
document tree, so the selectors `>`, `:first-child`, and `:nth-child()` see it. The
|
|
105
|
+
`data-picked-experience-id` attribute holds the ID of the experience. It is the same on the
|
|
106
|
+
server and in the browser.
|
|
107
|
+
|
|
108
|
+
Without `wrap`, the slot adds no element.
|
|
109
|
+
|
|
110
|
+
## Related
|
|
111
|
+
|
|
112
|
+
- [About server-side rendering](/explanation/why-server-side-rendering) — why placements
|
|
113
|
+
have an order.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: reference
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Steps
|
|
7
|
+
|
|
8
|
+
Render your steps with `<StepRouter>` and `<Step>` from `@picked/react` or
|
|
9
|
+
`@picked/svelte`. Reach the active step through `useStep()` or `getStep()`.
|
|
10
|
+
|
|
11
|
+
## Reach the step context
|
|
12
|
+
|
|
13
|
+
Inside a `Step` component, or inside any component that a `Step` renders, call
|
|
14
|
+
`getStep()` or `useStep()` to reach the reactive context of the step:
|
|
15
|
+
|
|
16
|
+
:::code-group
|
|
17
|
+
|
|
18
|
+
```tsx [React]
|
|
19
|
+
import { useStep, useStoreGlobal } from '@picked/react/hooks'
|
|
20
|
+
|
|
21
|
+
function ContactForm() {
|
|
22
|
+
const { step, fields } = useStep()
|
|
23
|
+
const currentStep = useStoreGlobal(step)
|
|
24
|
+
const currentFields = useStoreGlobal(fields)
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
<h1>{currentStep.label}</h1>
|
|
29
|
+
|
|
30
|
+
{currentFields.map((field) => (
|
|
31
|
+
<div key={field.id}>
|
|
32
|
+
<label>{field.label}</label>
|
|
33
|
+
<input
|
|
34
|
+
name={field.id}
|
|
35
|
+
value={field.value ?? ''}
|
|
36
|
+
onChange={field.onChange}
|
|
37
|
+
onBlur={field.onBlur}
|
|
38
|
+
/>
|
|
39
|
+
{field.error && field.touched && <p className="error">{field.error.userMessage}</p>}
|
|
40
|
+
</div>
|
|
41
|
+
))}
|
|
42
|
+
</>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```svelte [Svelte]
|
|
48
|
+
<script lang="ts">
|
|
49
|
+
import { getStep } from '@picked/svelte/hooks'
|
|
50
|
+
|
|
51
|
+
const { step, fields } = getStep()
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<h1>{$step.label}</h1>
|
|
55
|
+
|
|
56
|
+
{#each $fields as field}
|
|
57
|
+
<label>{field.label}</label>
|
|
58
|
+
<input
|
|
59
|
+
name={field.id}
|
|
60
|
+
value={field.value ?? ''}
|
|
61
|
+
onchange={field.onChange}
|
|
62
|
+
onblur={field.onBlur}
|
|
63
|
+
/>
|
|
64
|
+
{#if field.error && field.touched}
|
|
65
|
+
<p class="error">{field.error.userMessage}</p>
|
|
66
|
+
{/if}
|
|
67
|
+
{/each}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
:::
|
|
71
|
+
|
|
72
|
+
`getStep()` in Svelte, and `useStep()` in React, return a `StepContext` with these
|
|
73
|
+
properties:
|
|
74
|
+
|
|
75
|
+
| Property | Type | Description |
|
|
76
|
+
| ---------- | --------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
77
|
+
| `step` | `Store<ExperienceStep>` | A reactive store that holds the `id` and the `label` of the step |
|
|
78
|
+
| `fields` | `Store<FormField[]>` | A reactive store of the fields, with their current values, errors, and handlers |
|
|
79
|
+
| `isActive` | `Store<boolean>` (Svelte) / `boolean` (React) | Whether the router holds this step as the active one |
|
|
80
|
+
| `form` | Object | The handlers for a form submission. See [Forms](#forms). |
|
|
81
|
+
|
|
82
|
+
## Related
|
|
83
|
+
|
|
84
|
+
- [How to render a step form](/how-to/render-a-step-form)
|
|
85
|
+
- [How to run code on a form submission](/how-to/run-code-on-a-form-submission)
|
|
86
|
+
- [About how Picked works](/explanation/how-picked-works) — why a flow is a graph.
|