diff --git a/fame-lahjoitukset.php b/fame-lahjoitukset.php index c3cb253..0c02b6e 100644 --- a/fame-lahjoitukset.php +++ b/fame-lahjoitukset.php @@ -7,7 +7,7 @@ /** * Plugin Name: Lahjoitin * Description: Wordpress plugin for Fame lahjoitukset system. - * Version: 1.1.1 + * Version: 1.1.2 * Requires at least: 6.7 * Requires PHP: 8.3 * Author: Fame Helsinki diff --git a/src/Blocks/contact-form/block.json b/src/Blocks/contact-form/block.json index 7f81f17..06474a5 100644 --- a/src/Blocks/contact-form/block.json +++ b/src/Blocks/contact-form/block.json @@ -9,130 +9,44 @@ "parent": ["famehelsinki/donation-form"], "example": {}, "attributes": { - "showLegend": { - "type": "boolean", - "default": true - }, - "legend": { - "type": "string", - "source": "text", - "selector": "legend.fame-form__legend", - "default": "Contacts" - }, - "contact": { - "type": "boolean", - "source": "attribute", - "selector": ".fame-form__group--required:has(input[name=\"email\"])", - "attribute": "class", - "default": false - }, - "showAddress": { - "type": "boolean", - "source": "attribute", - "selector": "input[name=\"address\"]", - "attribute": "name", - "default": true - }, - "showPhone": { - "type": "boolean", - "source": "attribute", - "selector": "input[name=\"phone\"]", - "attribute": "name", - "default": true - }, - "first_name_label": { - "type": "string", - "source": "html", - "selector": ".fame-form__label[for=\"contact-first_name\"]", - "default": "First name" - }, - "last_name_label": { - "type": "string", - "source": "html", - "selector": ".fame-form__label[for=\"contact-last_name\"]", - "default": "Last name" - }, - "name_help": { - "type": "string", - "source": "html", - "selector": "#contact-name-help", - "default": "" - }, - "email_label": { - "type": "string", - "source": "html", - "selector": ".fame-form__label[for=\"contact-email\"]", - "default": "Email" - }, - "email_help": { - "type": "string", - "source": "html", - "selector": "#contact-email-help", - "default": "" - }, - "address_label": { - "type": "string", - "source": "html", - "selector": ".fame-form__label[for=\"contact-address\"]", - "default": "Address" - }, - "address_help": { - "type": "string", - "source": "html", - "selector": "#contact-address-help", - "default": "" - }, - "city_label": { - "type": "string", - "source": "html", - "selector": ".fame-form__label[for=\"contact-city\"]", - "default": "City" - }, - "city_postal_code_help": { - "type": "string", - "source": "html", - "selector": "#contact-city_postal_code-help", - "default": "City" - }, - "postal_code_label": { - "type": "string", - "source": "html", - "selector": ".fame-form__label[for=\"contact-postal_code\"]", - "default": "Postal code" - }, - "postal_code_help": { - "type": "string", - "source": "html", - "selector": "#contact-postal_code-help", - "default": "Postal code" - }, - "phone_label": { - "type": "string", - "source": "html", - "selector": ".fame-form__label[for=\"contact-phone\"]", - "default": "Phone" - }, - "phone_help": { - "type": "string", - "source": "html", - "selector": "#contact-phone-help", - "default": "" - } + "showLegend": { "type": "boolean", "default": true }, + "legend": { "type": "string", "default": "Contacts" }, + + "contact": { "type": "boolean", "default": false }, + "showAddress": { "type": "boolean", "default": true }, + "showPhone": { "type": "boolean", "default": true }, + + "first_name_label": { "type": "string", "default": "First name" }, + "last_name_label": { "type": "string", "default": "Last name" }, + "name_help": { "type": "string", "default": "" }, + + "email_label": { "type": "string", "default": "Email" }, + "email_help": { "type": "string", "default": "" }, + + "address_label": { "type": "string", "default": "Address" }, + "address_help": { "type": "string", "default": "" }, + + "city_label": { "type": "string", "default": "City" }, + "city_postal_code_help": { "type": "string", "default": "City" }, + + "postal_code_label": { "type": "string", "default": "Postal code" }, + "postal_code_help": { "type": "string", "default": "Postal code" }, + + "phone_label": { "type": "string", "default": "Phone" }, + "phone_help": { "type": "string", "default": "" }, + + "show": { "type": "boolean", "default": true }, + "legendAlign": { "type": "string", "default": "left" } }, "supports": { "multiple": false, - "color": { - "background": false, - "text": true - }, + "color": { "background": false, "text": true }, "html": false, - "typography": { - "fontSize": true - } + "typography": { "fontSize": true } }, "icon": "id", "textdomain": "fame_lahjoitukset", "editorScript": "file:./index.js", "editorStyle": "file:./index.css", - "style": "file:./style-index.css" + "render": "file:./render.php" } diff --git a/src/Blocks/contact-form/edit.css b/src/Blocks/contact-form/edit.css index e0e860d..f0edd7b 100644 --- a/src/Blocks/contact-form/edit.css +++ b/src/Blocks/contact-form/edit.css @@ -1,9 +1,6 @@ .contact-form .fame-form__fake-input { border: var(--form-element-border); padding: var(--form-element-padding); - - /* This is pretty ugly, should set line height - * somewhere, so this should be 1em * line-height */ height: 1.7em; } @@ -14,4 +11,3 @@ .fame-form__help { font-size: 14px !important; } - diff --git a/src/Blocks/contact-form/edit.tsx b/src/Blocks/contact-form/edit.tsx index 16f2774..790888c 100644 --- a/src/Blocks/contact-form/edit.tsx +++ b/src/Blocks/contact-form/edit.tsx @@ -1,6 +1,12 @@ import React from 'react' import { __ } from '@wordpress/i18n' -import { InspectorControls, RichText, useBlockProps } from '@wordpress/block-editor' +import { + InspectorControls, + RichText, + useBlockProps, + AlignmentToolbar, + BlockControls, +} from '@wordpress/block-editor' import { PanelBody, TextControl, ToggleControl } from '@wordpress/components' import ContactInputControl from './ContactInputControl.tsx' import ContactInputGroup from './ContactInputGroup.tsx' @@ -13,97 +19,149 @@ import { EditProps } from '../common/types.ts' * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit */ export default function Edit({ attributes, setAttributes }: EditProps): React.JSX.Element { - const { contact, showAddress, showPhone, showLegend, legend } = attributes + const { contact, showAddress, showPhone, showLegend, legend, legendAlign = 'left' } = attributes + const { show = true } = attributes as { show?: boolean } return ( <> + + setAttributes({ legendAlign: next || 'left' })} + /> + setAttributes({ contact })} - /> - setAttributes({ showAddress })} - /> - setAttributes({ showPhone })} - /> - setAttributes({ showLegend: checked })} - /> - setAttributes({ legend: value })} + label={__('Show contact fields', 'fame_lahjoitukset')} + checked={attributes.show} + onChange={value => setAttributes({ show: value })} /> + + {show && ( + <> + setAttributes({ contact: value })} + /> + setAttributes({ showAddress: value })} + /> + setAttributes({ showPhone: value })} + /> + setAttributes({ showLegend: checked })} + help={__( + 'If disabled, the legend is marked visually hidden.', + 'fame_lahjoitukset' + )} + /> + setAttributes({ legend: value })} + /> + + )}
- {showLegend && ( - setAttributes({ legend: value })} - /> - )} - - - {showAddress && ( + {show ? ( <> - setAttributes({ legend: value })} + style={{ + textAlign: legendAlign as React.CSSProperties['textAlign'], + }} + /> + )} + - + {showAddress && ( + <> + + + + )} + {showPhone && ( + + )} - )} - {showPhone && ( - + ) : ( +
+ {__( + 'The contact form is not in use. Use the toggle in the sidebar to enable it.', + 'fame_lahjoitukset' + )} +
)}
diff --git a/src/Blocks/contact-form/render.php b/src/Blocks/contact-form/render.php new file mode 100644 index 0000000..f1a95f9 --- /dev/null +++ b/src/Blocks/contact-form/render.php @@ -0,0 +1,139 @@ +|null $attributes */ +$attributes = $attributes ?? []; + +$show = array_key_exists('show', $attributes) ? (bool) $attributes['show'] : true; +if (!$show) { + return; +} + +$showLegend = array_key_exists('showLegend', $attributes) ? (bool) $attributes['showLegend'] : true; +$legend = isset($attributes['legend']) && trim((string) $attributes['legend']) !== '' + ? (string) $attributes['legend'] + : __('Contacts', 'fame_lahjoitukset'); + +$contact = array_key_exists('contact', $attributes) ? (bool) $attributes['contact'] : false; +$showAddress = array_key_exists('showAddress', $attributes) ? (bool) $attributes['showAddress'] : true; +$showPhone = array_key_exists('showPhone', $attributes) ? (bool) $attributes['showPhone'] : true; + +$wrapper_attrs = get_block_wrapper_attributes([ + 'class' => 'fame-form__fieldset', +]); + +$get = static function (array $attrs, string $key, string $fallback = ''): string { + return isset($attrs[$key]) ? (string) $attrs[$key] : $fallback; +}; + +$render_input = static function ( + array $attrs, + string $name, + string $type, + bool $required = false, + ?string $ariaDescribedBy = null +) use ($get): void { + $label = $get($attrs, "{$name}_label", ''); + $help = $get($attrs, "{$name}_help", ''); + + $aria_id = $ariaDescribedBy ?: ($help !== '' ? "contact-{$name}-help" : ''); +?> +
+ + + + class="fame-form__input" + id="" + + aria-describedby="" + /> + + + " class="fame-form__help"> + + + +
+ +
+ + + + + + + +
+ +
> + + + + + + 'first_name', 'type' => 'text', 'required' => $contact], + ['name' => 'last_name', 'type' => 'text', 'required' => $contact], + ]); + + $render_input($attributes, 'email', 'email', $contact, null); + + if ($showAddress) { + $render_input($attributes, 'address', 'text', false, null); + + $render_group($attributes, 'city_postal_code', [ + ['name' => 'city', 'type' => 'text', 'required' => false], + ['name' => 'postal_code', 'type' => 'text', 'required' => false], + ]); + } + + if ($showPhone) { + $render_input($attributes, 'phone', 'tel', false, null); + } + ?> +
\ No newline at end of file diff --git a/src/Blocks/contact-form/save.tsx b/src/Blocks/contact-form/save.tsx index 95699e6..af2f1fd 100644 --- a/src/Blocks/contact-form/save.tsx +++ b/src/Blocks/contact-form/save.tsx @@ -1,59 +1,6 @@ -import { useBlockProps } from '@wordpress/block-editor' -import React from 'react' -import { ContactInputContent } from './ContactInputControl.tsx' -import { ContactGroupContent } from './ContactInputGroup.tsx' -import { SaveProps } from '../common/types.ts' - /** - * The save function defines the way in which the different attributes should - * be combined into the final markup, which is then serialized by the block - * editor into `post_content`. + * Dynamic block – rendering happens in PHP (render.php). */ -export default function save({ attributes }: SaveProps): React.JSX.Element { - const { contact, showAddress, showPhone, showLegend, legend } = attributes - - return ( -
- {showLegend && {legend}} - - - {showAddress && ( - <> - - - - )} - {showPhone && } -
- ) +export default function save() { + return null } diff --git a/src/Blocks/donation-amounts/block.json b/src/Blocks/donation-amounts/block.json index b781053..67a4134 100644 --- a/src/Blocks/donation-amounts/block.json +++ b/src/Blocks/donation-amounts/block.json @@ -12,94 +12,46 @@ "attributes": { "settings": { "type": "array", - "source": "query", - "selector": ".donation-amounts", - "query": { - "type": { - "type": "string", - "source": "attribute", - "attribute": "data-type" + "default": [ + { + "type": "single", + "default": true, + "defaultAmount": 10, + "minAmount": 10, + "maxAmount": 10000, + "unit": "€", + "amounts": [{ "value": 10 }, { "value": 20 }, { "value": 30 }] }, - "default": { - "type": "boolean", - "source": "attribute", - "attribute": "data-default" - }, - "defaultAmount": { - "type": "integer", - "source": "attribute", - "attribute": "data-default-amount" - }, - "minAmount": { - "type": "integer", - "source": "attribute", - "attribute": "data-min-amount" - }, - "maxAmount": { - "type": "integer", - "source": "attribute", - "attribute": "data-max-amount" - }, - "unit": { - "type": "string", - "selector": ".donation-amounts__unit", - "source": "text" - }, - "amounts": { - "type": "array", - "source": "query", - "selector": "input[type=\"radio\"]", - "query": { - "value": { - "type": "string", - "source": "attribute", - "attribute": "value" - } - } + { + "type": "recurring", + "default": false, + "defaultAmount": 10, + "minAmount": 10, + "maxAmount": 10000, + "unit": "€", + "amounts": [{ "value": 10 }, { "value": 20 }, { "value": 30 }] } - } + ] }, - "showLegend": { - "type": "boolean", - "source": "attribute", - "selector": ".fame-form__legend:not(.screen-reader-text)", - "attribute": "class", - "default": true - }, - "legend": { - "type": "string", - "source": "text", - "selector": ".fame-form__legend", - "default": "Donation amount" - }, - "other": { - "type": "boolean", - "source": "attribute", - "selector": ".donation-amounts__other", - "attribute": "class", - "default": false - }, - "otherLabel": { - "type": "string", - "source": "text", - "selector": ".donation-amounts__other label", - "default": "Other amount" + "showLegend": { "type": "boolean", "default": true }, + "legend": { "type": "string", "default": "Donation amount" }, + "other": { "type": "boolean", "default": false }, + "otherLabel": { "type": "string", "default": "Other amount" }, + "legendAlign": { "type": "string", "default": "left" }, + "colsAmounts": { + "type": "number", + "default": 3 } }, "supports": { "multiple": false, - "color": { - "background": false, - "text": true - }, + "color": { "background": false, "text": true }, "html": false, - "typography": { - "fontSize": true - } + "typography": { "fontSize": true } }, "icon": "database", "textdomain": "fame_lahjoitukset", "editorScript": "file:./index.js", "editorStyle": "file:./index.css", - "style": "file:./style-index.css" + "render": "file:./render.php" } diff --git a/src/Blocks/donation-amounts/deprecated/block-v1.json b/src/Blocks/donation-amounts/deprecated/block-v1.json deleted file mode 100644 index fa9e9a4..0000000 --- a/src/Blocks/donation-amounts/deprecated/block-v1.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "attributes": { - "settings": { - "type": "array", - "source": "query", - "selector": ".donation-amounts", - "query": { - "type": { - "type": "string", - "source": "attribute", - "attribute": "data-type" - }, - "default": { - "type": "boolean", - "source": "attribute", - "attribute": "data-default" - }, - "defaultAmount": { - "type": "integer", - "source": "attribute", - "attribute": "data-default-amount" - }, - "unit": { - "type": "string", - "selector": ".donation-amounts__unit", - "source": "text" - }, - "amounts": { - "type": "array", - "source": "query", - "selector": "input[type=\"radio\"]", - "query": { - "value": { - "type": "string", - "source": "attribute", - "attribute": "value" - } - } - } - } - }, - "showLegend": { - "type": "boolean", - "source": "attribute", - "selector": ".fame-form__legend:not(.screen-reader-text)", - "attribute": "class", - "default": true - }, - "legend": { - "type": "string", - "source": "text", - "selector": ".fame-form__legend", - "default": "Donation amount" - }, - "other": { - "type": "boolean", - "source": "attribute", - "selector": ".donation-amounts__other", - "attribute": "class", - "default": false - }, - "otherLabel": { - "type": "string", - "source": "text", - "selector": ".donation-amounts__other label", - "default": "Other amount" - } - } -} diff --git a/src/Blocks/donation-amounts/deprecated/save-v1.tsx b/src/Blocks/donation-amounts/deprecated/save-v1.tsx deleted file mode 100644 index 2056d3f..0000000 --- a/src/Blocks/donation-amounts/deprecated/save-v1.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { RichText, useBlockProps } from '@wordpress/block-editor' -import React from 'react' -import { SaveProps } from '../../common/types.ts' -import { DEFAULT_AMOUNT, DEFAULT_LEGEND } from '../../common/donation-amount.ts' -import { Attributes } from '../edit.tsx' -import { __ } from '@wordpress/i18n' - -type SaveAttributes = Attributes & { - type?: boolean -} - -/** - * The save function defines the way in which the different attributes should - * be combined into the final markup, which is then serialized by the block - * editor into `post_content`. - */ -export default function save({ attributes }: SaveProps): React.JSX.Element { - const { settings, other, otherLabel, showLegend, legend } = attributes - - // Visible if other amount is shown or amount buttons is not empty. - const visible = other || settings?.some(type => type?.amounts?.length) - const blockProps = useBlockProps.save({ - className: visible - ? 'fame-form__fieldset fame-form__fieldset--amounts' - : 'fame-form__hidden', - }) - - // Amount must be in cents. - const defaultAmount = - parseInt( - (settings?.find(type => type.default)?.defaultAmount || DEFAULT_AMOUNT).toString() - ) * 100 - - if (!visible) { - return ( -
- -
- ) - } - - return ( -
- - - {settings?.map(type => ( -
- {type.amounts?.map(({ value }, idx) => ( -
- -
- ))} - {other && ( -
-
- {type.unit} - -
- -
- )} -
- ))} - - {/* - The server only cares about `name` field. This hidden - input field must be kept up to date with radio buttons - and free amount field with javascript. - */} - -
- ) -} diff --git a/src/Blocks/donation-amounts/edit.css b/src/Blocks/donation-amounts/edit.css index 69047c7..d6d4ad8 100644 --- a/src/Blocks/donation-amounts/edit.css +++ b/src/Blocks/donation-amounts/edit.css @@ -1,34 +1,2 @@ -.donation-amounts { - display: flex; - align-items: stretch; - gap: 16px; -} - -.donation-amounts > * { - flex: 1; -} - -.wp-block-famehelsinki-donation-amounts .donation-amounts__other-label, -.wp-block-famehelsinki-donation-amounts .donation-amounts__minmax { - font-size: 16px !important; - padding-top: 5px; -} - -.wp-block-famehelsinki-donation-amounts .rich-text { - text-wrap: nowrap !important; -} - -.donation-amounts__other__placeholder, -.donation-amounts .fame-form__label { - padding: var(--form-element-padding); - border: var(--form-element-border); - - &.fame-form__label--default { - border-color: #db1414; - } -} - -.donation-amounts__other__placeholder { - align-self: stretch; - width: 10ch; -} +/* Intentionally left empty. + Styles for this block are defined in the main stylesheet. */ diff --git a/src/Blocks/donation-amounts/edit.tsx b/src/Blocks/donation-amounts/edit.tsx index 04bd67c..f81bb00 100644 --- a/src/Blocks/donation-amounts/edit.tsx +++ b/src/Blocks/donation-amounts/edit.tsx @@ -1,7 +1,20 @@ -import React, { useEffect } from 'react' +import React, { CSSProperties, useEffect } from 'react' import { __ } from '@wordpress/i18n' -import { Button, Flex, PanelBody, TextControl, ToggleControl } from '@wordpress/components' -import { InspectorControls, RichText, useBlockProps } from '@wordpress/block-editor' +import { + Button, + Flex, + PanelBody, + RangeControl, + TextControl, + ToggleControl, +} from '@wordpress/components' +import { + InspectorControls, + RichText, + useBlockProps, + AlignmentToolbar, + BlockControls, +} from '@wordpress/block-editor' import { getDonationLabel, useCurrentDonationType } from '../common/donation-type.ts' import { EditProps } from '../common/types.ts' import { @@ -25,6 +38,8 @@ export type Attributes = { legend?: string other?: boolean otherLabel?: string + legendAlign?: string + colsAmounts?: number } /** @@ -80,7 +95,15 @@ export default function Edit({ clientId, }: EditProps): React.JSX.Element { const { 'famehelsinki/donation-types': types } = context - const { settings, other, legend, showLegend, otherLabel } = attributes + const { + settings, + other, + legend, + showLegend, + otherLabel, + legendAlign = 'left', + colsAmounts: colsAmountsAttr, + } = attributes const currentType = useCurrentDonationType(clientId) const current = settings?.find(({ type }) => type === currentType) @@ -132,7 +155,12 @@ export default function Edit({ }) }, [types, currentType, settings, setAttributes]) - const blockProps = useBlockProps({ className: 'fame-form__fieldset--amounts' }) + const colsAmounts = Math.max(1, Math.min(3, colsAmountsAttr ?? 3)) + + const blockProps = useBlockProps({ + className: 'fame-form__fieldset--amounts', + style: { ['--amount-cols' as any]: String(colsAmounts) }, + }) // Use effect hook should ensure that settings will be set to an array. if (!settings) return
Loading...
@@ -142,6 +170,12 @@ export default function Edit({ return ( <> + + setAttributes({ legendAlign: next || 'left' })} + /> + setAttributes({ showLegend: value })} /> - {visible && !showLegend && ( - setAttributes({ legend: value })} - /> - )} + setAttributes({ legend: value })} + /> + setAttributes({ colsAmounts: v ?? 3 })} + /> + + + \ No newline at end of file diff --git a/src/Blocks/form-controls/save.tsx b/src/Blocks/form-controls/save.tsx index 6a5149d..9e8345e 100644 --- a/src/Blocks/form-controls/save.tsx +++ b/src/Blocks/form-controls/save.tsx @@ -1,46 +1,8 @@ -import { __ } from '@wordpress/i18n' -import { useBlockProps } from '@wordpress/block-editor' -import React from 'react' -import { SaveProps } from '../common/types.ts' +/** + * Dynamic block – rendering happens in PHP (render.php). + * Nothing is saved to post_content. + */ -type Attributes = { - submitLabel?: string // legacy - submitLabelSingle?: string - submitLabelRecurring?: string +export default function save() { + return null } - -const Save = ({ - attributes: { - submitLabel = __('Donate', 'fame_lahjoitukset'), - submitLabelSingle, - submitLabelRecurring, - }, -}: SaveProps): React.JSX.Element => { - // What text should be placed directly inside the button? - // if single/recurring same -> ok - // otherwise use legacy and give data attributes to JS for replacement - const resolved = - submitLabelSingle && submitLabelRecurring && submitLabelSingle === submitLabelRecurring - ? submitLabelSingle - : submitLabel - - return ( -
- - - -
- ) -} - -export default Save