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
17 changes: 0 additions & 17 deletions assets/css/easyadmin-theme/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -886,23 +886,6 @@ a.user-menu-wrapper .user-details:hover {
}
}

/* Modal for delete action */
.modal-content {
border-color: var(--modal-border-color);
}
.modal-body {
background: var(--modal-bg);
}
.modal-body h4 {
font-size: var(--font-size-lg);
}

.modal-footer {
background: var(--modal-footer-bg);
border-color: var(--modal-border-color);
padding: 8px 10px;
}

/* Flash messages */
#flash-messages {
background: transparent;
Expand Down
8 changes: 6 additions & 2 deletions assets/css/easyadmin-theme/datagrids.css
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,11 @@ table.datagrid:not(.datagrid-empty) tr:not(.empty-row) td.actions.actions-as-dro
#modal-filters .modal-content {
background: var(--modal-bg);
border: 1px solid var(--modal-border-color);
border-radius: var(--border-radius);
border-radius: var(--border-radius-xl);
/* the filters layout manages its own spacing, so opt out of the modal
component's default content padding/gap and render the sections flush */
padding: 0;
gap: 0;
}
#modal-filters .modal-header {
background: var(--modal-header-bg);
Expand All @@ -517,7 +521,7 @@ table.datagrid:not(.datagrid-empty) tr:not(.empty-row) td.actions.actions-as-dro
#modal-filters .modal-body {
background: var(--modal-bg);
border-block-end: 0;
border-radius: var(--border-radius);
border-radius: var(--border-radius-xl);
padding: 15px;
}
.action-filters-button .icon {
Expand Down
172 changes: 172 additions & 0 deletions assets/css/easyadmin-theme/modals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/* Modals: shadcn alert-dialog look on top of Bootstrap's markup + JS */

/* Backdrop: no solid black overlay, just a slight blur over the page */
.modal-backdrop {
--bs-backdrop-bg: transparent;
--bs-backdrop-opacity: 1;
background-color: rgba(0, 0, 0, 0.1);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
transition: opacity 0.1s ease;
}

/* Always center the dialog vertically and horizontally on screen.
The max-width cap (applied at every viewport, not just ≥576px like Bootstrap's
default) makes wider sizes shrink to fit small screens so they never overflow. */
.modal-dialog {
display: flex;
align-items: center;
min-block-size: calc(100% - var(--bs-modal-margin) * 2);
max-inline-size: min(var(--bs-modal-width), calc(100% - var(--bs-modal-margin) * 2));
/* center horizontally at every viewport: Bootstrap only auto-centers at ≥576px,
but our max-width cap applies below that too, so without this the dialog would
be left-aligned on small/medium screens. */
margin-inline: auto;
}

/* Width scale (shadcn-inspired). The default (380px, set in variables-theme.css)
is intentionally narrow so simple confirmation dialogs don't look empty; opt into
wider modals with size="lg"/"xl". EA-namespaced class names avoid colliding with
Bootstrap's own .modal-sm/.modal-lg. */
.modal-dialog-sm {
--bs-modal-width: 320px;
}
.modal-dialog-lg {
--bs-modal-width: 512px;
}
.modal-dialog-xl {
--bs-modal-width: 800px;
}

/* Appear/disappear in place with a quick fade + zoom (shadcn) instead of sliding from the top.
Bootstrap only fades the backdrop, so we add opacity to the dialog here. Closing reuses the
.fade state (opacity 0 + scale .95), so fade-out + zoom-out come for free. */
.modal.fade .modal-dialog {
transform: scale(0.95);
opacity: 0;
transition: transform 0.1s ease, opacity 0.1s ease;
}
.modal.show .modal-dialog {
transform: scale(1);
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
.modal.fade .modal-dialog {
transition: none;
}
}

/* Lay out the sections (header/body/footer) on a grid with a consistent gap, like
shadcn's alert-dialog content (`grid gap-4 p-4`). The padding lives here and the
sections below drop their own padding, so the spacing between sections is a single
16px grid gap instead of per-section padding. */
.modal-content {
display: grid;
gap: 24px;
padding: 16px;
border-color: var(--modal-border-color);
/* Bootstrap's dist CSS is built with $enable-shadows: false, so its modal
box-shadow rule is never emitted (the --bs-modal-box-shadow var is defined
but unused). Apply it explicitly so the modal keeps its elevation. */
box-shadow: var(--bs-modal-box-shadow);
}

/* Header: seamless (no background, no separating border) with a shadcn-style heading.
The modal window keeps its rounded corners on every side even without a header,
because the body/footer have no background of their own and let the rounded
.modal-content background show through. */
.modal-header {
background: transparent;
border: 0;
padding: 0;
}
.modal-title {
color: var(--text-color);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-medium);
}

/* The Modal component always wraps the body block in .modal-body-content, which
stacks its children on a grid so ANY block content (headings, paragraphs, lists, …)
is separated by one consistent gap. */
.modal-body {
padding: 0;
}
.modal-body h4 {
color: var(--text-color);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-medium);
line-height: 1.5;
}
.modal-body p {
color: var(--text-muted);
}
.modal-body-content {
display: grid;
gap: 6px;
}
/* let the grid gap be the only separator, whatever the children are */
.modal-body-content > * {
margin-block: 0;
}

/* Optional leading/top icon for confirmation-style modals (shadcn): a soft tinted
rounded square shown next to (icon) or above (topIcon) the body content. */
.modal-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
inline-size: 40px;
block-size: 40px;
border-radius: var(--border-radius-lg);
background: var(--modal-icon-bg);
color: var(--modal-icon-color);
}
.modal-icon .icon {
font-size: 20px;
line-height: 1;
}
.modal-icon .icon svg {
inline-size: 1em;
block-size: 1em;
}

/* `icon`: icon top-left, body content beside it */
.modal-body--icon {
display: flex;
align-items: flex-start;
gap: 16px;
}

/* `topIcon`: larger icon stacked above the body content */
.modal-body--top-icon {
display: flex;
flex-direction: column;
gap: 12px;
}
.modal-icon--top {
inline-size: 48px;
block-size: 48px;
}
.modal-icon--top .icon {
font-size: 24px;
}

/* Seamless footer: no background, no separating border, right-aligned buttons. */
.modal-footer {
background: transparent;
border-block-start: 0;
padding: 0;
}

/* Stack footer buttons (reversed, full-width) on narrow screens, like shadcn. */
@media (max-width: 575.98px) {
.modal-footer {
flex-direction: column-reverse;
align-items: stretch;
}
.modal-footer .btn {
justify-content: center;
}
}
1 change: 1 addition & 0 deletions assets/css/easyadmin-theme/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@import "./variables-theme.css";

@import "./base.css";
@import "./modals.css";
@import "./menu.css";
@import "./datagrids.css";
@import "./detail-page.css";
Expand Down
22 changes: 16 additions & 6 deletions assets/css/easyadmin-theme/variables-theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
--font-size-xxl: 24px;
--font-size-xxxl: 28px;

--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;

--shadow-md: 0 4px 6px -1px rgb(15, 23, 43, 0.1), 0 2px 4px -2px rgb(15, 23, 42, 0.1);
--shadow-lg: 0 10px 15px -3px rgb(15, 23, 43, 0.1), 0 4px 6px -4px rgb(15, 23, 42, 0.1);
--shadow-xl: 0 20px 25px -5px rgba(15, 23, 42, 0.2), 0 8px 10px -6px rgba(15, 23, 42, 0.2);
Expand Down Expand Up @@ -136,11 +141,13 @@
--field-language-badge-border-color: var(--gray-300);
--field-country-flag-border-color: var(--gray-200);
--modal-bg: var(--white);
--modal-border-color: var(--gray-200);
--modal-border-color: var(--border-secondary-color);
--modal-header-bg: var(--gray-50);
--modal-header-border-color: var(--gray-300);
--modal-footer-bg: var(--gray-100);
--modal-title-color: var(--gray-700);
--modal-icon-bg: var(--gray-100);
--modal-icon-color: var(--gray-700);
--detail-label-tooltip-underline-color: var(--gray-400);
--form-label-color: var(--gray-800);
--form-control-bg: var(--white);
Expand Down Expand Up @@ -420,6 +427,7 @@

--border-radius: 4px;
--border-radius-lg: 8px;
--border-radius-xl: 12px;
--border-radius-sm: 2px;
--border-width: 1px;
--border-style: solid;
Expand Down Expand Up @@ -528,11 +536,13 @@
--field-language-badge-border-color: var(--true-gray-600);
--field-country-flag-border-color: var(--true-gray-600);
--modal-bg: var(--true-gray-800);
--modal-border-color: var(--true-gray-600);
--modal-border-color: var(--border-secondary-color);
--modal-header-bg: var(--true-gray-900);
--modal-header-border-color: var(--true-gray-600);
--modal-footer-bg: var(--true-gray-700);
--modal-title-color: var(--true-gray-400);
--modal-icon-bg: var(--true-gray-700);
--modal-icon-color: var(--true-gray-300);
--pagination-disabled-color: var(--true-gray-600);
--detail-label-tooltip-underline-color: var(--true-gray-500);
--form-label-color: var(--true-gray-300);
Expand Down Expand Up @@ -873,16 +883,16 @@
}
.modal {
--bs-modal-zindex: 2040;
--bs-modal-width: 500px;
--bs-modal-width: 380px;
--bs-modal-padding: 1rem 1.25rem;
--bs-modal-margin: 0.5rem;
--bs-modal-color: var(--text-color);
--bs-modal-bg: var(--modal-bg);
--bs-modal-border-color: var(--modal-border-color);
--bs-modal-border-width: var(--border-width);
--bs-modal-border-radius: var(--border-radius);
--bs-modal-box-shadow: var(--bs-box-shadow-sm);
--bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));
--bs-modal-border-radius: var(--border-radius-xl);
--bs-modal-box-shadow: var(--box-shadow-lg);
--bs-modal-inner-border-radius: calc(var(--border-radius-xl) - (var(--border-width)));
--bs-modal-header-padding-x: 1.25rem;
--bs-modal-header-padding-y: 1rem;
--bs-modal-header-padding: 1rem 1.25rem;
Expand Down
47 changes: 37 additions & 10 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ import { sanitizeUrl, toggleVisibilityClasses } from './helpers';
// Provide Bootstrap variable globally to allow custom backend pages to use it
window.bootstrap = bootstrap;

