diff --git a/assets/css/basic.css b/assets/css/basic.css index 1a3f5c18..2c69d35c 100644 --- a/assets/css/basic.css +++ b/assets/css/basic.css @@ -283,6 +283,68 @@ input:disabled + label { color: var(--val-color--text--muted); } +/* + * Dialog component + */ + +.dialog { + padding: 0; + width: 100%; + max-width: 32rem; + color: var(--val-color--text); + background-color: var(--val-color--bg); + border: none; + border-radius: 0.5rem; + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); +} +.dialog::backdrop { + background-color: rgba(0, 0, 0, 0.5); +} + +.dialog-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 1rem 1.25rem; + border-bottom: 1px solid color-mix(in srgb, var(--val-color--border) 15%, var(--val-color--bg)); +} + +.dialog-title { + margin: 0; + font-size: 1.125rem; + font-weight: 600; +} + +.dialog-close { + width: 1.5rem; + height: 1.5rem; + padding: 0; + line-height: 1; + color: var(--val-color--text--muted); + background-color: transparent; + border: none; + cursor: pointer; +} +.dialog-close::before { + content: "\2715"; +} +.dialog-close:hover { + color: var(--val-color--text); +} + +.dialog-body { + padding: 1.25rem; +} + +.dialog-footer { + display: flex; + justify-content: flex-end; + gap: 0.5rem; + padding: 1rem 1.25rem; + border-top: 1px solid color-mix(in srgb, var(--val-color--border) 15%, var(--val-color--bg)); +} + /* * Dropdown component */ @@ -303,18 +365,14 @@ input:disabled + label { cursor: pointer; } .dropdown-toggle::after { - content: ""; + content: "\25BC"; display: inline-block; - width: 0.4em; - height: 0.4em; margin-left: 0.35em; - border-right: 0.125em solid currentColor; - border-bottom: 0.125em solid currentColor; - transform: rotate(45deg); + font-size: 0.8em; transition: transform .15s ease-in-out; } .dropdown:has(> .dropdown-menu.show) > .dropdown-toggle::after { - transform: rotate(-135deg); + transform: rotate(180deg); } .dropdown-menu { @@ -330,10 +388,12 @@ input:disabled + label { border: 1px solid color-mix(in srgb, var(--val-color--border) 15%, var(--val-color--bg)); border-radius: 0.375rem; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); -} -.dropdown-menu.hide { + /* Closed by default */ display: none; } +.dropdown-menu.show { + display: block; +} .dropdown-item { display: block; diff --git a/assets/js/basic.dialog.init.js b/assets/js/basic.dialog.init.js new file mode 100644 index 00000000..e40780b9 --- /dev/null +++ b/assets/js/basic.dialog.init.js @@ -0,0 +1,45 @@ +// Abre y cierra los componentes `Dialog` de Basic (elemento `` nativo) interpretando el +// vocabulario de atributos `data-dialog-*`, común a todos los temas: Bootsier traduce estos mismos +// nombres a los suyos propios (`data-bs-*`) en `setup()` para que el JS de Bootstrap los reconozca, +// así el marcado de un disparador o de un botón de cierre es idéntico en los dos temas. Sin +// dependencias: ni htmx, ni Bootstrap, ni ninguna otra librería. + +// Abre el diálogo referenciado por `data-dialog-target` al pulsar un elemento con +// `data-dialog-toggle="modal"`. +document.addEventListener("click", function (event) { + var toggle = event.target.closest('[data-dialog-toggle="modal"]'); + if (!toggle) { + return; + } + var target = toggle.getAttribute("data-dialog-target"); + var dialog = target && document.querySelector(target); + if (!(dialog instanceof HTMLDialogElement)) { + return; + } + event.preventDefault(); + dialog.showModal(); +}); + +// Cierra el `` ascendente más cercano al pulsar un elemento con +// `data-dialog-dismiss="modal"`. +document.addEventListener("click", function (event) { + var dismiss = event.target.closest('[data-dialog-dismiss="modal"]'); + if (!dismiss) { + return; + } + var dialog = dismiss.closest("dialog"); + if (dialog) { + event.preventDefault(); + dialog.close(); + } +}); + +// Cierra el diálogo al pulsar sobre el fondo (`::backdrop`): un clic ahí llega con el propio +// `` como `event.target`, porque el fondo no forma parte de su caja de contenido. +document.querySelectorAll("dialog.dialog").forEach(function (dialog) { + dialog.addEventListener("click", function (event) { + if (event.target === dialog) { + dialog.close(); + } + }); +}); diff --git a/assets/js/basic.dropdown.init.js b/assets/js/basic.dropdown.init.js new file mode 100644 index 00000000..2f593cea --- /dev/null +++ b/assets/js/basic.dropdown.init.js @@ -0,0 +1,66 @@ +// Conecta cada `Dropdown` (standalone o embebido en un `Nav`) con `accessible-menu` (ver +// basic.menu.min.js), que añade los roles ARIA, el `aria-expanded` y la navegación por teclado +// sobre el marcado ya generado en el servidor. Selecciona por clase, no por etiqueta, porque el +// disparador es un `