From 7b7237f14b2787cf2f53811f7bec23f44ca7c5c7 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Mon, 24 Feb 2025 17:23:16 +0100
Subject: [PATCH 01/14] blocks

---
 src/AlignButtons/AlignButtons.jsx           |  40 ++
 src/BlockPlaceholders/BlockPlaceholders.jsx |  21 +
 src/BlockPlaceholders/Placeholder.jsx       |  14 +
 src/ColorSelector/ColorSelector.jsx         |  63 +++
 src/DuoInput/DuoInput.jsx                   |  43 ++
 src/FlexibleTable/FlexibleTable.jsx         | 426 ++++++++++++++++++++
 src/FontStyles/FontStyles.jsx               | 103 +++++
 src/NumberWithUnit/NumberWithUnit.jsx       |  69 ++++
 src/QuatroInput/QuatroInput.jsx             |  87 ++++
 9 files changed, 866 insertions(+)
 create mode 100644 src/AlignButtons/AlignButtons.jsx
 create mode 100644 src/BlockPlaceholders/BlockPlaceholders.jsx
 create mode 100644 src/BlockPlaceholders/Placeholder.jsx
 create mode 100644 src/ColorSelector/ColorSelector.jsx
 create mode 100644 src/DuoInput/DuoInput.jsx
 create mode 100644 src/FlexibleTable/FlexibleTable.jsx
 create mode 100644 src/FontStyles/FontStyles.jsx
 create mode 100644 src/NumberWithUnit/NumberWithUnit.jsx
 create mode 100644 src/QuatroInput/QuatroInput.jsx

