Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f08043a
Prepare for Contao 5.7
qzminski Mar 6, 2026
0c009ab
CS
qzminski Mar 6, 2026
f6ade03
Rector
qzminski Mar 6, 2026
d6ab309
Update the code to newest standards
qzminski Mar 6, 2026
82e6f3d
PHPStan fixes
qzminski Mar 6, 2026
db93eed
Switch to Stimulus and TomSelect
qzminski Mar 12, 2026
9272f45
Downgrade codefog/contao-haste to 5.3
qzminski Mar 12, 2026
7957703
Fix the extension class not loading YAML files properly
qzminski Mar 12, 2026
754d9c3
Improve backend assets
qzminski Mar 12, 2026
40c60cc
CS
qzminski Mar 13, 2026
dd7766f
Add missing dependencies to composer.json
qzminski Mar 13, 2026
acf9dd2
Rector + CS
qzminski Mar 13, 2026
23220b4
Remove Travis
qzminski Mar 13, 2026
8741ef2
Replace php-coveralls integration in CI workflow
qzminski Mar 13, 2026
c7079b9
Stylelint + ESlint
qzminski Mar 13, 2026
a5056c7
Biome
qzminski Mar 13, 2026
60640ac
Fix installing deps in CI
qzminski Mar 13, 2026
c32c5fe
Fix installing deps in CI
qzminski Mar 13, 2026
6c8a091
Fix the unit tests
qzminski Mar 13, 2026
282a0d8
Fix the "unit-tests" script in composer.json
qzminski Mar 13, 2026
416af8c
CS + Rector
qzminski Mar 13, 2026
941ba17
Fix unit tests
qzminski Mar 13, 2026
9ba732e
Remove code coverage stuff
qzminski Mar 13, 2026
a918de1
Update PHPUnit configuration: upgrade schema to 12.5, enforce stricte…
qzminski Mar 13, 2026
04f9f64
Get rid of old hook definitions
qzminski Mar 16, 2026
70ce232
Update the documentation. Change from `tagsSortable` to `isSortable`
qzminski Mar 17, 2026
3c5fbaf
Improve the backend widget
qzminski Mar 17, 2026
1cb66c2
Stylelint
qzminski Mar 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
push: ~
pull_request: ~

permissions: read-all

jobs:
ci:
uses: 'terminal42/contao-build-tools/.github/workflows/build-tools.yml@main'

php:
name: PHP ${{ matrix.php-version }}
runs-on: ubuntu-latest

strategy:
matrix:
php-version:
- '8.3'
- '8.4'
- '8.5'

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}

- name: Install dependencies
run: COMPOSER_MEMORY_LIMIT=-1 composer install --dev --no-interaction

- name: Run tests
run: composer run unit-tests
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@
/build
/node_modules
/vendor
/yarn.lock
20 changes: 0 additions & 20 deletions .travis.yml

This file was deleted.

4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
[![Version](https://img.shields.io/packagist/v/codefog/tags-bundle)](https://packagist.org/packages/codefog/tags-bundle)
[![License](https://img.shields.io/github/license/codefog/tags-bundle)](https://github.com/codefog/tags-bundle/blob/master/LICENSE.txt)
[![Downloads](https://img.shields.io/packagist/dm/codefog/tags-bundle)](https://packagist.org/packages/codefog/tags-bundle)
[![Build Status](https://travis-ci.org/codefog/tags-bundle.svg?branch=master)](https://travis-ci.org/codefog/tags-bundle)
[![Coverage Status](https://coveralls.io/repos/github/codefog/tags-bundle/badge.svg?branch=master)](https://coveralls.io/github/codefog/tags-bundle?branch=master)

Tags Bundle is an extension for the [Contao Open Source CMS](https://contao.org).

The extension provides the tagging functionality in Contao. Backend widget is powered up by the excellent
UI widget [selectize.js](https://github.com/selectize/selectize.js) that allows to easily manage tags.
UI widget [Tom Select](https://tom-select.js.org/) that allows to easily manage tags.

> **IMPORTANT NOTE:** This project is aimed at the developers and it does not provide tagging
to any of standard Contao features by default!
Expand Down
1 change: 1 addition & 0 deletions assets/add--dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions assets/tags-widget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import './tags-widget.scss';

import { Application, Controller } from '@hotwired/stimulus';
import TomSelect from 'tom-select';

const application = Application.start();
application.debug = process.env.NODE_ENV === 'development';
application.register(
'codefog--tags-widget',
class extends Controller {
static targets = ['input'];

static values = {
config: {
type: Object,
default: {},
},
};

connect() {
this.tomSelect = new TomSelect(this.inputTarget, this.#getOptions());
}

disconnect() {
this.tomSelect.destroy();
}

add(event) {
this.tomSelect.addItem(event.target.value);
}

remove(event) {
this.tomSelect.removeItem(event.target.value);
}

#getOptions() {
const config = this.configValue;

const options = {
delimiter: ',',
options: config.allTags,
items: config.valueTags,
persist: false,
render: {
// biome-ignore format: long line
option_create: (data, escape) => `<div class="create">${config.addLabel} <strong>${escape(data.input)}</strong>&hellip;</div>`,
// biome-ignore format: long line
item: (data, escape) => `<div>${escape(data.text)}<button type="button" class="cfg-tags-widget__remove" value="${data.value}" aria-label="${config.removeLabel} ${escape(data.text)}" data-action="click->codefog--tags-widget#remove:prevent">${config.removeLabel}</button></div>`,
no_results: () => `<div class="no-results">${config.noResultsLabel}</div>`,
},
};

if (config.allowCreate) {
options.create = (input) => ({ value: input, text: input });
}

if (config.maxItems) {
options.maxItems = config.maxItems;
}

if (config.sortable) {
options.plugins = ['drag_drop'];
}

return options;
}
},
);
172 changes: 172 additions & 0 deletions assets/tags-widget.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
$select-border-radius: var(--border-radius);
$select-color-border: var(--form-border);
$select-color-dropdown-item-active-text: #fff;
$select-color-dropdown-item-active: var(--choices-highlighted);
$select-color-dropdown-item-create-text: var(--text);
$select-color-dropdown: var(--choices-bg-color-dropdown, #fff);
$select-color-input: var(--form-bg);
$select-font-size: inherit;
$select-line-height: 13px;
$select-max-height-dropdown: 300px;
$select-padding-dropdown-item-x: 6px;
$select-padding-dropdown-item-y: 3px;
$select-padding-x: 6px;
$select-padding-y: 5px;

@import '~tom-select/dist/scss/tom-select';

.cfg-tags-widget {
margin-top: 3px;
margin-bottom: 3px;

&__all {
margin-top: 3px;

&-headline {
margin-bottom: 5px;
margin-top: 5px;
font-size: 0.75rem;
}

&-items {
display: flex;
flex-wrap: wrap;
gap: 3px;
}
}

&__tag {
display: block;
padding: 5px 10px 5px 25px;
background: transparent url('./add.svg') no-repeat 6px center;
border: 1px solid var(--form-border);
border-radius: var(--border-radius);
box-sizing: border-box;
font-size: 13px;
cursor: pointer;
transition: background 0.2s ease;

html[data-color-scheme='dark'] & {
background-image: url('./add--dark.svg');
}

&:hover {
color: inherit;
background-color: var(--form-button-hover);
}

&:active {
color: var(--form-button-active);
}
}

&__remove {
position: relative;
display: inline-block;
width: var(--choices-button-dimension, 8px);
margin: 0 calc(var(--choices-button-offset, 8px) * -0.5) 0 var(--choices-button-offset, 8px);
padding-bottom: 2px;
padding-left: calc(var(--choices-button-offset, 8px) * 2);
border: 0;
border-left: 1px solid var(--choices-border);
border-radius: var(--choices-button-border-radius, 0);
background-color: transparent;
background-image: var(
--choices-icon-cross,
url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==')
);
background-position: center;
background-repeat: no-repeat;
background-size: var(--choices-button-dimension, 8px);
opacity: var(--choices-button-opacity, 0.75);
cursor: pointer;
line-height: var(--choices-button-line-height, 1);
text-indent: -9999px;
appearance: none;

html:not([data-color-scheme='dark']) & {
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMjIyIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==');
}

&:is(:hover, :focus) {
--choices-button-opacity: var(--choices-button-opacity-hover, 1);
}

&:focus {
box-shadow: 0 0 0 2px var(--choices-highlight-color, #005f75);
}
}

.ts-wrapper {
&.dropdown-active .ts-control {
border-radius: var(--choices-border-radius, 2.5px) var(--choices-border-radius, 2.5px) 0 0;
}

&.multi.has-items .ts-control {
padding-left: 3px;

input {
margin: -3px 4px 0 !important;
}
}

.ts-control {
> div {
margin: 0 3px 3px 0;
padding: 1px 5px 1px 6px;
background-color: var(--choices-bg);
border: 1px solid var(--choices-border);
border-radius: 3px;
color: var(--choices-item-color, #fff);
cursor: auto;
}

input {
color: var(--text);
}
}

.create {
padding-left: 25px !important;
background-image: url('./add.svg');
background-repeat: no-repeat;
background-position: 6px center;

&.active,
html[data-color-scheme='dark'] & {
background-image: url('./add--dark.svg');
}
}
}

.ts-dropdown {
margin-top: -1px;
border: 1px solid var(--choices-keyline-color, #ddd);
border-radius: 0 0 var(--choices-border-radius, 2.5px) var(--choices-border-radius, 2.5px);
box-shadow: none;
overflow: hidden;

* {
font-size: 13px;
}

.no-results {
color: var(--text);
}

[data-selectable] {
&.option {
color: var(--text);
}

&.active {
color: #fff;
}

.highlight {
background-color: var(--orange);
color: #fff;
}
}
}
}
31 changes: 19 additions & 12 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@
}
],
"require": {
"php": "^8.0",
"ext-pdo": "*",
"contao/core-bundle": "^4.13 || ^5.0",
"codefog/contao-haste": "^5.0",
"doctrine/dbal": "^2.12 || ^3.0",
"symfony/config": "^5.4 || ^6.4 || ^7.0"
"php": "^8.3",
"contao/core-bundle": "^5.7",
"codefog/contao-haste": "^5.3",
"doctrine/dbal": "^3.7 || ^4.3",
"symfony/asset": "^7.4 || ^8.0",
"symfony/dependency-injection": "^7.4 || ^8.0",
"symfony/http-foundation": "^7.4 || ^8.0",
"symfony/http-kernel": "^7.4 || ^8.0",
"symfony/config": "^7.4 || ^8.0"
},
"require-dev": {
"contao/manager-plugin": "^2.0",
"contao/easy-coding-standard": "^5.3",
"phpunit/phpunit": "^7.0 || ^9.3",
"contao/test-case": "^4.13",
"php-coveralls/php-coveralls": "^2.2"
"contao/test-case": "^5.7",
"terminal42/contao-build-tools": "dev-main",
"phpunit/phpunit": "^12.5"
},
"conflict": {
"contao/manager-plugin": "<2.0 || >=3.0"
Expand All @@ -43,10 +45,15 @@
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
"contao-components/installer": false,
"contao/manager-plugin": false,
"contao-community-alliance/composer-plugin": false,
"dealerdirect/phpcodesniffer-composer-installer": true,
"php-http/discovery": true,
"terminal42/contao-build-tools": true
}
},
"scripts": {
"cs-fixer": "vendor/bin/ecs check src tests --fix --ansi"
"unit-tests": "@php vendor/bin/phpunit"
}
}
Loading
Loading