vanilla-jui 1.5.9 → 1.5.11
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/index.d.ts +4 -1
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/flow.ts +181 -77
- package/src/components/form.ts +103 -60
- package/src/css/form.css +33 -23
- package/src/primitives/toast.ts +3 -2
- package/src/validation/validator.ts +104 -12
package/package.json
CHANGED
package/src/components/flow.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createDeepStore, flushSync, jsx } from 'vanilla-signal';
|
|
1
|
+
import { createDeepStore, createMemo, flushSync, jsx } from 'vanilla-signal';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
type FunctionalComponent,
|
|
@@ -101,6 +101,9 @@ export interface FlowState extends Record<string, unknown> {
|
|
|
101
101
|
|
|
102
102
|
export interface FlowGoToOptions {
|
|
103
103
|
direction?: FlowDirection;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface InternalFlowGoToOptions extends FlowGoToOptions {
|
|
104
107
|
internal?: boolean;
|
|
105
108
|
}
|
|
106
109
|
|
|
@@ -312,6 +315,8 @@ interface FlowError extends Error {
|
|
|
312
315
|
code?: string;
|
|
313
316
|
}
|
|
314
317
|
|
|
318
|
+
type ActionToken = number | null;
|
|
319
|
+
|
|
315
320
|
const DEFAULT_CLASS_NAMES: FlowClassNames = {
|
|
316
321
|
root: 'j-flow',
|
|
317
322
|
header: 'flow-header',
|
|
@@ -341,6 +346,27 @@ function clonePlainObject(value: unknown): FlowData {
|
|
|
341
346
|
return isPlainObject(value) ? { ...(value as FlowData) } : {};
|
|
342
347
|
}
|
|
343
348
|
|
|
349
|
+
function cloneStepData(
|
|
350
|
+
value: Record<string, FlowData>
|
|
351
|
+
): Record<string, FlowData> {
|
|
352
|
+
const result: Record<string, FlowData> = {};
|
|
353
|
+
for (const key of Object.keys(value)) {
|
|
354
|
+
result[key] = clonePlainObject(value[key]);
|
|
355
|
+
}
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function cloneSnapshot(value: FlowSnapshot): FlowSnapshot {
|
|
360
|
+
return {
|
|
361
|
+
...value,
|
|
362
|
+
history: [...value.history],
|
|
363
|
+
data: clonePlainObject(value.data),
|
|
364
|
+
stepData: cloneStepData(value.stepData),
|
|
365
|
+
currentData: clonePlainObject(value.currentData),
|
|
366
|
+
currentStep: value.currentStep ? { ...value.currentStep } : null,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
344
370
|
function cloneSteps(steps: unknown): FlowStep[] {
|
|
345
371
|
if (!Array.isArray(steps)) return [];
|
|
346
372
|
return steps.map((step) => {
|
|
@@ -378,7 +404,9 @@ function normalizeStepResult(
|
|
|
378
404
|
if (typeof result === 'string') return { id: result };
|
|
379
405
|
if (!isPlainObject(result)) return { id: fallbackId };
|
|
380
406
|
const source = result as { id?: unknown; data?: unknown };
|
|
381
|
-
if (typeof source.id !== 'string')
|
|
407
|
+
if (typeof source.id !== 'string') {
|
|
408
|
+
return { id: fallbackId, data: result as FlowPayload };
|
|
409
|
+
}
|
|
382
410
|
return {
|
|
383
411
|
id: source.id,
|
|
384
412
|
data:
|
|
@@ -526,7 +554,7 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
526
554
|
direction: null,
|
|
527
555
|
history: [initialStepId],
|
|
528
556
|
data: clonePlainObject(initialGlobal),
|
|
529
|
-
stepData:
|
|
557
|
+
stepData: cloneStepData(initialStepData),
|
|
530
558
|
loading: false,
|
|
531
559
|
error: null,
|
|
532
560
|
busyAction: null,
|
|
@@ -541,6 +569,9 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
541
569
|
};
|
|
542
570
|
const subscribers = new Set<FlowSubscriber>();
|
|
543
571
|
const cleanupTasks = new Set<FlowCleanup>();
|
|
572
|
+
const cancelledActions = new Set<number>();
|
|
573
|
+
let actionToken = 0;
|
|
574
|
+
let activeActionToken: number | null = null;
|
|
544
575
|
let ui: FunctionalComponent<
|
|
545
576
|
Record<string, unknown>,
|
|
546
577
|
FlowState,
|
|
@@ -554,9 +585,6 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
554
585
|
throw new Error(`Flow.${method}: instance has been destroyed.`);
|
|
555
586
|
}
|
|
556
587
|
};
|
|
557
|
-
const currentStep = (): FlowStep => steps[state.currentIndex];
|
|
558
|
-
const getStepData = (stepId: string): FlowData =>
|
|
559
|
-
clonePlainObject(state.stepData[stepId]);
|
|
560
588
|
const publicStep = (
|
|
561
589
|
step: FlowStep | null | undefined
|
|
562
590
|
): PublicFlowStep | null => {
|
|
@@ -572,7 +600,23 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
572
600
|
} = step;
|
|
573
601
|
return result;
|
|
574
602
|
};
|
|
575
|
-
const
|
|
603
|
+
const currentStepMemo = createMemo(() => steps[state.currentIndex]);
|
|
604
|
+
const currentDataMemo = createMemo(() => {
|
|
605
|
+
const version = state.version;
|
|
606
|
+
return version >= 0
|
|
607
|
+
? clonePlainObject(state.stepData[state.currentId])
|
|
608
|
+
: {};
|
|
609
|
+
});
|
|
610
|
+
const publicCurrentStepMemo = createMemo(() => publicStep(currentStepMemo()));
|
|
611
|
+
const publicStepsMemo = createMemo(() => steps.map(publicStep));
|
|
612
|
+
const canBackMemo = createMemo(
|
|
613
|
+
() => state.currentIndex > 0 && !state.loading
|
|
614
|
+
);
|
|
615
|
+
const canNextMemo = createMemo(
|
|
616
|
+
() => state.currentIndex < steps.length - 1 && !state.loading
|
|
617
|
+
);
|
|
618
|
+
const isLastMemo = createMemo(() => state.currentIndex === steps.length - 1);
|
|
619
|
+
const snapshotMemo = createMemo<FlowSnapshot>(() => ({
|
|
576
620
|
id: state.id,
|
|
577
621
|
currentId: state.currentId,
|
|
578
622
|
currentIndex: state.currentIndex,
|
|
@@ -581,16 +625,20 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
581
625
|
direction: state.direction,
|
|
582
626
|
history: [...state.history],
|
|
583
627
|
data: clonePlainObject(state.data),
|
|
584
|
-
stepData:
|
|
585
|
-
currentData:
|
|
586
|
-
currentStep:
|
|
587
|
-
canBack:
|
|
588
|
-
canNext:
|
|
589
|
-
isLast:
|
|
628
|
+
stepData: cloneStepData(state.stepData),
|
|
629
|
+
currentData: currentDataMemo(),
|
|
630
|
+
currentStep: publicCurrentStepMemo(),
|
|
631
|
+
canBack: canBackMemo(),
|
|
632
|
+
canNext: canNextMemo(),
|
|
633
|
+
isLast: isLastMemo(),
|
|
590
634
|
loading: state.loading,
|
|
591
635
|
busyAction: state.busyAction,
|
|
592
636
|
error: state.error,
|
|
593
|
-
});
|
|
637
|
+
}));
|
|
638
|
+
const currentStep = (): FlowStep => currentStepMemo();
|
|
639
|
+
const getStepData = (stepId: string): FlowData =>
|
|
640
|
+
clonePlainObject(state.stepData[stepId]);
|
|
641
|
+
const snapshot = (): FlowSnapshot => cloneSnapshot(snapshotMemo());
|
|
594
642
|
const emitChange = (previous: FlowSnapshot | null = null): void => {
|
|
595
643
|
const next = snapshot();
|
|
596
644
|
for (const subscriber of subscribers) subscriber(next, flow, previous);
|
|
@@ -608,7 +656,10 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
608
656
|
assertActive('setStepData');
|
|
609
657
|
validateParam('stepId', stepId, { type: 'string' }, 'Flow.setStepData');
|
|
610
658
|
validateParam('data', data, FLOW_PAYLOAD_RULE, 'Flow.setStepData');
|
|
611
|
-
if (!
|
|
659
|
+
if (!stepMap.has(stepId)) {
|
|
660
|
+
throw new Error(`Flow.setStepData: step "${stepId}" does not exist.`);
|
|
661
|
+
}
|
|
662
|
+
if (!data) return flow;
|
|
612
663
|
flushSync(() => {
|
|
613
664
|
state.stepData[stepId] = {
|
|
614
665
|
...clonePlainObject(state.stepData[stepId]),
|
|
@@ -647,15 +698,32 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
647
698
|
error.code = 'FLOW_ABORTED';
|
|
648
699
|
return error;
|
|
649
700
|
};
|
|
701
|
+
const isAbortError = (error: unknown): error is FlowError =>
|
|
702
|
+
error instanceof Error && (error as FlowError).code === 'FLOW_ABORTED';
|
|
703
|
+
const assertActionCurrent = (token: ActionToken): void => {
|
|
704
|
+
if (runtime.destroyed) throw createAbortError();
|
|
705
|
+
if (token != null && cancelledActions.has(token)) {
|
|
706
|
+
throw createAbortError();
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
const cancelActiveAction = (): void => {
|
|
710
|
+
const token = activeActionToken;
|
|
711
|
+
if (token != null) cancelledActions.add(token);
|
|
712
|
+
runtime.actionController?.abort();
|
|
713
|
+
runtime.actionController = null;
|
|
714
|
+
runtime.activeAction = null;
|
|
715
|
+
activeActionToken = null;
|
|
716
|
+
};
|
|
650
717
|
const callHook = async <TResult>(
|
|
651
718
|
hook: ((...args: never[]) => TResult | Promise<TResult>) | null | undefined,
|
|
652
|
-
args: unknown[]
|
|
719
|
+
args: unknown[],
|
|
720
|
+
token: ActionToken = activeActionToken
|
|
653
721
|
): Promise<TResult | undefined> => {
|
|
654
722
|
if (typeof hook !== 'function') return undefined;
|
|
655
723
|
const result = await (
|
|
656
724
|
hook as (...values: unknown[]) => TResult | Promise<TResult>
|
|
657
725
|
)(...args);
|
|
658
|
-
|
|
726
|
+
assertActionCurrent(token);
|
|
659
727
|
return result;
|
|
660
728
|
};
|
|
661
729
|
const createContext = (
|
|
@@ -717,26 +785,33 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
717
785
|
};
|
|
718
786
|
const runAction = async (
|
|
719
787
|
action: FlowAction,
|
|
720
|
-
task: () => Promise<FlowSnapshot | null>,
|
|
788
|
+
task: (token: ActionToken) => Promise<FlowSnapshot | null>,
|
|
721
789
|
internal = false
|
|
722
790
|
): Promise<FlowSnapshot | null> => {
|
|
723
791
|
const outer = !internal && !state.loading;
|
|
792
|
+
const token = outer ? ++actionToken : activeActionToken;
|
|
724
793
|
if (outer) {
|
|
725
794
|
runtime.actionController = new AbortController();
|
|
726
795
|
runtime.activeAction = action;
|
|
796
|
+
activeActionToken = token;
|
|
727
797
|
setLoading(true, action);
|
|
728
798
|
}
|
|
729
799
|
try {
|
|
730
|
-
|
|
800
|
+
assertActionCurrent(token);
|
|
801
|
+
const result = await task(token);
|
|
802
|
+
assertActionCurrent(token);
|
|
803
|
+
return result;
|
|
731
804
|
} catch (error) {
|
|
732
|
-
if (runtime.destroyed) return null;
|
|
805
|
+
if (runtime.destroyed || isAbortError(error)) return null;
|
|
733
806
|
if (state.error !== error) handleError(error);
|
|
734
807
|
throw error;
|
|
735
808
|
} finally {
|
|
736
|
-
if (
|
|
809
|
+
if (token != null) cancelledActions.delete(token);
|
|
810
|
+
if (outer && !runtime.destroyed && activeActionToken === token) {
|
|
737
811
|
setLoading(false, null);
|
|
738
812
|
runtime.activeAction = null;
|
|
739
813
|
runtime.actionController = null;
|
|
814
|
+
activeActionToken = null;
|
|
740
815
|
}
|
|
741
816
|
}
|
|
742
817
|
};
|
|
@@ -748,7 +823,7 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
748
823
|
direction: state.direction,
|
|
749
824
|
history: [...state.history],
|
|
750
825
|
data: clonePlainObject(state.data),
|
|
751
|
-
stepData:
|
|
826
|
+
stepData: cloneStepData(state.stepData),
|
|
752
827
|
loading: state.loading,
|
|
753
828
|
busyAction: state.busyAction,
|
|
754
829
|
error: state.error,
|
|
@@ -764,7 +839,7 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
764
839
|
state.history.splice(0, state.history.length, ...captured.history);
|
|
765
840
|
replaceObject(state.data, captured.data);
|
|
766
841
|
replaceObject(state.stepData, captured.stepData);
|
|
767
|
-
state.loading =
|
|
842
|
+
state.loading = captured.loading;
|
|
768
843
|
state.busyAction = captured.busyAction;
|
|
769
844
|
state.error = captured.error;
|
|
770
845
|
state.version = captured.version + 1;
|
|
@@ -784,31 +859,39 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
784
859
|
toStep: FlowStep,
|
|
785
860
|
direction: FlowDirection,
|
|
786
861
|
payload: FlowPayload,
|
|
787
|
-
fromStep: FlowStep
|
|
862
|
+
fromStep: FlowStep,
|
|
863
|
+
token: ActionToken
|
|
788
864
|
): Promise<void> => {
|
|
789
865
|
const previous = snapshot();
|
|
790
866
|
const rollback = captureState();
|
|
791
867
|
try {
|
|
792
868
|
if (payload) setStepData(fromStep.id, payload, { silent: true });
|
|
793
869
|
if (fromStep.canLeave) {
|
|
794
|
-
const allowed = await callHook(
|
|
795
|
-
|
|
796
|
-
|
|
870
|
+
const allowed = await callHook(
|
|
871
|
+
fromStep.canLeave,
|
|
872
|
+
[createContext({ direction, targetId: toStep.id })],
|
|
873
|
+
token
|
|
874
|
+
);
|
|
797
875
|
if (allowed === false) {
|
|
798
876
|
throw new Error(`Flow: step "${fromStep.id}" blocked leaving.`);
|
|
799
877
|
}
|
|
800
878
|
}
|
|
801
879
|
if (toStep.canEnter) {
|
|
802
|
-
const allowed = await callHook(
|
|
803
|
-
|
|
804
|
-
|
|
880
|
+
const allowed = await callHook(
|
|
881
|
+
toStep.canEnter,
|
|
882
|
+
[createContext({ direction, fromId: fromStep.id, step: toStep })],
|
|
883
|
+
token
|
|
884
|
+
);
|
|
805
885
|
if (allowed === false) {
|
|
806
886
|
throw new Error(`Flow: step "${toStep.id}" blocked entering.`);
|
|
807
887
|
}
|
|
808
888
|
}
|
|
809
|
-
await callHook(
|
|
810
|
-
|
|
811
|
-
|
|
889
|
+
await callHook(
|
|
890
|
+
fromStep.onLeave,
|
|
891
|
+
[createContext({ direction, step: fromStep, targetId: toStep.id })],
|
|
892
|
+
token
|
|
893
|
+
);
|
|
894
|
+
assertActionCurrent(token);
|
|
812
895
|
const previousId = state.currentId;
|
|
813
896
|
const previousIndex = state.currentIndex;
|
|
814
897
|
flushSync(() => {
|
|
@@ -822,12 +905,16 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
822
905
|
state.error = null;
|
|
823
906
|
state.version += 1;
|
|
824
907
|
});
|
|
825
|
-
await callHook(
|
|
826
|
-
|
|
827
|
-
|
|
908
|
+
await callHook(
|
|
909
|
+
toStep.onEnter,
|
|
910
|
+
[createContext({ direction, fromId: previousId, step: toStep })],
|
|
911
|
+
token
|
|
912
|
+
);
|
|
913
|
+
assertActionCurrent(token);
|
|
828
914
|
emitChange(previous);
|
|
829
915
|
} catch (error) {
|
|
830
916
|
if (runtime.destroyed) throw error;
|
|
917
|
+
if (isAbortError(error)) throw error;
|
|
831
918
|
if (props.rollbackOnError) restoreState(rollback);
|
|
832
919
|
handleError(error, previous);
|
|
833
920
|
throw error;
|
|
@@ -837,17 +924,39 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
837
924
|
type: 'next' | 'back',
|
|
838
925
|
step: FlowStep,
|
|
839
926
|
payload: FlowPayload,
|
|
840
|
-
fallbackId: string
|
|
927
|
+
fallbackId: string,
|
|
928
|
+
token: ActionToken
|
|
841
929
|
): Promise<FlowStepResult | FlowPayload | void> => {
|
|
842
930
|
const stepHook = type === 'back' ? step.onBack : step.onNext;
|
|
843
931
|
const globalHook = type === 'back' ? props.onBack : props.onNext;
|
|
844
932
|
const context = createContext({ payload, targetId: fallbackId });
|
|
845
|
-
const stepResult = await callHook(stepHook, [context]);
|
|
933
|
+
const stepResult = await callHook(stepHook, [context], token);
|
|
846
934
|
if (stepResult != null) return stepResult;
|
|
847
|
-
const globalResult = await callHook(globalHook, [context]);
|
|
935
|
+
const globalResult = await callHook(globalHook, [context], token);
|
|
848
936
|
return globalResult ?? fallbackId;
|
|
849
937
|
};
|
|
850
938
|
|
|
939
|
+
const goToInternal = async (
|
|
940
|
+
target: FlowTarget,
|
|
941
|
+
payload: FlowPayload,
|
|
942
|
+
options: InternalFlowGoToOptions = {}
|
|
943
|
+
): Promise<FlowSnapshot | null> =>
|
|
944
|
+
runAction(
|
|
945
|
+
'goTo',
|
|
946
|
+
async (token) => {
|
|
947
|
+
const index = resolveStepIndex(target);
|
|
948
|
+
const to = steps[index];
|
|
949
|
+
const from = currentStep();
|
|
950
|
+
if (to.id === state.currentId) {
|
|
951
|
+
if (payload) setStepData(state.currentId, payload);
|
|
952
|
+
return snapshot();
|
|
953
|
+
}
|
|
954
|
+
await transitionTo(to, options.direction || 'go', payload, from, token);
|
|
955
|
+
return snapshot();
|
|
956
|
+
},
|
|
957
|
+
!!options.internal
|
|
958
|
+
);
|
|
959
|
+
|
|
851
960
|
const createRenderContext = (
|
|
852
961
|
fallback: () => RenderableContent<FlowRenderContext>
|
|
853
962
|
): FlowRenderContext => {
|
|
@@ -856,7 +965,7 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
856
965
|
flow,
|
|
857
966
|
snapshot: current,
|
|
858
967
|
state,
|
|
859
|
-
steps:
|
|
968
|
+
steps: publicStepsMemo(),
|
|
860
969
|
currentStep: current.currentStep,
|
|
861
970
|
currentData: current.currentData,
|
|
862
971
|
data: current.data,
|
|
@@ -1044,16 +1153,16 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
1044
1153
|
return currentStep();
|
|
1045
1154
|
},
|
|
1046
1155
|
get currentData() {
|
|
1047
|
-
return
|
|
1156
|
+
return clonePlainObject(currentDataMemo());
|
|
1048
1157
|
},
|
|
1049
1158
|
get canBack() {
|
|
1050
|
-
return
|
|
1159
|
+
return canBackMemo();
|
|
1051
1160
|
},
|
|
1052
1161
|
get canNext() {
|
|
1053
|
-
return
|
|
1162
|
+
return canNextMemo();
|
|
1054
1163
|
},
|
|
1055
1164
|
get isLast() {
|
|
1056
|
-
return
|
|
1165
|
+
return isLastMemo();
|
|
1057
1166
|
},
|
|
1058
1167
|
build() {
|
|
1059
1168
|
assertActive('build');
|
|
@@ -1093,17 +1202,23 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
1093
1202
|
validateParam('payload', payload, FLOW_PAYLOAD_RULE, 'Flow.next');
|
|
1094
1203
|
const busy = handleBusy('next');
|
|
1095
1204
|
if (busy) return busy;
|
|
1096
|
-
return runAction('next', async () => {
|
|
1205
|
+
return runAction('next', async (token) => {
|
|
1097
1206
|
if (flow.isLast) {
|
|
1098
1207
|
if (payload) setStepData(state.currentId, payload);
|
|
1099
|
-
await callHook(props.onFinish, [snapshot(), flow]);
|
|
1208
|
+
await callHook(props.onFinish, [snapshot(), flow], token);
|
|
1100
1209
|
return snapshot();
|
|
1101
1210
|
}
|
|
1102
1211
|
const from = currentStep();
|
|
1103
1212
|
const fallback = steps[state.currentIndex + 1]?.id;
|
|
1104
|
-
const result = await runMoveHook(
|
|
1213
|
+
const result = await runMoveHook(
|
|
1214
|
+
'next',
|
|
1215
|
+
from,
|
|
1216
|
+
payload,
|
|
1217
|
+
fallback,
|
|
1218
|
+
token
|
|
1219
|
+
);
|
|
1105
1220
|
const target = normalizeStepResult(result, fallback);
|
|
1106
|
-
return
|
|
1221
|
+
return goToInternal(target.id, target.data ?? payload, {
|
|
1107
1222
|
direction: 'next',
|
|
1108
1223
|
internal: true,
|
|
1109
1224
|
});
|
|
@@ -1115,12 +1230,18 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
1115
1230
|
const busy = handleBusy('back');
|
|
1116
1231
|
if (busy) return busy;
|
|
1117
1232
|
if (!flow.canBack) return snapshot();
|
|
1118
|
-
return runAction('back', async () => {
|
|
1233
|
+
return runAction('back', async (token) => {
|
|
1119
1234
|
const from = currentStep();
|
|
1120
1235
|
const fallback = steps[state.currentIndex - 1]?.id;
|
|
1121
|
-
const result = await runMoveHook(
|
|
1236
|
+
const result = await runMoveHook(
|
|
1237
|
+
'back',
|
|
1238
|
+
from,
|
|
1239
|
+
payload,
|
|
1240
|
+
fallback,
|
|
1241
|
+
token
|
|
1242
|
+
);
|
|
1122
1243
|
const target = normalizeStepResult(result, fallback);
|
|
1123
|
-
return
|
|
1244
|
+
return goToInternal(target.id, target.data ?? payload, {
|
|
1124
1245
|
direction: 'back',
|
|
1125
1246
|
internal: true,
|
|
1126
1247
|
});
|
|
@@ -1129,29 +1250,17 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
1129
1250
|
async goTo(target, payload = null, options = {}) {
|
|
1130
1251
|
assertActive('goTo');
|
|
1131
1252
|
validateParam('payload', payload, FLOW_PAYLOAD_RULE, 'Flow.goTo');
|
|
1132
|
-
const busy =
|
|
1253
|
+
const busy = handleBusy('goTo');
|
|
1133
1254
|
if (busy) return busy;
|
|
1134
|
-
return
|
|
1135
|
-
'goTo',
|
|
1136
|
-
async () => {
|
|
1137
|
-
const index = resolveStepIndex(target);
|
|
1138
|
-
const to = steps[index];
|
|
1139
|
-
const from = currentStep();
|
|
1140
|
-
if (to.id === state.currentId) {
|
|
1141
|
-
if (payload) setStepData(state.currentId, payload);
|
|
1142
|
-
return snapshot();
|
|
1143
|
-
}
|
|
1144
|
-
await transitionTo(to, options.direction || 'go', payload, from);
|
|
1145
|
-
return snapshot();
|
|
1146
|
-
},
|
|
1147
|
-
!!options.internal
|
|
1148
|
-
);
|
|
1255
|
+
return goToInternal(target, payload, { direction: options.direction });
|
|
1149
1256
|
},
|
|
1150
1257
|
setData,
|
|
1151
1258
|
setStepData,
|
|
1152
1259
|
getStepData,
|
|
1153
1260
|
reset() {
|
|
1154
1261
|
assertActive('reset');
|
|
1262
|
+
const previous = snapshot();
|
|
1263
|
+
cancelActiveAction();
|
|
1155
1264
|
flushSync(() => {
|
|
1156
1265
|
state.currentId = initialStepId;
|
|
1157
1266
|
state.currentIndex = stepMap.get(initialStepId) ?? 0;
|
|
@@ -1160,16 +1269,13 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
1160
1269
|
state.direction = null;
|
|
1161
1270
|
state.history.splice(0, state.history.length, initialStepId);
|
|
1162
1271
|
replaceObject(state.data, clonePlainObject(initialGlobal));
|
|
1163
|
-
replaceObject(
|
|
1164
|
-
state.stepData,
|
|
1165
|
-
clonePlainObject(initialStepData) as Record<string, FlowData>
|
|
1166
|
-
);
|
|
1272
|
+
replaceObject(state.stepData, cloneStepData(initialStepData));
|
|
1167
1273
|
state.loading = false;
|
|
1168
1274
|
state.busyAction = null;
|
|
1169
1275
|
state.error = null;
|
|
1170
1276
|
state.version += 1;
|
|
1171
1277
|
});
|
|
1172
|
-
emitChange();
|
|
1278
|
+
emitChange(previous);
|
|
1173
1279
|
return flow;
|
|
1174
1280
|
},
|
|
1175
1281
|
async finish(payload = null) {
|
|
@@ -1177,18 +1283,16 @@ export function createFlow(input: FlowProps = {}): Flow {
|
|
|
1177
1283
|
validateParam('payload', payload, FLOW_PAYLOAD_RULE, 'Flow.finish');
|
|
1178
1284
|
const busy = handleBusy('finish');
|
|
1179
1285
|
if (busy) return busy;
|
|
1180
|
-
return runAction('finish', async () => {
|
|
1286
|
+
return runAction('finish', async (token) => {
|
|
1181
1287
|
if (payload) setStepData(state.currentId, payload);
|
|
1182
|
-
await callHook(props.onFinish, [snapshot(), flow]);
|
|
1288
|
+
await callHook(props.onFinish, [snapshot(), flow], token);
|
|
1183
1289
|
return snapshot();
|
|
1184
1290
|
});
|
|
1185
1291
|
},
|
|
1186
1292
|
destroy() {
|
|
1187
1293
|
if (runtime.destroyed) return;
|
|
1188
1294
|
runtime.destroyed = true;
|
|
1189
|
-
|
|
1190
|
-
runtime.actionController = null;
|
|
1191
|
-
runtime.activeAction = null;
|
|
1295
|
+
cancelActiveAction();
|
|
1192
1296
|
ui?.destroy();
|
|
1193
1297
|
ui = null;
|
|
1194
1298
|
for (const cleanup of cleanupTasks) cleanup();
|