diff --git a/src/AlignButtons/AlignButtons.jsx b/src/AlignButtons/AlignButtons.jsx
new file mode 100644
index 0000000..8a6ea95
--- /dev/null
+++ b/src/AlignButtons/AlignButtons.jsx
@@ -0,0 +1,40 @@
+import {Button} from "@wordpress/components";
+function AlignButtons(props) {
+
+	const {label, onChange} = props;
+
+	const handleChange = function (newAlignment) {
+		if (onChange) {
+			onChange(newAlignment);
+		}
+	}
+
+	return (
+		<div className='fi-field-wrapper'>
+			<div className='fi-field-label-wrapper'>
+				<span className='fi-field-label'>{label}</span>
+			</div>
+			<div className='fi-field-buttons-wrapper fi-alignbuttons-wrapper'>
+				<Button
+					icon="editor-alignleft"
+					className="has-dashicons"
+					onClick={() => handleChange('left')}
+				/>
+				<Button
+					icon="editor-aligncenter"
+					className="has-dashicons"
+					onClick={() => handleChange('center')}
+				/>
+				<Button
+					icon="editor-alignright"
+					className="has-dashicons"
+					onClick={() => handleChange('right')}
+				/>
+			</div>
+		</div>
+	);
+}
+
+export default AlignButtons;
+
+
diff --git a/src/BlockPlaceholders/BlockPlaceholders.jsx b/src/BlockPlaceholders/BlockPlaceholders.jsx
new file mode 100644
index 0000000..e47a239
--- /dev/null
+++ b/src/BlockPlaceholders/BlockPlaceholders.jsx
@@ -0,0 +1,21 @@
+import {__} from "@wordpress/i18n";
+import {PanelBody} from "@wordpress/components";
+import Placeholder from "./Placeholder";
+
+function BlockPlaceholders(props) {
+	const {placeholders,children} = props;
+	return (
+		<PanelBody title={__('Placeholders', 'flexible-invoices-core')}>
+			{ children && <p className='fi-placeholders-list-description'>{children}</p> }
+			{
+				placeholders.map((placeholder) => {
+					return <Placeholder label={placeholder.label}>{placeholder.description}</Placeholder>
+				})
+			}
+		</PanelBody>
+	);
+}
+
+export default BlockPlaceholders;
+
+
diff --git a/src/BlockPlaceholders/Placeholder.jsx b/src/BlockPlaceholders/Placeholder.jsx
new file mode 100644
index 0000000..ca2e902
--- /dev/null
+++ b/src/BlockPlaceholders/Placeholder.jsx
@@ -0,0 +1,14 @@
+function Placeholder(props) {
+	const {label, children} = props;
+	return (
+		<div>
+			<input type='text' disabled value={label}/>
+			<p className='fi-placeholders-list-description'>{children}</p>
+			<hr/>
+		</div>
+	);
+}
+
+export default Placeholder;
+
+
diff --git a/src/ColorSelector/ColorSelector.jsx b/src/ColorSelector/ColorSelector.jsx
new file mode 100644
index 0000000..021c497
--- /dev/null
+++ b/src/ColorSelector/ColorSelector.jsx
@@ -0,0 +1,63 @@
+import {ColorPicker} from "@wordpress/components";
+import {useState} from "react";
+
+function ColorSelector(props) {
+
+	const {label, onChange, value, enableAlpha = true, returnFormat = 'rgb'} = props;
+	const [isPopupActive, setIsPopupActive] = useState(false);
+
+	const handleColorChange = function (newColor) {
+		let color = newColor;
+		if (returnFormat === 'hex') {
+			color = newColor.hex;
+		} else if (returnFormat === 'hsv') {
+			color = newColor.hsv;
+		} else if (returnFormat === 'hsl') {
+			color = newColor.hsl;
+		} else if (returnFormat === 'rgb') {
+			if (enableAlpha === true) {
+				color = `rgba(${newColor.rgb.r}, ${newColor.rgb.g}, ${newColor.rgb.b}, ${newColor.rgb.a})`;
+			} else {
+				color = `rgb(${newColor.rgb.r}, ${newColor.rgb.g}, ${newColor.rgb.b})`;
+			}
+		}
+
+
+		onChange(color);
+	}
+
+	return (
+		<div className='fi-field-wrapper'>
+			<div className='fi-field-label-wrapper'>
+				<span className='fi-field-label'>{label}</span>
+			</div>
+			<div className='fi-field-buttons-wrapper fi-colorpicker-wrapper fi-input-with-columns' style={{
+				width: '216px', //Width of the ColorPicker element
+				border: '1px solid #8c8f94',
+				borderRadius: '0px',
+			}}>
+				<div className='fi-color-preview' style={{
+					background: value,
+					height: '32px',
+					width: '100%'
+				}}
+					 onClick={() => {
+						 setIsPopupActive(!isPopupActive)
+					 }}
+				></div>
+			</div>
+			{isPopupActive &&
+				<div className='fi-field-buttons-wrapper fi-colorpicker-wrapper'>
+					<ColorPicker
+						enableAlpha={enableAlpha}
+						color={value}
+						onChangeComplete={handleColorChange}
+					/>
+				</div>}
+		</div>
+	);
+}
+
+export default ColorSelector;
+
+
diff --git a/src/DuoInput/DuoInput.jsx b/src/DuoInput/DuoInput.jsx
new file mode 100644
index 0000000..a497004
--- /dev/null
+++ b/src/DuoInput/DuoInput.jsx
@@ -0,0 +1,43 @@
+import NumberWithUnit from "../NumberWithUnit/NumberWithUnit";
+
+function DuoInput(props) {
+
+	const {label, onChange, values, layout = 'column'} = props;
+
+
+	const keys = Object.keys(values);
+	const valuesArray = Object.values(values);
+	const handleChange = (side, value) => {
+		const changedSide = keys[side];
+		const newValues = {...values, [changedSide]: value};
+
+		if (onChange) {
+			onChange(newValues);
+		}
+	};
+	return (
+		<div className='fi-field-wrapper'>
+			<div className='fi-field-label-wrapper'>
+				<span className='fi-field-label'>{label}</span>
+			</div>
+				<div className='fi-field-buttons-wrapper fi-duo-fields-wrapper' style={{flexDirection: layout }}>
+					<div className='fi-duo-fields'>
+						<NumberWithUnit
+							className='fi-duo-field fi-duo-field-input'
+							value={valuesArray[0]}
+							onChange={(e) => {
+								handleChange(0, e)
+							}}
+						/>
+						<NumberWithUnit
+							className='fi-duo-field fi-duo-field-input'
+							value={valuesArray[1]}
+							onChange={(e) => handleChange(1, e)}
+						/>
+					</div>
+				</div>
+		</div>
+	);
+}
+
+export default DuoInput;
diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
new file mode 100644
index 0000000..6b8b090
--- /dev/null
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -0,0 +1,426 @@
+import {__} from '@wordpress/i18n';
+import {
+	RichText,
+	InspectorControls,
+	useBlockProps,
+} from '@wordpress/block-editor';
+import {
+	PanelBody,
+	ToggleControl,
+} from '@wordpress/components';
+import {DragDropContext, Draggable, Droppable} from "react-beautiful-dnd";
+import ColorSelector from "../ColorSelector/ColorSelector";
+import NumberWithUnit from "../NumberWithUnit/NumberWithUnit";
+import AlignButtons from "../AlignButtons/AlignButtons";
+import FontStyles from "../FontStyles/FontStyles";
+import QuatroInput from "../QuatroInput/QuatroInput";
+import BlockPlaceholders from "../BlockPlaceholders/BlockPlaceholders";
+
+export default function Edit({attributes, setAttributes, placeholders, footer, additionalOptions }) {
+	const {
+		headers,
+		rows,
+		states,
+
+		headerBackground,
+		headerTextColor,
+		headerTextAlign,
+		headerFontWeight,
+		headerFontStyle,
+		headerTextDecoration,
+		headerFontSize,
+
+		headerBorderRowWidthTop,
+		headerBorderRowWidthLeft,
+		headerBorderRowWidthRight,
+		headerBorderRowWidthBottom,
+		headerBorderRowColor,
+		headerBorderCellWidthTop,
+		headerBorderCellWidthLeft,
+		headerBorderCellWidthRight,
+		headerBorderCellWidthBottom,
+		headerBorderCellColor,
+
+		bodyBackground,
+		multipleBodyBackground,
+		bodyBackgroundOdd,
+		bodyTextColor,
+		bodyTextAlign,
+		bodyFontWeight,
+		bodyFontStyle,
+		bodyTextDecoration,
+		bodyFontSize,
+
+		bodyBorderRowWidthTop,
+		bodyBorderRowWidthLeft,
+		bodyBorderRowWidthRight,
+		bodyBorderRowWidthBottom,
+		bodyBorderRowColor,
+		bodyBorderCellWidthTop,
+		bodyBorderCellWidthLeft,
+		bodyBorderCellWidthRight,
+		bodyBorderCellWidthBottom,
+		bodyBorderCellColor,
+
+	} = attributes;
+
+	const blockProps = useBlockProps();
+
+	const handleMultipleAttributesChange = (newValues) => {
+		setAttributes({...attributes, ...newValues});
+	}
+
+	const updateHeader = (index, value) => {
+		const newHeaders = [...headers];
+		newHeaders[index] = value;
+		setAttributes({headers: newHeaders});
+	};
+
+	const updateCell = (index, value) => {
+		const newRows = [...rows];
+		newRows[index] = value;
+		setAttributes({rows: newRows});
+	};
+
+	const toggleColumn = (index, value) => {
+		const newStates = [...states];
+		newStates[index] = value;
+		setAttributes({states: newStates});
+	};
+
+	const handleDragEnd = (result) => {
+		if (!result.destination) {
+			return;
+		}
+
+		const {source, destination} = result;
+		const newHeaders = [...headers];
+		const newStates = [...states];
+		const newRows = [...rows];
+
+		const [movedHeader] = newHeaders.splice(source.index, 1);
+		newHeaders.splice(destination.index, 0, movedHeader);
+
+		const [movedState] = newStates.splice(source.index, 1);
+		newStates.splice(destination.index, 0, movedState);
+
+		const [movedRow] = newRows.splice(source.index, 1);
+		newRows.splice(destination.index, 0, movedRow);
+
+		setAttributes({
+			headers: newHeaders,
+			states: newStates,
+			rows: newRows
+		});
+	};
+
+	return (
+		<>
+			<InspectorControls>
+				<PanelBody title={__("Table columns", "flexible-invoices-core")}>
+					<DragDropContext onDragEnd={handleDragEnd}>
+						<Droppable droppableId="table-columns">
+							{(provided) => (
+								<ul
+									{...provided.droppableProps}
+									ref={provided.innerRef}
+									style={{padding: 0, listStyle: "none"}}
+								>
+									{headers.map((header, index) => (
+										<Draggable key={header} draggableId={header} index={index}>
+											{(provided) => (
+												<li
+													ref={provided.innerRef}
+													{...provided.draggableProps}
+													{...provided.dragHandleProps}
+													className="fi-settings-toggle"
+													style={{
+														...provided.draggableProps.style,
+													}}
+												>
+													<div className="fi-settings-toggle-header">
+														<span className="dashicons dashicons-ellipsis"></span>
+														<span>{header}</span>
+													</div>
+													<ToggleControl
+														checked={states[index]}
+														onChange={(newValue) => toggleColumn(index, newValue)}
+													/>
+												</li>
+											)}
+										</Draggable>
+									))}
+									{provided.placeholder}
+								</ul>
+							)}
+						</Droppable>
+					</DragDropContext>
+				</PanelBody>
+				<PanelBody title={__('Header appearance', 'flexible-invoices-core')}>
+					<ColorSelector
+						label={__('Text Color', 'flexible-invoices-core')}
+						value={headerTextColor}
+						enableAlpha={false}
+						onChange={(value) => setAttributes({headerTextColor: value})}
+					/>
+					<ColorSelector
+						label={__('Background Color', 'flexible-invoices-core')}
+						value={headerBackground}
+						onChange={(value) => setAttributes({headerBackground: value})}
+					/>
+					<NumberWithUnit
+						label={__('Font Size', 'flexible-invoices-core')}
+						value={headerFontSize}
+						onChange={(value) => setAttributes({headerFontSize: value})}
+					/>
+					<AlignButtons
+						label={__('Text Align', 'flexible-invoices-core')}
+						onChange={(value) => setAttributes({headerTextAlign: value})}
+					/>
+					<FontStyles
+						label={__('Font style', 'flexible-invoices-core')}
+						onChange={(newFontStyles) => setAttributes({
+							...attributes,
+							headerFontStyle: newFontStyles.fontStyle,
+							headerFontWeight: newFontStyles.fontWeight,
+							headerTextDecoration: newFontStyles.textDecoration
+						})}
+						values={
+							{
+								fontStyle: headerFontStyle,
+								fontWeight: headerFontWeight,
+								textDecoration: headerTextDecoration
+							}
+						}
+					/>
+					<ColorSelector
+						label={__('Border row color', 'flexible-invoices-core')}
+						value={headerBorderRowColor}
+						enableAlpha={false}
+						returnFormat={'hex'}
+						onChange={(value) => setAttributes({headerBorderRowColor: value})}
+					/>
+					<QuatroInput
+						label={__("Border row width", 'flexible-invoices-core')}
+						values={
+							{
+								headerBorderRowWidthTop: headerBorderRowWidthTop,
+								headerBorderRowWidthLeft: headerBorderRowWidthLeft,
+								headerBorderRowWidthRight: headerBorderRowWidthRight,
+								headerBorderRowWidthBottom: headerBorderRowWidthBottom,
+							}
+						}
+						onChange={handleMultipleAttributesChange}
+					/>
+					<ColorSelector
+						label={__('Border cell color', 'flexible-invoices-core')}
+						value={headerBorderCellColor}
+						enableAlpha={false}
+						returnFormat={'hex'}
+						onChange={(value) => setAttributes({headerBorderCellColor: value})}
+					/>
+					<QuatroInput
+						label={__("Border cell width", 'flexible-invoices-core')}
+						values={
+							{
+								headerBorderCellWidthTop: headerBorderCellWidthTop,
+								headerBorderCellWidthLeft: headerBorderCellWidthLeft,
+								headerBorderCellWidthRight: headerBorderCellWidthRight,
+								headerBorderCellWidthBottom: headerBorderCellWidthBottom,
+							}
+						}
+						onChange={handleMultipleAttributesChange}
+					/>
+
+				</PanelBody>
+				<PanelBody title={__('Body appearance', 'flexible-invoices-core')}>
+					<ColorSelector
+						label={__('Text Color', 'flexible-invoices-core')}
+						value={bodyTextColor}
+						enableAlpha={false}
+						onChange={(value) => setAttributes({bodyTextColor: value})}
+					/>
+					<ColorSelector
+						label={__('Background Color', 'flexible-invoices-core')}
+						value={bodyBackground}
+						enableAlpha={false}
+						onChange={(value) => setAttributes({bodyBackground: value})}
+					/>
+
+					<ToggleControl
+						label={__('Duo color table', 'flexible-invoices-core')}
+						checked={multipleBodyBackground}
+						onChange={(newValue) => setAttributes({multipleBodyBackground: newValue})}
+					/>
+
+					{multipleBodyBackground ? <ColorSelector
+						label={__('Background color (odd rows)', 'flexible-invoices-core')}
+						value={bodyBackgroundOdd}
+						onChange={(value) => setAttributes({bodyBackgroundOdd: value})}
+					/> : ''}
+
+					<NumberWithUnit
+						label={__('Font size', 'flexible-invoices-core')}
+						value={bodyFontSize}
+						onChange={(value) => setAttributes({bodyFontSize: value})}
+					/>
+					<AlignButtons
+						label={__('Text align', 'flexible-invoices-core')}
+						onChange={(value) => setAttributes({bodyTextAlign: value})}
+					/>
+					<FontStyles
+						label={__('Font style', 'flexible-invoices-core')}
+						onChange={(newFontStyles) => setAttributes({
+							...attributes,
+							bodyFontStyle: newFontStyles.fontStyle,
+							bodyFontWeight: newFontStyles.fontWeight,
+							bodyTextDecoration: newFontStyles.textDecoration
+						})}
+						values={
+							{
+								fontStyle: bodyFontStyle,
+								fontWeight: bodyFontWeight,
+								textDecoration: bodyTextDecoration
+							}
+						}
+					/>
+					<ColorSelector
+						label={__('Border row color', 'flexible-invoices-core')}
+						value={bodyBorderRowColor}
+						enableAlpha={false}
+						onChange={(value) => setAttributes({bodyBorderRowColor: value})}
+					/>
+					<QuatroInput
+						label={__("Border row width", 'flexible-invoices-core')}
+						values={
+							{
+								bodyBorderRowWidthTop: bodyBorderRowWidthTop,
+								bodyBorderRowWidthLeft: bodyBorderRowWidthLeft,
+								bodyBorderRowWidthRight: bodyBorderRowWidthRight,
+								bodyBorderRowWidthBottom: bodyBorderRowWidthBottom,
+							}
+						}
+						onChange={handleMultipleAttributesChange}
+					/>
+					<ColorSelector
+						label={__('Border cell color', 'flexible-invoices-core')}
+						value={bodyBorderCellColor}
+						enableAlpha={false}
+						onChange={(value) => setAttributes({bodyBorderCellColor: value})}
+					/>
+					<QuatroInput
+						label={__("Border cell width", 'flexible-invoices-core')}
+						values={
+							{
+								bodyBorderCellWidthTop: bodyBorderCellWidthTop,
+								bodyBorderCellWidthLeft: bodyBorderCellWidthLeft,
+								bodyBorderCellWidthRight: bodyBorderCellWidthRight,
+								bodyBorderCellWidthBottom: bodyBorderCellWidthBottom,
+							}
+						}
+						onChange={handleMultipleAttributesChange}
+					/>
+				</PanelBody>
+				{additionalOptions}
+				{placeholders && <BlockPlaceholders
+					placeholders={placeholders}
+				/>}
+			</InspectorControls>
+			<div {...blockProps}>
+				<table className="fitb-item-table has-background" style={{
+					borderCollapse: 'collapse'
+				}}>
+					<thead className={"fitb-item-table-header"} style={{
+						borderStyle: "solid",
+						borderColor: headerBorderRowColor,
+						borderTopWidth: headerBorderRowWidthTop,
+						borderLeftWidth: headerBorderRowWidthLeft,
+						borderRightWidth: headerBorderRowWidthRight,
+						borderBottomWidth: headerBorderRowWidthBottom,
+					}}>
+					<tr className={"fitb-item-table-header-row"} style={{
+						background: headerBackground,
+					}}>
+						{headers.map((header, index) => (
+							states[index] ?
+								<th className={"fitb-item-table-header-cell"} key={index}
+									style={{
+										borderStyle: "solid",
+										borderColor: headerBorderCellColor,
+										borderTopWidth: headerBorderCellWidthTop,
+										borderLeftWidth: headerBorderCellWidthLeft,
+										borderRightWidth: headerBorderCellWidthRight,
+										borderBottomWidth: headerBorderCellWidthBottom,
+									}}>
+									<RichText
+										tagName="div"
+										key={index}
+										value={header}
+										style={{
+											color: headerTextColor,
+											textAlign: headerTextAlign,
+											fontStyle: headerFontStyle,
+											textDecoration: headerTextDecoration,
+											fontSize: headerFontSize,
+											fontWeight: headerFontWeight,
+										}}
+										onChange={(value) => updateHeader(index, value)}
+									/>
+								</th> : ''
+						))}
+					</tr>
+					</thead>
+					<tbody className={"fitb-item-table-body"}>
+
+					{Array.from({length: 2}).map((_, index) => (
+						<tr className={"fitb-item-table-row"} style={{
+							background: (multipleBodyBackground && index === 1) ? bodyBackgroundOdd : bodyBackground,
+							borderStyle: "solid",
+							borderColor: bodyBorderRowColor,
+							borderTopWidth: bodyBorderRowWidthTop,
+							borderLeftWidth: bodyBorderRowWidthLeft,
+							borderRightWidth: bodyBorderRowWidthRight,
+							borderBottomWidth: bodyBorderRowWidthBottom,
+						}}>
+							{rows.map((row, index) => (
+								states[index] ?
+									<td className={"fitb-item-table-cell"} key={index}
+										style={{
+											borderStyle: "solid",
+											borderColor: bodyBorderCellColor,
+											borderTopWidth: bodyBorderCellWidthTop,
+											borderLeftWidth: bodyBorderCellWidthLeft,
+											borderRightWidth: bodyBorderCellWidthRight,
+											borderBottomWidth: bodyBorderCellWidthBottom,
+										}}>
+										<RichText
+											tagName="div"
+											key={index}
+											value={row}
+											style={{
+												color: bodyTextColor,
+												textAlign: bodyTextAlign,
+												fontStyle: bodyFontStyle,
+												fontSize: bodyFontSize,
+												textDecoration: bodyTextDecoration,
+												fontWeight: bodyFontWeight,
+											}}
+											onChange={(value) =>
+												updateCell(index, value)
+											}
+										/>
+									</td> : ''
+							))}
+						</tr>
+					))}
+					</tbody>
+					{footer &&
+						<tfoot>
+						{footer}
+						</tfoot>
+					}
+				</table>
+			</div>
+		</>
+	);
+}
diff --git a/src/FontStyles/FontStyles.jsx b/src/FontStyles/FontStyles.jsx
new file mode 100644
index 0000000..a3dc1ba
--- /dev/null
+++ b/src/FontStyles/FontStyles.jsx
@@ -0,0 +1,103 @@
+import {Button} from "@wordpress/components";
+import {useState} from "react";
+
+function FontStyles(props) {
+
+	const {label, onChange, values} = props;
+
+	const [isItalic, setIsItalic] = useState(values.fontStyle === 'italic');
+	const [isBold, setIsBold] = useState(values.fontWeight === 'bold');
+	const [isUnderline, setIsUnderline] = useState(values.textDecoration.includes('underline'));
+	const [isLineThrough, setIsLineThrough] = useState(values.textDecoration.includes('line-through'));
+
+	const fontStyle = isItalic ? 'italic' : 'normal';
+	const fontWeight = isBold ? 'bold' : 'normal';
+	const textDecoration = isUnderline ? 'underline' : isLineThrough ? 'line-through' : 'none';
+
+	const handleChange = function (updatedValues) {
+		console.log([updatedValues, textDecoration]);
+		const newStyles = {
+			fontStyle: updatedValues.fontStyle || fontStyle,
+			fontWeight: updatedValues.fontWeight || fontWeight,
+			textDecoration: (updatedValues.textDecoration === '' ? 'none' : updatedValues.textDecoration)  || textDecoration
+		};
+		setIsItalic( newStyles.fontStyle === 'italic');
+		setIsBold( newStyles.fontWeight === 'bold');
+		setIsUnderline( newStyles.textDecoration.includes('underline'));
+		setIsLineThrough( newStyles.textDecoration.includes('line-through'));
+		console.log(newStyles);
+
+		if (onChange) {
+			onChange(newStyles);
+		}
+	};
+
+
+	const handleButtonPress = function (styleToChange) {
+		switch (styleToChange) {
+			case 'bold':
+				setIsBold(prev => {
+					const newValue = !prev;
+					handleChange({fontWeight: newValue ? 'bold' : 'normal'});
+					return newValue;
+				});
+				break;
+			case 'italic':
+				setIsItalic(prev => {
+					const newValue = !prev;
+					handleChange({fontStyle: newValue ? 'italic' : 'normal'});
+					return newValue;
+				});
+				break;
+			case 'underline':
+				setIsUnderline(prev => {
+					const newValue = !prev;
+					handleChange({textDecoration: (newValue ? 'underline ' : '') + (isLineThrough ? 'line-through' : '')});
+					return newValue;
+				});
+				break;
+			case 'line-through':
+				setIsLineThrough(prev => {
+					const newValue = !prev;
+					handleChange({textDecoration: (newValue ? 'line-through ' : '') + (isUnderline ? 'underline' : '')});
+					return newValue;
+				});
+				break;
+			default:
+				break;
+		}
+	};
+
+
+	return (
+		<div className='fi-field-wrapper'>
+			<div className='fi-field-label-wrapper'>
+				<span className='fi-field-label'>{label}</span>
+			</div>
+			<div className='fi-field-buttons-wrapper fi-fontstylingbuttons-wrapper'>
+				<Button
+					icon="editor-italic"
+					className={"has-dashicons" + (isItalic ? ' fi-btn-active' : '')}
+					onClick={() => handleButtonPress('italic')}
+				/>
+				<Button
+					icon="editor-bold"
+					className={"has-dashicons" + (isBold ? ' fi-btn-active' : '')}
+					onClick={() => handleButtonPress('bold')}
+				/>
+				<Button
+					icon="editor-strikethrough"
+					className={"has-dashicons" + (isLineThrough ? ' fi-btn-active' : '')}
+					onClick={() => handleButtonPress('line-through')}
+				/>
+				<Button
+					icon="editor-underline"
+					className={"has-dashicons" + (isUnderline ? ' fi-btn-active' : '')}
+					onClick={() => handleButtonPress('underline')}
+				/>
+			</div>
+		</div>
+	);
+}
+
+export default FontStyles;
diff --git a/src/NumberWithUnit/NumberWithUnit.jsx b/src/NumberWithUnit/NumberWithUnit.jsx
new file mode 100644
index 0000000..79524bc
--- /dev/null
+++ b/src/NumberWithUnit/NumberWithUnit.jsx
@@ -0,0 +1,69 @@
+import {SelectControl, TextControl} from "@wordpress/components";
+import {useState} from "react";
+
+function NumberWithUnit(props) {
+
+	const {label, onChange, value, className} = props;
+
+	const splitValues = function (fontSize) {
+		const match = fontSize.match(/^(\d+)([a-z%]+)$/i);
+		if (!match) {
+			return {size: "16", unit: "px"};
+		}
+		return {
+			size: match[1],
+			unit: match[2]
+		};
+	}
+
+	const [unit, setUnit] = useState(splitValues(value).unit);
+	const [size, setSize] = useState(splitValues(value).size);
+
+
+
+	const handleChange = (newSize, newUnit) => {
+		if (onChange) {
+			onChange(newSize + newUnit);
+		}
+	};
+
+
+	return (
+		<div className='fi-field-wrapper'>
+			<div className='fi-field-label-wrapper'>
+				<span className='fi-field-label'>{label}</span>
+			</div>
+			<div className={'fi-field-buttons-wrapper fi-input-with-columns fi-numberwithfields-wrapper' + ' ' + className}>
+				<TextControl
+					style={{flex: 3}}
+					type={'number'}
+					placeholder={'16'}
+					value={size}
+					onChange={(value) => {
+						setSize(value);
+						handleChange(value, unit);
+					}}
+				/>
+				<SelectControl
+					options={[
+						{label: 'px', value: 'px'},
+						{label: '%', value: '%'},
+						{label: 'em', value: 'em'},
+						{label: 'pt', value: 'pt'},
+					]}
+					style={{minWidth:'56px'}}
+					value={unit}
+					onChange={(value) => {
+						setUnit(value);
+						handleChange(size, value);
+					}}
+
+				/>
+			</div>
+		</div>
+	);
+}
+
+export default NumberWithUnit;
+
+
diff --git a/src/QuatroInput/QuatroInput.jsx b/src/QuatroInput/QuatroInput.jsx
new file mode 100644
index 0000000..545d3e3
--- /dev/null
+++ b/src/QuatroInput/QuatroInput.jsx
@@ -0,0 +1,87 @@
+import NumberWithUnit from "../NumberWithUnit/NumberWithUnit";
+
+function QuatroInput(props) {
+
+	const {label, onChange, values, layout = 'cross'} = props;
+
+
+	const keys = Object.keys(values);
+	const valuesArray = Object.values(values);
+	const handleChange = (side, value) => {
+		const changedSide = keys[side];
+		const newValues = {...values, [changedSide]: value};
+
+		if (onChange) {
+			onChange(newValues);
+		}
+	};
+	return (
+		<div className='fi-field-wrapper'>
+			<div className='fi-field-label-wrapper'>
+				<span className='fi-field-label'>{label}</span>
+			</div>
+			{layout === 'cross' &&
+				<div className='fi-field-buttons-wrapper fi-quatro-fields-wrapper'>
+					<div className='fi-quatro-fields'>
+						<NumberWithUnit
+							className='fi-quatro-field fi-quatro-field-input'
+							value={valuesArray[0]}
+							onChange={(e) => {
+								handleChange(0, e)
+							}}
+						/>
+					</div>
+					<div className='fi-quatro-fields fields-mid'>
+						<NumberWithUnit
+							className='fi-quatro-field fi-quatro-field-input'
+							value={valuesArray[1]}
+							onChange={(e) => handleChange(1, e)}
+						/>
+						<NumberWithUnit
+							className='fi-quatro-field fi-quatro-field-input'
+							value={valuesArray[2]}
+							onChange={(e) => handleChange(2, e)}
+						/>
+					</div>
+					<div className='fi-quatro-fields'>
+						<NumberWithUnit
+							className='fi-quatro-field fi-quatro-field-input'
+							value={valuesArray[3]}
+							onChange={(e) => handleChange(3, e)}
+						/>
+					</div>
+				</div>}
+			{layout === 'box' &&
+				<div className='fi-field-buttons-wrapper fi-quatro-fields-wrapper'>
+					<div className='fi-quatro-fields'>
+						<NumberWithUnit
+							className='fi-quatro-field fi-quatro-field-input'
+							value={valuesArray[0]}
+							onChange={(e) => {
+								handleChange(0, e)
+							}}
+						/>
+						<NumberWithUnit
+							className='fi-quatro-field fi-quatro-field-input'
+							value={valuesArray[1]}
+							onChange={(e) => handleChange(1, e)}
+						/>
+					</div>
+					<div className='fi-quatro-fields fields-mid'>
+						<NumberWithUnit
+							className='fi-quatro-field fi-quatro-field-input'
+							value={valuesArray[2]}
+							onChange={(e) => handleChange(2, e)}
+						/>
+						<NumberWithUnit
+							className='fi-quatro-field fi-quatro-field-input'
+							value={valuesArray[3]}
+							onChange={(e) => handleChange(3, e)}
+						/>
+					</div>
+				</div>}
+		</div>
+	);
+}
+
+export default QuatroInput;
-- 
GitLab


