@vaadin/master-detail-layout 25.2.0-alpha4 → 25.2.0-alpha5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/master-detail-layout",
3
- "version": "25.2.0-alpha4",
3
+ "version": "25.2.0-alpha5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -34,16 +34,16 @@
34
34
  "web-component"
35
35
  ],
36
36
  "dependencies": {
37
- "@vaadin/a11y-base": "25.2.0-alpha4",
38
- "@vaadin/component-base": "25.2.0-alpha4",
39
- "@vaadin/vaadin-themable-mixin": "25.2.0-alpha4",
37
+ "@vaadin/a11y-base": "25.2.0-alpha5",
38
+ "@vaadin/component-base": "25.2.0-alpha5",
39
+ "@vaadin/vaadin-themable-mixin": "25.2.0-alpha5",
40
40
  "lit": "^3.0.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@vaadin/aura": "25.2.0-alpha4",
44
- "@vaadin/chai-plugins": "25.2.0-alpha4",
43
+ "@vaadin/aura": "25.2.0-alpha5",
44
+ "@vaadin/chai-plugins": "25.2.0-alpha5",
45
45
  "@vaadin/testing-helpers": "^2.0.0",
46
- "@vaadin/vaadin-lumo-styles": "25.2.0-alpha4",
46
+ "@vaadin/vaadin-lumo-styles": "25.2.0-alpha5",
47
47
  "sinon": "^21.0.2"
48
48
  },
49
49
  "customElements": "custom-elements.json",
@@ -51,5 +51,5 @@
51
51
  "web-types.json",
52
52
  "web-types.lit.json"
53
53
  ],
54
- "gitHead": "fc054a1bed540874ef3b5000828c191cc12044ec"
54
+ "gitHead": "2f0c822a389571591b1b9d2c27d45e008ccbae6b"
55
55
  }
@@ -56,11 +56,14 @@ export const masterDetailLayoutStyles = css`
56
56
  }
57
57
 
58
58
  #detail-placeholder {
59
- visibility: hidden;
59
+ z-index: 1;
60
+ opacity: 0;
61
+ pointer-events: none;
60
62
  }
61
63
 
62
64
  :host([has-detail-placeholder]:not([has-detail], [overlay])) #detail-placeholder {
63
- visibility: visible;
65
+ opacity: 1;
66
+ pointer-events: auto;
64
67
  }
65
68
 
66
69
  #master {
@@ -86,7 +89,7 @@ export const masterDetailLayoutStyles = css`
86
89
  #backdrop {
87
90
  position: absolute;
88
91
  inset: 0;
89
- z-index: 1;
92
+ z-index: 2;
90
93
  opacity: 0;
91
94
  pointer-events: none;
92
95
  background: var(--vaadin-overlay-backdrop-background, rgba(0, 0, 0, 0.2));
@@ -125,19 +128,21 @@ export const masterDetailLayoutStyles = css`
125
128
  var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color-secondary));
126
129
  }
127
130
 
131
+ #outgoing {
132
+ position: absolute;
133
+ z-index: 3;
134
+ }
135
+
128
136
  /* Detail transition: off-screen by default, on-screen when has-detail */
129
137
  #detail {
130
138
  translate: var(--_detail-offscreen);
131
- visibility: hidden;
139
+ opacity: 0;
140
+ z-index: 4;
132
141
  }
133
142
 
134
143
  :host([has-detail]) #detail {
135
144
  translate: none;
136
- visibility: visible;
137
- }
138
-
139
- #outgoing:not([hidden]) {
140
- z-index: 1;
145
+ opacity: 1;
141
146
  }
142
147
 
143
148
  :host([overlay]) {
@@ -150,7 +155,6 @@ export const masterDetailLayoutStyles = css`
150
155
 
