@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.
Files changed (103) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/lib/useful-types.d.ts +1 -4
  3. package/package.json +1 -2
  4. package/src/blocks/MoveError.ts +0 -7
  5. package/src/blocks/PathError.ts +0 -18
  6. package/src/blocks/SNPFlexibleItemsListComponent.tsx +0 -30
  7. package/src/blocks/SNPGroupComponent.tsx +0 -38
  8. package/src/blocks/SNPListComponent.tsx +0 -25
  9. package/src/blocks/SNPTreeContext.tsx +0 -13
  10. package/src/blocks/basic-custom-block-bindings-support.tsx +0 -248
  11. package/src/blocks/common-native-property-constructors.tsx +0 -927
  12. package/src/blocks/conditions.ts +0 -261
  13. package/src/blocks/csnp-api.ts +0 -221
  14. package/src/blocks/data-controller/actions.ts +0 -20
  15. package/src/blocks/data-controller/reducer.ts +0 -146
  16. package/src/blocks/data-controller/trigger-handlers.ts +0 -150
  17. package/src/blocks/data-controller/utils.ts +0 -415
  18. package/src/blocks/data-controller-manager.ts +0 -50
  19. package/src/blocks/data-controller.ts +0 -165
  20. package/src/blocks/hooks/built-in-suspendable-option-protocols/select.ts +0 -51
  21. package/src/blocks/hooks/built-in-suspendable-option-protocols/settings.ts +0 -70
  22. package/src/blocks/hooks/useSuspendableOptions.ts +0 -122
  23. package/src/blocks/index.ts +0 -23
  24. package/src/blocks/layered-styles-api.ts +0 -142
  25. package/src/blocks/layered-styles-impl.ts +0 -95
  26. package/src/blocks/layout/LaidOutProperty.tsx +0 -72
  27. package/src/blocks/layout/LaidOutPropertyRow.tsx +0 -28
  28. package/src/blocks/layout/NodeContext.tsx +0 -54
  29. package/src/blocks/layout/PanelRoot.tsx +0 -30
  30. package/src/blocks/layout/TabsRoot.tsx +0 -56
  31. package/src/blocks/layout/ToolsPanelContext.tsx +0 -22
  32. package/src/blocks/problematic-blocks-blocker.ts +0 -24
  33. package/src/blocks/problematic-variations-blocker.ts +0 -32
  34. package/src/blocks/shared-exportable-types.ts +0 -6
  35. package/src/blocks/shared-internal-types.ts +0 -18
  36. package/src/blocks/simple-block.tsx +0 -74
  37. package/src/blocks/simple-native-property-api.ts +0 -173
  38. package/src/blocks/simple-native-property-impl.tsx +0 -335
  39. package/src/blocks/simple-native-property-internal-shared.ts +0 -19
  40. package/src/blocks/snp-api.ts +0 -5
  41. package/src/blocks/snp-data-store.ts +0 -72
  42. package/src/blocks/utilities.ts +0 -66
  43. package/src/controls/AsynchronousFormTokenField.tsx +0 -86
  44. package/src/controls/BaseSortableItemsControl.tsx +0 -84
  45. package/src/controls/ExtendedFormTokenField.tsx +0 -144
  46. package/src/controls/ExtendedPostPicker.ts +0 -57
  47. package/src/controls/ExtendedRadioControl.tsx +0 -107
  48. package/src/controls/ExtendedTaxonomyPicker.tsx +0 -100
  49. package/src/controls/ExtendedTermPicker.tsx +0 -61
  50. package/src/controls/ExtendedTextareaControl.tsx +0 -65
  51. package/src/controls/ExtendedUserPicker.ts +0 -56
  52. package/src/controls/FileControl.tsx +0 -48
  53. package/src/controls/FullSizeToggleControl.tsx +0 -95
  54. package/src/controls/ImageControl.tsx +0 -143
  55. package/src/controls/InspectorPanel.tsx +0 -37
  56. package/src/controls/LazySuggestionsComboboxControl.tsx +0 -64
  57. package/src/controls/MultiSelectControl.tsx +0 -59
  58. package/src/controls/PickOne.tsx +0 -88
  59. package/src/controls/PromisableComponent.tsx +0 -56
  60. package/src/controls/ProperLinkControl.tsx +0 -98
  61. package/src/controls/SimpleToggle.tsx +0 -9
  62. package/src/controls/SortableFlexibleItemsControl.tsx +0 -37
  63. package/src/controls/SortableItemsControl.tsx +0 -22
  64. package/src/controls/basicNumericallyIdedItemPicker.tsx +0 -75
  65. package/src/controls/hooks/useImprovedTokenManager.ts +0 -163
  66. package/src/controls/hooks/useMultiSingleConversionLayer.ts +0 -17
  67. package/src/controls/hooks/useNonRenderingCounter.ts +0 -6
  68. package/src/controls/hooks/useOutputMemoizingFilter.ts +0 -16
  69. package/src/controls/hooks/useSortableItemsModel.ts +0 -196
  70. package/src/controls/hooks/useSuggestions.ts +0 -91
  71. package/src/controls/hooks/useTokenManager.ts +0 -177
  72. package/src/controls/index.ts +0 -24
  73. package/src/controls/shared.ts +0 -50
  74. package/src/controls/types.ts +0 -18
  75. package/src/editor/insert-sibling-or-child-block-shortcut.tsx +0 -60
  76. package/src/editor/install-insert-sole-allowed-block-shortcut-support.tsx +0 -51
  77. package/src/editor/simple-gutenberg-endpoints-api.ts +0 -31
  78. package/src/editor/simple-gutenberg-endpoints-impl.ts +0 -126
  79. package/src/index.ts +0 -30
  80. package/src/lib/compat-types.ts +0 -21
  81. package/src/lib/gutenberg-api-extensions-state/custom-block-bindings-support-logic.ts +0 -35
  82. package/src/lib/gutenberg-api-extensions-state/general-logic.ts +0 -41
  83. package/src/lib/gutenberg-api-extensions-state/layered-block-styles-logic.ts +0 -43
  84. package/src/lib/gutenberg-api-extensions-state/snp-logic.ts +0 -240
  85. package/src/lib/gutenberg-api-extensions-state.ts +0 -69
  86. package/src/lib/helpers.ts +0 -115
  87. package/src/lib/modified-fast-deep-equals.ts +0 -91
  88. package/src/lib/plaudit-icons/column-1.tsx +0 -6
  89. package/src/lib/plaudit-icons/column-2.tsx +0 -6
  90. package/src/lib/plaudit-icons/column-3.tsx +0 -6
  91. package/src/lib/plaudit-icons/placement-center.tsx +0 -3
  92. package/src/lib/plaudit-icons/placement-end.tsx +0 -3
  93. package/src/lib/plaudit-icons/placement-start.tsx +0 -3
  94. package/src/lib/plaudit-icons/placement-stretch.tsx +0 -3
  95. package/src/lib/plaudit-icons/plaudit-icon.tsx +0 -4
  96. package/src/lib/plaudit-icons/reusable-block-marker.tsx +0 -3
  97. package/src/lib/plaudit-icons.ts +0 -13
  98. package/src/lib/sectioned-cache-store.ts +0 -120
  99. package/src/lib/suspense/promise-handlers.ts +0 -72
  100. package/src/lib/suspense.tsx +0 -18
  101. package/src/lib/useful-types.ts +0 -82
  102. package/src/schemas/README.md +0 -1
  103. package/src/schemas/plaudit-block-schema.json +0 -818
