@plaudit/gutenberg-api-extensions 2.95.0 → 2.96.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.
- package/CHANGELOG.md +8 -0
- package/dist/lib/useful-types.d.ts +1 -4
- package/package.json +1 -2
- package/src/blocks/MoveError.ts +0 -7
- package/src/blocks/PathError.ts +0 -18
- package/src/blocks/SNPFlexibleItemsListComponent.tsx +0 -30
- package/src/blocks/SNPGroupComponent.tsx +0 -38
- package/src/blocks/SNPListComponent.tsx +0 -25
- package/src/blocks/SNPTreeContext.tsx +0 -13
- package/src/blocks/basic-custom-block-bindings-support.tsx +0 -248
- package/src/blocks/common-native-property-constructors.tsx +0 -927
- package/src/blocks/conditions.ts +0 -261
- package/src/blocks/csnp-api.ts +0 -221
- package/src/blocks/data-controller/actions.ts +0 -20
- package/src/blocks/data-controller/reducer.ts +0 -146
- package/src/blocks/data-controller/trigger-handlers.ts +0 -150
- package/src/blocks/data-controller/utils.ts +0 -415
- package/src/blocks/data-controller-manager.ts +0 -50
- package/src/blocks/data-controller.ts +0 -165
- package/src/blocks/hooks/built-in-suspendable-option-protocols/select.ts +0 -51
- package/src/blocks/hooks/built-in-suspendable-option-protocols/settings.ts +0 -70
- package/src/blocks/hooks/useSuspendableOptions.ts +0 -122
- package/src/blocks/index.ts +0 -23
- package/src/blocks/layered-styles-api.ts +0 -142
- package/src/blocks/layered-styles-impl.ts +0 -95
- package/src/blocks/layout/LaidOutProperty.tsx +0 -72
- package/src/blocks/layout/LaidOutPropertyRow.tsx +0 -28
- package/src/blocks/layout/NodeContext.tsx +0 -54
- package/src/blocks/layout/PanelRoot.tsx +0 -30
- package/src/blocks/layout/TabsRoot.tsx +0 -56
- package/src/blocks/layout/ToolsPanelContext.tsx +0 -22
- package/src/blocks/problematic-blocks-blocker.ts +0 -24
- package/src/blocks/problematic-variations-blocker.ts +0 -32
- package/src/blocks/shared-exportable-types.ts +0 -6
- package/src/blocks/shared-internal-types.ts +0 -18
- package/src/blocks/simple-block.tsx +0 -74
- package/src/blocks/simple-native-property-api.ts +0 -173
- package/src/blocks/simple-native-property-impl.tsx +0 -335
- package/src/blocks/simple-native-property-internal-shared.ts +0 -19
- package/src/blocks/snp-api.ts +0 -5
- package/src/blocks/snp-data-store.ts +0 -72
- package/src/blocks/utilities.ts +0 -66
- package/src/controls/AsynchronousFormTokenField.tsx +0 -86
- package/src/controls/BaseSortableItemsControl.tsx +0 -84
- package/src/controls/ExtendedFormTokenField.tsx +0 -144
- package/src/controls/ExtendedPostPicker.ts +0 -57
- package/src/controls/ExtendedRadioControl.tsx +0 -107
- package/src/controls/ExtendedTaxonomyPicker.tsx +0 -100
- package/src/controls/ExtendedTermPicker.tsx +0 -61
- package/src/controls/ExtendedTextareaControl.tsx +0 -65
- package/src/controls/ExtendedUserPicker.ts +0 -56
- package/src/controls/FileControl.tsx +0 -48
- package/src/controls/FullSizeToggleControl.tsx +0 -95
- package/src/controls/ImageControl.tsx +0 -143
- package/src/controls/InspectorPanel.tsx +0 -37
- package/src/controls/LazySuggestionsComboboxControl.tsx +0 -64
- package/src/controls/MultiSelectControl.tsx +0 -59
- package/src/controls/PickOne.tsx +0 -88
- package/src/controls/PromisableComponent.tsx +0 -56
- package/src/controls/ProperLinkControl.tsx +0 -98
- package/src/controls/SimpleToggle.tsx +0 -9
- package/src/controls/SortableFlexibleItemsControl.tsx +0 -37
- package/src/controls/SortableItemsControl.tsx +0 -22
- package/src/controls/basicNumericallyIdedItemPicker.tsx +0 -75
- package/src/controls/hooks/useImprovedTokenManager.ts +0 -163
- package/src/controls/hooks/useMultiSingleConversionLayer.ts +0 -17
- package/src/controls/hooks/useNonRenderingCounter.ts +0 -6
- package/src/controls/hooks/useOutputMemoizingFilter.ts +0 -16
- package/src/controls/hooks/useSortableItemsModel.ts +0 -196
- package/src/controls/hooks/useSuggestions.ts +0 -91
- package/src/controls/hooks/useTokenManager.ts +0 -177
- package/src/controls/index.ts +0 -24
- package/src/controls/shared.ts +0 -50
- package/src/controls/types.ts +0 -18
- package/src/editor/insert-sibling-or-child-block-shortcut.tsx +0 -60
- package/src/editor/install-insert-sole-allowed-block-shortcut-support.tsx +0 -51
- package/src/editor/simple-gutenberg-endpoints-api.ts +0 -31
- package/src/editor/simple-gutenberg-endpoints-impl.ts +0 -126
- package/src/index.ts +0 -30
- package/src/lib/compat-types.ts +0 -21
- package/src/lib/gutenberg-api-extensions-state/custom-block-bindings-support-logic.ts +0 -35
- package/src/lib/gutenberg-api-extensions-state/general-logic.ts +0 -41
- package/src/lib/gutenberg-api-extensions-state/layered-block-styles-logic.ts +0 -43
- package/src/lib/gutenberg-api-extensions-state/snp-logic.ts +0 -240
- package/src/lib/gutenberg-api-extensions-state.ts +0 -69
- package/src/lib/helpers.ts +0 -115
- package/src/lib/modified-fast-deep-equals.ts +0 -91
- package/src/lib/plaudit-icons/column-1.tsx +0 -6
- package/src/lib/plaudit-icons/column-2.tsx +0 -6
- package/src/lib/plaudit-icons/column-3.tsx +0 -6
- package/src/lib/plaudit-icons/placement-center.tsx +0 -3
- package/src/lib/plaudit-icons/placement-end.tsx +0 -3
- package/src/lib/plaudit-icons/placement-start.tsx +0 -3
- package/src/lib/plaudit-icons/placement-stretch.tsx +0 -3
- package/src/lib/plaudit-icons/plaudit-icon.tsx +0 -4
- package/src/lib/plaudit-icons/reusable-block-marker.tsx +0 -3
- package/src/lib/plaudit-icons.ts +0 -13
- package/src/lib/sectioned-cache-store.ts +0 -120
- package/src/lib/suspense/promise-handlers.ts +0 -72
- package/src/lib/suspense.tsx +0 -18
- package/src/lib/useful-types.ts +0 -82
- package/src/schemas/README.md +0 -1
- package/src/schemas/plaudit-block-schema.json +0 -818
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import {ActionReducerMapBuilder, CaseReducer, createAction, PayloadAction, PayloadActionCreator} from "@reduxjs/toolkit";
|
|
2
|
-
import type {Draft} from "immer";
|
|
3
|
-
|
|
4
|
-
import type {NodePath, RawPath} from "../simple-native-property-api";
|
|
5
|
-
import {PathError} from "../PathError";
|
|
6
|
-
import type {DCNode, DCStoreState} from "../data-controller";
|
|
7
|
-
import {resolveValueForCondition, testCondition} from "../conditions";
|
|
8
|
-
import {
|
|
9
|
-
applyToTree,
|
|
10
|
-
getDataStore,
|
|
11
|
-
getOptionalValue,
|
|
12
|
-
recordBackupForNode,
|
|
13
|
-
removeValueFromBackingDataStore,
|
|
14
|
-
restoreValueFromPotentialBackup,
|
|
15
|
-
TreeMutatorResult,
|
|
16
|
-
walkToNode,
|
|
17
|
-
walkToNodeInValue,
|
|
18
|
-
writeValueToBackingDataStore,
|
|
19
|
-
writeValueToBackingDataStoreWithCorrections,
|
|
20
|
-
} from "./utils";
|
|
21
|
-
|
|
22
|
-
type BasePayload = {[key in string]: any}&{type?: never, inBatch?: never};
|
|
23
|
-
type BaseMetadata = {[key in string]: any}&{inBatch?: never};
|
|
24
|
-
type BaseReducer<State, P extends BasePayload, M extends BaseMetadata> = CaseReducer<State, PayloadAction<P, string, M&{inBatch?: boolean}>>;
|
|
25
|
-
function makeTriggerHandler<State, P extends BasePayload = {}, M extends BaseMetadata = {}>(reducer: BaseReducer<State, P, M>) {
|
|
26
|
-
type ActionType = PayloadAction<P, string, M&{inBatch?: boolean}>&{meta: M&{inBatch?: boolean}};
|
|
27
|
-
type TriggeringActionCreator = PayloadActionCreator<P, string, (...args: any[]) => {payload: P, meta: M&{inBatch?: boolean}}>;
|
|
28
|
-
const triggeringActionCreators: TriggeringActionCreator[] = [];
|
|
29
|
-
const isInstance = (action: any): action is ActionType => {
|
|
30
|
-
return triggeringActionCreators.some(actionCreator => actionCreator.match(action));
|
|
31
|
-
};
|
|
32
|
-
const shouldTrigger = (action: any): action is ActionType => isInstance(action) && (action.meta.inBatch !== true);
|
|
33
|
-
return {
|
|
34
|
-
addAction: (action: TriggeringActionCreator) => triggeringActionCreators.push(action), isInstance, shouldTrigger,
|
|
35
|
-
attachTo: (builder: ActionReducerMapBuilder<State>) => builder.addMatcher(shouldTrigger, reducer)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const triggerHandlers = {
|
|
40
|
-
validation: makeTriggerHandler<DCStoreState, {path: NodePath}>((state, {payload: {path}}) => {
|
|
41
|
-
validateNode(path, state);
|
|
42
|
-
return state;
|
|
43
|
-
}),
|
|
44
|
-
conditionChecks: makeTriggerHandler<DCStoreState>(performConditionChecks),
|
|
45
|
-
changedByManagedControl: makeTriggerHandler<DCStoreState>(state => {
|
|
46
|
-
state.changedByManagedControl = true;
|
|
47
|
-
}),
|
|
48
|
-
} as const;
|
|
49
|
-
|
|
50
|
-
type CommonTriggers = {changedByManagedControl?: boolean, conditionChecks?: boolean};
|
|
51
|
-
type BaseActionMeta<ADDITIONAL_TRIGGERS extends {[K in Exclude<keyof typeof triggerHandlers, keyof CommonTriggers>]?: boolean}> = {inBatch?: boolean, triggers: CommonTriggers&ADDITIONAL_TRIGGERS};
|
|
52
|
-
|
|
53
|
-
export type GlobalActionMeta = BaseActionMeta<{}>;
|
|
54
|
-
export function createGlobalAction<P extends {[key: Exclude<string, 'type'|'inBatch'>]: any} = {}, T extends string = string>(type: T, triggers: GlobalActionMeta['triggers'] = {}) {
|
|
55
|
-
const res = createAction(type, (payload: P, inBatch: boolean = false) => ({payload, meta: {inBatch, triggers}}));
|
|
56
|
-
for (const [trigger, enabled] of Object.entries(triggers)) {
|
|
57
|
-
if (enabled) {
|
|
58
|
-
triggerHandlers[trigger as keyof typeof triggers].addAction(res);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return res;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type PathedActionMeta = BaseActionMeta<{validation?: boolean}>;
|
|
65
|
-
export function createPathedAction<P extends {[key: Exclude<string, 'type'|'path'|'inBatch'>]: any} = {}, T extends string = string>(type: T, triggers: PathedActionMeta['triggers'] = {}) {
|
|
66
|
-
const res = createAction(type, (payload: P&{path: NodePath}, inBatch: boolean = false) => ({payload, meta: {inBatch, triggers}}));
|
|
67
|
-
for (const [trigger, enabled] of Object.entries(triggers)) {
|
|
68
|
-
if (enabled) {
|
|
69
|
-
triggerHandlers[trigger as keyof typeof triggers].addAction(res);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return res;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function validateNode(path: NodePath, draft: Draft<DCStoreState>) {
|
|
76
|
-
if (validateFoundNode(walkToNode(draft.treeRoot, path), path, draft)) {
|
|
77
|
-
draft.rerenderTrigger = draft.rerenderTrigger + 1;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @return true iff the validation state changed
|
|
83
|
-
*/
|
|
84
|
-
export function validateFoundNode(node: DCNode, path: NodePath, draft: Draft<DCStoreState>) {
|
|
85
|
-
const definition = node.definition;
|
|
86
|
-
if (definition) {
|
|
87
|
-
let validationError: string;
|
|
88
|
-
const value = getOptionalValue(path, draft);
|
|
89
|
-
if (definition.required && !value) {
|
|
90
|
-
validationError = `"${definition.label || definition.name}" is required.`;
|
|
91
|
-
} else {
|
|
92
|
-
validationError = definition.validator?.(value) ?? "";
|
|
93
|
-
}
|
|
94
|
-
if (node.validationError !== validationError) {
|
|
95
|
-
node.validationError = validationError;
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
type PropertyValueResolverBuilder = (path: RawPath) => (propertyPath: RawPath) => any;
|
|
103
|
-
function performConditionChecks(state: Draft<DCStoreState>) {
|
|
104
|
-
const blockClientId = state.blockClientId;
|
|
105
|
-
const propertyValueResolverBuilder: PropertyValueResolverBuilder =
|
|
106
|
-
path => propertyPath => resolveValueForCondition(propertyPath, path, blockClientId,
|
|
107
|
-
property => getDataStore(property, state, false));
|
|
108
|
-
applyToTree(state.treeRoot, (node, path) => {
|
|
109
|
-
const condition = node.condition;
|
|
110
|
-
if (condition === undefined) {
|
|
111
|
-
if (node.rendered === undefined) {
|
|
112
|
-
if (getDataStoreAndCurrentValue(path, state).currentValue === undefined) {
|
|
113
|
-
// Restore from backup if we are transitioning from unrendered -> rendered
|
|
114
|
-
const stateNode = walkToNode(state.treeRoot, path);
|
|
115
|
-
writeValueToBackingDataStoreWithCorrections(path, state, restoreValueFromPotentialBackup(stateNode));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
node.rendered = true;
|
|
119
|
-
return TreeMutatorResult.CONTINUE;
|
|
120
|
-
} else {
|
|
121
|
-
const newRendered = testCondition(condition, propertyValueResolverBuilder(path));
|
|
122
|
-
if (newRendered !== node.rendered) {
|
|
123
|
-
const {dataStore, currentValue} = getDataStoreAndCurrentValue(path, state);
|
|
124
|
-
if (!newRendered) {
|
|
125
|
-
// Create a backup if we are transitioning from rendered -> unrendered
|
|
126
|
-
recordBackupForNode(node, currentValue);
|
|
127
|
-
removeValueFromBackingDataStore(dataStore, path);
|
|
128
|
-
} else if (currentValue === undefined) {
|
|
129
|
-
// Restore from backup if we are transitioning from unrendered -> rendered
|
|
130
|
-
const stateNode = walkToNode(state.treeRoot, path);
|
|
131
|
-
writeValueToBackingDataStore(dataStore, path, restoreValueFromPotentialBackup(stateNode));
|
|
132
|
-
}
|
|
133
|
-
node.rendered = newRendered;
|
|
134
|
-
}
|
|
135
|
-
return node.rendered ? TreeMutatorResult.CONTINUE : TreeMutatorResult.SKIP_DESCENDANTS;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function getDataStoreAndCurrentValue(path: NodePath, state: Draft<DCStoreState>) {
|
|
141
|
-
const dataStore = getDataStore(path[0], state, true);
|
|
142
|
-
try {
|
|
143
|
-
return {dataStore, currentValue: walkToNodeInValue(dataStore.getValue(path[0]), path)} as const;
|
|
144
|
-
} catch (e) {
|
|
145
|
-
if (!(e instanceof PathError)) {
|
|
146
|
-
throw e;
|
|
147
|
-
}
|
|
148
|
-
return {dataStore, currentValue: undefined} as const;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
import {doAction} from "@wordpress/hooks";
|
|
2
|
-
|
|
3
|
-
import {Draft, produce} from "immer";
|
|
4
|
-
|
|
5
|
-
import type {DCNode, DCStoreState} from "../data-controller";
|
|
6
|
-
import {MoveError} from "../MoveError";
|
|
7
|
-
import {PathError, PathErrorType} from "../PathError";
|
|
8
|
-
import type {DataStore, HydratedSimpleNativeProperty, NodePath, RawPath} from "../simple-native-property-api";
|
|
9
|
-
|
|
10
|
-
export function removeFromParentNode(parentNode: any|undefined, path: RawPath) {
|
|
11
|
-
if (typeof parentNode !== 'object' || parentNode === undefined || parentNode === null) {
|
|
12
|
-
throw new PathError("Encountered a non-existent node while expecting one.", path);
|
|
13
|
-
}
|
|
14
|
-
const nodeName = path[path.length - 1];
|
|
15
|
-
if (typeof nodeName === 'string') {
|
|
16
|
-
if (Array.isArray(parentNode)) {
|
|
17
|
-
throw new PathError(
|
|
18
|
-
"Encountered a node with indexed descendants while expecting one with string-keyed descendants.",
|
|
19
|
-
path, path.length - 1);
|
|
20
|
-
}
|
|
21
|
-
delete parentNode[nodeName];
|
|
22
|
-
} else if (nodeName === undefined) {
|
|
23
|
-
throw new PathError("Encountered an undefined name in a path.", path);
|
|
24
|
-
} else {
|
|
25
|
-
if (!Array.isArray(parentNode)) {
|
|
26
|
-
throw new PathError(
|
|
27
|
-
"Encountered a node with string-keyed descendants while expecting one with indexed descendants.",
|
|
28
|
-
path, path.length - 1);
|
|
29
|
-
}
|
|
30
|
-
parentNode.splice(nodeName, 1);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function moveNodeWithinParent(parentNode: any|undefined|null, path: RawPath, move: {from: number, to: number}|{from: string, to: string}) {
|
|
35
|
-
if (typeof parentNode !== 'object' || parentNode === undefined || parentNode === null) {
|
|
36
|
-
throw new PathError("Encountered a non-existent node while expecting one.", path);
|
|
37
|
-
}
|
|
38
|
-
if (isNumericMove(move)) {
|
|
39
|
-
if (!Array.isArray(parentNode)) {
|
|
40
|
-
throw new MoveError("Attempted to move a child of a node with string-keyed descendants by index", path, move);
|
|
41
|
-
}
|
|
42
|
-
if (move.from > move.to) {
|
|
43
|
-
parentNode.splice(move.to, 0, ...parentNode.splice(move.from, 1));
|
|
44
|
-
} else { //We know that
|
|
45
|
-
parentNode.splice(move.to - 1, 0, ...parentNode.splice(move.from, 1));
|
|
46
|
-
}
|
|
47
|
-
} else {
|
|
48
|
-
if (Array.isArray(parentNode)) {
|
|
49
|
-
throw new MoveError("Attempted to move a child of a node with indexed descendants by string-key", path, move);
|
|
50
|
-
}
|
|
51
|
-
if (parentNode[move.to] !== undefined) {
|
|
52
|
-
throw new MoveError("Attempted to move a string-keyed child to the same key as another", path, move);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
export function isNumericMove(move: {from: number, to: number}|{from: string, to: string}): move is {from: number, to: number} {
|
|
57
|
-
return typeof move.from === 'number' && typeof move.to === 'number';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function resolveParentNodeDefinition(root: DCNode, path: RawPath): DCNode['definition'] {
|
|
61
|
-
if (path.length < 2) {
|
|
62
|
-
throw new PathError("Unable to get the parent node of a path with less than two nodes", path, path.length - 1, PathErrorType.INSUFFICIENT_LENGTH);
|
|
63
|
-
}
|
|
64
|
-
return walkToNode(root, path.slice(0, path.length - (typeof path[path.length - 2] === 'number' ? 2 : 1))).definition;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function populateValueBasedOnDefinition(value: any, definition: HydratedSimpleNativeProperty<{uuid: UUID}>|undefined): any {
|
|
68
|
-
const typedChildren = Array.isArray(definition?.children) ? definition.children : definition?.children?.[value?.type];
|
|
69
|
-
if (!typedChildren?.length) {
|
|
70
|
-
return value ?? buildDefaultValueFromDefinition(definition);
|
|
71
|
-
} else if (value === null || value === undefined) {
|
|
72
|
-
return buildDefaultValueFromDefinition(definition);
|
|
73
|
-
} else if (typeof value !== 'object') {
|
|
74
|
-
return value;
|
|
75
|
-
} else if (Array.isArray(value)) {
|
|
76
|
-
return value;
|
|
77
|
-
}
|
|
78
|
-
return {...value, ...Object.fromEntries(typedChildren.map(def => [def.name, populateValueBasedOnDefinition(value[def.name], def)]))};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function walkToNode(root: DCNode, path: RawPath): DCNode {
|
|
82
|
-
let current = root;
|
|
83
|
-
for (let i = 0; i < path.length; i++) {
|
|
84
|
-
current = descendIntoDCNodeChild(current, path, i);
|
|
85
|
-
}
|
|
86
|
-
return current;
|
|
87
|
-
}
|
|
88
|
-
export function descendIntoDCNodeChild(current: DCNode|undefined, path: RawPath, i: number): DCNode {
|
|
89
|
-
if (current?.children === undefined) {
|
|
90
|
-
throw new PathError("Encountered a leaf node while expecting one with descendants.", path, i, PathErrorType.UNEXPECTED_LEAF);
|
|
91
|
-
}
|
|
92
|
-
const pathNode = path[i];
|
|
93
|
-
if (typeof pathNode === 'string') {
|
|
94
|
-
if (Array.isArray(current.children)) {
|
|
95
|
-
throw new PathError("Encountered a node with indexed descendants while expecting one with string-keyed descendants.", path, i);
|
|
96
|
-
}
|
|
97
|
-
current = current.children[pathNode];
|
|
98
|
-
} else if (pathNode === undefined) {
|
|
99
|
-
throw new PathError("Encountered an undefined name in a path.", path, i);
|
|
100
|
-
} else {
|
|
101
|
-
if (!Array.isArray(current.children)) {
|
|
102
|
-
throw new PathError("Encountered a node with string-keyed descendants while expecting one with indexed descendants.", path, i);
|
|
103
|
-
}
|
|
104
|
-
current = current.children[pathNode];
|
|
105
|
-
}
|
|
106
|
-
if (current === undefined) {
|
|
107
|
-
throw new PathError("Encountered a non-existent node while expecting one.", path, i, PathErrorType.UNEXPECTED_LEAF);
|
|
108
|
-
}
|
|
109
|
-
return current;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function getDataStore(property: string, state: Draft<DCStoreState>, throwOnError: true): DataStore;
|
|
113
|
-
export function getDataStore(property: string, state: Draft<DCStoreState>, throwOnError?: undefined|boolean|false): DataStore|undefined;
|
|
114
|
-
export function getDataStore(property: string, state: Draft<DCStoreState>, throwOnError = true): DataStore|undefined {
|
|
115
|
-
let dataStore = state.dataStores.byProperty[property];
|
|
116
|
-
if (dataStore) {
|
|
117
|
-
return dataStore;
|
|
118
|
-
}
|
|
119
|
-
dataStore = state.dataStores.list.find(dataStore => dataStore.handlesProperty(property));
|
|
120
|
-
if (dataStore) {
|
|
121
|
-
console.warn(`Unable to locate the DataStore for ${property} via the byProperty map, but did find a DataStore that handles that property. This should not be possible.`);
|
|
122
|
-
} else if (throwOnError) {
|
|
123
|
-
throw new Error(`Unable to resolve the dataStore for ${property} on block ${state.blockClientId}`);
|
|
124
|
-
}
|
|
125
|
-
return dataStore;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function getOptionalValue(path: NodePath, state: Draft<DCStoreState>) {
|
|
129
|
-
try {
|
|
130
|
-
return walkToNodeInValue(getDataStore(path[0], state, true).getValue(path[0]), path);
|
|
131
|
-
} catch (e) {
|
|
132
|
-
if (e instanceof PathError) {
|
|
133
|
-
return undefined;
|
|
134
|
-
}
|
|
135
|
-
throw e;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function removeValueFromBackingDataStore(dataStore: DataStore, path: NodePath) {
|
|
140
|
-
const dsValue = dataStore.getValue(path[0]);
|
|
141
|
-
if (path.length === 1) {
|
|
142
|
-
dataStore.setValue(path[0], undefined);
|
|
143
|
-
} else {
|
|
144
|
-
dataStore.setValue(path[0], produce(dsValue, (dsValueDraft: any) => {
|
|
145
|
-
const nodeParent = walkToNodeInValue(dsValueDraft, path.slice(0, path.length - 1));
|
|
146
|
-
const nodeName = path[path.length - 1];
|
|
147
|
-
if (typeof nodeName === 'string') {
|
|
148
|
-
delete nodeParent[nodeName];
|
|
149
|
-
} else if (nodeName === undefined) {
|
|
150
|
-
throw new PathError("Encountered an undefined name in a path.", path);
|
|
151
|
-
} else {
|
|
152
|
-
(nodeParent as any[]).splice(nodeName, 1);
|
|
153
|
-
}
|
|
154
|
-
}));
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
export function writeValueToBackingDataStoreWithCorrections(path: NodePath, state: DCStoreState, value: any) {
|
|
158
|
-
if (typeof value === 'number' && Number.isNaN(value)) {
|
|
159
|
-
value = undefined; // This ensures that NaN's don't get stored
|
|
160
|
-
}
|
|
161
|
-
const rootDCNode = state.treeRoot.children[path[0]];
|
|
162
|
-
if (rootDCNode === undefined) {
|
|
163
|
-
throw new PathError("Encountered a path pointing to a non-existent root node.", path, 0);
|
|
164
|
-
}
|
|
165
|
-
const dataStore = getDataStore(path[0], state, true);
|
|
166
|
-
|
|
167
|
-
// This path both handles scalar values and ensures that the root "value" of Groups and Lists are initialized before writing the actual attribute
|
|
168
|
-
// To avoid accidentally overwriting group data when lazily adding items (i.e., via a ToolsPanel instance), we skip this code path if the group's root value has already been initialized
|
|
169
|
-
if (path.length === 1 || (value === undefined && dataStore.getValue(path[0]) === undefined)) {
|
|
170
|
-
const writtenValue = value ?? buildDefaultValueFromDefinition(rootDCNode.definition, path.length === 1);
|
|
171
|
-
dataStore.setValue(path[0], writtenValue);
|
|
172
|
-
if (rootDCNode.definition) {
|
|
173
|
-
doAction('plaudit.gutenbergApiExtensions.simpleNativeProperties.afterWrite', writtenValue, rootDCNode.definition, dataStore);
|
|
174
|
-
}
|
|
175
|
-
if (path.length === 1) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
let closestUsableDefinition = rootDCNode.definition;
|
|
181
|
-
let lastWrittenValue: unknown;
|
|
182
|
-
let didWriting = true;
|
|
183
|
-
dataStore.setValue(path[0], produce(dataStore.getValue(path[0]), (dsValueDraft: any) => {
|
|
184
|
-
let currentDSValue = dsValueDraft;
|
|
185
|
-
let currentDCNode = rootDCNode;
|
|
186
|
-
let parentCUD: DCNode['definition'] = undefined;
|
|
187
|
-
for (let i = 1; i < path.length - 1; i++) {
|
|
188
|
-
const nodeName = path[i];
|
|
189
|
-
currentDCNode = descendIntoDCNodeChild(currentDCNode, path, i);
|
|
190
|
-
if (typeof nodeName === 'string') {
|
|
191
|
-
//We only update the definition being used when we descend into a new node proper
|
|
192
|
-
parentCUD = closestUsableDefinition;
|
|
193
|
-
closestUsableDefinition = currentDCNode.definition;
|
|
194
|
-
} else if (nodeName === undefined) {
|
|
195
|
-
throw new PathError("Encountered an undefined name in a path.", path, i);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
let nextDSValue = descendIntoDSValueNodeChild(currentDSValue, path, i);
|
|
199
|
-
if (nextDSValue === undefined) {
|
|
200
|
-
lastWrittenValue = currentDSValue[nodeName] = buildDefaultValueFromDefinition(closestUsableDefinition);
|
|
201
|
-
currentDSValue = descendIntoDSValueNodeChild(currentDSValue, path, i);
|
|
202
|
-
if (currentDSValue === undefined) {
|
|
203
|
-
if (parentCUD?.branching) {
|
|
204
|
-
didWriting = false;
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
throw new PathError("Encountered a leaf node while expecting one with descendants.", path, i, PathErrorType.UNEXPECTED_LEAF);
|
|
208
|
-
}
|
|
209
|
-
} else {
|
|
210
|
-
currentDSValue = nextDSValue;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
const nodeName = path[path.length - 1];
|
|
214
|
-
if (nodeName === undefined) {
|
|
215
|
-
throw new PathError("Encountered an undefined name in a path.", path);
|
|
216
|
-
}
|
|
217
|
-
lastWrittenValue = currentDSValue[nodeName] = value;
|
|
218
|
-
}));
|
|
219
|
-
if (didWriting && closestUsableDefinition) {
|
|
220
|
-
doAction('plaudit.gutenbergApiExtensions.simpleNativeProperties.afterWrite', lastWrittenValue, closestUsableDefinition, dataStore);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
export function writeValueToBackingDataStore(dataStore: DataStore, path: NodePath, value: any) {
|
|
224
|
-
if (typeof value === 'number' && Number.isNaN(value)) {
|
|
225
|
-
value = undefined; // This ensures that NaN's don't get stored
|
|
226
|
-
}
|
|
227
|
-
const dsValue = dataStore.getValue(path[0]);
|
|
228
|
-
if (path.length === 1) {
|
|
229
|
-
dataStore.setValue(path[0], value);
|
|
230
|
-
} else {
|
|
231
|
-
dataStore.setValue(path[0], produce(dsValue, (dsValueDraft: any) => {
|
|
232
|
-
const nodeParent = walkToNodeInValue(dsValueDraft, path.slice(0, path.length - 1));
|
|
233
|
-
const nodeName = path[path.length - 1];
|
|
234
|
-
if (nodeName === undefined) {
|
|
235
|
-
throw new PathError("Encountered an undefined name in a path.", path);
|
|
236
|
-
}
|
|
237
|
-
nodeParent[nodeName] = value;
|
|
238
|
-
}));
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* @param dsValue a value from a {@link DataStore}
|
|
244
|
-
* @param path this is expected to be the entire path (including the node that was used to get {@link dsValue}
|
|
245
|
-
*/
|
|
246
|
-
export function walkToNodeInValue(dsValue: any, path: RawPath) {
|
|
247
|
-
let current = dsValue;
|
|
248
|
-
for (let i = 1; i < path.length; i++) {
|
|
249
|
-
if (current === undefined) {
|
|
250
|
-
throw new PathError("Encountered a leaf node while expecting one with descendants.", path, i, PathErrorType.UNEXPECTED_LEAF);
|
|
251
|
-
}
|
|
252
|
-
current = descendIntoDSValueNodeChild(current, path, i);
|
|
253
|
-
}
|
|
254
|
-
return current;
|
|
255
|
-
}
|
|
256
|
-
function descendIntoDSValueNodeChild(current: any, path: RawPath, i: number) {
|
|
257
|
-
const pathNode = path[i];
|
|
258
|
-
if (typeof pathNode === 'string') {
|
|
259
|
-
if (Array.isArray(current)) {
|
|
260
|
-
throw new PathError("Encountered a node with indexed descendants while expecting one with string-keyed descendants.", path, i);
|
|
261
|
-
}
|
|
262
|
-
current = current[pathNode];
|
|
263
|
-
} else if (pathNode === undefined) {
|
|
264
|
-
throw new PathError("Encountered an undefined name in a path.", path, i);
|
|
265
|
-
} else {
|
|
266
|
-
if (!Array.isArray(current)) {
|
|
267
|
-
throw new PathError("Encountered a node with string-keyed descendants while expecting one with indexed descendants.", path, i);
|
|
268
|
-
}
|
|
269
|
-
current = current[pathNode];
|
|
270
|
-
}
|
|
271
|
-
return current;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export enum TreeMutatorResult {
|
|
275
|
-
STOP, CONTINUE, SKIP_DESCENDANTS
|
|
276
|
-
}
|
|
277
|
-
export function applyToTree(tree: DCNode, mutator: (node: DCNode, path: NodePath) => TreeMutatorResult, path: RawPath = []) {
|
|
278
|
-
if (tree.children === undefined) {
|
|
279
|
-
return TreeMutatorResult.CONTINUE;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const steps: Array<[NodePath, DCNode]> = Array.isArray(tree.children)
|
|
283
|
-
? tree.children.map((node, index) => [path.toSpliced(path.length, 0, index) as NodePath, node])
|
|
284
|
-
: Object.entries(tree.children).map(([name, node]) => [path.toSpliced(path.length, 0, name) as NodePath, node]);
|
|
285
|
-
|
|
286
|
-
for (const [nodePath, node] of steps) {
|
|
287
|
-
const mutatorRes = mutator(node, nodePath);
|
|
288
|
-
if (!mutatorRes) {
|
|
289
|
-
return TreeMutatorResult.STOP;
|
|
290
|
-
} else if (mutatorRes === TreeMutatorResult.CONTINUE) {
|
|
291
|
-
if (!applyToTree(node, mutator, nodePath)) {
|
|
292
|
-
return TreeMutatorResult.STOP;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return TreeMutatorResult.CONTINUE;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export type UUID = ReturnType<typeof crypto.randomUUID>;
|
|
300
|
-
const cloneableDefaultValuesForNodes: Record<UUID, HydratedSimpleNativeProperty['default']> = {};
|
|
301
|
-
export function recordCloneableDefaultValueForNode<T extends HydratedSimpleNativeProperty<{uuid?: UUID}>>(definition: T, overwriteExisting = false) {
|
|
302
|
-
if (!definition.uuid) {
|
|
303
|
-
definition.uuid = crypto.randomUUID();
|
|
304
|
-
}
|
|
305
|
-
if (overwriteExisting || !(definition.uuid in cloneableDefaultValuesForNodes)) {
|
|
306
|
-
try {
|
|
307
|
-
cloneableDefaultValuesForNodes[definition.uuid] = (definition.default === undefined ? undefined : structuredClone(definition.default));
|
|
308
|
-
} catch (e) {
|
|
309
|
-
console.error(`Encountered an unexpected un-cloneable default value: ${e}`);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return definition as HydratedSimpleNativeProperty<{uuid: UUID}>;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const recordedBackupValues: Record<UUID, any> = {};
|
|
317
|
-
export function recordBackupForNode(node: DCNode, value: any) {
|
|
318
|
-
if (!node.backup) {
|
|
319
|
-
node.backup = crypto.randomUUID();
|
|
320
|
-
}
|
|
321
|
-
recordedBackupValues[node.backup] = value;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export function restoreValueFromPotentialBackup(node: DCNode) {
|
|
325
|
-
if (node.backup && node.backup in recordedBackupValues) {
|
|
326
|
-
const res = recordedBackupValues[node.backup];
|
|
327
|
-
delete recordedBackupValues[node.backup];
|
|
328
|
-
return res ?? buildDefaultValueFromDefinition(node.definition);
|
|
329
|
-
}
|
|
330
|
-
return buildDefaultValueFromDefinition(node.definition)
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
export function removeBackupForNode(node: DCNode) {
|
|
334
|
-
if (node.backup && node.backup in recordedBackupValues) {
|
|
335
|
-
delete recordedBackupValues[node.backup];
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
export function buildDefaultValueFromDefinition(definition: (HydratedSimpleNativeProperty<{uuid: UUID}>)|undefined, requireNonNullValueOnObject = true): any {
|
|
340
|
-
if (definition === undefined) {
|
|
341
|
-
return undefined;
|
|
342
|
-
} else if (definition.uuid in cloneableDefaultValuesForNodes) {
|
|
343
|
-
return structuredClone(cloneableDefaultValuesForNodes[definition.uuid]);
|
|
344
|
-
} else if (definition.default !== undefined) {
|
|
345
|
-
return structuredClone(definition.default);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const typedChildren = Array.isArray(definition.children) ? definition.children : undefined;
|
|
349
|
-
if (!typedChildren?.length) {
|
|
350
|
-
return structuredClone(definition.uuid in cloneableDefaultValuesForNodes ? cloneableDefaultValuesForNodes[definition.uuid] : definition.default);
|
|
351
|
-
} else if (definition.type === 'array') {
|
|
352
|
-
return [];
|
|
353
|
-
} else if (definition.type !== 'object') {
|
|
354
|
-
return undefined;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const res = Object.fromEntries(typedChildren.map(def => [def.name, buildDefaultValueFromDefinition(def)]));
|
|
358
|
-
if (requireNonNullValueOnObject) {
|
|
359
|
-
for (const value of Object.values(res)) {
|
|
360
|
-
if (value !== undefined) {
|
|
361
|
-
return res;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return undefined;
|
|
365
|
-
}
|
|
366
|
-
return res;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
export function buildNodeFromDataAndDefinition(data: any, definition: HydratedSimpleNativeProperty<{uuid?: UUID}>): DCNode {
|
|
370
|
-
const children = definition.children;
|
|
371
|
-
if (children) {
|
|
372
|
-
if (data === undefined || data === null) {
|
|
373
|
-
if (definition.type === 'array') {
|
|
374
|
-
return {condition: definition.condition, definition: recordCloneableDefaultValueForNode(definition), validationError: "", children: []};
|
|
375
|
-
} else if (definition.type === 'object') {
|
|
376
|
-
if (Array.isArray(children)) {
|
|
377
|
-
return {
|
|
378
|
-
condition: definition.condition, definition: recordCloneableDefaultValueForNode(definition), validationError: "",
|
|
379
|
-
children: Object.fromEntries(children.map(def => [def.name, buildNodeFromDataAndDefinition(undefined, def)]))
|
|
380
|
-
};
|
|
381
|
-
} else {
|
|
382
|
-
//TODO: We might need to throw an error here
|
|
383
|
-
return {condition: definition.condition, definition: recordCloneableDefaultValueForNode(definition), validationError: "", children: {}};
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
} else if (typeof data === 'object') {
|
|
387
|
-
if (Array.isArray(data)) {
|
|
388
|
-
return {
|
|
389
|
-
condition: definition.condition, definition: recordCloneableDefaultValueForNode(definition), validationError: "", children: data.map(item => {
|
|
390
|
-
const typedChildren = Array.isArray(children) ? children : children[item.type];
|
|
391
|
-
if (typedChildren === undefined) {
|
|
392
|
-
//TODO: We might need to throw an error here
|
|
393
|
-
return {children: [], validationError: ""};
|
|
394
|
-
}
|
|
395
|
-
return {
|
|
396
|
-
children: Object.fromEntries(typedChildren.map(def => [def.name, buildNodeFromDataAndDefinition(item[def.name], def)])),
|
|
397
|
-
validationError: ""
|
|
398
|
-
};
|
|
399
|
-
})
|
|
400
|
-
};
|
|
401
|
-
} else {
|
|
402
|
-
const typedChildren = Array.isArray(children) ? children : children[data.type];
|
|
403
|
-
if (typedChildren === undefined) {
|
|
404
|
-
//TODO: We might need to throw an error here
|
|
405
|
-
return {condition: definition.condition, definition: recordCloneableDefaultValueForNode(definition), validationError: "", children: []};
|
|
406
|
-
}
|
|
407
|
-
return {
|
|
408
|
-
condition: definition.condition, definition: recordCloneableDefaultValueForNode(definition), validationError: "",
|
|
409
|
-
children: Object.fromEntries(typedChildren.map(def => [def.name, buildNodeFromDataAndDefinition(data[def.name], def)]))
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return {condition: definition.condition, definition: recordCloneableDefaultValueForNode(definition), validationError: ""};
|
|
415
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type {DataController} from "./simple-native-property-api";
|
|
2
|
-
|
|
3
|
-
export type DataControllerManager = {
|
|
4
|
-
addDataController(blockClientId: string, dataController: DataController): void,
|
|
5
|
-
getDataController(blockClientId: string): DataController|undefined,
|
|
6
|
-
removeDataController(blockClientId: string): void,
|
|
7
|
-
notifyOfValidationErrorStatusChange(blockClientId: string, hasValidationError: boolean): void
|
|
8
|
-
}
|
|
9
|
-
type DataControllerManagerInternals = DataControllerManager&{
|
|
10
|
-
dataControllers: Record<string, { dataController: DataController, hasValidationError: boolean }>,
|
|
11
|
-
styleSheetQueue?: Promise<CSSStyleSheet>
|
|
12
|
-
writeStyleInformation(): void
|
|
13
|
-
}
|
|
14
|
-
export function createDataControllerManager(): DataControllerManager {
|
|
15
|
-
// The add and remove methods for dataControllers don't sync up the style information. This is done deliberately to cut down on the number of CSS updates that we create.
|
|
16
|
-
return {
|
|
17
|
-
dataControllers: {},
|
|
18
|
-
addDataController(blockClientId, dataController) {
|
|
19
|
-
this.dataControllers[blockClientId] = {dataController, hasValidationError: false};
|
|
20
|
-
},
|
|
21
|
-
getDataController(blockClientId) {
|
|
22
|
-
return this.dataControllers[blockClientId]?.dataController;
|
|
23
|
-
},
|
|
24
|
-
removeDataController(blockClientId) {
|
|
25
|
-
delete this.dataControllers[blockClientId];
|
|
26
|
-
},
|
|
27
|
-
notifyOfValidationErrorStatusChange(blockClientId, hasValidationError) {
|
|
28
|
-
if (this.dataControllers[blockClientId] && this.dataControllers[blockClientId].hasValidationError !== hasValidationError) {
|
|
29
|
-
this.dataControllers[blockClientId].hasValidationError = hasValidationError;
|
|
30
|
-
this.writeStyleInformation();
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
async writeStyleInformation() {
|
|
34
|
-
if (this.styleSheetQueue === undefined) {
|
|
35
|
-
this.styleSheetQueue = new Promise(resolve => {
|
|
36
|
-
const styleSheet = new CSSStyleSheet();
|
|
37
|
-
document.adoptedStyleSheets.push(styleSheet);
|
|
38
|
-
resolve(styleSheet);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
this.styleSheetQueue = this.styleSheetQueue.then(styleSheet => {
|
|
42
|
-
const blockMarkers = Object.entries(this.dataControllers)
|
|
43
|
-
.filter(([_, {hasValidationError}]) => hasValidationError).map(([blockClientId]) => blockClientId)
|
|
44
|
-
.map(blockClientId => `[data-block="${blockClientId}"]`)
|
|
45
|
-
.join(", ");
|
|
46
|
-
return styleSheet.replace(`.block-editor-list-view-tree tbody tr:is(${blockMarkers}) {background: var(--plaudit-gae-validation-error-bg) !important;}`);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
} satisfies DataControllerManagerInternals as DataControllerManagerInternals;
|
|
50
|
-
}
|