From 35cfe0efdeb742fd00c4d3d55958ff5e07e11383 Mon Sep 17 00:00:00 2001 From: Jay George Date: Tue, 10 Mar 2026 20:24:03 +0000 Subject: [PATCH 1/6] Fix stack overlay flicker when multiple stacks are open by moving the overlay outside the container, and removing will-change to reduce composite layer thrashing --- resources/css/components/stacks.css | 12 ------------ resources/js/components/ui/Stack/Stack.vue | 13 +++++-------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/resources/css/components/stacks.css b/resources/css/components/stacks.css index c76fbd68d42..1a9ca64ead7 100644 --- a/resources/css/components/stacks.css +++ b/resources/css/components/stacks.css @@ -42,21 +42,9 @@ @apply relative h-[calc(100svh-1rem)]; } - .stack-overlay-fade-enter-active, - .stack-overlay-fade-leave-active { - transition: opacity 200ms ease-out; - will-change: opacity; - } - - .stack-overlay-fade-enter-from, - .stack-overlay-fade-leave-to { - opacity: 0; - } - .stack-slide-enter-active, .stack-slide-leave-active { transition: transform 200ms ease-out, opacity 200ms ease-out; - will-change: transform, opacity; } .stack-slide-enter-from { diff --git a/resources/js/components/ui/Stack/Stack.vue b/resources/js/components/ui/Stack/Stack.vue index c914c0f887a..f1f4d397ca3 100644 --- a/resources/js/components/ui/Stack/Stack.vue +++ b/resources/js/components/ui/Stack/Stack.vue @@ -202,6 +202,11 @@ provide('closeStack', close);
+
+ - -
- -
Date: Tue, 10 Mar 2026 20:24:24 +0000 Subject: [PATCH 2/6] add a direct @keydown.esc handler for more reliable escape key handling --- resources/js/components/ui/Stack/Stack.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/js/components/ui/Stack/Stack.vue b/resources/js/components/ui/Stack/Stack.vue index f1f4d397ca3..8b8df8d9259 100644 --- a/resources/js/components/ui/Stack/Stack.vue +++ b/resources/js/components/ui/Stack/Stack.vue @@ -213,6 +213,7 @@ provide('closeStack', close); class="stack-container outline-none" :class="{ 'stack-is-current': isTopStack }" :style="direction === 'ltr' ? { left: `${leftOffset}px` } : { right: `${leftOffset}px` }" + @keydown.esc="isTopStack && runCloseCallback()" >
Date: Tue, 10 Mar 2026 20:41:45 +0000 Subject: [PATCH 3/6] Improve Safari animation - Animate stack container with transform so animation stays on the compositor. --- resources/css/components/stacks.css | 13 +++++++------ resources/js/components/ui/Stack/Stack.vue | 14 +++++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/resources/css/components/stacks.css b/resources/css/components/stacks.css index 1a9ca64ead7..219870e6ef2 100644 --- a/resources/css/components/stacks.css +++ b/resources/css/components/stacks.css @@ -23,11 +23,8 @@ } .stack-container { @apply absolute inset-0; - transition: left 200ms ease-out; - - [dir='rtl'] & { - transition: right 200ms ease-out; - } + transition: transform 200ms ease-out; + -webkit-backface-visibility: hidden; } .stack-overlay { @@ -69,10 +66,14 @@ @media all and (max-width: 980px) { .stacks-on-stacks .stack-container { left: 0 !important; + right: 0 !important; + width: 100% !important; + transform: translateX(0) !important; [dir='rtl'] & { - left: unset !important; + left: 0 !important; right: 0 !important; + transform: translateX(0) !important; } } } diff --git a/resources/js/components/ui/Stack/Stack.vue b/resources/js/components/ui/Stack/Stack.vue index 8b8df8d9259..bc4560a82bf 100644 --- a/resources/js/components/ui/Stack/Stack.vue +++ b/resources/js/components/ui/Stack/Stack.vue @@ -91,6 +91,18 @@ const leftOffset = computed(() => { const hasChild = computed(() => stacks.count() > depth.value); const direction = computed(() => config.get('direction', 'ltr')); +const containerStyle = computed(() => { + if (props.size === 'full') { + return direction.value === 'ltr' ? { left: 0, transform: 'translateZ(0)' } : { right: 0, transform: 'translateZ(0)' }; + } + const x = leftOffset.value; + const width = `calc(100% - ${x}px)`; + if (direction.value === 'ltr') { + return { left: 0, width, transform: `translateX(${x}px) translateZ(0)` }; + } + return { right: 0, width, transform: `translateX(-${x}px) translateZ(0)` }; +}); + const clickedHitArea = () => { if (!visible.value) return; if (!runCloseCallback()) return; @@ -212,7 +224,7 @@ provide('closeStack', close); loop class="stack-container outline-none" :class="{ 'stack-is-current': isTopStack }" - :style="direction === 'ltr' ? { left: `${leftOffset}px` } : { right: `${leftOffset}px` }" + :style="containerStyle" @keydown.esc="isTopStack && runCloseCallback()" >
Date: Tue, 10 Mar 2026 21:31:42 +0000 Subject: [PATCH 4/6] =?UTF-8?q?Do=20no=20animate=20the=20stack=20when=20a?= =?UTF-8?q?=20new=20field=20is=20picked.=20Picking=20a=20new=20field=20cre?= =?UTF-8?q?ates=20a=20lot=20of=20DOM=20movement=20as=20options=20are=20loa?= =?UTF-8?q?ded=20in=E2=80=94this=20in=20addition=20to=20shuffling=20the=20?= =?UTF-8?q?stacks=20can=20create=20performance=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/js/components/blueprints/Fields.vue | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/resources/js/components/blueprints/Fields.vue b/resources/js/components/blueprints/Fields.vue index 9166a02ef77..8764dfc2870 100644 --- a/resources/js/components/blueprints/Fields.vue +++ b/resources/js/components/blueprints/Fields.vue @@ -34,26 +34,25 @@
+ - - - - + Date: Tue, 10 Mar 2026 22:47:06 +0000 Subject: [PATCH 5/6] =?UTF-8?q?Surpress=20hover=20during=20the=20new=20sta?= =?UTF-8?q?ck=E2=80=99s=20enter=20animation,=20to=20prevent=20the=20previo?= =?UTF-8?q?us=20stack=20wobbling=20left=20then=20right=20as=20the=20hover?= =?UTF-8?q?=20effect=20is=20removed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/portals/PortalTargets.vue | 41 ++++++++++++++++++- resources/js/components/ui/Stack/Stack.vue | 13 +++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/resources/js/components/portals/PortalTargets.vue b/resources/js/components/portals/PortalTargets.vue index 627183d123f..37e78976ab2 100644 --- a/resources/js/components/portals/PortalTargets.vue +++ b/resources/js/components/portals/PortalTargets.vue @@ -1,18 +1,31 @@ diff --git a/resources/js/components/ui/Stack/Stack.vue b/resources/js/components/ui/Stack/Stack.vue index bc4560a82bf..3172762e683 100644 --- a/resources/js/components/ui/Stack/Stack.vue +++ b/resources/js/components/ui/Stack/Stack.vue @@ -45,6 +45,7 @@ const stack = ref(null); const mounted = ref(false); const visible = ref(false); const isHovering = ref(false); +const isStackEntering = ref(false); const escBinding = ref(null); const windowInnerWidth = ref(window.innerWidth); @@ -123,6 +124,13 @@ const mouseOutHitArea = () => { }; const windowResized = () => windowInnerWidth.value = window.innerWidth; +const stackEnteringChanged = (entering) => { + isStackEntering.value = entering; + + if (entering) { + isHovering.value = false; + } +}; function open() { if (!stack.value) stack.value = stacks.add(instance.proxy); @@ -192,10 +200,13 @@ watch( ); onMounted(() => { + events.$on('stacks.entering', stackEnteringChanged); + if (props.open) open(); }); onBeforeUnmount(() => { + events.$off('stacks.entering', stackEnteringChanged); cleanup(); }); @@ -241,7 +252,7 @@ provide('closeStack', close); class="stack-content fixed flex flex-col sm:end-1.5 bg-content-bg dark:bg-dark-content-bg overflow-hidden rounded-xl shadow-[0_8px_5px_-6px_rgba(0,0,0,0.1),_0_3px_8px_0_rgba(0,0,0,0.02),_0_30px_22px_-22px_rgba(39,39,42,0.15)] dark:shadow-[0_5px_20px_rgba(0,0,0,.5)] transition-transform duration-200 ease-out will-change-transform" :class="[ size === 'full' ? 'inset-2 w-[calc(100svw-1rem)]' : 'inset-y-2', - { '-translate-x-4 rtl:translate-x-4': isHovering } + { '-translate-x-4 rtl:translate-x-4': isHovering && !isStackEntering } ]" >