@kirill.konshin/utils 0.0.13 → 0.0.15
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/.ctirc +2 -1
- package/.turbo/turbo-build.log +28 -23
- package/.turbo/turbo-test.log +78 -25
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/demo/{cache-demo-clean.ts → worker-demo.ts} +1 -1
- package/dist/bootstrap/controls.d.ts +5 -2
- package/dist/bootstrap/controls.d.ts.map +1 -1
- package/dist/bootstrap/controls.js +1 -0
- package/dist/bootstrap/controls.js.map +1 -1
- package/dist/bootstrap/controls.stories.d.ts +9 -0
- package/dist/bootstrap/controls.stories.d.ts.map +1 -0
- package/dist/bootstrap/error.js +1 -0
- package/dist/bootstrap/error.js.map +1 -1
- package/dist/bootstrap/error.stories.d.ts +8 -0
- package/dist/bootstrap/error.stories.d.ts.map +1 -0
- package/dist/bootstrap/field.stories.d.ts +8 -0
- package/dist/bootstrap/field.stories.d.ts.map +1 -0
- package/dist/bootstrap/globalLoading.stories.d.ts +7 -0
- package/dist/bootstrap/globalLoading.stories.d.ts.map +1 -0
- package/dist/bootstrap/index.js +2 -1
- package/dist/bootstrap/loading.stories.d.ts +9 -0
- package/dist/bootstrap/loading.stories.d.ts.map +1 -0
- package/dist/bootstrap/responsiveHelper.js +1 -0
- package/dist/bootstrap/responsiveHelper.js.map +1 -1
- package/dist/bootstrap/screen.stories.d.ts +14 -0
- package/dist/bootstrap/screen.stories.d.ts.map +1 -0
- package/dist/bootstrap/toaster.js +1 -0
- package/dist/bootstrap/toaster.js.map +1 -1
- package/dist/bootstrap/toaster.stories.d.ts +8 -0
- package/dist/bootstrap/toaster.stories.d.ts.map +1 -0
- package/dist/bootstrap/useModal.stories.d.ts +9 -0
- package/dist/bootstrap/useModal.stories.d.ts.map +1 -0
- package/dist/core/cache.d.ts +7 -8
- package/dist/core/cache.d.ts.map +1 -1
- package/dist/core/cache.js +7 -4
- package/dist/core/cache.js.map +1 -1
- package/dist/core/cache.test.d.ts +2 -0
- package/dist/core/cache.test.d.ts.map +1 -0
- package/dist/core/files.d.ts +3 -0
- package/dist/core/files.d.ts.map +1 -0
- package/dist/core/files.js +27 -0
- package/dist/core/files.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +14 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/ls.d.ts +6 -0
- package/dist/core/ls.d.ts.map +1 -0
- package/dist/core/ls.js +12 -0
- package/dist/core/ls.js.map +1 -0
- package/dist/core/worker.d.ts +23 -9
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/core/worker.js +93 -90
- package/dist/core/worker.js.map +1 -1
- package/dist/core/worker.test.d.ts +2 -0
- package/dist/core/worker.test.d.ts.map +1 -0
- package/dist/core/workerUtils.d.ts +3 -0
- package/dist/core/workerUtils.d.ts.map +1 -0
- package/dist/core/workerUtils.js +31 -0
- package/dist/core/workerUtils.js.map +1 -0
- package/dist/core/wrpc.d.ts +45 -0
- package/dist/core/wrpc.d.ts.map +1 -0
- package/dist/core/wrpc.fixture.d.ts +29 -0
- package/dist/core/wrpc.fixture.d.ts.map +1 -0
- package/dist/core/wrpc.js +242 -0
- package/dist/core/wrpc.js.map +1 -0
- package/dist/core/wrpc.test.d.ts +1 -0
- package/dist/core/wrpc.test.d.ts.map +1 -0
- package/dist/mui/formControlFieldset.stories.d.ts +7 -0
- package/dist/mui/formControlFieldset.stories.d.ts.map +1 -0
- package/dist/next/appLink.js +1 -0
- package/dist/next/appLink.js.map +1 -1
- package/dist/next/noSSR.js +1 -0
- package/dist/next/noSSR.js.map +1 -1
- package/dist/next/useIsInner.js +1 -0
- package/dist/next/useIsInner.js.map +1 -1
- package/dist/react/form/client.js +1 -0
- package/dist/react/form/client.js.map +1 -1
- package/dist/react/form/form.d.ts +7 -6
- package/dist/react/form/form.d.ts.map +1 -1
- package/dist/react/form/form.js +14 -20
- package/dist/react/form/form.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +8 -2
- package/dist/react/index.js.map +1 -1
- package/dist/react/keyboard.d.ts +11 -0
- package/dist/react/keyboard.d.ts.map +1 -0
- package/dist/react/keyboard.js +38 -0
- package/dist/react/keyboard.js.map +1 -0
- package/dist/react/useFetch.js +1 -0
- package/dist/react/useFetch.js.map +1 -1
- package/dist/tailwind/responsiveHelper.js +1 -0
- package/dist/tailwind/responsiveHelper.js.map +1 -1
- package/package.json +6 -2
- package/src/bootstrap/controls.stories.tsx +2 -2
- package/src/bootstrap/controls.tsx +3 -3
- package/src/bootstrap/error.stories.tsx +3 -3
- package/src/bootstrap/field.stories.tsx +2 -2
- package/src/bootstrap/globalLoading.stories.tsx +2 -2
- package/src/bootstrap/loading.stories.tsx +2 -2
- package/src/bootstrap/screen.stories.tsx +2 -2
- package/src/bootstrap/toaster.stories.tsx +2 -2
- package/src/bootstrap/useModal.stories.tsx +3 -3
- package/src/core/README.md +91 -0
- package/src/core/cache.ts +12 -12
- package/src/core/files.ts +24 -0
- package/src/core/index.ts +4 -0
- package/src/core/ls.ts +11 -0
- package/src/core/worker.test.ts +197 -0
- package/src/core/worker.ts +106 -93
- package/src/core/workerUtils.ts +32 -0
- package/src/core/wrpc.fixture.ts +72 -0
- package/src/core/wrpc.test.ts +225 -0
- package/src/core/wrpc.ts +359 -0
- package/src/mui/formControlFieldset.stories.tsx +1 -1
- package/src/react/form/form.tsx +53 -33
- package/src/react/index.ts +1 -0
- package/src/react/keyboard.tsx +49 -0
- package/src/tailwind/README.md +8 -0
- package/tsconfig.json +2 -1
- package/vite.config.ts +13 -4
- package/vite.exports.ts +3 -3
- package/demo/cache-demo.ts +0 -343
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../../src/react/form/form.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,CAAC,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../../src/react/form/form.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,CAAC,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAM3E,eAAO,MAAM,cAAc,mBAAgE,CAAC;AAC5F,eAAO,MAAM,SAAS,WAAY,CAAC,CAAC,SAAS,KAAG,MAA+B,CAAC;AAChF,eAAO,MAAM,SAAS,WAAY,CAAC,CAAC,SAAS,KAAG,MAA+B,CAAC;AAChF,eAAO,MAAM,UAAU,WAAY,CAAC,CAAC,SAAS,KAAG,OAAgC,CAAC;AAElF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,SAAS,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAChG,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,SAAS,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;AACzF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,SAAS,IACpC;IACI,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC;CAClB,GACD;IACI,OAAO,EAAE,KAAK,CAAC;IACf,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CACtB,CAAC;AAER,eAAO,MAAM,WAAW;YACZ,SAAS;EACJ,CAAC;AAElB,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,SAAS,wBAAwB;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,4CAG3F,CAAC;AAKF,wBAAgB,MAAM,CAAC,CAAC,SAAS,SAAS,EAAE,MAAM,EAAE,CAAC;qBAKzB,MAAM,MAAM,CAAC,CAAC,CAAC,SAAS,WAAW,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,CAAC,CAAC,QAAO,OAAO;;;;;;;;;;;yBA2BpE,QAAQ,KAAG,UAAU,CAAC,CAAC,CAAC;4BARrB,WAAW,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,CAAC,CAAC,KAAG,UAAU,CAAC,CAAC,CAAC;EAwBnF;AAuCD,wBAAgB,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,2CAE3E"}
|
package/dist/react/form/form.js
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import { jsx
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { createContext, useMemo } from "react";
|
|
4
|
+
import "clsx";
|
|
3
5
|
const nonEmpty = "This field cannot be empty";
|
|
4
6
|
const stringRequired = () => z.string({ required_error: nonEmpty }).min(1, nonEmpty);
|
|
5
7
|
const maxLength = (schema) => schema.maxLength || 0;
|
|
6
8
|
const minLength = (schema) => schema.minLength || 0;
|
|
7
9
|
const isRequired = (schema) => minLength(schema) > 0;
|
|
10
|
+
const FormContext = createContext(null);
|
|
11
|
+
const Form = ({ schema, children }) => {
|
|
12
|
+
const value = useMemo(() => ({ schema }), [schema]);
|
|
13
|
+
return /* @__PURE__ */ jsx(FormContext.Provider, { value, children });
|
|
14
|
+
};
|
|
15
|
+
const getShape = (schema) => schema.shape || schema.sourceType().shape;
|
|
8
16
|
function create(schema) {
|
|
9
|
-
|
|
10
|
-
if (!getShape()) {
|
|
17
|
+
if (!getShape(schema)) {
|
|
11
18
|
throw new Error("Invalid schema: only z.object() or z.object().refine() are supported");
|
|
12
19
|
}
|
|
13
20
|
function register(name, data, errors, mui = false) {
|
|
14
21
|
var _a, _b;
|
|
15
|
-
const field = getShape()[name];
|
|
22
|
+
const field = getShape(schema)[name];
|
|
16
23
|
return {
|
|
17
24
|
name,
|
|
18
25
|
id: name,
|
|
@@ -44,27 +51,14 @@ function create(schema) {
|
|
|
44
51
|
}
|
|
45
52
|
return { success: true, data };
|
|
46
53
|
}
|
|
47
|
-
|
|
48
|
-
children,
|
|
49
|
-
name,
|
|
50
|
-
errors,
|
|
51
|
-
hint
|
|
52
|
-
}) {
|
|
53
|
-
var _a;
|
|
54
|
-
const { description } = getShape()[name];
|
|
55
|
-
return /* @__PURE__ */ jsxs("div", { className: "form-row", children: [
|
|
56
|
-
description && /* @__PURE__ */ jsx("label", { htmlFor: name, children: description }),
|
|
57
|
-
children,
|
|
58
|
-
hint && /* @__PURE__ */ jsx(Hint, { children: hint }),
|
|
59
|
-
(_a = errors == null ? void 0 : errors[name]) == null ? void 0 : _a.map((e) => /* @__PURE__ */ jsx(Hint, { error: true, children: e }, e))
|
|
60
|
-
] });
|
|
61
|
-
}
|
|
62
|
-
return { register, validate, Field, validationError };
|
|
54
|
+
return { register, validate, validationError };
|
|
63
55
|
}
|
|
64
56
|
function Hint({ children, error }) {
|
|
65
57
|
return /* @__PURE__ */ jsx("div", { className: `hint ${error ? "hint-error" : ""}`, children });
|
|
66
58
|
}
|
|
67
59
|
export {
|
|
60
|
+
Form,
|
|
61
|
+
FormContext,
|
|
68
62
|
Hint,
|
|
69
63
|
create,
|
|
70
64
|
isRequired,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.js","sources":["../../../src/react/form/form.tsx"],"sourcesContent":["import { typeToFlattenedError, z, SafeParseReturnType, TypeOf } from 'zod';\n\nconst nonEmpty = 'This field cannot be empty';\n\nexport const stringRequired = () => z.string({ required_error: nonEmpty }).min(1, nonEmpty);\nexport const maxLength = (schema: z.ZodString): number => schema.maxLength || 0;\nexport const minLength = (schema: z.ZodString): number => schema.minLength || 0;\nexport const isRequired = (schema: z.ZodString): boolean => minLength(schema) > 0;\n\nexport type ZodObject = z.ZodObject<any> | z.ZodEffects<z.ZodObject<any>>; // z.ZodType<any, any, any>\nexport type MaybeTypeOf<S extends ZodObject> = Partial<TypeOf<S>>;\nexport type SafeTypeOf<S extends ZodObject> = SafeParseReturnType<TypeOf<S>, TypeOf<S>>['data'];\nexport type Errors<S extends ZodObject> = typeToFlattenedError<TypeOf<S>>['fieldErrors'];\nexport type Validation<S extends ZodObject> =\n | {\n success: true; // this is true only if form was validated successfully\n data: SafeTypeOf<S>;\n errors?: never;\n }\n | {\n success: false;\n data?: MaybeTypeOf<S>;\n errors?: Errors<S>;\n };\n\nexport
|
|
1
|
+
{"version":3,"file":"form.js","sources":["../../../src/react/form/form.tsx"],"sourcesContent":["import { typeToFlattenedError, z, SafeParseReturnType, TypeOf } from 'zod';\nimport { createContext, useContext, useMemo } from 'react';\nimport clsx from 'clsx';\n\nconst nonEmpty = 'This field cannot be empty';\n\nexport const stringRequired = () => z.string({ required_error: nonEmpty }).min(1, nonEmpty);\nexport const maxLength = (schema: z.ZodString): number => schema.maxLength || 0;\nexport const minLength = (schema: z.ZodString): number => schema.minLength || 0;\nexport const isRequired = (schema: z.ZodString): boolean => minLength(schema) > 0;\n\nexport type ZodObject = z.ZodObject<any> | z.ZodEffects<z.ZodObject<any>>; // z.ZodType<any, any, any>\nexport type MaybeTypeOf<S extends ZodObject> = Partial<TypeOf<S>>;\nexport type SafeTypeOf<S extends ZodObject> = SafeParseReturnType<TypeOf<S>, TypeOf<S>>['data'];\nexport type Errors<S extends ZodObject> = typeToFlattenedError<TypeOf<S>>['fieldErrors'];\nexport type Validation<S extends ZodObject> =\n | {\n success: true; // this is true only if form was validated successfully\n data: SafeTypeOf<S>;\n errors?: never;\n }\n | {\n success: false;\n data?: MaybeTypeOf<S>;\n errors?: Errors<S>;\n };\n\nexport const FormContext = createContext<{\n schema: ZodObject;\n}>(null as never);\n\nexport const Form = <S extends ZodObject>({ schema, children }: { schema: S; children: any }) => {\n const value = useMemo(() => ({ schema }), [schema]);\n return <FormContext.Provider value={value}>{children}</FormContext.Provider>;\n};\n\nconst getShape = <S extends ZodObject>(schema: S) =>\n (schema as z.ZodObject<any>).shape || (schema as z.ZodEffects<z.ZodObject<any>>).sourceType().shape;\n\nexport function create<S extends ZodObject>(schema: S) {\n if (!getShape(schema)) {\n throw new Error('Invalid schema: only z.object() or z.object().refine() are supported');\n }\n\n function register(name: keyof TypeOf<S>, data?: MaybeTypeOf<S>, errors?: Errors<S>, mui: boolean = false) {\n const field = getShape(schema)[name];\n return {\n name,\n id: name,\n required: isRequired(field),\n maxLength: maxLength(field),\n type: field.isEmail ? 'email' : (name as string).includes('password') ? 'password' : 'text',\n defaultValue: data?.[name],\n ...(mui\n ? {\n label: field.description,\n helperText: errors?.[name]?.join(', '),\n error: !!errors?.[name]?.length,\n }\n : {}),\n };\n }\n\n function validationError(data: MaybeTypeOf<S>, errors: Errors<S>): Validation<S> {\n return {\n success: false,\n data, // data is undefined if there are errors\n errors, // Next.js will butcher error object, so we provide something more primitive\n };\n }\n\n function validate(formData: FormData): Validation<S> {\n const rawData = Object.fromEntries(formData) as TypeOf<S>;\n const { error, data } = schema.safeParse(rawData);\n\n // console.log('Validate result', { error, data, rawData });\n\n if (error) {\n // data is undefined if there are errors\n // Next.js will butcher error object, so we provide something more primitive\n return validationError(rawData, error.flatten().fieldErrors as any);\n }\n\n return { success: true, data };\n }\n\n return { register, validate, validationError };\n}\n\nfunction Field<S extends ZodObject>({\n children,\n name,\n errors,\n hint,\n className,\n labelProps,\n ...props\n}: {\n children?: any;\n name: keyof TypeOf<S>;\n errors?: Validation<S>['errors'];\n hint?: string;\n className?: string;\n labelProps?: any;\n}) {\n const { schema } = useContext(FormContext);\n const { description } = getShape(schema)[name];\n\n return (\n <div {...props} className={clsx('form-row', className)}>\n {description && (\n <label {...labelProps} htmlFor={name}>\n {description}\n </label>\n )}\n {children}\n {hint && <Hint>{hint}</Hint>}\n {errors?.[name]?.map((e: string) => (\n <Hint error key={e}>\n {e}\n </Hint>\n ))}\n </div>\n );\n}\n\nexport function Hint({ children, error }: { children: any; error?: boolean }) {\n return <div className={`hint ${error ? 'hint-error' : ''}`}>{children}</div>;\n}\n"],"names":[],"mappings":";;;;AAIA,MAAM,WAAW;AAEJ,MAAA,iBAAiB,MAAM,EAAE,OAAO,EAAE,gBAAgB,UAAU,EAAE,IAAI,GAAG,QAAQ;AACnF,MAAM,YAAY,CAAC,WAAgC,OAAO,aAAa;AACvE,MAAM,YAAY,CAAC,WAAgC,OAAO,aAAa;AACvE,MAAM,aAAa,CAAC,WAAiC,UAAU,MAAM,IAAI;AAkBnE,MAAA,cAAc,cAExB,IAAa;AAET,MAAM,OAAO,CAAsB,EAAE,QAAQ,eAA6C;AACvF,QAAA,QAAQ,QAAQ,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC;AAClD,SAAQ,oBAAA,YAAY,UAAZ,EAAqB,OAAe,SAAS,CAAA;AACzD;AAEA,MAAM,WAAW,CAAsB,WAClC,OAA4B,SAAU,OAA0C,aAAa;AAE3F,SAAS,OAA4B,QAAW;AAC/C,MAAA,CAAC,SAAS,MAAM,GAAG;AACb,UAAA,IAAI,MAAM,sEAAsE;AAAA,EAAA;AAG1F,WAAS,SAAS,MAAuB,MAAuB,QAAoB,MAAe,OAAO;;AACtG,UAAM,QAAQ,SAAS,MAAM,EAAE,IAAI;AAC5B,WAAA;AAAA,MACH;AAAA,MACA,IAAI;AAAA,MACJ,UAAU,WAAW,KAAK;AAAA,MAC1B,WAAW,UAAU,KAAK;AAAA,MAC1B,MAAM,MAAM,UAAU,UAAW,KAAgB,SAAS,UAAU,IAAI,aAAa;AAAA,MACrF,cAAc,6BAAO;AAAA,MACrB,GAAI,MACE;AAAA,QACI,OAAO,MAAM;AAAA,QACb,aAAY,sCAAS,UAAT,mBAAgB,KAAK;AAAA,QACjC,OAAO,CAAC,GAAC,sCAAS,UAAT,mBAAgB;AAAA,MAAA,IAE7B,CAAA;AAAA,IACV;AAAA,EAAA;AAGK,WAAA,gBAAgB,MAAsB,QAAkC;AACtE,WAAA;AAAA,MACH,SAAS;AAAA,MACT;AAAA;AAAA,MACA;AAAA;AAAA,IACJ;AAAA,EAAA;AAGJ,WAAS,SAAS,UAAmC;AAC3C,UAAA,UAAU,OAAO,YAAY,QAAQ;AAC3C,UAAM,EAAE,OAAO,KAAA,IAAS,OAAO,UAAU,OAAO;AAIhD,QAAI,OAAO;AAGP,aAAO,gBAAgB,SAAS,MAAM,QAAA,EAAU,WAAkB;AAAA,IAAA;AAG/D,WAAA,EAAE,SAAS,MAAM,KAAK;AAAA,EAAA;AAG1B,SAAA,EAAE,UAAU,UAAU,gBAAgB;AACjD;AAuCO,SAAS,KAAK,EAAE,UAAU,SAA6C;AACnE,SAAA,oBAAC,SAAI,WAAW,QAAQ,QAAQ,eAAe,EAAE,IAAK,SAAS,CAAA;AAC1E;"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { createClient } from "./form/client.js";
|
|
2
|
-
import { Hint, create, isRequired, maxLength, minLength, stringRequired } from "./form/form.js";
|
|
2
|
+
import { Form, FormContext, Hint, create, isRequired, maxLength, minLength, stringRequired } from "./form/form.js";
|
|
3
3
|
import { apiCall, jsonContentType } from "./apiCall.js";
|
|
4
|
+
import { HotkeysContext, HotkeysProvider, useHotkeys } from "./keyboard.js";
|
|
4
5
|
import { useFetch } from "./useFetch.js";
|
|
5
6
|
import { useFetcher } from "./useFetcher.js";
|
|
6
7
|
export {
|
|
8
|
+
Form,
|
|
9
|
+
FormContext,
|
|
7
10
|
Hint,
|
|
11
|
+
HotkeysContext,
|
|
12
|
+
HotkeysProvider,
|
|
8
13
|
apiCall,
|
|
9
14
|
create,
|
|
10
15
|
createClient,
|
|
@@ -14,6 +19,7 @@ export {
|
|
|
14
19
|
minLength,
|
|
15
20
|
stringRequired,
|
|
16
21
|
useFetch,
|
|
17
|
-
useFetcher
|
|
22
|
+
useFetcher,
|
|
23
|
+
useHotkeys
|
|
18
24
|
};
|
|
19
25
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
export type Hotkeys = Record<KeyboardEvent['code'], (e: KeyboardEvent) => void>;
|
|
3
|
+
export declare const HotkeysContext: import('react').Context<{
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
setEnabled: Dispatch<SetStateAction<boolean>>;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const HotkeysProvider: ({ children }: {
|
|
8
|
+
children: any;
|
|
9
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare const useHotkeys: (hotkeys: Hotkeys) => void;
|
|
11
|
+
//# sourceMappingURL=keyboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../src/react/keyboard.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA+C,QAAQ,EAAE,cAAc,EAAc,MAAM,OAAO,CAAC;AAM1G,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC,CAAC;AAEhF,eAAO,MAAM,cAAc;aACd,OAAO;gBACJ,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;EAChC,CAAC;AAElB,eAAO,MAAM,eAAe;;6CAM3B,CAAC;AAEF,eAAO,MAAM,UAAU,YAAa,OAAO,SAyB1C,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useState, useMemo, useContext, useEffect } from "react";
|
|
4
|
+
const isCtrlOrMeta = (e) => e.metaKey || e.ctrlKey;
|
|
5
|
+
const EVENT = "keydown";
|
|
6
|
+
const HotkeysContext = createContext(null);
|
|
7
|
+
const HotkeysProvider = ({ children }) => {
|
|
8
|
+
const [enabled, setEnabled] = useState(true);
|
|
9
|
+
const control = useMemo(() => ({ enabled, setEnabled }), [enabled, setEnabled]);
|
|
10
|
+
return /* @__PURE__ */ jsx(HotkeysContext.Provider, { value: control, children });
|
|
11
|
+
};
|
|
12
|
+
const useHotkeys = (hotkeys) => {
|
|
13
|
+
const { enabled } = useContext(HotkeysContext);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (typeof document === "undefined" || !enabled) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const listeners = (e) => {
|
|
19
|
+
if (!isCtrlOrMeta(e)) return;
|
|
20
|
+
for (const [code, callback] of Object.entries(hotkeys)) {
|
|
21
|
+
if (e.code === code) {
|
|
22
|
+
callback(e);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
window.addEventListener(EVENT, listeners, true);
|
|
28
|
+
return () => {
|
|
29
|
+
window.removeEventListener(EVENT, listeners);
|
|
30
|
+
};
|
|
31
|
+
}, [hotkeys, enabled]);
|
|
32
|
+
};
|
|
33
|
+
export {
|
|
34
|
+
HotkeysContext,
|
|
35
|
+
HotkeysProvider,
|
|
36
|
+
useHotkeys
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=keyboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard.js","sources":["../../src/react/keyboard.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, createContext, useMemo, useState, Dispatch, SetStateAction, useContext } from 'react';\n\nconst isCtrlOrMeta = (e: KeyboardEvent) => e.metaKey || e.ctrlKey;\n\nconst EVENT = 'keydown';\n\nexport type Hotkeys = Record<KeyboardEvent['code'], (e: KeyboardEvent) => void>;\n\nexport const HotkeysContext = createContext<{\n enabled: boolean;\n setEnabled: Dispatch<SetStateAction<boolean>>;\n}>(null as never);\n\nexport const HotkeysProvider = ({ children }) => {\n const [enabled, setEnabled] = useState(true);\n\n const control = useMemo(() => ({ enabled, setEnabled }), [enabled, setEnabled]);\n\n return <HotkeysContext.Provider value={control}>{children}</HotkeysContext.Provider>;\n};\n\nexport const useHotkeys = (hotkeys: Hotkeys) => {\n const { enabled } = useContext(HotkeysContext);\n\n useEffect(() => {\n if (typeof document === 'undefined' || !enabled) {\n return;\n }\n\n const listeners = (e: KeyboardEvent) => {\n if (!isCtrlOrMeta(e)) return;\n\n for (const [code, callback] of Object.entries(hotkeys)) {\n if (e.code === code) {\n callback(e);\n return;\n }\n }\n };\n\n window.addEventListener(EVENT, listeners, true);\n\n return () => {\n window.removeEventListener(EVENT, listeners);\n };\n }, [hotkeys, enabled]);\n};\n"],"names":[],"mappings":";;;AAIA,MAAM,eAAe,CAAC,MAAqB,EAAE,WAAW,EAAE;AAE1D,MAAM,QAAQ;AAID,MAAA,iBAAiB,cAG3B,IAAa;AAET,MAAM,kBAAkB,CAAC,EAAE,eAAe;AAC7C,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAErC,QAAA,UAAU,QAAQ,OAAO,EAAE,SAAS,eAAe,CAAC,SAAS,UAAU,CAAC;AAE9E,6BAAQ,eAAe,UAAf,EAAwB,OAAO,SAAU,UAAS;AAC9D;AAEa,MAAA,aAAa,CAAC,YAAqB;AAC5C,QAAM,EAAE,QAAA,IAAY,WAAW,cAAc;AAE7C,YAAU,MAAM;AACZ,QAAI,OAAO,aAAa,eAAe,CAAC,SAAS;AAC7C;AAAA,IAAA;AAGE,UAAA,YAAY,CAAC,MAAqB;AAChC,UAAA,CAAC,aAAa,CAAC,EAAG;AAEtB,iBAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,OAAO,GAAG;AAChD,YAAA,EAAE,SAAS,MAAM;AACjB,mBAAS,CAAC;AACV;AAAA,QAAA;AAAA,MACJ;AAAA,IAER;AAEO,WAAA,iBAAiB,OAAO,WAAW,IAAI;AAE9C,WAAO,MAAM;AACF,aAAA,oBAAoB,OAAO,SAAS;AAAA,IAC/C;AAAA,EAAA,GACD,CAAC,SAAS,OAAO,CAAC;AACzB;"}
|
package/dist/react/useFetch.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFetch.js","sources":["../../src/react/useFetch.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useState, useTransition } from 'react';\n\n//TODO useFetch https://use-http.com\n//TODO SWR?\n//TODO Tanstack Query?\nexport function useFetch<R>(\n fn: (...args: any[]) => Promise<R>,\n defaultValue: R | null = null,\n): [R | null, typeof fn, boolean, Error | undefined] {\n // An async function was passed to useActionState, but it was dispatched outside of an action context.\n // This is likely not what you intended. Either pass the dispatch function to an `action` prop, or dispatch manually inside `startTransition`\n const [isPending, startTransition] = useTransition();\n const [data, setData] = useState<R | null>(defaultValue);\n const [error, setError] = useState<Error>();\n\n const actionFn = useCallback(\n (...args: Parameters<typeof fn>) => {\n const promise = fn(...args);\n // https://react.dev/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition\n startTransition(() => promise.then(setData).catch(setError)); //FIXME sub-chain...\n return promise;\n },\n [fn],\n );\n\n return [data, actionFn, isPending, error];\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useFetch.js","sources":["../../src/react/useFetch.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useState, useTransition } from 'react';\n\n//TODO useFetch https://use-http.com\n//TODO SWR?\n//TODO Tanstack Query?\nexport function useFetch<R>(\n fn: (...args: any[]) => Promise<R>,\n defaultValue: R | null = null,\n): [R | null, typeof fn, boolean, Error | undefined] {\n // An async function was passed to useActionState, but it was dispatched outside of an action context.\n // This is likely not what you intended. Either pass the dispatch function to an `action` prop, or dispatch manually inside `startTransition`\n const [isPending, startTransition] = useTransition();\n const [data, setData] = useState<R | null>(defaultValue);\n const [error, setError] = useState<Error>();\n\n const actionFn = useCallback(\n (...args: Parameters<typeof fn>) => {\n const promise = fn(...args);\n // https://react.dev/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition\n startTransition(() => promise.then(setData).catch(setError)); //FIXME sub-chain...\n return promise;\n },\n [fn],\n );\n\n return [data, actionFn, isPending, error];\n}\n"],"names":[],"mappings":";;AAOgB,SAAA,SACZ,IACA,eAAyB,MACwB;AAGjD,QAAM,CAAC,WAAW,eAAe,IAAI,cAAc;AACnD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAmB,YAAY;AACvD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAgB;AAE1C,QAAM,WAAW;AAAA,IACb,IAAI,SAAgC;AAC1B,YAAA,UAAU,GAAG,GAAG,IAAI;AAE1B,sBAAgB,MAAM,QAAQ,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AACpD,aAAA;AAAA,IACX;AAAA,IACA,CAAC,EAAE;AAAA,EACP;AAEA,SAAO,CAAC,MAAM,UAAU,WAAW,KAAK;AAC5C;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responsiveHelper.js","sources":["../../src/tailwind/responsiveHelper.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nexport function ResponsiveHelperTW() {\n return (\n <span className=\"fixed bottom-0 right-2 z-30 leading-none text-xs\">\n <small className=\"hidden xs:max-sm:block\">XS</small>\n <small className=\"hidden sm:max-md:block\">SM</small>\n <small className=\"hidden md:max-lg:block\">MD</small>\n <small className=\"hidden lg:max-xl:block\">LG</small>\n <small className=\"hidden xl:max-2xl:block\">XL</small>\n <small className=\"hidden 2xl:block\">XXL</small>\n </span>\n );\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"responsiveHelper.js","sources":["../../src/tailwind/responsiveHelper.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nexport function ResponsiveHelperTW() {\n return (\n <span className=\"fixed bottom-0 right-2 z-30 leading-none text-xs\">\n <small className=\"hidden xs:max-sm:block\">XS</small>\n <small className=\"hidden sm:max-md:block\">SM</small>\n <small className=\"hidden md:max-lg:block\">MD</small>\n <small className=\"hidden lg:max-xl:block\">LG</small>\n <small className=\"hidden xl:max-2xl:block\">XL</small>\n <small className=\"hidden 2xl:block\">XXL</small>\n </span>\n );\n}\n"],"names":[],"mappings":";;AAIO,SAAS,qBAAqB;AAE7B,SAAA,qBAAC,QAAK,EAAA,WAAU,oDACZ,UAAA;AAAA,IAAC,oBAAA,SAAA,EAAM,WAAU,0BAAyB,UAAE,MAAA;AAAA,IAC3C,oBAAA,SAAA,EAAM,WAAU,0BAAyB,UAAE,MAAA;AAAA,IAC3C,oBAAA,SAAA,EAAM,WAAU,0BAAyB,UAAE,MAAA;AAAA,IAC3C,oBAAA,SAAA,EAAM,WAAU,0BAAyB,UAAE,MAAA;AAAA,IAC3C,oBAAA,SAAA,EAAM,WAAU,2BAA0B,UAAE,MAAA;AAAA,IAC5C,oBAAA,SAAA,EAAM,WAAU,oBAAmB,UAAG,MAAA,CAAA;AAAA,EAAA,GAC3C;AAER;"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kirill.konshin/utils",
|
|
3
3
|
"description": "Utilities",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.15",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"----- BUILD -----": "",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"expo-file-system": "^18.0.7",
|
|
25
25
|
"expo-sharing": "^13.0.1",
|
|
26
26
|
"expo-updates": "^0.26.13",
|
|
27
|
+
"file-saver": "^2.0.5",
|
|
27
28
|
"many-keys-map": "^2.0.1",
|
|
28
29
|
"psd": "^3.4.0",
|
|
29
30
|
"react-hook-form": "^7.54.2",
|
|
@@ -43,8 +44,10 @@
|
|
|
43
44
|
"@storybook/react": "^8.5.2",
|
|
44
45
|
"@storybook/react-vite": "^8.5.2",
|
|
45
46
|
"@storybook/test": "^8.5.2",
|
|
47
|
+
"@types/file-saver": "^2",
|
|
46
48
|
"@types/psd": "^3.4.3",
|
|
47
49
|
"@vitest/coverage-v8": "^3.0.4",
|
|
50
|
+
"@vitest/web-worker": "^3.0.5",
|
|
48
51
|
"bootstrap": "^5.3.3",
|
|
49
52
|
"bootstrap-icons": "^1.11.3",
|
|
50
53
|
"create-ts-index": "^1.14.0",
|
|
@@ -57,6 +60,7 @@
|
|
|
57
60
|
"next": "^15.1.6",
|
|
58
61
|
"react-bootstrap": "^2.10.8",
|
|
59
62
|
"react-native": "^0.77.0",
|
|
63
|
+
"rollup-preserve-directives": "^1.1.3",
|
|
60
64
|
"sass-embedded": "^1.83.4",
|
|
61
65
|
"storybook": "^8.5.2",
|
|
62
66
|
"tailwindcss": "^4.0.1",
|
|
@@ -178,4 +182,4 @@
|
|
|
178
182
|
"main": "./dist/core/index.js",
|
|
179
183
|
"module": "./dist/core/index.js",
|
|
180
184
|
"types": "./dist/core/index.d.ts"
|
|
181
|
-
}
|
|
185
|
+
}
|
|
@@ -7,7 +7,7 @@ import { Select, Control, Range, Checkbox } from './controls';
|
|
|
7
7
|
const defaultOptions = { num: 1, bool: false, select: 'bar' };
|
|
8
8
|
|
|
9
9
|
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
|
10
|
-
const meta = {
|
|
10
|
+
const meta: Meta<typeof Control> = {
|
|
11
11
|
title: 'Bootstrap / Controls',
|
|
12
12
|
parameters: {
|
|
13
13
|
layout: 'centered',
|
|
@@ -32,7 +32,7 @@ const meta = {
|
|
|
32
32
|
|
|
33
33
|
return <div style={{ width: '300px' }}>{cloneElement(children as any, { ...safeArgs, ...controlProps })}</div>;
|
|
34
34
|
},
|
|
35
|
-
}
|
|
35
|
+
};
|
|
36
36
|
|
|
37
37
|
export default meta;
|
|
38
38
|
|
|
@@ -4,7 +4,7 @@ const lcFirst = (str) => str[0].toLowerCase() + str.substring(1, str.length);
|
|
|
4
4
|
|
|
5
5
|
const toProperty = (str) => lcFirst(str).split(' ').join('');
|
|
6
6
|
|
|
7
|
-
interface ControlProps {
|
|
7
|
+
export interface ControlProps {
|
|
8
8
|
value?: any;
|
|
9
9
|
setValue?: (v: any) => void;
|
|
10
10
|
defaultValue?: any;
|
|
@@ -22,9 +22,9 @@ interface ControlProps {
|
|
|
22
22
|
hideValue?: boolean;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
type LabelProps = Pick<ControlProps, 'name' | 'value' | 'defaultValue'> & { reset: () => void };
|
|
25
|
+
export type LabelProps = Pick<ControlProps, 'name' | 'value' | 'defaultValue'> & { reset: () => void };
|
|
26
26
|
|
|
27
|
-
const Label = memo<LabelProps>(function Label({ name, reset, value, defaultValue }) {
|
|
27
|
+
export const Label = memo<LabelProps>(function Label({ name, reset, value, defaultValue }) {
|
|
28
28
|
return (
|
|
29
29
|
<label className="flex-grow-1 mb-0 d-flex align-items-center justify-content-start gap-2">
|
|
30
30
|
{name}
|
|
@@ -4,7 +4,7 @@ import { fn } from '@storybook/test';
|
|
|
4
4
|
import { ErrorAlert } from './error';
|
|
5
5
|
|
|
6
6
|
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
|
7
|
-
const meta = {
|
|
7
|
+
const meta: Meta<typeof ErrorAlert> = {
|
|
8
8
|
title: 'Bootstrap / ErrorAlert',
|
|
9
9
|
component: ErrorAlert,
|
|
10
10
|
parameters: {
|
|
@@ -16,7 +16,7 @@ const meta = {
|
|
|
16
16
|
children: { control: 'text' },
|
|
17
17
|
},
|
|
18
18
|
args: { onRetry: fn() },
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
|
|
21
21
|
export default meta;
|
|
22
22
|
type Story = StoryObj<typeof meta>;
|
|
@@ -29,6 +29,6 @@ export const String: Story = {
|
|
|
29
29
|
|
|
30
30
|
export const ErrorObject: Story = {
|
|
31
31
|
args: {
|
|
32
|
-
children: new Error('An error occurred'),
|
|
32
|
+
children: new Error('An error occurred') as any,
|
|
33
33
|
},
|
|
34
34
|
};
|
|
@@ -6,7 +6,7 @@ import { FieldGroup } from './field';
|
|
|
6
6
|
import { FormControl } from 'react-bootstrap';
|
|
7
7
|
|
|
8
8
|
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
|
9
|
-
const meta = {
|
|
9
|
+
const meta: Meta<typeof FieldGroup> = {
|
|
10
10
|
title: 'Bootstrap / FieldGroup',
|
|
11
11
|
component: FieldGroup,
|
|
12
12
|
parameters: {
|
|
@@ -19,7 +19,7 @@ const meta = {
|
|
|
19
19
|
horizontal: { control: 'check' },
|
|
20
20
|
},
|
|
21
21
|
args: { onClick: fn() },
|
|
22
|
-
}
|
|
22
|
+
};
|
|
23
23
|
|
|
24
24
|
export default meta;
|
|
25
25
|
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
2
2
|
|
|
3
3
|
import { GlobalLoading } from './globalLoading';
|
|
4
4
|
|
|
5
|
-
const meta = {
|
|
5
|
+
const meta: Meta<typeof GlobalLoading> = {
|
|
6
6
|
title: 'Bootstrap / GlobalLoading',
|
|
7
7
|
component: GlobalLoading,
|
|
8
8
|
parameters: {
|
|
@@ -12,7 +12,7 @@ const meta = {
|
|
|
12
12
|
argTypes: {
|
|
13
13
|
loading: { control: 'check' },
|
|
14
14
|
},
|
|
15
|
-
}
|
|
15
|
+
};
|
|
16
16
|
|
|
17
17
|
export default meta;
|
|
18
18
|
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
2
2
|
|
|
3
3
|
import { Loading } from './loading';
|
|
4
4
|
|
|
5
|
-
const meta = {
|
|
5
|
+
const meta: Meta<typeof Loading> = {
|
|
6
6
|
title: 'Bootstrap / Loading',
|
|
7
7
|
component: Loading,
|
|
8
8
|
parameters: {
|
|
@@ -14,7 +14,7 @@ const meta = {
|
|
|
14
14
|
show: { control: 'check' },
|
|
15
15
|
size: { control: 'select', options: ['sm', 'md'] },
|
|
16
16
|
},
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
|
|
19
19
|
export default meta;
|
|
20
20
|
|
|
@@ -45,7 +45,7 @@ const lorem = (
|
|
|
45
45
|
</>
|
|
46
46
|
);
|
|
47
47
|
|
|
48
|
-
const meta = {
|
|
48
|
+
const meta: Meta<typeof Screen> = {
|
|
49
49
|
title: 'Bootstrap / Screen',
|
|
50
50
|
component: Screen,
|
|
51
51
|
parameters: {
|
|
@@ -75,7 +75,7 @@ const meta = {
|
|
|
75
75
|
),
|
|
76
76
|
backCb: fn(),
|
|
77
77
|
},
|
|
78
|
-
}
|
|
78
|
+
};
|
|
79
79
|
|
|
80
80
|
export default meta;
|
|
81
81
|
|
|
@@ -4,7 +4,7 @@ import { fn } from '@storybook/test';
|
|
|
4
4
|
import { Toaster } from './toaster';
|
|
5
5
|
|
|
6
6
|
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
|
7
|
-
const meta = {
|
|
7
|
+
const meta: Meta<typeof Toaster> = {
|
|
8
8
|
title: 'Bootstrap / Toaster',
|
|
9
9
|
component: Toaster,
|
|
10
10
|
parameters: {
|
|
@@ -13,7 +13,7 @@ const meta = {
|
|
|
13
13
|
tags: ['autodocs'],
|
|
14
14
|
argTypes: {},
|
|
15
15
|
args: { onClick: fn() },
|
|
16
|
-
}
|
|
16
|
+
};
|
|
17
17
|
|
|
18
18
|
export default meta;
|
|
19
19
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React, { FC } from 'react';
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
3
|
import { useModal, UseModalProps } from './useModal';
|
|
4
|
-
import { Button
|
|
4
|
+
import { Button } from 'react-bootstrap';
|
|
5
5
|
|
|
6
6
|
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
|
7
|
-
const meta = {
|
|
7
|
+
const meta: Meta<FC<UseModalProps>> = {
|
|
8
8
|
title: 'Bootstrap / useModal',
|
|
9
9
|
parameters: {
|
|
10
10
|
layout: 'centered',
|
|
@@ -34,7 +34,7 @@ const meta = {
|
|
|
34
34
|
);
|
|
35
35
|
},
|
|
36
36
|
// args: { onSubmit: (e) => e.preventDefault() },
|
|
37
|
-
}
|
|
37
|
+
};
|
|
38
38
|
|
|
39
39
|
export default meta;
|
|
40
40
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# WRPC
|
|
2
|
+
|
|
3
|
+
TODO
|
|
4
|
+
|
|
5
|
+
- [ ] Add to readme https://stackoverflow.com/questions/77727664/how-to-get-returned-value-from-async-generator-when-using-for-await
|
|
6
|
+
- [ ] Value passed to next are ignored https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator/next#sending_values_to_the_generator
|
|
7
|
+
|
|
8
|
+
```mermaid
|
|
9
|
+
sequenceDiagram
|
|
10
|
+
box Main
|
|
11
|
+
participant Code
|
|
12
|
+
participant Caller
|
|
13
|
+
end
|
|
14
|
+
box Worker
|
|
15
|
+
participant Worker
|
|
16
|
+
participant Resolver
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
%% autonumber
|
|
20
|
+
|
|
21
|
+
Code->>Caller: caller.generator(payload)
|
|
22
|
+
|
|
23
|
+
Caller-->>Code: iterator & promise
|
|
24
|
+
|
|
25
|
+
Code->>Code: Start main CONSUMER loop
|
|
26
|
+
|
|
27
|
+
Code->>Caller: iterator[Symbol]()
|
|
28
|
+
|
|
29
|
+
Caller->>Caller: Start replica GENERATOR loop
|
|
30
|
+
|
|
31
|
+
Code->>Caller: iterator.next()
|
|
32
|
+
|
|
33
|
+
Caller->>Worker: postMessage(payload)
|
|
34
|
+
|
|
35
|
+
Worker->>Resolver: resolver(payload)
|
|
36
|
+
|
|
37
|
+
Resolver-->>Worker: iterator
|
|
38
|
+
|
|
39
|
+
Worker->>Worker: Start replica CONSUMER loop
|
|
40
|
+
|
|
41
|
+
Worker->>Resolver: iterator[Symbol]()
|
|
42
|
+
|
|
43
|
+
Resolver->>Resolver: Start GENERATOR loop
|
|
44
|
+
|
|
45
|
+
rect rgb(232, 244, 248)
|
|
46
|
+
|
|
47
|
+
loop Synchronized iteration
|
|
48
|
+
Worker->>+Resolver: iterator.next(payload)
|
|
49
|
+
|
|
50
|
+
Resolver-->>-Worker: yield value
|
|
51
|
+
|
|
52
|
+
Worker-->>Caller: postMessage(value, done=false)
|
|
53
|
+
|
|
54
|
+
Note over Worker: Wait for ACK or ABORT
|
|
55
|
+
|
|
56
|
+
Caller-->>Code: yield value
|
|
57
|
+
|
|
58
|
+
Code->>Caller: iterator.next(nextPayload)
|
|
59
|
+
|
|
60
|
+
Caller->>Worker: postMessage(ack, nextPayload)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
break Break or AbortSignal (maybe)
|
|
66
|
+
Code-->>Code: Loop exits
|
|
67
|
+
Code->>Caller: signal.abort()
|
|
68
|
+
|
|
69
|
+
Caller-->>Caller: Loop exits
|
|
70
|
+
|
|
71
|
+
Caller->>Worker: postMessage(abort)
|
|
72
|
+
|
|
73
|
+
Worker-->>Worker: Loop exits via Break
|
|
74
|
+
|
|
75
|
+
Resolver-->>Resolver: Loop exits
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
opt Occurs on Error
|
|
79
|
+
Resolver-->>Worker: throw Error
|
|
80
|
+
|
|
81
|
+
Worker-->>Caller: postMessage(error, done=true)
|
|
82
|
+
|
|
83
|
+
Caller-->>Code: throw Error
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
Resolver-->>Worker: return value
|
|
87
|
+
|
|
88
|
+
Worker-->>Caller: postMessage(value, done=true)
|
|
89
|
+
|
|
90
|
+
Caller-->>Code: return value
|
|
91
|
+
```
|
package/src/core/cache.ts
CHANGED
|
@@ -64,6 +64,7 @@ export function memo<Key extends any[], Val, SerializedKey extends any[] = Key>(
|
|
|
64
64
|
) {
|
|
65
65
|
const map = new ManyKeysMap<SerializedKey, Val>();
|
|
66
66
|
|
|
67
|
+
//TODO Extend ManyKeysMap
|
|
67
68
|
const clear = (...condition: Key | any[]) => {
|
|
68
69
|
if (!condition?.length) {
|
|
69
70
|
map.forEach((value, serializedKey) => {
|
|
@@ -114,17 +115,12 @@ export function memo<Key extends any[], Val, SerializedKey extends any[] = Key>(
|
|
|
114
115
|
*
|
|
115
116
|
* This makes possible to implement various one-off and subsequent transformations.
|
|
116
117
|
*
|
|
117
|
-
* 1. `write`:
|
|
118
|
-
* 2. `read`:
|
|
119
|
-
* TODO Better names: once, folloing
|
|
120
|
-
* TODO Better names: memoize, recover
|
|
121
|
-
* TODO Better names: writeTransformer, readTransformer
|
|
122
|
-
* TODO Better names: writeModifier, readModifier
|
|
123
|
-
* TODO Better names: write, read
|
|
118
|
+
* 1. `write`: Transform value BEFORE writing to cache, called only once if cache IS NOT present or IS NOT valid
|
|
119
|
+
* 2. `read`: Transform value AFTER it's read from cache, always called if cache IS valid, keep in mind this transform is applied on top of BEFORE
|
|
124
120
|
*
|
|
125
121
|
* Both should return same type or null.
|
|
126
122
|
*
|
|
127
|
-
* If newValue is null, old is returned, and no cache is set.
|
|
123
|
+
* If `newValue` is null, old is returned, and no cache is set.
|
|
128
124
|
*
|
|
129
125
|
* ```ts
|
|
130
126
|
* class InputCache extends TypedCache<string, ImageBitmap> {
|
|
@@ -186,7 +182,7 @@ export function memo<Key extends any[], Val, SerializedKey extends any[] = Key>(
|
|
|
186
182
|
*/
|
|
187
183
|
export abstract class TransformerMap<Key, Val = Key> extends Map<Key, Val> {
|
|
188
184
|
constructor(protected readonly name: string) {
|
|
189
|
-
console.warn('CREATE cache', name);
|
|
185
|
+
// console.warn('CREATE cache', name);
|
|
190
186
|
super();
|
|
191
187
|
}
|
|
192
188
|
|
|
@@ -199,13 +195,17 @@ export abstract class TransformerMap<Key, Val = Key> extends Map<Key, Val> {
|
|
|
199
195
|
}
|
|
200
196
|
|
|
201
197
|
delete(key: Key) {
|
|
202
|
-
const
|
|
203
|
-
|
|
198
|
+
const val = this.get(key);
|
|
199
|
+
const has = typeof val !== 'undefined';
|
|
200
|
+
if (has) this.dispose(val, key);
|
|
204
201
|
super.delete(key);
|
|
205
202
|
return has;
|
|
206
203
|
}
|
|
207
204
|
|
|
208
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Do something with the value and key before removing it from cache
|
|
207
|
+
* For example, close a file or a bitmap
|
|
208
|
+
*/
|
|
209
209
|
protected dispose(value: Val, key: Key) {}
|
|
210
210
|
|
|
211
211
|
clear() {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { saveAs } from 'file-saver';
|
|
2
|
+
|
|
3
|
+
export const downloadFile = async (filename, text, type = 'application/json') => {
|
|
4
|
+
//FIXME https://github.com/eligrey/FileSaver.js/issues/731
|
|
5
|
+
saveAs(new Blob([text], { type }), filename);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const openFile = (): Promise<string> =>
|
|
9
|
+
new Promise((res, rej) => {
|
|
10
|
+
const input = document.createElement('input');
|
|
11
|
+
input.type = 'file';
|
|
12
|
+
input.addEventListener('change', function readFile(e) {
|
|
13
|
+
input.removeEventListener('change', readFile);
|
|
14
|
+
// @ts-expect-error file is always there
|
|
15
|
+
const [file] = e.target.files;
|
|
16
|
+
if (!file) return;
|
|
17
|
+
const reader = new FileReader();
|
|
18
|
+
reader.onload = (e) => res(e.target?.result as string);
|
|
19
|
+
reader.onerror = rej;
|
|
20
|
+
reader.onabort = rej;
|
|
21
|
+
reader.readAsText(file);
|
|
22
|
+
});
|
|
23
|
+
input.click();
|
|
24
|
+
});
|