floppy-disk 1.0.0

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.
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createStores = void 0;
4
+ const react_1 = require("react");
5
+ const utils_1 = require("../utils");
6
+ const vanilla_1 = require("../vanilla");
7
+ const hashStoreKey = (obj) => JSON.stringify(obj, Object.keys(obj).sort());
8
+ const createStores = (initializer, options = {}) => {
9
+ const { onBeforeChangeKey = utils_1.noop, defaultDeps } = options;
10
+ const stores = new Map();
11
+ const getStore = (key) => {
12
+ const normalizedKey = hashStoreKey(key);
13
+ if (!stores.has(normalizedKey)) {
14
+ stores.set(normalizedKey, (0, vanilla_1.initStore)((api) => initializer({ key, ...api }), options));
15
+ }
16
+ return stores.get(normalizedKey);
17
+ };
18
+ /**
19
+ * IMPORTANT NOTE: selectDeps function must not be changed after initialization.
20
+ */
21
+ const useStores = (...args) => {
22
+ const [_key = {}, selectDeps = defaultDeps] = (typeof args[0] === 'function' ? [{}, args[0]] : args);
23
+ const key = _key;
24
+ const normalizedKey = hashStoreKey(key);
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
+ const { get, subscribe } = (0, react_1.useMemo)(() => getStore(key), [normalizedKey]);
27
+ const [state, setState] = (0, react_1.useState)(get);
28
+ const isFirstRender = (0, react_1.useRef)(true);
29
+ const prevKey = (0, react_1.useRef)(key);
30
+ (0, react_1.useEffect)(() => {
31
+ if (!isFirstRender.current) {
32
+ onBeforeChangeKey(key, prevKey.current);
33
+ setState(get);
34
+ }
35
+ isFirstRender.current = false;
36
+ prevKey.current = key;
37
+ const unsubs = subscribe(setState, selectDeps);
38
+ return unsubs;
39
+ // eslint-disable-next-line react-hooks/exhaustive-deps
40
+ }, [normalizedKey]);
41
+ return state;
42
+ };
43
+ useStores.get = (key = {}) => {
44
+ const store = getStore(key);
45
+ return store.get();
46
+ };
47
+ useStores.getAll = () => {
48
+ const allStores = [];
49
+ stores.forEach((store) => {
50
+ allStores.push(store.get());
51
+ });
52
+ return allStores;
53
+ };
54
+ useStores.getAllWithSubscriber = () => {
55
+ const allStores = [];
56
+ stores.forEach((store) => {
57
+ const subscribers = store.getSubscribers();
58
+ if (subscribers.size > 0)
59
+ allStores.push(store.get());
60
+ });
61
+ return allStores;
62
+ };
63
+ useStores.set = (key = {}, value, silent) => {
64
+ const store = getStore(key);
65
+ store.set(value, silent);
66
+ };
67
+ useStores.setAll = (value, silent) => {
68
+ stores.forEach((store) => {
69
+ store.set(value, silent);
70
+ });
71
+ };
72
+ useStores.subscribe = (key = {}, fn, selectDeps) => {
73
+ const store = getStore(key);
74
+ return store.subscribe(fn, selectDeps);
75
+ };
76
+ useStores.getSubscribers = (key = {}) => {
77
+ const store = getStore(key);
78
+ return store.getSubscribers();
79
+ };
80
+ const Watch = ({ storeKey = {}, selectDeps, render }) => {
81
+ const store = useStores(storeKey, selectDeps);
82
+ return render(store);
83
+ };
84
+ useStores.Watch = Watch;
85
+ return useStores;
86
+ };
87
+ exports.createStores = createStores;
@@ -0,0 +1,5 @@
1
+ import { ReactNode } from 'react';
2
+ export declare const withContext: <T>(initFn: () => T) => readonly [({ children, onInitialize, }: {
3
+ children: ReactNode;
4
+ onInitialize?: ((value: T) => void) | undefined;
5
+ }) => JSX.Element, () => T | null];
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withContext = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importStar(require("react"));
6
+ const withContext = (initFn) => {
7
+ const Context = (0, react_1.createContext)(null);
8
+ const Provider = ({ children, onInitialize, }) => {
9
+ const [value] = (0, react_1.useState)(() => {
10
+ const store = initFn();
11
+ onInitialize && onInitialize(store);
12
+ return store;
13
+ });
14
+ return react_1.default.createElement(Context.Provider, { value: value }, children);
15
+ };
16
+ const useCurrentContext = () => (0, react_1.useContext)(Context);
17
+ return [Provider, useCurrentContext];
18
+ };
19
+ exports.withContext = withContext;
@@ -0,0 +1,2 @@
1
+ export declare const noop: () => void;
2
+ export declare const identityFn: <T>(a: T) => T;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.identityFn = exports.noop = void 0;
4
+ const noop = () => { };
5
+ exports.noop = noop;
6
+ const identityFn = (a) => a;
7
+ exports.identityFn = identityFn;
@@ -0,0 +1,23 @@
1
+ export declare type StoreData = Record<string, any>;
2
+ export declare type SetStoreData<T> = Partial<T> | ((prevState: T) => Partial<T>);
3
+ export declare type SelectDeps<T> = ((state: T) => any[]) | undefined;
4
+ export declare type Subscribers<T> = Map<(state: T) => void, SelectDeps<T>>;
5
+ export declare type StoreInitializer<T> = (api: {
6
+ get: () => T;
7
+ set: (value: SetStoreData<T>, silent?: boolean) => void;
8
+ }) => T;
9
+ export declare type StoreEvent<T> = (state: T) => void;
10
+ export declare type InitStoreOptions<T> = {
11
+ intercept?: (nextState: T, prevState: T) => Partial<T>;
12
+ onFirstSubscribe?: StoreEvent<T>;
13
+ onSubscribe?: StoreEvent<T>;
14
+ onUnsubscribe?: StoreEvent<T>;
15
+ onLastUnsubscribe?: StoreEvent<T>;
16
+ };
17
+ export declare type InitStoreReturn<T> = {
18
+ get: () => T;
19
+ set: (value: SetStoreData<T>, silent?: boolean) => void;
20
+ subscribe: (fn: (state: T) => void, selectDeps?: SelectDeps<T>) => () => void;
21
+ getSubscribers: () => Subscribers<T>;
22
+ };
23
+ export declare const initStore: <T extends StoreData>(initializer: StoreInitializer<T>, options?: InitStoreOptions<T>) => InitStoreReturn<T>;
package/lib/vanilla.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initStore = void 0;
4
+ const utils_1 = require("./utils");
5
+ const initStore = (initializer, options = {}) => {
6
+ const { intercept, onFirstSubscribe = utils_1.noop, onSubscribe = utils_1.noop, onUnsubscribe = utils_1.noop, onLastUnsubscribe = utils_1.noop, } = options;
7
+ const subscribers = new Map();
8
+ const getSubscribers = () => subscribers;
9
+ let data;
10
+ let keys;
11
+ const get = () => data;
12
+ const set = (value, silent = false) => {
13
+ const prevData = data;
14
+ if (typeof value === 'function') {
15
+ data = { ...data, ...value(data) };
16
+ }
17
+ else {
18
+ data = { ...data, ...value };
19
+ }
20
+ if (intercept) {
21
+ data = { ...data, ...intercept(data, prevData) };
22
+ }
23
+ if (silent)
24
+ return;
25
+ subscribers.forEach((selectDeps, fn) => {
26
+ if (!selectDeps) {
27
+ for (let i = 0, n = keys.length; i < n; i++) {
28
+ if (prevData[keys[i]] !== data[keys[i]]) {
29
+ fn(data);
30
+ break;
31
+ }
32
+ }
33
+ return;
34
+ }
35
+ const prevs = selectDeps(prevData);
36
+ const nexts = selectDeps(data);
37
+ for (let i = 0, n = prevs.length; i < n; i++) {
38
+ if (prevs[i] !== nexts[i]) {
39
+ fn(data);
40
+ break;
41
+ }
42
+ }
43
+ });
44
+ };
45
+ const subscribe = (fn, selectDeps) => {
46
+ subscribers.set(fn, selectDeps);
47
+ if (subscribers.size === 1)
48
+ onFirstSubscribe(data);
49
+ onSubscribe(data);
50
+ return () => {
51
+ subscribers.delete(fn);
52
+ onUnsubscribe(data);
53
+ if (subscribers.size === 0)
54
+ onLastUnsubscribe(data);
55
+ };
56
+ };
57
+ data = initializer({ get, set });
58
+ keys = Object.keys(data);
59
+ return { get, set, subscribe, getSubscribers };
60
+ };
61
+ exports.initStore = initStore;
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "floppy-disk",
3
+ "version": "1.0.0",
4
+ "description": "FloppyDisk - lightweight, simple, and powerful state management library",
5
+ "keywords": [
6
+ "state",
7
+ "manager",
8
+ "management",
9
+ "react",
10
+ "hooks",
11
+ "store",
12
+ "utilities"
13
+ ],
14
+ "author": "Muhammad Afifudin",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/afiiif/floppy-disk"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/afiiif/floppy-disk/issues"
22
+ },
23
+ "homepage": "https://github.com/afiiif/floppy-disk#readme",
24
+ "main": "lib/index.js",
25
+ "module": "esm/index.js",
26
+ "sideEffects": false,
27
+ "files": [
28
+ "lib/",
29
+ "esm/"
30
+ ],
31
+ "types": "lib/index.d.ts",
32
+ "typings": "lib/index.d.ts",
33
+ "scripts": {
34
+ "prepare": "husky install",
35
+ "build:cjs": "tsc",
36
+ "build:es": "tsc -m esNext --outDir esm",
37
+ "build": "yarn clean && yarn build:cjs && yarn build:es",
38
+ "clean": "rimraf lib esm",
39
+ "format": "prettier --check .",
40
+ "format:fix": "prettier --write .",
41
+ "lint": "eslint .",
42
+ "lint:fix": "eslint . --fix",
43
+ "lint:types": "tsc --noEmit"
44
+ },
45
+ "devDependencies": {
46
+ "@commitlint/cli": "^17.0.0",
47
+ "@commitlint/config-conventional": "^17.0.0",
48
+ "@types/react": "^18.0.9",
49
+ "@types/react-dom": "^18.0.4",
50
+ "eslint": "^8.16.0",
51
+ "eslint-config-prettier": "^8.5.0",
52
+ "eslint-config-react-app": "^7.0.1",
53
+ "eslint-plugin-simple-import-sort": "^7.0.0",
54
+ "husky": "^8.0.1",
55
+ "lint-staged": "^12.4.1",
56
+ "prettier": "^2.6.2",
57
+ "react": "^18.1.0",
58
+ "react-dom": "^18.1.0",
59
+ "rimraf": "^3.0.2",
60
+ "typescript": "^4.6.4"
61
+ },
62
+ "peerDependencies": {
63
+ "react": "^17.0.0 || ^18.0.0",
64
+ "react-dom": "^17.0.0 || ^18.0.0"
65
+ }
66
+ }