From a7ed0ea491daca1d087e47bc4793500cfe743288 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Mon, 24 Feb 2025 22:44:22 +0100
Subject: [PATCH 02/14] fix: flexible table fix

---
 package-lock.json                   | 121 +++++++++++++++++++++++++++-
 package.json                        |   3 +-
 src/FlexibleTable/FlexibleTable.jsx |   4 +-
 3 files changed, 125 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 13a50e2..e42dea2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,8 @@
 				"@wordpress/block-editor": "^14.13.0",
 				"@wordpress/components": "^29.4.0",
 				"@wordpress/i18n": "^5.18.0",
-				"react": "^18.3.1"
+				"react": "^18.3.1",
+				"react-beautiful-dnd": "^13.1.1"
 			}
 		},
 		"node_modules/@ariakit/core": {
@@ -892,6 +893,16 @@
 			"integrity": "sha512-9VZUA5omXBfn+hDxFjUDu1FOJTBM3LmvqfDey+Z6Aa8B8/JmF5SMj6FBrjfgJ/Q3YXOZd3qyTDfJyMZSs/wCUA==",
 			"license": "MIT"
 		},
+		"node_modules/@types/hoist-non-react-statics": {
+			"version": "3.3.6",
+			"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz",
+			"integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==",
+			"license": "MIT",
+			"dependencies": {
+				"@types/react": "*",
+				"hoist-non-react-statics": "^3.3.0"
+			}
+		},
 		"node_modules/@types/mousetrap": {
 			"version": "1.6.15",
 			"resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.15.tgz",
@@ -929,6 +940,27 @@
 				"@types/react": "^18.0.0"
 			}
 		},
+		"node_modules/@types/react-redux": {
+			"version": "7.1.34",
+			"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz",
+			"integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@types/hoist-non-react-statics": "^3.3.0",
+				"@types/react": "*",
+				"hoist-non-react-statics": "^3.3.0",
+				"redux": "^4.0.0"
+			}
+		},
+		"node_modules/@types/react-redux/node_modules/redux": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+			"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/runtime": "^7.9.2"
+			}
+		},
 		"node_modules/@use-gesture/core": {
 			"version": "10.3.1",
 			"resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz",
@@ -1946,6 +1978,15 @@
 				"node": ">=10"
 			}
 		},
+		"node_modules/css-box-model": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
+			"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
+			"license": "MIT",
+			"dependencies": {
+				"tiny-invariant": "^1.0.6"
+			}
+		},
 		"node_modules/csstype": {
 			"version": "3.1.3",
 			"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -2382,6 +2423,12 @@
 			"integrity": "sha512-yywVJy8ctVlN5lNPxsep5urnZ6TTclwPEyigM9M3Bi8vseJBOfqNrGWN/r8NzuIt3PovM323W04blJfGQfQSVg==",
 			"license": "MIT"
 		},
+		"node_modules/memoize-one": {
+			"version": "5.2.1",
+			"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
+			"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
+			"license": "MIT"
+		},
 		"node_modules/moment": {
 			"version": "2.30.1",
 			"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
@@ -2677,6 +2724,12 @@
 				"react-is": "^16.13.1"
 			}
 		},
+		"node_modules/raf-schd": {
+			"version": "4.0.3",
+			"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
+			"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==",
+			"license": "MIT"
+		},
 		"node_modules/re-resizable": {
 			"version": "6.11.0",
 			"resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.0.tgz",
@@ -2714,6 +2767,35 @@
 				"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0"
 			}
 		},
+		"node_modules/react-beautiful-dnd": {
+			"version": "13.1.1",
+			"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
+			"integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
+			"deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"@babel/runtime": "^7.9.2",
+				"css-box-model": "^1.2.0",
+				"memoize-one": "^5.1.1",
+				"raf-schd": "^4.0.2",
+				"react-redux": "^7.2.0",
+				"redux": "^4.0.4",
+				"use-memo-one": "^1.1.1"
+			},
+			"peerDependencies": {
+				"react": "^16.8.5 || ^17.0.0 || ^18.0.0",
+				"react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
+			}
+		},
+		"node_modules/react-beautiful-dnd/node_modules/redux": {
+			"version": "4.2.1",
+			"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+			"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/runtime": "^7.9.2"
+			}
+		},
 		"node_modules/react-colorful": {
 			"version": "5.6.1",
 			"resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
@@ -2757,6 +2839,37 @@
 			"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
 			"license": "MIT"
 		},
+		"node_modules/react-redux": {
+			"version": "7.2.9",
+			"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
+			"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/runtime": "^7.15.4",
+				"@types/react-redux": "^7.1.20",
+				"hoist-non-react-statics": "^3.3.2",
+				"loose-envify": "^1.4.0",
+				"prop-types": "^15.7.2",
+				"react-is": "^17.0.2"
+			},
+			"peerDependencies": {
+				"react": "^16.8.3 || ^17 || ^18"
+			},
+			"peerDependenciesMeta": {
+				"react-dom": {
+					"optional": true
+				},
+				"react-native": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/react-redux/node_modules/react-is": {
+			"version": "17.0.2",
+			"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+			"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+			"license": "MIT"
+		},
 		"node_modules/react-remove-scroll": {
 			"version": "2.6.3",
 			"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz",
@@ -3075,6 +3188,12 @@
 			"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
 			"license": "MIT"
 		},
+		"node_modules/tiny-invariant": {
+			"version": "1.3.3",
+			"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+			"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+			"license": "MIT"
+		},
 		"node_modules/tslib": {
 			"version": "2.8.1",
 			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
diff --git a/package.json b/package.json
index bb0dd5e..677b5e5 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
 		"@wordpress/block-editor": "^14.13.0",
 		"@wordpress/components": "^29.4.0",
 		"@wordpress/i18n": "^5.18.0",
-		"react": "^18.3.1"
+		"react": "^18.3.1",
+		"react-beautiful-dnd": "^13.1.1"
 	}
 }
diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
index 6b8b090..40f6937 100644
--- a/src/FlexibleTable/FlexibleTable.jsx
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -16,7 +16,7 @@ import FontStyles from "../FontStyles/FontStyles";
 import QuatroInput from "../QuatroInput/QuatroInput";
 import BlockPlaceholders from "../BlockPlaceholders/BlockPlaceholders";
 
-export default function Edit({attributes, setAttributes, placeholders, footer, additionalOptions }) {
+function FlexibleTable({attributes, setAttributes, placeholders, footer, additionalOptions }) {
 	const {
 		headers,
 		rows,
@@ -424,3 +424,5 @@ export default function Edit({attributes, setAttributes, placeholders, footer, a
 		</>
 	);
 }
+
+export default FlexibleTable;
-- 
GitLab


From 4771218ed38bc518649fe04d7ea5f6f9aaf84c77 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 27 Feb 2025 10:51:40 +0100
Subject: [PATCH 03/14] refactor: export default, removed console logs

---
 src/AlignButtons/AlignButtons.jsx           |  9 ++---
 src/BlockPlaceholders/BlockPlaceholders.jsx |  9 ++---
 src/BlockPlaceholders/Placeholder.jsx       |  7 +---
 src/ColorSelector/ColorSelector.jsx         |  7 +---
 src/DuoInput/DuoInput.jsx                   | 37 +++++++++------------
 src/FlexibleTable/FlexibleTable.jsx         |  4 +--
 src/FontStyles/FontStyles.jsx               |  8 +----
 src/NumberWithUnit/NumberWithUnit.jsx       |  8 +----
 src/QuatroInput/QuatroInput.jsx             |  7 +---
 9 files changed, 26 insertions(+), 70 deletions(-)

diff --git a/src/AlignButtons/AlignButtons.jsx b/src/AlignButtons/AlignButtons.jsx
index 8a6ea95..03bebea 100644
--- a/src/AlignButtons/AlignButtons.jsx
+++ b/src/AlignButtons/AlignButtons.jsx
@@ -1,9 +1,8 @@
 import {Button} from "@wordpress/components";
-function AlignButtons(props) {
+export default function AlignButtons ({label, onChange}) {
 
-	const {label, onChange} = props;
 
-	const handleChange = function (newAlignment) {
+	const handleChange =  (newAlignment) => {
 		if (onChange) {
 			onChange(newAlignment);
 		}
@@ -34,7 +33,3 @@ function AlignButtons(props) {
 		</div>
 	);
 }
-
-export default AlignButtons;
-
-
diff --git a/src/BlockPlaceholders/BlockPlaceholders.jsx b/src/BlockPlaceholders/BlockPlaceholders.jsx
index e47a239..9728205 100644
--- a/src/BlockPlaceholders/BlockPlaceholders.jsx
+++ b/src/BlockPlaceholders/BlockPlaceholders.jsx
@@ -2,11 +2,10 @@ import {__} from "@wordpress/i18n";
 import {PanelBody} from "@wordpress/components";
 import Placeholder from "./Placeholder";
 
-function BlockPlaceholders(props) {
-	const {placeholders,children} = props;
+export default function BlockPlaceholders({placeholders, children}) {
 	return (
 		<PanelBody title={__('Placeholders', 'flexible-invoices-core')}>
-			{ children && <p className='fi-placeholders-list-description'>{children}</p> }
+			{children && <p className='fi-placeholders-list-description'>{children}</p>}
 			{
 				placeholders.map((placeholder) => {
 					return <Placeholder label={placeholder.label}>{placeholder.description}</Placeholder>
@@ -15,7 +14,3 @@ function BlockPlaceholders(props) {
 		</PanelBody>
 	);
 }
-
-export default BlockPlaceholders;
-
-
diff --git a/src/BlockPlaceholders/Placeholder.jsx b/src/BlockPlaceholders/Placeholder.jsx
index ca2e902..0062dd5 100644
--- a/src/BlockPlaceholders/Placeholder.jsx
+++ b/src/BlockPlaceholders/Placeholder.jsx
@@ -1,5 +1,4 @@
-function Placeholder(props) {
-	const {label, children} = props;
+export default function Placeholder({label, children}) {
 	return (
 		<div>
 			<input type='text' disabled value={label}/>
@@ -8,7 +7,3 @@ function Placeholder(props) {
 		</div>
 	);
 }
-
-export default Placeholder;
-
-
diff --git a/src/ColorSelector/ColorSelector.jsx b/src/ColorSelector/ColorSelector.jsx
index 021c497..018f477 100644
--- a/src/ColorSelector/ColorSelector.jsx
+++ b/src/ColorSelector/ColorSelector.jsx
@@ -1,9 +1,8 @@
 import {ColorPicker} from "@wordpress/components";
 import {useState} from "react";
 
-function ColorSelector(props) {
+export default function ColorSelector({label, onChange, value, enableAlpha = true, returnFormat = 'rgb'}) {
 
-	const {label, onChange, value, enableAlpha = true, returnFormat = 'rgb'} = props;
 	const [isPopupActive, setIsPopupActive] = useState(false);
 
 	const handleColorChange = function (newColor) {
@@ -57,7 +56,3 @@ function ColorSelector(props) {
 		</div>
 	);
 }
-
-export default ColorSelector;
-
-
diff --git a/src/DuoInput/DuoInput.jsx b/src/DuoInput/DuoInput.jsx
index a497004..97150fa 100644
--- a/src/DuoInput/DuoInput.jsx
+++ b/src/DuoInput/DuoInput.jsx
@@ -1,9 +1,6 @@
 import NumberWithUnit from "../NumberWithUnit/NumberWithUnit";
 
-function DuoInput(props) {
-
-	const {label, onChange, values, layout = 'column'} = props;
-
+export default function DuoInput({label, onChange, values, layout = 'column'}) {
 
 	const keys = Object.keys(values);
 	const valuesArray = Object.values(values);
@@ -20,24 +17,22 @@ function DuoInput(props) {
 			<div className='fi-field-label-wrapper'>
 				<span className='fi-field-label'>{label}</span>
 			</div>
-				<div className='fi-field-buttons-wrapper fi-duo-fields-wrapper' style={{flexDirection: layout }}>
-					<div className='fi-duo-fields'>
-						<NumberWithUnit
-							className='fi-duo-field fi-duo-field-input'
-							value={valuesArray[0]}
-							onChange={(e) => {
-								handleChange(0, e)
-							}}
-						/>
-						<NumberWithUnit
-							className='fi-duo-field fi-duo-field-input'
-							value={valuesArray[1]}
-							onChange={(e) => handleChange(1, e)}
-						/>
-					</div>
+			<div className='fi-field-buttons-wrapper fi-duo-fields-wrapper' style={{flexDirection: layout}}>
+				<div className='fi-duo-fields'>
+					<NumberWithUnit
+						className='fi-duo-field fi-duo-field-input'
+						value={valuesArray[0]}
+						onChange={(e) => {
+							handleChange(0, e)
+						}}
+					/>
+					<NumberWithUnit
+						className='fi-duo-field fi-duo-field-input'
+						value={valuesArray[1]}
+						onChange={(e) => handleChange(1, e)}
+					/>
 				</div>
+			</div>
 		</div>
 	);
 }
-
-export default DuoInput;
diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
index 40f6937..1ff228c 100644
--- a/src/FlexibleTable/FlexibleTable.jsx
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -16,7 +16,7 @@ import FontStyles from "../FontStyles/FontStyles";
 import QuatroInput from "../QuatroInput/QuatroInput";
 import BlockPlaceholders from "../BlockPlaceholders/BlockPlaceholders";
 
-function FlexibleTable({attributes, setAttributes, placeholders, footer, additionalOptions }) {
+export default function FlexibleTable({attributes, setAttributes, placeholders, footer, additionalOptions }) {
 	const {
 		headers,
 		rows,
@@ -424,5 +424,3 @@ function FlexibleTable({attributes, setAttributes, placeholders, footer, additio
 		</>
 	);
 }
-
-export default FlexibleTable;
diff --git a/src/FontStyles/FontStyles.jsx b/src/FontStyles/FontStyles.jsx
index a3dc1ba..897f720 100644
--- a/src/FontStyles/FontStyles.jsx
+++ b/src/FontStyles/FontStyles.jsx
@@ -1,9 +1,7 @@
 import {Button} from "@wordpress/components";
 import {useState} from "react";
 
-function FontStyles(props) {
-
-	const {label, onChange, values} = props;
+export default function  FontStyles({label, onChange, values} ) {
 
 	const [isItalic, setIsItalic] = useState(values.fontStyle === 'italic');
 	const [isBold, setIsBold] = useState(values.fontWeight === 'bold');
@@ -15,7 +13,6 @@ function FontStyles(props) {
 	const textDecoration = isUnderline ? 'underline' : isLineThrough ? 'line-through' : 'none';
 
 	const handleChange = function (updatedValues) {
-		console.log([updatedValues, textDecoration]);
 		const newStyles = {
 			fontStyle: updatedValues.fontStyle || fontStyle,
 			fontWeight: updatedValues.fontWeight || fontWeight,
@@ -25,7 +22,6 @@ function FontStyles(props) {
 		setIsBold( newStyles.fontWeight === 'bold');
 		setIsUnderline( newStyles.textDecoration.includes('underline'));
 		setIsLineThrough( newStyles.textDecoration.includes('line-through'));
-		console.log(newStyles);
 
 		if (onChange) {
 			onChange(newStyles);
@@ -99,5 +95,3 @@ function FontStyles(props) {
 		</div>
 	);
 }
-
-export default FontStyles;
diff --git a/src/NumberWithUnit/NumberWithUnit.jsx b/src/NumberWithUnit/NumberWithUnit.jsx
index 79524bc..2745f92 100644
--- a/src/NumberWithUnit/NumberWithUnit.jsx
+++ b/src/NumberWithUnit/NumberWithUnit.jsx
@@ -1,9 +1,7 @@
 import {SelectControl, TextControl} from "@wordpress/components";
 import {useState} from "react";
 
-function NumberWithUnit(props) {
-
-	const {label, onChange, value, className} = props;
+export default function NumberWithUnit({label, onChange, value, className}) {
 
 	const splitValues = function (fontSize) {
 		const match = fontSize.match(/^(\d+)([a-z%]+)$/i);
@@ -63,7 +61,3 @@ function NumberWithUnit(props) {
 		</div>
 	);
 }
-
-export default NumberWithUnit;
-
-
diff --git a/src/QuatroInput/QuatroInput.jsx b/src/QuatroInput/QuatroInput.jsx
index 545d3e3..15e2795 100644
--- a/src/QuatroInput/QuatroInput.jsx
+++ b/src/QuatroInput/QuatroInput.jsx
@@ -1,9 +1,6 @@
 import NumberWithUnit from "../NumberWithUnit/NumberWithUnit";
 
-function QuatroInput(props) {
-
-	const {label, onChange, values, layout = 'cross'} = props;
-
+export default function QuatroInput({label, onChange, values, layout = 'cross'}) {
 
 	const keys = Object.keys(values);
 	const valuesArray = Object.values(values);
@@ -83,5 +80,3 @@ function QuatroInput(props) {
 		</div>
 	);
 }
-
-export default QuatroInput;
-- 
GitLab


From c9525161624f163a311abe7c17a12f42b6180216 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 27 Feb 2025 10:55:45 +0100
Subject: [PATCH 04/14] refactor: index.js

---
 src/index.js | 8 ++++++++
 1 file changed, 8 insertions(+)
 create mode 100644 src/index.js

diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..dcb9b4d
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,8 @@
+export { default as AlignButtons } from "./AlignButtons/AlignButtons";
+export { default as BlockPlaceholders } from "./BlockPlaceholders/BlockPlaceholders";
+export { default as ColorSelector } from "./ColorSelector/ColorSelector";
+export { default as DuoInput } from "./DuoInput/DuoInput";
+export { default as FlexibleTable } from "./FlexibleTable/FlexibleTable";
+export { default as FontStyles } from "./FontStyles/FontStyles";
+export { default as NumberWithUnit } from "./NumberWithUnit/NumberWithUnit";
+export { default as QuatroInput } from "./QuatroInput/QuatroInput";
-- 
GitLab


From 1c6aed01b64571c348e8a2e4d891e8c65a024f37 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 27 Feb 2025 11:43:47 +0100
Subject: [PATCH 05/14] refactor: use prev value

---
 src/ColorSelector/ColorSelector.jsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ColorSelector/ColorSelector.jsx b/src/ColorSelector/ColorSelector.jsx
index 018f477..18452f0 100644
--- a/src/ColorSelector/ColorSelector.jsx
+++ b/src/ColorSelector/ColorSelector.jsx
@@ -41,7 +41,7 @@ export default function ColorSelector({label, onChange, value, enableAlpha = tru
 					width: '100%'
 				}}
 					 onClick={() => {
-						 setIsPopupActive(!isPopupActive)
+						 setIsPopupActive((isPopupActive) => !isPopupActive)
 					 }}
 				></div>
 			</div>
-- 
GitLab


From e6ecb8d7ff8824e44c40e4434e1f8037ec5dc6f7 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 27 Feb 2025 13:18:45 +0100
Subject: [PATCH 06/14] feature: text color selector for odd rows

---
 src/FlexibleTable/FlexibleTable.jsx | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
index 1ff228c..7ff2c36 100644
--- a/src/FlexibleTable/FlexibleTable.jsx
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -44,6 +44,7 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 		bodyBackground,
 		multipleBodyBackground,
 		bodyBackgroundOdd,
+		bodyTextColorOdd,
 		bodyTextColor,
 		bodyTextAlign,
 		bodyFontWeight,
@@ -235,13 +236,13 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 				</PanelBody>
 				<PanelBody title={__('Body appearance', 'flexible-invoices-core')}>
 					<ColorSelector
-						label={__('Text Color', 'flexible-invoices-core')}
+						label={__('Text color', 'flexible-invoices-core')}
 						value={bodyTextColor}
 						enableAlpha={false}
 						onChange={(value) => setAttributes({bodyTextColor: value})}
 					/>
 					<ColorSelector
-						label={__('Background Color', 'flexible-invoices-core')}
+						label={__('Background color', 'flexible-invoices-core')}
 						value={bodyBackground}
 						enableAlpha={false}
 						onChange={(value) => setAttributes({bodyBackground: value})}
@@ -253,11 +254,23 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 						onChange={(newValue) => setAttributes({multipleBodyBackground: newValue})}
 					/>
 
-					{multipleBodyBackground ? <ColorSelector
+					{multipleBodyBackground ?
+						<>
+							<ColorSelector
 						label={__('Background color (odd rows)', 'flexible-invoices-core')}
 						value={bodyBackgroundOdd}
 						onChange={(value) => setAttributes({bodyBackgroundOdd: value})}
-					/> : ''}
+					/>
+						<ColorSelector
+							label={__('Text color (odd rows)', 'flexible-invoices-core')}
+							value={bodyTextColorOdd}
+							enableAlpha={false}
+							onChange={(value) => setAttributes({bodyTextColorOdd: value})}
+						/>
+						</>
+						: ''
+
+					}
 
 					<NumberWithUnit
 						label={__('Font size', 'flexible-invoices-core')}
@@ -398,7 +411,7 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 											key={index}
 											value={row}
 											style={{
-												color: bodyTextColor,
+												color: (multipleBodyBackground && index === 1) ? bodyTextColorOdd : bodyTextColor,
 												textAlign: bodyTextAlign,
 												fontStyle: bodyFontStyle,
 												fontSize: bodyFontSize,
-- 
GitLab


From e40ab2741b5f31300e2e3c787e7a6018399ec8cb Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 27 Feb 2025 13:35:18 +0100
Subject: [PATCH 07/14] fix: moved index.js

---
 index.js     | 8 ++++++++
 src/index.js | 8 --------
 2 files changed, 8 insertions(+), 8 deletions(-)
 create mode 100644 index.js
 delete mode 100644 src/index.js

diff --git a/index.js b/index.js
new file mode 100644
index 0000000..27d9ba3
--- /dev/null
+++ b/index.js
@@ -0,0 +1,8 @@
+export { default as AlignButtons } from "./src/AlignButtons/AlignButtons";
+export { default as BlockPlaceholders } from "./src/BlockPlaceholders/BlockPlaceholders";
+export { default as ColorSelector } from "./src/ColorSelector/ColorSelector";
+export { default as DuoInput } from "./src/DuoInput/DuoInput";
+export { default as FlexibleTable } from "./src/FlexibleTable/FlexibleTable";
+export { default as FontStyles } from "./src/FontStyles/FontStyles";
+export { default as NumberWithUnit } from "./src/NumberWithUnit/NumberWithUnit";
+export { default as QuatroInput } from "./src/QuatroInput/QuatroInput";
diff --git a/src/index.js b/src/index.js
deleted file mode 100644
index dcb9b4d..0000000
--- a/src/index.js
+++ /dev/null
@@ -1,8 +0,0 @@
-export { default as AlignButtons } from "./AlignButtons/AlignButtons";
-export { default as BlockPlaceholders } from "./BlockPlaceholders/BlockPlaceholders";
-export { default as ColorSelector } from "./ColorSelector/ColorSelector";
-export { default as DuoInput } from "./DuoInput/DuoInput";
-export { default as FlexibleTable } from "./FlexibleTable/FlexibleTable";
-export { default as FontStyles } from "./FontStyles/FontStyles";
-export { default as NumberWithUnit } from "./NumberWithUnit/NumberWithUnit";
-export { default as QuatroInput } from "./QuatroInput/QuatroInput";
-- 
GitLab


From a4efbb0d708da8ff9b2dde860ab0e0237a1378e8 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 27 Feb 2025 13:59:43 +0100
Subject: [PATCH 08/14] fix: multi color fix

---
 src/FlexibleTable/FlexibleTable.jsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
index 7ff2c36..2940e2f 100644
--- a/src/FlexibleTable/FlexibleTable.jsx
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -385,9 +385,9 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 					</thead>
 					<tbody className={"fitb-item-table-body"}>
 
-					{Array.from({length: 2}).map((_, index) => (
+					{Array.from({length: 2}).map((_, trIndex) => (
 						<tr className={"fitb-item-table-row"} style={{
-							background: (multipleBodyBackground && index === 1) ? bodyBackgroundOdd : bodyBackground,
+							background: (multipleBodyBackground && trIndex === 1) ? bodyBackgroundOdd : bodyBackground,
 							borderStyle: "solid",
 							borderColor: bodyBorderRowColor,
 							borderTopWidth: bodyBorderRowWidthTop,
@@ -411,7 +411,7 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 											key={index}
 											value={row}
 											style={{
-												color: (multipleBodyBackground && index === 1) ? bodyTextColorOdd : bodyTextColor,
+												color: (multipleBodyBackground && trIndex === 1) ? bodyTextColorOdd : bodyTextColor,
 												textAlign: bodyTextAlign,
 												fontStyle: bodyFontStyle,
 												fontSize: bodyFontSize,
-- 
GitLab


From ba5652226fcba334890d2e060fc29b966d56db20 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 27 Feb 2025 14:03:15 +0100
Subject: [PATCH 09/14] fix: even rows not odd lol

---
 src/FlexibleTable/FlexibleTable.jsx | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
index 2940e2f..4296e7a 100644
--- a/src/FlexibleTable/FlexibleTable.jsx
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -43,8 +43,8 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 
 		bodyBackground,
 		multipleBodyBackground,
-		bodyBackgroundOdd,
-		bodyTextColorOdd,
+		bodyBackgroundEven,
+		bodyTextColorEven,
 		bodyTextColor,
 		bodyTextAlign,
 		bodyFontWeight,
@@ -257,15 +257,15 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 					{multipleBodyBackground ?
 						<>
 							<ColorSelector
-						label={__('Background color (odd rows)', 'flexible-invoices-core')}
-						value={bodyBackgroundOdd}
-						onChange={(value) => setAttributes({bodyBackgroundOdd: value})}
+						label={__('Background color (even rows)', 'flexible-invoices-core')}
+						value={bodyBackgroundEven}
+						onChange={(value) => setAttributes({bodyBackgroundEven: value})}
 					/>
 						<ColorSelector
-							label={__('Text color (odd rows)', 'flexible-invoices-core')}
-							value={bodyTextColorOdd}
+							label={__('Text color (even rows)', 'flexible-invoices-core')}
+							value={bodyTextColorEven}
 							enableAlpha={false}
-							onChange={(value) => setAttributes({bodyTextColorOdd: value})}
+							onChange={(value) => setAttributes({bodyTextColorEven: value})}
 						/>
 						</>
 						: ''
@@ -387,7 +387,7 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 
 					{Array.from({length: 2}).map((_, trIndex) => (
 						<tr className={"fitb-item-table-row"} style={{
-							background: (multipleBodyBackground && trIndex === 1) ? bodyBackgroundOdd : bodyBackground,
+							background: (multipleBodyBackground && trIndex === 1) ? bodyBackgroundEven : bodyBackground,
 							borderStyle: "solid",
 							borderColor: bodyBorderRowColor,
 							borderTopWidth: bodyBorderRowWidthTop,
@@ -411,7 +411,7 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 											key={index}
 											value={row}
 											style={{
-												color: (multipleBodyBackground && trIndex === 1) ? bodyTextColorOdd : bodyTextColor,
+												color: (multipleBodyBackground && trIndex === 1) ? bodyTextColorEven : bodyTextColor,
 												textAlign: bodyTextAlign,
 												fontStyle: bodyFontStyle,
 												fontSize: bodyFontSize,
-- 
GitLab


From 65133353e2334a06ad077346df971d4a3784b8b7 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Wed, 5 Mar 2025 16:01:31 +0100
Subject: [PATCH 10/14] fix: return format for border colors as it is Alpha is
 not supported by mpdf

---
 src/FlexibleTable/FlexibleTable.jsx | 34 +++++++++++++++--------------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
index 4296e7a..f10770d 100644
--- a/src/FlexibleTable/FlexibleTable.jsx
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -205,10 +205,10 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 						label={__("Border row width", 'flexible-invoices-core')}
 						values={
 							{
-								headerBorderRowWidthTop: headerBorderRowWidthTop,
-								headerBorderRowWidthLeft: headerBorderRowWidthLeft,
-								headerBorderRowWidthRight: headerBorderRowWidthRight,
-								headerBorderRowWidthBottom: headerBorderRowWidthBottom,
+								headerBorderRowWidthTop,
+								headerBorderRowWidthLeft,
+								headerBorderRowWidthRight,
+								headerBorderRowWidthBottom,
 							}
 						}
 						onChange={handleMultipleAttributesChange}
@@ -224,10 +224,10 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 						label={__("Border cell width", 'flexible-invoices-core')}
 						values={
 							{
-								headerBorderCellWidthTop: headerBorderCellWidthTop,
-								headerBorderCellWidthLeft: headerBorderCellWidthLeft,
-								headerBorderCellWidthRight: headerBorderCellWidthRight,
-								headerBorderCellWidthBottom: headerBorderCellWidthBottom,
+								headerBorderCellWidthTop,
+								headerBorderCellWidthLeft,
+								headerBorderCellWidthRight,
+								headerBorderCellWidthBottom,
 							}
 						}
 						onChange={handleMultipleAttributesChange}
@@ -301,16 +301,17 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 						label={__('Border row color', 'flexible-invoices-core')}
 						value={bodyBorderRowColor}
 						enableAlpha={false}
+						returnFormat={'hex'}
 						onChange={(value) => setAttributes({bodyBorderRowColor: value})}
 					/>
 					<QuatroInput
 						label={__("Border row width", 'flexible-invoices-core')}
 						values={
 							{
-								bodyBorderRowWidthTop: bodyBorderRowWidthTop,
-								bodyBorderRowWidthLeft: bodyBorderRowWidthLeft,
-								bodyBorderRowWidthRight: bodyBorderRowWidthRight,
-								bodyBorderRowWidthBottom: bodyBorderRowWidthBottom,
+								bodyBorderRowWidthTop,
+								bodyBorderRowWidthLeft,
+								bodyBorderRowWidthRight,
+								bodyBorderRowWidthBottom,
 							}
 						}
 						onChange={handleMultipleAttributesChange}
@@ -319,16 +320,17 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 						label={__('Border cell color', 'flexible-invoices-core')}
 						value={bodyBorderCellColor}
 						enableAlpha={false}
+						returnFormat={'hex'}
 						onChange={(value) => setAttributes({bodyBorderCellColor: value})}
 					/>
 					<QuatroInput
 						label={__("Border cell width", 'flexible-invoices-core')}
 						values={
 							{
-								bodyBorderCellWidthTop: bodyBorderCellWidthTop,
-								bodyBorderCellWidthLeft: bodyBorderCellWidthLeft,
-								bodyBorderCellWidthRight: bodyBorderCellWidthRight,
-								bodyBorderCellWidthBottom: bodyBorderCellWidthBottom,
+								bodyBorderCellWidthTop,
+								bodyBorderCellWidthLeft,
+								bodyBorderCellWidthRight,
+								bodyBorderCellWidthBottom,
 							}
 						}
 						onChange={handleMultipleAttributesChange}
-- 
GitLab


From 5581b9dd93dab5a1511388f287dce59feb4153f7 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 6 Mar 2025 15:29:56 +0100
Subject: [PATCH 11/14] fix: duo input layout fixes

---
 src/DuoInput/DuoInput.jsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/DuoInput/DuoInput.jsx b/src/DuoInput/DuoInput.jsx
index 97150fa..81b6e0e 100644
--- a/src/DuoInput/DuoInput.jsx
+++ b/src/DuoInput/DuoInput.jsx
@@ -17,8 +17,8 @@ export default function DuoInput({label, onChange, values, layout = 'column'}) {
 			<div className='fi-field-label-wrapper'>
 				<span className='fi-field-label'>{label}</span>
 			</div>
-			<div className='fi-field-buttons-wrapper fi-duo-fields-wrapper' style={{flexDirection: layout}}>
-				<div className='fi-duo-fields'>
+			<div className='fi-field-buttons-wrapper fi-duo-fields-wrapper'>
+				<div className='fi-duo-fields' style={{ display: 'flex', flexDirection: layout}}>
 					<NumberWithUnit
 						className='fi-duo-field fi-duo-field-input'
 						value={valuesArray[0]}
-- 
GitLab


From 7b845178abdee0eaafecd047d2bfc068d0f47085 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 6 Mar 2025 15:30:14 +0100
Subject: [PATCH 12/14] fix: FlexibleTable borders fixed

---
 src/FlexibleTable/FlexibleTable.jsx | 125 +++++++++++++---------------
 1 file changed, 56 insertions(+), 69 deletions(-)

diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
index f10770d..d4299a6 100644
--- a/src/FlexibleTable/FlexibleTable.jsx
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -1,20 +1,8 @@
 import {__} from '@wordpress/i18n';
-import {
-	RichText,
-	InspectorControls,
-	useBlockProps,
-} from '@wordpress/block-editor';
-import {
-	PanelBody,
-	ToggleControl,
-} from '@wordpress/components';
+import {RichText, InspectorControls, useBlockProps,} from '@wordpress/block-editor';
+import {PanelBody, ToggleControl} from '@wordpress/components';
 import {DragDropContext, Draggable, Droppable} from "react-beautiful-dnd";
-import ColorSelector from "../ColorSelector/ColorSelector";
-import NumberWithUnit from "../NumberWithUnit/NumberWithUnit";
-import AlignButtons from "../AlignButtons/AlignButtons";
-import FontStyles from "../FontStyles/FontStyles";
-import QuatroInput from "../QuatroInput/QuatroInput";
-import BlockPlaceholders from "../BlockPlaceholders/BlockPlaceholders";
+import {DuoInput, NumberWithUnit, ColorSelector, FontStyles, AlignButtons, BlockPlaceholders} from "../../index";
 
 export default function FlexibleTable({attributes, setAttributes, placeholders, footer, additionalOptions }) {
 	const {
@@ -30,16 +18,22 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 		headerTextDecoration,
 		headerFontSize,
 
+		tableBorderColor,
+		tableBorderWidth,
+
+		bodyBorderColumnColor,
+		bodyBorderColumnWidthLeft,
+		bodyBorderColumnWidthRight,
+		bodyBorderRowColor,
+		bodyBorderRowWidthTop,
+		bodyBorderRowWidthBottom,
+
+		headerBorderColumnColor,
+		headerBorderColumnWidthLeft,
+		headerBorderColumnWidthRight,
+		headerBorderRowColor,
 		headerBorderRowWidthTop,
-		headerBorderRowWidthLeft,
-		headerBorderRowWidthRight,
 		headerBorderRowWidthBottom,
-		headerBorderRowColor,
-		headerBorderCellWidthTop,
-		headerBorderCellWidthLeft,
-		headerBorderCellWidthRight,
-		headerBorderCellWidthBottom,
-		headerBorderCellColor,
 
 		bodyBackground,
 		multipleBodyBackground,
@@ -51,18 +45,6 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 		bodyFontStyle,
 		bodyTextDecoration,
 		bodyFontSize,
-
-		bodyBorderRowWidthTop,
-		bodyBorderRowWidthLeft,
-		bodyBorderRowWidthRight,
-		bodyBorderRowWidthBottom,
-		bodyBorderRowColor,
-		bodyBorderCellWidthTop,
-		bodyBorderCellWidthLeft,
-		bodyBorderCellWidthRight,
-		bodyBorderCellWidthBottom,
-		bodyBorderCellColor,
-
 	} = attributes;
 
 	const blockProps = useBlockProps();
@@ -194,6 +176,21 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 							}
 						}
 					/>
+
+					<ColorSelector
+						label={__('Table border color', 'flexible-invoices-core')}
+						value={tableBorderColor}
+						enableAlpha={false}
+						returnFormat={'hex'}
+						onChange={(value) => setAttributes({tableBorderColor: value})}
+					/>
+
+					<NumberWithUnit
+						label={__('Table border width', 'flexible-invoices-core')}
+						value={tableBorderWidth}
+						onChange={(value) => setAttributes({tableBorderWidth: value})}
+					/>
+
 					<ColorSelector
 						label={__('Border row color', 'flexible-invoices-core')}
 						value={headerBorderRowColor}
@@ -201,13 +198,12 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 						returnFormat={'hex'}
 						onChange={(value) => setAttributes({headerBorderRowColor: value})}
 					/>
-					<QuatroInput
+
+					<DuoInput
 						label={__("Border row width", 'flexible-invoices-core')}
 						values={
 							{
 								headerBorderRowWidthTop,
-								headerBorderRowWidthLeft,
-								headerBorderRowWidthRight,
 								headerBorderRowWidthBottom,
 							}
 						}
@@ -215,21 +211,20 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 					/>
 					<ColorSelector
 						label={__('Border cell color', 'flexible-invoices-core')}
-						value={headerBorderCellColor}
+						value={headerBorderColumnColor}
 						enableAlpha={false}
 						returnFormat={'hex'}
-						onChange={(value) => setAttributes({headerBorderCellColor: value})}
+						onChange={(value) => setAttributes({headerBorderColumnColor: value})}
 					/>
-					<QuatroInput
+					<DuoInput
 						label={__("Border cell width", 'flexible-invoices-core')}
 						values={
 							{
-								headerBorderCellWidthTop,
-								headerBorderCellWidthLeft,
-								headerBorderCellWidthRight,
-								headerBorderCellWidthBottom,
+								headerBorderColumnWidthLeft,
+								headerBorderColumnWidthRight,
 							}
 						}
+						layout={'row'}
 						onChange={handleMultipleAttributesChange}
 					/>
 
@@ -304,13 +299,11 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 						returnFormat={'hex'}
 						onChange={(value) => setAttributes({bodyBorderRowColor: value})}
 					/>
-					<QuatroInput
+					<DuoInput
 						label={__("Border row width", 'flexible-invoices-core')}
 						values={
 							{
 								bodyBorderRowWidthTop,
-								bodyBorderRowWidthLeft,
-								bodyBorderRowWidthRight,
 								bodyBorderRowWidthBottom,
 							}
 						}
@@ -318,21 +311,20 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 					/>
 					<ColorSelector
 						label={__('Border cell color', 'flexible-invoices-core')}
-						value={bodyBorderCellColor}
+						value={bodyBorderColumnColor}
 						enableAlpha={false}
 						returnFormat={'hex'}
-						onChange={(value) => setAttributes({bodyBorderCellColor: value})}
+						onChange={(value) => setAttributes({bodyBorderColumnColor: value})}
 					/>
-					<QuatroInput
+					<DuoInput
 						label={__("Border cell width", 'flexible-invoices-core')}
 						values={
 							{
-								bodyBorderCellWidthTop,
-								bodyBorderCellWidthLeft,
-								bodyBorderCellWidthRight,
-								bodyBorderCellWidthBottom,
+								bodyBorderColumnWidthRight,
+								bodyBorderColumnWidthLeft,
 							}
 						}
+						layout={'row'}
 						onChange={handleMultipleAttributesChange}
 					/>
 				</PanelBody>
@@ -343,14 +335,15 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 			</InspectorControls>
 			<div {...blockProps}>
 				<table className="fitb-item-table has-background" style={{
+					borderStyle: "solid",
+					borderColor: tableBorderColor,
+					borderWidth: tableBorderWidth,
 					borderCollapse: 'collapse'
 				}}>
 					<thead className={"fitb-item-table-header"} style={{
 						borderStyle: "solid",
 						borderColor: headerBorderRowColor,
 						borderTopWidth: headerBorderRowWidthTop,
-						borderLeftWidth: headerBorderRowWidthLeft,
-						borderRightWidth: headerBorderRowWidthRight,
 						borderBottomWidth: headerBorderRowWidthBottom,
 					}}>
 					<tr className={"fitb-item-table-header-row"} style={{
@@ -361,11 +354,9 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 								<th className={"fitb-item-table-header-cell"} key={index}
 									style={{
 										borderStyle: "solid",
-										borderColor: headerBorderCellColor,
-										borderTopWidth: headerBorderCellWidthTop,
-										borderLeftWidth: headerBorderCellWidthLeft,
-										borderRightWidth: headerBorderCellWidthRight,
-										borderBottomWidth: headerBorderCellWidthBottom,
+										borderColor: headerBorderColumnColor,
+										borderLeftWidth: headerBorderColumnWidthLeft,
+										borderRightWidth: headerBorderColumnWidthRight,
 									}}>
 									<RichText
 										tagName="div"
@@ -393,8 +384,6 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 							borderStyle: "solid",
 							borderColor: bodyBorderRowColor,
 							borderTopWidth: bodyBorderRowWidthTop,
-							borderLeftWidth: bodyBorderRowWidthLeft,
-							borderRightWidth: bodyBorderRowWidthRight,
 							borderBottomWidth: bodyBorderRowWidthBottom,
 						}}>
 							{rows.map((row, index) => (
@@ -402,11 +391,9 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 									<td className={"fitb-item-table-cell"} key={index}
 										style={{
 											borderStyle: "solid",
-											borderColor: bodyBorderCellColor,
-											borderTopWidth: bodyBorderCellWidthTop,
-											borderLeftWidth: bodyBorderCellWidthLeft,
-											borderRightWidth: bodyBorderCellWidthRight,
-											borderBottomWidth: bodyBorderCellWidthBottom,
+											borderColor: bodyBorderColumnColor,
+											borderLeftWidth: bodyBorderColumnWidthLeft,
+											borderRightWidth: bodyBorderColumnWidthRight,
 										}}>
 										<RichText
 											tagName="div"
-- 
GitLab


From 77f49f8c2241f454c90ae950f5b8364d92c2a401 Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Thu, 6 Mar 2025 15:30:28 +0100
Subject: [PATCH 13/14] fix: NumberWithUnit error handling

---
 src/NumberWithUnit/NumberWithUnit.jsx | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/NumberWithUnit/NumberWithUnit.jsx b/src/NumberWithUnit/NumberWithUnit.jsx
index 2745f92..b4853af 100644
--- a/src/NumberWithUnit/NumberWithUnit.jsx
+++ b/src/NumberWithUnit/NumberWithUnit.jsx
@@ -4,7 +4,13 @@ import {useState} from "react";
 export default function NumberWithUnit({label, onChange, value, className}) {
 
 	const splitValues = function (fontSize) {
+
+		if( !fontSize ) {
+			return {size: "0", unit: "px"};
+		}
+
 		const match = fontSize.match(/^(\d+)([a-z%]+)$/i);
+
 		if (!match) {
 			return {size: "16", unit: "px"};
 		}
-- 
GitLab


From 76b25497e4ed73adb26a1eae172c7024e51a124b Mon Sep 17 00:00:00 2001
From: Eryk Mika <eryk.mika@wpdesk.eu>
Date: Tue, 11 Mar 2025 11:13:02 +0100
Subject: [PATCH 14/14] refactor: separated header, body to different files to
 make the component smaller

---
 src/FlexibleTable/FlexibleTable.jsx | 165 +++++++++++++---------------
 src/FlexibleTable/TableBody.jsx     |  69 ++++++++++++
 src/FlexibleTable/TableHead.jsx     |  58 ++++++++++
 3 files changed, 203 insertions(+), 89 deletions(-)
 create mode 100644 src/FlexibleTable/TableBody.jsx
 create mode 100644 src/FlexibleTable/TableHead.jsx

diff --git a/src/FlexibleTable/FlexibleTable.jsx b/src/FlexibleTable/FlexibleTable.jsx
index d4299a6..c9ad429 100644
--- a/src/FlexibleTable/FlexibleTable.jsx
+++ b/src/FlexibleTable/FlexibleTable.jsx
@@ -1,10 +1,20 @@
 import {__} from '@wordpress/i18n';
-import {RichText, InspectorControls, useBlockProps,} from '@wordpress/block-editor';
+import {InspectorControls, useBlockProps,} from '@wordpress/block-editor';
 import {PanelBody, ToggleControl} from '@wordpress/components';
 import {DragDropContext, Draggable, Droppable} from "react-beautiful-dnd";
 import {DuoInput, NumberWithUnit, ColorSelector, FontStyles, AlignButtons, BlockPlaceholders} from "../../index";
+import TableBody from "./TableBody";
+import TableHead from "./TableHead";
 
-export default function FlexibleTable({attributes, setAttributes, placeholders, footer, additionalOptions }) {
+export default function FlexibleTable({
+										  attributes,
+										  setAttributes,
+										  placeholders,
+										  additionalOptions,
+										  tableHeader,
+										  tableBody,
+										  tableFooter,
+									  }) {
 	const {
 		headers,
 		rows,
@@ -252,16 +262,16 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 					{multipleBodyBackground ?
 						<>
 							<ColorSelector
-						label={__('Background color (even rows)', 'flexible-invoices-core')}
-						value={bodyBackgroundEven}
-						onChange={(value) => setAttributes({bodyBackgroundEven: value})}
-					/>
-						<ColorSelector
-							label={__('Text color (even rows)', 'flexible-invoices-core')}
-							value={bodyTextColorEven}
-							enableAlpha={false}
-							onChange={(value) => setAttributes({bodyTextColorEven: value})}
-						/>
+								label={__('Background color (even rows)', 'flexible-invoices-core')}
+								value={bodyBackgroundEven}
+								onChange={(value) => setAttributes({bodyBackgroundEven: value})}
+							/>
+							<ColorSelector
+								label={__('Text color (even rows)', 'flexible-invoices-core')}
+								value={bodyTextColorEven}
+								enableAlpha={false}
+								onChange={(value) => setAttributes({bodyTextColorEven: value})}
+							/>
 						</>
 						: ''
 
@@ -340,85 +350,62 @@ export default function FlexibleTable({attributes, setAttributes, placeholders,
 					borderWidth: tableBorderWidth,
 					borderCollapse: 'collapse'
 				}}>
-					<thead className={"fitb-item-table-header"} style={{
-						borderStyle: "solid",
-						borderColor: headerBorderRowColor,
-						borderTopWidth: headerBorderRowWidthTop,
-						borderBottomWidth: headerBorderRowWidthBottom,
-					}}>
-					<tr className={"fitb-item-table-header-row"} style={{
-						background: headerBackground,
-					}}>
-						{headers.map((header, index) => (
-							states[index] ?
-								<th className={"fitb-item-table-header-cell"} key={index}
-									style={{
-										borderStyle: "solid",
-										borderColor: headerBorderColumnColor,
-										borderLeftWidth: headerBorderColumnWidthLeft,
-										borderRightWidth: headerBorderColumnWidthRight,
-									}}>
-									<RichText
-										tagName="div"
-										key={index}
-										value={header}
-										style={{
-											color: headerTextColor,
-											textAlign: headerTextAlign,
-											fontStyle: headerFontStyle,
-											textDecoration: headerTextDecoration,
-											fontSize: headerFontSize,
-											fontWeight: headerFontWeight,
-										}}
-										onChange={(value) => updateHeader(index, value)}
-									/>
-								</th> : ''
-						))}
-					</tr>
-					</thead>
-					<tbody className={"fitb-item-table-body"}>
+					{tableHeader ? tableHeader :
+						<TableHead
+							headers={headers}
+							updateHeader={updateHeader}
+							tableStyles={{
+								headerBackground,
+								headerTextColor,
+								headerTextAlign,
+								headerFontStyle,
+								headerTextDecoration,
+								headerFontSize,
+								headerFontWeight,
 
-					{Array.from({length: 2}).map((_, trIndex) => (
-						<tr className={"fitb-item-table-row"} style={{
-							background: (multipleBodyBackground && trIndex === 1) ? bodyBackgroundEven : bodyBackground,
-							borderStyle: "solid",
-							borderColor: bodyBorderRowColor,
-							borderTopWidth: bodyBorderRowWidthTop,
-							borderBottomWidth: bodyBorderRowWidthBottom,
-						}}>
-							{rows.map((row, index) => (
-								states[index] ?
-									<td className={"fitb-item-table-cell"} key={index}
-										style={{
-											borderStyle: "solid",
-											borderColor: bodyBorderColumnColor,
-											borderLeftWidth: bodyBorderColumnWidthLeft,
-											borderRightWidth: bodyBorderColumnWidthRight,
-										}}>
-										<RichText
-											tagName="div"
-											key={index}
-											value={row}
-											style={{
-												color: (multipleBodyBackground && trIndex === 1) ? bodyTextColorEven : bodyTextColor,
-												textAlign: bodyTextAlign,
-												fontStyle: bodyFontStyle,
-												fontSize: bodyFontSize,
-												textDecoration: bodyTextDecoration,
-												fontWeight: bodyFontWeight,
-											}}
-											onChange={(value) =>
-												updateCell(index, value)
-											}
-										/>
-									</td> : ''
-							))}
-						</tr>
-					))}
-					</tbody>
-					{footer &&
+								headerBorderColumnColor,
+								headerBorderColumnWidthLeft,
+								headerBorderColumnWidthRight,
+								headerBorderRowColor,
+								headerBorderRowWidthTop,
+								headerBorderRowWidthBottom
+							}}
+							states={states}
+						/>
+					}
+					{
+						tableBody ? tableBody :
+							<TableBody
+								rows={rows}
+								states={states}
+								updateCell={updateCell}
+								tableStyles={
+									{
+										bodyBackground,
+										bodyBackgroundEven,
+										bodyTextColor,
+										bodyTextColorEven,
+										bodyTextDecoration,
+										bodyTextAlign,
+										bodyFontStyle,
+										bodyFontSize,
+										bodyFontWeight,
+
+										bodyBorderColumnColor,
+										bodyBorderColumnWidthLeft,
+										bodyBorderColumnWidthRight,
+										bodyBorderRowColor,
+										bodyBorderRowWidthTop,
+										bodyBorderRowWidthBottom,
+
+										multipleBodyBackground
+									}
+								}
+							/>
+					}
+					{tableFooter &&
 						<tfoot>
-						{footer}
+						{tableFooter}
 						</tfoot>
 					}
 				</table>
diff --git a/src/FlexibleTable/TableBody.jsx b/src/FlexibleTable/TableBody.jsx
new file mode 100644
index 0000000..24f0a66
--- /dev/null
+++ b/src/FlexibleTable/TableBody.jsx
@@ -0,0 +1,69 @@
+import {RichText} from "@wordpress/block-editor";
+
+
+export default function TableBody({tableStyles, updateCell, rows, states}) {
+
+	const {
+		bodyBackground,
+		bodyBackgroundEven,
+		bodyTextColor,
+		bodyTextColorEven,
+		bodyTextDecoration,
+		bodyTextAlign,
+		bodyFontStyle,
+		bodyFontSize,
+		bodyFontWeight,
+
+		bodyBorderColumnColor,
+		bodyBorderColumnWidthLeft,
+		bodyBorderColumnWidthRight,
+		bodyBorderRowColor,
+		bodyBorderRowWidthTop,
+		bodyBorderRowWidthBottom,
+
+		multipleBodyBackground
+	} = tableStyles;
+
+	return (
+		<tbody className={"fitb-item-table-body"}>
+
+		{Array.from({length: 2}).map((_, trIndex) => (
+			<tr className={"fitb-item-table-row"} style={{
+				background: (multipleBodyBackground && trIndex === 1) ? bodyBackgroundEven : bodyBackground,
+				borderStyle: "solid",
+				borderColor: bodyBorderRowColor,
+				borderTopWidth: bodyBorderRowWidthTop,
+				borderBottomWidth: bodyBorderRowWidthBottom,
+			}}>
+				{rows.map((row, index) => (
+					states[index] ?
+						<td className={"fitb-item-table-cell"} key={index}
+							style={{
+								borderStyle: "solid",
+								borderColor: bodyBorderColumnColor,
+								borderLeftWidth: bodyBorderColumnWidthLeft,
+								borderRightWidth: bodyBorderColumnWidthRight,
+							}}>
+							<RichText
+								tagName="div"
+								key={index}
+								value={row}
+								style={{
+									color: (multipleBodyBackground && trIndex === 1) ? bodyTextColorEven : bodyTextColor,
+									textAlign: bodyTextAlign,
+									fontStyle: bodyFontStyle,
+									fontSize: bodyFontSize,
+									textDecoration: bodyTextDecoration,
+									fontWeight: bodyFontWeight,
+								}}
+								onChange={(value) =>
+									updateCell(index, value)
+								}
+							/>
+						</td> : ''
+				))}
+			</tr>
+		))}
+		</tbody>
+	);
+}
diff --git a/src/FlexibleTable/TableHead.jsx b/src/FlexibleTable/TableHead.jsx
new file mode 100644
index 0000000..1b07ee2
--- /dev/null
+++ b/src/FlexibleTable/TableHead.jsx
@@ -0,0 +1,58 @@
+import {RichText} from "@wordpress/block-editor";
+
+export default function TableHead( {headers, updateHeader, tableStyles, states} ) {
+	const {
+		headerBackground,
+		headerTextColor,
+		headerTextAlign,
+		headerFontStyle,
+		headerTextDecoration,
+		headerFontSize,
+		headerFontWeight,
+
+		headerBorderColumnColor,
+		headerBorderColumnWidthLeft,
+		headerBorderColumnWidthRight,
+		headerBorderRowColor,
+		headerBorderRowWidthTop,
+		headerBorderRowWidthBottom,
+	} = tableStyles;
+
+	return (
+		<thead className={"fitb-item-table-header"} style={{
+			borderStyle: "solid",
+			borderColor: headerBorderRowColor,
+			borderTopWidth: headerBorderRowWidthTop,
+			borderBottomWidth: headerBorderRowWidthBottom,
+		}}>
+		<tr className={"fitb-item-table-header-row"} style={{
+			background: headerBackground,
+		}}>
+			{headers.map((header, index) => (
+				states[index] ?
+					<th className={"fitb-item-table-header-cell"} key={index}
+						style={{
+							borderStyle: "solid",
+							borderColor: headerBorderColumnColor,
+							borderLeftWidth: headerBorderColumnWidthLeft,
+							borderRightWidth: headerBorderColumnWidthRight,
+						}}>
+						<RichText
+							tagName="div"
+							key={index}
+							value={header}
+							style={{
+								color: headerTextColor,
+								textAlign: headerTextAlign,
+								fontStyle: headerFontStyle,
+								textDecoration: headerTextDecoration,
+								fontSize: headerFontSize,
+								fontWeight: headerFontWeight,
+							}}
+							onChange={(value) => updateHeader(index, value)}
+						/>
+					</th> : ''
+			))}
+		</tr>
+		</thead>);
+}
-- 
GitLab