@picked/svelte 0.0.62 → 0.0.63-next.1
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/experience/ExperienceHost.svelte +112 -0
- package/dist/experience/ExperienceSlot.svelte +143 -0
- package/dist/experience/set-picked-context.d.ts +29 -0
- package/dist/experience/set-picked-context.js +1 -0
- package/dist/hooks/action.js +1 -1
- package/dist/hooks/predicate.js +1 -1
- package/dist/hooks/step.js +1 -1
- package/dist/hooks/stores.js +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +9 -1
- package/dist/plugins/devtools/components/Tabs.svelte +8 -5
- package/dist/render/browser.d.ts +24 -1
- package/dist/render/browser.js +1 -1
- package/package.json +24 -6
- package/dist/plugins/devtools/PickedLogo.svelte +0 -58
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* @file Internal host that instantiates a single experience, exposes the instance via context,
|
|
4
|
+
* and renders the resolved Svelte component.
|
|
5
|
+
*
|
|
6
|
+
* Lazy loaders and promise-wrapped components load asynchronously via `{#await}`: they render
|
|
7
|
+
* nothing on the server and on the hydration first paint, then swap in once resolved on the
|
|
8
|
+
* client, so the server and the client's first paint agree.
|
|
9
|
+
*/
|
|
10
|
+
import { setContext } from 'svelte'
|
|
11
|
+
import type { Component } from 'svelte'
|
|
12
|
+
import type { ExperienceStore, Picked } from '@picked/client'
|
|
13
|
+
import { createExperienceInstance } from '@picked/client/server'
|
|
14
|
+
import { isLazyLoader } from '@picked/client'
|
|
15
|
+
import { EXPERIENCE_CONTEXT_KEY } from '../context'
|
|
16
|
+
import { useUnit } from '../hooks'
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
experience: ExperienceStore
|
|
20
|
+
client: Picked
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { experience, client }: Props = $props()
|
|
24
|
+
|
|
25
|
+
// `client` and `experience` are stable for this host's lifetime — `Experience` keys each host
|
|
26
|
+
// by `experience.id`, so a different experience mounts a new host rather than mutating props.
|
|
27
|
+
// Capturing their initial values is therefore intentional.
|
|
28
|
+
// svelte-ignore state_referenced_locally
|
|
29
|
+
const instance = createExperienceInstance({ client, experience })
|
|
30
|
+
|
|
31
|
+
setContext(EXPERIENCE_CONTEXT_KEY, instance)
|
|
32
|
+
|
|
33
|
+
const resolved = useUnit(instance.componentAndFrameworkStore)
|
|
34
|
+
// svelte-ignore state_referenced_locally
|
|
35
|
+
const ready = useUnit(client.readyStore)
|
|
36
|
+
|
|
37
|
+
// Slot-rendered experiences have no DOM target, so the normal lifecycle never loads their CMS
|
|
38
|
+
// content. Once the client is ready (so `client.contentLoader` is set by `start()`), load it
|
|
39
|
+
// here; `getStepContent` picks it up reactively. `$effect` does not run during SSR, where
|
|
40
|
+
// content arrives pre-registered via the snapshot.
|
|
41
|
+
$effect(() => {
|
|
42
|
+
if ($ready) {
|
|
43
|
+
// Content fetch failures (e.g. a 404 when the content endpoint is unavailable) are already
|
|
44
|
+
// swallowed inside the effect; the explicit catch guarantees no unhandled rejection even if
|
|
45
|
+
// that contract changes, so a missing/erroring content endpoint never breaks rendering.
|
|
46
|
+
experience.prepareExperienceContentFx().catch(() => undefined)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Narrows to a directly-renderable Svelte component, excluding lazy loaders and promises.
|
|
52
|
+
*
|
|
53
|
+
* @param {unknown} value - The adapted component's `component` field.
|
|
54
|
+
* @returns {boolean} True when the value is a direct Svelte component.
|
|
55
|
+
* @example
|
|
56
|
+
* isDirectSvelteComponent(SomeComponent)
|
|
57
|
+
*/
|
|
58
|
+
function isDirectSvelteComponent(value: unknown): value is Component {
|
|
59
|
+
return typeof value === 'function' && !isLazyLoader(value) && !(value instanceof Promise)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns `value` when it is a directly-renderable Svelte component.
|
|
64
|
+
*
|
|
65
|
+
* @param {unknown} value - The candidate component (a loaded module's default export).
|
|
66
|
+
* @returns {Component} The Svelte component.
|
|
67
|
+
* @throws {Error} When the value is not a Svelte component.
|
|
68
|
+
* @example
|
|
69
|
+
* toComponent((await loader.load()).default)
|
|
70
|
+
*/
|
|
71
|
+
function toComponent(value: unknown): Component {
|
|
72
|
+
if (isDirectSvelteComponent(value)) {
|
|
73
|
+
return value
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new Error('Lazy experience component did not resolve to a Svelte component.')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns a promise resolving to the component for a lazy loader or promise-wrapped component,
|
|
81
|
+
* or null for a direct component (or when there is no component).
|
|
82
|
+
*
|
|
83
|
+
* @param {unknown} value - The adapted component's `component` field.
|
|
84
|
+
* @returns {Promise<Component> | null} The loader promise, or null.
|
|
85
|
+
* @example
|
|
86
|
+
* toComponentLoader(lazyLoader)
|
|
87
|
+
*/
|
|
88
|
+
function toComponentLoader(value: unknown): Promise<Component> | null {
|
|
89
|
+
if (isLazyLoader(value)) {
|
|
90
|
+
return value.load().then((mod) => toComponent(mod.default))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (value instanceof Promise) {
|
|
94
|
+
return value.then((mod) => toComponent(mod.default))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const adapted = $derived($resolved?.component.component)
|
|
101
|
+
const svelteComponent = $derived(adapted ? adapted.component : undefined)
|
|
102
|
+
const componentLoader = $derived(toComponentLoader(svelteComponent))
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
{#if isDirectSvelteComponent(svelteComponent)}
|
|
106
|
+
{@const Comp = svelteComponent}
|
|
107
|
+
<Comp {client} experience={instance} />
|
|
108
|
+
{:else if componentLoader}
|
|
109
|
+
{#await componentLoader then Comp}
|
|
110
|
+
<Comp {client} experience={instance} />
|
|
111
|
+
{/await}
|
|
112
|
+
{/if}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* @file Slot-based experience renderer. Renders every registered experience that targets the
|
|
4
|
+
* named slot and whose condition is active, ordered by placement. Active is read synchronously
|
|
5
|
+
* from the seeded condition-results store (sync on SSR / hydration first paint); on a cache miss
|
|
6
|
+
* (standalone, no snapshot) the condition is evaluated asynchronously and the store update
|
|
7
|
+
* re-renders reactively.
|
|
8
|
+
*/
|
|
9
|
+
import type { ExperienceStore, ExperienceTarget } from '@picked/client'
|
|
10
|
+
import { evaluateConditionMemo, hashRule, ruleReferencesAction } from '@picked/client/server'
|
|
11
|
+
import { getPicked, useUnit } from '../hooks'
|
|
12
|
+
import ExperienceHost from './ExperienceHost.svelte'
|
|
13
|
+
|
|
14
|
+
type ExperienceConfig = ReturnType<ExperienceStore['configStore']['getState']>
|
|
15
|
+
type ConditionRule = ExperienceConfig['decisionLogic']
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
/**
|
|
19
|
+
* Slot name. Maps to the `.picked-slot-<name>` selector that targeted experiences declare.
|
|
20
|
+
* Named `name` rather than `slot` because Svelte's compiler reserves the `slot` attribute for
|
|
21
|
+
* legacy named-slot assignment, so it cannot be used as a component prop.
|
|
22
|
+
*/
|
|
23
|
+
name: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { name }: Props = $props()
|
|
27
|
+
|
|
28
|
+
const client = getPicked()
|
|
29
|
+
const selector = $derived(`.picked-slot-${name}`)
|
|
30
|
+
|
|
31
|
+
const PLACEMENT_ORDER: ExperienceTarget['placement'][] = [
|
|
32
|
+
'replace',
|
|
33
|
+
'before',
|
|
34
|
+
'prepend',
|
|
35
|
+
'append',
|
|
36
|
+
'after',
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
interface Candidate {
|
|
40
|
+
experience: ExperienceStore
|
|
41
|
+
placement: ExperienceTarget['placement']
|
|
42
|
+
condition: ConditionRule
|
|
43
|
+
hash: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const experiences = useUnit(client.experiences.listStore)
|
|
47
|
+
const conditionResults = useUnit(client.conditionResults.store)
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns the sort rank for a placement; unknown placements sort last.
|
|
51
|
+
*
|
|
52
|
+
* @param {ExperienceTarget['placement']} placement - The target placement.
|
|
53
|
+
* @returns {number} The placement's index in the precedence order.
|
|
54
|
+
* @example
|
|
55
|
+
* placementRank('append')
|
|
56
|
+
*/
|
|
57
|
+
function placementRank(placement: ExperienceTarget['placement']): number {
|
|
58
|
+
const index = PLACEMENT_ORDER.indexOf(placement)
|
|
59
|
+
return index === -1 ? PLACEMENT_ORDER.length : index
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Maps an experience to a slot candidate, or null when it does not target this slot or carries
|
|
64
|
+
* an action-referencing condition.
|
|
65
|
+
*
|
|
66
|
+
* @param {ExperienceStore} experience - The experience to inspect.
|
|
67
|
+
* @returns {Candidate | null} The candidate, or null when the experience is not an eligible slot match.
|
|
68
|
+
* @example
|
|
69
|
+
* toCandidate(experienceStore)
|
|
70
|
+
*/
|
|
71
|
+
function toCandidate(experience: ExperienceStore): Candidate | null {
|
|
72
|
+
const config = experience.configStore.getState()
|
|
73
|
+
const target = config.targetSelectors.find(
|
|
74
|
+
(selectorTarget) => selectorTarget.selector === selector,
|
|
75
|
+
)
|
|
76
|
+
if (!target) {
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Action-referencing conditions are never cached (their side effect must re-run), so they can
|
|
81
|
+
// never resolve through the synchronous `peek` the slot path relies on. Slots do not support
|
|
82
|
+
// action-gated conditions in v1; excluding them also stops the selection effect from re-firing
|
|
83
|
+
// the action on every render.
|
|
84
|
+
if (ruleReferencesAction(config.decisionLogic)) {
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
experience,
|
|
90
|
+
placement: target.placement,
|
|
91
|
+
condition: config.decisionLogic,
|
|
92
|
+
hash: hashRule(config.decisionLogic),
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const candidates = $derived(
|
|
97
|
+
$experiences
|
|
98
|
+
.map(toCandidate)
|
|
99
|
+
.filter((candidate): candidate is Candidate => candidate !== null)
|
|
100
|
+
.sort((a, b) => placementRank(a.placement) - placementRank(b.placement)),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
// Referencing `$conditionResults` keeps this recompute reactive to late condition writes
|
|
104
|
+
// (the standalone cache-miss path), so a `peek` that returned `undefined` re-renders once
|
|
105
|
+
// the async evaluation resolves and writes the store. An active `replace` experience wins:
|
|
106
|
+
// the first one (it sorts first) renders alone, replacing any other slot matches.
|
|
107
|
+
const active = $derived.by(() => {
|
|
108
|
+
void $conditionResults
|
|
109
|
+
const activeCandidates = candidates.filter(
|
|
110
|
+
(candidate) => client.conditionResults.peek(candidate.hash) === true,
|
|
111
|
+
)
|
|
112
|
+
const firstReplace = activeCandidates.find((candidate) => candidate.placement === 'replace')
|
|
113
|
+
return firstReplace ? [firstReplace] : activeCandidates
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Builds a plain, JSON-serialisable subset of the current URL for the evaluation context.
|
|
118
|
+
* The live `Location` object is intentionally not passed: json-logic predicates read plain
|
|
119
|
+
* values, and this mirrors the `SerializeLocation` shape the server selects against.
|
|
120
|
+
*
|
|
121
|
+
* @returns {Record<string, string>} The plain location subset.
|
|
122
|
+
* @example
|
|
123
|
+
* buildLocationContext()
|
|
124
|
+
*/
|
|
125
|
+
function buildLocationContext(): Record<string, string> {
|
|
126
|
+
const { href, pathname, search, hostname, origin, protocol, port, host, hash } = window.location
|
|
127
|
+
return { href, pathname, search, hostname, origin, protocol, port, host, hash }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
$effect(() => {
|
|
131
|
+
for (const candidate of candidates) {
|
|
132
|
+
if (client.conditionResults.peek(candidate.hash) === undefined) {
|
|
133
|
+
void evaluateConditionMemo(client, candidate.condition, {
|
|
134
|
+
context: { location: buildLocationContext() },
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
{#each active as candidate (candidate.experience.id)}
|
|
142
|
+
<ExperienceHost experience={candidate.experience} {client} />
|
|
143
|
+
{/each}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CreateClientOptions, Picked } from '@picked/client';
|
|
2
|
+
import { PickedSnapshot } from '@picked/client/server';
|
|
3
|
+
type SetPickedContextParams = {
|
|
4
|
+
client: Picked;
|
|
5
|
+
snapshot?: PickedSnapshot;
|
|
6
|
+
} | {
|
|
7
|
+
options: CreateClientOptions;
|
|
8
|
+
snapshot?: PickedSnapshot;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Provides a Picked client to descendant components through Svelte context, starting it in the browser.
|
|
12
|
+
*
|
|
13
|
+
* Call this once at the top of a component's `<script>` so `setContext` and `onMount` run during
|
|
14
|
+
* component initialisation. Pass `options` to create the client and synchronously hydrate it from an
|
|
15
|
+
* optional SSR snapshot (a `versionId` mismatch is tolerated; the client reconciles via `start()`),
|
|
16
|
+
* or pass an existing `client` (such as the app's single global client) to render experiences
|
|
17
|
+
* against it — supply `snapshot` alongside `client` to SSR-seed that shared client rather than
|
|
18
|
+
* creating a second one. In the browser the client is started from `onMount`; `start()` is
|
|
19
|
+
* idempotent, so a client that was already started is left untouched.
|
|
20
|
+
*
|
|
21
|
+
* @param {SetPickedContextParams} params - `{ client, snapshot? }` or `{ options, snapshot? }`.
|
|
22
|
+
* @returns {Picked} The client placed into context.
|
|
23
|
+
* @example
|
|
24
|
+
* setPickedContext({ options })
|
|
25
|
+
* @example
|
|
26
|
+
* setPickedContext({ client: globalClient, snapshot })
|
|
27
|
+
*/
|
|
28
|
+
export declare function setPickedContext(params: SetPickedContextParams): Picked;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{CLIENT_CONTEXT_KEY as e}from"../context.js";import{onMount as t,setContext as n}from"svelte";import{createClient as r,supportsWindow as i}from"@picked/client";import{registerProjectConfig as a}from"@picked/client/server";function o(e,t){let n=e.session.store.getState().id;a(e,t.projectConfig,{isPreview:!1,sessionId:n}),t.content.forEach(t=>e.registerContent(t)),e.conditionResults.seed(t.conditionResults)}function s(e,t){let n=r(e);return t&&o(n,t),n}function c(r){let a;return`client`in r?(r.snapshot&&o(r.client,r.snapshot),a=r.client):a=s(r.options,r.snapshot),n(e,a),t(()=>{i()&&a.start()}),a}export{c as setPickedContext};
|
package/dist/hooks/action.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getPicked as e}from"./client.js";import{attach as t,createEffect as n,createEvent as r,createStore as i,sample as a}from"effector";
|
|
1
|
+
import{getPicked as e}from"./client.js";import"@picked/client";import{attach as t,createEffect as n,createEvent as r,createStore as i,sample as a}from"effector";function o(t,n){return e().actions.getEvokableAction(t,n)}function s(e,s){let c=o(e,s),l=i(void 0,{skipVoid:!1}),u=r(),d=t({source:c,mapParams:(e,t)=>t,effect:n(async e=>{if(e)return await e()})});return a({clock:u,target:d}),a({clock:d.doneData,target:l}),u(),a({clock:c,target:u}),l}export{o as getAction,s as getActionResult};
|
package/dist/hooks/predicate.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getPicked as e}from"./client.js";import{attach as t,createEffect as n,createEvent as r,createStore as i,sample as a}from"effector";
|
|
1
|
+
import{getPicked as e}from"./client.js";import"@picked/client";import{attach as t,createEffect as n,createEvent as r,createStore as i,sample as a}from"effector";function o(t){return e().predicates.getEvokablePredicate(t)}function s(e){let s=o(e),c=i(void 0,{skipVoid:!1}),l=r(),u=t({source:s,mapParams:(e,t)=>t,effect:n(async e=>{if(e)return await e()})});return a({clock:l,target:u}),a({clock:u.doneData,target:c}),l(),a({clock:s,target:l}),c}export{o as getPredicate,s as getPredicateResult};
|
package/dist/hooks/step.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{STEP_CONTEXT_KEY as e}from"../context.js";import{getPicked as
|
|
1
|
+
import{STEP_CONTEXT_KEY as e}from"../context.js";import{getExperience as t,getPicked as n}from"./client.js";import{getStepRouter as r}from"../router/context.js";import{getContext as i,setContext as a}from"svelte";import{combine as o}from"effector";import{createForm as s}from"../form/create-form.svelte";function c(t){let i=n(),{activeStep:c}=r(),l=c.map(e=>e?.key===t),u=i.steps.listStore.map(e=>e.find(e=>e.key===t)??{id:t,key:t,name:``,fieldKeys:[]}),d=s(t,u.map(e=>e.name),u.map(e=>e.fieldKeys));return a(e,{step:u,layoutContentStore:o(u,i.contents.indexStore,(e,t)=>e.layoutContentId?t[e.layoutContentId]??null:null,{skipVoid:!1}),isActive:l,isActiveStep:l,...d})}function l(){return i(e)}function u(){return(i(e)?.layoutContentStore??d()).map(e=>e?.data??null,{skipVoid:!1})}function d(){let e=t(),r=n();return o(e.treeStore,r.contents.indexStore,(e,t)=>{let n=e.steps.find(e=>e.layoutContentId);return n?.layoutContentId?t[n.layoutContentId]??null:null})}export{l as getStep,u as getStepContent,c as setStepContext};
|
package/dist/hooks/stores.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SCOPE_CONTEXT_KEY as e}from"../context.js";import{
|
|
1
|
+
import{SCOPE_CONTEXT_KEY as e}from"../context.js";import{getContext as t,setContext as n}from"svelte";import{createWatch as r,fork as i,is as a,scopeBind as o}from"effector";var s=t=>n(e,i(t)),c=()=>t(e);function l(e){let t=c();return t?a.effect(e)||a.event(e)&&typeof e==`function`?o(e,{scope:t}):{subscribe:n=>(n(t.getState(e)),r({unit:e,fn:n,scope:t}))}:e}export{c as getContextScope,s as setContextScope,l as useUnit};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Public entry for the `@picked/svelte` package.
|
|
3
|
+
*/
|
|
1
4
|
export { adaptSvelteComponent, isValidAdaptedComponent } from './component';
|
|
2
5
|
export type { ComponentWithProps } from './component';
|
|
3
6
|
export { LazyImage } from './components';
|
|
7
|
+
export { setPickedContext } from './experience/set-picked-context';
|
|
8
|
+
export { default as ExperienceSlot } from './experience/ExperienceSlot.svelte';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{adaptSvelteComponent as e,isValidAdaptedComponent as t}from"./component.js";import{LazyImage as n}from"./components/index.js";export{n as LazyImage,e as adaptSvelteComponent,t as isValidAdaptedComponent};
|
|
1
|
+
import{adaptSvelteComponent as e,isValidAdaptedComponent as t}from"./component.js";import{LazyImage as n}from"./components/index.js";import{setPickedContext as r}from"./experience/set-picked-context.js";import i from"./experience/ExperienceSlot.svelte";export{i as ExperienceSlot,n as LazyImage,e as adaptSvelteComponent,t as isValidAdaptedComponent,r as setPickedContext};
|
|
@@ -345,10 +345,18 @@
|
|
|
345
345
|
.launcher {
|
|
346
346
|
position: fixed;
|
|
347
347
|
bottom: 1rem;
|
|
348
|
-
right:
|
|
348
|
+
right: 0;
|
|
349
349
|
z-index: 999999;
|
|
350
350
|
display: flex;
|
|
351
351
|
align-items: stretch;
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
transform: translateX(calc(100% - 8px));
|
|
354
|
+
transition: transform 180ms ease-out;
|
|
355
|
+
|
|
356
|
+
&:hover,
|
|
357
|
+
&:focus-within {
|
|
358
|
+
transform: translateX(0);
|
|
359
|
+
}
|
|
352
360
|
}
|
|
353
361
|
|
|
354
362
|
.floating-button {
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script lang="ts" generics="P extends Record<string, unknown> = Record<string, never>">
|
|
2
2
|
/** @file Accessible tab container that renders dynamic Svelte components per tab. */
|
|
3
3
|
import type { Component } from 'svelte'
|
|
4
4
|
|
|
5
5
|
interface Tab {
|
|
6
6
|
id: string
|
|
7
7
|
label: string
|
|
8
|
-
component: Component<
|
|
9
|
-
props
|
|
8
|
+
component: Component<P>
|
|
9
|
+
props: P
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export let tabs: Tab[]
|
|
13
13
|
export let initialTab = 0
|
|
14
14
|
|
|
15
15
|
let activeTab = initialTab
|
|
16
|
-
|
|
16
|
+
// Keyed by tab index — written at render time via Svelte `bind:this`. A
|
|
17
|
+
// record rather than an array sidesteps `sonarjs/no-empty-collection`,
|
|
18
|
+
// whose flow analysis can't see the bind writes.
|
|
19
|
+
const tabButtons: Record<number, HTMLButtonElement | undefined> = {}
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* Sets the active tab to the given index and scrolls the corresponding button into the
|
|
@@ -94,7 +97,7 @@
|
|
|
94
97
|
hidden={activeTab !== i}
|
|
95
98
|
>
|
|
96
99
|
{#if activeTab === i}
|
|
97
|
-
<svelte:component this={tab.component} {...tab.props
|
|
100
|
+
<svelte:component this={tab.component} {...tab.props} />
|
|
98
101
|
{/if}
|
|
99
102
|
</div>
|
|
100
103
|
{/each}
|
package/dist/render/browser.d.ts
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import { AdaptedComponent, ExperienceInstance, Picked } from '@picked/client';
|
|
2
|
+
/**
|
|
3
|
+
* Builds the Svelte context map exposing the client and experience instance to a mounted component.
|
|
4
|
+
*
|
|
5
|
+
* @param {Picked} client - The Picked client.
|
|
6
|
+
* @param {ExperienceInstance} experience - The experience instance.
|
|
7
|
+
* @returns {Map<symbol, Picked | ExperienceInstance>} The context map keyed by the client and experience context symbols.
|
|
8
|
+
* @example
|
|
9
|
+
* createContext(client, experience)
|
|
10
|
+
*/
|
|
2
11
|
export declare function createContext(client: Picked, experience: ExperienceInstance): Map<symbol, Picked | ExperienceInstance>;
|
|
12
|
+
/**
|
|
13
|
+
* Renders an adapted Svelte component into a shadow root, injecting its stylesheets and inline CSS.
|
|
14
|
+
*
|
|
15
|
+
* @param {ShadowRoot} shadowRoot - The shadow root to render into.
|
|
16
|
+
* @param {AdaptedComponent<string, unknown>} component - The adapted component to render.
|
|
17
|
+
* @param {Picked} client - The Picked client.
|
|
18
|
+
* @param {ExperienceInstance} experience - The experience instance.
|
|
19
|
+
* @param {object} options - Stylesheets and inline CSS to inject into the shadow root.
|
|
20
|
+
* @param {string[]} [options.stylesheets] - Stylesheet URLs to inject as `<link>` elements.
|
|
21
|
+
* @param {string[]} [options.css] - Inline CSS blocks to inject as `<style>` elements.
|
|
22
|
+
* @returns {Promise<void>} A promise that resolves once the component is mounted.
|
|
23
|
+
* @throws {Error} When the component is not a valid adapted Svelte component.
|
|
24
|
+
* @example
|
|
25
|
+
* await renderSvelteComponent(shadowRoot, component, client, experience, {})
|
|
26
|
+
*/
|
|
3
27
|
export declare const renderSvelteComponent: (shadowRoot: ShadowRoot, component: AdaptedComponent<string, unknown>, client: Picked, experience: ExperienceInstance, options: {
|
|
4
28
|
stylesheets?: string[];
|
|
5
29
|
css?: string[];
|
|
6
30
|
}) => Promise<void>;
|
|
7
|
-
export declare const unRenderSvelteComponent: (target: Element) => void;
|
package/dist/render/browser.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isValidAdaptedComponent as e}from"../component.js";import{CLIENT_CONTEXT_KEY as t,EXPERIENCE_CONTEXT_KEY as n}from"../context.js";import{mergeArrays as r}from"../utils/array.js";import{mount as i}from"svelte";import{isLazyLoader as a}from"@picked/client";import o from"../utils/ErrorBoundary.svelte";function s(e,r){return new Map([[t,e],[n,r]])}var c=async(t,n,c,l,u)=>{if(!e(n))throw Error(`The component is invalid.`);let d=r(n.style?.sheets,u.stylesheets),f=r(n.style?.css,u.css);d.length&&d.forEach(e=>{let n=document.createElement(`link`);n.setAttribute(`rel`,`stylesheet`),n.setAttribute(`href`,e),n.setAttribute(`type`,`text/css`),n.setAttribute(`picked-injected`,`true`),t.appendChild(n)}),f.length&&f.forEach(e=>{let n=document.createElement(`style`);n.textContent=e,n.setAttribute(`type`,`text/css`),n.setAttribute(`picked-injected`,`true`),t.appendChild(n)});let p=e=>{i(o,{target:t,props:{component:e,componentProps:{client:c,experience:l}},context:s(c,l)})};if(a(n.component)){let{default:e}=await n.component.load();p(e);return}if(n.component instanceof Promise){let{default:e}=await n.component;p(e);return}p(n.component)}
|
|
1
|
+
import{isValidAdaptedComponent as e}from"../component.js";import{CLIENT_CONTEXT_KEY as t,EXPERIENCE_CONTEXT_KEY as n}from"../context.js";import{mergeArrays as r}from"../utils/array.js";import{mount as i}from"svelte";import{isLazyLoader as a}from"@picked/client";import o from"../utils/ErrorBoundary.svelte";function s(e,r){return new Map([[t,e],[n,r]])}var c=async(t,n,c,l,u)=>{if(!e(n))throw Error(`The component is invalid.`);let d=r(n.style?.sheets,u.stylesheets),f=r(n.style?.css,u.css);d.length&&d.forEach(e=>{let n=document.createElement(`link`);n.setAttribute(`rel`,`stylesheet`),n.setAttribute(`href`,e),n.setAttribute(`type`,`text/css`),n.setAttribute(`picked-injected`,`true`),t.appendChild(n)}),f.length&&f.forEach(e=>{let n=document.createElement(`style`);n.textContent=e,n.setAttribute(`type`,`text/css`),n.setAttribute(`picked-injected`,`true`),t.appendChild(n)});let p=e=>{i(o,{target:t,props:{component:e,componentProps:{client:c,experience:l}},context:s(c,l)})};if(a(n.component)){let{default:e}=await n.component.load();p(e);return}if(n.component instanceof Promise){let{default:e}=await n.component;p(e);return}p(n.component)};export{s as createContext,c as renderSvelteComponent};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@picked/svelte",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.63-next.1",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"d3": "^7.9.0"
|
|
6
6
|
},
|
|
@@ -10,12 +10,11 @@
|
|
|
10
10
|
"@repo/eslint-config": "workspace:*",
|
|
11
11
|
"@repo/typescript-config": "workspace:*",
|
|
12
12
|
"@size-limit/file": "^12.0.0",
|
|
13
|
-
"@sveltejs/package": "^2.3.11",
|
|
14
13
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
15
14
|
"@tsconfig/svelte": "^5.0.4",
|
|
16
15
|
"@types/d3": "^7.4.3",
|
|
17
16
|
"@types/node": "^22",
|
|
18
|
-
"@vitest/coverage-v8": "^4.1.
|
|
17
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
19
18
|
"effector": "^23.3.0",
|
|
20
19
|
"fast-glob": "^3.3.3",
|
|
21
20
|
"happy-dom": "^20.8.8",
|
|
@@ -26,7 +25,7 @@
|
|
|
26
25
|
"typedoc-plugin-markdown": "^4.6.3",
|
|
27
26
|
"vite": "^8.0.8",
|
|
28
27
|
"vite-plugin-dts": "^4.5.4",
|
|
29
|
-
"vitest": "^4.1.
|
|
28
|
+
"vitest": "^4.1.8"
|
|
30
29
|
},
|
|
31
30
|
"exports": {
|
|
32
31
|
".": {
|
|
@@ -77,18 +76,37 @@
|
|
|
77
76
|
],
|
|
78
77
|
"knip": {
|
|
79
78
|
"ignoreBinaries": [
|
|
79
|
+
"attw",
|
|
80
80
|
"knip",
|
|
81
81
|
"jscpd",
|
|
82
82
|
"eslint",
|
|
83
83
|
"tsgo"
|
|
84
84
|
],
|
|
85
85
|
"entry": [
|
|
86
|
+
"src/index.ts",
|
|
87
|
+
"src/server/index.ts",
|
|
88
|
+
"src/router/index.ts",
|
|
89
|
+
"src/form/index.ts",
|
|
90
|
+
"src/hooks/index.ts",
|
|
91
|
+
"src/plugins/framework/index.ts",
|
|
92
|
+
"src/plugins/devtools/index.ts",
|
|
86
93
|
"src/plugins/devtools/ui/index.ts"
|
|
87
|
-
]
|
|
94
|
+
],
|
|
95
|
+
"ignore": [
|
|
96
|
+
"eslint.config.js"
|
|
97
|
+
],
|
|
98
|
+
"ignoreDependencies": [
|
|
99
|
+
"@picked/analytics",
|
|
100
|
+
"@picked/client",
|
|
101
|
+
"@repo/eslint-config",
|
|
102
|
+
"@repo/typescript-config",
|
|
103
|
+
"@tsconfig/svelte"
|
|
104
|
+
],
|
|
105
|
+
"ignoreExportsUsedInFile": true
|
|
88
106
|
},
|
|
89
107
|
"module": "./dist/index.js",
|
|
90
108
|
"peerDependencies": {
|
|
91
|
-
"@picked/client": "0.0.
|
|
109
|
+
"@picked/client": "0.0.63-next.1",
|
|
92
110
|
"effector": "^23.3.0",
|
|
93
111
|
"svelte": "^5.46.4"
|
|
94
112
|
},
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
<!-- Logo.svelte -->
|
|
2
|
-
<script lang="ts">
|
|
3
|
-
export let width = 115
|
|
4
|
-
export let height = 40
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<svg {width} {height} viewBox="0 0 115 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
8
|
-
<path
|
|
9
|
-
d="M50.9328 16.3978C51.3237 15.8476 51.8595 15.3915 52.5401 15.0295C53.2351 14.653 54.0242 14.4648 54.9075 14.4648C55.9356 14.4648 56.8623 14.7182 57.6877 15.225C58.5275 15.7318 59.1863 16.4558 59.6642 17.397C60.1565 18.3237 60.4026 19.4024 60.4026 20.6332C60.4026 21.864 60.1565 22.9572 59.6642 23.9129C59.1863 24.8541 58.5275 25.5853 57.6877 26.1066C56.8623 26.6279 55.9356 26.8885 54.9075 26.8885C54.0242 26.8885 53.2423 26.7075 52.5618 26.3455C51.8957 25.9835 51.3527 25.5274 50.9328 24.9772V32.4271H47.892V14.6603H50.9328V16.3978ZM57.2967 20.6332C57.2967 19.9092 57.1447 19.2866 56.8406 18.7653C56.551 18.2296 56.16 17.8241 55.6677 17.549C55.1899 17.2739 54.6686 17.1363 54.1039 17.1363C53.5536 17.1363 53.0324 17.2811 52.5401 17.5707C52.0622 17.8458 51.6713 18.2513 51.3672 18.787C51.0776 19.3228 50.9328 19.9527 50.9328 20.6767C50.9328 21.4007 51.0776 22.0305 51.3672 22.5663C51.6713 23.102 52.0622 23.5147 52.5401 23.8043C53.0324 24.0794 53.5536 24.217 54.1039 24.217C54.6686 24.217 55.1899 24.0722 55.6677 23.7826C56.16 23.493 56.551 23.0803 56.8406 22.5446C57.1447 22.0088 57.2967 21.3717 57.2967 20.6332Z"
|
|
10
|
-
fill="#1E293B"
|
|
11
|
-
/>
|
|
12
|
-
<path
|
|
13
|
-
d="M63.0684 13.2267C62.5327 13.2267 62.0838 13.0602 61.7218 12.7272C61.3743 12.3797 61.2005 11.9525 61.2005 11.4457C61.2005 10.9389 61.3743 10.519 61.7218 10.186C62.0838 9.83845 62.5327 9.66469 63.0684 9.66469C63.6042 9.66469 64.0458 9.83845 64.3933 10.186C64.7553 10.519 64.9363 10.9389 64.9363 11.4457C64.9363 11.9525 64.7553 12.3797 64.3933 12.7272C64.0458 13.0602 63.6042 13.2267 63.0684 13.2267ZM64.5671 14.6603V26.6931L61.5263 26.6931V14.6603H64.5671Z"
|
|
14
|
-
fill="#1E293B"
|
|
15
|
-
/>
|
|
16
|
-
<path
|
|
17
|
-
d="M65.7035 20.6767C65.7035 19.4314 65.9569 18.3454 66.4637 17.4187C66.9705 16.4775 67.6727 15.7535 68.5705 15.2467C69.4683 14.7254 70.4963 14.4648 71.6547 14.4648C73.1462 14.4648 74.3769 14.8413 75.3471 15.5942C76.3317 16.3327 76.9906 17.3752 77.3236 18.7219H74.0439C73.8701 18.2006 73.5733 17.7952 73.1534 17.5056C72.748 17.2015 72.2412 17.0494 71.633 17.0494C70.7642 17.0494 70.0764 17.368 69.5696 18.0051C69.0628 18.6278 68.8094 19.5183 68.8094 20.6767C68.8094 21.8206 69.0628 22.7111 69.5696 23.3482C70.0764 23.9708 70.7642 24.2822 71.633 24.2822C72.8638 24.2822 73.6674 23.7319 74.0439 22.6314H77.3236C76.9906 23.9346 76.3317 24.97 75.3471 25.7374C74.3625 26.5048 73.1317 26.8885 71.6547 26.8885C70.4963 26.8885 69.4683 26.6351 68.5705 26.1283C67.6727 25.6071 66.9705 24.8831 66.4637 23.9564C65.9569 23.0152 65.7035 21.9219 65.7035 20.6767Z"
|
|
18
|
-
fill="#1E293B"
|
|
19
|
-
/>
|
|
20
|
-
<path
|
|
21
|
-
d="M85.3722 26.6931L81.2888 21.5672V26.6931H78.2481V10.6204H81.2888V19.7644L85.3287 14.6603L89.2817 14.6603L83.9821 20.6984L89.3252 26.6931H85.3722Z"
|
|
22
|
-
fill="#1E293B"
|
|
23
|
-
/>
|
|
24
|
-
<path
|
|
25
|
-
d="M100.163 20.416C100.163 20.8504 100.134 21.2414 100.076 21.5889H91.2796C91.352 22.4577 91.6561 23.1382 92.1919 23.6306C92.7276 24.1229 93.3865 24.369 94.1684 24.369C95.2978 24.369 96.1014 23.884 96.5793 22.9138H99.859C99.5115 24.0722 98.8454 25.0279 97.8608 25.7808C96.8761 26.5193 95.667 26.8885 94.2335 26.8885C93.0751 26.8885 92.0326 26.6351 91.1059 26.1283C90.1936 25.6071 89.4769 24.8758 88.9556 23.9346C88.4488 22.9934 88.1954 21.9075 88.1954 20.6767C88.1954 19.4314 88.4488 18.3382 88.9556 17.397C89.4624 16.4558 90.1719 15.7318 91.0842 15.225C91.9964 14.7182 93.0462 14.4648 94.2335 14.4648C95.3775 14.4648 96.3983 14.7109 97.296 15.2033C98.2083 15.6956 98.9106 16.3978 99.4029 17.3101C99.9097 18.2078 100.163 19.2431 100.163 20.416ZM97.0137 19.5472C96.9992 18.7653 96.7168 18.1427 96.1666 17.6793C95.6164 17.2015 94.9431 16.9626 94.1467 16.9626C93.3937 16.9626 92.7566 17.1942 92.2353 17.6576C91.7285 18.1065 91.4172 18.7364 91.3014 19.5472H97.0137Z"
|
|
26
|
-
fill="#1E293B"
|
|
27
|
-
/>
|
|
28
|
-
<path
|
|
29
|
-
d="M101.166 20.6332C101.166 19.4169 101.405 18.3382 101.883 17.397C102.375 16.4558 103.041 15.7318 103.881 15.225C104.721 14.7182 105.655 14.4648 106.683 14.4648C107.465 14.4648 108.211 14.6385 108.92 14.9861C109.63 15.3191 110.194 15.768 110.614 16.3327V10.6204L113.699 10.6204V26.6931H110.614V24.912C110.238 25.5057 109.709 25.9835 109.029 26.3455C108.348 26.7075 107.559 26.8885 106.661 26.8885C105.648 26.8885 104.721 26.6279 103.881 26.1066C103.041 25.5853 102.375 24.8541 101.883 23.9129C101.405 22.9572 101.166 21.864 101.166 20.6332ZM110.636 20.6767C110.636 19.9382 110.491 19.3083 110.202 18.787C109.912 18.2513 109.521 17.8458 109.029 17.5707C108.537 17.2811 108.008 17.1363 107.443 17.1363C106.879 17.1363 106.357 17.2739 105.879 17.549C105.402 17.8241 105.011 18.2296 104.707 18.7653C104.417 19.2866 104.272 19.9092 104.272 20.6332C104.272 21.3572 104.417 21.9943 104.707 22.5446C105.011 23.0803 105.402 23.493 105.879 23.7826C106.372 24.0722 106.893 24.217 107.443 24.217C108.008 24.217 108.537 24.0794 109.029 23.8043C109.521 23.5147 109.912 23.1093 110.202 22.588C110.491 22.0523 110.636 21.4151 110.636 20.6767Z"
|
|
30
|
-
fill="#1E293B"
|
|
31
|
-
/>
|
|
32
|
-
<g opacity="0.5">
|
|
33
|
-
<ellipse opacity="0.8" cx="32.5807" cy="7.38424" rx="6.29832" ry="6.29832" fill="#01AD7A" />
|
|
34
|
-
<circle opacity="0.6" cx="31.4947" cy="8.47015" r="6.29832" fill="#01AD7A" />
|
|
35
|
-
<ellipse opacity="0.4" cx="30.4088" cy="9.55607" rx="6.29832" ry="6.29832" fill="#01AD7A" />
|
|
36
|
-
<ellipse opacity="0.2" cx="29.3229" cy="10.642" rx="6.29832" ry="6.29832" fill="#01AD7A" />
|
|
37
|
-
<circle opacity="0.1" cx="28.237" cy="11.7279" r="6.29832" fill="#01AD7A" />
|
|
38
|
-
</g>
|
|
39
|
-
<ellipse cx="33.6664" cy="6.29832" rx="6.29832" ry="6.29832" fill="#00CD90" />
|
|
40
|
-
<path
|
|
41
|
-
fill-rule="evenodd"
|
|
42
|
-
clip-rule="evenodd"
|
|
43
|
-
d="M12.5997 16.1802C14.5788 16.1802 16.1832 14.5758 16.1832 12.5967C16.1832 10.6175 14.5788 9.01315 12.5997 9.01315C10.6206 9.01315 9.01617 10.6175 9.01617 12.5967C9.01617 14.5758 10.6206 16.1802 12.5997 16.1802ZM12.5997 18.895C16.0782 18.895 18.898 16.0751 18.898 12.5967C18.898 9.11821 16.0782 6.29836 12.5997 6.29836C9.12123 6.29836 6.30138 9.11821 6.30138 12.5967C6.30138 16.0751 9.12123 18.895 12.5997 18.895Z"
|
|
44
|
-
fill="#1549FF"
|
|
45
|
-
/>
|
|
46
|
-
<path
|
|
47
|
-
fill-rule="evenodd"
|
|
48
|
-
clip-rule="evenodd"
|
|
49
|
-
d="M12.5997 30.9486C14.5788 30.9486 16.1832 29.3442 16.1832 27.3651C16.1832 25.386 14.5788 23.7816 12.5997 23.7816C10.6206 23.7816 9.01617 25.386 9.01617 27.3651C9.01617 29.3442 10.6206 30.9486 12.5997 30.9486ZM12.5997 33.6634C16.0782 33.6634 18.898 30.8436 18.898 27.3651C18.898 23.8866 16.0782 21.0668 12.5997 21.0668C9.12123 21.0668 6.30138 23.8866 6.30138 27.3651C6.30138 30.8436 9.12123 33.6634 12.5997 33.6634Z"
|
|
50
|
-
fill="#1549FF"
|
|
51
|
-
/>
|
|
52
|
-
<path
|
|
53
|
-
fill-rule="evenodd"
|
|
54
|
-
clip-rule="evenodd"
|
|
55
|
-
d="M27.3683 30.9486C29.3474 30.9486 30.9518 29.3442 30.9518 27.3651C30.9518 25.386 29.3474 23.7816 27.3683 23.7816C25.3891 23.7816 23.7847 25.386 23.7847 27.3651C23.7847 29.3442 25.3891 30.9486 27.3683 30.9486ZM27.3683 33.6634C30.8467 33.6634 33.6666 30.8436 33.6666 27.3651C33.6666 23.8866 30.8467 21.0668 27.3683 21.0668C23.8898 21.0668 21.0699 23.8866 21.0699 27.3651C21.0699 30.8436 23.8898 33.6634 27.3683 33.6634Z"
|
|
56
|
-
fill="#1549FF"
|
|
57
|
-
/>
|
|
58
|
-
</svg>
|