-
-
Notifications
You must be signed in to change notification settings - Fork 3
Feat/shared dhtml toolbar #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,193 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Self-contained styling for the shared XOOPS DHTML editor toolbar (XoopsDhtmlToolbar). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Framework-neutral: every rule targets the `xo-edtb-*` prefix only, so this renders the same | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * whether or not Bootstrap/Tailwind/DaisyUI is loaded — the admin themes load no CSS framework. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Every colour, radius, spacing and size is a CSS custom property declared on the toolbar root, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * so a theme can restyle the toolbar by overriding variables instead of rewriting rules. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * You may not change or alter any portion of this comment or credits of supporting developers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * from this source code or any supporting source code which is considered copyrighted (c) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * material of the original comment or credit authors. This program is distributed in the hope | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @copyright 2000-2026 XOOPS Project (https://xoops.org) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-toolbar { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-bg: #f8f9fa; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-border: #ced4da; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-radius: 4px; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-gap: 4px; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-padding: 4px; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-font-size: 0.8125rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-size: 1.75rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-bg: #ffffff; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-fg: #212529; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-border: #ced4da; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-hover-bg: #e9ecef; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-active-bg: #dee2e6; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-menu-bg: #ffffff; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-menu-fg: #212529; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-menu-border: #ced4da; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-menu-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: flex; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| flex-wrap: wrap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| align-items: center; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gap: var(--xo-edtb-gap); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: var(--xo-edtb-padding); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| background: var(--xo-edtb-bg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border: 1px solid var(--xo-edtb-border); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border-radius: var(--xo-edtb-radius); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| font-size: var(--xo-edtb-font-size); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| line-height: 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-toolbar, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-toolbar * { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| box-sizing: border-box; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-group { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: flex; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| flex-wrap: wrap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| align-items: center; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gap: var(--xo-edtb-gap); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-btn { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: inline-flex; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| align-items: center; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| justify-content: center; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gap: 0.25rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| min-width: var(--xo-edtb-btn-size); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: var(--xo-edtb-btn-size); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: 0 0.4rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| background: var(--xo-edtb-btn-bg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| color: var(--xo-edtb-btn-fg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border: 1px solid var(--xo-edtb-btn-border); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border-radius: var(--xo-edtb-radius); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| font-size: inherit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| line-height: 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cursor: pointer; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user-select: none; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-btn:hover { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| background: var(--xo-edtb-btn-hover-bg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-btn:active { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| background: var(--xo-edtb-btn-active-bg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-btn-sm { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: 0 0.3rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+81
to
+91
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win Add a visible focus indicator for the buttons and menu items. The file documents that the admin themes ship a universal reset. Those resets commonly drop Add an explicit ♿ Proposed fix .xo-edtb-btn:active {
background: var(--xo-edtb-btn-active-bg);
}
+
+.xo-edtb-btn:focus-visible,
+.xo-edtb-menu-item:focus-visible {
+ outline: 2px solid currentColor;
+ outline-offset: 1px;
+}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* Dropdowns are native <details>/<summary> — no JS required for the open/close interaction. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-dropdown { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: relative; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-dropdown > summary.xo-edtb-btn { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| list-style: none; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-dropdown > summary::-webkit-details-marker { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: none; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-dropdown > summary::marker { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-menu { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: absolute; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| z-index: 1000; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| top: 100%; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| left: 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| margin: 2px 0 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: 4px 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| min-width: 9rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| max-height: 16rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| overflow-y: auto; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| list-style: none; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| background: var(--xo-edtb-menu-bg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| color: var(--xo-edtb-menu-fg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border: 1px solid var(--xo-edtb-menu-border); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border-radius: var(--xo-edtb-radius); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| box-shadow: var(--xo-edtb-menu-shadow); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Restore the resize grip on the editor's textarea. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * The "default" and "transition" ADMIN themes ship a universal reset — | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * `* { … resize:none; }` (modules/system/themes/{default,transition}/css/reset.css:13) — which | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * strips the grip from every element on the page, so the control panel's editor could not be | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * resized while the front end's could. The textarea is rendered as a sibling immediately after | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * the toolbar, so this sibling selector reaches it without needing a class on the element (which | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * would mean touching all five renderers). Specificity (0,1,1) comfortably beats `*` (0,0,0). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-toolbar ~ textarea { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resize: vertical; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Reset the list markers on the ITEMS, not just the list. The admin "default" theme ships a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * bare `li { list-style: square inside; }` (modules/system/themes/default/css/style.css:59-62), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * which applies directly to the <li> and therefore beats a `list-style: none` set only on the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * parent <ul>. That is why square bullets appeared in the control panel but not on the front | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * end. `.xo-edtb-menu > li` (0,1,1) outranks a bare `li` (0,0,1) without needing !important. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-menu > li { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| list-style: none; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| margin: 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-menu-item { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: flex; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| align-items: center; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gap: 0.4rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: 0.25rem 0.6rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| color: inherit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text-decoration: none; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| white-space: nowrap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-menu-item:hover { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| background: var(--xo-edtb-btn-hover-bg); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-swatch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: inline-block; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: 0.9rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: 0.9rem; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border: 1px solid var(--xo-edtb-menu-border); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border-radius: 2px; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @media (prefers-color-scheme: dark) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .xo-edtb-toolbar { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-bg: #2b2f33; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-border: #495057; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-bg: #343a40; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-fg: #e9ecef; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-border: #495057; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-hover-bg: #495057; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-btn-active-bg: #5a6268; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-menu-bg: #343a40; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-menu-fg: #e9ecef; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-menu-border: #495057; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --xo-edtb-menu-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,71 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Shared DHTML editor toolbar behaviour. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * The dropdowns are native <details>/<summary>, and each toolbar's set shares a name= so modern | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * browsers make them mutually exclusive on their own. This file supplies the rest: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 1. the same exclusivity for browsers that predate the name= "exclusive accordion" attribute | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * (name= on <details> is only supported in newer engines); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 2. closing the open menu when the user clicks anywhere outside it; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 3. closing it on Escape, and returning focus to the toggle. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Everything is delegated from document, so toolbars added to the page later (AJAX forms, a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * second editor) are handled without re-initialising anything. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'use strict'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (window.xoopsEditorToolbarInit) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.xoopsEditorToolbarInit = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var DROPDOWN = 'details.xo-edtb-dropdown'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Close every open dropdown except the one passed in. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {Element|null} keepOpen dropdown to leave alone | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {Element|null} scope toolbar to limit the sweep to, or null for the document | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function closeOthers(keepOpen, scope) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var root = scope || document; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var open = root.querySelectorAll(DROPDOWN + '[open]'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (var i = 0; i < open.length; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (open[i] !== keepOpen) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| open[i].removeAttribute('open'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1 + 2: opening one closes the others. 'toggle' fires on open AND close, so only act on open. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document.addEventListener('toggle', function (event) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var details = event.target; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!details || !details.matches || !details.matches(DROPDOWN) || !details.hasAttribute('open')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Limit to the owning toolbar so a second editor on the page is unaffected. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| closeOthers(details, details.closest('.xo-edtb-toolbar')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, true); // capture: 'toggle' does not bubble | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2: click anywhere outside an open dropdown closes it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document.addEventListener('click', function (event) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var inside = event.target.closest ? event.target.closest(DROPDOWN) : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| closeOthers(inside, null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 3: Escape closes the open dropdown and puts focus back on its toggle. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| document.addEventListener('keydown', function (event) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (event.key !== 'Escape' && event.keyCode !== 27) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var open = document.querySelectorAll(DROPDOWN + '[open]'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (var i = 0; i < open.length; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var summary = open[i].querySelector('summary'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| open[i].removeAttribute('open'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (summary && typeof summary.focus === 'function') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| summary.focus(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+58
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Escape moves focus even when the user is typing elsewhere. The handler closes every open dropdown and calls
Restrict the focus restore to the dropdown that currently contains focus. 🐛 Proposed fix var open = document.querySelectorAll(DROPDOWN + '[open]');
+ var active = document.activeElement;
for (var i = 0; i < open.length; i++) {
var summary = open[i].querySelector('summary');
+ var hadFocus = !!(active && open[i].contains(active));
open[i].removeAttribute('open');
- if (summary && typeof summary.focus === 'function') {
+ if (hadFocus && summary && typeof summary.focus === 'function') {
summary.focus();
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Declare the custom properties outside the toolbar wrapper too.
Every variable is declared on
.xo-edtb-toolbar. The buttons resolve them by inheritance, which works forrender(). It does not work for the compatibility path this PR keeps alive:renderFormDhtmlTAXoopsCode()andrenderFormDhtmlTATypography()return.xo-edtb-grouprows with no.xo-edtb-toolbarancestor. A subclass that calls those helpers directly gets unstyled buttons, becausemin-width,height,backgroundandborderall resolve against undefined variables.Declare the defaults on
:root(or on both.xo-edtb-toolbarand.xo-edtb-group) and keep the toolbar block for overrides.♻️ Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents