frontend: one component = one dir for hive-btn/hive-dialog/hive-toast
Splits the shadow-DOM custom elements out of the flat shared/src layout
into per-component directories:
hive-btn/hive-btn.{js,css}
hive-dialog/hive-dialog.{js,css}
hive-toast/hive-toast.{js,css}
hive-dialog and hive-toast were previously defined inline inside
modal.js alongside the openDialog/themedConfirm/themedPrompt/themedToast
orchestration helpers; modal.js is now a slim entry point that imports
the two component modules for their customElements.define side effect
and keeps only the orchestration functions, which aren't components
themselves. hive-dialog.js now imports hive-btn.js directly (it's the
actual consumer that creates <hive-btn> elements), instead of modal.js
importing it on hive-dialog's behalf.
Pulled the identical shadow-root-plus-adopted-stylesheet boilerplate
(previously duplicated between modal.js's local attachShadow() and
hive-btn.js's inline version) into a shared shadow-css.js helper,
attachShadowCss(host, cssText, shadowInit), used by all three
components. Behaviorally identical — same attachShadow() options per
component, just deduplicated.
No external import paths changed: every consumer only ever imported
the package-level @hive/shared/modal.js entry point, never the
component internals directly, so this is fully internal to the shared
package. Verified with a full frontend build (dashboard + agent
bundles).
This commit is contained in:
parent
44651544a8
commit
edf1c5a17f
8 changed files with 172 additions and 169 deletions
46
frontend/packages/shared/src/hive-btn/hive-btn.css
Normal file
46
frontend/packages/shared/src/hive-btn/hive-btn.css
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* hive-btn.css — shadow-DOM stylesheet for the <hive-btn> autonomous
|
||||
custom element (hive-btn.js). Loaded as raw text at build time
|
||||
(esbuild's `text` loader) and adopted into each instance's own shadow
|
||||
root. `:host` is an invisible wrapper (`display: contents`) — the
|
||||
real box is the plain `button` selector below, targeting the actual
|
||||
`<button>` the shadow root wraps (scope-isolated by the shadow
|
||||
boundary already, no clash risk with anything outside). `variant` is
|
||||
an attribute on the HOST (how callers set it, and where hive-dialog's
|
||||
own selectors read it back); `:host([variant="…"])` sets `color`,
|
||||
which then inherits down into the shadow tree the normal way. */
|
||||
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
:host([variant="cancel"]) { color: var(--subtext0); }
|
||||
:host([variant="confirm"]) { color: var(--green); }
|
||||
:host([variant="danger"]) { color: var(--red); }
|
||||
|
||||
button {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
background: transparent;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
border: 1px solid;
|
||||
padding: 0.25em 0.8em;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
text-shadow: 0 0 4px currentColor;
|
||||
box-shadow: 0 0 0 0 currentColor;
|
||||
transition: box-shadow 0.15s ease;
|
||||
}
|
||||
button:hover {
|
||||
background: color-mix(in srgb, var(--fg) 6%, transparent);
|
||||
text-shadow: 0 0 10px currentColor;
|
||||
box-shadow: 0 0 10px -2px currentColor;
|
||||
}
|
||||
button:disabled {
|
||||
opacity: 0.32;
|
||||
cursor: not-allowed;
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
56
frontend/packages/shared/src/hive-btn/hive-btn.js
Normal file
56
frontend/packages/shared/src/hive-btn/hive-btn.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// hive-btn.js — <hive-btn>, the button primitive for modal.js's dialog
|
||||
// buttons. Autonomous custom element (previously a customized built-in,
|
||||
// `<button is="hive-btn">` — dropped: `is=` upgrades can't host a shadow
|
||||
// root at all, which crashed every themed dialog, and reads as a hack
|
||||
// regardless once you know that). A real `<button>` lives inside the
|
||||
// shadow root instead, so native click/keyboard activation and
|
||||
// `:disabled` still come for free — just one level down from the host.
|
||||
//
|
||||
// `delegatesFocus: true` on the shadow root means `.focus()` on the host
|
||||
// (what modal.js calls) focuses the inner button directly, and a click
|
||||
// anywhere on the host focuses it too, matching native `<button>` feel.
|
||||
// The inner button's native `click` is a composed event, so it bubbles
|
||||
// out through the shadow boundary — callers add `click` listeners on the
|
||||
// `<hive-btn>` host exactly as they would on a plain `<button>`.
|
||||
//
|
||||
// Usage: `el('hive-btn', { type: 'button', variant: 'danger' }, 'label')`.
|
||||
// `variant` ∈ 'cancel' | 'confirm' | 'danger' | unset (neutral default) —
|
||||
// reflected straight through to the inner button so hive-btn.css's
|
||||
// `:host([variant="…"])` selectors keep working unchanged.
|
||||
// `disabled`/`type` are likewise plain attributes on the host, mirrored
|
||||
// onto the inner button on connect and on every attribute change.
|
||||
|
||||
import { attachShadowCss } from '../shadow-css.js';
|
||||
import hiveBtnCss from './hive-btn.css';
|
||||
|
||||
const OBSERVED = ['disabled', 'type'];
|
||||
|
||||
class HiveBtn extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return OBSERVED;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this._btn) {
|
||||
this._sync();
|
||||
return; // already built (e.g. re-parenting re-fires connectedCallback)
|
||||
}
|
||||
const root = attachShadowCss(this, hiveBtnCss, { delegatesFocus: true });
|
||||
const btn = document.createElement('button');
|
||||
btn.append(document.createElement('slot'));
|
||||
root.append(btn);
|
||||
this._btn = btn;
|
||||
this._sync();
|
||||
}
|
||||
|
||||
attributeChangedCallback() {
|
||||
this._sync();
|
||||
}
|
||||
|
||||
_sync() {
|
||||
if (!this._btn) return;
|
||||
this._btn.disabled = this.hasAttribute('disabled');
|
||||
this._btn.type = this.getAttribute('type') || 'button';
|
||||
}
|
||||
}
|
||||
customElements.define('hive-btn', HiveBtn);
|
||||
Loading…
Reference in a new issue