Skip to content

fix: detached glass move/resize/activate work without a mounted window#85

Merged
ohxyz merged 15 commits into
mainfrom
feat/detached-glass-enhancement
Jun 17, 2026
Merged

fix: detached glass move/resize/activate work without a mounted window#85
ohxyz merged 15 commits into
mainfrom
feat/detached-glass-enhancement

Conversation

@ohxyz

@ohxyz ohxyz commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Makes detached-glass move / resize / activate work even when no BinaryWindow is mounted — which is the case for windowless glasses created via the static addWindowlessGlass. (The windowless-glass and modal features themselves are already on main; this is the follow-up that makes them fully interactive standalone.)

The bug

move / resize / activate attach document-global, instance-independent listeners (they locate their target via closest('bw-glass[detached]') and never read this). But they were only installed by an instance's enableFeatures() during mount(). A page that only calls the static addWindowlessGlass never mounts an instance, so those listeners were never attached and the glass couldn't be moved/resized/focused. It only appeared to work on pages that happened to also mount a BinaryWindow.

The fix

  • Split the document-global listeners into enableDetachedGlassStandaloneFeatures(), invoked once at module load in binary-window.js (module evaluation is one-time, so no idempotency flag needed). Importing BinaryWindow is now enough to wire move/resize/activate.
  • enableDetachedGlassFeatures() (per-instance) keeps only the sill-bound restore handler, which genuinely needs this.sillElement.
  • Remove the long-disabled native-DnD detached-glass/drag.js in favor of free-floating move.js.

Renames (clarity)

  • enableGlassFeatureenableGlassFeatures
  • restore handler → enableRestoreFromMinimizedDetachedGlass

Dev page & docs

  • New bwin-detached-windowless-glass dev page exercising windowless move/resize/activate with no mounted window.
  • ARCHITECTURE.md §8.5 documents the standalone-vs-per-instance split; §8.6 notes windowless interactions work without a mounted window. Resolve the now-fixed drag.js entry in TECH_DEBT.md.

ohxyz added 14 commits June 16, 2026 13:26
…rollbars

Dragging a detached glass by its header could push it past the viewport
edge and grow the page, surfacing browser scrollbars. Clamp the move to
the viewport (clientWidth/Height, which exclude scrollbars), reserving the
resize-handle overhang on the right/bottom so hover handles stay on-screen.

Extract clamp() to utils and getResizeHandleOverhang() to detached-glass/utils.
Derive the containing block from the glass via offsetParent and bind
pointer listeners to document instead of windowElement. getResizeHandleOverhang
now reads the inherited handle-size var from the glass element itself.
addFreeGlass is a static BinaryWindow method that builds a detached glass and
appends it to document.body instead of a bw-window, managed by the shared glass
manager and tagged with a 'free' attribute. Free glasses default to close-only
actions since minimize/attach need an owning window.

Move math now resolves the containing-block origin via getContainingBlockOrigin:
the positioned bw-window for a detached glass, or the scroll-shifted viewport
origin for a free glass on a static body. CSS vars move to :root so a free glass
outside any bw-window still inherits them.
Move the detached-glass resize and activate pointer listeners from
windowElement to document so they also fire for a free glass living on
document.body, and normalize resize start geometry via getContainingBlockOrigin
(positioned bw-window for detached, scroll-shifted viewport for free).

bringToFront now clears the [active] marker across all managed glasses rather
than only :scope siblings, so a detached and a free glass (different parents)
can't both look active at once.

Dev page: add a fullscreen-popup button exercising a non-draggable free glass
inset 20px from every viewport edge.
"Free" described a property a detached glass already has (it floats and
moves freely within the window). The distinguishing trait is that this glass
has no owning bw-window, so name it for that: addWindowlessGlass,
DEFAULT_WINDOWLESS_GLASS_ACTIONS, and the bw-glass[windowless] attribute.
Split bw-window[theme='dark'] out of vars.css so vars.css holds only the
:root token defaults. Import theme.css last so its overrides win on source order.
Match bw-glass[windowless][theme='dark'] so a windowless glass on the page
body picks up dark tokens. TODO left to differentiate window vs windowless settings.
…glass button

Rename the dark-theme example to the bwin-* convention, add an 'Add windowless
glass' button, and have the index theme toggle also flip bw-glass[windowless].
Add a `modal` option to addWindowlessGlass that appends a
<bw-glass-backdrop for="<glassId>"> behind the glass to block
interaction underneath. The backdrop sits one z-index below its glass,
using the slot the manager's topZIndex reservation leaves free.

Tear down the backdrop in both removeWindowlessGlass and the detached
close action via a shared removeGlassBackdrop helper. Expand the
addWindowlessGlass JSDoc to enumerate all options.
Detached glass move/resize/activate attach document-global, instance-
independent listeners, but were only installed by an instance's
enableFeatures(). The static addWindowlessGlass() path never mounts an
instance, so windowless glasses on a window-less page weren't movable.

Split those listeners into enableDetachedGlassStandaloneFeatures(),
invoked once at module load, leaving only the sill-bound restore handler
per-instance. Drop the disabled native-DnD drag.js. Rename
enableGlassFeature -> enableGlassFeatures and the restore handler to
enableRestoreFromMinimizedDetachedGlass for clarity. Add a dedicated
bwin-detached-windowless-glass dev page.
Update ARCHITECTURE §8.5/§8.6 for the standalone/per-instance split:
activate/move/resize are document-global and installed once at module
load (enableDetachedGlassStandaloneFeatures), which is what lets the
static addWindowlessGlass work with no mounted window; restore stays
per-instance. Drop the deleted drag.js from the file table and remove
the now-resolved disabled-alternate-path entry from TECH_DEBT.
@ohxyz ohxyz changed the title feat/detached glass enhancement feat: windowless glass (floating on document.body) + modal option, with detached-glass move/resize hardening Jun 17, 2026
@ohxyz ohxyz changed the title feat: windowless glass (floating on document.body) + modal option, with detached-glass move/resize hardening fix: detached glass move/resize/activate work without a mounted window Jun 17, 2026
@ohxyz ohxyz merged commit 076abdc into main Jun 17, 2026
@ohxyz ohxyz deleted the feat/detached-glass-enhancement branch June 17, 2026 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant