Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 30 additions & 18 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
UPGRADE FROM EASYADMIN 4.X to 5.X
=================================
Upgrade Guide
=============

## Pretty URLs
## EasyAdmin 5.0.14

The HTML markup of the boolean switch has changed. Before, the element wrapping
the switch applied the `.form-switch` CSS class and the checkbox used the
`.form-check-input` class. Now, the switch is rendered with the new
`<twig:ea:Switch>` component: the wrapping element uses the `.ea-switch` class and
the checkbox uses the `.ea-switch-input` class. This affects both the switch shown
in the `index` page and the one displayed in the `edit`/`new` forms. Update any
custom CSS or JavaScript that targeted the old selectors.

## Upgrading from Symfony 4.x to 5.x

### Pretty URLs

Using pretty URLs is now mandatory. They are created with a custom route loader
that must be enabled in your application. If you use Symfony Flex, this file is
Expand All @@ -14,7 +26,7 @@ easyadmin:
type: easyadmin.routes
```

## Admin Context
### Admin Context

The global `ea` variable injected in all templates is removed in favor of the
equivalent `ea()` Twig function, which returns the current context of the
Expand All @@ -28,7 +40,7 @@ EasyAdmin application:
{{ ea().i18n.translationDomain }}
```

## Main Menus
### Main Menus

The `linkToCrud()` method used to link to CRUD controllers from the main menu of the
dashboard was removed in favor of the new `linkTo()` method:
Expand All @@ -45,7 +57,7 @@ yield MenuItem::linkTo(BlogPostCrudController::class, 'Blog Posts', 'fa fa-file-
yield MenuItem::linkTo(CommentCrudController::class);
```

## Custom CRUD Actions
### Custom CRUD Actions

Custom CRUD actions now require to apply the `#[AdminRoute]` attribute to them.
Otherwise, they are ignored when generating routes for the backend and code
Expand Down Expand Up @@ -115,7 +127,7 @@ class CommentCrudController extends AbstractCrudController
}
```

## Actions
### Actions

Some methods related to actions have been removed in favor of equivalent
methods with better names:
Expand All @@ -132,7 +144,7 @@ $action->renderAsButton()->...
$action->renderAsForm()->...
```

## Referrers
### Referrers

EasyAdmin URLs no longer include the `referrer` query parameter, and the
`AdminContext:getReferrer()` method was removed.
Expand All @@ -152,7 +164,7 @@ return $this->redirect($batchActionDto->getReferrer());
return $this->redirect($adminContext->getRequest()->headers->get('referer'));
```

## Forms
### Forms

Form panels are now called Form fieldsets and the `FormField::addPanel()` method
was removed:
Expand All @@ -165,16 +177,16 @@ yield FormField::addPanel('...');
yield FormField::addFieldset('...');
```

## Attributes
### Attributes

The `#[AdminCrud]` and `#[AdminAction]` attributes have been removed in favor
of the `#[AdminRoute]` attribute.

## Contracts
### Contracts

The following contract interfaces changed:

### `Contracts\Context\AdminContextInterface`
#### `Contracts\Context\AdminContextInterface`

```php
// Before (4.x)
Expand All @@ -186,7 +198,7 @@ public function getAdminControllers(): AdminControllerRegistry;

The `getSignedUrls()` and `getReferrer()` methods are removed.

### `Contracts\Controller\CrudControllerInterface`
#### `Contracts\Controller\CrudControllerInterface`

```php
// Before (4.x)
Expand All @@ -196,7 +208,7 @@ public function createEntity(string $entityFqcn);
public function createEntity(string $entityFqcn): object;
```

### `Contracts\Orm\EntityPaginatorInterface`
#### `Contracts\Orm\EntityPaginatorInterface`

```php
// Before (4.x)
Expand All @@ -206,7 +218,7 @@ public function getResultsAsJson(): string;
public function getResultsAsJson(?callable $callback = null, ?string $twigTemplate = null, bool $renderAsHtml = false): string;
```

### `Contracts\Provider\AdminContextInterface`
#### `Contracts\Provider\AdminContextInterface`

```php
// Before (4.x)
Expand All @@ -217,13 +229,13 @@ public function hasContext(): bool;
// alternative: check if getContext() return value is null
```

### `Contracts\Menu\MenuItemMatcherInterface`
#### `Contracts\Menu\MenuItemMatcherInterface`

The `isSelected()` and `isExpanded()` methods were removed. A new
`markSelectedMenuItem(array<MenuItemDto> $menuItems, Request $request)` method
has been added.

### `Contracts\Router\AdminRouteGeneratorInterface`
#### `Contracts\Router\AdminRouteGeneratorInterface`

```php
// Before (4.x)
Expand All @@ -235,7 +247,7 @@ public function findRouteName(string|null $dashboardFqcn = null, string|null $cr

The `usesPrettyUrls()` method was removed.

## Static Analysis
### Static Analysis

In 5.x, PHPStan will report an error if a class extends
`AbstractCrudController` without specifying the entity type:
Expand Down
11 changes: 0 additions & 11 deletions assets/css/easyadmin-theme/datagrids.css
Original file line number Diff line number Diff line change
Expand Up @@ -367,17 +367,6 @@ table.datagrid:not(.datagrid-empty) tr:not(.empty-row) td.actions.actions-as-dro
padding: 6px 8px;
}

.datagrid .field-boolean .form-switch {
display: inline-flex;
justify-content: center;
margin-block-end: 0;
padding-inline-start: 0;
}
.datagrid .field-boolean .form-switch input {
margin-block-start: 0;
position: relative;
inset-block-start: 3px;
}
@media (max-width: 992px) {
.datagrid .field-country {
text-align: left !important;
Expand Down
121 changes: 101 additions & 20 deletions assets/css/easyadmin-theme/switches.css
Original file line number Diff line number Diff line change
@@ -1,31 +1,112 @@
/* Switches (toggles)
/* ------------------------------------------------------------------------- */
/* <twig:ea:Switch> component. A native checkbox is layered (invisible) on top
/* of a track + thumb, so the thumb can slide with a CSS transform like the
/* shadcn/ui switch. The colors are themed via the --switch-* design tokens
/* defined in variables-theme.css (light + dark schemes). */

.form-switch .form-check-input {
background-color: var(--form-switch-bg);
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='rgba(148, 163, 184, 0.8)'/%3E%3C/svg%3E");
border-color: var(--form-switch-border-color);
.ea-switch {
--ea-switch-width: 32px;
--ea-switch-height: 18px;
--ea-switch-thumb-size: 14px;
--ea-switch-thumb-inset: 2px;

position: relative;
display: inline-flex;
align-items: center;
vertical-align: middle;
inline-size: var(--ea-switch-width);
block-size: var(--ea-switch-height);
flex-shrink: 0;
}

.ea-switch-sm {
--ea-switch-width: 24px;
--ea-switch-height: 14px;
--ea-switch-thumb-size: 10px;
}

/* the real checkbox sits on top of the visuals, fully transparent, so it stays
keyboard-focusable, clickable and submittable while the track/thumb render */
.ea-switch-input {
position: absolute;
inset: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
block-size: 18px;
inline-size: 32px;
-webkit-appearance: none; /* needed for Safari */
z-index: 1;
}

.ea-switch-track {
position: absolute;
inset: 0;
border-radius: 999px;
background-color: var(--switch-bg);
transition: background-color 0.15s ease-in-out;
pointer-events: none;
}

.ea-switch-thumb {
position: absolute;
inset-block-start: var(--ea-switch-thumb-inset);
inset-inline-start: var(--ea-switch-thumb-inset);
inline-size: var(--ea-switch-thumb-size);
block-size: var(--ea-switch-thumb-size);
border-radius: 50%;
background-color: var(--switch-thumb-bg);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
transition: transform 0.15s ease-in-out;
}

.ea-switch-input:checked ~ .ea-switch-track {
background-color: var(--switch-checked-bg);
}
.form-switch .form-check-input:checked,
.ea-dark-scheme .form-switch .form-check-input:checked {
background-color: var(--form-switch-checked-bg);
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='rgb(255, 255, 255)'/%3E%3C/svg%3E");
border-color: var(--form-switch-checked-bg);

.ea-switch-input:checked ~ .ea-switch-track .ea-switch-thumb {
transform: translateX(calc(var(--ea-switch-width) - var(--ea-switch-thumb-size) - 2 * var(--ea-switch-thumb-inset)));
}

.ea-switch-input:focus-visible ~ .ea-switch-track {
box-shadow: 0 0 0 3px var(--switch-focus-ring);
}
.ea-dark-scheme .form-switch .form-check-input:checked {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='rgba(255, 255, 255, 0.8)'/%3E%3C/svg%3E");

/* color variants: only the checked track + focus ring change, reusing the same
colors as the button variants. The unchecked track stays neutral gray */
.ea-switch-success {
--switch-checked-bg: var(--switch-success-bg);
--switch-focus-ring: color-mix(in srgb, var(--switch-success-bg) 35%, transparent);
}
.ea-switch-warning {
--switch-checked-bg: var(--switch-warning-bg);
--switch-focus-ring: color-mix(in srgb, var(--switch-warning-bg) 35%, transparent);
}
.ea-dark-scheme .form-switch .form-check-input {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='rgba(163, 163, 163, 0.8)'/%3E%3C/svg%3E");
.ea-switch-danger {
--switch-checked-bg: var(--switch-danger-bg);
--switch-focus-ring: color-mix(in srgb, var(--switch-danger-bg) 35%, transparent);
}
.form-switch.disabled,
.form-switch .form-check-input[disabled] {

/* disabled: dimmed and non-interactive (the wrapper gets .ea-switch-disabled
from the component, and the input itself is also disabled in forms) */
.ea-switch-disabled,
.ea-switch:has(.ea-switch-input:disabled) {
opacity: 0.5;
}

.ea-switch-disabled .ea-switch-input,
.ea-switch-input:disabled {
cursor: not-allowed;
}
.form-switch .form-check-input:focus {
box-shadow: none;

/* switch inside a form (edit/new): the <twig:ea:Switch> is nested in the field
label, so lay them out as a row with the label text next to the switch */
.ea-switch-check {
padding-inline-start: 0;
}
.ea-switch-check .form-check-label {
display: inline-flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
}
19 changes: 13 additions & 6 deletions assets/css/easyadmin-theme/variables-theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,15 @@
--form-input-text-color: var(--gray-700);
--form-input-group-text-bg: var(--form-control-bg);
--form-input-group-text-border-color: var(--form-input-border-color);
--form-switch-bg: var(--body-bg);
--form-switch-border-color: var(--gray-400);
--form-switch-checked-bg: var(--indigo-500);
--switch-bg: var(--gray-300);
--switch-checked-bg: var(--indigo-500);
--switch-thumb-bg: var(--white);
--switch-focus-ring: rgba(99, 102, 241, 0.35);
/* switch color variants reuse the button variant colors (these forward to
scheme-aware tokens, so they resolve correctly in dark mode too) */
--switch-success-bg: var(--button-success-hover-bg);
--switch-warning-bg: var(--button-warning-hover-bg);
--switch-danger-bg: var(--button-danger-hover-bg);
--form-type-check-input-border-color: var(--gray-400);
--form-type-check-input-box-shadow: 0 1px 2px 0 var(--gray-50);
--form-type-check-input-checked-bg: var(--indigo-500);
Expand Down Expand Up @@ -549,9 +555,10 @@
--form-input-text-color: var(--true-gray-200);
--form-input-group-text-bg: var(--true-gray-800);
--form-input-group-text-border-color: var(--true-gray-600);
--form-switch-bg: var(--true-gray-600);
--form-switch-border-color: var(--true-gray-700);
--form-switch-checked-bg: var(--blue-600);
--switch-bg: var(--true-gray-600);
--switch-checked-bg: var(--blue-600);
--switch-thumb-bg: var(--gray-300);
--switch-focus-ring: rgba(90, 168, 252, 0.4);
--form-type-check-input-border-color: var(--true-gray-400);
--form-type-check-input-box-shadow: 0 1px 2px 0 var(--true-gray-800);
--form-type-check-input-checked-bg: var(--blue-600);
Expand Down
4 changes: 2 additions & 2 deletions assets/js/field-boolean.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ document.addEventListener('DOMContentLoaded', () => {
// toggle switches are only created in index page (i.e. in datagrid tables) because
// in other pages they act like simple checkboxes or labels. Only in index page
// the toggle switch can change the value of an entity propert via Ajax requests
document.querySelectorAll('td.field-boolean .form-switch input[type="checkbox"]').forEach((toggleField) => {
document.querySelectorAll('td.field-boolean .ea-switch input[type="checkbox"]').forEach((toggleField) => {
new ToggleSwitch(toggleField);
});
});
Expand Down Expand Up @@ -39,6 +39,6 @@ class ToggleSwitch {
#disableField() {
this.field.checked = !this.field.checked;
this.field.disabled = true;
this.field.closest('.form-switch').classList.add('disabled');
this.field.closest('.ea-switch').classList.add('ea-switch-disabled');
}
}
16 changes: 16 additions & 0 deletions doc/fields/BooleanField.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,20 @@ If you prefer to not allow changing the property value in this way, use this opt

yield BooleanField::new('...')->renderAsSwitch(false);

Colored Switches
~~~~~~~~~~~~~~~~

By default the switch uses a neutral color when it's turned on. If you want to
highlight its meaning, use one of the following methods to color the "on" state
with the same colors as the ``success``, ``warning`` and ``danger`` buttons.
These methods also enable the switch rendering, so you don't need to call
``renderAsSwitch()`` too::

yield BooleanField::new('...')->renderAsSuccessSwitch(); // green when on
yield BooleanField::new('...')->renderAsWarningSwitch(); // amber when on
yield BooleanField::new('...')->renderAsDangerSwitch(); // red when on

The color is applied everywhere the field is rendered as a switch (the ``index``
toggle and the ``edit``/``new`` forms). The "off" state always uses the neutral color.

.. _`CheckboxType`: https://symfony.com/doc/current/reference/forms/types/checkbox.html
2 changes: 1 addition & 1 deletion public/app.5892cfa7.css → public/app.483f9549.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions public/entrypoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"entrypoints": {
"app": {
"css": [
"/app.5892cfa7.css"
"/app.483f9549.css"
],
"js": [
"/app.302b82dd.js"
Expand All @@ -25,7 +25,7 @@
},
"field-boolean": {
"js": [
"/field-boolean.6eb3e3a7.js"
"/field-boolean.6adeff4e.js"
]
},
"field-code-editor": {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading