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 / toggle / index.tsx

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

453 lines 14.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import {
2 useBlockProps,
3 useInnerBlocksProps,
4 store,
5 InnerBlocks,
6 InspectorControls,
7 } from "@wordpress/block-editor";
8 import {
9 BlockEditProps,
10 BlockInstance,
11 registerBlockType,
12 } from "@wordpress/blocks";
13 import metadata from "./block.json";
14 import "./style.scss";
15 import "./editor.scss";
16 import { useSelect, useDispatch } from "@wordpress/data";
17 import { createBlock } from "@wordpress/blocks";
18 import {
19 Button,
20 __experimentalConfirmDialog as ConfirmDialog,
21 TextControl,
22 } from "@wordpress/components";
23 import {
24 positionLeft,
25 positionRight,
26 positionCenter,
27 stretchFullWidth,
28 reset,
29 plus,
30 } from "@wordpress/icons";
31 import { useState } from "react";
32 import { SpacingControlSingle } from "@tableberg/components";
33 import {
34 __experimentalToggleGroupControl as ToggleGroupControl,
35 __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon,
36 __experimentalToolsPanel as ToolsPanel,
37 __experimentalToolsPanelItem as ToolsPanelItem,
38 __experimentalNumberControl as NumberControl,
39 } from "@wordpress/components";
40 import { Icon } from "@wordpress/icons";
41 import { __ } from "@wordpress/i18n";
42 import { ColorControl } from "@tableberg/components";
43 import { getSpacingCssSingle } from "@tableberg/shared/utils/styling-helpers";
44 import blockIcon from "@tableberg/shared/icons/tableberg";
45
46 export interface ToggleBlockTypes {
47 tabs: Array<string>;
48 defaultActiveTabIndex: number;
49 alignment: string;
50 gap: string;
51 tabType: string;
52 activeTabTextColor: string;
53 activeTabBackgroundColor: string;
54 inactiveTabTextColor: string;
55 inactiveTabBackgroundColor: string;
56 tabBorderRadius: string;
57 }
58
59 interface AlignmentControls {
60 icon: JSX.Element;
61 title: string;
62 value: string;
63 }
64
65 const alignmentOptions: Array<AlignmentControls> = [
66 {
67 icon: positionLeft,
68 title: "Align left",
69 value: "left",
70 },
71 {
72 icon: positionCenter,
73 title: "Align center",
74 value: "center",
75 },
76 {
77 icon: positionRight,
78 title: "Align right",
79 value: "right",
80 },
81 {
82 icon: stretchFullWidth,
83 title: "Stretch full width",
84 value: "full",
85 },
86 ];
87
88 function edit({
89 clientId,
90 attributes,
91 setAttributes,
92 isSelected,
93 }: BlockEditProps<ToggleBlockTypes>) {
94 const blockProps = useBlockProps();
95 const { children, ...innerBlocksProps } = useInnerBlocksProps(blockProps, {
96 allowedBlocks: ["tableberg/table"],
97 template: [
98 ["tableberg/table"],
99 ["tableberg/table"],
100 ["tableberg/table"],
101 ],
102 // @ts-ignore
103 renderAppender: false,
104 });
105
106 const {
107 tabs,
108 alignment,
109 defaultActiveTabIndex,
110 gap,
111 activeTabTextColor,
112 inactiveTabTextColor,
113 activeTabBackgroundColor,
114 inactiveTabBackgroundColor,
115 tabBorderRadius,
116 } = attributes;
117
118 const [activeTab, setActiveTab] = useState(defaultActiveTabIndex);
119
120 const { innerBlocks, innerBlocksLength } = useSelect(
121 select => {
122 const innerBlocks = (
123 select(store) as BlockEditorStoreSelectors
124 ).getBlock(clientId)?.innerBlocks;
125 return {
126 innerBlocks,
127 innerBlocksLength: innerBlocks?.length,
128 };
129 },
130 [clientId]
131 );
132
133 const [deleteConfirmDialogIsOpen, setDeleteConfirmDialogIsOpen] =
134 useState(false);
135 const [deleteIndex, setDeleteIndex] = useState(0);
136
137 const { insertBlock, removeBlock } = useDispatch(
138 store
139 ) as unknown as BlockEditorStoreActions;
140
141 function tabAdditionHandler() {
142 insertBlock(
143 createBlock("tableberg/table"),
144 innerBlocksLength,
145 clientId,
146 false
147 );
148
149 setActiveTab(innerBlocksLength!);
150 setAttributes({
151 tabs: [...tabs, "Untitled Tab"],
152 });
153 }
154
155 function removeTabHandler(i: number) {
156 if (!innerBlocks || !innerBlocksLength) {
157 return;
158 }
159
160 removeBlock(innerBlocks[i].clientId, false);
161
162 const newTabs = [...tabs.slice(0, i), ...tabs.slice(i + 1)];
163
164 setActiveTab(0);
165 setAttributes({
166 tabs: newTabs,
167 });
168 }
169
170 function cssTemplate({ clientId }: BlockInstance, i: number) {
171 return `
172 #block-${clientId} {
173 display: ${activeTab === i ? "block" : "none"};
174 }`;
175 }
176
177 const SidebarControls = (
178 <>
179 <InspectorControls>
180 <ToolsPanel
181 label={__("Toggle Settings", "tableberg")}
182 resetAll={() => {}}
183 >
184 <ToolsPanelItem
185 label={__("Default Active Tab")}
186 hasValue={() => true}
187 >
188 <TextControl
189 label="Current Tab Title"
190 value={tabs[activeTab]}
191 onChange={val => {
192 const newTabs = [...tabs];
193 newTabs[activeTab] = val;
194 setAttributes({
195 tabs: newTabs,
196 });
197 }}
198 />
199 <NumberControl
200 label="Default Active Tab"
201 value={defaultActiveTabIndex + 1}
202 onChange={newVal =>
203 setAttributes({
204 defaultActiveTabIndex: Math.max(
205 0,
206 Number(newVal || 1) - 1
207 ),
208 })
209 }
210 min={1}
211 max={tabs.length}
212 />
213 </ToolsPanelItem>
214 <ToolsPanelItem
215 label={__("Tabs alignment", "tableberg")}
216 hasValue={() => true}
217 >
218 <ToggleGroupControl
219 __nextHasNoMarginBottom
220 label={__("Tabs alignment", "tableberg")}
221 value={alignment}
222 onChange={(newAlignment: any) =>
223 setAttributes({
224 alignment: newAlignment,
225 })
226 }
227 >
228 {alignmentOptions.map(({ icon, title, value }) => (
229 <ToggleGroupControlOptionIcon
230 key={value}
231 icon={icon}
232 value={value}
233 label={title}
234 />
235 ))}
236 </ToggleGroupControl>
237 </ToolsPanelItem>
238 </ToolsPanel>
239 </InspectorControls>
240 <InspectorControls group="color">
241 <ColorControl
242 label={__("Active Tab Text Color", "tableberg")}
243 value={activeTabTextColor}
244 onChange={(newColor: string) =>
245 setAttributes({
246 activeTabTextColor: newColor,
247 })
248 }
249 onDeselect={() =>
250 setAttributes({
251 activeTabTextColor: "",
252 })
253 }
254 />
255 <ColorControl
256 label={__("Inactive Tab Text Color", "tableberg")}
257 value={inactiveTabTextColor}
258 onChange={(newColor: string) =>
259 setAttributes({
260 inactiveTabTextColor: newColor,
261 })
262 }
263 onDeselect={() =>
264 setAttributes({
265 inactiveTabTextColor: "",
266 })
267 }
268 />
269 <ColorControl
270 label={__("Active Tab Background Color", "tableberg")}
271 value={activeTabBackgroundColor}
272 onChange={(newColor: string) =>
273 setAttributes({
274 activeTabBackgroundColor: newColor,
275 })
276 }
277 onDeselect={() =>
278 setAttributes({
279 activeTabBackgroundColor: "",
280 })
281 }
282 />
283 <ColorControl
284 label={__("Inactive Tab Background Color", "tableberg")}
285 value={inactiveTabBackgroundColor}
286 onChange={(newColor: string) =>
287 setAttributes({
288 inactiveTabBackgroundColor: newColor,
289 })
290 }
291 onDeselect={() =>
292 setAttributes({
293 inactiveTabBackgroundColor: "",
294 })
295 }
296 />
297 </InspectorControls>
298 <InspectorControls group="dimensions">
299 <ToolsPanelItem
300 label={__("Spacing Under Tabs", "tableberg")}
301 hasValue={() => true}
302 >
303 <SpacingControlSingle
304 label={__("Spacing Under Tabs", "tableberg")}
305 value={gap}
306 onChange={newGap =>
307 setAttributes({
308 gap: newGap,
309 })
310 }
311 />
312 </ToolsPanelItem>
313 </InspectorControls>
314 <InspectorControls group="border">
315 <ToolsPanelItem
316 label={__("Tab Heading Border Radius", "tableberg")}
317 hasValue={() => true}
318 >
319 <SpacingControlSingle
320 label={__("Tab Heading Border Radius", "tableberg")}
321 value={tabBorderRadius}
322 onChange={newValue =>
323 setAttributes({
324 tabBorderRadius: newValue,
325 })
326 }
327 />
328 </ToolsPanelItem>
329 </InspectorControls>
330 </>
331 );
332
333 return (
334 <div {...blockProps} className="toggle-block">
335 {SidebarControls}
336 <div {...innerBlocksProps}>
337 <nav
338 data-toolbar-trigger="true"
339 className={`tab-headings ${alignment}`}
340 style={{ marginBottom: getSpacingCssSingle(gap) }}
341 >
342 {tabs.map((tab, i) => (
343 <ToggleTab
344 key={`${i}-${tab}`}
345 active={activeTab === i}
346 title={tab}
347 onClick={() => setActiveTab(i)}
348 onDelete={() => {
349 setDeleteIndex(i);
350 setDeleteConfirmDialogIsOpen(true);
351 }}
352 styles={{
353 activeTabBackgroundColor,
354 inactiveTabBackgroundColor,
355 activeTabTextColor,
356 inactiveTabTextColor,
357 tabBorderRadius,
358 }}
359 />
360 ))}
361
362 {deleteConfirmDialogIsOpen && (
363 <ConfirmDialog
364 isOpen={deleteConfirmDialogIsOpen}
365 onCancel={() => setDeleteConfirmDialogIsOpen(false)}
366 onConfirm={() => {
367 removeTabHandler(deleteIndex);
368 setDeleteConfirmDialogIsOpen(false);
369 }}
370 >
371 Are you sure you want to delete this tab?
372 </ConfirmDialog>
373 )}
374
375 {isSelected && (
376 <div className="tab-heading-add">
377 <Button icon={plus} onClick={tabAdditionHandler} />
378 </div>
379 )}
380 </nav>
381 <style>{innerBlocks?.map(cssTemplate).join("\n")}</style>
382 <div className="tab-content">
383 <div>
384 <div>{children}</div>
385 </div>
386 </div>
387 </div>
388 </div>
389 );
390 }
391
392 function ToggleTab({
393 title,
394 active = false,
395 styles,
396 onClick,
397 onDelete,
398 }: {
399 title: string;
400 active: boolean;
401 onClick: () => void;
402 onDelete: () => void;
403 styles: {
404 activeTabBackgroundColor: string;
405 inactiveTabBackgroundColor: string;
406 activeTabTextColor: string;
407 inactiveTabTextColor: string;
408 tabBorderRadius: string;
409 };
410 }) {
411 const activeStyles = {
412 backgroundColor: styles.activeTabBackgroundColor,
413 borderRadius: getSpacingCssSingle(styles.tabBorderRadius),
414 color: styles.activeTabTextColor,
415 };
416 const inactiveStyles = {
417 backgroundColor: styles.inactiveTabBackgroundColor,
418 borderRadius: getSpacingCssSingle(styles.tabBorderRadius),
419 color: styles.inactiveTabTextColor,
420 };
421
422 return (
423 <div
424 className={`tab-heading ${active ? "active" : ""}`}
425 onClick={onClick}
426 data-toolbar-trigger="true"
427 style={active ? activeStyles : inactiveStyles}
428 >
429 <p tabIndex={0}>{title}</p>
430 <button className="tab-heading-remove" onClick={onDelete}>
431 <Icon icon={reset} />
432 </button>
433 </div>
434 );
435 }
436
437 function save() {
438 const blockProps = useBlockProps.save();
439
440 return (
441 <div {...blockProps}>
442 <InnerBlocks.Content />
443 </div>
444 );
445 }
446
447 registerBlockType(metadata as any, {
448 attributes: metadata.attributes as any,
449 icon: blockIcon,
450 edit,
451 save,
452 });
453