@webiny/react-properties 0.0.0-unstable.06b2ede40f → 0.0.0-unstable.0d717d18dd
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/AsyncProperties.d.ts +14 -0
- package/AsyncProperties.js +43 -0
- package/AsyncProperties.js.map +1 -0
- package/Await.d.ts +7 -0
- package/Await.js +44 -0
- package/Await.js.map +1 -0
- package/DevToolsSection.d.ts +40 -0
- package/DevToolsSection.js +54 -0
- package/DevToolsSection.js.map +1 -0
- package/Properties.d.ts +4 -1
- package/Properties.js +129 -213
- package/Properties.js.map +1 -1
- package/PropertyPriority.d.ts +8 -0
- package/PropertyPriority.js +11 -0
- package/PropertyPriority.js.map +1 -0
- package/README.md +7 -61
- package/createConfigurableComponent.d.ts +1 -1
- package/createConfigurableComponent.js +60 -78
- package/createConfigurableComponent.js.map +1 -1
- package/domain/PropertyStore.d.ts +52 -0
- package/domain/PropertyStore.js +162 -0
- package/domain/PropertyStore.js.map +1 -0
- package/domain/index.d.ts +1 -0
- package/domain/index.js +1 -0
- package/index.d.ts +9 -5
- package/index.js +9 -7
- package/package.json +19 -16
- package/useDebugConfig.d.ts +26 -1
- package/useDebugConfig.js +40 -14
- package/useDebugConfig.js.map +1 -1
- package/useIdGenerator.js +19 -10
- package/useIdGenerator.js.map +1 -1
- package/utils.d.ts +1 -1
- package/utils.js +38 -37
- package/utils.js.map +1 -1
- package/index.js.map +0 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Property } from "./Properties.js";
|
|
3
|
+
interface AsyncPropertiesContextValue {
|
|
4
|
+
incrementPending(): void;
|
|
5
|
+
decrementPending(): void;
|
|
6
|
+
}
|
|
7
|
+
export declare function useAsyncProperties(): AsyncPropertiesContextValue | undefined;
|
|
8
|
+
interface AsyncPropertiesProps {
|
|
9
|
+
name?: string;
|
|
10
|
+
onChange?(properties: Property[]): void;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
export declare const AsyncProperties: ({ name, onChange, children }: AsyncPropertiesProps) => React.JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import react, { createContext, useCallback, useContext, useRef } from "react";
|
|
2
|
+
import { Properties, useProperties } from "./Properties.js";
|
|
3
|
+
const AsyncPropertiesContext = /*#__PURE__*/ createContext(void 0);
|
|
4
|
+
function useAsyncProperties() {
|
|
5
|
+
return useContext(AsyncPropertiesContext);
|
|
6
|
+
}
|
|
7
|
+
const StoreCapture = ({ storeRef, children })=>{
|
|
8
|
+
const { store } = useProperties();
|
|
9
|
+
storeRef.current = store;
|
|
10
|
+
return /*#__PURE__*/ react.createElement(react.Fragment, null, children);
|
|
11
|
+
};
|
|
12
|
+
const AsyncProperties = ({ name, onChange, children })=>{
|
|
13
|
+
const pendingRef = useRef(0);
|
|
14
|
+
const onChangeRef = useRef(onChange);
|
|
15
|
+
onChangeRef.current = onChange;
|
|
16
|
+
const storeRef = useRef(null);
|
|
17
|
+
const interceptedOnChange = useCallback((properties)=>{
|
|
18
|
+
if (pendingRef.current > 0) return;
|
|
19
|
+
onChangeRef.current?.(properties);
|
|
20
|
+
}, []);
|
|
21
|
+
const contextValue = useRef({
|
|
22
|
+
incrementPending () {
|
|
23
|
+
pendingRef.current++;
|
|
24
|
+
},
|
|
25
|
+
decrementPending () {
|
|
26
|
+
pendingRef.current = Math.max(0, pendingRef.current - 1);
|
|
27
|
+
if (0 === pendingRef.current) setTimeout(()=>{
|
|
28
|
+
if (0 === pendingRef.current && storeRef.current) storeRef.current.notify();
|
|
29
|
+
}, 0);
|
|
30
|
+
}
|
|
31
|
+
}).current;
|
|
32
|
+
return /*#__PURE__*/ react.createElement(AsyncPropertiesContext.Provider, {
|
|
33
|
+
value: contextValue
|
|
34
|
+
}, /*#__PURE__*/ react.createElement(Properties, {
|
|
35
|
+
name: name,
|
|
36
|
+
onChange: interceptedOnChange
|
|
37
|
+
}, /*#__PURE__*/ react.createElement(StoreCapture, {
|
|
38
|
+
storeRef: storeRef
|
|
39
|
+
}, children)));
|
|
40
|
+
};
|
|
41
|
+
export { AsyncProperties, useAsyncProperties };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=AsyncProperties.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AsyncProperties.js","sources":["../src/AsyncProperties.tsx"],"sourcesContent":["import React, { createContext, useCallback, useContext, useRef } from \"react\";\nimport { Properties, useProperties } from \"./Properties.js\";\nimport type { Property } from \"./Properties.js\";\nimport type { PropertyStore } from \"./domain/index.js\";\n\ninterface AsyncPropertiesContextValue {\n incrementPending(): void;\n decrementPending(): void;\n}\n\nconst AsyncPropertiesContext = createContext<AsyncPropertiesContextValue | undefined>(undefined);\n\nexport function useAsyncProperties() {\n return useContext(AsyncPropertiesContext);\n}\n\ninterface AsyncPropertiesProps {\n name?: string;\n onChange?(properties: Property[]): void;\n children: React.ReactNode;\n}\n\nconst StoreCapture = ({\n storeRef,\n children\n}: {\n storeRef: React.MutableRefObject<PropertyStore | null>;\n children: React.ReactNode;\n}) => {\n const { store } = useProperties();\n storeRef.current = store;\n return <>{children}</>;\n};\n\nexport const AsyncProperties = ({ name, onChange, children }: AsyncPropertiesProps) => {\n const pendingRef = useRef(0);\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n const storeRef = useRef<PropertyStore | null>(null);\n\n const interceptedOnChange = useCallback((properties: Property[]) => {\n if (pendingRef.current > 0) {\n return;\n }\n onChangeRef.current?.(properties);\n }, []);\n\n const contextValue = useRef<AsyncPropertiesContextValue>({\n incrementPending() {\n pendingRef.current++;\n },\n decrementPending() {\n pendingRef.current = Math.max(0, pendingRef.current - 1);\n if (pendingRef.current === 0) {\n setTimeout(() => {\n if (pendingRef.current === 0 && storeRef.current) {\n storeRef.current.notify();\n }\n }, 0);\n }\n }\n }).current;\n\n return (\n <AsyncPropertiesContext.Provider value={contextValue}>\n <Properties name={name} onChange={interceptedOnChange}>\n <StoreCapture storeRef={storeRef}>{children}</StoreCapture>\n </Properties>\n </AsyncPropertiesContext.Provider>\n );\n};\n"],"names":["AsyncPropertiesContext","createContext","undefined","useAsyncProperties","useContext","StoreCapture","storeRef","children","store","useProperties","AsyncProperties","name","onChange","pendingRef","useRef","onChangeRef","interceptedOnChange","useCallback","properties","contextValue","Math","setTimeout","Properties"],"mappings":";;AAUA,MAAMA,yBAAyB,WAAHA,GAAGC,cAAuDC;AAE/E,SAASC;IACZ,OAAOC,WAAWJ;AACtB;AAQA,MAAMK,eAAe,CAAC,EAClBC,QAAQ,EACRC,QAAQ,EAIX;IACG,MAAM,EAAEC,KAAK,EAAE,GAAGC;IAClBH,SAAS,OAAO,GAAGE;IACnB,OAAO,WAAP,GAAO,0CAAGD;AACd;AAEO,MAAMG,kBAAkB,CAAC,EAAEC,IAAI,EAAEC,QAAQ,EAAEL,QAAQ,EAAwB;IAC9E,MAAMM,aAAaC,OAAO;IAC1B,MAAMC,cAAcD,OAAOF;IAC3BG,YAAY,OAAO,GAAGH;IACtB,MAAMN,WAAWQ,OAA6B;IAE9C,MAAME,sBAAsBC,YAAY,CAACC;QACrC,IAAIL,WAAW,OAAO,GAAG,GACrB;QAEJE,YAAY,OAAO,GAAGG;IAC1B,GAAG,EAAE;IAEL,MAAMC,eAAeL,OAAoC;QACrD;YACID,WAAW,OAAO;QACtB;QACA;YACIA,WAAW,OAAO,GAAGO,KAAK,GAAG,CAAC,GAAGP,WAAW,OAAO,GAAG;YACtD,IAAIA,AAAuB,MAAvBA,WAAW,OAAO,EAClBQ,WAAW;gBACP,IAAIR,AAAuB,MAAvBA,WAAW,OAAO,IAAUP,SAAS,OAAO,EAC5CA,SAAS,OAAO,CAAC,MAAM;YAE/B,GAAG;QAEX;IACJ,GAAG,OAAO;IAEV,OAAO,WAAP,GACI,oBAACN,uBAAuB,QAAQ;QAAC,OAAOmB;qBACpC,oBAACG,YAAUA;QAAC,MAAMX;QAAM,UAAUK;qBAC9B,oBAACX,cAAYA;QAAC,UAAUC;OAAWC;AAInD"}
|
package/Await.d.ts
ADDED
package/Await.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import react, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useAsyncProperties } from "./AsyncProperties.js";
|
|
3
|
+
function Await({ fn, children }) {
|
|
4
|
+
const async = useAsyncProperties();
|
|
5
|
+
const [result, setResult] = useState(null);
|
|
6
|
+
const registeredRef = useRef(false);
|
|
7
|
+
const settledRef = useRef(false);
|
|
8
|
+
if (!registeredRef.current && async) {
|
|
9
|
+
registeredRef.current = true;
|
|
10
|
+
async.incrementPending();
|
|
11
|
+
}
|
|
12
|
+
useEffect(()=>{
|
|
13
|
+
let cancelled = false;
|
|
14
|
+
fn().then((data)=>{
|
|
15
|
+
if (!cancelled) {
|
|
16
|
+
settledRef.current = true;
|
|
17
|
+
setResult({
|
|
18
|
+
data
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}, (error)=>{
|
|
22
|
+
if (!cancelled) {
|
|
23
|
+
settledRef.current = true;
|
|
24
|
+
setResult({
|
|
25
|
+
error
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return ()=>{
|
|
30
|
+
cancelled = true;
|
|
31
|
+
if (!settledRef.current && async) async.decrementPending();
|
|
32
|
+
};
|
|
33
|
+
}, []);
|
|
34
|
+
useEffect(()=>{
|
|
35
|
+
if (null !== result && async) async.decrementPending();
|
|
36
|
+
}, [
|
|
37
|
+
result
|
|
38
|
+
]);
|
|
39
|
+
if (result && "data" in result) return /*#__PURE__*/ react.createElement(react.Fragment, null, children(result.data));
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
export { Await };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=Await.js.map
|
package/Await.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Await.js","sources":["../src/Await.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { useAsyncProperties } from \"./AsyncProperties.js\";\n\ninterface AwaitProps<T> {\n fn: () => Promise<T>;\n children: (value: T) => React.ReactNode;\n}\n\nexport function Await<T>({ fn, children }: AwaitProps<T>) {\n const async = useAsyncProperties();\n const [result, setResult] = useState<{ data: T } | { error: unknown } | null>(null);\n const registeredRef = useRef(false);\n const settledRef = useRef(false);\n\n if (!registeredRef.current && async) {\n registeredRef.current = true;\n async.incrementPending();\n }\n\n useEffect(() => {\n let cancelled = false;\n\n fn().then(\n data => {\n if (!cancelled) {\n settledRef.current = true;\n setResult({ data });\n }\n },\n error => {\n if (!cancelled) {\n settledRef.current = true;\n setResult({ error });\n }\n }\n );\n\n return () => {\n cancelled = true;\n if (!settledRef.current && async) {\n async.decrementPending();\n }\n };\n }, []);\n\n useEffect(() => {\n if (result !== null && async) {\n async.decrementPending();\n }\n }, [result]);\n\n if (result && \"data\" in result) {\n return <>{children(result.data)}</>;\n }\n\n return null;\n}\n"],"names":["Await","fn","children","async","useAsyncProperties","result","setResult","useState","registeredRef","useRef","settledRef","useEffect","cancelled","data","error"],"mappings":";;AAQO,SAASA,MAAS,EAAEC,EAAE,EAAEC,QAAQ,EAAiB;IACpD,MAAMC,QAAQC;IACd,MAAM,CAACC,QAAQC,UAAU,GAAGC,SAAkD;IAC9E,MAAMC,gBAAgBC,OAAO;IAC7B,MAAMC,aAAaD,OAAO;IAE1B,IAAI,CAACD,cAAc,OAAO,IAAIL,OAAO;QACjCK,cAAc,OAAO,GAAG;QACxBL,MAAM,gBAAgB;IAC1B;IAEAQ,UAAU;QACN,IAAIC,YAAY;QAEhBX,KAAK,IAAI,CACLY,CAAAA;YACI,IAAI,CAACD,WAAW;gBACZF,WAAW,OAAO,GAAG;gBACrBJ,UAAU;oBAAEO;gBAAK;YACrB;QACJ,GACAC,CAAAA;YACI,IAAI,CAACF,WAAW;gBACZF,WAAW,OAAO,GAAG;gBACrBJ,UAAU;oBAAEQ;gBAAM;YACtB;QACJ;QAGJ,OAAO;YACHF,YAAY;YACZ,IAAI,CAACF,WAAW,OAAO,IAAIP,OACvBA,MAAM,gBAAgB;QAE9B;IACJ,GAAG,EAAE;IAELQ,UAAU;QACN,IAAIN,AAAW,SAAXA,UAAmBF,OACnBA,MAAM,gBAAgB;IAE9B,GAAG;QAACE;KAAO;IAEX,IAAIA,UAAU,UAAUA,QACpB,OAAO,WAAP,GAAO,0CAAGH,SAASG,OAAO,IAAI;IAGlC,OAAO;AACX"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
type DevToolsView = "browse" | "raw";
|
|
2
|
+
interface DevToolsSectionProps {
|
|
3
|
+
name: string;
|
|
4
|
+
data: unknown;
|
|
5
|
+
/**
|
|
6
|
+
* Group name for the sidebar. Items with the same group appear
|
|
7
|
+
* under the same header. Defaults to "Sections".
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <DevToolsSection name="Article" group="CMS" data={model} />
|
|
12
|
+
* <DevToolsSection name="Author" group="CMS" data={author} />
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
group?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Which views to show in the detail panel.
|
|
18
|
+
* - `"browse"` — split view with root keys on the left, value tree on the right
|
|
19
|
+
* - `"raw"` — full JSON tree view
|
|
20
|
+
*
|
|
21
|
+
* Accepts a single view or an array. First item is the default tab.
|
|
22
|
+
* @default ["browse", "raw"]
|
|
23
|
+
*/
|
|
24
|
+
views?: DevToolsView | DevToolsView[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Registers a named section in the Webiny DevTools extension.
|
|
28
|
+
* Renders nothing — purely a data registration side-effect.
|
|
29
|
+
*
|
|
30
|
+
* When the component unmounts (e.g., route change), the section
|
|
31
|
+
* is automatically removed from DevTools.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <DevToolsSection name="CMS Model" data={model} />
|
|
36
|
+
* <DevToolsSection name="Article" group="CMS" data={model} views="raw" />
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function DevToolsSection({ name, data, group, views }: DevToolsSectionProps): null;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { getHook } from "./useDebugConfig.js";
|
|
3
|
+
function DevToolsSection({ name, data, group, views }) {
|
|
4
|
+
const dataKey = safeStringify(data);
|
|
5
|
+
useEffect(()=>{
|
|
6
|
+
if ("development" !== process.env.NODE_ENV) return;
|
|
7
|
+
const normalizedViews = views ? Array.isArray(views) ? views : [
|
|
8
|
+
views
|
|
9
|
+
] : [
|
|
10
|
+
"browse",
|
|
11
|
+
"raw"
|
|
12
|
+
];
|
|
13
|
+
let plainData;
|
|
14
|
+
try {
|
|
15
|
+
plainData = JSON.parse(dataKey);
|
|
16
|
+
} catch {
|
|
17
|
+
plainData = data;
|
|
18
|
+
}
|
|
19
|
+
const hook = getHook();
|
|
20
|
+
hook.sections[name] = {
|
|
21
|
+
data: plainData,
|
|
22
|
+
group: group || "Sections",
|
|
23
|
+
views: normalizedViews,
|
|
24
|
+
updatedAt: Date.now()
|
|
25
|
+
};
|
|
26
|
+
hook.revision++;
|
|
27
|
+
return ()=>{
|
|
28
|
+
if (window.__WEBINY_DEVTOOLS_HOOK__) {
|
|
29
|
+
delete window.__WEBINY_DEVTOOLS_HOOK__.sections[name];
|
|
30
|
+
window.__WEBINY_DEVTOOLS_HOOK__.revision++;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}, [
|
|
34
|
+
name,
|
|
35
|
+
dataKey,
|
|
36
|
+
group,
|
|
37
|
+
Array.isArray(views) ? views.join(",") : views
|
|
38
|
+
]);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function safeStringify(value) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.stringify(value, (_key, v)=>{
|
|
44
|
+
if ("function" == typeof v) return `[Function: ${v.name || "anonymous"}]`;
|
|
45
|
+
if (void 0 === v) return "[undefined]";
|
|
46
|
+
return v;
|
|
47
|
+
});
|
|
48
|
+
} catch {
|
|
49
|
+
return String(value);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export { DevToolsSection };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=DevToolsSection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevToolsSection.js","sources":["../src/DevToolsSection.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport { getHook } from \"./useDebugConfig.js\";\n\ntype DevToolsView = \"browse\" | \"raw\";\n\ninterface DevToolsSectionProps {\n name: string;\n data: unknown;\n /**\n * Group name for the sidebar. Items with the same group appear\n * under the same header. Defaults to \"Sections\".\n *\n * @example\n * ```tsx\n * <DevToolsSection name=\"Article\" group=\"CMS\" data={model} />\n * <DevToolsSection name=\"Author\" group=\"CMS\" data={author} />\n * ```\n */\n group?: string;\n /**\n * Which views to show in the detail panel.\n * - `\"browse\"` — split view with root keys on the left, value tree on the right\n * - `\"raw\"` — full JSON tree view\n *\n * Accepts a single view or an array. First item is the default tab.\n * @default [\"browse\", \"raw\"]\n */\n views?: DevToolsView | DevToolsView[];\n}\n\n/**\n * Registers a named section in the Webiny DevTools extension.\n * Renders nothing — purely a data registration side-effect.\n *\n * When the component unmounts (e.g., route change), the section\n * is automatically removed from DevTools.\n *\n * @example\n * ```tsx\n * <DevToolsSection name=\"CMS Model\" data={model} />\n * <DevToolsSection name=\"Article\" group=\"CMS\" data={model} views=\"raw\" />\n * ```\n */\nexport function DevToolsSection({ name, data, group, views }: DevToolsSectionProps) {\n // Snapshot the data on every render to get a stable, plain value.\n // This also ensures that if data is a MobX observable, we capture\n // the current state as a plain object (no proxies on the hook).\n const dataKey = safeStringify(data);\n\n useEffect(() => {\n if (process.env.NODE_ENV !== \"development\") {\n return;\n }\n\n const normalizedViews: DevToolsView[] = views\n ? Array.isArray(views)\n ? views\n : [views]\n : [\"browse\", \"raw\"];\n\n let plainData: unknown;\n try {\n plainData = JSON.parse(dataKey);\n } catch {\n plainData = data;\n }\n\n const hook = getHook();\n hook.sections[name] = {\n data: plainData,\n group: group || \"Sections\",\n views: normalizedViews,\n updatedAt: Date.now()\n };\n hook.revision++;\n\n return () => {\n if (window.__WEBINY_DEVTOOLS_HOOK__) {\n delete window.__WEBINY_DEVTOOLS_HOOK__.sections[name];\n window.__WEBINY_DEVTOOLS_HOOK__.revision++;\n }\n };\n }, [name, dataKey, group, Array.isArray(views) ? views.join(\",\") : views]);\n\n return null;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value, (_key, v) => {\n if (typeof v === \"function\") {\n return `[Function: ${v.name || \"anonymous\"}]`;\n }\n if (typeof v === \"undefined\") {\n return \"[undefined]\";\n }\n return v;\n });\n } catch {\n return String(value);\n }\n}\n"],"names":["DevToolsSection","name","data","group","views","dataKey","safeStringify","useEffect","process","normalizedViews","Array","plainData","JSON","hook","getHook","Date","window","value","_key","v","String"],"mappings":";;AA2CO,SAASA,gBAAgB,EAAEC,IAAI,EAAEC,IAAI,EAAEC,KAAK,EAAEC,KAAK,EAAwB;IAI9E,MAAMC,UAAUC,cAAcJ;IAE9BK,UAAU;QACN,IAAIC,AAAyB,kBAAzBA,QAAQ,GAAG,CAAC,QAAQ,EACpB;QAGJ,MAAMC,kBAAkCL,QAClCM,MAAM,OAAO,CAACN,SACVA,QACA;YAACA;SAAM,GACX;YAAC;YAAU;SAAM;QAEvB,IAAIO;QACJ,IAAI;YACAA,YAAYC,KAAK,KAAK,CAACP;QAC3B,EAAE,OAAM;YACJM,YAAYT;QAChB;QAEA,MAAMW,OAAOC;QACbD,KAAK,QAAQ,CAACZ,KAAK,GAAG;YAClB,MAAMU;YACN,OAAOR,SAAS;YAChB,OAAOM;YACP,WAAWM,KAAK,GAAG;QACvB;QACAF,KAAK,QAAQ;QAEb,OAAO;YACH,IAAIG,OAAO,wBAAwB,EAAE;gBACjC,OAAOA,OAAO,wBAAwB,CAAC,QAAQ,CAACf,KAAK;gBACrDe,OAAO,wBAAwB,CAAC,QAAQ;YAC5C;QACJ;IACJ,GAAG;QAACf;QAAMI;QAASF;QAAOO,MAAM,OAAO,CAACN,SAASA,MAAM,IAAI,CAAC,OAAOA;KAAM;IAEzE,OAAO;AACX;AAEA,SAASE,cAAcW,KAAc;IACjC,IAAI;QACA,OAAOL,KAAK,SAAS,CAACK,OAAO,CAACC,MAAMC;YAChC,IAAI,AAAa,cAAb,OAAOA,GACP,OAAO,CAAC,WAAW,EAAEA,EAAE,IAAI,IAAI,YAAY,CAAC,CAAC;YAEjD,IAAI,AAAa,WAANA,GACP,OAAO;YAEX,OAAOA;QACX;IACJ,EAAE,OAAM;QACJ,OAAOC,OAAOH;IAClB;AACJ"}
|
package/Properties.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { PropertyStore } from "./domain/index.js";
|
|
2
3
|
export interface ConnectToPropertiesProps {
|
|
3
4
|
name: string;
|
|
4
5
|
children: React.ReactNode;
|
|
@@ -16,10 +17,11 @@ export interface Property {
|
|
|
16
17
|
interface AddPropertyOptions {
|
|
17
18
|
after?: string;
|
|
18
19
|
before?: string;
|
|
20
|
+
priority?: number;
|
|
19
21
|
}
|
|
20
22
|
interface PropertiesContext {
|
|
21
23
|
name?: string;
|
|
22
|
-
|
|
24
|
+
store: PropertyStore;
|
|
23
25
|
getAncestor(name: string): PropertiesContext | undefined;
|
|
24
26
|
getObject<T = unknown>(): T;
|
|
25
27
|
addProperty(property: Property, options?: AddPropertyOptions): void;
|
|
@@ -34,6 +36,7 @@ interface PropertiesProps {
|
|
|
34
36
|
}
|
|
35
37
|
export declare const Properties: ({ name, onChange, children }: PropertiesProps) => React.JSX.Element;
|
|
36
38
|
export declare function useProperties(): PropertiesContext;
|
|
39
|
+
export declare function useMaybeProperties(): PropertiesContext | undefined;
|
|
37
40
|
export declare function useAncestorByName(name: string | undefined): PropertiesContext | undefined;
|
|
38
41
|
interface PropertyProps {
|
|
39
42
|
id?: string;
|
package/Properties.js
CHANGED
|
@@ -1,224 +1,140 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getUniqueId, toObject } from "./utils";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import react, { createContext, useContext, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { getUniqueId, toObject } from "./utils.js";
|
|
3
|
+
import { PropertyStore } from "./domain/index.js";
|
|
4
|
+
import { usePropertyPriority } from "./PropertyPriority.js";
|
|
5
|
+
const PropertiesTargetContext = /*#__PURE__*/ createContext(void 0);
|
|
6
|
+
const ConnectToProperties = ({ name, children })=>/*#__PURE__*/ react.createElement(PropertiesTargetContext.Provider, {
|
|
7
|
+
value: name
|
|
8
|
+
}, children);
|
|
9
|
+
const PropertiesContext = /*#__PURE__*/ createContext(void 0);
|
|
10
|
+
const Properties = ({ name, onChange, children })=>{
|
|
11
|
+
const storeRef = useRef(null);
|
|
12
|
+
if (!storeRef.current) storeRef.current = new PropertyStore();
|
|
13
|
+
const store = storeRef.current;
|
|
14
|
+
let parent;
|
|
15
|
+
try {
|
|
16
|
+
parent = useProperties();
|
|
17
|
+
} catch {}
|
|
18
|
+
useEffect(()=>{
|
|
19
|
+
if (!onChange) return;
|
|
20
|
+
return store.subscribe((properties)=>{
|
|
21
|
+
onChange(properties);
|
|
22
|
+
});
|
|
23
|
+
}, [
|
|
24
|
+
store,
|
|
25
|
+
onChange
|
|
26
|
+
]);
|
|
27
|
+
const context = useMemo(()=>({
|
|
28
|
+
name,
|
|
29
|
+
store,
|
|
30
|
+
getAncestor (ancestorName) {
|
|
31
|
+
if (!parent) return;
|
|
32
|
+
return parent && parent.name === ancestorName ? parent : parent.getAncestor(ancestorName);
|
|
33
|
+
},
|
|
34
|
+
getObject () {
|
|
35
|
+
return toObject(store.allProperties);
|
|
36
|
+
},
|
|
37
|
+
addProperty (property, options = {}) {
|
|
38
|
+
store.addProperty(property, options);
|
|
39
|
+
},
|
|
40
|
+
removeProperty (id) {
|
|
41
|
+
store.removeProperty(id);
|
|
42
|
+
},
|
|
43
|
+
replaceProperty (id, property) {
|
|
44
|
+
store.replaceProperty(id, property);
|
|
45
|
+
}
|
|
46
|
+
}), [
|
|
47
|
+
store
|
|
48
|
+
]);
|
|
49
|
+
return /*#__PURE__*/ react.createElement(PropertiesContext.Provider, {
|
|
50
|
+
value: context
|
|
51
|
+
}, children);
|
|
11
52
|
};
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
function putPropertyBefore(properties, property, before) {
|
|
18
|
-
const existingIndex = properties.findIndex(prop => prop.id === property.id);
|
|
19
|
-
if (existingIndex > -1) {
|
|
20
|
-
const existingProperty = properties[existingIndex];
|
|
21
|
-
const newProperties = properties.filter(p => p.id !== property.id);
|
|
22
|
-
const targetIndex = before.endsWith("$first") ? 0 : newProperties.findIndex(prop => prop.id === before);
|
|
23
|
-
return [...newProperties.slice(0, targetIndex), existingProperty, ...newProperties.slice(targetIndex)];
|
|
24
|
-
}
|
|
25
|
-
const targetIndex = properties.findIndex(prop => prop.id === before);
|
|
26
|
-
return [...properties.slice(0, targetIndex), property, ...properties.slice(targetIndex)];
|
|
53
|
+
function useProperties() {
|
|
54
|
+
const context = useContext(PropertiesContext);
|
|
55
|
+
if (!context) throw Error("Properties context provider is missing!");
|
|
56
|
+
return context;
|
|
27
57
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
if (existingIndex > -1) {
|
|
31
|
-
const [removedProperty] = properties.splice(existingIndex, 1);
|
|
32
|
-
const targetIndex = after.endsWith("$last") ? properties.length - 1 : properties.findIndex(prop => prop.id === after);
|
|
33
|
-
return [...properties.slice(0, targetIndex + 1), removedProperty, ...properties.slice(targetIndex + 1)];
|
|
34
|
-
}
|
|
35
|
-
const targetIndex = properties.findIndex(prop => prop.id === after);
|
|
36
|
-
return [...properties.slice(0, targetIndex + 1), property, ...properties.slice(targetIndex + 1)];
|
|
37
|
-
}
|
|
38
|
-
function mergeProperty(properties, property) {
|
|
39
|
-
const index = properties.findIndex(prop => prop.id === property.id);
|
|
40
|
-
if (index > -1) {
|
|
41
|
-
return [...properties.slice(0, index), {
|
|
42
|
-
...properties[index],
|
|
43
|
-
...property
|
|
44
|
-
}, ...properties.slice(index + 1)];
|
|
45
|
-
}
|
|
46
|
-
return properties;
|
|
47
|
-
}
|
|
48
|
-
const PropertiesContext = /*#__PURE__*/createContext(undefined);
|
|
49
|
-
export const Properties = ({
|
|
50
|
-
name,
|
|
51
|
-
onChange,
|
|
52
|
-
children
|
|
53
|
-
}) => {
|
|
54
|
-
const [properties, setProperties] = useState([]);
|
|
55
|
-
let parent;
|
|
56
|
-
try {
|
|
57
|
-
parent = useProperties();
|
|
58
|
-
} catch {
|
|
59
|
-
// Do nothing, if there's no parent.
|
|
60
|
-
}
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
if (onChange) {
|
|
63
|
-
onChange(properties);
|
|
64
|
-
}
|
|
65
|
-
}, [properties]);
|
|
66
|
-
const context = useMemo(() => ({
|
|
67
|
-
name,
|
|
68
|
-
properties,
|
|
69
|
-
getAncestor(ancestorName) {
|
|
70
|
-
if (!parent) {
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
return parent && parent.name === ancestorName ? parent : parent.getAncestor(ancestorName);
|
|
74
|
-
},
|
|
75
|
-
getObject() {
|
|
76
|
-
return toObject(properties);
|
|
77
|
-
},
|
|
78
|
-
addProperty(property, options = {}) {
|
|
79
|
-
setProperties(properties => {
|
|
80
|
-
const index = properties.findIndex(prop => prop.id === property.id);
|
|
81
|
-
if (index > -1) {
|
|
82
|
-
const newProperties = mergeProperty(properties, property);
|
|
83
|
-
if (options.after) {
|
|
84
|
-
return putPropertyAfter(newProperties, property, options.after);
|
|
85
|
-
}
|
|
86
|
-
if (options.before) {
|
|
87
|
-
return putPropertyBefore(newProperties, property, options.before);
|
|
88
|
-
}
|
|
89
|
-
return newProperties;
|
|
90
|
-
}
|
|
91
|
-
if (options.after) {
|
|
92
|
-
return putPropertyAfter(properties, property, options.after);
|
|
93
|
-
}
|
|
94
|
-
if (options.before) {
|
|
95
|
-
return putPropertyBefore(properties, property, options.before);
|
|
96
|
-
}
|
|
97
|
-
return [...properties, property];
|
|
98
|
-
});
|
|
99
|
-
},
|
|
100
|
-
removeProperty(id) {
|
|
101
|
-
setProperties(properties => {
|
|
102
|
-
return removeByParent(id, properties.filter(prop => prop.id !== id));
|
|
103
|
-
});
|
|
104
|
-
},
|
|
105
|
-
replaceProperty(id, property) {
|
|
106
|
-
setProperties(properties => {
|
|
107
|
-
const toReplace = properties.findIndex(prop => prop.id === id);
|
|
108
|
-
if (toReplace > -1) {
|
|
109
|
-
// Replace the property and remove all remaining child properties.
|
|
110
|
-
return removeByParent(id, [...properties.slice(0, toReplace), property, ...properties.slice(toReplace + 1)]);
|
|
111
|
-
}
|
|
112
|
-
return properties;
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}), [properties]);
|
|
116
|
-
return /*#__PURE__*/React.createElement(PropertiesContext.Provider, {
|
|
117
|
-
value: context
|
|
118
|
-
}, children);
|
|
119
|
-
};
|
|
120
|
-
export function useProperties() {
|
|
121
|
-
const properties = useContext(PropertiesContext);
|
|
122
|
-
if (!properties) {
|
|
123
|
-
throw Error("Properties context provider is missing!");
|
|
124
|
-
}
|
|
125
|
-
return properties;
|
|
58
|
+
function useMaybeProperties() {
|
|
59
|
+
return useContext(PropertiesContext);
|
|
126
60
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return parent.getAncestor(name);
|
|
137
|
-
}, [name]);
|
|
61
|
+
function useAncestorByName(name) {
|
|
62
|
+
const parent = useMaybeProperties();
|
|
63
|
+
return useMemo(()=>{
|
|
64
|
+
if (!name || !parent) return;
|
|
65
|
+
if (parent.name === name) return parent;
|
|
66
|
+
return parent.getAncestor(name);
|
|
67
|
+
}, [
|
|
68
|
+
name
|
|
69
|
+
]);
|
|
138
70
|
}
|
|
139
|
-
const PropertyContext = /*#__PURE__*/createContext(
|
|
140
|
-
|
|
141
|
-
|
|
71
|
+
const PropertyContext = /*#__PURE__*/ createContext(void 0);
|
|
72
|
+
function useParentProperty() {
|
|
73
|
+
return useContext(PropertyContext);
|
|
142
74
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return newParent ? matchOrGetAncestor(newParent, params) : undefined;
|
|
155
|
-
};
|
|
156
|
-
return property ? matchOrGetAncestor(property, params) : undefined;
|
|
75
|
+
function useAncestor(params) {
|
|
76
|
+
const property = useParentProperty();
|
|
77
|
+
const { store } = useProperties();
|
|
78
|
+
const matchOrGetAncestor = (property, params)=>{
|
|
79
|
+
const children = store.getChildrenOf(property.id);
|
|
80
|
+
const matchedProps = children.filter((prop)=>prop.name in params && prop.value === params[prop.name]);
|
|
81
|
+
if (matchedProps.length === Object.keys(params).length) return property;
|
|
82
|
+
const newParent = property.parent ? store.getById(property.parent) : void 0;
|
|
83
|
+
return newParent ? matchOrGetAncestor(newParent, params) : void 0;
|
|
84
|
+
};
|
|
85
|
+
return property ? matchOrGetAncestor(property, params) : void 0;
|
|
157
86
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const properties = targetName ? ancestorByName : immediateProperties;
|
|
177
|
-
if (!properties) {
|
|
178
|
-
throw Error("<Properties> provider is missing higher in the hierarchy!");
|
|
179
|
-
}
|
|
180
|
-
const {
|
|
181
|
-
addProperty,
|
|
182
|
-
removeProperty,
|
|
183
|
-
replaceProperty
|
|
184
|
-
} = properties;
|
|
185
|
-
const parentId = parent ? parent : root ? "" : parentProperty?.id || "";
|
|
186
|
-
const property = {
|
|
187
|
-
id: uniqueId,
|
|
188
|
-
name,
|
|
189
|
-
value,
|
|
190
|
-
parent: parentId,
|
|
191
|
-
array
|
|
192
|
-
};
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
if (remove) {
|
|
195
|
-
removeProperty(uniqueId);
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (replace) {
|
|
199
|
-
replaceProperty(replace, property);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
const $isFirst = before === "$first";
|
|
203
|
-
const $isLast = after === "$last";
|
|
204
|
-
addProperty({
|
|
205
|
-
...property,
|
|
206
|
-
$isFirst,
|
|
207
|
-
$isLast
|
|
208
|
-
}, {
|
|
209
|
-
after,
|
|
210
|
-
before
|
|
211
|
-
});
|
|
212
|
-
return () => {
|
|
213
|
-
removeProperty(uniqueId);
|
|
87
|
+
const Property = ({ id, name, value, children, after, before, replace, remove = false, array = false, root = false, parent })=>{
|
|
88
|
+
const targetName = useContext(PropertiesTargetContext);
|
|
89
|
+
const uniqueId = useMemo(()=>id || getUniqueId(), []);
|
|
90
|
+
const parentProperty = useParentProperty();
|
|
91
|
+
const immediateProperties = useProperties();
|
|
92
|
+
const ancestorByName = useAncestorByName(targetName);
|
|
93
|
+
const previousValue = useRef(value);
|
|
94
|
+
const priority = usePropertyPriority();
|
|
95
|
+
const properties = targetName && ancestorByName ? ancestorByName : immediateProperties;
|
|
96
|
+
if (!properties) throw Error("<Properties> provider is missing higher in the hierarchy!");
|
|
97
|
+
const { addProperty, removeProperty, replaceProperty, store: propertyStore } = properties;
|
|
98
|
+
const parentId = parent ? parent : root ? "" : parentProperty?.id || "";
|
|
99
|
+
const property = {
|
|
100
|
+
id: uniqueId,
|
|
101
|
+
name,
|
|
102
|
+
value,
|
|
103
|
+
parent: parentId,
|
|
104
|
+
array
|
|
214
105
|
};
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
106
|
+
if (!remove) propertyStore.registerLookup(property);
|
|
107
|
+
useEffect(()=>{
|
|
108
|
+
if (remove) return void removeProperty(uniqueId);
|
|
109
|
+
if (replace) return void replaceProperty(replace, property);
|
|
110
|
+
const $isFirst = "$first" === before;
|
|
111
|
+
const $isLast = "$last" === after;
|
|
112
|
+
addProperty({
|
|
113
|
+
...property,
|
|
114
|
+
$isFirst,
|
|
115
|
+
$isLast
|
|
116
|
+
}, {
|
|
117
|
+
after,
|
|
118
|
+
before,
|
|
119
|
+
priority
|
|
120
|
+
});
|
|
121
|
+
return ()=>{
|
|
122
|
+
removeProperty(uniqueId);
|
|
123
|
+
};
|
|
124
|
+
}, []);
|
|
125
|
+
useEffect(()=>{
|
|
126
|
+
if (previousValue.current !== value) {
|
|
127
|
+
previousValue.current = value;
|
|
128
|
+
if (!remove && !replace) replaceProperty(uniqueId, property);
|
|
129
|
+
}
|
|
130
|
+
}, [
|
|
131
|
+
value
|
|
132
|
+
]);
|
|
133
|
+
if (children) return /*#__PURE__*/ react.createElement(PropertyContext.Provider, {
|
|
134
|
+
value: property
|
|
219
135
|
}, children);
|
|
220
|
-
|
|
221
|
-
return null;
|
|
136
|
+
return null;
|
|
222
137
|
};
|
|
138
|
+
export { ConnectToProperties, Properties, Property, useAncestor, useAncestorByName, useMaybeProperties, useParentProperty, useProperties };
|
|
223
139
|
|
|
224
140
|
//# sourceMappingURL=Properties.js.map
|