PluginProbe
Tableberg – Simple Gutenberg Table Block / 1.2.0
Tableberg – Simple Gutenberg Table Block v1.2.0
1.2.0 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 All 43 releases
tableberg / src / blocks / cell / row-column-background.tsx

row-column-background.tsx in Tableberg – Simple Gutenberg Table Block 1.2.0, at src/blocks/cell/row-column-background.tsx

159 lines 6.1 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 InspectorControls,
4 store as blockEditorStore,
5 } from "@wordpress/block-editor";
6 import { useDispatch, useRegistry, useSelect } from "@wordpress/data";
7 import { __ } from "@wordpress/i18n";
8 import { ColorControl } from "@tableberg/components";
9
10 import { ColorControlProps } from "../../hooks";
11 import LockedControl from "../../components/LockedControl";
12 import { buildOccupancy, cellColumn, GridRow } from "../table/grid-model";
13
14 /**
15 * Column background colour — bulk-apply the picked colour to every cell in
16 * the selected cell's column, mirroring `CellDimensionsControls` (column
17 * width is also set from the cell you select, since that's what the user
18 * has in hand — a column has no block of its own, unlike a row). This
19 * doesn't change a shared attribute: it writes each target cell's own
20 * `backgroundColor` — the exact same attribute (and legacy-migration
21 * behaviour) the per-cell background control already reads and renders, so
22 * the two never disagree about which key holds the cell's colour.
23 */
24 export interface ColumnBackgroundContext {
25 columnBackgroundColorControl: ColorControlProps;
26 }
27
28 export function CellColumnBackgroundControls({
29 clientId,
30 cellDefaultsBackgroundColor,
31 // Injected by the pro plugin; undefined when pro is not installed. A
32 // render function rather than a plain node: resolving the column's
33 // sibling cells and bulk-writing their styles needs the block registry,
34 // which pro's editor.BlockEdit HOC can't reach from outside the tree.
35 ProColumnBackgroundContent,
36 }: {
37 clientId: string;
38 cellDefaultsBackgroundColor: string;
39 ProColumnBackgroundContent?: (ctx: ColumnBackgroundContext) => ReactNode;
40 }) {
41 const { updateBlockAttributes } = useDispatch(blockEditorStore) as any;
42 const registry = useRegistry() as any;
43
44 const info = useSelect(
45 select => {
46 const be = select(blockEditorStore) as any;
47 const rowClientId = be.getBlockRootClientId(clientId);
48 const tableClientId = rowClientId
49 ? be.getBlockRootClientId(rowClientId)
50 : null;
51
52 if (!rowClientId || !tableClientId) {
53 return null;
54 }
55
56 const rows: GridRow[] = (
57 be.getBlock(tableClientId)?.innerBlocks ?? []
58 )
59 .filter((b: any) => b.name === "tableberg/row")
60 .map((rowBlock: any) =>
61 rowBlock.innerBlocks
62 .filter((b: any) => b.name === "tableberg/cell")
63 .map((cellBlock: any) => ({
64 id: cellBlock.clientId,
65 rowSpan: cellBlock.attributes?.span?.rowSpan ?? 1,
66 colSpan: cellBlock.attributes?.span?.colSpan ?? 1,
67 }))
68 );
69
70 const column = cellColumn(rows, clientId);
71 let columnCellIds: string[] = [];
72 if (column !== null) {
73 const occupancy = buildOccupancy(rows);
74 const seen = new Set<string>();
75 for (let r = 0; r < rows.length; r++) {
76 const id = occupancy.matrix[r]?.[column];
77 if (id && !seen.has(id)) {
78 seen.add(id);
79 columnCellIds.push(id);
80 }
81 }
82 }
83
84 const getResolvedColor = (id: string) => {
85 const cellAttrs = be.getBlockAttributes(id);
86 // New attribute first, legacy `styles.backgroundColor`
87 // fallback for cells edited before it existed — same
88 // resolution the per-cell background control uses.
89 const cellColor = (cellAttrs?.backgroundColor ??
90 cellAttrs?.styles?.backgroundColor) as
91 | string
92 | undefined;
93 return cellColor && cellColor.trim() !== ""
94 ? cellColor
95 : cellDefaultsBackgroundColor;
96 };
97
98 let columnColor: string | undefined;
99 if (columnCellIds.length > 0) {
100 const [first, ...rest] = columnCellIds.map(getResolvedColor);
101 columnColor = rest.every(color => color === first)
102 ? first
103 : undefined;
104 }
105
106 return { columnCellIds, columnColor };
107 },
108 [clientId, cellDefaultsBackgroundColor]
109 );
110
111 if (!info) {
112 return null;
113 }
114
115 const applyToCells = (backgroundColor: string) => {
116 const be = registry.select(blockEditorStore);
117 registry.batch(() => {
118 info.columnCellIds.forEach(id => {
119 const currentStyles = be.getBlockAttributes(id)?.styles ?? {};
120 // Write the canonical attribute and drop the legacy key, so
121 // a cell never ends up with both disagreeing.
122 const { backgroundColor: _legacy, ...styles } = currentStyles;
123 updateBlockAttributes(id, {
124 backgroundColor: backgroundColor || null,
125 styles,
126 });
127 });
128 });
129 };
130
131 const context: ColumnBackgroundContext = {
132 columnBackgroundColorControl: {
133 label: __("Column Background Color", "tableberg"),
134 value: info.columnColor,
135 onChange: (color: string) => applyToCells(color),
136 onDeselect: () => applyToCells(""),
137 },
138 };
139
140 return (
141 <InspectorControls group="color">
142 {/*
143 * The colour control renders a ToolsPanel item, so it only shows
144 * up inside a ToolsPanel. The editor's own "Color" group is one;
145 * a plain PanelBody is not.
146 */}
147 {ProColumnBackgroundContent ? (
148 ProColumnBackgroundContent(context)
149 ) : (
150 <LockedControl isEnhanced selected="col-bg">
151 <ColorControl
152 {...context.columnBackgroundColorControl}
153 />
154 </LockedControl>
155 )}
156 </InspectorControls>
157 );
158 }
159