@@ -1,335 +0,0 @@
1
- import {store as blockEditorStore} from "@wordpress/block-editor";
2
- import {createHigherOrderComponent} from "@wordpress/compose";
3
- import {dispatch, select} from "@wordpress/data";
4
- import {useEffect, useMemo} from "@wordpress/element";
5
- import {addAction, addFilter} from "@wordpress/hooks";
6
-
7
- import {produce} from "immer";
8
-
9
- import {useDataController, walkToNodeInValue} from "./data-controller";
10
- import {DataControllerManager, createDataControllerManager} from "./data-controller-manager";
11
- import {extractStyleLayerValueFromAttributes, LayeredStylesDataStore} from "./layered-styles-impl";
12
- import {TabsRoot} from "./layout/TabsRoot";
13
- import {PanelRoot} from "./layout/PanelRoot";
14
- import {store as gutenbergApiExtensionsStore} from "../lib/gutenberg-api-extensions-state";
15
- import {ActualBEPAttrs, ActualBlockEditProps, ActualBlockListBlockProps, isBlockJsonNativePropsConfig, RegisterBlockAttrs} from "../lib/useful-types";
16
- import {PathError} from "./PathError";
17
- import type {DataController, DataStore, HydratedSimpleNativeProperty, InspectorPanelGroup} from "./simple-native-property-api";
18
- import type {HydratedSimpleNativePanel, PerBlockExtraPropTransforms, PreppedSimpleNativePanelsAndTabs} from "./simple-native-property-internal-shared";
19
- import {SNPDataStore} from "./snp-data-store";
20
- import {SNPTreeContext} from "./SNPTreeContext";
21
-
22
- class AttributeDataStore extends SNPDataStore {
23
- public getAttribute(name: string): any {
24
- return this.getRawBlockAttributes()[name];
25
- }
26
-
27
- public setAttribute(name: string, value: any) {
28
- this.setRawBlockAttributes(name === 'className' ? {className: value?.toString()} : {[name]: value});
29
- }
30
- }
31
-
32
- type ActualBlockSaveProps = {[key: Exclude<string, 'className'|'style'>]: unknown|undefined, className?: string|undefined, style?: Record<string, unknown>};
33
-
34
- export function installSimpleNativePropertiesSupport() {
35
- if (installSimpleNativePropertiesSupport.called) {
36
- return;
37
- }
38
- installSimpleNativePropertiesSupport.called = true;
39
-
40
- const dataControllerManager = createDataControllerManager();
41
-
42
- const registeredBlocks = new Map<string, boolean>();
43
- addFilter('blocks.registerBlockType', 'plaudit/gutenberg-api-extensions/simple-native-properties', (atts: RegisterBlockAttrs<Record<string, any>>) => {
44
- let isPlauditNative: boolean;
45
- if (registeredBlocks.has(atts.name)) {
46
- isPlauditNative = registeredBlocks.get(atts.name)!;
47
- } else {
48
- dispatch(gutenbergApiExtensionsStore).recordBaselineBlockAttrs(atts);
49
- if ((isPlauditNative = isBlockJsonNativePropsConfig(atts.plaudit)) && atts.plaudit.properties !== undefined
50
- && (Array.isArray(atts.plaudit.properties) ? atts.plaudit.properties.length : atts.plaudit.properties)
51
- ) {
52
- dispatch(gutenbergApiExtensionsStore).addProperties(atts.name, atts.plaudit.properties);
53
- }
54
- registeredBlocks.set(atts.name, isPlauditNative);
55
- }
56
- //TODO: Figure out some way to re-register a block when its attributes are changed
57
- const blockSimpleNativePanelsAndTabs = select(gutenbergApiExtensionsStore).preppedProperties(atts.name);
58
- if (blockSimpleNativePanelsAndTabs) {
59
- const extraPropTransforms = select(gutenbergApiExtensionsStore).extraPropTransforms(atts.name) ?? {};
60
- const attsAttributes = {...atts.attributes};
61
- if (isPlauditNative && Object.values(extraPropTransforms).some(cfg => cfg.styleProperty)) {
62
- attsAttributes['__plaudit/computed-style-attr'] = {type: "object"};
63
- }
64
- const injectableProperties: Record<string, any> = {};
65
- for (const panel of blockSimpleNativePanelsAndTabs.panels) {
66
- attachSNPsInPanel(panel[1].properties, injectableProperties);
67
- }
68
- for (const tabGroup of Object.values(blockSimpleNativePanelsAndTabs.tabs)) {
69
- for (const tab of Object.values(tabGroup[1])) {
70
- for (const item of tab.items) {
71
- if ('properties' in item) {
72
- attachSNPsInPanel(item.properties, injectableProperties);
73
- } else if (Array.isArray(item)) {
74
- attachSNPsInPanel(item, injectableProperties);
75
- } else {
76
- attachSNP(item, injectableProperties);
77
- }
78
- }
79
- }
80
- }
81
- for (const [name, config] of Object.entries(injectableProperties)) {
82
- attsAttributes[name] = config;
83
- }
84
- return {...atts, attributes: attsAttributes};
85
- }
86
- return atts;
87
- });
88
-
89
- addFilter('editor.BlockEdit', 'plaudit/gutenberg-api-extensions/simple-native-properties',
90
- createHigherOrderComponent(BlockEdit => function SNPBlockEdit(blockEditProps: ActualBlockEditProps) {
91
- const blockSimpleNativePanelsAndTabs = select(gutenbergApiExtensionsStore).preppedProperties(blockEditProps.name);
92
- if (!blockSimpleNativePanelsAndTabs) {
93
- return <BlockEdit {...blockEditProps} />;
94
- }
95
- if (select(blockEditorStore).getBlockParentsByBlockName(blockEditProps.clientId, "core/block")?.length) {
96
- // If we have a core/block ancestor, then we can't be edited, and attempting to self-edit will cause a crash
97
- return <BlockEdit {...blockEditProps} />;
98
- }
99
- return <DataControllerInitializationIsolator bep={blockEditProps} dcm={dataControllerManager} BlockEdit={BlockEdit} snpPanelsAndTabs={blockSimpleNativePanelsAndTabs} />;
100
- }, 'plauditGutenbergApiExtensionsSimpleNativeProperties'));
101
-
102
- addAction('plaudit.gutenbergApiExtensions.simpleNativeProperties.afterWrite', 'plaudit/gutenberg-api-extensions/simple-native-properties',
103
- (value: unknown, definition: HydratedSimpleNativeProperty, dataStore: DataStore) => {
104
- if ("styleProperty" in definition && definition.styleProperty) {
105
- const computedStyleAttr: {[key: string]: { property: string, value: string, className: string }} = dataStore.getValue('__plaudit/computed-style-attr') ?? {};
106
- const valueStr = value?.toString();
107
- if (computedStyleAttr[definition.name]?.value !== valueStr) {
108
- if (valueStr?.length) {
109
- dataStore.setValue('__plaudit/computed-style-attr', {
110
- ...computedStyleAttr,
111
- [definition.name]: {property: definition.styleProperty, value: valueStr, className: makeHasPropClassName(definition.name)}
112
- });
113
- } else {
114
- const entries = Object.entries(computedStyleAttr)
115
- .filter(([key]) => key !== definition.name);
116
- dataStore.setValue('__plaudit/computed-style-attr', entries.length ? Object.fromEntries(entries) : undefined);
117
- }
118
- }
119
- }
120
- });
121
-
122
- addFilter('editor.BlockListBlock', 'plaudit/gutenberg-api-extensions/simple-native-properties',
123
- createHigherOrderComponent(BlockListBlock => function SNPBlockListBlock(props: ActualBlockListBlockProps) {
124
- const wrapperProps: {className?: string, style?: Record<string, unknown>} = {...props.wrapperProps};
125
- const extraPropTransforms = select(gutenbergApiExtensionsStore).computedExtraPropTransforms(props.name);
126
- if (!extraPropTransforms || !extraPropTransforms.appliesInEditor) {
127
- const dataController = dataControllerManager.getDataController(props.clientId);
128
- if (!dataController) {
129
- return <BlockListBlock {...props} />;
130
- }
131
-
132
- const hasValidationError = dataController.hasValidationErrors();
133
- dataControllerManager.notifyOfValidationErrorStatusChange(props.clientId, hasValidationError);
134
- dataController.checkConditions(); // We don't want to wrap this in a useEffect because this function is already only called when necessary.
135
-
136
- if (hasValidationError) {
137
- //TODO: Should I set isValid?
138
- wrapperProps.className = wrapperProps.className ? `${wrapperProps.className} has-validation-error` : "has-validation-error";
139
- return <BlockListBlock {...props} wrapperProps={wrapperProps} />;
140
- }
141
- return <BlockListBlock {...props} />;
142
- }
143
-
144
- const dataController = dataControllerManager.getDataController(props.clientId);
145
- if (!dataController) {
146
- return <BlockListBlock
147
- {...props}
148
- wrapperProps={applyExtraPropTransforms(extraPropTransforms.transforms, props.attributes, wrapperProps)}
149
- />;
150
- }
151
-
152
- const hasValidationError = dataController.hasValidationErrors();
153
- dataControllerManager.notifyOfValidationErrorStatusChange(props.clientId, hasValidationError);
154
- dataController.checkConditions(); // We don't want to wrap this in a useEffect because this function is already only called when necessary.
155
-
156
- if (hasValidationError) {
157
- //TODO: Should I set isValid?
158
- wrapperProps.className = wrapperProps.className ? `${wrapperProps.className} has-validation-error` : "has-validation-error";
159
- }
160
- return (
161
- <BlockListBlock
162
- {...props}
163
- wrapperProps={applyExtraPropTransforms(extraPropTransforms.transforms, props.attributes, wrapperProps)}
164
- />
165
- );
166
- }, 'plauditGutenbergApiExtensionsSimpleNativeWrapperMarkup'));
167
-
168
- addFilter('blocks.getSaveContent.extraProps', 'plaudit/gutenberg-api-extensions/simple-native-properties',
169
- (props: ActualBlockSaveProps, blockType: RegisterBlockAttrs, attributes: ActualBEPAttrs) => {
170
- if (isBlockJsonNativePropsConfig(blockType.plaudit)) {
171
- return props;
172
- }
173
- const extraPropTransforms = select(gutenbergApiExtensionsStore).computedExtraPropTransforms(blockType.name);
174
-
175
- return extraPropTransforms && extraPropTransforms.appliesInEditor ? produce(props, draft => {
176
- applyExtraPropTransforms(extraPropTransforms.transforms, attributes, draft);
177
- }) : props;
178
- });
179
- }
180
- export namespace installSimpleNativePropertiesSupport {
181
- export let called: boolean = false;
182
- }
183
-
184
- type DataControllerInitializationIsolatorProps = {bep: ActualBlockEditProps, dcm: DataControllerManager, snpPanelsAndTabs: PreppedSimpleNativePanelsAndTabs, BlockEdit: any};
185
- function DataControllerInitializationIsolator({bep, dcm, snpPanelsAndTabs, BlockEdit}: DataControllerInitializationIsolatorProps) {
186
- const dataController = useDataController(bep.clientId);
187
- useEffect(() => {
188
- dcm.addDataController(bep.clientId, dataController);
189
- return () => dcm.removeDataController(bep.clientId);
190
- }, [dataController, bep.clientId]);
191
-
192
- return <>
193
- <BlockEdit {...bep} />
194
- <SNPPropsWrapper blockEditProps={bep} blockSimpleNativePanelsAndTabs={snpPanelsAndTabs} dataController={dataController} />
195
- </>;
196
- }
197
-
198
- function applyExtraPropTransforms(
199
- extraPropTransforms: PerBlockExtraPropTransforms['transforms'], attributes: ActualBEPAttrs, draft: {className?: string, style?: Record<string, unknown>}
200
- ) {
201
- for (const [propPath, propTransform] of extraPropTransforms) {
202
- let value: any;
203
- try {
204
- value = propTransform.forLayeredStyles ? extractStyleLayerValueFromAttributes(attributes, propPath[0]) : walkToNodeInValue(attributes[propPath[0]], propPath);
205
- } catch (e) {
206
- if (e instanceof PathError) {
207
- value = undefined;
208
- } else {
209
- console.error("Encountered an error while applying extra props transforms:", e);
210
- continue;
211
- }
212
- }
213
-
214
- if (propTransform.styleProperty) {
215
- const hasPropNameClass = makeHasPropClassName(propTransform.propName);
216
- if (typeof value === 'string' || typeof value === 'number') {
217
- const styleValue = typeof value === 'number' ? value.toString() : value.trim();
218
- if (styleValue.length) {
219
- if (draft.style) {
220
- draft.style[propTransform.styleProperty] = styleValue;
221
- } else {
222
- draft.style = {[propTransform.styleProperty]: styleValue};
223
- }
224
- if (!draft.className) {
225
- draft.className = hasPropNameClass;
226
- } else if (!draft.className.split(/\s+/).includes(hasPropNameClass)) {
227
- draft.className = draft.className + " " + hasPropNameClass;
228
- }
229
- } else {
230
- if (draft.style) {
231
- delete draft.style[propTransform.styleProperty];
232
- }
233
- if (draft.className) {
234
- draft.className = draft.className.split(/\s+/).filter(cn => cn !== hasPropNameClass).join(" ");
235
- }
236
- }
237
- } else if (value === undefined || value === null) {
238
- if (draft.style) {
239
- delete draft.style[propTransform.styleProperty];
240
- }
241
- if (draft.className) {
242
- draft.className = draft.className.split(/\s+/).filter(cn => cn !== hasPropNameClass).join(" ");
243
- }
244
- } else {
245
- console.error(`Encountered a SNP that had a styleProperty set but evaluated to a ${typeof value}.`, "Prop path:", propPath.join("."));
246
- }
247
- }
248
- }
249
- return draft;
250
- }
251
-
252
- function makeDataStoreForPanelOrTab(
253
- blockEditProps: ActualBlockEditProps, group: string|undefined, attributeCache: Record<string, any>, identifier: string, dataStoreTypeOverride?: 'layered-styles'|'attribute'
254
- ) {
255
- const args = [blockEditProps, attributeCache, `${blockEditProps.clientId}-${identifier}`] as const;
256
- if (dataStoreTypeOverride) {
257
- return dataStoreTypeOverride === 'layered-styles' ? new LayeredStylesDataStore(...args) : new AttributeDataStore(...args);
258
- }
259
- return group === 'layered-styles' ? new LayeredStylesDataStore(...args) : new AttributeDataStore(...args);
260
- }
261
- type SNPPropsWrapperProps = {blockSimpleNativePanelsAndTabs: PreppedSimpleNativePanelsAndTabs, blockEditProps: ActualBlockEditProps, dataController: DataController};
262
- function SNPPropsWrapper({blockSimpleNativePanelsAndTabs, blockEditProps, dataController}: SNPPropsWrapperProps) {
263
- useMemo(() => {
264
- const attributeCache = Object.create(null);
265
- for (const [identifier, panel] of blockSimpleNativePanelsAndTabs.panels) {
266
- const dataStore = makeDataStoreForPanelOrTab(blockEditProps, panel.group, attributeCache, identifier, panel.dataStoreTypeOverride);
267
- dataController.addProperties(panel.properties, dataStore, true);
268
- }
269
- for (const [group, [identifier, tabs]] of Object.entries(blockSimpleNativePanelsAndTabs.tabs)) {
270
- const dataStore = makeDataStoreForPanelOrTab(blockEditProps, group, attributeCache, identifier);
271
- for (const tab of Object.values(tabs)) {
272
- for (const panel of tab.items) {
273
- dataController.addProperties(panel.properties, dataStore, true);
274
- }
275
- }
276
- }
277
- dataController.checkConditions();
278
- dataController.validateNodes();
279
- }, [blockSimpleNativePanelsAndTabs, dataController]); // We don't depend on blockEditProps here - that is handled by the following useEffect
280
-
281
- // We have to reattach dataStores in the useMemo segment in order for Panel- and Tab-level conditions to work
282
- useMemo(() => {
283
- const attributeCache = Object.create(null);
284
- for (const dataStore of dataController.getAllDataStores()) {
285
- dataStore.reattach(attributeCache);
286
- }
287
- }, [dataController.getAllDataStores(), blockEditProps]);
288
-
289
- useEffect(() => {
290
- //TODO: This is a temporary patch. The proper solution is to subscribe to the block-editor redux store and use that to determine when properties have changed.
291
- if (dataController.getWasChangedByManagedControl()) {
292
- dataController.markManagedControlChangeHandled();
293
- } else{
294
- dataController.checkConditions();
295
- dataController.validateNodes();
296
- }
297
- }, [dataController.getAllDataStores(), blockEditProps]);
298
-
299
- //TODO: Hook into block-focus change listener and run validation on focus change
300
- return <SNPTreeContext.Provider value={dataController}>
301
- {...blockSimpleNativePanelsAndTabs.panels.map(panel => <PanelRoot key={panel[0]} panel={panel[1]} />)}
302
- {...Object.entries(blockSimpleNativePanelsAndTabs.tabs).map(
303
- ([group, tabs]) => <TabsRoot key={tabs[0]} tabs={tabs[1]} tabGroupName={group as InspectorPanelGroup} />)}
304
- </SNPTreeContext.Provider>;
305
- }
306
-
307
- function attachSNP(property: HydratedSimpleNativeProperty, injectableProperties: Record<string, any>) {
308
- injectableProperties[property.name] = {type: property.type, default: property.alwaysStore !== false ? undefined : property.default};
309
- if ('enum' in property) {
310
- injectableProperties[property.name].enum = property.enum;
311
- }
312
- }
313
-
314
- function attachSNPsInPanel(properties: HydratedSimpleNativePanel['properties'], injectableProperties: Record<string, any>) {
315
- for (const property of properties) {
316
- if (Array.isArray(property)) {
317
- for (const prop of property) {
318
- attachSNP(prop, injectableProperties);
319
- }
320
- } else {
321
- attachSNP(property, injectableProperties);
322
- }
323
- }
324
- }
325
-
326
- export function makeHasPropClassName(propName: string) {
327
- return "has-" + kebabCase(propName);
328
- }
329
- const kebabCaseRegexes = [/([a-z])([A-Z])/g, /[\s_]+/g] as const;
330
- export function kebabCase(value: string) {
331
- return value
332
- .replace(kebabCaseRegexes[0], "$1-$2")
333
- .replace(kebabCaseRegexes[1], '-')
334
- .toLowerCase();
335
- }
@@ -1,19 +0,0 @@
1
- import type {HydratedSimpleNativeProperty, InspectorPanelGroup, SimpleNativePanel, SimpleNativeTab} from "./simple-native-property-api";
2
-
3
- export type ExtraPropTransformConfig = {propName: string, forLayeredStyles: boolean, styleProperty?: string};
4
- export type ExtraPropTransformsConfig = {[key: string]: ExtraPropTransformConfig};
5
- export type PerBlockExtraPropTransforms = {transforms: [[string, ...string[]], ExtraPropTransformConfig][], appliesInEditor: boolean};
6
-
7
- export type HydratedSimpleNativeTabItem = Omit<HydratedSimpleNativePanel, 'group'>;
8
- export type HydratedSimpleNativeTab = Omit<SimpleNativeTab, 'items'>&{items: HydratedSimpleNativeTabItem[]};
9
- export type HydratedSimpleNativeTabGroup = {[tabTitle: string]: HydratedSimpleNativeTab};
10
- export type HydratedSimpleNativePanel = Omit<SimpleNativePanel, 'properties'>&{properties: HydratedLaidOutProperties};
11
-
12
- export type HydratedLaidOutProperties = (HydratedSimpleNativeProperty|HydratedSimpleNativeProperty[])[];
13
-
14
- export type HydratedInspectorPanelGroupedTabs = {[key in InspectorPanelGroup]?: [string, HydratedSimpleNativeTabGroup]};
15
- export type PreppedSimpleNativePanelsAndTabs = {panels: [string, HydratedSimpleNativePanel][], tabs: HydratedInspectorPanelGroupedTabs};
16
-
17
- export function isRootLevelPanelGroup(panelGroup: string|undefined) {
18
- return panelGroup === undefined || panelGroup === "default" || panelGroup === "advanced" || panelGroup === "settings" || panelGroup === "styles";
19
- }
@@ -1,5 +0,0 @@
1
- export type {ActualBEPAttrs, ActualBlockEditProps, ActualBlockListBlockProps, ActualBlockSaveProps, BlockJsonNativePropsConfig, RegisterBlockAttrs, NonEmptyArray} from "../lib/useful-types";
2
- export {isBlockJsonNativePropsConfig, arrayIsNotEmpty} from "../lib/useful-types";
3
- export type {CSNPConfig} from "./csnp-api";
4
- export type * from "./common-native-property-constructors";
5
- export type * from "./simple-native-property-api";
@@ -1,72 +0,0 @@
1
- import {store as blockEditorStore} from "@wordpress/block-editor";
2
- import {dispatch, select} from "@wordpress/data";
3
-
4
- import {fastDeepEquals} from "../lib/modified-fast-deep-equals";
5
-
6
- import type {UUID} from "./data-controller/utils";
7
- import type {ActualBlockEditProps, BlockName} from "../lib/useful-types";
8
- import type {DataStore, HydratedSimpleNativeProperty} from "./simple-native-property-api";
9
-
10
- export abstract class SNPDataStore implements DataStore {
11
- private readonly managedPropertyNames: {[propertyName in string]: true};
12
-
13
- protected readonly blockClientId: string;
14
- protected readonly blockName: BlockName;
15
-
16
- public constructor(
17
- explicitBlockEditProps: ActualBlockEditProps,
18
- private attributeCache: Record<string, any>,
19
- public readonly id: string
20
- ) {
21
- this.managedPropertyNames = Object.create(null);
22
-
23
- this.blockClientId = explicitBlockEditProps.clientId;
24
- this.blockName = explicitBlockEditProps.name;
25
- }
26
-
27
- getValue(attr: string): any {
28
- if (attr in this.attributeCache) {
29
- return this.attributeCache[attr];
30
- }
31
- const existingValue = this.getAttribute(attr);
32
- return this.attributeCache[attr] = existingValue;
33
- }
34
- setValue(attr: string, value: any) {
35
- const existingValue = this.getValue(attr);
36
- if (!fastDeepEquals(existingValue, value)) {
37
- this.attributeCache[attr] = value;
38
- // WordPress doesn't handle saving and loading of empty objects consistently. As a result, we cannot safely write empty objects if the existing value is undefined
39
- if (existingValue !== undefined || typeof value !== 'object' || !value || Object.entries(value).some(([, v]) => v !== undefined)) {
40
- this.setAttribute(attr, value);
41
- }
42
- }
43
- }
44
- handlesProperty(name: string): boolean {
45
- return name in this.managedPropertyNames;
46
- }
47
-
48
- reattach(attributeCache: Record<string, any>) {
49
- this.attributeCache = attributeCache;
50
- }
51
-
52
- protected abstract getAttribute(name: string): any;
53
- protected abstract setAttribute(name: string, value: any): void;
54
-
55
- protected getRawBlockAttributes() {
56
- return select(blockEditorStore).getBlockAttributes(this.blockClientId) ?? {};
57
- }
58
- protected setRawBlockAttributes(attributes: Record<string, any>) {
59
- dispatch(blockEditorStore).updateBlockAttributes!(this.blockClientId, attributes);
60
- }
61
-
62
- protected getCachedValue(attr: string) {
63
- return this.attributeCache[attr];
64
- }
65
- protected hasCachedValue(attr: string) {
66
- return attr in this.attributeCache;
67
- }
68
-
69
- addProperty(property: HydratedSimpleNativeProperty<{uuid: UUID}>) {
70
- this.managedPropertyNames[property.name] = true;
71
- }
72
- }
@@ -1,66 +0,0 @@
1
- import {type BlockStyle, type BlockVariation, registerBlockStyle, registerBlockVariation, unregisterBlockStyle, unregisterBlockVariation} from "@wordpress/blocks";
2
- import domReady from "@wordpress/dom-ready";
3
-
4
- import type {PairNormalizedPickableOptions} from "../controls";
5
-
6
- export function registerBlockStyles(blockName: string|string[], ...blockStyles: BlockStyle[]) {
7
- domReady(() => {
8
- forStringOrStrings(blockName, bn => {
9
- for (const blockStyle of blockStyles) {
10
- registerBlockStyle(bn, blockStyle);
11
- }
12
- });
13
- if (Array.isArray(blockName)) {
14
- for (const bn of blockName) {
15
- for (const blockStyle of blockStyles) {
16
- registerBlockStyle(bn, blockStyle);
17
- }
18
- }
19
- } else {
20
- for (const blockStyle of blockStyles) {
21
- registerBlockStyle(blockName, blockStyle);
22
- }
23
- }
24
- });
25
- }
26
- export function unregisterBlockStyles(blockName: string|string[], ...blockStyleNames: string[]) {
27
- domReady(() => {
28
- forStringOrStrings(blockName, bn => {
29
- for (const blockStyleName of blockStyleNames) {
30
- unregisterBlockStyle(bn, blockStyleName);
31
- }
32
- });
33
- });
34
- }
35
-
36
- export function registerBlockVariations(blockName: string, ...blockVariations: BlockVariation[]) {
37
- domReady(() => {
38
- forStringOrStrings(blockName, bn => registerBlockVariation(bn, blockVariations));
39
- });
40
- }
41
- export function unregisterBlockVariations(blockName: string, ...blockVariationNames: string[]) {
42
- domReady(() => {
43
- forStringOrStrings(blockName, bn => unregisterBlockVariation(bn, blockVariationNames));
44
- });
45
- }
46
-
47
- export function convertToSelectControlOptions(response: unknown): PairNormalizedPickableOptions<string> {
48
- if (response === null || response === undefined) {
49
- return [];
50
- } else if (typeof response === 'object') {
51
- return (Array.isArray(response) ? response : Object.entries(response)).toSorted(([a], [b]) => a.localeCompare(b));
52
- } else {
53
- console.warn("An invalid response type was passed to convertToSelectControlOptions:", response);
54
- return [[String(response), String(response)]];
55
- }
56
- }
57
-
58
- export function forStringOrStrings(stringOrStrings: string|string[], action: (string: string) => void) {
59
- if (Array.isArray(stringOrStrings)) {
60
- for (const string of stringOrStrings) {
61
- action(string);
62
- }
63
- } else {
64
- action(stringOrStrings);
65
- }
66
- }
@@ -1,86 +0,0 @@
1
- import {Spinner, FormTokenField} from '@wordpress/components';
2
- import {useCallback} from "@wordpress/element";
3
- import {__} from "@wordpress/i18n";
4
-
5
- import type {SNPControlSlots} from "../blocks";
6
- import {useMultiSingleConversionLayer} from "./hooks/useMultiSingleConversionLayer";
7
- import {useSuggestions} from "./hooks/useSuggestions";
8
- import {useTokenManager} from "./hooks/useTokenManager";
9
- import type {TokenItem} from "../lib/useful-types";
10
-
11
- // The strange values correspond to the literals that are expected by TokenItem.status, which allows the assignment code to be cleaner
12
- export const enum ValidationState {
13
- Valid = 'success',
14
- Invalid = 'error',
15
- Validating = 'validating',
16
- Waiting = 'waiting'
17
- }
18
-
19
- type AsynchronousFormTokenFieldPropsBase = {
20
- label?: string;
21
- help?: string;
22
-
23
- validationQuery(tokens: string[]): Promise<Array<TokenItem>>;
24
- suggestionQuery(input: string): Promise<Array<string>>;
25
- makeTokenFromSuggestion(suggestion: string): TokenItem;
26
- validator?: (value: string) => boolean;
27
- expandOnFocus?: boolean;
28
- }&Partial<Pick<SNPControlSlots, 'Messages'>>;
29
- type AsynchronousFormTokenFieldPropsSingle = AsynchronousFormTokenFieldPropsBase&{
30
- value?: string;
31
- onChange: (value: string) => void;
32
- multiple: false;
33
- };
34
- type AsynchronousFormTokenFieldPropsMultiple = AsynchronousFormTokenFieldPropsBase&{
35
- value?: string[];
36
- onChange: (value: string[]) => void;
37
- multiple?: true|undefined;
38
- };
39
- export type AsynchronousFormTokenFieldProps = AsynchronousFormTokenFieldPropsSingle|AsynchronousFormTokenFieldPropsMultiple;
40
-
41
- /**
42
- * @deprecated use ExtendedFormTokenField instead
43
- */
44
- export function AsynchronousFormTokenField(props: AsynchronousFormTokenFieldProps) {
45
- const {help, expandOnFocus = false} = props;
46
-
47
- const {value, setValue} = useMultiSingleConversionLayer(props.value, props.onChange, props.makeTokenFromSuggestion, props.multiple);
48
-
49
- const {
50
- currentTokens, dispatchValueUpdate, hasValidationError, isInitializing, isValidating, tokenTitleMappings
51
- } = useTokenManager(value, setValue, props.validationQuery, props.makeTokenFromSuggestion);
52
- const {
53
- hasLoadingError, isLoading, suggestions, input, setInput
54
- } = useSuggestions(undefined, {expandOnFocus, getSuggestions: props.suggestionQuery});
55
-
56
- const displayTransform = useCallback((token: string) => tokenTitleMappings[token] ?? token, [tokenTitleMappings]);
57
- const onFocus = useCallback(() => setInput(input), [input]);
58
-
59
- if (isInitializing) {
60
- return <Spinner />;
61
- }
62
-
63
- //TODO: If focus is in field but user hasn't started typing, show a message telling them to start typing
64
- return <>
65
- <FormTokenField
66
- __next40pxDefaultSize
67
- value={currentTokens}
68
- label={props.label}
69
- placeholder="Start typing to see suggestions"
70
- suggestions={suggestions}
71
- onChange={dispatchValueUpdate}
72
- maxLength={props.multiple === false ? 1 : undefined}
73
- __experimentalValidateInput={props.validator}
74
- __experimentalAutoSelectFirstMatch={true}
75
- __experimentalExpandOnFocus={expandOnFocus}
76
- displayTransform={displayTransform}
77
- onInputChange={setInput}
78
- onFocus={onFocus}
79
- />
80
- {help && <div><span className="components-form-token-field__help">{help}</span></div>}
81
- {hasLoadingError && <div><Spinner /><span className="components-form-token-field__help">{__("An Error Occurred While Loading Suggestions")}</span></div>}
82
- {isLoading && <div><Spinner /><span className="components-form-token-field__help">{__("Updating Suggestions")}</span></div>}
83
- {hasValidationError && <div><Spinner /><span className="components-form-token-field__help">{__("An Error Occurred While Validating")}</span></div>}
84
- {isValidating && <div><Spinner /><span className="components-form-token-field__help">{__("Validating")}</span></div>}
85
- </>;
86
- }
@@ -1,84 +0,0 @@
1
- import {BaseControl, Button, useBaseControlProps} from "@wordpress/components";
2
- import {useCallback} from "@wordpress/element";
3
- import {dragHandle, Icon} from "@wordpress/icons";
4
-
5
- import {Reorder, useDragControls} from "framer-motion";
6
- import type {PointerEvent, ReactNode} from "react";
7
-
8
- import {ProvidedSlot} from "../blocks";
9
- import {IndexedItemActionsMaker, KeyedValue, useSortableItemsModel} from "./hooks/useSortableItemsModel";
10
-
11
- export type BaseSortableItemsControlProps<D> = Omit<Parameters<typeof useBaseControlProps>[0], 'children'|'id'>&{
12
- id?: string,
13
- value: D[]|undefined,
14
- onAdd?: (index: number, value: D) => void,
15
- onChange?: (value: D[]) => void,
16
- onRemove?: (index: number) => void,
17
- onReorder?: (oldIndex: number, newIndex: number) => void,
18
- children: (datum: D, onDatumChange: (datum: D) => void, index: number) => ReactNode,
19
- buttonsArea: (props: {addHandler: (added: D, index?: number) => void, disabled: boolean}) => ReactNode,
20
- min?: number,
21
- max?: number,
22
- emptyValue?: D,
23
- Label?: ProvidedSlot, Messages?: ProvidedSlot
24
- };
25
-
26
- export function BaseSortableItemsControl<D>({
27
- value, onChange, onRemove, onReorder, onAdd, children: makeChild, min, max, label, Label, Messages, buttonsArea, emptyValue, ...wrapperProps
28
- }: BaseSortableItemsControlProps<D>) {
29
- const {add: addHandler, commit, makeIndexedItemActions, keyedValues} = useSortableItemsModel({onAdd, onChange, onRemove, onReorder, emptyValue, value});
30
-
31
- const listLength = value?.length ?? 0;
32
- const addDisabled = max !== undefined && listLength >= max;
33
- const removeDisabled = min !== undefined && listLength <= min;
34
- const {baseControlProps, controlProps} = useBaseControlProps({...wrapperProps, label: Label ? <Label /> : label});
35
-
36
- return <BaseControl {...baseControlProps} __nextHasNoMarginBottom>
37
- <div {...controlProps} className="plaudit-sortable-items-container">
38
- <Reorder.Group
39
- axis="y" onReorder={commit} values={keyedValues}
40
- children={keyedValues.map((datum, index) => <SortableItemsListNode
41
- addDisabled={addDisabled} datum={datum} index={index} key={datum.key} listLength={listLength}
42
- makeChild={makeChild} makeIndexedItemActions={makeIndexedItemActions} removeDisabled={removeDisabled}
43
- />)}
44
- />
45
- </div>
46
- {buttonsArea({addHandler, disabled: addDisabled})}
47
- {Messages && <Messages />}
48
- </BaseControl>;
49
- }
50
-
51
- type SortableItemsListNodeProps<D> = {
52
- addDisabled: boolean, datum: KeyedValue<D>, index: number, listLength: number,
53
- makeChild: BaseSortableItemsControlProps<D>['children'], removeDisabled: boolean,
54
- makeIndexedItemActions: IndexedItemActionsMaker<D>
55
- };
56
- function SortableItemsListNode<D>({datum, index, listLength, makeChild, makeIndexedItemActions, removeDisabled}: SortableItemsListNodeProps<D>) {
57
- const {remove, moveUp, moveDown, change} = makeIndexedItemActions(index, datum.key);
58
- //TODO: If used, implement addBefore and addAfter the same way as moveUp and moveDown
59
-
60
- const dragControls = useDragControls();
61
- const dragStartHandler = useCallback((event: PointerEvent) => dragControls.start(event), [dragControls]);
62
-
63
- return <Reorder.Item className="plaudit-sortable-items-row" data-index={index} dragListener={false} dragControls={dragControls} layout="position" value={datum}>
64
- <div className="plaudit-sortable-items-ordering-controls">
65
- <div className="plaudit-sortable-items-ordering-control">
66
- <Icon icon={dragHandle} onPointerDown={dragStartHandler} />
67
- </div>
68
- <div className="plaudit-sortable-items-ordering-control">
69
- <Button icon="arrow-up" aria-label="Move Up" disabled={index < 1} onClick={moveUp} __next40pxDefaultSize accessibleWhenDisabled />
70
- <Button icon="arrow-down" aria-label="Move Down" disabled={index >= listLength - 1} onClick={moveDown} __next40pxDefaultSize accessibleWhenDisabled />
71
- </div>
72
- </div>
73
- <div className="plaudit-sortable-items-inputs plaudit-sortable-items-padded">
74
- {makeChild(datum.value, change, index)}
75
- </div>
76
- <div className="plaudit-sortable-items-presence-controls plaudit-sortable-items-padded">
77
- {/*addEmptyHandlers && index === 0 &&
78
- <Button icon="insert" className="insert-before" disabled={addDisabled} onClick={addEmptyHandlers[0]} aria-label="Insert Before"/>*/}
79
- <Button icon="remove" disabled={removeDisabled} onClick={remove} aria-label="Remove" __next40pxDefaultSize accessibleWhenDisabled />
80
- {/*addEmptyHandlers && <Button icon="insert" className="insert-after" disabled={addDisabled} onClick={addEmptyHandlers[1]}
81
- aria-label={index < listLength - 1 ? "Insert Between" : "Insert After"}/>*/}
82
- </div>
83
- </Reorder.Item>;
84
- }