@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
266 lines (265 loc) • 8.12 kB
JavaScript
"use client";
import _pushInstanceProperty from "core-js-pure/stable/instance/push.js";
import _Object$hasOwn from "core-js-pure/stable/object/has-own.js";
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
export function useWeakSharedState(id, initialData = undefined, onChange = null) {
return useSharedState(id, initialData, onChange, {
weak: true
});
}
export function useSharedState(id, initialData = undefined, onChange = null, {
weak = false
} = {}) {
const instanceRef = useRef({});
const sharedState = useMemo(() => {
if (id) {
return createSharedState(id, initialData);
}
return undefined;
}, [id, initialData]);
const sharedAttachment = useMemo(() => {
if (id) {
return createSharedState(createReferenceKey(id, 'oc'), {
onChange
});
}
return undefined;
}, [id, onChange]);
const syncAttachment = useCallback(newData => {
if (id) {
var _sharedAttachment$dat, _sharedAttachment$dat2;
(_sharedAttachment$dat = sharedAttachment.data) === null || _sharedAttachment$dat === void 0 || (_sharedAttachment$dat2 = _sharedAttachment$dat.onChange) === null || _sharedAttachment$dat2 === void 0 || _sharedAttachment$dat2.call(_sharedAttachment$dat, newData);
}
}, [id, sharedAttachment]);
const subscribe = useCallback(onStoreChange => {
if (!id || !sharedState) {
return () => {};
}
const subscriber = () => onStoreChange();
subscriber['ref'] = instanceRef.current;
sharedState.subscribe(subscriber);
return () => {
sharedState.unsubscribe(subscriber);
if (weak && sharedState.subscribersRef.current.length === 0) {
sharedState.update(undefined);
}
};
}, [id, sharedState, weak]);
const getSnapshot = useCallback(() => {
var _sharedState$get, _sharedState$get2;
return id ? (_sharedState$get = sharedState === null || sharedState === void 0 || (_sharedState$get2 = sharedState.get) === null || _sharedState$get2 === void 0 ? void 0 : _sharedState$get2.call(sharedState)) !== null && _sharedState$get !== void 0 ? _sharedState$get : undefined : undefined;
}, [id, sharedState]);
const data = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
const get = useCallback(() => {
if (id) {
var _sharedState$get3;
return sharedState === null || sharedState === void 0 || (_sharedState$get3 = sharedState.get) === null || _sharedState$get3 === void 0 ? void 0 : _sharedState$get3.call(sharedState);
}
return undefined;
}, [id, sharedState]);
const update = useCallback((newData, opts) => {
if (id) {
sharedState.update(newData, {
...opts,
callerRef: instanceRef.current
});
}
}, [id, sharedState]);
const set = useCallback((newData, opts) => {
if (!id) {
return false;
}
const changed = sharedState.set(newData, {
...opts,
callerRef: instanceRef.current
});
if (changed) {
syncAttachment(newData);
}
return changed;
}, [id, sharedState, syncAttachment]);
const extend = useCallback((newData, opts) => {
if (!id) {
return false;
}
const changed = sharedState.extend(newData, {
...opts,
callerRef: instanceRef.current
});
if (changed) {
syncAttachment(newData);
}
return changed;
}, [id, sharedState, syncAttachment]);
useEffect(() => {
var _sharedAttachment$dat3;
if (id && onChange && !((_sharedAttachment$dat3 = sharedAttachment.data) !== null && _sharedAttachment$dat3 !== void 0 && _sharedAttachment$dat3.onChange)) {
sharedAttachment.set({
onChange
});
}
}, [id, onChange, sharedAttachment]);
return {
get,
data: data,
hadInitialData: sharedState === null || sharedState === void 0 ? void 0 : sharedState.hadInitialData,
update,
set,
extend
};
}
const sharedStates = new Map();
export function createSharedState(id, initialData) {
if (!sharedStates.get(id)) {
const subscribersRef = {
current: []
};
const sync = (opts = {}) => {
subscribersRef.current.forEach(subscriber => {
if (opts.preventSyncOfSameInstance && opts.callerRef !== undefined && subscriber['ref'] === opts.callerRef) {
return;
}
subscriber();
});
};
const get = () => sharedStates.get(id).data;
const set = (newData, opts) => {
const store = sharedStates.get(id);
const current = store.data;
if (newData === undefined) {
if (current === undefined) {
return false;
}
store.data = undefined;
if (!(opts !== null && opts !== void 0 && opts.silent)) {
sync(opts);
}
return true;
}
const next = cloneData(newData);
if (!(opts !== null && opts !== void 0 && opts.forceSync) && shallowEqual(current, next)) {
return false;
}
store.data = next;
if (!(opts !== null && opts !== void 0 && opts.silent)) {
sync(opts);
}
return true;
};
const update = (newData, opts) => {
set(newData, opts);
};
const extend = (newData, opts) => {
const store = sharedStates.get(id);
const current = store.data;
if (!isMergeableObject(newData)) {
return set(newData, opts);
}
const base = isMergeableObject(current) ? current : {};
const next = {
...base,
...newData
};
if (!(opts !== null && opts !== void 0 && opts.forceSync) && shallowEqual(base, next)) {
return false;
}
store.data = next;
if (!(opts !== null && opts !== void 0 && opts.silent)) {
sync(opts);
}
return true;
};
const subscribe = subscriber => {
if (!subscribersRef.current.includes(subscriber)) {
var _context;
_pushInstanceProperty(_context = subscribersRef.current).call(_context, subscriber);
}
};
const unsubscribe = subscriber => {
subscribersRef.current = subscribersRef.current.filter(sub => sub !== subscriber);
};
sharedStates.set(id, {
data: undefined,
get,
set,
extend,
update,
subscribe,
unsubscribe,
hadInitialData: Boolean(initialData),
subscribersRef
});
if (initialData) {
extend(initialData);
}
} else if (sharedStates.get(id).data === undefined && initialData !== undefined) {
sharedStates.get(id).data = cloneData(initialData);
}
return sharedStates.get(id);
}
export function preSeedSharedState(id, data) {
createSharedState(id);
const store = sharedStates.get(id);
if (store && store.data === undefined && data !== undefined) {
store.data = cloneData(data);
}
}
export function createReferenceKey(ref1, ref2) {
if (!cache.has(ref1)) {
cache.set(ref1, new Map());
}
const innerMap = cache.get(ref1);
if (!innerMap.has(ref2)) {
innerMap.set(ref2, {});
}
return innerMap.get(ref2);
}
const cache = new Map();
function isObjectLike(value) {
return typeof value === 'object' && value !== null;
}
function isMergeableObject(value) {
return isObjectLike(value) && !Array.isArray(value);
}
function cloneData(value) {
if (Array.isArray(value)) {
return [...value];
}
if (isMergeableObject(value)) {
return {
...value
};
}
return value;
}
export function shallowEqual(a, b) {
if (Object.is(a, b)) {
return true;
}
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!Object.is(a[i], b[i])) {
return false;
}
}
return true;
}
if (isMergeableObject(a) && isMergeableObject(b)) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) {
return false;
}
for (const key of keysA) {
if (!_Object$hasOwn(b, key) || !Object.is(a[key], b[key])) {
return false;
}
}
return true;
}
return false;
}
//# sourceMappingURL=useSharedState.js.map