// maps an action variant to its button CSS class; shared by the action and
// batch action confirmation modals so their confirm button mirrors the
// variant/color of the action that opened the modal
const variantToClass = {
default: 'btn-secondary',
primary: 'btn-primary',
success: 'btn-success',
warning: 'btn-warning',
danger: 'btn-danger',
};
const allVariantClasses = Object.values(variantToClass);

document.addEventListener('DOMContentLoaded', () => {
window.EasyAdminApp = new App();
});
Expand Down Expand Up @@ -336,7 +348,7 @@ class App {
});
});

const modalTitle = document.querySelector('#batch-action-confirmation-title');
const modalTitle = document.querySelector('#modal-batch-action .modal-body-content h4');
const titleContentWithPlaceholders = modalTitle?.textContent;

document.querySelectorAll('[data-action-batch]').forEach((dataActionBatch) => {
Expand Down Expand Up @@ -390,7 +402,14 @@ class App {
.replace('%action_name%', actionName)
.replace('%num_items%', selectedItems.length.toString());

document.querySelector('#modal-batch-action-button').addEventListener('click', submitBatchAction);
// apply to the modal button the same variant as the action that opened the modal
const modalButton = document.querySelector('#modal-batch-action-button');
const variant = actionElement.getAttribute('data-action-variant') || 'danger';
const variantClass = variantToClass[variant] || 'btn-danger';
modalButton.classList.remove(...allVariantClasses);
modalButton.classList.add(variantClass);

modalButton.addEventListener('click', submitBatchAction, { once: true });
}
});
});
Expand All @@ -405,17 +424,10 @@ class App {

#createActionConfirmationModals() {
const modalTitle = document.querySelector('#action-confirmation-title');
const modalContent = document.querySelector('#action-confirmation-content');
const modalButton = document.querySelector('#modal-action-confirmation-button');
const defaultTitleTemplate = modalTitle?.textContent;
const defaultButtonLabel = modalButton?.textContent;
const variantToClass = {
default: 'btn-secondary',
primary: 'btn-primary',
success: 'btn-success',
warning: 'btn-warning',
danger: 'btn-danger',
};
const allVariantClasses = Object.values(variantToClass);

document.querySelectorAll('[data-action-confirmation="true"]').forEach((actionElement) => {
actionElement.addEventListener('click', (event) => {
Expand All @@ -434,6 +446,21 @@ class App {
.replace('%entity_name%', entityName)
.replace('%entity_id%', entityId);

// optional description below the title (e.g. the DELETE action); hidden when absent
const contentMessage = actionElement.getAttribute('data-action-confirmation-content');
if (modalContent) {
if (contentMessage) {
modalContent.textContent = contentMessage
.replace('%action_name%', actionName)
.replace('%entity_name%', entityName)
.replace('%entity_id%', entityId);
modalContent.classList.remove('d-none');
} else {
modalContent.textContent = '';
modalContent.classList.add('d-none');
}
}

// use custom button label if provided, otherwise use default
const customButtonLabel = actionElement.getAttribute('data-action-confirmation-button');
modalButton.textContent = customButtonLabel ?? defaultButtonLabel;
Expand Down
4 changes: 2 additions & 2 deletions public/app.5892cfa7.css → public/app.066a0e62.css

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions public/app.302b82dd.js

This file was deleted.

2 changes: 2 additions & 0 deletions public/app.3d13b0df.js

Large diffs are not rendered by default.

File renamed without changes.
4 changes: 2 additions & 2 deletions public/entrypoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"entrypoints": {
"app": {
"css": [
"/app.5892cfa7.css"
"/app.066a0e62.css"
],
"js": [
"/app.302b82dd.js"
"/app.3d13b0df.js"
]
},
"form": {
Expand Down
Loading
Loading