-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.ts
110 lines (94 loc) · 2.79 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import * as React from "react";
import { ComponentType, ReactElement, useMemo } from "react";
export type Schema = Record<string, unknown>;
export type Entry<T extends Schema> = {
[K in keyof T]: readonly [K, T[K]];
}[keyof T];
export type Step<
T extends Schema,
K extends keyof T & string = keyof T & string,
> = {
key: K;
component: ComponentType<MozardStepProps<T[K]>>;
props: Omit<MozardStepProps<T[K]>, "onSubmit">;
};
export type StepConstructor<T extends Schema> = <
K extends keyof T & string,
P extends MozardStepProps<T[K]>,
>(
key: K,
component: ComponentType<P>,
props: Omit<P, "onSubmit">,
) => Generator<Step<T, K>, T[K], T[K]>;
export type MozardConfig<T extends Schema, R> = {
values: Entry<T>[];
onNext(entries: Entry<T>[]): unknown;
do(step: StepConstructor<T>): Generator<Step<T>, R, unknown>;
};
export type MozardStepProps<T> = {
onSubmit(data: T): unknown;
};
export type UseMozardReturn<T extends Schema, R> = {
elements: ReactElement[];
get<K extends keyof T & string>(key: K): T[K] | undefined;
} & ({ done: true; value: R } | { done: false; value?: undefined });
export const useMozard = <T extends Schema, R>(
config: MozardConfig<T, R>,
deps: readonly unknown[],
) => {
const { values, onNext } = config;
const get = <K extends keyof T & string>(key: K) => {
for (const i of values) {
if (i[0] === key) {
return i[1] as T[K];
}
}
};
return useMemo((): UseMozardReturn<T, R> => {
const elements: ReactElement[] = [];
const i = config.do(function* <
K extends keyof T & string,
P extends MozardStepProps<T[K]>,
>(
key: K,
component: ComponentType<P>,
props: Omit<P, "onSubmit">,
): Generator<Step<T, K>, T[K], T[K]> {
return yield { key, component: component as any, props };
});
let index = 0;
let lastValue;
while (true) {
const { value, done } = i.next(lastValue);
if (done) {
return { elements, done, value, get };
}
const { key, component, props } = value;
const currentIndex = index;
elements.push(
React.createElement(component, {
...props,
key,
onSubmit(data) {
const newEntry: Entry<T> = [key, data];
onNext(
currentIndex < values.length
? values.with(currentIndex, newEntry)
: values.concat([newEntry]),
);
},
}),
);
if (index >= values.length) {
return { elements, done: false, get };
}
const [oldKey, oldValue] = values[index];
if (oldKey !== key) {
return { elements, done: false, get };
}
index++;
lastValue = oldValue;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [values, ...deps]);
};