[ 'label' => $l . 'bg_variant.label', 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', 'items' => [ ['label' => $l . 'bg_variant.option.none', 'value' => 'none'], ['label' => $l . 'bg_variant.option.orange', 'value' => 'orange'], ['label' => $l . 'bg_variant.option.blue', 'value' => 'blue'], ['label' => $l . 'bg_variant.option.graphite', 'value' => 'graphite'], ['label' => $l . 'bg_variant.option.midnight', 'value' => 'midnight'], ], 'default' => 'none', ], ], ]); // ------------------------------------------------------------------------- // Shared background image for the VITEC containers, with the two settings a // background actually needs: how it scales and where it sits. // // Deliberately no crop variants (Clause 9.9): a background is framed by // background-size / background-position in CSS, not by cropping the file. // Cropping here would fight the two selects below. // // The values are written so the front end can hand them straight to CSS. // `stretch` is the one exception - it has no CSS keyword and maps to // `100% 100%`; spelled out here because "stretch" is what an editor looks // for, not "100% 100%". // ------------------------------------------------------------------------- ExtensionManagementUtility::addTCAcolumns('tt_content', [ 'tx_vitec_bg_image' => [ 'label' => $l . 'bg_image.label', 'description' => $l . 'bg_image.description', 'config' => [ 'type' => 'file', 'maxitems' => 1, 'allowed' => 'common-image-types', ], ], 'tx_vitec_bg_size' => [ 'label' => $l . 'bg_size.label', // Re-renders the form when the value changes, so the "Size %" field // below appears the moment "Custom" is picked instead of only after // a save. FormEngine keeps the current form state across the reload // (SingleFieldContainer -> ReloadOnFieldChange); whether it asks // first is the editor's own "verify on change" preference. 'onChange' => 'reload', 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', 'items' => [ ['label' => $l . 'bg_size.option.cover', 'value' => 'cover'], ['label' => $l . 'bg_size.option.contain', 'value' => 'contain'], ['label' => $l . 'bg_size.option.stretch', 'value' => 'stretch'], ['label' => $l . 'bg_size.option.auto', 'value' => 'auto'], ['label' => $l . 'bg_size.option.custom', 'value' => 'custom'], ], 'default' => 'cover', ], ], // Only meaningful together with bg_size = custom, and hidden otherwise // so the form never shows a percentage that has no effect. The renderer // resolves it into the emitted `size` (e.g. "80%"), so the front end // keeps getting one CSS-ready value instead of a second special case. 'tx_vitec_bg_size_percent' => [ 'label' => $l . 'bg_size_percent.label', 'description' => $l . 'bg_size_percent.description', 'displayCond' => 'FIELD:tx_vitec_bg_size:=:custom', 'config' => [ 'type' => 'number', 'size' => 6, 'default' => 100, 'range' => [ 'lower' => 1, 'upper' => 500, ], ], ], 'tx_vitec_bg_position' => [ 'label' => $l . 'bg_position.label', // Same reason as on bg_size: reveal the custom fields immediately. 'onChange' => 'reload', 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', 'items' => [ ['label' => $l . 'bg_position.option.top_left', 'value' => 'left top'], ['label' => $l . 'bg_position.option.top_center', 'value' => 'center top'], ['label' => $l . 'bg_position.option.top_right', 'value' => 'right top'], ['label' => $l . 'bg_position.option.center_left', 'value' => 'left center'], ['label' => $l . 'bg_position.option.centered', 'value' => 'center center'], ['label' => $l . 'bg_position.option.center_right', 'value' => 'right center'], ['label' => $l . 'bg_position.option.bottom_left', 'value' => 'left bottom'], ['label' => $l . 'bg_position.option.bottom_center', 'value' => 'center bottom'], ['label' => $l . 'bg_position.option.bottom_right', 'value' => 'right bottom'], ['label' => $l . 'bg_position.option.custom', 'value' => 'custom'], ], 'default' => 'center center', ], ], ]); // Custom position: four edge offsets in percent, shown only when the select // is on "custom". `nullable` matters here - an empty field has to stay // distinguishable from an explicit 0, which is a valid offset (flush against // that edge). Empty means "centred on this axis". // // CSS cannot take a top AND a bottom offset at once, so these four are not // written out literally; ContainerBackgroundRenderer folds them into the // percentage form, where "10% from the right" is simply 90%. $positionOffsets = []; foreach (['top', 'bottom', 'left', 'right'] as $edge) { $positionOffsets['tx_vitec_bg_pos_' . $edge] = [ 'label' => $l . 'bg_pos.' . $edge, 'description' => $l . 'bg_pos.description', 'displayCond' => 'FIELD:tx_vitec_bg_position:=:custom', 'config' => [ 'type' => 'number', 'size' => 5, 'nullable' => true, // No range on purpose: background-position accepts values below // 0 and above 100, which is how you push an image partly out of // its container. Capping at 0..100 would forbid exactly the // layouts these free offsets exist for. ], ]; } ExtensionManagementUtility::addTCAcolumns('tt_content', $positionOffsets); // One palette, so size and position sit on a single row under the image. $GLOBALS['TCA']['tt_content']['palettes']['vitec_background'] = [ 'showitem' => 'tx_vitec_bg_image, --linebreak--, tx_vitec_bg_size, tx_vitec_bg_size_percent, tx_vitec_bg_position,' . ' --linebreak--, tx_vitec_bg_pos_top, tx_vitec_bg_pos_bottom, tx_vitec_bg_pos_left, tx_vitec_bg_pos_right', ]; // ------------------------------------------------------------------------- // Shared parent-level setting: column gap scale (1–5) for the whole grid. // Concerns the container itself (not a single column); maps to a spacing // token in the React frontend. Rendered in the "Grid" palette on top of the // Appearance tab, above the per-column rows. // ------------------------------------------------------------------------- ExtensionManagementUtility::addTCAcolumns('tt_content', [ 'tx_vitec_gap' => [ 'label' => $l . 'gap.label', 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', 'items' => [ ['label' => $l . 'gap.option.1', 'value' => '1'], ['label' => $l . 'gap.option.2', 'value' => '2'], ['label' => $l . 'gap.option.3', 'value' => '3'], ['label' => $l . 'gap.option.4', 'value' => '4'], ['label' => $l . 'gap.option.5', 'value' => '5'], ], 'default' => '3', ], ], ]); // Parent-element palette: settings that concern the whole grid, not a column. $GLOBALS['TCA']['tt_content']['palettes']['vitec_grid'] = [ 'showitem' => 'tx_vitec_gap', ]; // ------------------------------------------------------------------------- // Shared per-column flex alignment fields for the X-column containers. // Positional: col1 = first column, col2 = second, ... (max 4 columns). // "Align" -> CSS align-items (cross-axis alignment of the column's items) // "Justify" -> CSS justify-content (main-axis distribution within the column) // Reused across vitec_cols_50_50 / 33_66 / 66_33 / 33_33_33 / 25_25_25_25. // ------------------------------------------------------------------------- $alignItems = [ ['label' => $l . 'flex.align.flex_start', 'value' => 'flex-start'], ['label' => $l . 'flex.align.center', 'value' => 'center'], ['label' => $l . 'flex.align.flex_end', 'value' => 'flex-end'], ['label' => $l . 'flex.align.stretch', 'value' => 'stretch'], ]; $justifyItems = [ ['label' => $l . 'flex.justify.flex_start', 'value' => 'flex-start'], ['label' => $l . 'flex.justify.center', 'value' => 'center'], ['label' => $l . 'flex.justify.space_between', 'value' => 'space-between'], ['label' => $l . 'flex.justify.space_around', 'value' => 'space-around'], ['label' => $l . 'flex.justify.space_evenly', 'value' => 'space-evenly'], ]; $columnFields = []; for ($i = 1; $i <= 4; $i++) { $columnFields['tx_vitec_col' . $i . '_align'] = [ 'label' => $l . 'flex.align.label', 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', 'items' => $alignItems, 'default' => 'stretch', ], ]; $columnFields['tx_vitec_col' . $i . '_justify'] = [ 'label' => $l . 'flex.justify.label', 'config' => [ 'type' => 'select', 'renderType' => 'selectSingle', 'items' => $justifyItems, 'default' => 'flex-start', ], ]; } ExtensionManagementUtility::addTCAcolumns('tt_content', $columnFields); // One palette per column: Align + Justify on a single row. The visible row // label is overridden per container in its showitem (--palette--;;). for ($i = 1; $i <= 4; $i++) { $GLOBALS['TCA']['tt_content']['palettes']['vitec_col' . $i . '_flex'] = [ 'showitem' => 'tx_vitec_col' . $i . '_align, tx_vitec_col' . $i . '_justify', ]; } // Register VITEC wizard group header $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'] = ($GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'] ?? '') . "\n" . <<<'TSCONFIG' mod.wizards.newContentElement.wizardItems.vitec { header = LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:group.header show = * } TSCONFIG; })();