eddev 2.0.0-beta.115 → 2.0.0-beta.117

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 (39) hide show
  1. package/css/editor-styles.css +4 -0
  2. package/dist/app/entry/ssr-root-client.js +3 -2
  3. package/dist/app/entry/ssr-root.js +13 -13
  4. package/dist/app/lib/blocks/EditableText.d.ts +14 -1
  5. package/dist/app/lib/blocks/EditableText.js +8 -2
  6. package/dist/app/lib/blocks/InnerBlocks.d.ts +9 -5
  7. package/dist/app/lib/blocks/InnerBlocks.js +66 -27
  8. package/dist/app/lib/blocks/defineBlock.d.ts +3 -0
  9. package/dist/app/lib/blocks/defineBlock.js +7 -0
  10. package/dist/app/lib/blocks/editor/EditorHighlights.js +43 -9
  11. package/dist/app/lib/blocks/editor/EditorSupport.js +13 -7
  12. package/dist/app/lib/blocks/editor/block-templates.d.ts +6 -0
  13. package/dist/app/lib/blocks/editor/block-templates.js +64 -0
  14. package/dist/app/lib/blocks/editor/create-block.d.ts +9 -0
  15. package/dist/app/lib/blocks/editor/create-block.js +13 -0
  16. package/dist/app/lib/blocks/editor/editor-config.d.ts +38 -3
  17. package/dist/app/lib/blocks/editor/editor-config.js +19 -74
  18. package/dist/app/lib/blocks/editor/installGutenbergHooks.d.ts +3 -0
  19. package/dist/app/lib/blocks/editor/installGutenbergHooks.js +82 -3
  20. package/dist/app/lib/blocks/index.d.ts +5 -4
  21. package/dist/app/lib/blocks/index.js +5 -4
  22. package/dist/app/lib/blocks/inline-editing.d.ts +8 -0
  23. package/dist/app/lib/devtools/hooks/useTailwind.d.ts +5 -5
  24. package/dist/app/lib/routing/components/BrowserRouter.d.ts +1 -0
  25. package/dist/app/lib/routing/components/BrowserRouter.js +3 -0
  26. package/dist/app/server/render-ssr-page.js +1 -0
  27. package/dist/app/server/server-context.d.ts +2 -0
  28. package/dist/app/server/server-context.js +107 -0
  29. package/dist/app/utils/query-monitor.d.ts +25 -0
  30. package/dist/app/utils/query-monitor.js +7 -0
  31. package/dist/node/cli/cli.js +8 -6
  32. package/dist/node/cli/version.d.ts +1 -1
  33. package/dist/node/cli/version.js +1 -1
  34. package/dist/node/compiler/get-vite-config.d.ts +5 -1
  35. package/dist/node/compiler/get-vite-config.js +48 -6
  36. package/dist/node/compiler/vinxi-codegen.js +3 -0
  37. package/dist/node/types/block-type.d.ts +2 -2
  38. package/package.json +1 -1
  39. package/types.meta.d.ts +105 -0
@@ -92,3 +92,7 @@
92
92
  .acf-block-panel .acf-empty-block-fields {
93
93
  display: none;
94
94
  }
95
+
96
+ .simple-appender {
97
+ display: inline-flex !important;
98
+ }
@@ -1,11 +1,12 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect } from "react";
3
3
  import { useSnapshot } from "valtio";
4
4
  import { BrowserRouter } from "../lib/routing/components/BrowserRouter.js";
5
5
  import { clientMetaTags } from "../lib/routing/context.js";
6
6
  import { APIProvider } from "../utils/APIProvider.js";
7
+ import { DevUILoader } from "../lib/devtools/loader.js";
7
8
  export function SSRClientRoot(props) {
8
- return (_jsxs(APIProvider, { children: [_jsx(DynamicMetaTags, {}), _jsx(BrowserRouter, {})] }));
9
+ return (_jsxs(APIProvider, { children: [_jsx(BrowserRouter, {}), _jsxs(_Fragment, { children: [_jsx(DynamicMetaTags, {}), _jsx(DevUILoader, {})] })] }));
9
10
  }
10
11
  function DynamicMetaTags() {
11
12
  const dynamicTags = useSnapshot(clientMetaTags).tags;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { SSRRouter } from "../lib/routing/components/SSRRouter.js";
3
3
  import { getRouteMeta, normalizeRoute } from "../lib/routing/utils.js";
4
4
  import { APIProvider } from "../utils/APIProvider.js";
@@ -6,16 +6,16 @@ export function SSRRoot(props) {
6
6
  const loader = props.loader;
7
7
  loader.setAppData(props.initialData.appData.data);
8
8
  loader.populateRouteData(props.pathname, props.initialData);
9
- return (_jsx(APIProvider, { children: _jsx(SSRRouter, { loader: loader, route: normalizeRoute({
10
- id: "initial",
11
- component: loader.getRouteComponent(props.initialData.view),
12
- key: "",
13
- props: props.initialData.viewData.data,
14
- view: props.initialData.view,
15
- search: "",
16
- pathname: props.pathname,
17
- query: {},
18
- hash: "",
19
- meta: getRouteMeta(props.initialData),
20
- }) }) }));
9
+ return (_jsxs(APIProvider, { children: [_jsx(SSRRouter, { loader: loader, route: normalizeRoute({
10
+ id: "initial",
11
+ component: loader.getRouteComponent(props.initialData.view),
12
+ key: "",
13
+ props: props.initialData.viewData.data,
14
+ view: props.initialData.view,
15
+ search: "",
16
+ pathname: props.pathname,
17
+ query: {},
18
+ hash: "",
19
+ meta: getRouteMeta(props.initialData),
20
+ }) }), _jsx(_Fragment, {})] }));
21
21
  }
@@ -1,12 +1,25 @@
1
1
  import { ElementType } from "react";
2
2
  import { InlineValueStore } from "./inline-editing.js";
3
3
  export type InlineTextValueStore = InlineValueStore<string>;
4
+ type BuiltinFormats = "core/bold" | "core/code" | "core/italic" | "core/link" | "core/strikethrough" | "core/underline" | "core/subscript" | "core/superscript" | "core/unknown" | "core/non-breaking-space" | "core/footnote";
4
5
  type Props<T extends ElementType> = {
5
6
  /** Specify a tag name or React component */
6
7
  as?: T;
7
8
  /** Prevents this text element from being multi-line */
8
9
  disableLineBreaks?: boolean;
9
- /** Enables the rich text toolbar */
10
+ /**
11
+ * Specify which formatting options are allowed in this text element (bold, italics etc).
12
+ *
13
+ * By default, all options are enabled.
14
+ *
15
+ * Set to an empty array to disable formatting options.
16
+ *
17
+ * You can register new formats in `_editor.tsx` using the `defineEditorConfig` function.
18
+ **/
19
+ allowedFormats?: (BuiltinFormats | (string & {}))[];
20
+ /**
21
+ * @deprecated use `allowedFormats` instead
22
+ */
10
23
  inlineToolbar?: boolean;
11
24
  /** Specify default content to use on the frontend if nothing has been entered */
12
25
  defaultValue?: string;
@@ -6,8 +6,13 @@ export function EditableText({ id, as, appendOnEnter, store, ...props }) {
6
6
  const readOnly = useBlockContext()?.readonly;
7
7
  if (!readOnly) {
8
8
  const [value, setValue] = useValueStore(store ?? id);
9
+ // const defaultFormats = wp.data.useSelect((select) => {
10
+ // const formats = (select(wp.richText.store) as any).getFormatTypes()
11
+ // console.log("U", formats)
12
+ // return formats.map((f: any) => f.name)
13
+ // }, [])
9
14
  const appendBlocks = useBlockAppender();
10
- return (_jsx(wp.blockEditor.RichText, { ...props, placeholder: props.placeholder ?? props.defaultValue, tagName: as, value: value || "", onChange: setValue, inlineToolbar: props.inlineToolbar, disableLineBreaks: props.disableLineBreaks, onKeyDownCapture: (e) => {
15
+ return (_jsx(wp.blockEditor.RichText, { ...props, placeholder: props.placeholder ?? props.defaultValue, tagName: as, value: value || "", onChange: setValue, allowedFormats: props.inlineToolbar === false ? [] : props.allowedFormats, disableLineBreaks: props.disableLineBreaks, "data-allowed-formats": props.allowedFormats?.join(" "), onKeyDownCapture: (e) => {
11
16
  if (e.key === "Enter" && appendOnEnter && appendBlocks) {
12
17
  appendBlocks([
13
18
  wp.blocks.createBlock(typeof appendOnEnter === "string" ? appendOnEnter : "core/paragraph"),
@@ -21,11 +26,12 @@ export function EditableText({ id, as, appendOnEnter, store, ...props }) {
21
26
  let [value] = useValueStore(store ?? id);
22
27
  const handleClickEvent = useRouter((r) => r.handleClickEvent);
23
28
  const otherProps = { ...props };
24
- delete otherProps.inlineToolbar;
25
29
  delete otherProps.disableLineBreaks;
26
30
  delete otherProps.id;
27
31
  otherProps.as = otherProps.asProp ?? undefined;
28
32
  delete otherProps.asProp;
33
+ delete otherProps.allowedFormats;
34
+ delete otherProps.disableLineBreaks;
29
35
  delete otherProps.placeholder;
30
36
  if (value === "" || typeof value !== "string") {
31
37
  if (props.defaultValue) {
@@ -2,7 +2,7 @@ import { FunctionComponent } from "react";
2
2
  import { ContentBlockLayoutProps } from "./ContentBlocks.js";
3
3
  import { BlockTemplate } from "./editor/block-templates.js";
4
4
  type AppenderConfig = {
5
- type: "default" | "button" | CustomBlockAppender;
5
+ type: "default" | "button" | "simple" | CustomBlockAppender;
6
6
  className?: string;
7
7
  };
8
8
  export type CustomBlockAppender = FunctionComponent<{
@@ -26,9 +26,13 @@ type InnerBlocksProps = {
26
26
  * NOTE: This will have no effect on the frontend, since no wrapper div is created on the frontend.
27
27
  **/
28
28
  adminClassName?: string;
29
- /**
30
- *
31
- */
29
+ /** The default blocks to insert when there are no (non-templated) blocks */
30
+ defaultBlocks?: BlockTemplate;
31
+ /** Blocks to ensure are inserted at the top of the page */
32
+ headerTemplate?: BlockTemplate;
33
+ /** Blocks to ensure are inserted at the bottom of the page */
34
+ footerTemplate?: BlockTemplate;
35
+ /** A full-page block template */
32
36
  template?: BlockTemplate;
33
37
  /**
34
38
  * `false` allows all operations
@@ -41,7 +45,7 @@ type InnerBlocksProps = {
41
45
  * @default false
42
46
  *
43
47
  */
44
- templateLock?: "all" | "insert" | "contentOnly" | false;
48
+ templateLock?: "all" | "insert" | "contentOnly" | "none" | false;
45
49
  appender?: AppenderConfig;
46
50
  prioritizedInserterBlocks?: ChildBlockTypeName[];
47
51
  } & ContentBlockLayoutProps;
@@ -1,25 +1,58 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo } from "react";
2
3
  import { ContentBlocks } from "./ContentBlocks.js";
3
- import { transformBlockTemplate } from "./editor/block-templates.js";
4
+ import { applyTemplateBlocks, transformBlockTemplate } from "./editor/block-templates.js";
4
5
  import { blocksByTag } from "./editor/blocks-by-tag.js";
5
6
  import { useBlockContext, useInnerBlocks } from "./inline-editing.js";
6
- const Appender = (props) => {
7
+ import { hash } from "object-code";
8
+ const Appender = ({ config, ...props }) => {
7
9
  const clientId = useBlockContext()?.block[1].clientId;
8
- if (props.type === "button") {
9
- return _jsx(wp.blockEditor.ButtonBlockAppender, { className: props.className, rootClientId: clientId });
10
+ if (config?.type === "button") {
11
+ return _jsx(wp.blockEditor.ButtonBlockAppender, { ...props, rootClientId: clientId, className: config.className });
10
12
  }
11
- else if (typeof props.type === "function") {
12
- const Type = props.type;
13
- return (_jsx(wp.blockEditor.Inserter, { renderToggle: (p) => {
13
+ else if (config?.type === "simple") {
14
+ return (_jsx(wp.blockEditor.ButtonBlockAppender, { ...props, rootClientId: clientId, className: (props.className || "") + " block-editor-inserter__toggle has-icon simple-appender" }));
15
+ }
16
+ else if (typeof config?.type === "function") {
17
+ const Type = config?.type;
18
+ return (_jsx(wp.blockEditor.Inserter, { rootClientId: clientId, renderToggle: (p) => {
14
19
  return _jsx(Type, { ...p });
15
- }, rootClientId: clientId }));
20
+ }, isAppender: true,
21
+ // @ts-ignore
22
+ __experimentalIsQuick: true }));
23
+ // } else if (config?.type === "simple") {
24
+ // return (
25
+ // <wp.blockEditor.Inserter
26
+ // clientId={clientId}
27
+ // rootClientId={clientId}
28
+ // renderToggle={(p: any) => {
29
+ // return (
30
+ // <button
31
+ // className="simple-appender components-button block-editor-inserter__toggle !inline-flex has-icon"
32
+ // onClick={() => p.onToggle()}
33
+ // >
34
+ // <svg
35
+ // xmlns="http://www.w3.org/2000/svg"
36
+ // viewBox="0 0 24 24"
37
+ // width="24"
38
+ // height="24"
39
+ // aria-hidden="true"
40
+ // focusable="false"
41
+ // >
42
+ // <path d="M11 12.5V17.5H12.5V12.5H17.5V11H12.5V6H11V11H6V12.5H11Z"></path>
43
+ // </svg>
44
+ // </button>
45
+ // )
46
+ // }}
47
+ // isAppender
48
+ // // @ts-ignore
49
+ // __experimentalIsQuick
50
+ // />
51
+ // )
16
52
  }
17
53
  else {
18
- return (_jsx(wp.blockEditor.DefaultBlockAppender
19
54
  // @ts-ignore
20
- , {
21
- // @ts-ignore
22
- className: props.className, rootClientId: clientId, lastBlockClientId: clientId }));
55
+ return _jsx(wp.blockEditor.InnerBlocks.DefaultBlockAppender, { ...props });
23
56
  }
24
57
  };
25
58
  export function createAppender(comp) {
@@ -33,29 +66,35 @@ export function createAppender(comp) {
33
66
  export function InnerBlocks(props) {
34
67
  if (env.admin) {
35
68
  const inlineContext = useBlockContext();
69
+ const appender = useMemo(() => {
70
+ return (p) => _jsx(Appender, { config: props.appender, ...p });
71
+ }, [props.appender]);
36
72
  if (!inlineContext?.readonly) {
37
73
  const innerBlocksProps = wp.blockEditor.useInnerBlocksProps({}, {
38
74
  orientation: props.orientation ?? "vertical",
39
75
  allowedBlocks: props.allowedBlocks ? blocksByTag.expand(props.allowedBlocks) : undefined,
40
76
  prioritizedInserterBlocks: props.prioritizedInserterBlocks,
41
- renderAppender: props.appender
42
- ? () => _jsx(Appender, { ...props.appender })
43
- : wp.blockEditor.InnerBlocks.ButtonBlockAppender,
44
- templateLock: props.templateLock ?? false,
77
+ renderAppender: appender,
78
+ templateLock: props.templateLock === "none" ? false : (props.templateLock ?? false),
45
79
  template: props.template ? transformBlockTemplate(props.template) : undefined,
46
80
  });
81
+ /**
82
+ * A little bit experimental
83
+ *
84
+ * Adds support for headerTemplate/defaultBlocks/footerTemplate, which was first introduced in `_editor.tsx` for generate templates.
85
+ */
86
+ const blockId = inlineContext?.block[1].clientId;
87
+ useEffect(() => {
88
+ if (props.defaultBlocks || props.headerTemplate || props.footerTemplate) {
89
+ const newBlocks = applyTemplateBlocks(inlineContext?.innerBlocks ?? [], props);
90
+ wp.data.dispatch(wp.blockEditor.store).replaceInnerBlocks(blockId, newBlocks);
91
+ }
92
+ }, [
93
+ hash(inlineContext?.innerBlocks.map((b) => [b.blockName, b.clientId])),
94
+ hash([props.template, props.defaultBlocks, props.headerTemplate, props.footerTemplate]),
95
+ blockId,
96
+ ]);
47
97
  return (_jsx("div", { ...innerBlocksProps, className: [innerBlocksProps.className, props.adminClassName].filter(Boolean).join(" ") }));
48
- // return (
49
- // <wp.blockEditor.InnerBlocks
50
- // // @ts-ignore
51
- // orientation={props.orientation}
52
- // prioritizedInserterBlocks={props.prioritizedInserterBlocks}
53
- // allowedBlocks={props.allowedBlocks ? blocksByTag.expand(props.allowedBlocks) : undefined}
54
- // renderAppender={props.appender ? () => <Appender {...props.appender!} /> : undefined}
55
- // templateLock={(props.templateLock as any) ?? false}
56
- // template={props.template ? transformBlockTemplate(props.template) : undefined}
57
- // />
58
- // )
59
98
  }
60
99
  }
61
100
  const blocks = useInnerBlocks();
@@ -1,2 +1,5 @@
1
1
  import { ComponentType, ReactNode } from "react";
2
2
  export declare function defineBlock<TName extends keyof BlockProps>(name: TName, component: (props: BlockProps[TName]) => ReactNode): ComponentType<BlockProps[TName]>;
3
+ export declare namespace defineBlock {
4
+ var meta: (name: string, meta: any) => void;
5
+ }
@@ -1,3 +1,10 @@
1
+ import { blockMetaDescriptors } from "./editor/installGutenbergHooks";
2
+ import { resolveAcfBlockName } from "./editor/block-templates";
1
3
  export function defineBlock(name, component) {
2
4
  return component;
3
5
  }
6
+ defineBlock.meta = (name, meta) => {
7
+ if (env.admin) {
8
+ blockMetaDescriptors.set(resolveAcfBlockName(name), meta);
9
+ }
10
+ };
@@ -1,7 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState } from "react";
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
3
  export function EditorHighlights(props) {
4
- const ref = useRef(null);
4
+ const [element, setElement] = useState(null);
5
5
  const [controller, setController] = useState(null);
6
6
  // wp.data.select('core/editor').isBlockSelected
7
7
  // const isSelected = wp.data.useSelect(
@@ -9,12 +9,12 @@ export function EditorHighlights(props) {
9
9
  // [props.clientId]
10
10
  // )
11
11
  useEffect(() => {
12
- if (!props.enabled)
12
+ if (!props.enabled || !element)
13
13
  return;
14
- const controller = new HighlightController(ref.current);
14
+ const controller = new HighlightController(element);
15
15
  setController(controller);
16
16
  return () => controller.teardown();
17
- }, [props.enabled]);
17
+ }, [props.enabled, element]);
18
18
  useEffect(() => {
19
19
  if (controller) {
20
20
  controller.isSelected = false;
@@ -22,7 +22,38 @@ export function EditorHighlights(props) {
22
22
  controller.update();
23
23
  }
24
24
  }, [controller, false]);
25
- return (_jsx("div", { ref: ref, style: { display: "contents" }, onPointerEnter: () => controller?.enable(), onPointerLeave: () => controller?.disable(), children: props.children }));
25
+ useEffect(() => {
26
+ const element = document.getElementById(`block-${props.clientId}`);
27
+ if (element) {
28
+ setElement(element);
29
+ }
30
+ else {
31
+ setElement(null);
32
+ }
33
+ }, [props.clientId]);
34
+ useEffect(() => {
35
+ if (controller && element) {
36
+ const onPointerEnter = () => controller.enable();
37
+ const onPointerLeave = () => controller.disable();
38
+ element.addEventListener("pointerenter", onPointerEnter);
39
+ element.addEventListener("pointerleave", onPointerLeave);
40
+ return () => {
41
+ element.removeEventListener("pointerenter", onPointerEnter);
42
+ element.removeEventListener("pointerleave", onPointerLeave);
43
+ };
44
+ }
45
+ }, [element, controller]);
46
+ return _jsx(_Fragment, { children: props.children });
47
+ // return (
48
+ // <div
49
+ // ref={ref}
50
+ // style={{ display: "contents" }}
51
+ // // onPointerEnter={() => controller?.enable()}
52
+ // // onPointerLeave={() => controller?.disable()}
53
+ // >
54
+ // {props.children}
55
+ // </div>
56
+ // )
26
57
  }
27
58
  class HighlightController {
28
59
  root;
@@ -40,7 +71,7 @@ class HighlightController {
40
71
  let editables = Array.from(this.root.querySelectorAll("[contenteditable=true], .editable-slot"));
41
72
  let childBlocks = Array.from(this.root.querySelectorAll(".wp-block"));
42
73
  const childIsSelected = this.root.querySelectorAll(".editor-highlighter-root-selected").length > 0;
43
- this.block = childBlocks[0];
74
+ this.block = this.root;
44
75
  childBlocks = childBlocks.slice(1);
45
76
  editables = childIsSelected
46
77
  ? []
@@ -96,7 +127,10 @@ class HighlightController {
96
127
  el.style.position = "absolute";
97
128
  el.style.pointerEvents = "none";
98
129
  el.style.zIndex = "20";
99
- el.className = "editor-highlight";
130
+ el.classList.add("editor-highlight");
131
+ if (target.getAttribute("data-editor-higlight-class")) {
132
+ el.classList.add(target.getAttribute("data-editor-higlight-class"));
133
+ }
100
134
  this.block.appendChild(el);
101
135
  return el;
102
136
  }
@@ -1,17 +1,23 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, Suspense, useContext } from "react";
2
+ import { createContext, Suspense, useContext, useEffect } from "react";
3
3
  import { blockManifestReader } from "../../internal/read-block-manifest.js";
4
4
  import { APIProvider } from "../../../utils/APIProvider.js";
5
5
  import { ErrorBoundaryEditor } from "./ErrorBoundaryEditor.js";
6
6
  export const BlockContext = createContext(undefined);
7
7
  export function EditableBlock({ payload }) {
8
- const block = useContext(BlockContext);
9
- if (!block)
8
+ if (!env.admin)
9
+ throw new Error("`EditableBlock` can only be used in the admin environment");
10
+ const info = useContext(BlockContext);
11
+ useEffect(() => {
12
+ const block = wp.data.select("core/block-editor").getBlock(info?.props.clientId);
13
+ if (block && block.attributes) {
14
+ // @ts-ignore
15
+ block.attributes.props = payload;
16
+ }
17
+ }, [payload, info?.props.clientId]);
18
+ if (!info)
10
19
  return null;
11
- const getBlock = () => {
12
- return blockManifestReader.value[block.name];
13
- };
14
- const BlockComponent = getBlock();
20
+ const BlockComponent = blockManifestReader.value[info.name];
15
21
  if (!BlockComponent)
16
22
  return _jsx("div", { children: "Unable to load block component" });
17
23
  return (_jsx(ErrorBoundaryEditor, { children: _jsx(APIProvider, { children: _jsx(Suspense, { children: _jsx(BlockComponent, { ...payload }) }) }) }));
@@ -1,3 +1,9 @@
1
1
  export type BlockTemplate = [name: ChildBlockTypeName, props: any, children?: BlockTemplate][];
2
2
  export declare function resolveAcfBlockName(name: string): string;
3
3
  export declare function transformBlockTemplate(template: BlockTemplate): BlockTemplate;
4
+ export declare function applyTemplateBlocks(currentBlocks: any[], config: {
5
+ defaultBlocks?: BlockTemplate;
6
+ headerTemplate?: BlockTemplate;
7
+ footerTemplate?: BlockTemplate;
8
+ }): any[];
9
+ export declare function transformTemplateToBlocks(template: BlockTemplate, locked?: boolean, isFromTemplate?: boolean): any;
@@ -7,3 +7,67 @@ export function transformBlockTemplate(template) {
7
7
  return [resolveAcfBlockName(name), props, children ? transformBlockTemplate(children) : undefined];
8
8
  });
9
9
  }
10
+ export function applyTemplateBlocks(currentBlocks, config) {
11
+ const templateBlocks = currentBlocks.filter((block) => block.attributes.isFromTemplate);
12
+ let header = config.headerTemplate ? syncBlocks(transformTemplateToBlocks(config.headerTemplate)) : [];
13
+ let footer = config.footerTemplate ? syncBlocks(transformTemplateToBlocks(config.footerTemplate)) : [];
14
+ let blocks = currentBlocks.filter((block) => !header.includes(block) && !footer.includes(block));
15
+ blocks.forEach((block) => {
16
+ delete block.attributes.lock;
17
+ delete block.isFromTemplate;
18
+ });
19
+ // const blocksToDelete = currentBlocks.filter((block: any) => {
20
+ // return !header.includes(block) && !footer.includes(block) && !blocks.includes(block)
21
+ // })
22
+ // blocksToDelete.forEach((block: any) => {
23
+ // delete block.attributes.lock
24
+ // delete block.isFromTemplate
25
+ // })
26
+ // blocks = [...blocks, ...blocksToDelete]
27
+ // console.log("blocks", blocks)
28
+ // console.log("blocksToDelete", blocksToDelete)
29
+ if (!blocks.length && config.defaultBlocks) {
30
+ blocks = transformTemplateToBlocks(config.defaultBlocks, false, false);
31
+ }
32
+ let newBlocks = [...header, ...blocks, ...footer];
33
+ function syncBlocks(blocks) {
34
+ return blocks.map((block) => {
35
+ const matched = templateBlocks.find((templateBlock) => templateBlock.name === block.name);
36
+ templateBlocks.splice(templateBlocks.indexOf(matched), 1);
37
+ if (matched) {
38
+ matched.attributes.lock = block.attributes.lock;
39
+ return matched;
40
+ }
41
+ return block;
42
+ });
43
+ }
44
+ return newBlocks;
45
+ }
46
+ export function transformTemplateToBlocks(template, locked = true, isFromTemplate = true) {
47
+ return template.map(([name, props, children]) => {
48
+ const attributes = {
49
+ data: {},
50
+ inline: {},
51
+ isFromTemplate: isFromTemplate,
52
+ lock: undefined,
53
+ };
54
+ if (props.locked === false) {
55
+ attributes.lock = { move: false, remove: false };
56
+ }
57
+ else if (locked || props.locked === true || props.locked === "all") {
58
+ attributes.lock = { move: true, remove: true };
59
+ }
60
+ else {
61
+ attributes.lock = props.lock;
62
+ }
63
+ Object.assign(attributes, props);
64
+ return {
65
+ clientId: "block-" + Math.random().toString(36),
66
+ name: resolveAcfBlockName(name),
67
+ attributes: attributes,
68
+ innerBlocks: children ? transformTemplateToBlocks(children, false, isFromTemplate) : [],
69
+ isValid: true,
70
+ validationIssues: [],
71
+ };
72
+ });
73
+ }
@@ -0,0 +1,9 @@
1
+ import { BlockInstance } from "../inline-editing";
2
+ /**
3
+ * Creates a block instance for the editor. This doesn't add the block to the editor on it's own.
4
+ * @param name The name of the block
5
+ * @param attributes
6
+ * @param innerBlocks
7
+ * @returns
8
+ */
9
+ export declare function createBlock(name: ChildBlockTypeName, attributes?: Record<string, any>, innerBlocks?: BlockInstance[]): BlockInstance;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Creates a block instance for the editor. This doesn't add the block to the editor on it's own.
3
+ * @param name The name of the block
4
+ * @param attributes
5
+ * @param innerBlocks
6
+ * @returns
7
+ */
8
+ export function createBlock(name, attributes = {}, innerBlocks = []) {
9
+ if (!env.admin) {
10
+ throw new Error("`createBlock` can only be used in the admin environment");
11
+ }
12
+ return wp.blocks.createBlock(name, attributes, innerBlocks);
13
+ }
@@ -44,7 +44,6 @@ export declare const editorConfigStore: {
44
44
  currentBlocksConfig: EditorConfigItem;
45
45
  };
46
46
  export declare function configureEditorBlocks(config: EditorConfigItem): void;
47
- export declare function transformTemplateToBlocks(template: BlockTemplate, locked?: boolean, isFromTemplate?: boolean): any;
48
47
  type PostInfo = {
49
48
  type: PostTypeName;
50
49
  isPattern: false;
@@ -72,9 +71,45 @@ type Matcher = {
72
71
  /** Configuration for the editor, when the post type and template match */
73
72
  config: EditorConfigItem | ((post: PostInfo) => EditorConfigItem);
74
73
  };
74
+ export type BlockTransform = {
75
+ from: ChildBlockTypeName[];
76
+ to: ChildBlockTypeName;
77
+ };
78
+ export type CustomRichTextFormat = {
79
+ /** The ID of the format, eg. `"custom/fancy"` */
80
+ id: string;
81
+ /** The title of the format, shown on the tooltip */
82
+ title: string;
83
+ /**
84
+ * The HTML tag name to use.
85
+ * @default `"span"`
86
+ **/
87
+ tagName?: string;
88
+ /**
89
+ * The class name to apply to the element.
90
+ * @default `undefined`
91
+ **/
92
+ className?: string;
93
+ /**
94
+ * The icon to use in the toolbar.
95
+ */
96
+ icon?: string;
97
+ /**
98
+ * Whether format makes content interactive or not.
99
+ * @default `false`
100
+ */
101
+ interactive?: boolean;
102
+ /**
103
+ * Whether to disable this format by default. If `true`, you must pass the format ID to `allowedFormats` on a `EditableText` component.
104
+ *
105
+ * @default `false`
106
+ */
107
+ disabledByDefault?: boolean;
108
+ };
75
109
  type EditorConfig = {
76
- /** A list of template/post type matchers, and the resulting editor config */
77
- matchers: Matcher[];
110
+ customRichTextFormats?: CustomRichTextFormat[];
111
+ /** A list of template/post type matchers, and resulting editor config that they will apply */
112
+ matchers?: Matcher[];
78
113
  };
79
114
  /**
80
115
  * This call should be placed in blocks/_editor.tsx