Skip to content
Merged
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
6 changes: 3 additions & 3 deletions packages/master-detail-layout/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ The `>=` (not `>`) is intentional: when `keep-detail-column-offscreen` or `:not(

Layout detection is split into two methods to avoid forced reflows:

- **`__computeLayoutState()`** — pure reads: `checkVisibility()`, `getComputedStyle()`, `getFocusableElements()`. Called in the ResizeObserver callback where layout is already computed — no forced reflow.
- **`__applyLayoutState(state)`** — pure writes: toggles `has-detail`, `overlay`, `keep-detail-column-offscreen`; calls `requestUpdate()` for ARIA; focuses detail. No DOM/style reads.
- **`__readLayoutState()`** — pure reads: `checkVisibility()`, `getComputedStyle()`, `getFocusableElements()`. Called in the ResizeObserver callback where layout is already computed — no forced reflow.
- **`__writeLayoutState(state)`** — pure writes: toggles `has-detail`, `overlay`, `keep-detail-column-offscreen`; calls `requestUpdate()` for ARIA; focuses detail. No DOM/style reads.

### ResizeObserver

- **Observes**: host + shadow DOM parts (`master`, `detail`) + direct slotted children (`:scope >` prevents observing nested descendants)
- ResizeObserver callback: calls `__computeLayoutState()` (read), cancels any pending rAF via `cancelAnimationFrame`, then defers `__applyLayoutState()` (write) via `requestAnimationFrame`. Cancelling ensures the write phase always uses the latest state when multiple callbacks fire per frame.
- ResizeObserver callback: calls `__readLayoutState()` (read), cancels any pending rAF via `cancelAnimationFrame`, then defers `__writeLayoutState()` (write) via `requestAnimationFrame`. Cancelling ensures the write phase always uses the latest state when multiple callbacks fire per frame.
- **Property observers** (`masterSize`/`detailSize`) only update CSS custom properties — ResizeObserver picks up the resulting size changes automatically

## Overlay Modes
Expand Down
16 changes: 8 additions & 8 deletions packages/master-detail-layout/src/vaadin-master-detail-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,17 +297,17 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
* @private
*/
__onResize() {
const state = this.__computeLayoutState();
const state = this.__readLayoutState();
cancelAnimationFrame(this.__resizeRaf);
this.__resizeRaf = requestAnimationFrame(() => this.__applyLayoutState(state));
this.__resizeRaf = requestAnimationFrame(() => this.__writeLayoutState(state));
}

/**
* Reads DOM/style state needed for layout detection. Safe to call in
* ResizeObserver callback where layout is already computed (no forced reflow).
* @private
*/
__computeLayoutState() {
__readLayoutState() {
const detailContent = this.querySelector(':scope > [slot="detail"]');
const detailPlaceholder = this.querySelector(':scope > [slot="detail-placeholder"]');

Expand All @@ -324,7 +324,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
* Applies layout state to DOM attributes. Pure writes, no reads.
* @private
*/
__applyLayoutState({ hadDetail, hasDetail, hasDetailPlaceholder, hasOverflow, focusTarget }) {
__writeLayoutState({ hadDetail, hasDetail, hasDetailPlaceholder, hasOverflow, focusTarget }) {
// Set keep-detail-column-offscreen when detail first appears with overlay
// to prevent master width from jumping.
if (!hadDetail && hasDetail && hasOverflow) {
Expand Down Expand Up @@ -414,8 +414,8 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
if (skipTransition || this.noAnimation) {
updateSlot();
queueMicrotask(() => {
const state = this.__computeLayoutState();
this.__applyLayoutState(state);
const state = this.__readLayoutState();
this.__writeLayoutState(state);
});
return Promise.resolve();
}
Expand Down Expand Up @@ -561,8 +561,8 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
* @protected
*/
_finishTransition() {
const state = this.__computeLayoutState();
this.__applyLayoutState(state);
const state = this.__readLayoutState();
this.__writeLayoutState(state);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('vaadin-master-detail-layout', () => {
let onResizeSpy;

beforeEach(() => {
onResizeSpy = sinon.spy(layout, '__applyLayoutState');
onResizeSpy = sinon.spy(layout, '__writeLayoutState');
});

it('should trigger observer when layout is resized', async () => {
Expand Down
Loading