PluginProbe
Tableberg – Simple Gutenberg Table Block / 1.1.5
Tableberg – Simple Gutenberg Table Block v1.1.5
1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 trunk 0.0.2 0.2.1 0.3.2 0.3.3 0.4.1 0.5.0 0.5.1 0.5.2 0.5.3 0.5.4 0.5.5 0.5.6 0.5.7 All 42 releases
tableberg / src / blocks / row / index.tsx

index.tsx in Tableberg – Simple Gutenberg Table Block 1.1.5, at src/blocks/row/index.tsx

215 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { ReactNode } from "react";
2 import {
3 InnerBlocks,
4 InspectorControls,
5 store as blockEditorStore,
6 useBlockProps,
7 useInnerBlocksProps,
8 } from "@wordpress/block-editor";
9 import { BlockEditProps, registerBlockType } from "@wordpress/blocks";
10 import { useDispatch, useRegistry } from "@wordpress/data";
11
12 import metadata from "./block.json";
13 import {
14 PanelBody,
15 __experimentalUnitControl as UnitControl,
16 } from "@wordpress/components";
17 import { __ } from "@wordpress/i18n";
18 import { ColorControl, BorderControl } from "@tableberg/components";
19 import blockIcon from "@tableberg/shared/icons/tableberg";
20 import LockedControl from "../../components/LockedControl";
21 import { ColorControlProps } from "../../hooks";
22 import { Border } from "../../attributes";
23
24 const EMPTY_BORDER: Border = { top: "", right: "", bottom: "", left: "" };
25
26 export interface RowBlockAttrs {
27 height?: string;
28 // Pro-owned: declared in the schema for preservation while pro is inactive.
29 // Free never writes it.
30 backgroundColor?: string;
31 border?: Border;
32 }
33
34 /** Everything pro's row background colour control needs. */
35 export interface RowBackgroundContext {
36 rowBackgroundColorControl: ColorControlProps;
37 }
38
39 /** Everything pro's row border control needs. */
40 export interface RowBorderContext {
41 rowBorderControlProps: {
42 label: string;
43 value: Border;
44 hasValue: () => boolean;
45 onChange: (newBorder: Border) => void;
46 onDeselect: () => void;
47 };
48 }
49
50 function RowEdit({
51 clientId,
52 attributes,
53 setAttributes,
54 isSelected,
55 // Injected by the pro plugin; undefined when pro is not installed. A
56 // render function rather than a plain node: claiming priority over a
57 // bulk-applied colour (even/odd, etc.) already sitting on this row's
58 // cells needs the block registry, which pro's editor.BlockEdit HOC
59 // can't reach from outside the tree.
60 ProRowBackgroundContent,
61 // Injected by the pro plugin; undefined when pro is not installed. A
62 // plain node (unlike background) — the row's own border only ever
63 // competes with adjacent borders through the browser's own
64 // border-collapse resolution, so there's no store state to clear.
65 ProRowBorderControl,
66 }: BlockEditProps<RowBlockAttrs> & {
67 ProRowBackgroundContent?: (ctx: RowBackgroundContext) => ReactNode;
68 ProRowBorderControl?: ReactNode;
69 }) {
70 const registry = useRegistry() as any;
71 const { updateBlockAttributes } = useDispatch(blockEditorStore) as any;
72 // The schema keeps pro-owned values while pro is inactive, but the free
73 // editor must not preview behavior the frontend is currently gating out.
74 const proExtensionActive = Boolean(
75 ProRowBackgroundContent || ProRowBorderControl
76 );
77 const border = proExtensionActive
78 ? (attributes.border ?? EMPTY_BORDER)
79 : EMPTY_BORDER;
80 const blockProps = useBlockProps({
81 style: {
82 height: attributes.height || undefined,
83 backgroundColor: proExtensionActive
84 ? attributes.backgroundColor || undefined
85 : undefined,
86 borderTop: border.top || undefined,
87 borderRight: border.right || undefined,
88 borderBottom: border.bottom || undefined,
89 borderLeft: border.left || undefined,
90 },
91 });
92 const innerBlocksProps = useInnerBlocksProps(blockProps, {
93 allowedBlocks: ["tableberg/cell"],
94 renderAppender: false,
95 });
96
97 // A colour already bulk-applied to this row's own cells (even/odd, a
98 // column colour, or an older individual choice) would otherwise sit on
99 // top of the row's own background and hide it, since each cell paints
100 // its own background over the row's. Claiming a colour for the row
101 // clears it back off those cells so the row's colour actually shows.
102 const clearCellBackgrounds = () => {
103 const be = registry.select(blockEditorStore);
104 const cellIds: string[] = (be.getBlock(clientId)?.innerBlocks ?? [])
105 .filter((b: any) => b.name === "tableberg/cell")
106 .map((b: any) => b.clientId);
107
108 registry.batch(() => {
109 cellIds.forEach(id => {
110 const currentStyles = be.getBlockAttributes(id)?.styles ?? {};
111 const { backgroundColor: _legacy, ...styles } = currentStyles;
112 updateBlockAttributes(id, {
113 backgroundColor: null,
114 styles,
115 });
116 });
117 });
118 };
119
120 const rowBackgroundContext: RowBackgroundContext = {
121 rowBackgroundColorControl: {
122 label: __("Row Background Color", "tableberg"),
123 value: attributes.backgroundColor ?? "",
124 onChange: (value: string) => {
125 setAttributes({ backgroundColor: value || undefined });
126 if (value) {
127 clearCellBackgrounds();
128 }
129 },
130 onDeselect: () => setAttributes({ backgroundColor: undefined }),
131 },
132 };
133
134 const rowBorderContext: RowBorderContext = {
135 rowBorderControlProps: {
136 label: __("Row Border", "tableberg"),
137 value: border,
138 hasValue: () =>
139 !!border.top || !!border.right || !!border.bottom || !!border.left,
140 onChange: (newBorder: Border) =>
141 setAttributes({ border: newBorder }),
142 onDeselect: () => setAttributes({ border: undefined }),
143 },
144 };
145
146 return (
147 <>
148 {isSelected && (
149 <>
150 <InspectorControls>
151 <PanelBody title={__("Row Settings", "tableberg")}>
152 <UnitControl
153 label={__("Row Height", "tableberg")}
154 value={attributes.height ?? ""}
155 onChange={(height?: string) =>
156 setAttributes({
157 height: height || undefined,
158 })
159 }
160 />
161 </PanelBody>
162 </InspectorControls>
163 {/*
164 * The colour control renders a ToolsPanel item, so it
165 * only shows up inside a ToolsPanel. The editor's own
166 * "Color" group is one; a plain PanelBody is not.
167 */}
168 <InspectorControls group="color">
169 {ProRowBackgroundContent ? (
170 ProRowBackgroundContent(rowBackgroundContext)
171 ) : (
172 <LockedControl isEnhanced selected="row-bg">
173 <ColorControl
174 label={__("Row Background Color", "tableberg")}
175 value=""
176 onChange={() => null}
177 onDeselect={() => null}
178 />
179 </LockedControl>
180 )}
181 </InspectorControls>
182 {/*
183 * BorderControl renders a ToolsPanelItem too, so it
184 * only shows up inside a ToolsPanel. WordPress's own
185 * "Border" group (Styles tab) is one, same as "color".
186 */}
187 <InspectorControls group="border">
188 {ProRowBorderControl ?? (
189 <LockedControl isEnhanced selected="row-border">
190 <BorderControl
191 label={__("Row Border", "tableberg")}
192 value={EMPTY_BORDER}
193 hasValue={() => false}
194 onChange={() => null}
195 onDeselect={() => null}
196 />
197 </LockedControl>
198 )}
199 </InspectorControls>
200 </>
201 )}
202 <tr {...innerBlocksProps} />
203 </>
204 );
205 }
206
207 export function registerNativeRowBlock() {
208 registerBlockType(metadata.name, {
209 ...(metadata as any),
210 icon: blockIcon,
211 edit: RowEdit,
212 save: () => <InnerBlocks.Content />,
213 });
214 }
215