@webiny/react-properties 6.3.0 → 6.4.0-beta.1
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/DevToolsSection.js +47 -63
- package/DevToolsSection.js.map +1 -1
- package/Properties.js +126 -179
- package/Properties.js.map +1 -1
- package/PropertyPriority.js +8 -12
- package/PropertyPriority.js.map +1 -1
- package/createConfigurableComponent.js +58 -103
- package/createConfigurableComponent.js.map +1 -1
- package/domain/PropertyStore.js +149 -224
- package/domain/PropertyStore.js.map +1 -1
- package/domain/index.js +0 -2
- package/index.js +0 -2
- package/package.json +6 -6
- package/useDebugConfig.js +38 -46
- package/useDebugConfig.js.map +1 -1
- package/useIdGenerator.js +18 -9
- package/useIdGenerator.js.map +1 -1
- package/utils.js +38 -37
- package/utils.js.map +1 -1
- package/domain/index.js.map +0 -1
- package/index.js.map +0 -1
|
@@ -1,112 +1,67 @@
|
|
|
1
|
-
import
|
|
1
|
+
import react, { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { Compose, makeDecoratable } from "@webiny/react-composition";
|
|
3
3
|
import { Properties, toObject } from "./index.js";
|
|
4
4
|
import { useDebugConfig } from "./useDebugConfig.js";
|
|
5
5
|
import { PropertyPriorityProvider } from "./PropertyPriority.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}) => {
|
|
31
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, children);
|
|
32
|
-
});
|
|
33
|
-
const ConfigApplySecondary = makeDecoratable(`${name}ConfigApply<Secondary>`, ({
|
|
34
|
-
children
|
|
35
|
-
}) => {
|
|
36
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, children);
|
|
37
|
-
});
|
|
38
|
-
const Config = ({
|
|
39
|
-
priority = "primary",
|
|
40
|
-
children
|
|
41
|
-
}) => {
|
|
42
|
-
if (priority === "primary") {
|
|
43
|
-
return /*#__PURE__*/React.createElement(Compose, {
|
|
44
|
-
component: ConfigApplyPrimary,
|
|
45
|
-
with: createHOC(children)
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
return /*#__PURE__*/React.createElement(Compose, {
|
|
49
|
-
component: ConfigApplySecondary,
|
|
50
|
-
with: createHOC(children)
|
|
6
|
+
const createHOC = (newChildren)=>(BaseComponent)=>function({ children }) {
|
|
7
|
+
return /*#__PURE__*/ react.createElement(BaseComponent, null, newChildren, children);
|
|
8
|
+
};
|
|
9
|
+
function createConfigurableComponent(name) {
|
|
10
|
+
const ConfigApplyPrimary = makeDecoratable(`${name}ConfigApply<Primary>`, ({ children })=>/*#__PURE__*/ react.createElement(react.Fragment, null, children));
|
|
11
|
+
const ConfigApplySecondary = makeDecoratable(`${name}ConfigApply<Secondary>`, ({ children })=>/*#__PURE__*/ react.createElement(react.Fragment, null, children));
|
|
12
|
+
const Config = ({ priority = "primary", children })=>{
|
|
13
|
+
if ("primary" === priority) return /*#__PURE__*/ react.createElement(Compose, {
|
|
14
|
+
component: ConfigApplyPrimary,
|
|
15
|
+
with: createHOC(children)
|
|
16
|
+
});
|
|
17
|
+
return /*#__PURE__*/ react.createElement(Compose, {
|
|
18
|
+
component: ConfigApplySecondary,
|
|
19
|
+
with: createHOC(children)
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
const defaultContext = {
|
|
23
|
+
properties: []
|
|
24
|
+
};
|
|
25
|
+
const ViewContext = /*#__PURE__*/ react.createContext(defaultContext);
|
|
26
|
+
const ConfigApplyTree = /*#__PURE__*/ react.memo(function() {
|
|
27
|
+
return /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(ConfigApplyPrimary, null), /*#__PURE__*/ react.createElement(PropertyPriorityProvider, {
|
|
28
|
+
priority: 1
|
|
29
|
+
}, /*#__PURE__*/ react.createElement(ConfigApplySecondary, null)));
|
|
51
30
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
31
|
+
const WithConfig = ({ onProperties, children })=>{
|
|
32
|
+
const [properties, setProperties] = useState(null);
|
|
33
|
+
const resolvedProperties = properties ?? [];
|
|
34
|
+
useDebugConfig(name, resolvedProperties);
|
|
35
|
+
const context = {
|
|
36
|
+
properties: resolvedProperties
|
|
37
|
+
};
|
|
38
|
+
useEffect(()=>{
|
|
39
|
+
if (null !== properties && "function" == typeof onProperties) onProperties(properties);
|
|
40
|
+
}, [
|
|
41
|
+
properties
|
|
42
|
+
]);
|
|
43
|
+
const stateUpdater = useCallback((properties)=>{
|
|
44
|
+
setProperties(properties);
|
|
45
|
+
}, []);
|
|
46
|
+
return /*#__PURE__*/ react.createElement(ViewContext.Provider, {
|
|
47
|
+
value: context
|
|
48
|
+
}, /*#__PURE__*/ react.createElement(Properties, {
|
|
49
|
+
name: name,
|
|
50
|
+
onChange: stateUpdater
|
|
51
|
+
}, /*#__PURE__*/ react.createElement(ConfigApplyTree, null)), null !== properties ? children : null);
|
|
52
|
+
};
|
|
53
|
+
function useConfig() {
|
|
54
|
+
const { properties } = useContext(ViewContext);
|
|
55
|
+
return useMemo(()=>toObject(properties), [
|
|
56
|
+
properties
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
WithConfig,
|
|
61
|
+
Config,
|
|
62
|
+
useConfig
|
|
83
63
|
};
|
|
84
|
-
useEffect(() => {
|
|
85
|
-
if (properties !== null && typeof onProperties === "function") {
|
|
86
|
-
onProperties(properties);
|
|
87
|
-
}
|
|
88
|
-
}, [properties]);
|
|
89
|
-
const stateUpdater = useCallback(properties => {
|
|
90
|
-
setProperties(properties);
|
|
91
|
-
}, []);
|
|
92
|
-
return /*#__PURE__*/React.createElement(ViewContext.Provider, {
|
|
93
|
-
value: context
|
|
94
|
-
}, /*#__PURE__*/React.createElement(Properties, {
|
|
95
|
-
name: name,
|
|
96
|
-
onChange: stateUpdater
|
|
97
|
-
}, /*#__PURE__*/React.createElement(ConfigApplyTree, null)), properties !== null ? children : null);
|
|
98
|
-
};
|
|
99
|
-
function useConfig() {
|
|
100
|
-
const {
|
|
101
|
-
properties
|
|
102
|
-
} = useContext(ViewContext);
|
|
103
|
-
return useMemo(() => toObject(properties), [properties]);
|
|
104
|
-
}
|
|
105
|
-
return {
|
|
106
|
-
WithConfig,
|
|
107
|
-
Config,
|
|
108
|
-
useConfig
|
|
109
|
-
};
|
|
110
64
|
}
|
|
65
|
+
export { createConfigurableComponent };
|
|
111
66
|
|
|
112
67
|
//# sourceMappingURL=createConfigurableComponent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"file":"createConfigurableComponent.js","sources":["../src/createConfigurableComponent.tsx"],"sourcesContent":["import React, { useCallback, useContext, useEffect, useMemo, useState } from \"react\";\nimport type { Decorator } from \"@webiny/react-composition\";\nimport { Compose, makeDecoratable } from \"@webiny/react-composition\";\nimport type { GenericComponent } from \"@webiny/react-composition/types.js\";\nimport type { Property } from \"~/index.js\";\nimport { Properties, toObject } from \"~/index.js\";\nimport { useDebugConfig } from \"./useDebugConfig.js\";\nimport { PropertyPriorityProvider } from \"./PropertyPriority.js\";\n\n/**\n * Each `<Config>` call composes a new HOC around the previous one via `Compose`.\n * The last composed HOC is the outermost wrapper. By placing `{newChildren}`\n * (this HOC's addition) before `{children}` (all previously composed configs),\n * the final render order matches declaration order:\n *\n * <Config>A</Config> → renders first (outermost HOC, its newChildren rendered first)\n * <Config>B</Config> → renders second\n * <Config>C</Config> → renders third (innermost, rendered last via children chain)\n *\n * This is important because Property components register in mount order,\n * so declaration order = mount order = predictable config resolution.\n */\nconst createHOC =\n (newChildren: React.ReactNode): Decorator<GenericComponent<{ children?: React.ReactNode }>> =>\n BaseComponent => {\n return function ConfigHOC({ children }) {\n return (\n <BaseComponent>\n {newChildren}\n {children}\n </BaseComponent>\n );\n };\n };\n\nexport interface WithConfigProps {\n children: React.ReactNode;\n onProperties?(properties: Property[]): void;\n}\n\ninterface ConfigApplyProps {\n children?: React.ReactNode;\n}\n\nexport interface ConfigProps {\n children: React.ReactNode;\n priority?: \"primary\" | \"secondary\";\n}\n\nexport function createConfigurableComponent<TConfig>(name: string) {\n const ConfigApplyPrimary = makeDecoratable(\n `${name}ConfigApply<Primary>`,\n ({ children }: ConfigApplyProps) => {\n return <>{children}</>;\n }\n );\n\n const ConfigApplySecondary = makeDecoratable(\n `${name}ConfigApply<Secondary>`,\n ({ children }: ConfigApplyProps) => {\n return <>{children}</>;\n }\n );\n\n const Config = ({ priority = \"primary\", children }: ConfigProps) => {\n if (priority === \"primary\") {\n return <Compose component={ConfigApplyPrimary} with={createHOC(children)} />;\n }\n return <Compose component={ConfigApplySecondary} with={createHOC(children)} />;\n };\n\n interface ViewContext {\n properties: Property[];\n }\n\n const defaultContext = { properties: [] };\n\n const ViewContext = React.createContext<ViewContext>(defaultContext);\n\n /**\n * Memoized config subtree — ConfigApply components don't depend on WithConfig\n * props, so they must not remount when the parent re-renders. Without this,\n * every parent re-render causes Property components inside HOCs to unmount\n * and remount, corrupting the config object.\n */\n const ConfigApplyTree = React.memo(function ConfigApplyTree() {\n return (\n <>\n <ConfigApplyPrimary />\n <PropertyPriorityProvider priority={1}>\n <ConfigApplySecondary />\n </PropertyPriorityProvider>\n </>\n );\n });\n\n const WithConfig = ({ onProperties, children }: WithConfigProps) => {\n // `null` = config not yet collected; `[]` = collected but empty.\n // This distinction is critical: children must NOT render until the\n // PropertyStore debounce has flushed and delivered the initial config.\n // Rendering children with partial/empty config causes errors in\n // consumers like LexicalEditor that require a complete config on mount.\n const [properties, setProperties] = useState<Property[] | null>(null);\n const resolvedProperties = properties ?? [];\n useDebugConfig(name, resolvedProperties);\n const context = { properties: resolvedProperties };\n\n useEffect(() => {\n if (properties !== null && typeof onProperties === \"function\") {\n onProperties(properties);\n }\n }, [properties]);\n\n const stateUpdater = useCallback((properties: Property[]) => {\n setProperties(properties);\n }, []);\n\n return (\n <ViewContext.Provider value={context}>\n {/* ConfigApplyTree always renders so Property components inside\n composed HOCs can mount and register with the PropertyStore.\n It lives outside the children gate below. */}\n <Properties name={name} onChange={stateUpdater}>\n <ConfigApplyTree />\n </Properties>\n {/* Gate: only render children once the PropertyStore has flushed\n its first batch (properties !== null). This guarantees that\n useConfig() returns a complete config object on first render. */}\n {properties !== null ? children : null}\n </ViewContext.Provider>\n );\n };\n\n function useConfig<TExtra extends object>(): TConfig & TExtra {\n const { properties } = useContext(ViewContext);\n return useMemo(() => toObject<TConfig & TExtra>(properties), [properties]);\n }\n\n return {\n WithConfig,\n Config,\n useConfig\n };\n}\n"],"names":["createHOC","newChildren","BaseComponent","children","createConfigurableComponent","name","ConfigApplyPrimary","makeDecoratable","ConfigApplySecondary","Config","priority","Compose","defaultContext","ViewContext","React","ConfigApplyTree","PropertyPriorityProvider","WithConfig","onProperties","properties","setProperties","useState","resolvedProperties","useDebugConfig","context","useEffect","stateUpdater","useCallback","Properties","useConfig","useContext","useMemo","toObject"],"mappings":";;;;;AAsBA,MAAMA,YACF,CAACC,cACDC,CAAAA,gBACW,SAAmB,EAAEC,QAAQ,EAAE;YAClC,OAAO,WAAP,GACI,oBAACD,eAAAA,MACID,aACAE;QAGb;AAiBD,SAASC,4BAAqCC,IAAY;IAC7D,MAAMC,qBAAqBC,gBACvB,GAAGF,KAAK,oBAAoB,CAAC,EAC7B,CAAC,EAAEF,QAAQ,EAAoB,GACpB,WAAP,GAAO,0CAAGA;IAIlB,MAAMK,uBAAuBD,gBACzB,GAAGF,KAAK,sBAAsB,CAAC,EAC/B,CAAC,EAAEF,QAAQ,EAAoB,GACpB,WAAP,GAAO,0CAAGA;IAIlB,MAAMM,SAAS,CAAC,EAAEC,WAAW,SAAS,EAAEP,QAAQ,EAAe;QAC3D,IAAIO,AAAa,cAAbA,UACA,OAAO,WAAP,GAAO,oBAACC,SAAOA;YAAC,WAAWL;YAAoB,MAAMN,UAAUG;;QAEnE,OAAO,WAAP,GAAO,oBAACQ,SAAOA;YAAC,WAAWH;YAAsB,MAAMR,UAAUG;;IACrE;IAMA,MAAMS,iBAAiB;QAAE,YAAY,EAAE;IAAC;IAExC,MAAMC,cAAc,WAAdA,GAAcC,MAAAA,aAAmB,CAAcF;IAQrD,MAAMG,kBAAkB,WAAlBA,GAAkBD,MAAAA,IAAU,CAAC;QAC/B,OAAO,WAAP,GACI,wDACI,oBAACR,oBAAAA,OAAAA,WAAAA,GACD,oBAACU,0BAAwBA;YAAC,UAAU;yBAChC,oBAACR,sBAAAA;IAIjB;IAEA,MAAMS,aAAa,CAAC,EAAEC,YAAY,EAAEf,QAAQ,EAAmB;QAM3D,MAAM,CAACgB,YAAYC,cAAc,GAAGC,SAA4B;QAChE,MAAMC,qBAAqBH,cAAc,EAAE;QAC3CI,eAAelB,MAAMiB;QACrB,MAAME,UAAU;YAAE,YAAYF;QAAmB;QAEjDG,UAAU;YACN,IAAIN,AAAe,SAAfA,cAAuB,AAAwB,cAAxB,OAAOD,cAC9BA,aAAaC;QAErB,GAAG;YAACA;SAAW;QAEf,MAAMO,eAAeC,YAAY,CAACR;YAC9BC,cAAcD;QAClB,GAAG,EAAE;QAEL,OAAO,WAAP,GACI,oBAACN,YAAY,QAAQ;YAAC,OAAOW;yBAIzB,oBAACI,YAAUA;YAAC,MAAMvB;YAAM,UAAUqB;yBAC9B,oBAACX,iBAAAA,QAKJI,AAAe,SAAfA,aAAsBhB,WAAW;IAG9C;IAEA,SAAS0B;QACL,MAAM,EAAEV,UAAU,EAAE,GAAGW,WAAWjB;QAClC,OAAOkB,QAAQ,IAAMC,SAA2Bb,aAAa;YAACA;SAAW;IAC7E;IAEA,OAAO;QACHF;QACAR;QACAoB;IACJ;AACJ"}
|
package/domain/PropertyStore.js
CHANGED
|
@@ -1,229 +1,154 @@
|
|
|
1
1
|
import debounce from "lodash/debounce.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
2
|
+
class PropertyStore {
|
|
3
|
+
get allProperties() {
|
|
4
|
+
return this.order.filter((id)=>this.map.has(id)).map((id)=>this.map.get(id));
|
|
5
|
+
}
|
|
6
|
+
subscribe(listener) {
|
|
7
|
+
this.listeners.add(listener);
|
|
8
|
+
return ()=>{
|
|
9
|
+
this.listeners.delete(listener);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
getChildrenOf(parentId) {
|
|
13
|
+
return Array.from(this.lookup.values()).filter((p)=>p.parent === parentId);
|
|
14
|
+
}
|
|
15
|
+
getById(id) {
|
|
16
|
+
return this.lookup.get(id);
|
|
17
|
+
}
|
|
18
|
+
registerLookup(property) {
|
|
19
|
+
if (this.lookup.has(property.id)) {
|
|
20
|
+
const existing = this.lookup.get(property.id);
|
|
21
|
+
this.lookup.set(property.id, {
|
|
22
|
+
...existing,
|
|
23
|
+
...property
|
|
24
|
+
});
|
|
25
|
+
} else this.lookup.set(property.id, property);
|
|
26
|
+
}
|
|
27
|
+
addProperty(property, options = {}) {
|
|
28
|
+
this.registerLookup(property);
|
|
29
|
+
this.queue.push({
|
|
30
|
+
type: "add",
|
|
31
|
+
property,
|
|
32
|
+
options
|
|
33
|
+
});
|
|
34
|
+
this.scheduleFlush();
|
|
35
|
+
}
|
|
36
|
+
removeProperty(id) {
|
|
37
|
+
this.lookup.delete(id);
|
|
38
|
+
this.queue.push({
|
|
39
|
+
type: "remove",
|
|
40
|
+
id
|
|
41
|
+
});
|
|
42
|
+
this.scheduleFlush();
|
|
43
|
+
}
|
|
44
|
+
replaceProperty(oldId, newProperty) {
|
|
45
|
+
this.lookup.delete(oldId);
|
|
46
|
+
this.lookup.set(newProperty.id, newProperty);
|
|
47
|
+
this.queue.push({
|
|
48
|
+
type: "replace",
|
|
49
|
+
oldId,
|
|
50
|
+
newProperty
|
|
51
|
+
});
|
|
52
|
+
this.scheduleFlush();
|
|
53
|
+
}
|
|
54
|
+
processQueue() {
|
|
55
|
+
if (0 === this.queue.length) return;
|
|
56
|
+
const ops = this.queue.splice(0);
|
|
57
|
+
ops.sort((a, b)=>{
|
|
58
|
+
const pa = "add" === a.type ? a.options.priority ?? 0 : 0;
|
|
59
|
+
const pb = "add" === b.type ? b.options.priority ?? 0 : 0;
|
|
60
|
+
return pa - pb;
|
|
61
|
+
});
|
|
62
|
+
for (const op of ops)switch(op.type){
|
|
63
|
+
case "add":
|
|
64
|
+
this.executeAdd(op.property, op.options);
|
|
65
|
+
break;
|
|
66
|
+
case "remove":
|
|
67
|
+
this.executeRemove(op.id);
|
|
68
|
+
break;
|
|
69
|
+
case "replace":
|
|
70
|
+
this.executeReplace(op.oldId, op.newProperty);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
this.order.sort((a, b)=>{
|
|
74
|
+
if (this.positioned.has(a) || this.positioned.has(b)) return 0;
|
|
75
|
+
return (this.priorities.get(a) ?? 0) - (this.priorities.get(b) ?? 0);
|
|
76
|
+
});
|
|
77
|
+
const properties = this.allProperties;
|
|
78
|
+
for (const listener of this.listeners)listener(properties);
|
|
79
|
+
}
|
|
80
|
+
executeAdd(property, options) {
|
|
81
|
+
if (options.after || options.before) this.positioned.add(property.id);
|
|
82
|
+
const exists = this.map.has(property.id);
|
|
83
|
+
if (exists) {
|
|
84
|
+
const existing = this.map.get(property.id);
|
|
85
|
+
this.map.set(property.id, {
|
|
86
|
+
...existing,
|
|
87
|
+
...property
|
|
88
|
+
});
|
|
89
|
+
if (options.after) this.reposition(property.id, options.after, "after");
|
|
90
|
+
else if (options.before) this.reposition(property.id, options.before, "before");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.map.set(property.id, property);
|
|
94
|
+
this.priorities.set(property.id, options.priority ?? 0);
|
|
95
|
+
if (options.after) this.insertAfter(property.id, options.after);
|
|
96
|
+
else if (options.before) this.insertBefore(property.id, options.before);
|
|
97
|
+
else this.order.push(property.id);
|
|
98
|
+
}
|
|
99
|
+
executeRemove(id) {
|
|
100
|
+
if (!this.map.has(id)) return;
|
|
101
|
+
this.map.delete(id);
|
|
102
|
+
this.priorities.delete(id);
|
|
103
|
+
this.positioned.delete(id);
|
|
104
|
+
this.order = this.order.filter((oid)=>oid !== id);
|
|
105
|
+
}
|
|
106
|
+
executeReplace(oldId, newProperty) {
|
|
107
|
+
const idx = this.order.indexOf(oldId);
|
|
108
|
+
if (-1 === idx) return;
|
|
109
|
+
this.map.delete(oldId);
|
|
110
|
+
this.map.set(newProperty.id, newProperty);
|
|
111
|
+
this.order[idx] = newProperty.id;
|
|
112
|
+
this.removeDescendants(oldId);
|
|
113
|
+
}
|
|
114
|
+
insertBefore(id, before) {
|
|
115
|
+
if (before.endsWith("$first")) return void this.order.unshift(id);
|
|
116
|
+
const targetIdx = this.order.indexOf(before);
|
|
117
|
+
if (-1 === targetIdx) return void this.order.push(id);
|
|
118
|
+
this.order.splice(targetIdx, 0, id);
|
|
119
|
+
}
|
|
120
|
+
insertAfter(id, after) {
|
|
121
|
+
if (after.endsWith("$last")) return void this.order.push(id);
|
|
122
|
+
const targetIdx = this.order.indexOf(after);
|
|
123
|
+
if (-1 === targetIdx) return void this.order.push(id);
|
|
124
|
+
this.order.splice(targetIdx + 1, 0, id);
|
|
125
|
+
}
|
|
126
|
+
reposition(id, targetId, position) {
|
|
127
|
+
this.order = this.order.filter((oid)=>oid !== id);
|
|
128
|
+
if ("before" === position) this.insertBefore(id, targetId);
|
|
129
|
+
else this.insertAfter(id, targetId);
|
|
130
|
+
}
|
|
131
|
+
removeDescendants(parentId) {
|
|
132
|
+
const children = Array.from(this.map.values()).filter((p)=>p.parent === parentId);
|
|
133
|
+
for (const child of children){
|
|
134
|
+
this.map.delete(child.id);
|
|
135
|
+
this.order = this.order.filter((oid)=>oid !== child.id);
|
|
136
|
+
this.removeDescendants(child.id);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
constructor(){
|
|
140
|
+
this.map = new Map();
|
|
141
|
+
this.order = [];
|
|
142
|
+
this.queue = [];
|
|
143
|
+
this.listeners = new Set();
|
|
144
|
+
this.priorities = new Map();
|
|
145
|
+
this.positioned = new Set();
|
|
146
|
+
this.lookup = new Map();
|
|
147
|
+
this.scheduleFlush = debounce(()=>{
|
|
148
|
+
this.processQueue();
|
|
149
|
+
}, 0);
|
|
113
150
|
}
|
|
114
|
-
|
|
115
|
-
// Stable-sort the order array by priority, but only for properties
|
|
116
|
-
// that were NOT explicitly positioned via before/after. Explicitly
|
|
117
|
-
// positioned properties keep their placement.
|
|
118
|
-
this.order.sort((a, b) => {
|
|
119
|
-
if (this.positioned.has(a) || this.positioned.has(b)) {
|
|
120
|
-
return 0;
|
|
121
|
-
}
|
|
122
|
-
return (this.priorities.get(a) ?? 0) - (this.priorities.get(b) ?? 0);
|
|
123
|
-
});
|
|
124
|
-
const properties = this.allProperties;
|
|
125
|
-
for (const listener of this.listeners) {
|
|
126
|
-
listener(properties);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
executeAdd(property, options) {
|
|
130
|
-
if (options.after || options.before) {
|
|
131
|
-
this.positioned.add(property.id);
|
|
132
|
-
}
|
|
133
|
-
const exists = this.map.has(property.id);
|
|
134
|
-
if (exists) {
|
|
135
|
-
// Merge into existing property. Keep the original priority so
|
|
136
|
-
// that a secondary config overriding a primary property doesn't
|
|
137
|
-
// cause the re-sort to move it after all primary properties.
|
|
138
|
-
const existing = this.map.get(property.id);
|
|
139
|
-
this.map.set(property.id, {
|
|
140
|
-
...existing,
|
|
141
|
-
...property
|
|
142
|
-
});
|
|
143
|
-
if (options.after) {
|
|
144
|
-
this.reposition(property.id, options.after, "after");
|
|
145
|
-
} else if (options.before) {
|
|
146
|
-
this.reposition(property.id, options.before, "before");
|
|
147
|
-
}
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
this.map.set(property.id, property);
|
|
151
|
-
// Set priority only for new properties — not merges (handled above).
|
|
152
|
-
this.priorities.set(property.id, options.priority ?? 0);
|
|
153
|
-
if (options.after) {
|
|
154
|
-
this.insertAfter(property.id, options.after);
|
|
155
|
-
} else if (options.before) {
|
|
156
|
-
this.insertBefore(property.id, options.before);
|
|
157
|
-
} else {
|
|
158
|
-
this.order.push(property.id);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
executeRemove(id) {
|
|
162
|
-
if (!this.map.has(id)) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
this.map.delete(id);
|
|
166
|
-
this.priorities.delete(id);
|
|
167
|
-
this.positioned.delete(id);
|
|
168
|
-
this.order = this.order.filter(oid => oid !== id);
|
|
169
|
-
// Note: we intentionally do NOT call removeDescendants here.
|
|
170
|
-
// React's component lifecycle ensures that when a parent Property
|
|
171
|
-
// unmounts, all child Properties unmount too — each triggering its
|
|
172
|
-
// own removeProperty call. Calling removeDescendants would wipe
|
|
173
|
-
// children that belong to OTHER still-mounted configs sharing the
|
|
174
|
-
// same parent ID (e.g., id="pageSettings" used by both primary
|
|
175
|
-
// and secondary configs).
|
|
176
|
-
}
|
|
177
|
-
executeReplace(oldId, newProperty) {
|
|
178
|
-
const idx = this.order.indexOf(oldId);
|
|
179
|
-
if (idx === -1) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
this.map.delete(oldId);
|
|
183
|
-
this.map.set(newProperty.id, newProperty);
|
|
184
|
-
this.order[idx] = newProperty.id;
|
|
185
|
-
this.removeDescendants(oldId);
|
|
186
|
-
}
|
|
187
|
-
insertBefore(id, before) {
|
|
188
|
-
if (before.endsWith("$first")) {
|
|
189
|
-
this.order.unshift(id);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const targetIdx = this.order.indexOf(before);
|
|
193
|
-
if (targetIdx === -1) {
|
|
194
|
-
this.order.push(id);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
this.order.splice(targetIdx, 0, id);
|
|
198
|
-
}
|
|
199
|
-
insertAfter(id, after) {
|
|
200
|
-
if (after.endsWith("$last")) {
|
|
201
|
-
this.order.push(id);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
const targetIdx = this.order.indexOf(after);
|
|
205
|
-
if (targetIdx === -1) {
|
|
206
|
-
this.order.push(id);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
this.order.splice(targetIdx + 1, 0, id);
|
|
210
|
-
}
|
|
211
|
-
reposition(id, targetId, position) {
|
|
212
|
-
this.order = this.order.filter(oid => oid !== id);
|
|
213
|
-
if (position === "before") {
|
|
214
|
-
this.insertBefore(id, targetId);
|
|
215
|
-
} else {
|
|
216
|
-
this.insertAfter(id, targetId);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
removeDescendants(parentId) {
|
|
220
|
-
const children = Array.from(this.map.values()).filter(p => p.parent === parentId);
|
|
221
|
-
for (const child of children) {
|
|
222
|
-
this.map.delete(child.id);
|
|
223
|
-
this.order = this.order.filter(oid => oid !== child.id);
|
|
224
|
-
this.removeDescendants(child.id);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
151
|
}
|
|
152
|
+
export { PropertyStore };
|
|
228
153
|
|
|
229
154
|
//# sourceMappingURL=PropertyStore.js.map
|