151
156
  :host([has-detail][overlay]) :is(#detail, #outgoing) {
152
157
  position: absolute;
153
- z-index: 2;
154
158
  background: var(--vaadin-master-detail-layout-detail-background, var(--vaadin-background-color));
155
159
  box-shadow: var(--vaadin-master-detail-layout-detail-shadow, 0 0 20px 0 rgba(0, 0, 0, 0.3));
156
160
  grid-column: none;
@@ -572,23 +572,25 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
572
572
  * Starts an animated transition for adding, replacing or removing the
573
573
  * detail area using the Web Animations API.
574
574
  *
575
- * For 'remove', the DOM update is deferred until the slide-out completes.
576
- * For 'add'/'replace', the DOM is updated immediately and the slide-in
577
- * plays on the new content.
575
+ * For 'add'/'replace': DOM is updated immediately, then animation
576
+ * starts after a microtask (so Lit elements render and layout is
577
+ * recalculated before animation params are read).
578
578
  *
579
- * Animations are interruptible: starting a new transition cancels any
580
- * in-progress animation and the new animation picks up from the
581
- * interrupted position (see `__captureDetailState`).
579
+ * For 'remove': animation plays first, then DOM is updated after
580
+ * the slide-out completes.
581
+ *
582
+ * Interruptible: a new transition cancels any in-progress animation
583
+ * and picks up from the interrupted position.
582
584
  *
583
585
  * @param transitionType
584
586
  * @param updateCallback
585
587
  * @return {Promise<void>}
586
588
  * @protected
587
589
  */
588
- _startTransition(transitionType, updateCallback) {
590
+ async _startTransition(transitionType, updateCallback) {
589
591
  if (this.noAnimation) {
590
592
  updateCallback();
591
- return Promise.resolve();
593
+ return;
592
594
  }
593
595
 
594
596
  // Capture mid-flight state before cancelling active animations
@@ -602,66 +604,67 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
602
604
 
603
605
  this.setAttribute('transition', transitionType);
604
606
 
607
+ const version = (this.__transitionVersion = (this.__transitionVersion || 0) + 1);
608
+
605
609
  if (transitionType !== 'remove') {
610
+ // Add/Replace: update DOM, wait for Lit rendering + recalculateLayout
606
611
  updateCallback();
612
+ await Promise.resolve();
613
+ if (this.__transitionVersion !== version) return;
607
614
  }
608
615
 
609
616
  const opts = this.__getAnimationParams();
610
617
  opts.interrupted = interrupted;
611
618
  opts.overlay = this.hasAttribute('overlay');
612
619
 
613
- return this.__animateTransition(transitionType, opts, updateCallback);
620
+ // Run animations and wait for the detail slide to finish
621
+ await this.__runAnimations(transitionType, opts);
622
+ if (this.__transitionVersion !== version) return;
623
+
624
+ if (transitionType === 'remove') {
625
+ // Remove: deferred DOM update after slide-out completes
626
+ updateCallback();
627
+ await Promise.resolve();
628
+ }
629
+
630
+ this.__endTransition();
614
631
  }
615
632
 
616
633
  /**
617
- * Creates slide animation(s) for the given transition type and returns
618
- * a promise that resolves when the primary animation completes.
619
- * A version counter prevents stale callbacks from executing after
620
- * a newer transition has started.
634
+ * Starts slide animation(s) for the given transition type and returns
635
+ * a promise that resolves when the detail slide completes.
621
636
  *
622
637
  * @param {string} transitionType
623
638
  * @param {{ offscreen: string, duration: number, easing: string, interrupted?: { translate: string, opacity: string }, overlay?: boolean }} opts
624
- * @param {Function} updateCallback
625
639
  * @return {Promise<void>}
626
640
  * @private
627
641
  */
628
- __animateTransition(transitionType, opts, updateCallback) {
629
- const version = (this.__transitionVersion = (this.__transitionVersion || 0) + 1);
630
-
631
- return new Promise((resolve) => {
632
- this.__transitionResolve = resolve;
633
-
634
- const onFinish = (callback) => {
635
- if (this.__transitionVersion === version) {
636
- if (callback) {
637
- callback();
638
- }
639
- this.__endTransition();
640
- }
641
- };
642
-
643
- if (transitionType === 'remove') {
644
- this.__slide(this.$.detail, false, opts).then(() => onFinish(updateCallback));
645
- } else if (transitionType === 'replace') {
646
- // Outgoing slides out on top (z-index), revealing incoming underneath.
647
- // In overlay mode, the incoming also slides in simultaneously.
648
- this.__slide(this.$.outgoing, false, opts).then(() => onFinish());
649
- if (opts.overlay) {
650
- this.__slide(this.$.detail, true, { ...opts, interrupted: null });
651
- }
652
- } else {
653
- this.__slide(this.$.detail, true, opts).then(() => onFinish());
642
+ __runAnimations(transitionType, opts) {
643
+ let slide;
644
+
645
+ if (transitionType === 'remove') {
646
+ slide = this.__slide(this.$.detail, false, opts);
647
+ } else if (transitionType === 'replace') {
648
+ // Outgoing slides out while incoming is revealed underneath.
649
+ // In overlay mode, the incoming also slides in simultaneously.
650
+ slide = this.__slide(this.$.outgoing, false, opts);
651
+ if (opts.overlay) {
652
+ this.__slide(this.$.detail, true, { ...opts, interrupted: null });
654
653
  }
654
+ } else {
655
+ slide = this.__slide(this.$.detail, true, opts);
656
+ }
655
657
 
656
- // Fade backdrop in/out for overlay add/remove (not replace — backdrop stays visible)
657
- if (opts.overlay && transitionType !== 'replace') {
658
- const fadeIn = transitionType !== 'remove';
659
- this.__animate(this.$.backdrop, [{ opacity: fadeIn ? 0 : 1 }, { opacity: fadeIn ? 1 : 0 }], {
660
- duration: opts.duration,
661
- easing: 'linear',
662
- });
663
- }
664
- });
658
+ // Fade backdrop in/out for overlay add/remove (not replace — backdrop stays visible)
659
+ if (opts.overlay && transitionType !== 'replace') {
660
+ const fadeIn = transitionType !== 'remove';
661
+ this.__animate(this.$.backdrop, [{ opacity: fadeIn ? 0 : 1 }, { opacity: fadeIn ? 1 : 0 }], {
662
+ duration: opts.duration,
663
+ easing: 'linear',
664
+ });
665
+ }
666
+
667
+ return slide;
665
668
  }
666
669
 
667
670
  /**
@@ -768,8 +771,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
768
771
  }
769
772
 
770
773
  /**
771
- * Cancels in-progress animations, cleans up state, and resolves the
772
- * pending transition promise.
774
+ * Cancels in-progress animations and cleans up transition state.
773
775
  * @private
774
776
  */
775
777
  __endTransition() {
@@ -779,10 +781,6 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
779
781
  }
780
782
  this.removeAttribute('transition');
781
783
  this.__clearOutgoing();
782
- if (this.__transitionResolve) {
783
- this.__transitionResolve();
784
- this.__transitionResolve = null;
785
- }
786
784
  }
787
785
 
788
786
  /**
@@ -797,6 +795,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
797
795
  return;
798
796
  }
799
797
  currentDetail.setAttribute('slot', 'detail-outgoing');
798
+ this.$.outgoing.style.width = this.__detailCachedSize;
800
799
  this.__replacing = true;
801
800
  }
802
801
 
@@ -806,6 +805,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
806
805
  */
807
806
  __clearOutgoing() {
808
807
  this.querySelectorAll('[slot="detail-outgoing"]').forEach((el) => el.remove());
808
+ this.$.outgoing.style.width = '';
809
809
  this.__replacing = false;
810
810
  }
811
811
 
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/master-detail-layout",
4
- "version": "25.2.0-alpha4",
4
+ "version": "25.2.0-alpha5",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/master-detail-layout",
4
- "version": "25.2.0-alpha4",
4
+ "version": "25.2.0-alpha5",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {