├── contao ├── templates │ ├── ce_grid_column_stop.html5 │ ├── forms │ │ ├── form_grid_column_stop.html5 │ │ ├── form_grid_stop.html5 │ │ ├── form_grid_column_start.html5 │ │ └── form_grid_start.html5 │ ├── ce_grid_stop.html5 │ ├── ce_grid_column_start.html5 │ └── ce_grid_start.html5 ├── dca │ ├── tl_layout.php │ ├── tl_form_field.php │ └── tl_content.php ├── languages │ ├── de │ │ ├── tl_layout.php │ │ ├── default.php │ │ ├── tl_form_field.php │ │ └── tl_content.php │ └── en │ │ ├── tl_layout.php │ │ ├── default.php │ │ ├── tl_form_field.php │ │ └── tl_content.php └── config │ └── config.php ├── .gitignore ├── config └── services.yaml ├── .editorconfig ├── phpunit.xml.dist ├── tests └── ContaoClickpressJsBundleTest.php ├── src ├── ContaoClickpressGridBundle.php ├── ContaoManager │ └── Plugin.php ├── Forms │ ├── FormGridStop.php │ ├── FormGridColumnStop.php │ ├── FormGridColumnStart.php │ └── FormGridStart.php ├── Element │ ├── GridColumnStop.php │ ├── GridColumnStart.php │ ├── GridStop.php │ └── GridStart.php └── EventListener │ └── ClickpressGridListener.php ├── public ├── clickpress-grid.css.map ├── clickpress-grid.css └── clickpress-grid.scss ├── composer.json ├── .php_cs.dist ├── README.md └── LICENSE /contao/templates/ce_grid_column_stop.html5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contao/templates/forms/form_grid_column_stop.html5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contao/templates/ce_grid_stop.html5: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /contao/templates/forms/form_grid_stop.html5: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /contao/templates/forms/form_grid_column_start.html5: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Composer 2 | /composer.lock 3 | /vendor/ 4 | 5 | .idea/ 6 | 7 | # PhpUnit 8 | /phpunit.xml 9 | -------------------------------------------------------------------------------- /contao/templates/forms/form_grid_start.html5: -------------------------------------------------------------------------------- 1 |
cssID ?>> 2 | 3 |
-------------------------------------------------------------------------------- /config/services.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | _defaults: 3 | autowire: true 4 | autoconfigure: true 5 | 6 | Clickpress\ContaoClickpressGridBundle\: 7 | resource: ../src 8 | exclude: ../src/{ContaoManager} 9 | -------------------------------------------------------------------------------- /contao/templates/ce_grid_column_start.html5: -------------------------------------------------------------------------------- 1 |
cssID ?>style): ?> style="style ?>"> 2 | 3 | headline): ?> 4 | <hl ?>>headline ?>hl ?>> 5 | 6 | -------------------------------------------------------------------------------- /contao/templates/ce_grid_start.html5: -------------------------------------------------------------------------------- 1 |
cssID ?>> 2 | block('headline'); ?> 3 | headline): ?> 4 | <hl ?>>headline ?>hl ?>> 5 | 6 | endblock(); ?> 7 | 8 |
9 | -------------------------------------------------------------------------------- /contao/dca/tl_layout.php: -------------------------------------------------------------------------------- 1 | 'checkbox', 9 | 'label' => &$GLOBALS['TL_LANG']['tl_layout']['cp_grid_load_css'], 10 | 'sql' => "char(1) NOT NULL default '1'", 11 | ]; 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.{php,twig,yml}] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | [*.{html5,svg,min.css,min.js}] 17 | insert_final_newline = false 18 | -------------------------------------------------------------------------------- /contao/languages/de/tl_layout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ./tests 15 | 16 | 17 | 18 | 19 | 20 | ./src 21 | 22 | ./src/Resources 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/ContaoClickpressJsBundleTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Clickpress\ContaoClickpressGridBundle\ContaoClickpressGridBundle', $bundle); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ContaoClickpressGridBundle.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle; 17 | 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; 20 | use Symfony\Component\HttpKernel\Bundle\AbstractBundle; 21 | 22 | class ContaoClickpressGridBundle extends AbstractBundle 23 | { 24 | public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void 25 | { 26 | $container->import('../config/services.yaml'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ContaoManager/Plugin.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\ContaoManager; 17 | 18 | use Clickpress\ContaoClickpressGridBundle\ContaoClickpressGridBundle; 19 | use Contao\CoreBundle\ContaoCoreBundle; 20 | use Contao\ManagerPlugin\Bundle\BundlePluginInterface; 21 | use Contao\ManagerPlugin\Bundle\Config\BundleConfig; 22 | use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; 23 | 24 | class Plugin implements BundlePluginInterface 25 | { 26 | public function getBundles(ParserInterface $parser): array 27 | { 28 | return [ 29 | BundleConfig::create(ContaoClickpressGridBundle::class) 30 | ->setLoadAfter([ContaoCoreBundle::class]), 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contao/languages/de/default.php: -------------------------------------------------------------------------------- 1 | 18 | * @author Stefan Schulz-Lauterbach 19 | * @author Jannik Nölke 20 | */ 21 | 22 | use Clickpress\ContaoClickpressGridBundle\Forms\FormGridColumnStart; 23 | use Clickpress\ContaoClickpressGridBundle\Forms\FormGridColumnStop; 24 | use Clickpress\ContaoClickpressGridBundle\Forms\FormGridStart; 25 | use Clickpress\ContaoClickpressGridBundle\Forms\FormGridStop; 26 | 27 | $GLOBALS['TL_WRAPPERS']['start'][] = 'cp_grid_start'; 28 | $GLOBALS['TL_WRAPPERS']['stop'][] = 'cp_grid_stop'; 29 | $GLOBALS['TL_WRAPPERS']['start'][] = 'cp_column_start'; 30 | $GLOBALS['TL_WRAPPERS']['stop'][] = 'cp_column_stop'; 31 | 32 | $GLOBALS['TL_FFL']['cp_grid_start'] = FormGridStart::class; 33 | $GLOBALS['TL_FFL']['cp_grid_stop'] = FormGridStop::class; 34 | $GLOBALS['TL_FFL']['cp_column_start'] = FormGridColumnStart::class; 35 | $GLOBALS['TL_FFL']['cp_column_stop'] = FormGridColumnStop::class; 36 | -------------------------------------------------------------------------------- /public/clickpress-grid.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["clickpress-grid.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;;AAIJ;EACE;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;;AAKJ;EACE;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAGF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE","file":"clickpress-grid.css"} -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clickpress/contao-clickpress-grid", 3 | "type": "contao-bundle", 4 | "description": "Grid System for Contao based on CSS Grid", 5 | "license": "LGPL-3.0-or-later", 6 | "authors": [ 7 | { 8 | "name": "Stefan Schulz-Lauterbach", 9 | "email": "ssl@clickpress.de", 10 | "homepage": "https://clickpress.de", 11 | "role": "Developer" 12 | }, 13 | { 14 | "name": "Martin Auswöger", 15 | "email": "martin@madeyourday.net", 16 | "homepage": "https://madeyourday.net", 17 | "role": "Developer" 18 | }, 19 | { 20 | "name": "Jannik Nölke", 21 | "email": "mail@jaynoe.de", 22 | "homepage": "https://plakart.net", 23 | "role": "Developer" 24 | } 25 | ], 26 | "require": { 27 | "php": "^8.1", 28 | "contao/core-bundle": "^5.3" 29 | }, 30 | "conflict": { 31 | "contao/manager-plugin": "<2.0 || >=3.0" 32 | }, 33 | "require-dev": { 34 | "contao/manager-plugin": "^2.3", 35 | "contao/easy-coding-standard": "^3.1", 36 | "phpunit/phpunit": "^9.3", 37 | "symfony/phpunit-bridge": "^5.4" 38 | }, 39 | "extra": { 40 | "contao-manager-plugin": "Clickpress\\ContaoClickpressGridBundle\\ContaoManager\\Plugin" 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "Clickpress\\ContaoClickpressGridBundle\\": "src/" 45 | } 46 | }, 47 | "autoload-dev": { 48 | "psr-4": { 49 | "Clickpress\\ContaoClickpressGridBundle\\Tests\\": "tests/" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Forms/FormGridStop.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\Forms; 17 | 18 | use Contao\BackendTemplate; 19 | use Contao\System; 20 | use Contao\Widget; 21 | 22 | /** 23 | * Grid stop content element Taken with friendly permission from RockSolid Columns. 24 | */ 25 | class FormGridStop extends Widget 26 | { 27 | /** 28 | * @var string Template 29 | */ 30 | protected $strTemplate = 'form_grid_stop'; 31 | 32 | /** 33 | * Do not validate. 34 | */ 35 | public function validate(): void 36 | { 37 | } 38 | 39 | /** 40 | * Parse the template file and return it as string. 41 | * 42 | * @param array $arrAttributes An optional attributes array 43 | * 44 | * @return string The template markup 45 | */ 46 | public function parse($arrAttributes = null): string 47 | { 48 | $request = System::getContainer()->get('request_stack')->getCurrentRequest(); 49 | 50 | if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request)) { 51 | $objTemplate = new BackendTemplate('be_wildcard'); 52 | 53 | return $objTemplate->parse(); 54 | } 55 | 56 | return parent::parse($arrAttributes); 57 | } 58 | 59 | /** 60 | * Generate the widget and return it as string. 61 | * 62 | * @return string The widget markup 63 | */ 64 | public function generate(): string 65 | { 66 | return ''; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Forms/FormGridColumnStop.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\Forms; 17 | 18 | use Contao\BackendTemplate; 19 | use Contao\System; 20 | use Contao\Widget; 21 | 22 | /** 23 | * Column stop content element Taken with friendly permission from RockSolid Columns. 24 | */ 25 | class FormGridColumnStop extends Widget 26 | { 27 | /** 28 | * @var string Template 29 | */ 30 | protected $strTemplate = 'form_grid_column_stop'; 31 | 32 | /** 33 | * Do not validate. 34 | */ 35 | public function validate(): void 36 | { 37 | } 38 | 39 | /** 40 | * Parse the template file and return it as string. 41 | * 42 | * @param array $arrAttributes An optional attributes array 43 | * 44 | * @return string The template markup 45 | */ 46 | public function parse($arrAttributes = null): string 47 | { 48 | $request = System::getContainer()->get('request_stack')->getCurrentRequest(); 49 | 50 | if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request)) { 51 | $objTemplate = new BackendTemplate('be_wildcard'); 52 | 53 | return $objTemplate->parse(); 54 | } 55 | 56 | return parent::parse($arrAttributes); 57 | } 58 | 59 | /** 60 | * Generate the widget and return it as string. 61 | * 62 | * @return string The widget markup 63 | */ 64 | public function generate(): string 65 | { 66 | return ''; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Forms/FormGridColumnStart.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\Forms; 17 | 18 | use Contao\BackendTemplate; 19 | use Contao\System; 20 | use Contao\Widget; 21 | 22 | /** 23 | * Column start content element Taken with friendly permission from RockSolid Columns. 24 | */ 25 | class FormGridColumnStart extends Widget 26 | { 27 | /** 28 | * @var string Template 29 | */ 30 | protected $strTemplate = 'form_grid_column_start'; 31 | 32 | /** 33 | * Do not validate. 34 | */ 35 | public function validate(): void 36 | { 37 | } 38 | 39 | /** 40 | * Parse the template file and return it as string. 41 | * 42 | * @param array $arrAttributes An optional attributes array 43 | * 44 | * @return string The template markup 45 | */ 46 | public function parse($arrAttributes = null): string 47 | { 48 | $request = System::getContainer()->get('request_stack')->getCurrentRequest(); 49 | 50 | if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request)) { 51 | $objTemplate = new BackendTemplate('be_wildcard'); 52 | 53 | return $objTemplate->parse(); 54 | } 55 | 56 | return parent::parse($arrAttributes); 57 | } 58 | 59 | /** 60 | * Generate the widget and return it as string. 61 | * 62 | * @return string The widget markup 63 | */ 64 | public function generate(): string 65 | { 66 | return ''; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Element/GridColumnStop.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\Element; 17 | 18 | use Contao\ContentModel; 19 | use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController; 20 | use Contao\CoreBundle\DependencyInjection\Attribute\AsContentElement; 21 | use Contao\CoreBundle\Routing\ScopeMatcher; 22 | use Contao\Template; 23 | use Symfony\Component\HttpFoundation\Request; 24 | use Symfony\Component\HttpFoundation\RequestStack; 25 | use Symfony\Component\HttpFoundation\Response; 26 | 27 | /** 28 | * Column stop content element Taken with friendly permission from RockSolid Columns. 29 | */ 30 | #[AsContentElement(type: 'cp_column_stop', category: 'cp_grid', template: 'ce_grid_column_stop')] 31 | class GridColumnStop extends AbstractContentElementController 32 | { 33 | public function __construct(readonly RequestStack $requestStack, readonly ScopeMatcher $scopeMatcher) 34 | { 35 | } 36 | 37 | protected function getResponse(Template $template, ContentModel $model, Request $request): Response 38 | { 39 | if ($this->scopeMatcher->isBackendRequest($this->requestStack->getCurrentRequest())) { 40 | return new Response(''); 41 | } 42 | 43 | $parentKey = ($model->ptable ?: 'tl_article').'__'.$model->pid; 44 | if (isset($GLOBALS['TL_CP_GRID'][$parentKey]) && !$GLOBALS['TL_CP_GRID'][$parentKey]['active']) { 45 | $GLOBALS['TL_CP_GRID'][$parentKey]['active'] = true; 46 | } 47 | 48 | return $template->getResponse(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contao/languages/en/tl_form_field.php: -------------------------------------------------------------------------------- 1 | '1 column', 12 | 'grid_50_50' => '2 columns', 13 | 'grid_33_33_33' => '3 columns', 14 | 'grid_25_25_25_25' => '4 columns', 15 | 'grid_75_25' => '2 columns - divided into 75% 25%', 16 | 'grid_25_75' => '2 columns - divided into 25% 75%', 17 | 'grid_66_33' => '2 columns - divided into 66% 33%', 18 | 'grid_33_66' => '2 columns - divided into 33% 66%', 19 | 'grid_60_40' => '2 columns - divided into 60% 40%', 20 | 'grid_40_60' => '2 columns - divided into 40% 60%', 21 | 'grid_50_25_25' => '3 columns - divided into 50% 25% 25%', 22 | 'grid_25_50_25' => '3 columns - divided into 25% 50% 25%', 23 | 'grid_25_25_50' => '3 columns - divided into 25% 25% 50%', 24 | 'grid_40_30_30' => '3 columns - divided into 40% 30% 30%', 25 | 'grid_30_40_30' => '3 columns - divided into 30% 40% 30%', 26 | 'grid_30_30_40' => '3 columns - divided into 30% 30% 40%', 27 | 'grid_20_40_40' => '3 columns - divided into 20% 40% 40%', 28 | 'grid_40_20_40' => '3 columns - divided into 40% 20% 40%', 29 | 'grid_40_40_20' => '3 columns - divided into 40% 40% 20%', 30 | 'grid_40_20_20_20' => '4 columns - divided into 40% 20% 20% 20%', 31 | 'grid_20_40_20_20' => '4 columns - divided into 20% 40% 20% 20%', 32 | 'grid_20_20_40_20' => '4 columns - divided into 20% 20% 40% 20%', 33 | 'grid_20_20_20_40' => '4 columns - divided into 20% 20% 20% 40%', 34 | ]; 35 | 36 | // Legend 37 | $GLOBALS['TL_LANG']['tl_form_field']['form_cp_grid_legend'] = 'Column configuration'; 38 | -------------------------------------------------------------------------------- /src/Element/GridColumnStart.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\Element; 17 | 18 | use Contao\ContentModel; 19 | use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController; 20 | use Contao\CoreBundle\DependencyInjection\Attribute\AsContentElement; 21 | use Contao\CoreBundle\Routing\ScopeMatcher; 22 | use Contao\StringUtil; 23 | use Contao\Template; 24 | use Symfony\Component\HttpFoundation\Request; 25 | use Symfony\Component\HttpFoundation\RequestStack; 26 | use Symfony\Component\HttpFoundation\Response; 27 | 28 | /** 29 | * Column start content element Taken with friendly permission from RockSolid Columns. 30 | */ 31 | #[AsContentElement(type: 'cp_column_start', category: 'cp_grid', template: 'ce_grid_column_start')] 32 | class GridColumnStart extends AbstractContentElementController 33 | { 34 | public function __construct(readonly RequestStack $requestStack, readonly ScopeMatcher $scopeMatcher) 35 | { 36 | } 37 | 38 | protected function getResponse(Template $template, ContentModel $model, Request $request): Response 39 | { 40 | if ($this->scopeMatcher->isBackendRequest($this->requestStack->getCurrentRequest())) { 41 | $listingTitle = ''; 42 | 43 | if($model->headline) { 44 | $headline = StringUtil::deserialize($model->headline, true); 45 | $listingTitle = $headline['value']; 46 | } 47 | 48 | if($model->title) { 49 | $listingTitle = $model->title; 50 | } 51 | 52 | return new Response($listingTitle); 53 | } 54 | 55 | return $template->getResponse(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contao/languages/de/tl_form_field.php: -------------------------------------------------------------------------------- 1 | '1 Spalte', 12 | 'grid_50_50' => '2 Spalten', 13 | 'grid_33_33_33' => '3 Spalten', 14 | 'grid_25_25_25_25' => '4 Spalten', 15 | 'grid_20_20_20_20_20' => '5 Spalten', 16 | 'grid_75_25' => '2 Spalten - aufgeteilt in 75% 25%', 17 | 'grid_25_75' => '2 Spalten - aufgeteilt in 25% 75%', 18 | 'grid_66_33' => '2 Spalten - aufgeteilt in 66% 33%', 19 | 'grid_33_66' => '2 Spalten - aufgeteilt in 33% 66%', 20 | 'grid_60_40' => '2 Spalten - aufgeteilt in 60% 40%', 21 | 'grid_40_60' => '2 Spalten - aufgeteilt in 40% 60%', 22 | 'grid_50_25_25' => '3 Spalten - aufgeteilt in 50% 25% 25%', 23 | 'grid_25_50_25' => '3 Spalten - aufgeteilt in 25% 50% 25%', 24 | 'grid_25_25_50' => '3 Spalten - aufgeteilt in 25% 25% 50%', 25 | 'grid_40_30_30' => '3 Spalten - aufgeteilt in 40% 30% 30%', 26 | 'grid_30_40_30' => '3 Spalten - aufgeteilt in 30% 40% 30%', 27 | 'grid_30_30_40' => '3 Spalten - aufgeteilt in 30% 30% 40%', 28 | 'grid_20_40_40' => '3 Spalten - aufgeteilt in 20% 40% 40%', 29 | 'grid_40_20_40' => '3 Spalten - aufgeteilt in 40% 20% 40%', 30 | 'grid_40_40_20' => '3 Spalten - aufgeteilt in 40% 40% 20%', 31 | 'grid_40_20_20_20' => '4 Spalten - aufgeteilt in 40% 20% 20% 20%', 32 | 'grid_20_40_20_20' => '4 Spalten - aufgeteilt in 20% 40% 20% 20%', 33 | 'grid_20_20_40_20' => '4 Spalten - aufgeteilt in 20% 20% 40% 20%', 34 | 'grid_20_20_20_40' => '4 Spalten - aufgeteilt in 20% 20% 20% 40%', 35 | ]; 36 | 37 | // Legend 38 | $GLOBALS['TL_LANG']['tl_form_field']['form_cp_grid_legend'] = 'Spalten-Konfiguration'; 39 | -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | exclude('Resources') 14 | ->in([__DIR__.'/src', __DIR__.'/tests']) 15 | ; 16 | 17 | return PhpCsFixer\Config::create() 18 | ->setRules([ 19 | '@Symfony' => true, 20 | '@Symfony:risky' => true, 21 | '@PHPUnit60Migration:risky' => true, 22 | 'align_multiline_comment' => true, 23 | 'array_syntax' => ['syntax' => 'short'], 24 | 'combine_consecutive_issets' => true, 25 | 'combine_consecutive_unsets' => true, 26 | 'compact_nullable_typehint' => true, 27 | 'concat_space' => ['spacing' => 'one'], 28 | 'general_phpdoc_annotation_remove' => [ 29 | 'expectedException', 30 | 'expectedExceptionMessage', 31 | ], 32 | 'header_comment' => ['header' => $header], 33 | 'heredoc_to_nowdoc' => true, 34 | 'linebreak_after_opening_tag' => true, 35 | 'native_function_invocation' => [ 36 | 'include' => ['@compiler_optimized'], 37 | ], 38 | 'no_null_property_initialization' => true, 39 | 'no_superfluous_elseif' => true, 40 | 'no_unreachable_default_argument_value' => true, 41 | 'no_useless_else' => true, 42 | 'no_useless_return' => true, 43 | 'ordered_class_elements' => true, 44 | 'ordered_imports' => true, 45 | 'php_unit_strict' => true, 46 | 'phpdoc_add_missing_param_annotation' => true, 47 | 'phpdoc_order' => true, 48 | 'phpdoc_types_order' => [ 49 | 'null_adjustment' => 'always_last', 50 | 'sort_algorithm' => 'none', 51 | ], 52 | 'strict_comparison' => true, 53 | 'strict_param' => true, 54 | ]) 55 | ->setFinder($finder) 56 | ->setRiskyAllowed(true) 57 | ->setUsingCache(false) 58 | ; 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Version](http://img.shields.io/packagist/v/clickpress/contao-clickpress-grid.svg?style=flat-square)](https://packagist.org/packages/clickpress/contao-clickpress-grid) [![GitHub license](https://img.shields.io/badge/license-GPL-blue.svg?style=flat-square)](https://raw.githubusercontent.com/clickpress/contao-clickpress-grid/master/LICENSE) 2 | # Grid CSS for Contao 3 | 4 | Add columns to your page content. 5 | 6 | Contao Clickpress Grid uses [CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) and so less HTML is needed. 7 | 8 | ## Installation 9 | ```console 10 | composer require clickpress/contao-clickpress-grid 11 | ``` 12 | or use the Contao Manager. 13 | 14 | ## Usage 15 | 1. Enable the built-in CSS grid in your Contao page layout settings or create your own custom CSS. 16 | 2. Add a new content element of type "Gridset Start". The corresponding "Gridset End" will be added automatically. 17 | 3. Configure your column settings in the "Gridset Start" element. 18 | 4. Place your desired content elements between the "Gridset Start" and "Gridset End" elements. 19 | 20 | If you need multiple content elements within a single column, wrap them with "Column Start" and "Column End" elements. 21 | 22 | ## Customized CSS 23 | To override the default grid styles: 24 | 1. Uncheck "Load Clickpress Grid CSS" in the page layout settings. 25 | 2. Copy and customize the clickpress-grid.scss file (https://github.com/clickpress/contao-clickpress-grid/blob/main/public/clickpress-grid.scss) 26 | 3. Include your custom CSS as an external stylesheet in the layout settings. 27 | 28 | ## Inside 29 | This is the result of a 40% 30% 30% setting on desktop. So, three columns will be displayed in a row. The fourth column will be placed in a new row, automatically. 30 | ```html 31 |
32 |
...
33 |
...
34 |
...
35 |
...
36 |
...
37 |
38 | ``` 39 | 40 | ## Credits 41 | Big thanks to [rocksolid_columns](https://github.com/madeyourday/contao-rocksolid-columns) for some parts of the module. 42 | -------------------------------------------------------------------------------- /src/Element/GridStop.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\Element; 17 | 18 | use Contao\ContentModel; 19 | use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController; 20 | use Contao\CoreBundle\DependencyInjection\Attribute\AsContentElement; 21 | use Contao\CoreBundle\Routing\ScopeMatcher; 22 | use Contao\Template; 23 | use Symfony\Component\HttpFoundation\Request; 24 | use Symfony\Component\HttpFoundation\RequestStack; 25 | use Symfony\Component\HttpFoundation\Response; 26 | 27 | /** 28 | * Grid stop content element Taken with friendly permission from RockSolid Columns. 29 | */ 30 | #[AsContentElement(type: 'cp_grid_stop', category: 'cp_grid', template: 'ce_grid_stop')] 31 | class GridStop extends AbstractContentElementController 32 | { 33 | public function __construct(readonly RequestStack $requestStack, readonly ScopeMatcher $scopeMatcher) 34 | { 35 | } 36 | 37 | protected function getResponse(Template $template, ContentModel $model, Request $request): Response 38 | { 39 | if ($this->scopeMatcher->isBackendRequest($this->requestStack->getCurrentRequest())) { 40 | return new Response(''); 41 | } 42 | 43 | $parentKey = ($model->ptable ?: 'tl_article').'__'.$model->pid; 44 | 45 | if (isset($GLOBALS['TL_CP_GRID'][$parentKey])) { 46 | if (!$GLOBALS['TL_CP_GRID'][$parentKey]['active']) { 47 | trigger_error( 48 | 'Missing column stop element before column wrapper stop element ID '.$model->id.'.', 49 | E_USER_WARNING, 50 | ); 51 | } 52 | unset($GLOBALS['TL_CP_GRID'][$parentKey]); 53 | } 54 | 55 | return $template->getResponse(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Forms/FormGridStart.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\Forms; 17 | 18 | use Contao\BackendTemplate; 19 | use Contao\System; 20 | use Contao\Widget; 21 | 22 | /** 23 | * Grid start content element Taken with friendly permission from RockSolid Columns. 24 | */ 25 | class FormGridStart extends Widget 26 | { 27 | /** 28 | * @var string Template 29 | */ 30 | protected $strTemplate = 'form_grid_start'; 31 | 32 | /** 33 | * Do not validate. 34 | */ 35 | public function validate(): void 36 | { 37 | } 38 | 39 | /** 40 | * Parse the template file and return it as string. 41 | * 42 | * @param array $arrAttributes An optional attributes array 43 | * 44 | * @return string The template markup 45 | */ 46 | public function parse($arrAttributes = null): string 47 | { 48 | $request = System::getContainer()->get('request_stack')->getCurrentRequest(); 49 | if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request)) { 50 | $objTemplate = new BackendTemplate('be_wildcard'); 51 | 52 | return $objTemplate->parse(); 53 | } 54 | $gridClasses = [ 55 | str_replace('grid', 'grid_desktop', $this->form_cp_grid_desktop), 56 | str_replace('grid', 'grid_tablet', $this->form_cp_grid_tablet), 57 | str_replace('grid', 'grid_mobile', $this->form_cp_grid_mobile), 58 | ]; 59 | $arrAttributes['gridClasses'] = implode(' ', $gridClasses); 60 | 61 | return parent::parse($arrAttributes); 62 | } 63 | 64 | /** 65 | * Generate the widget and return it as string. 66 | * 67 | * @return string The widget markup 68 | */ 69 | public function generate(): string 70 | { 71 | return ''; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /contao/languages/en/tl_content.php: -------------------------------------------------------------------------------- 1 | '1 column', 28 | 'grid_50_50' => '2 columns', 29 | 'grid_33_33_33' => '3 columns', 30 | 'grid_25_25_25_25' => '4 columns', 31 | 'grid_20_20_20_20_20' => '5 columns', 32 | 'grid_16_16_16_16_16_16' => '6 columns', 33 | 'grid_75_25' => '2 columns - divided into 75% 25%', 34 | 'grid_25_75' => '2 columns - divided into 25% 75%', 35 | 'grid_66_33' => '2 columns - divided into 66% 33%', 36 | 'grid_33_66' => '2 columns - divided into 33% 66%', 37 | 'grid_60_40' => '2 columns - divided into 60% 40%', 38 | 'grid_40_60' => '2 columns - divided into 40% 60%', 39 | 'grid_50_25_25' => '3 columns - divided into 50% 25% 25%', 40 | 'grid_25_50_25' => '3 columns - divided into 25% 50% 25%', 41 | 'grid_25_25_50' => '3 columns - divided into 25% 25% 50%', 42 | 'grid_40_30_30' => '3 columns - divided into 40% 30% 30%', 43 | 'grid_30_40_30' => '3 columns - divided into 30% 40% 30%', 44 | 'grid_30_30_40' => '3 columns - divided into 30% 30% 40%', 45 | 'grid_20_40_40' => '3 columns - divided into 20% 40% 40%', 46 | 'grid_40_20_40' => '3 columns - divided into 40% 20% 40%', 47 | 'grid_40_40_20' => '3 columns - divided into 40% 40% 20%', 48 | 'grid_40_20_20_20' => '4 columns - divided into 40% 20% 20% 20%', 49 | 'grid_20_40_20_20' => '4 columns - divided into 20% 40% 20% 20%', 50 | 'grid_20_20_40_20' => '4 columns - divided into 20% 20% 40% 20%', 51 | 'grid_20_20_20_40' => '4 columns - divided into 20% 20% 20% 40%', 52 | ]; 53 | 54 | $GLOBALS['TL_LANG']['tl_content']['cp_gap_options'] = [ 55 | 'gap_0' => 'no gap', 56 | 'gap_1' => '1', 57 | 'gap_2' => '2', 58 | 'gap_3' => '3', 59 | 'gap_4' => '4', 60 | 'gap_5' => '5', 61 | 'gap_6' => '6', 62 | 'gap_7' => '7', 63 | 'gap_8' => '8', 64 | 'gap_9' => '9', 65 | 'gap_10' => '10', 66 | 'gap_11' => '11', 67 | 'gap_12' => '12', 68 | ]; 69 | 70 | // Legend 71 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_legend'] = 'Column configuration'; 72 | -------------------------------------------------------------------------------- /contao/languages/de/tl_content.php: -------------------------------------------------------------------------------- 1 | 'oben', 34 | 'center' => 'zentriert', 35 | 'bottom' => 'unten', 36 | 'stretch' => 'strecken', 37 | 'baseline' => 'Grundlinie', 38 | ]; 39 | 40 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_halign_options'] = [ 41 | 'left' => 'links', 42 | 'center' => 'zentriert', 43 | 'right' => 'rechts', 44 | 'stretch' => 'strecken', 45 | ]; 46 | 47 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_options'] = [ 48 | 'grid_100' => '1 Spalte', 49 | 'grid_50_50' => '2 Spalten', 50 | 'grid_33_33_33' => '3 Spalten', 51 | 'grid_25_25_25_25' => '4 Spalten', 52 | 'grid_20_20_20_20_20' => '5 Spalten', 53 | 'grid_16_16_16_16_16_16' => '6 Spalten', 54 | 'grid_75_25' => '2 Spalten - aufgeteilt in 75% 25%', 55 | 'grid_25_75' => '2 Spalten - aufgeteilt in 25% 75%', 56 | 'grid_66_33' => '2 Spalten - aufgeteilt in 66% 33%', 57 | 'grid_33_66' => '2 Spalten - aufgeteilt in 33% 66%', 58 | 'grid_60_40' => '2 Spalten - aufgeteilt in 60% 40%', 59 | 'grid_40_60' => '2 Spalten - aufgeteilt in 40% 60%', 60 | 'grid_50_25_25' => '3 Spalten - aufgeteilt in 50% 25% 25%', 61 | 'grid_25_50_25' => '3 Spalten - aufgeteilt in 25% 50% 25%', 62 | 'grid_25_25_50' => '3 Spalten - aufgeteilt in 25% 25% 50%', 63 | 'grid_40_30_30' => '3 Spalten - aufgeteilt in 40% 30% 30%', 64 | 'grid_30_40_30' => '3 Spalten - aufgeteilt in 30% 40% 30%', 65 | 'grid_30_30_40' => '3 Spalten - aufgeteilt in 30% 30% 40%', 66 | 'grid_20_40_40' => '3 Spalten - aufgeteilt in 20% 40% 40%', 67 | 'grid_40_20_40' => '3 Spalten - aufgeteilt in 40% 20% 40%', 68 | 'grid_40_40_20' => '3 Spalten - aufgeteilt in 40% 40% 20%', 69 | 'grid_40_20_20_20' => '4 Spalten - aufgeteilt in 40% 20% 20% 20%', 70 | 'grid_20_40_20_20' => '4 Spalten - aufgeteilt in 20% 40% 20% 20%', 71 | 'grid_20_20_40_20' => '4 Spalten - aufgeteilt in 20% 20% 40% 20%', 72 | 'grid_20_20_20_40' => '4 Spalten - aufgeteilt in 20% 20% 20% 40%', 73 | ]; 74 | 75 | $GLOBALS['TL_LANG']['tl_content']['cp_gap_options'] = [ 76 | 'gap_0' => 'kein Abstand', 77 | 'gap_1' => '1', 78 | 'gap_2' => '2', 79 | 'gap_3' => '3', 80 | 'gap_4' => '4', 81 | 'gap_5' => '5', 82 | 'gap_6' => '6', 83 | 'gap_7' => '7', 84 | 'gap_8' => '8', 85 | 'gap_9' => '9', 86 | 'gap_10' => '10', 87 | 'gap_11' => '11', 88 | 'gap_12' => '12', 89 | ]; 90 | 91 | // Legend 92 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_legend'] = 'Spalten-Konfiguration'; 93 | -------------------------------------------------------------------------------- /contao/dca/tl_form_field.php: -------------------------------------------------------------------------------- 1 | &$GLOBALS['TL_LANG']['tl_form_field']['form_cp_grid_desktop'], 14 | 'inputType' => 'select', 15 | 'options' => [ 16 | 'grid_100', 17 | 'grid_50_50', 18 | 'grid_33_33_33', 19 | 'grid_25_25_25_25', 20 | 'grid_20_20_20_20_20', 21 | 'grid_75_25', 22 | 'grid_25_75', 23 | 'grid_66_33', 24 | 'grid_33_66', 25 | 'grid_60_40', 26 | 'grid_40_60', 27 | 'grid_50_25_25', 28 | 'grid_25_50_25', 29 | 'grid_25_25_50', 30 | 'grid_40_30_30', 31 | 'grid_30_40_30', 32 | 'grid_30_30_40', 33 | 'grid_20_40_40', 34 | 'grid_40_20_40', 35 | 'grid_40_40_20', 36 | 'grid_40_20_20_20', 37 | 'grid_20_40_20_20', 38 | 'grid_20_20_40_20', 39 | 'grid_20_20_20_40', 40 | ], 41 | 'reference' => &$GLOBALS['TL_LANG']['tl_form_field']['form_cp_grid_options'], 42 | 'eval' => [ 43 | 'tl_class' => 'w33', 44 | ], 45 | 'sql' => "varchar(255) NOT NULL default ''", 46 | ]; 47 | 48 | $GLOBALS['TL_DCA']['tl_form_field']['fields']['form_cp_grid_tablet'] = [ 49 | 'label' => &$GLOBALS['TL_LANG']['tl_form_field']['form_cp_grid_tablet'], 50 | 'inputType' => 'select', 51 | 'options' => [ 52 | 'grid_100', 53 | 'grid_50_50', 54 | 'grid_33_33_33', 55 | 'grid_25_25_25_25', 56 | 'grid_75_25', 57 | 'grid_25_75', 58 | 'grid_66_33', 59 | 'grid_33_66', 60 | 'grid_50_25_25', 61 | 'grid_25_50_25', 62 | 'grid_25_25_50', 63 | 'grid_40_30_30', 64 | 'grid_30_40_30', 65 | 'grid_30_30_40', 66 | 'grid_20_40_40', 67 | 'grid_40_20_40', 68 | 'grid_40_40_20', 69 | ], 70 | 'reference' => &$GLOBALS['TL_LANG']['tl_form_field']['form_cp_grid_options'], 71 | 'eval' => [ 72 | 'tl_class' => 'w33', 73 | ], 74 | 'sql' => "varchar(255) NOT NULL default ''", 75 | ]; 76 | 77 | $GLOBALS['TL_DCA']['tl_form_field']['fields']['form_cp_grid_mobile'] = [ 78 | 'label' => &$GLOBALS['TL_LANG']['tl_form_field']['form_cp_grid_mobile'], 79 | 'inputType' => 'select', 80 | 'options' => [ 81 | 'grid_100', 82 | 'grid_50_50', 83 | 'grid_33_33_33', 84 | 'grid_25_25_25_25', 85 | 'grid_75_25', 86 | 'grid_25_75', 87 | 'grid_66_33', 88 | 'grid_33_66', 89 | 'grid_50_25_25', 90 | 'grid_25_50_25', 91 | 'grid_25_25_50', 92 | 'grid_40_30_30', 93 | 'grid_30_40_30', 94 | 'grid_30_30_40', 95 | ], 96 | 'reference' => &$GLOBALS['TL_LANG']['tl_form_field']['form_cp_grid_options'], 97 | 'eval' => [ 98 | 'tl_class' => 'w33', 99 | ], 100 | 'sql' => "varchar(255) NOT NULL default ''", 101 | ]; 102 | -------------------------------------------------------------------------------- /src/EventListener/ClickpressGridListener.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\EventListener; 17 | 18 | use Contao\CoreBundle\DependencyInjection\Attribute\AsCallback; 19 | use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; 20 | use Contao\Database; 21 | use Contao\DataContainer; 22 | use Contao\LayoutModel; 23 | use Contao\PageModel; 24 | use Contao\PageRegular; 25 | 26 | /** 27 | * ClickpressGridListener Taken with friendly permission from RockSolid Columns. 28 | * 29 | * Provide miscellaneous methods that are used by the data configuration arrays. 30 | */ 31 | class ClickpressGridListener 32 | { 33 | #[AsHook('generatePage')] 34 | public function onGeneratePage(PageModel $pageModel, LayoutModel $layout, PageRegular $pageRegular): void 35 | { 36 | if ($layout->cp_grid_load_css) { 37 | $GLOBALS['TL_CSS'][] = 'bundles/contaoclickpressgrid/clickpress-grid.css||static'; 38 | } 39 | } 40 | 41 | /** 42 | * tl_content DCA onsubmit callback. 43 | * 44 | * Creates a stop element after a start element was created 45 | */ 46 | #[AsCallback(table: 'tl_content', target: 'config.onsubmit')] 47 | public function onsubmitCallback(DataContainer $dc): void 48 | { 49 | $activeRecord = $dc->activeRecord; 50 | if (!$activeRecord) { 51 | return; 52 | } 53 | 54 | if ('cp_grid_start' === $activeRecord->type || 'cp_column_start' === $activeRecord->type) { 55 | // Find the next columns or column element 56 | $nextElement = Database::getInstance() 57 | ->prepare(' 58 | SELECT type 59 | FROM tl_content 60 | WHERE pid = ? 61 | AND (ptable = ? OR ptable = ?) 62 | AND type IN (\'cp_column_start\', \'cp_column_stop\', \'cp_grid_start\', \'cp_grid_stop\') 63 | AND sorting > ? 64 | ORDER BY sorting 65 | LIMIT 1 66 | ') 67 | ->execute( 68 | $activeRecord->pid, 69 | $activeRecord->ptable ?: 'tl_article', 70 | 'tl_article' === $activeRecord->ptable ? '' : $activeRecord->ptable, 71 | $activeRecord->sorting, 72 | ) 73 | ; 74 | 75 | // Check if a stop element should be created 76 | if ( 77 | !$nextElement->type 78 | || ('cp_grid_start' === $activeRecord->type && 'cp_column_stop' === $nextElement->type) 79 | || ('cp_column_start' === $activeRecord->type && ( 80 | 'cp_column_start' === $nextElement->type || 'cp_grid_stop' === $nextElement->type 81 | )) 82 | ) { 83 | $set = []; 84 | 85 | // Get all default values for the new entry 86 | foreach ($GLOBALS['TL_DCA']['tl_content']['fields'] as $field => $config) { 87 | if (\array_key_exists('default', $config)) { 88 | $set[$field] = \is_array($config['default']) ? serialize($config['default']) : $config['default']; 89 | } 90 | } 91 | 92 | $set['pid'] = $activeRecord->pid; 93 | $set['ptable'] = $activeRecord->ptable ?: 'tl_article'; 94 | $set['type'] = substr($activeRecord->type, 0, -5).'stop'; 95 | $set['sorting'] = $activeRecord->sorting + 1; 96 | $set['invisible'] = $activeRecord->invisible; 97 | $set['start'] = $activeRecord->start; 98 | $set['stop'] = $activeRecord->stop; 99 | $set['tstamp'] = time(); 100 | 101 | Database::getInstance() 102 | ->prepare('INSERT INTO tl_content %s') 103 | ->set($set) 104 | ->execute() 105 | ; 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Element/GridStart.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Martin Auswöger 10 | * @author Jannik Nölke 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace Clickpress\ContaoClickpressGridBundle\Element; 17 | 18 | use Contao\ContentModel; 19 | use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController; 20 | use Contao\CoreBundle\DependencyInjection\Attribute\AsContentElement; 21 | use Contao\CoreBundle\Routing\ScopeMatcher; 22 | use Contao\StringUtil; 23 | use Contao\System; 24 | use Contao\Template; 25 | use Symfony\Component\HttpFoundation\Request; 26 | use Symfony\Component\HttpFoundation\RequestStack; 27 | use Symfony\Component\HttpFoundation\Response; 28 | 29 | /** 30 | * Grid start content element Taken with friendly permission from RockSolid Columns. 31 | */ 32 | #[AsContentElement(type: 'cp_grid_start', category: 'cp_grid', template: 'ce_grid_start')] 33 | class GridStart extends AbstractContentElementController 34 | { 35 | public function __construct(readonly RequestStack $requestStack, readonly ScopeMatcher $scopeMatcher) 36 | { 37 | } 38 | 39 | /** 40 | * Generate the columns classes. 41 | * 42 | * @param array $data Data array 43 | */ 44 | public static function getColumnsConfiguration(array $data): array 45 | { 46 | $config = []; 47 | 48 | foreach (['desktop', 'tablet', 'mobile'] as $media) { 49 | $mediaClass = 'cp_grid_' . $media; 50 | if (isset($data[$mediaClass])) { 51 | $columns = str_replace('grid', 'grid_' . $media, $data['cp_grid_' . $media]); 52 | $config[$media] = $columns; 53 | } else { 54 | $config[$media] = null; 55 | } 56 | } 57 | 58 | return $config; 59 | } 60 | 61 | /** 62 | * Generate the device-specific class based on gap presence and type. 63 | * 64 | * @param string $deviceType Type of the device (e.g., desktop, tablet, mobile) 65 | * @param mixed $gap Gap information (can be any type depending on its usage) 66 | * 67 | * @return string Generated device class 68 | */ 69 | public function generateGapClass(string $deviceType, string $gap): string 70 | { 71 | $gapArray = explode('_', $gap); 72 | 73 | return $gap ? " {$gapArray[0]}_{$deviceType}_{$gapArray[1]} " : ''; 74 | } 75 | 76 | protected function getResponse(Template $template, ContentModel $model, Request $request): Response 77 | { 78 | if ($this->scopeMatcher->isBackendRequest($this->requestStack->getCurrentRequest())) { 79 | $listingTitle = ''; 80 | 81 | if($model->headline) { 82 | $headline = StringUtil::deserialize($model->headline, true); 83 | $listingTitle = $headline['value']; 84 | } 85 | 86 | if($model->title) { 87 | $listingTitle = $model->title; 88 | } 89 | 90 | return new Response($listingTitle. "
" . $this->getConfigInfo($model)); 91 | } 92 | 93 | $template->gridClasses = ''; 94 | 95 | $parentKey = ($model->ptable ?: 'tl_article') . '__' . $model->pid; 96 | 97 | $GLOBALS['TL_CP_GRID'][$parentKey] = [ 98 | 'active' => true, 99 | 'count' => 0, 100 | 'config' => static::getColumnsConfiguration($model->row()), 101 | ]; 102 | $template->gridClasses = implode(' ', $GLOBALS['TL_CP_GRID'][$parentKey]['config']); 103 | 104 | if ($model->cp_grid_valign) { 105 | $template->gridClasses .= ' ' . $model->cp_grid_valign; 106 | } 107 | 108 | if ($model->cp_grid_halign) { 109 | $template->gridClasses .= ' ' . $model->cp_grid_halign; 110 | } 111 | 112 | $template->gridClasses .= $model->cp_gap_mobile ? $this->generateGapClass('mobile', $model->cp_gap_mobile) : ''; 113 | $template->gridClasses .= $model->cp_gap_tablet ? $this->generateGapClass('tablet', $model->cp_gap_tablet) : ''; 114 | $template->gridClasses .= $model->cp_gap_desktop ? $this->generateGapClass( 115 | 'desktop', 116 | $model->cp_gap_desktop 117 | ) : ''; 118 | 119 | return $template->getResponse(); 120 | } 121 | 122 | /** 123 | * Generates a configuration information string for the given content model. 124 | * 125 | * @param ContentModel $model the content model containing configuration information 126 | * 127 | * @return string the formatted configuration information string 128 | */ 129 | private function getConfigInfo(ContentModel $model): string 130 | { 131 | System::loadLanguageFile('tl_content'); 132 | 133 | $configInfo = ''; 134 | 135 | $configInfo .= \sprintf( 136 | '%s: %s %s, ', 137 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_mobile'][0], 138 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_options'][$model->cp_grid_mobile], 139 | isset($model->cp_gap_mobile) ? $this->formatGapOption($model->cp_gap_mobile) : '', 140 | ); 141 | 142 | $configInfo .= \sprintf( 143 | '%s: %s %s, ', 144 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_tablet'][0], 145 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_options'][$model->cp_grid_tablet], 146 | isset($model->cp_gap_tablet) ? $this->formatGapOption($model->cp_gap_tablet) : '', 147 | ); 148 | 149 | $configInfo .= \sprintf( 150 | '%s: %s %s, ', 151 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_desktop'][0], 152 | $GLOBALS['TL_LANG']['tl_content']['cp_grid_options'][$model->cp_grid_desktop], 153 | isset($model->cp_gap_desktop) ? $this->formatGapOption($model->cp_gap_desktop) : '', 154 | ); 155 | 156 | $configInfo .= ''; 157 | 158 | return $configInfo; 159 | } 160 | 161 | /** 162 | * Format Gap Option. 163 | */ 164 | private function formatGapOption(string $gap): string 165 | { 166 | return !empty($gap) ? '(' . $GLOBALS['TL_LANG']['tl_content']['cp_gap_options'][$gap] . ')' : ''; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /contao/dca/tl_content.php: -------------------------------------------------------------------------------- 1 | =') ) { 18 | PaletteManipulator::create() 19 | ->addField('title', 'headline', PaletteManipulator::POSITION_BEFORE) 20 | ->applyToPalette('cp_grid_start', 'tl_content') 21 | ->applyToPalette('cp_grid_stop', 'tl_content') 22 | ->applyToPalette('cp_column_start', 'tl_content') 23 | ->applyToPalette('cp_column_stop', 'tl_content') 24 | ; 25 | } 26 | 27 | $GLOBALS['TL_DCA']['tl_content']['fields']['cp_grid_desktop'] = [ 28 | 'label' => &$GLOBALS['TL_LANG']['tl_content']['cp_grid_desktop'], 29 | 'inputType' => 'select', 30 | 'options' => [ 31 | 'grid_100', 32 | 'grid_50_50', 33 | 'grid_33_33_33', 34 | 'grid_25_25_25_25', 35 | 'grid_20_20_20_20_20', 36 | 'grid_16_16_16_16_16_16', 37 | 'grid_75_25', 38 | 'grid_25_75', 39 | 'grid_66_33', 40 | 'grid_33_66', 41 | 'grid_60_40', 42 | 'grid_40_60', 43 | 'grid_50_25_25', 44 | 'grid_25_50_25', 45 | 'grid_25_25_50', 46 | 'grid_40_30_30', 47 | 'grid_30_40_30', 48 | 'grid_30_30_40', 49 | 'grid_20_40_40', 50 | 'grid_40_20_40', 51 | 'grid_40_40_20', 52 | 'grid_40_20_20_20', 53 | 'grid_20_40_20_20', 54 | 'grid_20_20_40_20', 55 | 'grid_20_20_20_40', 56 | ], 57 | 'reference' => &$GLOBALS['TL_LANG']['tl_content']['cp_grid_options'], 58 | 'eval' => [ 59 | 'tl_class' => 'w33', 60 | ], 61 | 'sql' => "varchar(255) NOT NULL default ''", 62 | ]; 63 | 64 | $GLOBALS['TL_DCA']['tl_content']['fields']['cp_grid_tablet'] = [ 65 | 'label' => &$GLOBALS['TL_LANG']['tl_content']['cp_grid_tablet'], 66 | 'inputType' => 'select', 67 | 'options' => [ 68 | 'grid_100', 69 | 'grid_50_50', 70 | 'grid_33_33_33', 71 | 'grid_25_25_25_25', 72 | 'grid_75_25', 73 | 'grid_25_75', 74 | 'grid_66_33', 75 | 'grid_33_66', 76 | 'grid_60_40', 77 | 'grid_40_60', 78 | 'grid_50_25_25', 79 | 'grid_25_50_25', 80 | 'grid_25_25_50', 81 | 'grid_40_30_30', 82 | 'grid_30_40_30', 83 | 'grid_30_30_40', 84 | 'grid_20_40_40', 85 | 'grid_40_20_40', 86 | 'grid_40_40_20', 87 | ], 88 | 'reference' => &$GLOBALS['TL_LANG']['tl_content']['cp_grid_options'], 89 | 'eval' => [ 90 | 'tl_class' => 'w33', 91 | ], 92 | 'sql' => "varchar(255) NOT NULL default ''", 93 | ]; 94 | 95 | $GLOBALS['TL_DCA']['tl_content']['fields']['cp_grid_mobile'] = [ 96 | 'label' => &$GLOBALS['TL_LANG']['tl_content']['cp_grid_mobile'], 97 | 'inputType' => 'select', 98 | 'options' => [ 99 | 'grid_100', 100 | 'grid_50_50', 101 | 'grid_33_33_33', 102 | 'grid_25_25_25_25', 103 | 'grid_75_25', 104 | 'grid_25_75', 105 | 'grid_66_33', 106 | 'grid_33_66', 107 | 'grid_60_40', 108 | 'grid_40_60', 109 | 'grid_50_25_25', 110 | 'grid_25_50_25', 111 | 'grid_25_25_50', 112 | 'grid_40_30_30', 113 | 'grid_30_40_30', 114 | 'grid_30_30_40', 115 | ], 116 | 'reference' => &$GLOBALS['TL_LANG']['tl_content']['cp_grid_options'], 117 | 'eval' => [ 118 | 'tl_class' => 'w33', 119 | ], 120 | 'sql' => "varchar(255) NOT NULL default ''", 121 | ]; 122 | 123 | $GLOBALS['TL_DCA']['tl_content']['fields']['cp_gap_mobile'] = [ 124 | 'inputType' => 'select', 125 | 'reference' => &$GLOBALS['TL_LANG']['tl_content']['cp_gap_options'], 126 | 'default' => '', 127 | 'options' => [ 128 | 'gap_0', 129 | 'gap_1', 130 | 'gap_2', 131 | 'gap_3', 132 | 'gap_4', 133 | 'gap_5', 134 | 'gap_6', 135 | 'gap_7', 136 | 'gap_8', 137 | 'gap_9', 138 | 'gap_10', 139 | 'gap_11', 140 | 'gap_12', 141 | ], 142 | 'eval' => [ 143 | 'includeBlankOption' => true, 144 | 'blankOptionLabel' => '-', 145 | 'tl_class' => 'w33', 146 | ], 147 | 'sql' => "varchar(255) NOT NULL default ''", 148 | ]; 149 | $GLOBALS['TL_DCA']['tl_content']['fields']['cp_gap_tablet'] = [ 150 | 'inputType' => 'select', 151 | 'default' => '', 152 | 'options' => [ 153 | 'gap_0', 154 | 'gap_1', 155 | 'gap_2', 156 | 'gap_3', 157 | 'gap_4', 158 | 'gap_5', 159 | 'gap_6', 160 | 'gap_7', 161 | 'gap_8', 162 | 'gap_9', 163 | 'gap_10', 164 | 'gap_11', 165 | 'gap_12', 166 | ], 167 | 'reference' => &$GLOBALS['TL_LANG']['tl_content']['cp_gap_options'], 168 | 'eval' => [ 169 | 'includeBlankOption' => true, 170 | 'blankOptionLabel' => '-', 171 | 'tl_class' => 'w33', 172 | ], 173 | 'sql' => "varchar(255) NOT NULL default ''", 174 | ]; 175 | $GLOBALS['TL_DCA']['tl_content']['fields']['cp_gap_desktop'] = [ 176 | 'inputType' => 'select', 177 | 'default' => '', 178 | 'options' => [ 179 | 'gap_0', 180 | 'gap_1', 181 | 'gap_2', 182 | 'gap_3', 183 | 'gap_4', 184 | 'gap_5', 185 | 'gap_6', 186 | 'gap_7', 187 | 'gap_8', 188 | 'gap_9', 189 | 'gap_10', 190 | 'gap_11', 191 | 'gap_12', 192 | ], 193 | 'reference' => &$GLOBALS['TL_LANG']['tl_content']['cp_gap_options'], 194 | 'eval' => [ 195 | 'includeBlankOption' => true, 196 | 'blankOptionLabel' => '-', 197 | 'tl_class' => 'w33', 198 | ], 199 | 'sql' => "varchar(255) NOT NULL default ''", 200 | ]; 201 | 202 | $GLOBALS['TL_DCA']['tl_content']['fields']['cp_grid_valign'] = [ 203 | 'inputType' => 'select', 204 | 'default' => false, 205 | 'options' => [ 206 | 'items-start' => 'top', 207 | 'items-center' => 'center', 208 | 'items-end' => 'bottom', 209 | 'items-stretch' => 'stretch', 210 | 'items-baseline' => 'baseline', 211 | ], 212 | 'reference' => &$GLOBALS['TL_LANG']['tl_content']['cp_grid_valign_options'], 213 | 'eval' => [ 214 | 'tl_class' => 'w50 m12 clr', 215 | 'includeBlankOption' => true, 216 | ], 217 | 'sql' => "varchar(255) NOT NULL default ''", 218 | ]; 219 | 220 | $GLOBALS['TL_DCA']['tl_content']['fields']['cp_grid_halign'] = [ 221 | 'inputType' => 'select', 222 | 'default' => false, 223 | 'options' => [ 224 | 'justify-items-start' => 'left', 225 | 'justify-items-center' => 'center', 226 | 'justify-items-end' => 'right', 227 | 'justify-items-stretch' => 'stretch', 228 | ], 229 | 'reference' => &$GLOBALS['TL_LANG']['tl_content']['cp_grid_halign_options'], 230 | 'eval' => [ 231 | 'tl_class' => 'w50 m12', 232 | 'includeBlankOption' => true, 233 | ], 234 | 'sql' => "varchar(255) NOT NULL default ''", 235 | ]; 236 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /public/clickpress-grid.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CLICKPRESS CSS-Grid 3 | * @author: Stefan Schulz-Lauterbach 4 | * 5 | * thanks to Dinko Skopljak for co-working 6 | */ 7 | .justify-items-start { 8 | justify-items: start; 9 | } 10 | 11 | .justify-items-center { 12 | justify-items: center; 13 | } 14 | 15 | .justify-items-end { 16 | justify-items: end; 17 | } 18 | 19 | .justify-items-stretch { 20 | justify-items: stretch; 21 | } 22 | 23 | .content-start { 24 | align-content: start; 25 | } 26 | 27 | .content-center { 28 | align-content: center; 29 | } 30 | 31 | .content-end { 32 | align-content: end; 33 | } 34 | 35 | .items-start { 36 | align-items: start; 37 | } 38 | 39 | .items-center { 40 | align-items: center; 41 | } 42 | 43 | .items-end { 44 | align-items: end; 45 | } 46 | 47 | .items-stretch { 48 | align-items: stretch; 49 | } 50 | 51 | .items-baseline { 52 | align-items: baseline; 53 | } 54 | 55 | [class*=grid_] { 56 | display: grid !important; 57 | grid-gap: 1rem; 58 | } 59 | [class*=grid_] > .block { 60 | margin-bottom: 0; 61 | } 62 | 63 | .ce_cp_column_start:has(.content-swiper) { 64 | overflow: hidden; 65 | } 66 | 67 | @media (min-width: 576px) { 68 | .grid_mobile_100 { 69 | grid-template-columns: 1fr; 70 | } 71 | .grid_mobile_50_50 { 72 | grid-template-columns: repeat(2, 1fr); 73 | } 74 | .grid_mobile_33_33_33 { 75 | grid-template-columns: repeat(3, 1fr); 76 | } 77 | .grid_mobile_25_25_25_25 { 78 | grid-template-columns: repeat(4, 1fr); 79 | } 80 | .grid_mobile_75_25 { 81 | grid-template-columns: 3fr 1fr; 82 | } 83 | .grid_mobile_25_75 { 84 | grid-template-columns: 1fr 3fr; 85 | } 86 | .grid_mobile_66_33 { 87 | grid-template-columns: 2fr 1fr; 88 | } 89 | .grid_mobile_33_66 { 90 | grid-template-columns: 1fr 2fr; 91 | } 92 | .grid_mobile_60_40 { 93 | grid-template-columns: 3fr 2fr; 94 | } 95 | .grid_mobile_40_60 { 96 | grid-template-columns: 2fr 3fr; 97 | } 98 | .grid_mobile_50_25_25 { 99 | grid-template-columns: 2fr 1fr 1fr; 100 | } 101 | .grid_mobile_25_50_25 { 102 | grid-template-columns: 1fr 2fr 1fr; 103 | } 104 | .grid_mobile_25_25_50 { 105 | grid-template-columns: 1fr 1fr 2fr; 106 | } 107 | .grid_mobile_40_30_30 { 108 | grid-template-columns: 4fr 3fr 3fr; 109 | } 110 | .grid_mobile_30_40_30 { 111 | grid-template-columns: 3fr 4fr 3fr; 112 | } 113 | .grid_mobile_30_30_40 { 114 | grid-template-columns: 3fr 3fr 4fr; 115 | } 116 | .gap_mobile_0 { 117 | grid-gap: 0; 118 | } 119 | .gap_mobile_1 { 120 | grid-gap: 1rem; 121 | } 122 | .gap_mobile_2 { 123 | grid-gap: 2rem; 124 | } 125 | .gap_mobile_3 { 126 | grid-gap: 3rem; 127 | } 128 | .gap_mobile_4 { 129 | grid-gap: 4rem; 130 | } 131 | .gap_mobile_5 { 132 | grid-gap: 5rem; 133 | } 134 | .gap_mobile_6 { 135 | grid-gap: 6rem; 136 | } 137 | .gap_mobile_7 { 138 | grid-gap: 7rem; 139 | } 140 | .gap_mobile_8 { 141 | grid-gap: 8rem; 142 | } 143 | .gap_mobile_9 { 144 | grid-gap: 9rem; 145 | } 146 | .gap_mobile_10 { 147 | grid-gap: 10rem; 148 | } 149 | .gap_mobile_11 { 150 | grid-gap: 11rem; 151 | } 152 | .gap_mobile_12 { 153 | grid-gap: 12rem; 154 | } 155 | } 156 | @media (min-width: 768px) { 157 | .grid_tablet_100 { 158 | grid-template-columns: 1fr; 159 | } 160 | .grid_tablet_50_50 { 161 | grid-template-columns: repeat(2, 1fr); 162 | } 163 | .grid_tablet_33_33_33 { 164 | grid-template-columns: repeat(3, 1fr); 165 | } 166 | .grid_tablet_25_25_25_25 { 167 | grid-template-columns: repeat(4, 1fr); 168 | } 169 | .grid_tablet_75_25 { 170 | grid-template-columns: 3fr 1fr; 171 | } 172 | .grid_tablet_25_75 { 173 | grid-template-columns: 1fr 3fr; 174 | } 175 | .grid_tablet_66_33 { 176 | grid-template-columns: 2fr 1fr; 177 | } 178 | .grid_tablet_33_66 { 179 | grid-template-columns: 1fr 2fr; 180 | } 181 | .grid_tablet_60_40 { 182 | grid-template-columns: 3fr 2fr; 183 | } 184 | .grid_tablet_40_60 { 185 | grid-template-columns: 2fr 3fr; 186 | } 187 | .grid_tablet_50_25_25 { 188 | grid-template-columns: 2fr 1fr 1fr; 189 | } 190 | .grid_tablet_25_50_25 { 191 | grid-template-columns: 1fr 2fr 1fr; 192 | } 193 | .grid_tablet_25_25_50 { 194 | grid-template-columns: 1fr 1fr 2fr; 195 | } 196 | .grid_tablet_40_30_30 { 197 | grid-template-columns: 4fr 3fr 3fr; 198 | } 199 | .grid_tablet_30_40_30 { 200 | grid-template-columns: 3fr 4fr 3fr; 201 | } 202 | .grid_tablet_30_30_40 { 203 | grid-template-columns: 3fr 3fr 4fr; 204 | } 205 | .grid_tablet_20_40_40 { 206 | grid-template-columns: 1fr 2fr 2fr; 207 | } 208 | .grid_tablet_40_20_40 { 209 | grid-template-columns: 2fr 1fr 2fr; 210 | } 211 | .grid_tablet_40_40_20 { 212 | grid-template-columns: 2fr 2fr 1fr; 213 | } 214 | .gap_tablet_0 { 215 | grid-gap: 0; 216 | } 217 | .gap_tablet_1 { 218 | grid-gap: 1rem; 219 | } 220 | .gap_tablet_2 { 221 | grid-gap: 2rem; 222 | } 223 | .gap_tablet_3 { 224 | grid-gap: 3rem; 225 | } 226 | .gap_tablet_4 { 227 | grid-gap: 4rem; 228 | } 229 | .gap_tablet_5 { 230 | grid-gap: 5rem; 231 | } 232 | .gap_tablet_6 { 233 | grid-gap: 6rem; 234 | } 235 | .gap_tablet_7 { 236 | grid-gap: 7rem; 237 | } 238 | .gap_tablet_8 { 239 | grid-gap: 8rem; 240 | } 241 | .gap_tablet_9 { 242 | grid-gap: 9rem; 243 | } 244 | .gap_tablet_10 { 245 | grid-gap: 10rem; 246 | } 247 | .gap_tablet_11 { 248 | grid-gap: 11rem; 249 | } 250 | .gap_tablet_12 { 251 | grid-gap: 12rem; 252 | } 253 | } 254 | @media (min-width: 992px) { 255 | .grid_desktop_100 { 256 | grid-template-columns: 1fr; 257 | } 258 | .grid_desktop_50_50 { 259 | grid-template-columns: repeat(2, 1fr); 260 | } 261 | .grid_desktop_50_25_25 { 262 | grid-template-columns: 2fr 1fr 1fr; 263 | } 264 | .grid_desktop_33_33_33 { 265 | grid-template-columns: repeat(3, 1fr); 266 | } 267 | .grid_desktop_75_25 { 268 | grid-template-columns: 3fr 1fr; 269 | } 270 | .grid_desktop_25_75 { 271 | grid-template-columns: 1fr 3fr; 272 | } 273 | .grid_desktop_66_33 { 274 | grid-template-columns: 2fr 1fr; 275 | } 276 | .grid_desktop_60_40 { 277 | grid-template-columns: 3fr 2fr; 278 | } 279 | .grid_desktop_40_60 { 280 | grid-template-columns: 2fr 3fr; 281 | } 282 | .grid_desktop_33_66 { 283 | grid-template-columns: 1fr 2fr; 284 | } 285 | .grid_desktop_50_25_25 { 286 | grid-template-columns: 2fr 1fr 1fr; 287 | } 288 | .grid_desktop_40_30_30 { 289 | grid-template-columns: 4fr 3fr 3fr; 290 | } 291 | .grid_desktop_50_25_25 { 292 | grid-template-columns: 2fr 1fr 1fr; 293 | } 294 | .grid_desktop_25_50_25 { 295 | grid-template-columns: 1fr 2fr 1fr; 296 | } 297 | .grid_desktop_25_25_50 { 298 | grid-template-columns: 1fr 1fr 2fr; 299 | } 300 | .grid_desktop_40_30_30 { 301 | grid-template-columns: 4fr 3fr 3fr; 302 | } 303 | .grid_desktop_30_40_30 { 304 | grid-template-columns: 3fr 4fr 3fr; 305 | } 306 | .grid_desktop_30_30_40 { 307 | grid-template-columns: 3fr 3fr 4fr; 308 | } 309 | .grid_desktop_20_40_40 { 310 | grid-template-columns: 1fr 2fr 2fr; 311 | } 312 | .grid_desktop_40_20_40 { 313 | grid-template-columns: 2fr 1fr 2fr; 314 | } 315 | .grid_desktop_40_40_20 { 316 | grid-template-columns: 2fr 2fr 1fr; 317 | } 318 | .grid_desktop_25_25_25_25 { 319 | grid-template-columns: repeat(4, 1fr); 320 | } 321 | .grid_desktop_40_20_20_20 { 322 | grid-template-columns: 2fr 1fr 1fr 1fr; 323 | } 324 | .grid_desktop_20_40_20_20 { 325 | grid-template-columns: 1fr 2fr 1fr 1fr; 326 | } 327 | .grid_desktop_20_20_40_20 { 328 | grid-template-columns: 1fr 1fr 2fr 1fr; 329 | } 330 | .grid_desktop_20_20_20_40 { 331 | grid-template-columns: 1fr 1fr 1fr 2fr; 332 | } 333 | .grid_desktop_20_20_20_20_20 { 334 | grid-template-columns: repeat(5, 1fr); 335 | } 336 | .grid_desktop_16_16_16_16_16_16 { 337 | grid-template-columns: repeat(6, 1fr); 338 | } 339 | .gap_desktop_0 { 340 | grid-gap: 0; 341 | } 342 | .gap_desktop_1 { 343 | grid-gap: 1rem; 344 | } 345 | .gap_desktop_2 { 346 | grid-gap: 2rem; 347 | } 348 | .gap_desktop_3 { 349 | grid-gap: 3rem; 350 | } 351 | .gap_desktop_4 { 352 | grid-gap: 4rem; 353 | } 354 | .gap_desktop_5 { 355 | grid-gap: 5rem; 356 | } 357 | .gap_desktop_6 { 358 | grid-gap: 6rem; 359 | } 360 | .gap_desktop_7 { 361 | grid-gap: 7rem; 362 | } 363 | .gap_desktop_8 { 364 | grid-gap: 8rem; 365 | } 366 | .gap_desktop_9 { 367 | grid-gap: 9rem; 368 | } 369 | .gap_desktop_10 { 370 | grid-gap: 10rem; 371 | } 372 | .gap_desktop_11 { 373 | grid-gap: 11rem; 374 | } 375 | .gap_desktop_12 { 376 | grid-gap: 12rem; 377 | } 378 | } 379 | 380 | /*# sourceMappingURL=clickpress-grid.css.map */ 381 | -------------------------------------------------------------------------------- /public/clickpress-grid.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * CLICKPRESS CSS-Grid 3 | * @author: Stefan Schulz-Lauterbach 4 | * 5 | * thanks to Dinko Skopljak for co-working 6 | */ 7 | // 8 | // Media Queries 9 | // --------- 10 | $mq_small: 576px; // Small devices (landscape phones) 11 | $mq_medium: 768px; // Medium devices (tablets, 768px and up) 12 | $mq_large: 992px; // Large devices (desktops, 992px and up) 13 | $mq_extra: 1260px; // Extra large devices (large desktops, 1260px and up) 14 | 15 | // Alignment utilities 16 | .justify-items-start { 17 | justify-items: start; 18 | } 19 | 20 | .justify-items-center { 21 | justify-items: center; 22 | } 23 | 24 | .justify-items-end { 25 | justify-items: end; 26 | } 27 | 28 | .justify-items-stretch { 29 | justify-items: stretch; 30 | } 31 | 32 | .content-start { 33 | align-content: start; 34 | } 35 | 36 | .content-center { 37 | align-content: center; 38 | } 39 | 40 | .content-end { 41 | align-content: end; 42 | } 43 | 44 | .items-start { 45 | align-items: start; 46 | } 47 | 48 | .items-center { 49 | align-items: center; 50 | } 51 | 52 | .items-end { 53 | align-items: end; 54 | } 55 | 56 | .items-stretch { 57 | align-items: stretch; 58 | } 59 | 60 | .items-baseline { 61 | align-items: baseline; 62 | } 63 | 64 | [class*="grid_"] { 65 | display: grid !important; 66 | grid-gap: 1rem; 67 | 68 | > .block { 69 | margin-bottom: 0; 70 | } 71 | } 72 | 73 | .ce_cp_column_start:has(.content-swiper) { 74 | overflow: hidden; 75 | } 76 | 77 | @media (min-width: $mq_small) { 78 | .grid_mobile_100 { 79 | grid-template-columns: 1fr; 80 | } 81 | .grid_mobile_50_50 { 82 | grid-template-columns: repeat(2, 1fr); 83 | } 84 | .grid_mobile_33_33_33 { 85 | grid-template-columns: repeat(3, 1fr); 86 | } 87 | .grid_mobile_25_25_25_25 { 88 | grid-template-columns: repeat(4, 1fr); 89 | } 90 | .grid_mobile_75_25 { 91 | grid-template-columns: 3fr 1fr; 92 | } 93 | .grid_mobile_25_75 { 94 | grid-template-columns: 1fr 3fr; 95 | } 96 | .grid_mobile_66_33 { 97 | grid-template-columns: 2fr 1fr; 98 | } 99 | .grid_mobile_33_66 { 100 | grid-template-columns: 1fr 2fr; 101 | } 102 | .grid_mobile_60_40 { 103 | grid-template-columns: 3fr 2fr; 104 | } 105 | .grid_mobile_40_60 { 106 | grid-template-columns: 2fr 3fr; 107 | } 108 | .grid_mobile_50_25_25 { 109 | grid-template-columns: 2fr 1fr 1fr; 110 | } 111 | .grid_mobile_25_50_25 { 112 | grid-template-columns: 1fr 2fr 1fr; 113 | } 114 | .grid_mobile_25_25_50 { 115 | grid-template-columns: 1fr 1fr 2fr; 116 | } 117 | .grid_mobile_40_30_30 { 118 | grid-template-columns: 4fr 3fr 3fr; 119 | } 120 | .grid_mobile_30_40_30 { 121 | grid-template-columns: 3fr 4fr 3fr; 122 | } 123 | .grid_mobile_30_30_40 { 124 | grid-template-columns: 3fr 3fr 4fr; 125 | } 126 | .gap_mobile_0 { 127 | grid-gap: 0; 128 | } 129 | .gap_mobile_1 { 130 | grid-gap: 1rem; 131 | } 132 | .gap_mobile_2 { 133 | grid-gap: 2rem; 134 | } 135 | .gap_mobile_3 { 136 | grid-gap: 3rem; 137 | } 138 | .gap_mobile_4 { 139 | grid-gap: 4rem; 140 | } 141 | .gap_mobile_5 { 142 | grid-gap: 5rem; 143 | } 144 | .gap_mobile_6 { 145 | grid-gap: 6rem; 146 | } 147 | .gap_mobile_7 { 148 | grid-gap: 7rem; 149 | } 150 | .gap_mobile_8 { 151 | grid-gap: 8rem; 152 | } 153 | .gap_mobile_9 { 154 | grid-gap: 9rem; 155 | } 156 | .gap_mobile_10 { 157 | grid-gap: 10rem; 158 | } 159 | .gap_mobile_11 { 160 | grid-gap: 11rem; 161 | } 162 | .gap_mobile_12 { 163 | grid-gap: 12rem; 164 | } 165 | } 166 | 167 | @media (min-width: $mq_medium) { 168 | .grid_tablet_100 { 169 | grid-template-columns: 1fr; 170 | } 171 | .grid_tablet_50_50 { 172 | grid-template-columns: repeat(2, 1fr); 173 | } 174 | .grid_tablet_33_33_33 { 175 | grid-template-columns: repeat(3, 1fr) 176 | } 177 | .grid_tablet_25_25_25_25 { 178 | grid-template-columns: repeat(4, 1fr) 179 | } 180 | .grid_tablet_75_25 { 181 | grid-template-columns: 3fr 1fr; 182 | } 183 | .grid_tablet_25_75 { 184 | grid-template-columns: 1fr 3fr; 185 | } 186 | .grid_tablet_66_33 { 187 | grid-template-columns: 2fr 1fr; 188 | } 189 | .grid_tablet_33_66 { 190 | grid-template-columns: 1fr 2fr; 191 | } 192 | .grid_tablet_60_40 { 193 | grid-template-columns: 3fr 2fr; 194 | } 195 | .grid_tablet_40_60 { 196 | grid-template-columns: 2fr 3fr; 197 | } 198 | .grid_tablet_50_25_25 { 199 | grid-template-columns: 2fr 1fr 1fr; 200 | } 201 | .grid_tablet_25_50_25 { 202 | grid-template-columns: 1fr 2fr 1fr; 203 | } 204 | .grid_tablet_25_25_50 { 205 | grid-template-columns: 1fr 1fr 2fr; 206 | } 207 | .grid_tablet_40_30_30 { 208 | grid-template-columns: 4fr 3fr 3fr; 209 | } 210 | .grid_tablet_30_40_30 { 211 | grid-template-columns: 3fr 4fr 3fr; 212 | } 213 | .grid_tablet_30_30_40 { 214 | grid-template-columns: 3fr 3fr 4fr; 215 | } 216 | .grid_tablet_20_40_40 { 217 | grid-template-columns: 1fr 2fr 2fr; 218 | } 219 | .grid_tablet_40_20_40 { 220 | grid-template-columns: 2fr 1fr 2fr; 221 | } 222 | .grid_tablet_40_40_20 { 223 | grid-template-columns: 2fr 2fr 1fr; 224 | } 225 | .gap_tablet_0 { 226 | grid-gap: 0; 227 | } 228 | .gap_tablet_1 { 229 | grid-gap: 1rem; 230 | } 231 | .gap_tablet_2 { 232 | grid-gap: 2rem; 233 | } 234 | .gap_tablet_3 { 235 | grid-gap: 3rem; 236 | } 237 | .gap_tablet_4 { 238 | grid-gap: 4rem; 239 | } 240 | .gap_tablet_5 { 241 | grid-gap: 5rem; 242 | } 243 | .gap_tablet_6 { 244 | grid-gap: 6rem; 245 | } 246 | .gap_tablet_7 { 247 | grid-gap: 7rem; 248 | } 249 | .gap_tablet_8 { 250 | grid-gap: 8rem; 251 | } 252 | .gap_tablet_9 { 253 | grid-gap: 9rem; 254 | } 255 | .gap_tablet_10 { 256 | grid-gap: 10rem; 257 | } 258 | .gap_tablet_11 { 259 | grid-gap: 11rem; 260 | } 261 | .gap_tablet_12 { 262 | grid-gap: 12rem; 263 | } 264 | } 265 | 266 | 267 | @media (min-width: $mq_large) { 268 | .grid_desktop_100 { 269 | grid-template-columns: 1fr; 270 | } 271 | .grid_desktop_50_50 { 272 | grid-template-columns: repeat(2, 1fr); 273 | } 274 | .grid_desktop_50_25_25 { 275 | grid-template-columns: 2fr 1fr 1fr; 276 | } 277 | .grid_desktop_33_33_33 { 278 | grid-template-columns: repeat(3, 1fr); 279 | } 280 | .grid_desktop_75_25 { 281 | grid-template-columns: 3fr 1fr; 282 | } 283 | .grid_desktop_25_75 { 284 | grid-template-columns: 1fr 3fr; 285 | } 286 | .grid_desktop_66_33 { 287 | grid-template-columns: 2fr 1fr; 288 | } 289 | .grid_desktop_60_40 { 290 | grid-template-columns: 3fr 2fr; 291 | } 292 | .grid_desktop_40_60 { 293 | grid-template-columns: 2fr 3fr; 294 | } 295 | .grid_desktop_33_66 { 296 | grid-template-columns: 1fr 2fr; 297 | } 298 | .grid_desktop_50_25_25 { 299 | grid-template-columns: 2fr 1fr 1fr; 300 | } 301 | .grid_desktop_40_30_30 { 302 | grid-template-columns: 4fr 3fr 3fr; 303 | } 304 | .grid_desktop_50_25_25 { 305 | grid-template-columns: 2fr 1fr 1fr; 306 | } 307 | .grid_desktop_25_50_25 { 308 | grid-template-columns: 1fr 2fr 1fr; 309 | } 310 | .grid_desktop_25_25_50 { 311 | grid-template-columns: 1fr 1fr 2fr; 312 | } 313 | .grid_desktop_40_30_30 { 314 | grid-template-columns: 4fr 3fr 3fr; 315 | } 316 | .grid_desktop_30_40_30 { 317 | grid-template-columns: 3fr 4fr 3fr; 318 | } 319 | .grid_desktop_30_30_40 { 320 | grid-template-columns: 3fr 3fr 4fr; 321 | } 322 | .grid_desktop_20_40_40 { 323 | grid-template-columns: 1fr 2fr 2fr; 324 | } 325 | .grid_desktop_40_20_40 { 326 | grid-template-columns: 2fr 1fr 2fr; 327 | } 328 | .grid_desktop_40_40_20 { 329 | grid-template-columns: 2fr 2fr 1fr; 330 | } 331 | .grid_desktop_25_25_25_25 { 332 | grid-template-columns: repeat(4, 1fr); 333 | } 334 | .grid_desktop_40_20_20_20 { 335 | grid-template-columns: 2fr 1fr 1fr 1fr; 336 | } 337 | .grid_desktop_20_40_20_20 { 338 | grid-template-columns: 1fr 2fr 1fr 1fr; 339 | } 340 | .grid_desktop_20_20_40_20 { 341 | grid-template-columns: 1fr 1fr 2fr 1fr; 342 | } 343 | .grid_desktop_20_20_20_40 { 344 | grid-template-columns: 1fr 1fr 1fr 2fr; 345 | } 346 | .grid_desktop_20_20_20_20_20 { 347 | grid-template-columns: repeat(5, 1fr); 348 | } 349 | .grid_desktop_16_16_16_16_16_16 { 350 | grid-template-columns: repeat(6, 1fr); 351 | } 352 | 353 | .gap_desktop_0 { 354 | grid-gap: 0; 355 | } 356 | .gap_desktop_1 { 357 | grid-gap: 1rem; 358 | } 359 | .gap_desktop_2 { 360 | grid-gap: 2rem; 361 | } 362 | .gap_desktop_3 { 363 | grid-gap: 3rem; 364 | } 365 | .gap_desktop_4 { 366 | grid-gap: 4rem; 367 | } 368 | .gap_desktop_5 { 369 | grid-gap: 5rem; 370 | } 371 | .gap_desktop_6 { 372 | grid-gap: 6rem; 373 | } 374 | .gap_desktop_7 { 375 | grid-gap: 7rem; 376 | } 377 | .gap_desktop_8 { 378 | grid-gap: 8rem; 379 | } 380 | .gap_desktop_9 { 381 | grid-gap: 9rem; 382 | } 383 | .gap_desktop_10 { 384 | grid-gap: 10rem; 385 | } 386 | .gap_desktop_11 { 387 | grid-gap: 11rem; 388 | } 389 | .gap_desktop_12 { 390 | grid-gap: 12rem; 391 | } 392 | } 393 | 394 | @media (min-width: $mq_extra) { 395 | 396 | } 397 | 398 | --------------------------------------------------------------------------------