import { hierarchyMachine } from '$lib/@iioioo_utils/State/hierarchy.machine'; import type { ForkModel, IChoice, IFieldBase, IFork } from '$lib/Models'; import { ArrayModel, ChoiceModel, NumberModel, TextModel } from '$lib/Models'; import { createMachine, assign, send, sendParent, spawn } from 'xstate'; // import { raise } from 'xstate/lib/actions'; // CONTEXT export type ForkContext = { defaultMachine: any; validationDescriptionMachine: any; hierarchyMachine: any; field: IFork; fieldValueMap: { [key:string]: any }; potentialParent: any; potentialChildren: any[]; potentialDefault: any; defaultStream: ( children: any[] ) => ChoiceModel; validationStream: ( name: string, type: string ) => ArrayModel; } export type ForkState = { value: 'general'; context: ForkContext } | { value: 'inSelection'; context: ForkContext } | { value: 'nesting'; context: ForkContext } | { value: { forking: 'assignDefault' | 'assignForkingConditions' }; context: ForkContext }; const validationRegistry = { 'form': [ 'required', 'isValid' ], 'text': [ 'required', 'url', 'email', 'equalTo', 'isSaIdNumber' ], 'choice': [ 'required', 'chosenAtLeast' ], } const rootId = 'rootId'; export const forkMachine = createMachine( { tsTypes: {} as import("./fork.machine.typegen").Typegen0, schema: { context: {} as ForkContext, events: {} as { type: 'ITEM_SELECTED'; item: any } | { type: 'ASSIGN_POTENTIAL_PARENT'; item: any } | { type: 'ASSIGN_POTENTIAL_CHILD', item: any } | { type: 'UNASSIGN_POTENTIAL_PARENT_AND_CHILDREN'; item: any } | { type: 'UNASSIGN_POTENTIAL_CHILD', item: any } | { type: 'ADD_DEFAULT', data: { arr: IFieldBase[]; acc: any } } | { type: 'ADD_RELATIONSHIPS' } | { type: 'REMOVE_PARENT_CHILD_RELATIONSHIP' } | { type: 'REMOVE_ALL_PARENT_CHILD_RELATIONSHIPS' } | { type: 'REMOVE_NODE_FROM_GRAPH' } | { type: 'CHECK_RELATIONSHIPS' } | { type: 'RESET' } | { type: 'REFRESH_FIELDS'; fieldMap: any } }, context: { defaultMachine: null, validationDescriptionMachine: null, hierarchyMachine: null, field: null, fieldValueMap: {}, // field: f, // fieldValueMap: { one, two, three }, potentialParent: null, potentialChildren: [], potentialDefault: null, defaultStream: ( children: IFieldBase[] ) => new ChoiceModel({ id: 'defaultStreamId', placeholder: '', imageUrl: '', type: 'choice', subType: 'radio', name: 'defaultChild', description: 'Please choose your type', label: '', value: [], fork: null, validation: [{ operator: 'chosenAtLeast', anchorValues: [1], message: 'we can try' }], waitForHowManyMSBeforeSubmit: 1000, data: children, dataKey: 'name', scale: [], order: 1 }), validationStream: ( name: string, type: string ) => new ArrayModel({ id: 'validationStreamId', placeholder: `Specify the conditions for ${name}`, imageUrl: '', name, description: `Specify the conditions for ${name}`, label: '', value: [], fork: null, validation: [{ operator: 'chosenAtLeast', anchorValues: [ 1 ], message: 'we can try' }], waitForHowManyMSBeforeSubmit: 1000, classExample: () => [ new ChoiceModel({ id: 'validationId', placeholder: '', imageUrl: '', type: "choice", subType: 'radio', name: "Validator choice", description: "Please choose your type", label: "", value: [], fork: { 1: { operator: 'equalTo', anchorValues: [ 'required' ], message: 'This field is required'}, 2: { operator: 'equalTo', anchorValues: [ 'chosenAtLeast' ], message: 'This field is required'} }, validation: [ { operator: 'chosenAtLeast', anchorValues: [1], message: 'we can try' } ], waitForHowManyMSBeforeSubmit: 1000, data: validationRegistry[ type ], scale: [], order: 1 }), new ChoiceModel({ id: '1', placeholder: '', imageUrl: '', type: "choice", subType: 'radio', name: "Required stream", description: "Please choose your sub type", label: "", value: [], fork: null, validation: [ { operator: 'chosenAtLeast', anchorValues: [1], message: 'we can try' } ], waitForHowManyMSBeforeSubmit: 1000, data: [ 'required', 'yes required' ], scale: [], order: 2 }), new ChoiceModel({ id: '2', placeholder: '', imageUrl: '', type: "choice", subType: 'radio', name: "ChosenAtLeast stream", description: "Please choose your sub type", label: "", value: [], fork: null, validation: [ { operator: 'chosenAtLeast', anchorValues: [1], message: 'we can try' } ], waitForHowManyMSBeforeSubmit: 1000, data: [ 'chosen', 'at', 'least' ], scale: [], order: 3 }), // new TextModel({ // id: 'nameId', // placeholder: 'This question is called...', // imageUrl: '', // type: "text", // subType: 'singleLine', // name: "name", // description: "What is the question called?", // label: "", // value: "", // fork: null, // validation: [ { operator: 'required', message: 'This field is required'}], // waitForHowManyMSBeforeSubmit: 3000, // debounceFor: 300, // order: 3 // }), // new TextModel({ // id: 'descriptionId', // placeholder: 'Description please!', // imageUrl: '', // type: "text", // subType: 'singleLine', // name: "description", // description: "What is the question about?", // label: "", // value: "", // fork: null, // validation: [ { operator: 'required', message: 'This field is required'} ], // waitForHowManyMSBeforeSubmit: 3000, // debounceFor: 300, // order: 4 // }), // new NumberModel({ // id: 'waitforId', // placeholder: 'Wait for...?', // imageUrl: '', // name: "Wait for?", // description: "How long must the question waits before it submits itself?", // label: "", // value: [], // fork: null, // validation: [ { operator: 'required', message: 'This field is required'} ], // waitForHowManyMSBeforeSubmit: 3000, // min: 100, // max: 10000, // debounceFor: 200, // customVisual: null, // order: 5 // }) ], wrangleResult: (result: { arr: IFieldBase[]; acc: any }) => result }) }, id: 'forkMachine', initial: 'initializing', states: { initializing: { entry: [ 'assignHierarchyMachine' ], always: 'general' }, general: { on: { ITEM_SELECTED: { target: 'inSelection', actions: [ 'assignPotentialParent' ] } } }, inSelection: { on: { ITEM_SELECTED: { actions: [ 'assignPotentialChild' ] }, UNASSIGN_POTENTIAL_PARENT_AND_CHILDREN: { target: 'general', actions: [ 'unassignPotentialParentAndChildren' ] }, UNASSIGN_POTENTIAL_CHILD: { actions: [ 'unassignPotentialChild' ] }, CHECK_RELATIONSHIPS: 'checkingParentType' } }, checkingParentType: { always: [ { target: 'nesting', cond: 'isForm' }, // { target: 'nesting', cond: 'isNotForm' } { target: 'forking', cond: 'isNotForm' } ], }, nesting: { entry: [ // 'addParentChildrenRelationships', // 'unassignPotentialParentAndChildren', // 'notifyHierarchyMachine', // 'notifyParent', // 'reset' 'addParentChildrenRelationships', 'unassignPotentialParentAndChildren', 'notifyHierarchyMachine', 'notifyParent', 'reset' ] }, forking: { initial: 'assignDefault', states: { assignDefault: { entry: [ 'assignDefaultMachine' ], on: { ADD_DEFAULT: { target: 'assignForkingConditions' } }, exit: [ 'deassignDefaultMachine' ] }, assignForkingConditions: { entry: [ 'assignValidationDescriptionMachine' ], on: { ADD_RELATIONSHIPS: { actions: [ 'addLogicalRelationships', 'unassignPotentialParentAndChildren', 'notifyHierarchyMachine', 'notifyParent', 'reset' ] }, } } } } }, on: { RESET: { target: 'general' }, REFRESH_FIELDS: { actions: ['refreshFields', 'assignHierarchyMachine'] } } }, { guards: { isForm: (context, event) => context.potentialParent.type === 'form', isNotForm: (context, event) => context.potentialParent.type !== 'form' }, actions: { assignHierarchyMachine: assign({ hierarchyMachine: (context,event) => spawn( hierarchyMachine.withContext({ ...hierarchyMachine.context, data: context.field.value, publishDraftResult: true, hasParent: true, notifyParentEventType: 'STREAM_FORK_RESULT' }), { name: 'streamForkMachine', sync: true } ), }), assignDefaultMachine: assign({ defaultMachine: (context, event) => { return spawn( hierarchyMachine.withContext({ ...hierarchyMachine.context, data: [ context.defaultStream( context.potentialChildren ) ], publishDraftResult: true, hasParent: true, notifyParentEventType: 'ADD_DEFAULT' }), { name: 'forkDefaultMachine', sync: true } ) }, }), deassignDefaultMachine: assign({ defaultMachine: null }), assignValidationDescriptionMachine: assign({ potentialDefault: (context, event) => { const field = event.data.arr[0] as IChoice; const defaultChild = (field.data as any[]).find(item => item[ field.dataKey ] === event.data.acc.defaultChild); return defaultChild; }, validationDescriptionMachine: (context, event) => { const children = context.potentialChildren.filter(child => child.name !== event.data.acc.defaultChild ); return spawn( hierarchyMachine.withContext({ ...hierarchyMachine.context, data: children.map(child => context.validationStream( child.name, child.type )), publishDraftResult: false, hasParent: true, notifyParentEventType: 'ADD_RELATIONSHIPS' }), { name: 'forkValidationDescriptionMachine', sync: true } ) }, }), // assignValidationDescriptionMachine: assign({ // validationDescriptionMachine: (context, event) => { // return spawn( // hierarchyMachine.withContext({ // ...hierarchyMachine.context, // data: [ // new ChoiceModel({ // id: 'checkboxId', // placeholder: '', // imageUrl: '', // type: "choice", // subType: 'radio', // name: "type", // description: "Please choose your type", // label: "", // value: [], // fork: null, // validation: [ { operator: 'chosenAtLeast', anchorValues: [1], message: 'we can try' } ], // waitForHowManyMSBeforeSubmit: 1000, // data: [ // { typeName: 'smash'}, // { typeName: 'whats the story morning glory'}, // { typeName: 'great escape'} // ], // dataKey: 'typeName', // // data: null, // // dataSourceId: 'test', // // dataSourceUrl: 'https://jsonplaceholder.typicode.com/todos', // // dataKey: 'title', // scale: [], // order: 1 // }), // // new ChoiceModel({ // // id: 'subtypecheckboxId', // // placeholder: '', // // imageUrl: '', // // type: "choice", // // subType: 'radio', // // name: "subType", // // description: "Please choose your sub type", // // label: "", // // value: [], // // fork: null, // // validation: [ { operator: 'chosenAtLeast', anchorValues: [1], message: 'we can try' } ], // // waitForHowManyMSBeforeSubmit: 1000, // // // data: [ // // // { typeName: 'step'}, // // // { typeName: 'text'}, // // // { typeName: 'choice'} // // // ], // // // dataKey: 'typeName', // // data: null, // // dataRegistry: validationRegistry, // // dataSourceId: 'checkboxId', // // // dataSourceUrl: 'https://jsonplaceholder.typicode.com/todos', // // // dataKey: 'title', // // scale: [], // // order: 2 // // }), // // new TextModel({ // // id: 'nameId', // // placeholder: 'This question is called...', // // imageUrl: '', // // type: "text", // // subType: 'singleLine', // // name: "name", // // description: "What is the question called?", // // label: "", // // value: "", // // fork: null, // // validation: [ { operator: 'required', message: 'This field is required'}], // // waitForHowManyMSBeforeSubmit: 3000, // // debounceFor: 300, // // order: 3 // // }), // // new TextModel({ // // id: 'descriptionId', // // placeholder: 'Description please!', // // imageUrl: '', // // type: "text", // // subType: 'singleLine', // // name: "description", // // description: "What is the question about?", // // label: "", // // value: "", // // fork: null, // // validation: [ { operator: 'required', message: 'This field is required'} ], // // waitForHowManyMSBeforeSubmit: 3000, // // debounceFor: 300, // // order: 4 // // }), // // new NumberModel({ // // id: 'waitforId', // // placeholder: 'Wait for...?', // // imageUrl: '', // // name: "Wait for?", // // description: "How long must the question waits before it submits itself?", // // label: "", // // value: [], // // fork: null, // // validation: [ { operator: 'required', message: 'This field is required'} ], // // waitForHowManyMSBeforeSubmit: 3000, // // min: 100, // // max: 10000, // // debounceFor: 200, // // customVisual: null, // // order: 5 // // }) // ], // publishDraftResult: true, // hasParent: true, // notifyParentEventType: 'ADD_RELATIONSHIPS' // }), { name: 'forkValidationDescriptionMachine', sync: true } // ) // }, // }), assignPotentialParent: assign({ potentialParent: (context, event) => event.item }), unassignPotentialParentAndChildren: assign({ potentialParent: () => null, potentialChildren: () => [] }), assignPotentialChild: assign({ potentialChildren: (context, event) => [ ...context.potentialChildren, event.item ] }), unassignPotentialChild: assign({ potentialChildren: (context, event) => context.potentialChildren.filter(n => n.id !== event.item.id) }), // addParentChildrenRelationships: assign({ // field: (context, event) => { // context.potentialChildren.forEach(child => { // child.parentId = context.potentialParent.id; // }); // return context.field; // } // }), addParentChildrenRelationships: assign((context, event) => { const newFieldValueMap = context.field.value.reduce((acc, curr) => ({ ...acc, [ curr.id ]: curr }), {}); const fieldValueMap = { ...newFieldValueMap, ...context.fieldValueMap }; context.potentialChildren.forEach(child => { child.parentId = context.potentialParent.id; fieldValueMap[ child.id ] = child; }); context.field.value = Object.values(fieldValueMap); context.fieldValueMap = fieldValueMap; return context; }), addLogicalRelationships: assign({ field: (context, event) => { debugger; context.potentialChildren.forEach(child => { child.parentId = context.potentialParent.id; }); return context.field; } }), notifyHierarchyMachine: send({ type: 'REFRESH_DATA' }, { to: (context, event) => context.hierarchyMachine }), notifyParent: sendParent( ( context, event ) => { console.log("notify parent in fork machine: ", context, event); return { type: 'CHANGE', value: context.field }; }), refreshFields: assign((context, event) => { Object.values(event.fieldMap).forEach((val: IFieldBase) => { val.parentId = !!context.fieldValueMap[ val.id ] ? context.fieldValueMap[ val.id ].parentId : ''; }); context.fieldValueMap = event.fieldMap; context.field.value = Object.values(context.fieldValueMap); console.log("running after you: ", context, event); return context; }), // reset: raise('RESET') // taking out for now } }); // import { hierarchyMachine } from '$lib/@iioioo_utils/State/hierarchy.machine'; // import type { IFork } from '$lib/Models'; // import { ArrayModel, ChoiceModel, NumberModel, TextModel } from '$lib/Models'; // import { createMachine, assign, send, sendParent, spawn } from 'xstate'; // import { raise } from 'xstate/lib/actions'; // // CONTEXT // export type ForkContext = { // validationDescriptionMachine: any; // hierarchyMachine: any; // field: IFork; // potentialParent: any; // potentialChildren: any[]; // } // export type ForkState = // { // value: 'general'; // context: ForkContext // } | // { // value: 'inSelection'; // context: ForkContext // }; // const validationRegistry = { // 'text': [ 'singleLine', 'multiLine' ], // 'choice': [ 'radio', 'checkbox' ], // } // const rootId = 'rootId'; // export const forkMachine = createMachine( // { // tsTypes: {} as import("./fork.machine.typegen").Typegen0, // schema: { // context: {} as ForkContext, // events: {} as // { type: 'ITEM_SELECTED'; item: any } | // { type: 'ASSIGN_POTENTIAL_PARENT'; item: any } | // { type: 'ASSIGN_POTENTIAL_CHILD', item: any } | // { type: 'UNASSIGN_POTENTIAL_PARENT_AND_CHILDREN'; item: any } | // { type: 'UNASSIGN_POTENTIAL_CHILD', item: any } | // { type: 'ADD_RELATIONSHIPS' } | // { type: 'REMOVE_PARENT_CHILD_RELATIONSHIP' } | // { type: 'REMOVE_ALL_PARENT_CHILD_RELATIONSHIPS' } | // { type: 'REMOVE_NODE_FROM_GRAPH' } | // { type: 'CHECK_RELATIONSHIPS' } | // { type: 'RESET' } // }, // context: { // validationDescriptionMachine: null, // hierarchyMachine: null, // field: null, // potentialParent: null, // potentialChildren: [] // }, // id: 'forkMachine', // initial: 'initializing', // states: { // initializing: { // entry: [ 'setupHierarchy' ], // always: 'general' // }, // general: { // on: { // ITEM_SELECTED: { // target: 'inSelection', // actions: [ 'assignPotentialParent' ] // } // } // }, // inSelection: { // on: { // ITEM_SELECTED: { // actions: [ 'assignPotentialChild' ] // }, // UNASSIGN_POTENTIAL_PARENT_AND_CHILDREN: { // target: 'general', // actions: [ 'unassignPotentialParentAndChildren' ] // }, // UNASSIGN_POTENTIAL_CHILD: { // actions: [ 'unassignPotentialChild' ] // }, // CHECK_RELATIONSHIPS: 'describingRelationships' // } // }, // describingRelationships: { // initial: 'checkingParentType', // states: { // checkingParentType: { // always: [ // { target: 'nesting', cond: 'isForm' }, // { target: 'forking', cond: 'isNotForm' } // ], // }, // nesting: { // entry: [ // 'addParentChildrenRelationships', // 'unassignPotentialParentAndChildren', // 'notifyHierarchyMachine', // 'notifyParent', // 'reset' // ] // }, // forking: { // states: { // describeValidation: { // entry: [ 'assignValidationDescriptionMachine' ], // on: { // ADD_RELATIONSHIPS: { // actions: [ // 'addLogicalRelationships', // 'unassignPotentialParentAndChildren', // 'notifyHierarchyMachine', // 'notifyParent', // 'reset' // ] // }, // } // } // } // } // }, // on: { // RESET: { // target: 'general' // }, // } // } // } // }, // { // guards: { // isForm: (context, event) => context.potentialParent.type === 'form', // isNotForm: (context, event) => context.potentialParent.type !== 'form' // }, // actions: { // setupHierarchy: assign({ // hierarchyMachine: (context,event) => spawn( // hierarchyMachine.withContext({ // ...hierarchyMachine.context, // data: context.field.value, // publishDraftResult: true, // hasParent: true, // notifyParentEventType: 'STREAM_FORK_RESULT' // }), { name: 'streamForkMachine', sync: true } ), // }), // assignValidationDescriptionMachine: assign({ // validationDescriptionMachine: (context,event) => spawn( // hierarchyMachine.withContext({ // ...hierarchyMachine.context, // data: [ // new ChoiceModel({ // id: 'checkboxId', // placeholder: '', // imageUrl: '', // type: "choice", // subType: 'radio', // name: "type", // description: "Please choose your type", // label: "", // value: [], // fork: null, // validation: [ { operator: 'chosenAtLeast', anchorValues: [1], message: 'we can try' } ], // waitForHowManyMSBeforeSubmit: 1000, // data: [ // { typeName: 'smash'}, // { typeName: 'whats the story morning glory'}, // { typeName: 'great escape'} // ], // dataKey: 'typeName', // // data: null, // // dataSourceId: 'test', // // dataSourceUrl: 'https://jsonplaceholder.typicode.com/todos', // // dataKey: 'title', // scale: [], // order: 1 // }), // // new ChoiceModel({ // // id: 'subtypecheckboxId', // // placeholder: '', // // imageUrl: '', // // type: "choice", // // subType: 'radio', // // name: "subType", // // description: "Please choose your sub type", // // label: "", // // value: [], // // fork: null, // // validation: [ { operator: 'chosenAtLeast', anchorValues: [1], message: 'we can try' } ], // // waitForHowManyMSBeforeSubmit: 1000, // // // data: [ // // // { typeName: 'step'}, // // // { typeName: 'text'}, // // // { typeName: 'choice'} // // // ], // // // dataKey: 'typeName', // // data: null, // // dataRegistry: validationRegistry, // // dataSourceId: 'checkboxId', // // // dataSourceUrl: 'https://jsonplaceholder.typicode.com/todos', // // // dataKey: 'title', // // scale: [], // // order: 2 // // }), // // new TextModel({ // // id: 'nameId', // // placeholder: 'This question is called...', // // imageUrl: '', // // type: "text", // // subType: 'singleLine', // // name: "name", // // description: "What is the question called?", // // label: "", // // value: "", // // fork: null, // // validation: [ { operator: 'required', message: 'This field is required'}], // // waitForHowManyMSBeforeSubmit: 3000, // // debounceFor: 300, // // order: 3 // // }), // // new TextModel({ // // id: 'descriptionId', // // placeholder: 'Description please!', // // imageUrl: '', // // type: "text", // // subType: 'singleLine', // // name: "description", // // description: "What is the question about?", // // label: "", // // value: "", // // fork: null, // // validation: [ { operator: 'required', message: 'This field is required'} ], // // waitForHowManyMSBeforeSubmit: 3000, // // debounceFor: 300, // // order: 4 // // }), // // new NumberModel({ // // id: 'waitforId', // // placeholder: 'Wait for...?', // // imageUrl: '', // // name: "Wait for?", // // description: "How long must the question waits before it submits itself?", // // label: "", // // value: [], // // fork: null, // // validation: [ { operator: 'required', message: 'This field is required'} ], // // waitForHowManyMSBeforeSubmit: 3000, // // min: 100, // // max: 10000, // // debounceFor: 200, // // customVisual: null, // // order: 5 // // }) // ], // publishDraftResult: true, // hasParent: true, // notifyParentEventType: 'ADD_RELATIONSHIPS' // }), { name: 'forkValidationDescriptionMachine', sync: true } ), // }), // assignPotentialParent: assign({ // potentialParent: (context, event) => event.item // }), // unassignPotentialParentAndChildren: assign({ // potentialParent: () => null, // potentialChildren: () => [] // }), // assignPotentialChild: assign({ // potentialChildren: (context, event) => [ ...context.potentialChildren, event.item ] // }), // unassignPotentialChild: assign({ // potentialChildren: (context, event) => context.potentialChildren.filter(n => n.id !== event.item.id) // }), // addParentChildrenRelationships: assign({ // field: (context, event) => { // context.potentialChildren.forEach(child => { // child.parentId = context.potentialParent.id; // }); // return context.field; // } // }), // addLogicalRelationships: assign({ // field: (context, event) => { // context.potentialChildren.forEach(child => { // child.parentId = context.potentialParent.id; // }); // return context.field; // } // }), // notifyHierarchyMachine: send({ type: 'REFRESH_DATA' }, { to: (context, event) => context.hierarchyMachine }), // notifyParent: sendParent( ( context, event ) => { // console.log("notify parent in fork machine: ", context, event); // return { type: 'CHANGE', value: context.field }; // }), // reset: raise('RESET') // } // });