@frontkom/block-react-parser 0.1.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/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 16.16.0
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = Block;
7
+ var _Tree = _interopRequireDefault(require("./Tree"));
8
+ var _innerNode = _interopRequireDefault(require("../utils/innerNode"));
9
+ var _Context = require("./Context");
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ /**
12
+ * Block element.
13
+ *
14
+ * @param {object} componentProps - properties that includes the block object.
15
+ * @returns {JSX.Element | null | undefined}
16
+ */
17
+ function Block(_ref) {
18
+ let {
19
+ block
20
+ } = _ref;
21
+ const {
22
+ blockName = null,
23
+ innerContent,
24
+ innerBlocks
25
+ } = block;
26
+ const CustomBlock = (0, _Context.useBlockComponent)(blockName);
27
+ if (CustomBlock) {
28
+ return /*#__PURE__*/React.createElement(CustomBlock, {
29
+ block: block
30
+ });
31
+ }
32
+
33
+ // Filter out empty blocks.
34
+ if (!blockName && innerContent.length === 0) {
35
+ return null;
36
+ }
37
+ // Filter out empty lines and orphaned closing tags.
38
+ if (innerContent.length === 1 && (innerContent[0] === "\n" || innerContent[0].substring(0, 2) === "</")) {
39
+ return null;
40
+ }
41
+ const node = (0, _innerNode.default)(innerBlocks, innerContent);
42
+ if (node) {
43
+ return /*#__PURE__*/React.createElement(_Tree.default, {
44
+ node: node,
45
+ block: block
46
+ });
47
+ }
48
+ return null;
49
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Provider = void 0;
7
+ exports.useBlockComponent = useBlockComponent;
8
+ exports.useComponentContext = useComponentContext;
9
+ exports.useTagComponent = useTagComponent;
10
+ var _react = require("react");
11
+ var _elements = require("../elements");
12
+ const TxContext = /*#__PURE__*/(0, _react.createContext)();
13
+ const {
14
+ Provider
15
+ } = TxContext;
16
+ exports.Provider = Provider;
17
+ function useComponentContext() {
18
+ return (0, _react.useContext)(TxContext);
19
+ }
20
+ function useBlockComponent(name) {
21
+ const {
22
+ CustomBlocks = _elements.coreBlocks
23
+ } = (0, _react.useContext)(TxContext);
24
+ return name && CustomBlocks[name];
25
+ }
26
+ function useTagComponent(tag) {
27
+ const {
28
+ CustomTags = _elements.coreTags
29
+ } = (0, _react.useContext)(TxContext);
30
+ return tag && CustomTags[tag];
31
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = Tree;
7
+ var _Context = require("./Context");
8
+ var _Block = _interopRequireDefault(require("./Block"));
9
+ var _attribsProps = _interopRequireDefault(require("../utils/attribsProps"));
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ function Tree(_ref) {
12
+ var _node$children;
13
+ let {
14
+ node,
15
+ block
16
+ } = _ref;
17
+ const CustomTag = (0, _Context.useTagComponent)(node.name);
18
+ (0, _attribsProps.default)(node.attribs);
19
+ if (node.type === "text") {
20
+ if (node.data === "[innerBlocks]") {
21
+ var _block$innerBlocks;
22
+ // eslint-disable-next-line react/no-array-index-key
23
+ return (_block$innerBlocks = block.innerBlocks) === null || _block$innerBlocks === void 0 ? void 0 : _block$innerBlocks.map((inner, index) => /*#__PURE__*/React.createElement(_Block.default, {
24
+ block: inner,
25
+ key: index
26
+ }));
27
+ }
28
+ return node.data;
29
+ }
30
+
31
+ // Handle selfclosed elements (???)
32
+ if (CustomTag) {
33
+ return /*#__PURE__*/React.createElement(CustomTag, {
34
+ attribs: node.attribs
35
+ });
36
+ }
37
+ const Component = node.name;
38
+ const attrs = (0, _attribsProps.default)(node.attribs);
39
+ return /*#__PURE__*/React.createElement(Component, attrs, (_node$children = node.children) === null || _node$children === void 0 ? void 0 : _node$children.map((child, index) =>
40
+ /*#__PURE__*/
41
+ // eslint-disable-next-line react/no-array-index-key
42
+ React.createElement(Tree, {
43
+ node: child,
44
+ block: block,
45
+ key: index
46
+ })));
47
+ }
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.customTags = exports.customBlocks = exports.coreTags = exports.coreBlocks = void 0;
7
+ var _img = _interopRequireDefault(require("./tags/img"));
8
+ var _selfClosing = _interopRequireDefault(require("./tags/selfClosing"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ const coreTags = exports.coreTags = {
11
+ img: _ref => {
12
+ let {
13
+ attribs
14
+ } = _ref;
15
+ return /*#__PURE__*/React.createElement(_img.default, {
16
+ attribs: attribs
17
+ });
18
+ },
19
+ br: _ref2 => {
20
+ let {
21
+ attribs
22
+ } = _ref2;
23
+ return /*#__PURE__*/React.createElement(_selfClosing.default, {
24
+ attribs: attribs,
25
+ tag: "br"
26
+ });
27
+ },
28
+ hr: _ref3 => {
29
+ let {
30
+ attribs
31
+ } = _ref3;
32
+ return /*#__PURE__*/React.createElement(_selfClosing.default, {
33
+ attribs: attribs,
34
+ tag: "hr"
35
+ });
36
+ },
37
+ meta: _ref4 => {
38
+ let {
39
+ attribs
40
+ } = _ref4;
41
+ return null;
42
+ }
43
+ // area: ({attribs}) => <SelfClosing attribs={attribs} tag="area" />,
44
+ // base: ({attribs}) => <SelfClosing attribs={attribs} tag="base" />,
45
+ // col: ({attribs}) => <SelfClosing attribs={attribs} tag="col" />,
46
+ // embed: ({attribs}) => <SelfClosing attribs={attribs} tag="embed" />,
47
+ // input: ({attribs}) => <SelfClosing attribs={attribs} tag="input" />,
48
+ // link: ({attribs}) => <SelfClosing attribs={attribs} tag="link" />,
49
+ // meta: ({attribs}) => <SelfClosing attribs={attribs} tag="meta" />,
50
+ // param: ({attribs}) => <SelfClosing attribs={attribs} tag="param" />,
51
+ // source: ({attribs}) => <SelfClosing attribs={attribs} tag="source" />,
52
+ // track: ({attribs}) => <SelfClosing attribs={attribs} tag="track" />,
53
+ // wbr: ({attribs}) => <SelfClosing attribs={attribs} tag="wbr" />,
54
+ };
55
+ const coreBlocks = exports.coreBlocks = {
56
+ // 'core/archives': ({ block }) => doSomething(),
57
+ // 'core/audio': ({ block }) => doSomething(),
58
+ // 'core/avatar': ({ block }) => doSomething(),
59
+ // 'core/block': ({ block }) => doSomething(),
60
+ // 'core/button': ({ block }) => doSomething(),
61
+ // 'core/buttons': ({ block }) => doSomething(),
62
+ // 'core/calendar': ({ block }) => doSomething(),
63
+ // 'core/categories': ({ block }) => doSomething(),
64
+ // 'core/code': ({ block }) => doSomething(),
65
+ // 'core/column': ({ block }) => <p>Column</p>,
66
+ // 'core/columns': ({ block }) => doSomething(),
67
+ // 'core/comment-author-avatar': ({ block }) => doSomething(),
68
+ // 'core/comment-author-name': ({ block }) => doSomething(),
69
+ // 'core/comment-content': ({ block }) => doSomething(),
70
+ // 'core/comment-date': ({ block }) => doSomething(),
71
+ // 'core/comment-edit-link': ({ block }) => doSomething(),
72
+ // 'core/comment-reply-link': ({ block }) => doSomething(),
73
+ // 'core/comment-template': ({ block }) => doSomething(),
74
+ // 'core/comments': ({ block }) => doSomething(),
75
+ // 'core/comments-pagination': ({ block }) => doSomething(),
76
+ // 'core/comments-pagination-next': ({ block }) => doSomething(),
77
+ // 'core/comments-pagination-numbers': ({ block }) => doSomething(),
78
+ // 'core/comments-pagination-previous': ({ block }) => doSomething(),
79
+ // 'core/comments-title': ({ block }) => doSomething(),
80
+ // 'core/cover': ({ block }) => doSomething(),
81
+ // 'core/embed': ({ block }) => doSomething(),
82
+ // 'core/file': ({ block }) => doSomething(),
83
+ // 'core/freeform': ({ block }) => doSomething(),
84
+ // 'core/gallery': ({ block }) => doSomething(),
85
+ // 'core/group': ({ block }) => doSomething(),
86
+ // 'core/heading': ({ block }) => doSomething(),
87
+ // 'core/home-link': ({ block }) => doSomething(),
88
+ // 'core/html': ({ block }) => doSomething(),
89
+ // 'core/image': ({ block }) => doSomething(),
90
+ // 'core/latest-comments': ({ block }) => doSomething(),
91
+ // 'core/latest-posts': ({ block }) => doSomething(),
92
+ // 'core/list': ({ block }) => doSomething(),
93
+ // 'core/list-item': ({ block }) => doSomething(),
94
+ // 'core/loginout': ({ block }) => doSomething(),
95
+ // 'core/media-text': ({ block }) => doSomething(),
96
+ // 'core/missing': ({ block }) => doSomething(),
97
+ // 'core/more': ({ block }) => doSomething(),
98
+ // 'core/navigation': ({ block }) => doSomething(),
99
+ // 'core/navigation-link': ({ block }) => doSomething(),
100
+ // 'core/navigation-submenu': ({ block }) => doSomething(),
101
+ // 'core/nextpage': ({ block }) => doSomething(),
102
+ // 'core/page-list': ({ block }) => doSomething(),
103
+ // 'core/paragraph': ({ block }) => doSomething(),
104
+ // 'core/pattern': ({ block }) => doSomething(),
105
+ // 'core/post-author': ({ block }) => doSomething(),
106
+ // 'core/post-author-biography': ({ block }) => doSomething(),
107
+ // 'core/post-author-name': ({ block }) => doSomething(),
108
+ // 'core/post-comment': ({ block }) => doSomething(),
109
+ // 'core/post-comments-count': ({ block }) => doSomething(),
110
+ // 'core/post-comments-form': ({ block }) => doSomething(),
111
+ // 'core/post-comments-link': ({ block }) => doSomething(),
112
+ // 'core/post-content': ({ block }) => doSomething(),
113
+ // 'core/post-date': ({ block }) => doSomething(),
114
+ // 'core/post-excerpt': ({ block }) => doSomething(),
115
+ // 'core/post-featured-image': ({ block }) => doSomething(),
116
+ // 'core/post-navigation-link': ({ block }) => doSomething(),
117
+ // 'core/post-template': ({ block }) => doSomething(),
118
+ // 'core/post-terms': ({ block }) => doSomething(),
119
+ // 'core/post-title': ({ block }) => doSomething(),
120
+ // 'core/preformatted': ({ block }) => doSomething(),
121
+ // 'core/pullquote': ({ block }) => doSomething(),
122
+ // 'core/query': ({ block }) => doSomething(),
123
+ // 'core/query-no-results': ({ block }) => doSomething(),
124
+ // 'core/query-pagination': ({ block }) => doSomething(),
125
+ // 'core/query-pagination-next': ({ block }) => doSomething(),
126
+ // 'core/query-pagination-numbers': ({ block }) => doSomething(),
127
+ // 'core/query-pagination-previous': ({ block }) => doSomething(),
128
+ // 'core/query-title': ({ block }) => doSomething(),
129
+ // 'core/quote': ({ block }) => doSomething(),
130
+ // 'core/read-more': ({ block }) => doSomething(),
131
+ // 'core/rss': ({ block }) => doSomething(),
132
+ // 'core/search': ({ block }) => doSomething(),
133
+ // 'core/separator': ({ block }) => doSomething(),
134
+ // 'core/shortcode': ({ block }) => doSomething(),
135
+ // 'core/site-logo': ({ block }) => doSomething(),
136
+ // 'core/site-tagline': ({ block }) => doSomething(),
137
+ // 'core/site-title': ({ block }) => doSomething(),
138
+ // 'core/social-link': ({ block }) => doSomething(),
139
+ // 'core/social-links': ({ block }) => doSomething(),
140
+ // 'core/spacer': ({ block }) => doSomething(),
141
+ // 'core/table': ({ block }) => doSomething(),
142
+ // 'core/table-of-contents': ({ block }) => doSomething(),
143
+ // 'core/tag-cloud': ({ block }) => doSomething(),
144
+ // 'core/template-part': ({ block }) => doSomething(),
145
+ // 'core/term-description': ({ block }) => doSomething(),
146
+ // 'core/text-columns': ({ block }) => doSomething(),
147
+ // 'core/verse': ({ block }) => doSomething(),
148
+ // 'core/video': ({ block }) => doSomething(),
149
+ };
150
+
151
+ /**
152
+ * Helper function to setup custom tags definitions.
153
+ *
154
+ * @param {object} tags - Optional object with custom blocks definitions. Empty by default.
155
+ * @returns {object} Object with blocks definitions
156
+ */
157
+ const customTags = function () {
158
+ let tags = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
159
+ return {
160
+ ...coreTags,
161
+ ...tags
162
+ };
163
+ };
164
+
165
+ /**
166
+ * Helper function to setup custom blocks definitions.
167
+ *
168
+ * @param {object} blocks - Optional object with custom blocks definitions. Empty by default.
169
+ * @param {boolean} useDefaultBlocks - Optional boolean to use core blocks defaults. True by default.
170
+ * @returns {object} Object with blocks definitions
171
+ */
172
+ exports.customTags = customTags;
173
+ const customBlocks = function () {
174
+ let blocks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
175
+ return {
176
+ ...coreBlocks,
177
+ ...blocks
178
+ };
179
+ };
180
+ exports.customBlocks = customBlocks;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = Image;
7
+ function Image(_ref) {
8
+ let {
9
+ attribs
10
+ } = _ref;
11
+ const {
12
+ alt = '',
13
+ class: className = '',
14
+ src,
15
+ height,
16
+ width
17
+ } = attribs;
18
+ return /*#__PURE__*/React.createElement("img", {
19
+ alt: alt,
20
+ className: className,
21
+ src: src,
22
+ width: width,
23
+ height: height
24
+ });
25
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = SelfClosing;
7
+ var _attribsProps = _interopRequireDefault(require("../../utils/attribsProps"));
8
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
+ function SelfClosing(_ref) {
10
+ let {
11
+ attribs,
12
+ tag
13
+ } = _ref;
14
+ const Component = tag;
15
+ const attributes = (0, _attribsProps.default)(attribs);
16
+ return /*#__PURE__*/React.createElement(Component, attributes);
17
+ }
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "Block", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _Block.default;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "Provider", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _Context.Provider;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "Tree", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _Tree.default;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "attribsProps", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _attribsProps.default;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "customBlocks", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _elements.customBlocks;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "customTags", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _elements.customTags;
40
+ }
41
+ });
42
+ Object.defineProperty(exports, "innerNode", {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _innerNode.default;
46
+ }
47
+ });
48
+ Object.defineProperty(exports, "parseBlocks", {
49
+ enumerable: true,
50
+ get: function () {
51
+ return _parseBlocks.default;
52
+ }
53
+ });
54
+ var _Block = _interopRequireDefault(require("./components/Block"));
55
+ var _Tree = _interopRequireDefault(require("./components/Tree"));
56
+ var _Context = require("./components/Context");
57
+ var _elements = require("./elements");
58
+ var _attribsProps = _interopRequireDefault(require("./utils/attribsProps"));
59
+ var _innerNode = _interopRequireDefault(require("./utils/innerNode"));
60
+ var _parseBlocks = _interopRequireDefault(require("./utils/parseBlocks"));
61
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _reactAttrConverter = _interopRequireDefault(require("react-attr-converter"));
8
+ var _styleToJs = _interopRequireDefault(require("style-to-js"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ const attribsProps = attribs => {
11
+ if (attribs === undefined) {
12
+ return {};
13
+ }
14
+ const props = Object.fromEntries(Object.entries(attribs).map(attribute => {
15
+ if (attribute[0] === 'style') {
16
+ return [(0, _reactAttrConverter.default)(attribute[0]), (0, _styleToJs.default)(attribute[1])];
17
+ }
18
+ return [(0, _reactAttrConverter.default)(attribute[0]), attribute[1]];
19
+ }));
20
+ return props;
21
+ };
22
+ var _default = exports.default = attribsProps;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _htmlparser = require("htmlparser2");
8
+ const innerNode = (innerBlocks, innerContent) => {
9
+ var _tree$children$;
10
+ // If no inner blocks, return the block markup.
11
+ // If inner blocks, return the wrapping markup.
12
+ const innerHtml = !innerBlocks.length ? innerContent[0] : "".concat(innerContent[0], "[innerBlocks]").concat(innerContent[innerContent.length - 1]);
13
+ const html = innerHtml ? innerHtml.trim() : "";
14
+ const tree = (0, _htmlparser.parseDocument)(html, {
15
+ lowerCaseTags: true,
16
+ recognizeSelfClosing: true
17
+ });
18
+ return (_tree$children$ = tree.children[0]) !== null && _tree$children$ !== void 0 ? _tree$children$ : null;
19
+ };
20
+ var _default = exports.default = innerNode;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _blockSerializationDefaultParser = require("@wordpress/block-serialization-default-parser");
8
+ var _Block = _interopRequireDefault(require("../components/Block"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ /**
11
+ * Parse Gutenberg blocks from HTML markup.
12
+ *
13
+ * @param {string} html - markup rendered by Gutenberg editor.
14
+ * @returns {JSX.Element[]}
15
+ */
16
+ const parseBlocks = html => (0, _blockSerializationDefaultParser.parse)(html.trim()).map((block, key) => /*#__PURE__*/React.createElement(_Block.default, {
17
+ block: block,
18
+ key: key
19
+ }));
20
+ var _default = exports.default = parseBlocks;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@frontkom/block-react-parser",
3
+ "version": "0.1.0",
4
+ "description": "Gutenberg-generated HTML to React parser.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "scripts": {
8
+ "build": "rm -rf dist && NODE_ENV=production babel src --out-dir dist --copy-files"
9
+ },
10
+ "keywords": [
11
+ "html",
12
+ "react",
13
+ "parser",
14
+ "gutenberg"
15
+ ],
16
+ "author": "Roberto Ornelas <rob@frontkom.com>",
17
+ "license": "ISC",
18
+ "dependencies": {
19
+ "@wordpress/block-serialization-default-parser": "^4.21.0",
20
+ "htmlparser2": "^8.0.1",
21
+ "react-attr-converter": "^0.3.1",
22
+ "style-to-js": "^1.1.1"
23
+ },
24
+ "devDependencies": {
25
+ "@babel/cli": "^7.19.3",
26
+ "@babel/core": "^7.20.2",
27
+ "@babel/preset-env": "^7.20.2",
28
+ "@babel/preset-react": "^7.18.6",
29
+ "react": "^17.0.2",
30
+ "react-dom": "^17.0.2"
31
+ },
32
+ "peerDependencies": {
33
+ "react": "^17.0.2",
34
+ "react-dom": "^17.0.2"
35
+ },
36
+ "babel": {
37
+ "presets": [
38
+ "@babel/preset-env",
39
+ "@babel/preset-react"
40
+ ]
41
+ },
42
+ "browserslist": "> 0.25%, not dead"
43
+ }
@@ -0,0 +1,37 @@
1
+ import Tree from "./Tree";
2
+ import innerNode from "../utils/innerNode";
3
+ import { useBlockComponent } from "./Context";
4
+
5
+ /**
6
+ * Block element.
7
+ *
8
+ * @param {object} componentProps - properties that includes the block object.
9
+ * @returns {JSX.Element | null | undefined}
10
+ */
11
+ export default function Block({ block }) {
12
+ const { blockName = null, innerContent, innerBlocks } = block;
13
+ const CustomBlock = useBlockComponent(blockName);
14
+
15
+ if (CustomBlock) {
16
+ return <CustomBlock block={block} />;
17
+ }
18
+
19
+ // Filter out empty blocks.
20
+ if (!blockName && innerContent.length === 0) {
21
+ return null;
22
+ }
23
+ // Filter out empty lines and orphaned closing tags.
24
+ if (
25
+ innerContent.length === 1 &&
26
+ (innerContent[0] === "\n" || innerContent[0].substring(0, 2) === "</")
27
+ ) {
28
+ return null;
29
+ }
30
+
31
+ const node = innerNode(innerBlocks, innerContent);
32
+ if (node) {
33
+ return <Tree node={node} block={block} />;
34
+ }
35
+
36
+ return null;
37
+ }
@@ -0,0 +1,20 @@
1
+ import { createContext, useContext } from 'react';
2
+ import { coreTags, coreBlocks } from '../elements';
3
+
4
+ const TxContext = createContext();
5
+
6
+ export const { Provider } = TxContext;
7
+
8
+ export function useComponentContext() {
9
+ return useContext(TxContext);
10
+ }
11
+
12
+ export function useBlockComponent(name) {
13
+ const { CustomBlocks = coreBlocks } = useContext(TxContext);
14
+ return name && CustomBlocks[name];
15
+ }
16
+
17
+ export function useTagComponent(tag) {
18
+ const { CustomTags = coreTags } = useContext(TxContext);
19
+ return tag && CustomTags[tag];
20
+ }
@@ -0,0 +1,37 @@
1
+ import { useTagComponent } from "./Context";
2
+ import Block from "./Block";
3
+ import attribsProps from "../utils/attribsProps";
4
+
5
+ export default function Tree({ node, block }) {
6
+ const CustomTag = useTagComponent(node.name);
7
+
8
+ attribsProps(node.attribs);
9
+
10
+ if (node.type === "text") {
11
+ if (node.data === "[innerBlocks]") {
12
+ // eslint-disable-next-line react/no-array-index-key
13
+ return block.innerBlocks?.map((inner, index) => (
14
+ <Block block={inner} key={index} />
15
+ ));
16
+ }
17
+
18
+ return node.data;
19
+ }
20
+
21
+ // Handle selfclosed elements (???)
22
+ if (CustomTag) {
23
+ return <CustomTag attribs={node.attribs} />;
24
+ }
25
+
26
+ const Component = node.name;
27
+ const attrs = attribsProps(node.attribs);
28
+
29
+ return (
30
+ <Component {...attrs}>
31
+ {node.children?.map((child, index) => (
32
+ // eslint-disable-next-line react/no-array-index-key
33
+ <Tree node={child} block={block} key={index} />
34
+ ))}
35
+ </Component>
36
+ );
37
+ }
@@ -0,0 +1,135 @@
1
+ import Image from './tags/img';
2
+ import SelfClosing from './tags/selfClosing';
3
+
4
+ export const coreTags = {
5
+ img: ({ attribs }) => <Image attribs={attribs} />,
6
+ br: ({ attribs }) => <SelfClosing attribs={attribs} tag="br" />,
7
+ hr: ({ attribs }) => <SelfClosing attribs={attribs} tag="hr" />,
8
+ meta: ({ attribs }) => {
9
+ return null;
10
+ },
11
+ // area: ({attribs}) => <SelfClosing attribs={attribs} tag="area" />,
12
+ // base: ({attribs}) => <SelfClosing attribs={attribs} tag="base" />,
13
+ // col: ({attribs}) => <SelfClosing attribs={attribs} tag="col" />,
14
+ // embed: ({attribs}) => <SelfClosing attribs={attribs} tag="embed" />,
15
+ // input: ({attribs}) => <SelfClosing attribs={attribs} tag="input" />,
16
+ // link: ({attribs}) => <SelfClosing attribs={attribs} tag="link" />,
17
+ // meta: ({attribs}) => <SelfClosing attribs={attribs} tag="meta" />,
18
+ // param: ({attribs}) => <SelfClosing attribs={attribs} tag="param" />,
19
+ // source: ({attribs}) => <SelfClosing attribs={attribs} tag="source" />,
20
+ // track: ({attribs}) => <SelfClosing attribs={attribs} tag="track" />,
21
+ // wbr: ({attribs}) => <SelfClosing attribs={attribs} tag="wbr" />,
22
+ };
23
+
24
+ export const coreBlocks = {
25
+ // 'core/archives': ({ block }) => doSomething(),
26
+ // 'core/audio': ({ block }) => doSomething(),
27
+ // 'core/avatar': ({ block }) => doSomething(),
28
+ // 'core/block': ({ block }) => doSomething(),
29
+ // 'core/button': ({ block }) => doSomething(),
30
+ // 'core/buttons': ({ block }) => doSomething(),
31
+ // 'core/calendar': ({ block }) => doSomething(),
32
+ // 'core/categories': ({ block }) => doSomething(),
33
+ // 'core/code': ({ block }) => doSomething(),
34
+ // 'core/column': ({ block }) => <p>Column</p>,
35
+ // 'core/columns': ({ block }) => doSomething(),
36
+ // 'core/comment-author-avatar': ({ block }) => doSomething(),
37
+ // 'core/comment-author-name': ({ block }) => doSomething(),
38
+ // 'core/comment-content': ({ block }) => doSomething(),
39
+ // 'core/comment-date': ({ block }) => doSomething(),
40
+ // 'core/comment-edit-link': ({ block }) => doSomething(),
41
+ // 'core/comment-reply-link': ({ block }) => doSomething(),
42
+ // 'core/comment-template': ({ block }) => doSomething(),
43
+ // 'core/comments': ({ block }) => doSomething(),
44
+ // 'core/comments-pagination': ({ block }) => doSomething(),
45
+ // 'core/comments-pagination-next': ({ block }) => doSomething(),
46
+ // 'core/comments-pagination-numbers': ({ block }) => doSomething(),
47
+ // 'core/comments-pagination-previous': ({ block }) => doSomething(),
48
+ // 'core/comments-title': ({ block }) => doSomething(),
49
+ // 'core/cover': ({ block }) => doSomething(),
50
+ // 'core/embed': ({ block }) => doSomething(),
51
+ // 'core/file': ({ block }) => doSomething(),
52
+ // 'core/freeform': ({ block }) => doSomething(),
53
+ // 'core/gallery': ({ block }) => doSomething(),
54
+ // 'core/group': ({ block }) => doSomething(),
55
+ // 'core/heading': ({ block }) => doSomething(),
56
+ // 'core/home-link': ({ block }) => doSomething(),
57
+ // 'core/html': ({ block }) => doSomething(),
58
+ // 'core/image': ({ block }) => doSomething(),
59
+ // 'core/latest-comments': ({ block }) => doSomething(),
60
+ // 'core/latest-posts': ({ block }) => doSomething(),
61
+ // 'core/list': ({ block }) => doSomething(),
62
+ // 'core/list-item': ({ block }) => doSomething(),
63
+ // 'core/loginout': ({ block }) => doSomething(),
64
+ // 'core/media-text': ({ block }) => doSomething(),
65
+ // 'core/missing': ({ block }) => doSomething(),
66
+ // 'core/more': ({ block }) => doSomething(),
67
+ // 'core/navigation': ({ block }) => doSomething(),
68
+ // 'core/navigation-link': ({ block }) => doSomething(),
69
+ // 'core/navigation-submenu': ({ block }) => doSomething(),
70
+ // 'core/nextpage': ({ block }) => doSomething(),
71
+ // 'core/page-list': ({ block }) => doSomething(),
72
+ // 'core/paragraph': ({ block }) => doSomething(),
73
+ // 'core/pattern': ({ block }) => doSomething(),
74
+ // 'core/post-author': ({ block }) => doSomething(),
75
+ // 'core/post-author-biography': ({ block }) => doSomething(),
76
+ // 'core/post-author-name': ({ block }) => doSomething(),
77
+ // 'core/post-comment': ({ block }) => doSomething(),
78
+ // 'core/post-comments-count': ({ block }) => doSomething(),
79
+ // 'core/post-comments-form': ({ block }) => doSomething(),
80
+ // 'core/post-comments-link': ({ block }) => doSomething(),
81
+ // 'core/post-content': ({ block }) => doSomething(),
82
+ // 'core/post-date': ({ block }) => doSomething(),
83
+ // 'core/post-excerpt': ({ block }) => doSomething(),
84
+ // 'core/post-featured-image': ({ block }) => doSomething(),
85
+ // 'core/post-navigation-link': ({ block }) => doSomething(),
86
+ // 'core/post-template': ({ block }) => doSomething(),
87
+ // 'core/post-terms': ({ block }) => doSomething(),
88
+ // 'core/post-title': ({ block }) => doSomething(),
89
+ // 'core/preformatted': ({ block }) => doSomething(),
90
+ // 'core/pullquote': ({ block }) => doSomething(),
91
+ // 'core/query': ({ block }) => doSomething(),
92
+ // 'core/query-no-results': ({ block }) => doSomething(),
93
+ // 'core/query-pagination': ({ block }) => doSomething(),
94
+ // 'core/query-pagination-next': ({ block }) => doSomething(),
95
+ // 'core/query-pagination-numbers': ({ block }) => doSomething(),
96
+ // 'core/query-pagination-previous': ({ block }) => doSomething(),
97
+ // 'core/query-title': ({ block }) => doSomething(),
98
+ // 'core/quote': ({ block }) => doSomething(),
99
+ // 'core/read-more': ({ block }) => doSomething(),
100
+ // 'core/rss': ({ block }) => doSomething(),
101
+ // 'core/search': ({ block }) => doSomething(),
102
+ // 'core/separator': ({ block }) => doSomething(),
103
+ // 'core/shortcode': ({ block }) => doSomething(),
104
+ // 'core/site-logo': ({ block }) => doSomething(),
105
+ // 'core/site-tagline': ({ block }) => doSomething(),
106
+ // 'core/site-title': ({ block }) => doSomething(),
107
+ // 'core/social-link': ({ block }) => doSomething(),
108
+ // 'core/social-links': ({ block }) => doSomething(),
109
+ // 'core/spacer': ({ block }) => doSomething(),
110
+ // 'core/table': ({ block }) => doSomething(),
111
+ // 'core/table-of-contents': ({ block }) => doSomething(),
112
+ // 'core/tag-cloud': ({ block }) => doSomething(),
113
+ // 'core/template-part': ({ block }) => doSomething(),
114
+ // 'core/term-description': ({ block }) => doSomething(),
115
+ // 'core/text-columns': ({ block }) => doSomething(),
116
+ // 'core/verse': ({ block }) => doSomething(),
117
+ // 'core/video': ({ block }) => doSomething(),
118
+ };
119
+
120
+ /**
121
+ * Helper function to setup custom tags definitions.
122
+ *
123
+ * @param {object} tags - Optional object with custom blocks definitions. Empty by default.
124
+ * @returns {object} Object with blocks definitions
125
+ */
126
+ export const customTags = (tags = {}) => ({ ...coreTags, ...tags });
127
+
128
+ /**
129
+ * Helper function to setup custom blocks definitions.
130
+ *
131
+ * @param {object} blocks - Optional object with custom blocks definitions. Empty by default.
132
+ * @param {boolean} useDefaultBlocks - Optional boolean to use core blocks defaults. True by default.
133
+ * @returns {object} Object with blocks definitions
134
+ */
135
+ export const customBlocks = (blocks = {}) => ({ ...coreBlocks, ...blocks });
@@ -0,0 +1,13 @@
1
+ export default function Image({ attribs }) {
2
+ const { alt = '', class: className = '', src, height, width } = attribs;
3
+
4
+ return (
5
+ <img
6
+ alt={alt}
7
+ className={className}
8
+ src={src}
9
+ width={width}
10
+ height={height}
11
+ />
12
+ );
13
+ }
@@ -0,0 +1,8 @@
1
+ import attribsProps from '../../utils/attribsProps';
2
+
3
+ export default function SelfClosing({ attribs, tag }) {
4
+ const Component = tag;
5
+ const attributes = attribsProps(attribs);
6
+
7
+ return <Component {...attributes} />;
8
+ }
package/src/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { default as Block } from './components/Block';
2
+ export { default as Tree } from './components/Tree';
3
+ export { Provider } from './components/Context';
4
+ export { customBlocks, customTags } from './elements';
5
+ export { default as attribsProps } from './utils/attribsProps';
6
+ export { default as innerNode } from './utils/innerNode';
7
+ export { default as parseBlocks } from './utils/parseBlocks';
@@ -0,0 +1,21 @@
1
+ import convert from 'react-attr-converter';
2
+ import parseStyle from 'style-to-js';
3
+
4
+ const attribsProps = (attribs) => {
5
+ if (attribs === undefined) {
6
+ return {};
7
+ }
8
+
9
+ const props = Object.fromEntries(
10
+ Object.entries(attribs).map((attribute) => {
11
+ if (attribute[0] === 'style') {
12
+ return [convert(attribute[0]), parseStyle(attribute[1])];
13
+ }
14
+ return [convert(attribute[0]), attribute[1]];
15
+ })
16
+ );
17
+
18
+ return props;
19
+ };
20
+
21
+ export default attribsProps;
@@ -0,0 +1,19 @@
1
+ import { parseDocument } from "htmlparser2";
2
+
3
+ const innerNode = (innerBlocks, innerContent) => {
4
+ // If no inner blocks, return the block markup.
5
+ // If inner blocks, return the wrapping markup.
6
+ const innerHtml = !innerBlocks.length
7
+ ? innerContent[0]
8
+ : `${innerContent[0]}[innerBlocks]${innerContent[innerContent.length - 1]}`;
9
+ const html = innerHtml ? innerHtml.trim() : "";
10
+
11
+ const tree = parseDocument(html, {
12
+ lowerCaseTags: true,
13
+ recognizeSelfClosing: true,
14
+ });
15
+
16
+ return tree.children[0] ?? null;
17
+ };
18
+
19
+ export default innerNode;
@@ -0,0 +1,13 @@
1
+ import { parse } from "@wordpress/block-serialization-default-parser";
2
+ import Block from "../components/Block";
3
+
4
+ /**
5
+ * Parse Gutenberg blocks from HTML markup.
6
+ *
7
+ * @param {string} html - markup rendered by Gutenberg editor.
8
+ * @returns {JSX.Element[]}
9
+ */
10
+ const parseBlocks = (html) =>
11
+ parse(html.trim()).map((block, key) => <Block block={block} key={key} />);
12
+
13
+ export default parseBlocks;