@picked/svelte 0.0.1-dev.1771981674305 → 0.0.1-dev.1772057362040
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/component.d.ts +3 -3
- package/dist/form/build-responses.d.ts +13 -0
- package/dist/form/build-responses.js +1 -0
- package/dist/form/create-field-evaluator.d.ts +17 -0
- package/dist/form/create-field-evaluator.js +1 -0
- package/dist/form/create-field-validation.d.ts +41 -0
- package/dist/form/create-field-validation.js +1 -0
- package/dist/form/create-form.js +1 -0
- package/dist/form/create-form.svelte.d.ts +41 -0
- package/dist/form/create-form.svelte.js +1 -0
- package/dist/form/create-submit-pipeline.js +1 -0
- package/dist/form/create-submit-pipeline.svelte.d.ts +53 -0
- package/dist/form/create-submit-pipeline.svelte.js +1 -0
- package/dist/form/get-form.d.ts +16 -0
- package/dist/form/get-form.js +1 -0
- package/dist/form/index.d.ts +2 -16
- package/dist/form/index.js +1 -1
- package/dist/hooks/field.d.ts +5 -0
- package/dist/hooks/step.d.ts +8 -8
- package/dist/hooks/step.js +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +25 -173
- package/dist/plugins/devtools/components/ExperienceInstanceListItem.svelte +1 -1
- package/dist/plugins/devtools/components/ExperienceInstancesTable.svelte +10 -14
- package/dist/plugins/devtools/experience.d.ts +1 -0
- package/dist/plugins/devtools/experience.js +1 -0
- package/dist/plugins/devtools/plugin.d.ts +4 -12
- package/dist/plugins/devtools/plugin.js +1 -1
- package/dist/plugins/devtools/screens/components/ComponentsListScreen.svelte +13 -17
- package/dist/plugins/devtools/screens/events/EventsTable.svelte +20 -24
- package/dist/plugins/devtools/screens/experience-instances/ExperienceInstancesListScreen.svelte +3 -1
- package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +25 -29
- package/dist/plugins/devtools/screens/experiences/ExperienceExperienceInstancesListScreen.svelte +3 -1
- package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +2 -2
- package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +9 -11
- package/dist/plugins/devtools/screens/frameworks/FrameworksListScreen.svelte +12 -16
- package/dist/plugins/devtools/screens/history/HistoryEntriesTable.svelte +10 -14
- package/dist/plugins/devtools/screens/predicates/PredicatesListScreen.svelte +12 -16
- package/dist/plugins/devtools/screens/project/ProjectScreen.svelte +1 -47
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +10 -9
- package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +24 -26
- package/dist/plugins/devtools/ui/actions/Button.svelte +0 -4
- package/dist/plugins/devtools/ui/data/DataTable.svelte +3 -13
- package/dist/plugins/devtools/ui/tokens.css.js +1 -0
- package/dist/render/browser.js +1 -1
- package/dist/render/server.js +1 -1
- package/package.json +2 -2
- package/dist/_virtual/preload-helper.js +0 -1
- package/dist/plugins/devtools/PickedIcon.svelte +0 -19
package/dist/component.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { AdaptedComponent, AdaptedComponentStyle
|
|
1
|
+
import { AdaptedComponent, AdaptedComponentStyle } from '@picked/client';
|
|
2
2
|
import { Component } from 'svelte';
|
|
3
3
|
export type ComponentWithProps = Component<any>;
|
|
4
4
|
export declare const adaptSvelteComponent: (component: ComponentWithProps | Promise<{
|
|
5
5
|
default: ComponentWithProps;
|
|
6
|
-
}
|
|
6
|
+
}>, style?: AdaptedComponentStyle) => AdaptedComponent<"svelte", ComponentWithProps | Promise<{
|
|
7
7
|
default: ComponentWithProps;
|
|
8
|
-
}
|
|
8
|
+
}>>;
|
|
9
9
|
export declare const isValidAdaptedComponent: (component: AdaptedComponent<string, unknown>) => component is AdaptedComponent<"svelte", ComponentWithProps>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FieldBase, FieldValue } from '@picked/client';
|
|
2
|
+
type FieldLike = Pick<FieldBase, 'id' | 'type' | 'multiple'>;
|
|
3
|
+
/**
|
|
4
|
+
* Parses a FormData snapshot into a typed record of field responses.
|
|
5
|
+
*
|
|
6
|
+
* - Fields absent from FormData are mapped to `null`.
|
|
7
|
+
* - Empty string values are mapped to `null`.
|
|
8
|
+
* - Repeated entries for the same field (e.g. multi-select checkboxes) are
|
|
9
|
+
* merged into a typed array — matching the field's declared element type.
|
|
10
|
+
* - Values are coerced to the field's declared type (text → string, number → number, boolean → boolean).
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildResponsesFromFormData(fields: FieldLike[], formData: FormData): Record<string, FieldValue>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function i(e,o,n){const r=e[o];if(r===void 0){e[o]=n;return}if(Array.isArray(r)&&Array.isArray(n)&&n.length>0){typeof r[0]=="string"?e[o]=[...r,...n].map(String):typeof r[0]=="number"?e[o]=[...r,...n].map(Number):typeof r[0]=="boolean"?e[o]=[...r,...n].map(t=>!!t):e[o]=n;return}e[o]=n}function b(e,o){const n=Array.from(o.entries()),r={};return e.forEach(t=>{let m=n.filter(([s])=>s===t.id);m.length===0&&(m=[[t.id,""]]),m.forEach(([,s])=>{if(s===""){i(r,t.id,null);return}switch(t.type){case"text":case"object":i(r,t.id,t.multiple?[String(s)]:String(s));break;case"number":i(r,t.id,t.multiple?[Number(s)]:Number(s));break;case"boolean":i(r,t.id,t.multiple?[s==="true"]:s==="true");break}})}),r}export{b as buildResponsesFromFormData};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FieldValue, FieldWithStatus, Picked } from '@picked/client';
|
|
2
|
+
import { Store, StoreWritable } from 'effector';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a reactive store of evaluated fields for a given set of field IDs.
|
|
5
|
+
*
|
|
6
|
+
* Fields are re-evaluated whenever:
|
|
7
|
+
* - The available field IDs change (e.g. a step is entered)
|
|
8
|
+
* - Any response is committed (options may depend on prior answers)
|
|
9
|
+
* - The global field registry changes (fields registered after init)
|
|
10
|
+
* - Staged values change (field visibility conditions may reference responses.*)
|
|
11
|
+
*
|
|
12
|
+
* Evaluation runs `getStepFields` with effective responses (committed + staged) as context,
|
|
13
|
+
* so visibility conditions see live in-session values rather than only committed responses.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createFieldEvaluator(client: Picked, fieldIds: Store<string[]>, stagedValuesStore: StoreWritable<Record<string, FieldValue>>): {
|
|
16
|
+
fieldsStore: Store<FieldWithStatus[]>;
|
|
17
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getStepFields as m}from"@picked/client";import{createStore as u,attach as F,createEffect as S,sample as i}from"effector";function k(e,r,s){const o=u([]),t=F({source:{allFields:e.fields.listStore,fieldIds:r,staged:s,committed:e.responses.getAllStore},effect:S(async({allFields:l,fieldIds:a,staged:c,committed:d})=>{const f=l.filter(p=>a.includes(p.id)),n={...d,...c};return await m(e,f,{responses:n})})});return i({clock:t.doneData,target:o}),t(),i({clock:[e.responses.listStore,r,e.fields.listStore,s],target:t}),{fieldsStore:o}}export{k as createFieldEvaluator};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FieldValue, FieldWithStatus, Picked, ValidationError } from '@picked/client';
|
|
2
|
+
import { EventCallable, Store, StoreWritable } from 'effector';
|
|
3
|
+
export interface FieldValidation {
|
|
4
|
+
/** Dispatch when a field value changes — runs validation if the field has been touched. */
|
|
5
|
+
fieldChangeEvent: EventCallable<{
|
|
6
|
+
fieldId: string;
|
|
7
|
+
value: FieldValue;
|
|
8
|
+
}>;
|
|
9
|
+
/** Dispatch when a field loses focus — always runs full validation including required check. */
|
|
10
|
+
fieldBlurEvent: EventCallable<{
|
|
11
|
+
fieldId: string;
|
|
12
|
+
value: FieldValue;
|
|
13
|
+
}>;
|
|
14
|
+
/** Which fields have been interacted with — used by formFieldsStore to show/hide errors. */
|
|
15
|
+
touchedStore: StoreWritable<Partial<Record<string, boolean>>>;
|
|
16
|
+
/**
|
|
17
|
+
* Per-field validation errors — written by client-side validation and overwritten
|
|
18
|
+
* by server errors on commit failure. Exposed as writable so the submit pipeline
|
|
19
|
+
* can populate server errors and reset state on new submit attempts.
|
|
20
|
+
*/
|
|
21
|
+
fieldErrorsStore: StoreWritable<Partial<Record<string, ValidationError>>>;
|
|
22
|
+
/** Flat list of all current validation errors — derived from fieldErrorsStore. */
|
|
23
|
+
formErrorsStore: Store<ValidationError[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Current in-session field values — updated on every onChange/onBlur before commit.
|
|
26
|
+
* Used as the responses context for condition evaluation so conditions react to
|
|
27
|
+
* live input without writing to client.responses prematurely.
|
|
28
|
+
*/
|
|
29
|
+
stagedValuesStore: StoreWritable<Record<string, FieldValue>>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates the client-side validation slice for a form.
|
|
33
|
+
*
|
|
34
|
+
* Responsibilities:
|
|
35
|
+
* - Tracks which fields have been touched (first blur = touched)
|
|
36
|
+
* - Validates on blur (full: includes required check) and on change (if already touched)
|
|
37
|
+
* - Uses the same json-logic evaluator the server uses, so client and server results match
|
|
38
|
+
* - Maintains stagedValuesStore: current in-session values used as responses context for
|
|
39
|
+
* condition evaluation, avoiding premature writes to client.responses
|
|
40
|
+
*/
|
|
41
|
+
export declare function createFieldValidation(client: Picked, fieldsStore: Store<FieldWithStatus[]>, stagedValuesStore: StoreWritable<Record<string, FieldValue>>): FieldValidation;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{evaluateRuleLogic as M}from"@picked/client";import{createEvent as h,createStore as y,sample as p,attach as q}from"effector";function F(s){return s===null||s===""||Array.isArray(s)&&s.length===0}function R(s,k,c){const f=h(),a=h(),u=y({});p({clock:a,source:u,fn:(r,{fieldId:e})=>({...r,[e]:!0}),target:u});const g=y({}),v=g.map(r=>Object.values(r).filter(e=>e!==void 0)),m=q({source:{fields:k,staged:c,committed:s.responses.getAllStore},effect:async({fields:r,staged:e,committed:n},{fieldId:t,value:i,checkRequired:E})=>{const o=r.find(d=>d.id===t);if(!o)return{fieldId:t,error:null};if(o.validation&&Object.keys(o.validation).length>0){const d={...n,...e},l=await M(s,o.validation,{responses:d,$input:i});if(typeof l=="string")return{fieldId:t,error:{fieldId:t,userMessage:l,systemMessage:l,value:i}};if(l===!1)return{fieldId:t,error:{fieldId:t,userMessage:"This field is invalid",systemMessage:"Field validation rule returned false.",value:i}}}return E&&o.required&&F(i)?{fieldId:t,error:{fieldId:t,userMessage:"This field is required",systemMessage:"Field is required.",value:i}}:{fieldId:t,error:null}}});return p({clock:f,source:u,filter:(r,{fieldId:e})=>r[e]===!0,fn:(r,{fieldId:e,value:n})=>({fieldId:e,value:n,checkRequired:!1}),target:m}),p({clock:a,fn:({fieldId:r,value:e})=>({fieldId:r,value:e,checkRequired:!0}),target:m}),c.on(f,(r,{fieldId:e,value:n})=>({...r,[e]:n})),c.on(a,(r,{fieldId:e,value:n})=>({...r,[e]:n})),g.on(m.doneData,(r,{fieldId:e,error:n})=>{if(n===null){const t={...r};return delete t[e],t}return{...r,[e]:n}}),{fieldChangeEvent:f,fieldBlurEvent:a,touchedStore:u,fieldErrorsStore:g,formErrorsStore:v,stagedValuesStore:c}}export{R as createFieldValidation};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"svelte/internal/client";import{createStore as C,combine as P,attach as j,sample as u}from"effector";import{getPicked as N,getExperience as R}from"../hooks/client.js";import{createFieldEvaluator as W}from"./create-field-evaluator.js";import{createFieldValidation as q}from"./create-field-validation.js";import{createSubmitPipeline as w}from"./create-submit-pipeline.svelte";function x(m){const t=m.currentTarget;if(t instanceof HTMLInputElement)return t.type==="checkbox"?t.checked:t.value;if(t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement)return t.value}function X(m,t,k){const r=N(),g=R(),{currentStepStore:F,goToNextStep:I}=g,d=C({}),{fieldsStore:a}=W(r,k,d),{fieldChangeEvent:E,fieldBlurEvent:T,touchedStore:b,fieldErrorsStore:p,formErrorsStore:A}=q(r,a,d),y=P({fields:a,responses:r.responses.listStore,staged:d,fieldErrors:p,touched:b},({fields:s,responses:o,staged:i,fieldErrors:S,touched:f})=>s.map(e=>({...e,value:i[e.id]??o.find(n=>n.fieldId===e.id)?.value,error:S[e.id],touched:f[e.id]??!1,onChange:n=>{const c=x(n);c!==void 0&&E({fieldId:e.id,value:c})},onBlur:n=>{const c=x(n);c!==void 0&&T({fieldId:e.id,value:c})}}))),D=j({source:{staged:d,committed:r.responses.getAllStore,allFields:r.fields.listStore},effect:({staged:s,committed:o,allFields:i},S)=>{const f={...o,...s};for(const e of S){const n=i.find(v=>v.id===e);if(!n)continue;(n.dependsOn??[]).every(v=>{const l=f[v];return l!=null&&l!==""&&!(Array.isArray(l)&&l.length===0)})?r.fields.resolveAll([e],{force:!0,context:{responses:f}}):r.fields.setWaiting(e)}}});u({clock:E,source:{fields:a},fn:({fields:s},{fieldId:o})=>s.filter(i=>i.dependsOn?.includes(o)).map(i=>i.id),target:D});const{submitEvent:H,isFormSubmitting:L,onSubmit:M,onSuccess:O,onError:V,submitStarted:h,serverErrorsReceived:B}=w({client:r,experienceInstance:g,formId:m,title:t,stepStore:F,fieldsStore:a,goToNextStep:I});return u({clock:h,fn:()=>({}),target:b}),u({clock:h,fn:()=>({}),target:p}),u({clock:B,fn:s=>Object.fromEntries(s.map(o=>[o.fieldId,o])),target:p}),{form:{onsubmit:H,errors:A,submitting:L,onSubmit:M,onSuccess:O,onError:V},fields:y}}export{X as createForm};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FieldValue, FieldWithStatus, ValidationError } from '@picked/client';
|
|
2
|
+
import { EventCallable, Store } from 'effector';
|
|
3
|
+
import { ErrorCallback, SubmitCallback, SubmitEventWithCurrentTarget, SuccessCallback } from './create-submit-pipeline.svelte';
|
|
4
|
+
export type FormField = Omit<FieldWithStatus, 'error'> & {
|
|
5
|
+
value: FieldValue | undefined;
|
|
6
|
+
/** Validation error for this field, or undefined if valid. */
|
|
7
|
+
error: ValidationError | undefined;
|
|
8
|
+
touched: boolean;
|
|
9
|
+
onChange: (e: Event) => void;
|
|
10
|
+
onBlur: (e: Event) => void;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Return type for createForm()
|
|
14
|
+
* Provides form handlers, reactive stores, and callback registration
|
|
15
|
+
*/
|
|
16
|
+
export interface FormInstance<TFieldIds extends string = string> {
|
|
17
|
+
/** Form element props (bind to <form>) */
|
|
18
|
+
form: {
|
|
19
|
+
onsubmit: EventCallable<SubmitEventWithCurrentTarget>;
|
|
20
|
+
/** Flat list of all current validation errors — derived from fieldErrors */
|
|
21
|
+
errors: Store<ValidationError[]>;
|
|
22
|
+
/** Reactive store indicating if form is currently submitting */
|
|
23
|
+
submitting: Store<boolean>;
|
|
24
|
+
/** Register callbacks to transform/validate data before submission */
|
|
25
|
+
onSubmit: (callback: SubmitCallback<TFieldIds>) => void;
|
|
26
|
+
/** Register callbacks to run on successful form submission */
|
|
27
|
+
onSuccess: (callback: SuccessCallback<TFieldIds>) => void;
|
|
28
|
+
/** Register callbacks to run on form validation errors */
|
|
29
|
+
onError: (callback: ErrorCallback) => void;
|
|
30
|
+
};
|
|
31
|
+
/** Reactive store of fields with current values, errors, touched state, and event handlers */
|
|
32
|
+
fields: Store<FormField[]>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a form instance tied to a set of field IDs.
|
|
36
|
+
* Handles field evaluation, form submission, data transformation,
|
|
37
|
+
* validation, event bus tracking, and step navigation.
|
|
38
|
+
*
|
|
39
|
+
* Used internally by setStepContext — access via getStep() in components.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createForm(formId: string, title: Store<string>, fieldIds: Store<string[]>): FormInstance;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './create-form.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as C from"svelte/internal/client";import{createEvent as i,createStore as B,createEffect as l,attach as O,sample as s,split as R}from"effector";import{buildResponsesFromFormData as T}from"./build-responses.js";function k(c){let o=[];return{executeFx:l(async(...a)=>await c(o,...a)),register:a=>{const f=()=>{o=o.filter(p=>p!==a)};C.user_effect(()=>(o.push(a),f))}}}function q({client:c,experienceInstance:o,formId:u,title:m,stepStore:a,fieldsStore:f,goToNextStep:p}){const n=i(),y=i(),v=i(),_=i(),x=i(),d=B(!1),D=B(!1);D.on(_,()=>!0).on(n,()=>!1);const N=n.map(()=>{}),g=k(async(e,t)=>{await Promise.all(e.map(r=>Promise.resolve(r(t))))}),b=k(async(e,t)=>{await Promise.all(e.map(r=>Promise.resolve(r(t))))}),F=k(async(e,t)=>{let r=t;for(const E of e){const w=await Promise.resolve(E(r));w!==void 0&&(r=w)}return r}),P=l(async e=>{const t=await c.responses.commit(e);return{data:e,errors:t.errors}}),h=O({source:{step:a,title:m},mapParams:(e,t)=>({step:t.step,title:t.title,data:e.data}),effect:l(async({step:e,title:t,data:r})=>(c.eventBus({type:"form_submit_success",form:{id:u,title:t},experience:o,step:e??void 0}),r))}),S=O({source:{step:a,title:m},mapParams:(e,t)=>({step:t.step,title:t.title,errors:e.errors}),effect:l(async({step:e,title:t,errors:r})=>(c.eventBus({type:"form_submit_error",form:{id:u,title:t},exception:{reason:r},experience:o,step:e??void 0}),r.forEach(E=>{c.eventBus({type:"field_validation_error",exception:{reason:[E]},experience:o,step:e??void 0})}),r))});return s({clock:n,fn:()=>!0,target:d}),s({clock:n,source:{step:a,title:m},fn:({step:e,title:t})=>({formId:u,title:t,step:e}),target:l(({formId:e,title:t,step:r})=>{c.eventBus({type:"form_submit",form:{id:e,title:t},experience:o,step:r??void 0})})}),s({clock:n,source:{stepFields:f},fn:({stepFields:e},t)=>(t.preventDefault(),T(e,new FormData(t.currentTarget))),target:F.executeFx}),s({clock:F.executeFx.doneData,target:P}),R({source:P.doneData,match:{hasErrors:({errors:e})=>e.length>0},cases:{hasErrors:S,__:h}}),s({clock:h.doneData,target:y}),s({clock:y,fn:e=>({data:e,stopNavigation:_}),target:g.executeFx}),s({clock:g.executeFx.done,target:x}),s({clock:x,source:D,filter:e=>!e,target:p}),s({clock:S.doneData,target:v}),s({clock:v,target:b.executeFx}),s({clock:[x,b.executeFx.done],fn:()=>!1,target:d}),{submitEvent:n,isFormSubmitting:d,onSubmit:F.register,onSuccess:g.register,onError:b.register,submitStarted:N,serverErrorsReceived:v}}export{q as createSubmitPipeline};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ExperienceInstance, ExperienceStep, FieldValue, FieldWithStatus, Picked, ValidationError } from '@picked/client';
|
|
2
|
+
import { Event, EventCallable, Store } from 'effector';
|
|
3
|
+
export type SubmitEventWithCurrentTarget = SubmitEvent & {
|
|
4
|
+
currentTarget: EventTarget & HTMLFormElement;
|
|
5
|
+
};
|
|
6
|
+
export type SuccessCallback<TFieldIds extends string = string> = (options: {
|
|
7
|
+
data: Record<TFieldIds, FieldValue>;
|
|
8
|
+
stopNavigation: EventCallable<void>;
|
|
9
|
+
}) => void | Promise<void>;
|
|
10
|
+
export type ErrorCallback = (errors: ValidationError[]) => void | Promise<void>;
|
|
11
|
+
export type SubmitCallback<TFieldIds extends string = string> = (data: Record<TFieldIds, FieldValue>) => Record<TFieldIds, FieldValue> | void | Promise<Record<TFieldIds, FieldValue> | void>;
|
|
12
|
+
export interface SubmitPipelineConfig {
|
|
13
|
+
client: Picked;
|
|
14
|
+
experienceInstance: ExperienceInstance;
|
|
15
|
+
formId: string;
|
|
16
|
+
title: Store<string>;
|
|
17
|
+
stepStore: Store<ExperienceStep | null>;
|
|
18
|
+
fieldsStore: Store<FieldWithStatus[]>;
|
|
19
|
+
goToNextStep: EventCallable<void>;
|
|
20
|
+
}
|
|
21
|
+
export interface SubmitPipeline {
|
|
22
|
+
submitEvent: EventCallable<SubmitEventWithCurrentTarget>;
|
|
23
|
+
isFormSubmitting: Store<boolean>;
|
|
24
|
+
onSubmit: (callback: SubmitCallback) => void;
|
|
25
|
+
onSuccess: (callback: SuccessCallback) => void;
|
|
26
|
+
onError: (callback: ErrorCallback) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Fired when a submit attempt starts — callers use this to clear validation state.
|
|
29
|
+
* Separating this from `submitEvent` keeps the pipeline decoupled from the
|
|
30
|
+
* validation module's internal stores.
|
|
31
|
+
*/
|
|
32
|
+
submitStarted: Event<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Fired when the server returns validation errors — callers write these to
|
|
35
|
+
* `fieldErrorsStore` in the validation module.
|
|
36
|
+
*/
|
|
37
|
+
serverErrorsReceived: Event<ValidationError[]>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates the submit pipeline for a form instance.
|
|
41
|
+
*
|
|
42
|
+
* Manages the full lifecycle of a form submission:
|
|
43
|
+
* - Emitting event bus calls (form_submit, form_submit_success, form_submit_error)
|
|
44
|
+
* - Running onSubmit data-transform callbacks in series
|
|
45
|
+
* - Committing responses to the server
|
|
46
|
+
* - Running onSuccess/onError callbacks in parallel
|
|
47
|
+
* - Navigating to the next step (unless stopNavigation is called)
|
|
48
|
+
*
|
|
49
|
+
* Returns coordination events (`submitStarted`, `serverErrorsReceived`) that the
|
|
50
|
+
* caller wires to the validation module to keep validation state in sync without
|
|
51
|
+
* the pipeline needing direct access to validation stores.
|
|
52
|
+
*/
|
|
53
|
+
export declare function createSubmitPipeline({ client, experienceInstance, formId, title, stepStore, fieldsStore, goToNextStep, }: SubmitPipelineConfig): SubmitPipeline;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './create-submit-pipeline.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ValidationError } from '@picked/client';
|
|
2
|
+
type SubmitEventWithCurrentTarget = SubmitEvent & {
|
|
3
|
+
currentTarget: EventTarget & HTMLFormElement;
|
|
4
|
+
};
|
|
5
|
+
interface GetFormOptions {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
onSuccess?: () => void | Promise<void>;
|
|
9
|
+
onError?: (errors: ValidationError[]) => void | Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function getForm({ id: formId, title, onError, onSuccess }: GetFormOptions): {
|
|
12
|
+
form: {
|
|
13
|
+
onsubmit: import('effector').EventCallable<SubmitEventWithCurrentTarget>;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createEvent as g,createEffect as s,attach as u,sample as f,split as E}from"effector";import{getPicked as F,getExperience as b}from"../hooks/client.js";import"@picked/client";import{getStep as h}from"../hooks/step.js";import"svelte";import"svelte/store";import"@picked/analytics";import{buildResponsesFromFormData as S}from"./build-responses.js";function j({id:c,title:n,onError:d,onSuccess:l}){const r=F(),x=h(),o=b(),{currentStepStore:i}=o,p=g(),a=s(e=>r.responses.commit(e)),v=u({source:{step:i},effect:s(({step:e})=>{r.eventBus({type:"form_submit_success",form:{id:c,title:n},experience:o,step:e??void 0}),l?.()})}),_=u({source:{step:i},mapParams:(e,t)=>({step:t.step,errors:e.errors}),effect:s(({step:e,errors:t})=>{r.eventBus({type:"form_submit_error",form:{id:c,title:n},exception:{reason:t},experience:o,step:e??void 0}),t.forEach(m=>{r.eventBus({type:"field_validation_error",exception:{reason:[m]},experience:o,step:e??void 0})}),d?.(t)})});return f({clock:p,source:{step:i},fn:({step:e})=>({formId:c,title:n,step:e}),target:s(({formId:e,title:t,step:m})=>{r.eventBus({type:"form_submit",form:{id:e,title:t},experience:o,step:m??void 0})})}),f({clock:p,source:{stepFields:x.fields},fn:({stepFields:e},t)=>(t.preventDefault(),S(e,new FormData(t.currentTarget))),target:a}),E({source:a.doneData,match:{hasErrors:({errors:e})=>e.length>0},cases:{hasErrors:_,__:v}}),{form:{onsubmit:p}}}export{j as getForm};
|
package/dist/form/index.d.ts
CHANGED
|
@@ -1,16 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
id: string;
|
|
4
|
-
title: string;
|
|
5
|
-
onSuccess?: () => void | Promise<void>;
|
|
6
|
-
onError?: (errors: ValidationError[]) => void | Promise<void>;
|
|
7
|
-
}
|
|
8
|
-
type SubmitEventWithCurrentTarget = SubmitEvent & {
|
|
9
|
-
currentTarget: EventTarget & HTMLFormElement;
|
|
10
|
-
};
|
|
11
|
-
export declare function getForm({ id: formId, title, onError, onSuccess }: GetFormOptions): {
|
|
12
|
-
form: {
|
|
13
|
-
onsubmit: import('effector').EventCallable<SubmitEventWithCurrentTarget>;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
export {};
|
|
1
|
+
export type { FormField } from './create-form.svelte';
|
|
2
|
+
export { getForm } from './get-form';
|
package/dist/form/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{getForm as e}from"./get-form.js";export{e as getForm};
|
package/dist/hooks/field.d.ts
CHANGED
|
@@ -4,4 +4,9 @@ export interface GetFieldResponse {
|
|
|
4
4
|
value: Store<FieldValue>;
|
|
5
5
|
field: Store<Field | null>;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Field options and status are now available directly on the Field object
|
|
9
|
+
* from the step fields store. Use `field.options`, `field.status`, and `field.error`
|
|
10
|
+
* instead of accessing them through this hook.
|
|
11
|
+
*/
|
|
7
12
|
export declare function getField(id: string): GetFieldResponse;
|
package/dist/hooks/step.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Store } from 'effector';
|
|
2
|
-
import { ExperienceStep
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
export interface StepContext {
|
|
2
|
+
import { ExperienceStep } from '@picked/client';
|
|
3
|
+
import { FormInstance, FormField } from '../form/create-form.svelte';
|
|
4
|
+
export interface StepContext extends FormInstance {
|
|
7
5
|
step: Store<ExperienceStep>;
|
|
8
|
-
fields
|
|
6
|
+
/** Directly accessible fields with current response values — usable without a form */
|
|
7
|
+
fields: Store<FormField[]>;
|
|
8
|
+
isActive: Store<boolean>;
|
|
9
|
+
/** @deprecated Use `isActive` instead */
|
|
9
10
|
isActiveStep: Store<boolean>;
|
|
10
11
|
}
|
|
11
|
-
export declare function getStep(): StepContext;
|
|
12
12
|
export declare function setStepContext(stepId: string): StepContext;
|
|
13
|
-
export
|
|
13
|
+
export declare function getStep(): StepContext;
|
package/dist/hooks/step.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getContext as
|
|
1
|
+
import{getContext as x,onMount as h,setContext as A}from"svelte";import{createEffect as S,createEvent as E,sample as p}from"effector";import{STEP_CONTEXT_KEY as g}from"../context.js";import{createForm as O}from"../form/create-form.svelte";import{getStepRouter as k}from"../router/context.js";import{getPicked as C,getExperience as I}from"./client.js";function _(c){const s=I().treeStore.map(i=>{const d=i.steps.find(f=>f.id===c);if(!d)throw new Error(`Step with id "${c}" not found in the experience tree.`);return d}),l=s.map(i=>i.label),u=s.map(i=>i.fieldIds),n=O(c,l,u);return{stepStore:s,form:n}}function P(c){const{activeStep:a}=k(),s=a.map(e=>e?.id===c),{stepStore:l,form:u}=_(c),n=C(),i=S(e=>{n.fields.resolveAll(e)});function d(e){if(!e.dependsOn?.length)return!0;const r=n.responses.getAllStore.getState();return e.dependsOn.every(o=>{const t=r[o];return t!=null&&t!==""&&!(Array.isArray(t)&&t.length===0)})}const f=E();p({clock:f,source:{step:l,fields:n.fields.listStore},fn:({step:e,fields:r})=>e.fieldIds.filter(o=>!r.find(t=>t.id===o)?.dependsOn?.length),target:i});const m=S(({step:e,fields:r})=>{for(const o of e.fieldIds){const t=r.find(v=>v.id===o);t?.dependsOn?.length&&(d(t)?n.fields.resolveAll([o],{force:!0}):n.fields.setWaiting(o))}});return p({clock:f,source:{step:l,fields:n.fields.listStore},target:m}),h(()=>{f()}),p({clock:s,source:{step:l,fields:n.fields.listStore},filter:(e,r)=>r,fn:({step:e,fields:r})=>e.fieldIds.filter(o=>!r.find(t=>t.id===o)?.dependsOn?.length),target:i}),p({clock:s,source:{step:l,fields:n.fields.listStore},filter:(e,r)=>r,target:m}),A(g,{step:l,isActive:s,isActiveStep:s,...u})}function K(){return x(g)}export{K as getStep,P as setStepContext};
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
the change in the paired React component to maintain visual and behavioral parity.
|
|
7
7
|
-->
|
|
8
8
|
<script lang="ts">
|
|
9
|
-
import { getExperience
|
|
10
|
-
import { Button
|
|
9
|
+
import { getExperience } from '../../hooks'
|
|
10
|
+
import { Button } from './ui'
|
|
11
|
+
import PickedLogo from './PickedLogo.svelte'
|
|
11
12
|
import Tabs from './components/Tabs.svelte'
|
|
12
13
|
import ExperiencesListScreen from './screens/experiences/ExperiencesListScreen.svelte'
|
|
13
14
|
import ExperienceInstancesListScreen from './screens/experience-instances/ExperienceInstancesListScreen.svelte'
|
|
@@ -19,12 +20,8 @@
|
|
|
19
20
|
import ResponsesListScreen from './screens/responses/ResponsesListScreen.svelte'
|
|
20
21
|
import FeaturesListScreen from './screens/features/FeaturesListScreen.svelte'
|
|
21
22
|
import EventsListScreen from './screens/events/EventsListScreen.svelte'
|
|
22
|
-
import PickedIcon from './PickedIcon.svelte'
|
|
23
23
|
|
|
24
24
|
const experience = getExperience()
|
|
25
|
-
const client = getPicked()
|
|
26
|
-
|
|
27
|
-
const dashboardProjectUrl = `${client.dashboardUrl}/p/${client.projectId}`
|
|
28
25
|
|
|
29
26
|
const tabs = [
|
|
30
27
|
{
|
|
@@ -104,15 +101,10 @@
|
|
|
104
101
|
<div class="container">
|
|
105
102
|
<header class="toolbar" id="devtools-header">
|
|
106
103
|
<div class="toolbar-left">
|
|
107
|
-
<
|
|
108
|
-
<PickedIcon height={25} width={25}/>
|
|
109
|
-
<Heading size="small">Developer Tools</Heading>
|
|
110
|
-
</Stack>
|
|
104
|
+
<PickedLogo width={80} height={28} />
|
|
111
105
|
</div>
|
|
112
106
|
<div class="toolbar-center"></div>
|
|
113
107
|
<div class="toolbar-right">
|
|
114
|
-
<Button variant="ghost" size="small" href={client.documentationSiteUrl} target="_blank">Docs</Button>
|
|
115
|
-
<Button variant="ghost" size="small" href={dashboardProjectUrl} target="_blank">Dashboard</Button>
|
|
116
108
|
<Button variant="ghost" size="small" onclick={() => experience.close()}>Close</Button>
|
|
117
109
|
</div>
|
|
118
110
|
</header>
|
|
@@ -126,9 +118,9 @@
|
|
|
126
118
|
class="floating-button"
|
|
127
119
|
onclick={() => experience.open()}
|
|
128
120
|
aria-label="Open Picked Devtools"
|
|
129
|
-
title="Open Picked Devtools"
|
|
121
|
+
title="Open Picked Devtools (or press .)"
|
|
130
122
|
>
|
|
131
|
-
<
|
|
123
|
+
<PickedLogo />
|
|
132
124
|
</button>
|
|
133
125
|
{/if}
|
|
134
126
|
|
|
@@ -139,80 +131,6 @@
|
|
|
139
131
|
}
|
|
140
132
|
|
|
141
133
|
.devtools {
|
|
142
|
-
/* Colors — match Tailwind's default palette exactly */
|
|
143
|
-
--color-blue-500: #3b82f6;
|
|
144
|
-
--color-blue-600: #2563eb;
|
|
145
|
-
--color-blue-700: #1d4ed8;
|
|
146
|
-
--color-blue-800: #1e40af;
|
|
147
|
-
--color-gray-50: #f9fafb;
|
|
148
|
-
--color-gray-100: #f3f4f6;
|
|
149
|
-
--color-gray-200: #e5e7eb;
|
|
150
|
-
--color-gray-300: #d1d5db;
|
|
151
|
-
--color-gray-500: #6b7280;
|
|
152
|
-
--color-gray-600: #4b5563;
|
|
153
|
-
--color-gray-700: #374151;
|
|
154
|
-
--color-gray-900: #111827;
|
|
155
|
-
--color-red-500: #ef4444;
|
|
156
|
-
--color-red-600: #dc2626;
|
|
157
|
-
--color-red-700: #b91c1c;
|
|
158
|
-
--color-red-800: #991b1b;
|
|
159
|
-
--color-green-100: #dcfce7;
|
|
160
|
-
--color-green-700: #15803d;
|
|
161
|
-
--color-green-800: #166534;
|
|
162
|
-
--color-yellow-100: #fef9c3;
|
|
163
|
-
--color-yellow-700: #a16207;
|
|
164
|
-
--color-yellow-800: #854d0e;
|
|
165
|
-
--color-white: #ffffff;
|
|
166
|
-
--color-black: #000000;
|
|
167
|
-
|
|
168
|
-
/* Typography — match Tailwind's type scale */
|
|
169
|
-
--text-xs: 0.75rem;
|
|
170
|
-
--text-sm: 0.875rem;
|
|
171
|
-
--text-base: 1rem;
|
|
172
|
-
--text-lg: 1.125rem;
|
|
173
|
-
--text-xl: 1.25rem;
|
|
174
|
-
--text-2xl: 1.5rem;
|
|
175
|
-
--font-normal: 400;
|
|
176
|
-
--font-medium: 500;
|
|
177
|
-
--font-semibold: 600;
|
|
178
|
-
|
|
179
|
-
/* Spacing — match Tailwind's spacing scale */
|
|
180
|
-
--space-0: 0;
|
|
181
|
-
--space-0-5: 0.125rem;
|
|
182
|
-
--space-1: 0.25rem;
|
|
183
|
-
--space-1-5: 0.375rem;
|
|
184
|
-
--space-2: 0.5rem;
|
|
185
|
-
--space-2-5: 0.625rem;
|
|
186
|
-
--space-3: 0.75rem;
|
|
187
|
-
--space-4: 1rem;
|
|
188
|
-
--space-5: 1.25rem;
|
|
189
|
-
--space-6: 1.5rem;
|
|
190
|
-
--space-8: 2rem;
|
|
191
|
-
|
|
192
|
-
/* Radii */
|
|
193
|
-
--radius-sm: 0.125rem;
|
|
194
|
-
--radius-md: 0.375rem;
|
|
195
|
-
--radius-lg: 0.5rem;
|
|
196
|
-
--radius-xl: 0.75rem;
|
|
197
|
-
--radius-full: 9999px;
|
|
198
|
-
|
|
199
|
-
/* Shadows */
|
|
200
|
-
--shadow-card: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
|
201
|
-
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
|
|
202
|
-
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
203
|
-
|
|
204
|
-
/* Focus ring — matches dashboard's ring-2 ring-blue-500 ring-offset-2 */
|
|
205
|
-
--ring-color: var(--color-blue-500);
|
|
206
|
-
--ring-width: 2px;
|
|
207
|
-
--ring-offset: 2px;
|
|
208
|
-
|
|
209
|
-
/* Base typography reset */
|
|
210
|
-
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
|
211
|
-
'Helvetica Neue', Arial, sans-serif;
|
|
212
|
-
font-size: var(--text-sm);
|
|
213
|
-
line-height: 1.5;
|
|
214
|
-
color: var(--color-gray-900);
|
|
215
|
-
|
|
216
134
|
position: fixed;
|
|
217
135
|
inset: 0;
|
|
218
136
|
z-index: 999999999;
|
|
@@ -266,7 +184,6 @@
|
|
|
266
184
|
.toolbar-right {
|
|
267
185
|
display: flex;
|
|
268
186
|
align-items: center;
|
|
269
|
-
gap: var(--space-1);
|
|
270
187
|
}
|
|
271
188
|
|
|
272
189
|
.main {
|
|
@@ -297,101 +214,36 @@
|
|
|
297
214
|
background-color: var(--color-gray-50);
|
|
298
215
|
}
|
|
299
216
|
|
|
300
|
-
.devtools :global(th:first-child),
|
|
301
|
-
.devtools :global(td:first-child) {
|
|
302
|
-
padding-left: var(--space-4);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.devtools :global(th:last-child),
|
|
306
|
-
.devtools :global(td:last-child) {
|
|
307
|
-
padding-right: var(--space-4);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
217
|
.floating-button {
|
|
311
218
|
position: fixed;
|
|
312
|
-
bottom:
|
|
219
|
+
bottom: 1.5rem;
|
|
313
220
|
right: 1.5rem;
|
|
314
|
-
|
|
221
|
+
width: 4.5rem;
|
|
222
|
+
height: 4.5rem;
|
|
223
|
+
border-radius: var(--radius-full);
|
|
315
224
|
background-color: var(--color-white);
|
|
316
|
-
|
|
317
|
-
|
|
225
|
+
border: 1px solid var(--color-gray-200);
|
|
226
|
+
box-shadow: 0 4px 12px rgba(21, 73, 255, 0.2);
|
|
227
|
+
display: flex;
|
|
228
|
+
align-items: center;
|
|
229
|
+
justify-content: center;
|
|
318
230
|
cursor: pointer;
|
|
319
231
|
z-index: 999999;
|
|
320
|
-
|
|
321
|
-
padding:
|
|
322
|
-
box-shadow: 2px 2px 4px #00000029;
|
|
323
|
-
|
|
324
|
-
&:hover {
|
|
325
|
-
background-color: #f5f5f5;
|
|
326
|
-
}
|
|
232
|
+
transition: all 0.2s ease;
|
|
233
|
+
padding: 0;
|
|
327
234
|
}
|
|
328
235
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
font-size: var(--text-xs);
|
|
333
|
-
padding: var(--space-0-5) var(--space-1);
|
|
334
|
-
border: 1px solid var(--color-gray-300);
|
|
335
|
-
border-radius: var(--radius-sm);
|
|
336
|
-
font-family: inherit;
|
|
236
|
+
.floating-button:hover {
|
|
237
|
+
box-shadow: 0 6px 16px rgba(21, 73, 255, 0.3);
|
|
238
|
+
transform: scale(1.05);
|
|
337
239
|
}
|
|
338
240
|
|
|
339
|
-
.
|
|
340
|
-
|
|
241
|
+
.floating-button:active {
|
|
242
|
+
transform: scale(0.95);
|
|
341
243
|
}
|
|
342
244
|
|
|
343
|
-
.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
box-shadow: 0 0 0 var(--ring-width) var(--ring-color);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/** Textarea elements within devtools */
|
|
350
|
-
.devtools :global(textarea) {
|
|
351
|
-
width: 100%;
|
|
352
|
-
min-height: 60px;
|
|
353
|
-
font-family: monospace;
|
|
354
|
-
font-size: var(--text-xs);
|
|
355
|
-
padding: var(--space-1);
|
|
356
|
-
border: 1px solid var(--color-gray-300);
|
|
357
|
-
border-radius: var(--radius-md);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
.devtools :global(textarea:focus) {
|
|
361
|
-
outline: none;
|
|
362
|
-
box-shadow: 0 0 0 var(--ring-width) var(--ring-color);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/** Event detail panel styling */
|
|
366
|
-
.devtools :global(.details-cell) {
|
|
367
|
-
background-color: var(--color-gray-50);
|
|
368
|
-
padding: var(--space-4);
|
|
369
|
-
border-left: 3px solid var(--color-blue-500);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/** Expand/collapse icon for table rows */
|
|
373
|
-
.devtools :global(.expand-icon) {
|
|
374
|
-
margin-right: var(--space-2);
|
|
375
|
-
display: inline-block;
|
|
376
|
-
width: 12px;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/** Clickable table rows (e.g., expandable event rows) */
|
|
380
|
-
.devtools :global(.event-row) {
|
|
381
|
-
cursor: pointer;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/** Centered checkbox cell in tables */
|
|
385
|
-
.devtools :global(.include-cell) {
|
|
386
|
-
text-align: center;
|
|
387
|
-
width: 2.5rem;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/** Inline option label for checkbox/radio lists in table cells */
|
|
391
|
-
.devtools :global(.option-label) {
|
|
392
|
-
display: flex;
|
|
393
|
-
align-items: center;
|
|
394
|
-
gap: var(--space-2);
|
|
395
|
-
cursor: pointer;
|
|
245
|
+
.floating-button :global(svg) {
|
|
246
|
+
width: 2.25rem;
|
|
247
|
+
height: 2.25rem;
|
|
396
248
|
}
|
|
397
249
|
</style>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<td>{$currentNode?.id || ''}</td>
|
|
21
21
|
<td>{$currentNode?.type || ''}</td>
|
|
22
22
|
<td>{$currentStep?.id || ''}</td>
|
|
23
|
-
<td>{$currentStep?.
|
|
23
|
+
<td>{$currentStep?.label || ''}</td>
|
|
24
24
|
<td>{totalNextStepNodes}</td>
|
|
25
25
|
<td>{$stepsToOrigin[0]} - {$stepsToOrigin[1]}</td>
|
|
26
26
|
<td>{$stepsToLeaf[0]} - {$stepsToLeaf[1]}</td>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ExperienceInstance } from '@picked/client'
|
|
3
3
|
import ExperienceInstanceListItem from './ExperienceInstanceListItem.svelte'
|
|
4
|
-
import {
|
|
4
|
+
import { DataTable, EmptyState } from '../ui'
|
|
5
5
|
|
|
6
6
|
export let experienceInstances: ExperienceInstance[]
|
|
7
7
|
|
|
@@ -18,16 +18,12 @@
|
|
|
18
18
|
]
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{/snippet}
|
|
31
|
-
</DataTable>
|
|
32
|
-
</Card>
|
|
33
|
-
</Stack>
|
|
21
|
+
<DataTable columns={columns} rows={experienceInstances}>
|
|
22
|
+
{#snippet row(item)}
|
|
23
|
+
{@const experienceInstance = item}
|
|
24
|
+
<ExperienceInstanceListItem {experienceInstance} />
|
|
25
|
+
{/snippet}
|
|
26
|
+
{#snippet empty()}
|
|
27
|
+
<EmptyState message="No experience instances" />
|
|
28
|
+
{/snippet}
|
|
29
|
+
</DataTable>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const devtoolsExperience: import('@picked/client/plugins/create-experience').SerializableExperience;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createExperience as e}from"@picked/client/plugins/create-experience";import{adaptSvelteComponent as o}from"../../component.js";import t from"./DevToolsExperience.svelte";import p from"./ui/tokens.css.js";const r=e({id:"svelte-devtools",name:"Picked Devtools",decide:{},targets:[{selector:"body",placement:"append"}],component:o(t,{css:[p]})});r.addStep({label:"Tools"});export{r as devtoolsExperience};
|