Skip to content

Commit

Permalink
fix(collapse) - Add table to allowed blocks
Browse files Browse the repository at this point in the history
Resolves #173 - WiP #172
  • Loading branch information
lukasniebler committed Nov 28, 2024
1 parent 69db369 commit e7a2486
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 113 deletions.
2 changes: 1 addition & 1 deletion build/accordion/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '6d5d0dc82102893446ab');
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '08ab4b35aa99a71e2aa3');
2 changes: 1 addition & 1 deletion build/accordion/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/collapse/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'd5953ccb60e947d44a49');
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '2892d918579cf64448d5');
4 changes: 2 additions & 2 deletions build/collapse/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/collapsibles/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n', 'wp-primitives', 'wp-shortcode'), 'version' => '8aa9bb6009ee811a5471');
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n', 'wp-primitives'), 'version' => '90e46fc1aa0cc4bf4ef6');
2 changes: 1 addition & 1 deletion build/collapsibles/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/iconbox/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-keycodes', 'wp-primitives'), 'version' => '5c3ebeead1a2e4a95098');
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-keycodes', 'wp-primitives'), 'version' => '982e5ca1e34355fc0248');
2 changes: 1 addition & 1 deletion build/iconbox/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/tab/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '0aeb401a5f9b79655d02');
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'ff64fec09da5e24fe02c');
2 changes: 1 addition & 1 deletion build/tab/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/tabs/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('lodash', 'react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '84525967a6a190648b94');
<?php return array('dependencies' => array('lodash', 'react-jsx-runtime', 'wp-a11y', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '1d941c4d6842e35d61ee');
2 changes: 1 addition & 1 deletion build/tabs/index.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/collapse/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ const Edit= ({
"core/rss",
"core/search",
"core/tag-cloud",
"core/table",
"rrze-elements/alert",
"rrze/rrze-video",
]}
Expand Down
309 changes: 209 additions & 100 deletions src/collapsibles/transforms.ts
Original file line number Diff line number Diff line change
@@ -1,76 +1,38 @@
import { regexp, next, attrs, ShortcodeMatch } from "@wordpress/shortcode";
import { createBlock } from "@wordpress/blocks";
import { BlockInstance } from "@wordpress/blocks";
import { parse } from "path";
import iconJson from "../components/assets/fontawesome/fontawesomeIconNames.json";

/**
* Utility function to strip wpAutop generated HTML tags.
* Helper Functions
*/
function stripHTML(html: string): string {
let stripped = html.replace(/<\/?p>/g, "");
stripped = stripped.replace(/<br\s*\/?>/g, "");

return stripped.trim();
}

interface ShortcodeMatches {
attributes: {
named: {
[key: string]: string | boolean | undefined;
};
numeric: number[];
};
content: string;
}

interface ParsedShortcode {
attributes: {
named: { [key: string]: string | boolean | undefined };
numeric: number[];
};
content: string;
}

/**
* Function to parse an array of ShortcodeMatch objects based on the provided shortcode tag.
*/
const parseShortcodeMatches = (shortcodeTag: string, content: string): ParsedShortcode[] => {
const matchRegex: RegExp = regexp(shortcodeTag);
const matches: string[] = content.match(matchRegex) || [];
const parsedMatches: ParsedShortcode[] = [];

matches.forEach((match) => {
console.log("The match: ");
console.log(match);

const attrMatch = match.match(new RegExp(`${shortcodeTag}\\s+([^\\]]+)`));
const contentMatch = match.match(new RegExp(`\\]([\\s\\S]*?)\\[\\/${shortcodeTag}\\]`)); // Dynamic closing tag

const parsedAttributes = attrMatch && attrMatch[1]
? (attrs(attrMatch[1]) as unknown as ShortcodeMatches["attributes"])
: { named: {}, numeric: [] };

const numericAttributes = (parsedAttributes.numeric || []).map((value) => {
if (typeof value === "string") {
const numberValue = parseFloat(value);
return isNaN(numberValue) ? 0 : numberValue;
}
return value;
});

const innerContent = contentMatch ? contentMatch[1] : "";

parsedMatches.push({
attributes: {
named: parsedAttributes.named,
numeric: numericAttributes,
},
content: innerContent,
});
});

return parsedMatches;
};
function validateIcon(iconStr: string) {
// Splitting the string to see if it has a prefix.
if (iconStr === undefined) return "";

const parts = iconStr.split(" ");

let prefix, iconName;

if (parts.length === 1) {
// If only icon name is provided, use "solid" as the default prefix.
prefix = "solid";
iconName = parts[0];
} else if (parts.length === 2) {
prefix = parts[0];
iconName = parts[1];
} else {
// Invalid icon string format
return null;
}

if (["brands", "regular", "solid"].includes(prefix)) {
const key: keyof typeof iconJson = prefix as any;
if (iconJson[key].includes(iconName)) {
return `${prefix} ${iconName}`;
}
}

return "";
}

const transforms = {
from: [
Expand All @@ -86,37 +48,184 @@ const transforms = {
},
},
priority: 1,
transform: (attributes: any, data: any): BlockInstance[] => {
console.log("attributes: ", attributes);
console.log("data: ", data);
console.log("content: ", data.shortcode.content);

const exampleContent = data.shortcode.content || "";

const collapseExample = parseShortcodeMatches("collapse", exampleContent);
console.log("The collapse example: ");
console.log(collapseExample);
console.log("The value fed to parseShortcodeMatches: ");
console.log(collapseExample[0].content);

console.log("The parsed accordion : ");
const accordionExample = parseShortcodeMatches("accordion", collapseExample[0].content);
console.log(accordionExample);

const exampleAccordion = `[accordion]
[accordion-item title="Beispiel 2"]
Etwas Text
[/accordion-item]
[accordion-item title="Beispiel 3"]
Etwas Text
[/accordion-item]
[/accordion]
`;

console.log("The modified accordion parsed :")
console.log(parseShortcodeMatches("accordion", exampleAccordion));

return [];
transform: (attributes: any, data: any) => {
let blocks = [];
const globalInnerBlocks: any[] = [];

const cleanData = data.shortcode?.content.replace(/<\/?p>/g, "");
const regexCollapse =
/\[collapse(?=\s)((?:\s+\w+=(?:'[^']*'|"[^"]*"|“[^”]*”))*)\]([\s\S]*?)\[\/collapse\]/g;
const matchesCollapseContent = [
...cleanData.matchAll(regexCollapse),
];
let titleStore: { title: string; type: string; level: number; items?: any[] }[] = [];
console.log(data);
const originalContent = data?.content || "";

matchesCollapseContent.forEach((match, collapseIndex) => {
const collapseAttributesString = match[1];
const attributesRegex = /(\w+)="([^"]*)"/g;
let attributeMatches;
let collapseAttributes: { [key: string]: string } = {};
while (
(attributeMatches = attributesRegex.exec(
collapseAttributesString
)) !== null
) {
const key = attributeMatches[1];
const value = attributeMatches[2];
collapseAttributes[key] = value;
}

const contentInsideCollapse = match[2].trim();

let collapseInnerBlocks: any[] = [];
let accordionTitles: { title: string; type: string; level: number }[] = [];

const accordionRegex =
/\[accordion(?=\s|\])(?:\s+\w+="[^"]*")*\]([\s\S]*?)\[\/accordion\]/g;
const splitContents = contentInsideCollapse.split(accordionRegex);
splitContents.forEach((splitContent: string, index: number) => {
if (index % 2 === 0) {
// This should be freeform content outside of the accordions
if (splitContent.trim()) {
collapseInnerBlocks.push(
createBlock("core/freeform", {
content: splitContent.trim(),
})
);
}
} else {
// This should be content inside an accordion
const accordionItemsRegex =
/\[accordion-item(?=\s)((?:\s+\w+=(?:'[^']*'|"[^"]*"|“[^”]*”))*)\]([\s\S]*?)\[\/accordion-item\]/g;
const accordionItemMatches = [
...splitContent.matchAll(accordionItemsRegex),
];

let innerAccordionBlocks: any = [];

accordionItemMatches.forEach((accordionItem) => {
const accordionAttributesString = accordionItem[1];
const accordionContent = accordionItem[2].trim();

const accordionAttributeMatches =
accordionAttributesString.match(
/(\w+)=('[^']*'|"[^"]*"|“[^”]*”)/g
);
let accordionAttributes: { [key: string]: string } = {};

accordionAttributeMatches?.forEach((attr) => {
const [key, fullValue] = attr.split("=");
const actualValue = fullValue.slice(1, -1);
accordionAttributes[key] = actualValue; // <-- Populate the object correctly
});

accordionTitles.push(
{
title: accordionAttributes.title || "No title detected",
type: "accordion",
level: 2
}
);

innerAccordionBlocks.push(
createBlock(
"rrze-elements/accordion",
{ title: accordionAttributes.title || "Enter a title" },
[
createBlock("core/freeform", {
content: accordionContent,
}),
]
)
);
});

if (innerAccordionBlocks.length) {
collapseInnerBlocks.push(
createBlock(
"rrze-elements/accordions",
{},
innerAccordionBlocks
)
);
}
}
});

const colorChoice = (color: string) => {
switch (color) {
case "tf":
return "tf";
case "nat":
return "nat";
case "phil":
return "phil";
case "med":
return "med";
case "rw":
return "rw";
default:
return "";
}
};

titleStore.push({
title: collapseAttributes.title || `Collapse #${collapseIndex + 1}`,
type: "collapse",
level: 1,
items: accordionTitles,
});

globalInnerBlocks.push(
createBlock(
"rrze-elements/collapse",
{
title: collapseAttributes.title || "Enter a title",
color: colorChoice(collapseAttributes.color),
jumpName: collapseAttributes.name || "",
icon: validateIcon(collapseAttributes.icon) || "",
},
collapseInnerBlocks
)
);
});

const hstart = parseInt(attributes.named.hstart, 10) || 2;
blocks.push(
createBlock(
"rrze-elements/collapsibles",
{ hstart: hstart },
globalInnerBlocks
)
);

// Create the list of titles with bullet points
const formatTitles = (items: any[], level = 0): string =>
items
.map((item) => `${' '.repeat(level)}${item.title}\n${item.items ? formatTitles(item.items, level + 1) : ''}`)
.join("");

const titleList: string = formatTitles(titleStore);

// Ask user if they want to proceed and show the array of titles as bullet points
const proceed: boolean = confirm(
`Wichtiger Hinweis\n\nBitte überprüfen Sie Ihre Akkordeonstruktur, um sicherzustellen, dass alle Elemente vorhanden sind.\n\n${titleList}\n\nBestätigen Sie mit Ok, damit die Umwandlung in einen Block durchgeführt wird.`
);

if (!proceed) {
// else return it in a freeformblock
// empty blocks
blocks = [];
blocks.push(
createBlock("core/freeform", {
content: originalContent,
})
);
}

return blocks;
},
},
],
Expand Down

0 comments on commit e7a2486

Please sign in to comment.