@picked/svelte 0.0.50 → 0.0.51-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/form/create-form.js +1 -0
- package/dist/form/create-form.svelte.d.ts +24 -0
- package/dist/form/create-form.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/step.d.ts +8 -8
- package/dist/hooks/step.js +1 -1
- package/dist/plugins/devtools/components/ExperienceInstanceListItem.svelte +1 -1
- package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +11 -11
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +10 -9
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getExperience as e,getPicked as t}from"../hooks/client.js";import{createForm as n}from"@picked/client/form";import*as r from"svelte/internal/client";function i(i,a,o){let s=n({client:t(),experienceInstance:e()},i,a,o);return{...s,form:{...s.form,onBeforeSubmit:e=>{r.user_effect(()=>s.form.onBeforeSubmit(e))},onSuccess:e=>{r.user_effect(()=>s.form.onSuccess(e))},onError:e=>{r.user_effect(()=>s.form.onError(e))}}}}export{i as createForm};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Store } from 'effector';
|
|
2
|
+
import { FormInstance as CoreFormInstance, ErrorCallback, SubmitCallback, SuccessCallback } from '@picked/client/form';
|
|
3
|
+
export type { FormField } from '@picked/client/form';
|
|
4
|
+
/**
|
|
5
|
+
* Svelte FormInstance — identical to core but callback registration returns void
|
|
6
|
+
* (cleanup is handled automatically via $effect, not by the consumer).
|
|
7
|
+
*/
|
|
8
|
+
export interface FormInstance<TFieldIds extends string = string> extends Omit<CoreFormInstance<TFieldIds>, 'form'> {
|
|
9
|
+
form: Omit<CoreFormInstance<TFieldIds>['form'], 'onBeforeSubmit' | 'onSuccess' | 'onError'> & {
|
|
10
|
+
onBeforeSubmit: (callback: SubmitCallback<TFieldIds>) => void;
|
|
11
|
+
onSuccess: (callback: SuccessCallback<TFieldIds>) => void;
|
|
12
|
+
onError: (callback: ErrorCallback) => void;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a form instance with Svelte lifecycle management.
|
|
17
|
+
*
|
|
18
|
+
* Wraps the framework-agnostic createForm from @picked/client/form:
|
|
19
|
+
* - Injects client and experienceInstance from Svelte context
|
|
20
|
+
* - Auto-wires callback cleanup via $effect so consumers don't need to manage lifecycle
|
|
21
|
+
*
|
|
22
|
+
* Used internally by setStepContext — access via getStep() in components.
|
|
23
|
+
*/
|
|
24
|
+
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,16 @@
|
|
|
1
|
+
import { ValidationError } from '@picked/client';
|
|
2
|
+
interface GetFormOptions {
|
|
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 {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getExperience as e,getPicked as t}from"../hooks/client.js";import{getStep as n}from"../hooks/step.js";import"../hooks/index.js";import{buildResponsesFromFormData as r}from"@picked/client/form";import{attach as i,createEffect as a,createEvent as o,sample as s,split as c}from"effector";function l({id:l,title:u,onError:d,onSuccess:f}){let p=t(),m=n(),h=e(),{currentStepStore:g}=h,_=o(),v=a(e=>p.responses.commit(e)),y=i({source:{step:g},effect:a(({step:e})=>{p.eventBus({type:`form_submit_success`,form:{id:l,title:u},experience:h,step:e??void 0}),f?.()})}),b=i({source:{step:g},mapParams:(e,t)=>({step:t.step,errors:e.errors}),effect:a(({step:e,errors:t})=>{p.eventBus({type:`form_submit_error`,form:{id:l,title:u},exception:{reason:t},experience:h,step:e??void 0}),t.forEach(t=>{p.eventBus({type:`field_validation_error`,exception:{reason:[t]},experience:h,step:e??void 0})}),d?.(t)})});return s({clock:_,source:{step:g},fn:({step:e})=>({formId:l,title:u,step:e}),target:a(({formId:e,title:t,step:n})=>{p.eventBus({type:`form_submit`,form:{id:e,title:t},experience:h,step:n??void 0})})}),s({clock:_,source:{fields:p.fields.listStore,stepFields:m.fields,step:g},fn:({stepFields:e},t)=>(t.preventDefault(),r(e,new FormData(t.currentTarget))),target:v}),c({source:v.doneData,match:{hasErrors:({errors:e})=>e.length>0},cases:{hasErrors:b,__:y}}),{form:{onsubmit:_}}}export{l 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 '@picked/client/form';
|
|
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/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{STEP_CONTEXT_KEY as e}from"../context.js";import{getExperience as t
|
|
1
|
+
import{STEP_CONTEXT_KEY as e}from"../context.js";import{getExperience as t}from"./client.js";import{getStepRouter as n}from"../router/context.js";import{getContext as r,setContext as i}from"svelte";import{createForm as a}from"../form/create-form.svelte";function o(e){let n=t().treeStore.map(t=>{let n=t.steps.find(t=>t.id===e);if(!n)throw Error(`Step with id "${e}" not found in the experience tree.`);return n});return{stepStore:n,form:a(e,n.map(e=>e.label),n.map(e=>e.fieldIds))}}function s(t){let{activeStep:r}=n(),a=r.map(e=>e?.id===t),{stepStore:s,form:c}=o(t);return i(e,{step:s,isActive:a,isActiveStep:a,...c})}function c(){return r(e)}export{c as getStep,s as setStepContext};
|
|
@@ -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>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
let zoom = 1
|
|
10
10
|
let selectedNode = null
|
|
11
11
|
let highlightedPath: string[][] = []
|
|
12
|
-
let error = null
|
|
12
|
+
let error: string | null = null
|
|
13
13
|
const loading = false
|
|
14
14
|
|
|
15
15
|
let treeRenderingDone = false
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
$: steps = $tree.steps
|
|
20
20
|
$: nodes = $tree.nodes
|
|
21
21
|
$: edges = $tree.edges
|
|
22
|
-
$:
|
|
22
|
+
$: conditions = $tree.conditions
|
|
23
23
|
|
|
24
24
|
let zoomBehavior: d3.ZoomBehavior<SVGSVGElement, unknown>
|
|
25
25
|
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
d3.select(svgEl).selectAll('*').remove()
|
|
91
91
|
|
|
92
92
|
const stepsMap = new Map(steps.map((s) => [s.id, s]))
|
|
93
|
-
const
|
|
93
|
+
const conditionsMap = new Map(conditions.map((c) => [c.id, c]))
|
|
94
94
|
const outgoingEdges = new Map<string, ExperienceEdge[]>()
|
|
95
95
|
edges.forEach((e) => {
|
|
96
96
|
if (!outgoingEdges.has(e.sourceNodeId)) outgoingEdges.set(e.sourceNodeId, [])
|
|
@@ -101,10 +101,10 @@
|
|
|
101
101
|
if (node.type === 'origin') return 'Start'
|
|
102
102
|
if (node.type === 'step' && node.stepId)
|
|
103
103
|
return stepsMap.get(node.stepId)?.label ?? 'Unknown Step'
|
|
104
|
-
if (node.type === '
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
if (
|
|
104
|
+
if (node.type === 'condition' && node.conditionId) {
|
|
105
|
+
const condition = conditionsMap.get(node.conditionId)
|
|
106
|
+
if (condition?.label) return condition.label
|
|
107
|
+
if (condition?.condition) return formatLogicRule(condition.condition)
|
|
108
108
|
return 'Condition'
|
|
109
109
|
}
|
|
110
110
|
return 'Unknown Node'
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
.attr('class', 'link')
|
|
198
198
|
.attr('fill', 'none')
|
|
199
199
|
.attr('stroke', (d: any) => {
|
|
200
|
-
if (d.source.data.type !== '
|
|
200
|
+
if (d.source.data.type !== 'condition') return '#94a3b8'
|
|
201
201
|
const targetId = d.target.data._isReference
|
|
202
202
|
? d.target.data._referenceTo
|
|
203
203
|
: d.target.data.id
|
|
@@ -211,9 +211,9 @@
|
|
|
211
211
|
.attr('stroke-width', 2)
|
|
212
212
|
.attr('d', linkGenerator)
|
|
213
213
|
|
|
214
|
-
// Label true/false branches from
|
|
214
|
+
// Label true/false branches from condition nodes
|
|
215
215
|
g.selectAll('.edge-label')
|
|
216
|
-
.data(treeRoot.links().filter((d: any) => d.source.data.type === '
|
|
216
|
+
.data(treeRoot.links().filter((d: any) => d.source.data.type === 'condition'))
|
|
217
217
|
.enter()
|
|
218
218
|
.append('text')
|
|
219
219
|
.attr('class', 'edge-label')
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
.attr('fill', (d: any) => {
|
|
260
260
|
if (d.data._isReference) return '#38bdf8'
|
|
261
261
|
if (d.data.type === 'origin') return '#00cd90'
|
|
262
|
-
if (d.data.type === '
|
|
262
|
+
if (d.data.type === 'condition') return '#f59e0b'
|
|
263
263
|
return '#1748ff'
|
|
264
264
|
})
|
|
265
265
|
.attr('stroke', '#fff')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { ResponseListItem, PrimitiveFieldValue, Field } from '@picked/client'
|
|
2
|
+
import type { ResponseListItem, PrimitiveFieldValue, Field, FieldValue } from '@picked/client'
|
|
3
3
|
import { getPicked } from '../../../../hooks'
|
|
4
4
|
import { Button, Text, CodeBlock, Banner, Stack } from '../../ui'
|
|
5
5
|
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
let isEditing = false
|
|
12
12
|
let editValue = ''
|
|
13
13
|
let editError = ''
|
|
14
|
-
let selectedOptions: Set<
|
|
14
|
+
let selectedOptions: Set<FieldValue> = new Set()
|
|
15
15
|
|
|
16
|
-
$: hasOptions = field?.options && field.options.length > 0
|
|
16
|
+
$: hasOptions = Array.isArray(field?.options) && field.options.length > 0
|
|
17
|
+
$: staticOptions = Array.isArray(field?.options) ? field.options : null
|
|
17
18
|
$: currentValue = response?.value ?? null
|
|
18
19
|
|
|
19
20
|
function formatValue(value: unknown): string {
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
selectedOptions = new Set()
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
function toggleOption(optionValue:
|
|
56
|
+
function toggleOption(optionValue: FieldValue) {
|
|
56
57
|
if (field?.multiple) {
|
|
57
58
|
if (selectedOptions.has(optionValue)) {
|
|
58
59
|
selectedOptions.delete(optionValue)
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
function parseValue(input: string):
|
|
69
|
+
function parseValue(input: string): FieldValue {
|
|
69
70
|
input = input.trim()
|
|
70
71
|
|
|
71
72
|
if (input === '' || input === '-') {
|
|
@@ -99,14 +100,14 @@
|
|
|
99
100
|
|
|
100
101
|
async function saveEdit() {
|
|
101
102
|
try {
|
|
102
|
-
let newValue:
|
|
103
|
+
let newValue: FieldValue
|
|
103
104
|
|
|
104
105
|
if (hasOptions) {
|
|
105
106
|
const values = Array.from(selectedOptions)
|
|
106
107
|
if (field?.multiple) {
|
|
107
108
|
newValue = values
|
|
108
109
|
} else {
|
|
109
|
-
newValue = values
|
|
110
|
+
newValue = values.at(0) ?? null
|
|
110
111
|
}
|
|
111
112
|
} else {
|
|
112
113
|
newValue = parseValue(editValue)
|
|
@@ -142,9 +143,9 @@
|
|
|
142
143
|
<td>
|
|
143
144
|
{#if isEditing}
|
|
144
145
|
<Stack direction="vertical" gap="small">
|
|
145
|
-
{#if hasOptions &&
|
|
146
|
+
{#if hasOptions && staticOptions}
|
|
146
147
|
<Stack direction="vertical" gap="small">
|
|
147
|
-
{#each
|
|
148
|
+
{#each staticOptions as option (option.id)}
|
|
148
149
|
<label class="option-label">
|
|
149
150
|
{#if field.multiple}
|
|
150
151
|
<input
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@picked/svelte",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.51-next.1",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"d3": "^7.9.0"
|
|
6
6
|
},
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@vitest/coverage-v8": "^4.1.0",
|
|
19
19
|
"effector": "^23.3.0",
|
|
20
20
|
"fast-glob": "^3.3.3",
|
|
21
|
-
"happy-dom": "^20.
|
|
21
|
+
"happy-dom": "^20.8.8",
|
|
22
22
|
"size-limit": "^12.0.0",
|
|
23
23
|
"svelte": "^5.46.4",
|
|
24
24
|
"svelte-check": "^4.2.2",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
},
|
|
89
89
|
"module": "./dist/index.js",
|
|
90
90
|
"peerDependencies": {
|
|
91
|
-
"@picked/client": "0.0.
|
|
91
|
+
"@picked/client": "0.0.51-next.1",
|
|
92
92
|
"effector": "^23.3.0",
|
|
93
93
|
"svelte": "^5.46.4"
|
|
94
94
|
},
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"svelte-check": "svelte-check --tsconfig ./tsconfig.json",
|
|
109
109
|
"svelte-check:report": "mkdir -p ./.reports && pnpm svelte-check --tsconfig ./tsconfig.json --output=machine > ./.reports/svelte-check.txt || [ -f './.reports/svelte-check.txt' ]",
|
|
110
110
|
"test": "vitest run",
|
|
111
|
-
"test:coverage": "vitest
|
|
111
|
+
"test:coverage": "../../scripts/vitest-coverage.sh",
|
|
112
112
|
"test:packaged-types": "attw --pack",
|
|
113
113
|
"test:watch": "vitest",
|
|
114
114
|
"type-check": "tsgo -p . --noEmit"
|