diff --git a/resources/css/components/fieldtypes/bard.css b/resources/css/components/fieldtypes/bard.css index fcaf3ec5d7..d76315499d 100644 --- a/resources/css/components/fieldtypes/bard.css +++ b/resources/css/components/fieldtypes/bard.css @@ -154,6 +154,8 @@ .bard-fixed-toolbar { z-index: var(--z-index-portal); position: sticky; + /* Opt out the toolbar of scroll anchoring. Firefox's scroll anchoring adjusts the scroll position to compensate for the perceived layout change, scrolling up to the parent Bard's top. */ + overflow-anchor: none; @apply sm:-top-2; /* Prevent the sticky toolbar from hitting the very end of container, which causes it to overlap the container's border-radius */ margin-block-end: 8px; diff --git a/resources/js/components/fieldtypes/Fieldtype.vue b/resources/js/components/fieldtypes/Fieldtype.vue index e4b824a767..2004e662b6 100644 --- a/resources/js/components/fieldtypes/Fieldtype.vue +++ b/resources/js/components/fieldtypes/Fieldtype.vue @@ -4,7 +4,7 @@ import debounce from '@/util/debounce.js'; import props from './props.js'; import emits from './emits.js'; import { publishContextKey } from '@/components/ui'; -import { isRef } from 'vue'; +import { isRef, markRaw } from 'vue'; export default { emits, @@ -24,15 +24,17 @@ export default { this.$emit('update:value', value); }, - updateDebounced: debounce(function (value) { - this.update(value); - }, 150), - updateMeta(value) { this.$emit('update:meta', value); }, }, + created() { + this.updateDebounced = markRaw(debounce((value) => { + this.update(value); + }, 150)); + }, + computed: { publishContainer() { // The injectedPublishContainer contains refs. We'll unwrap everything so that we can do diff --git a/resources/js/components/fieldtypes/bard/BardFieldtype.vue b/resources/js/components/fieldtypes/bard/BardFieldtype.vue index ee2a1fd405..82a2af7832 100644 --- a/resources/js/components/fieldtypes/bard/BardFieldtype.vue +++ b/resources/js/components/fieldtypes/bard/BardFieldtype.vue @@ -416,17 +416,20 @@ export default { if (JSON.stringify(json) === JSON.stringify(oldJson)) return; - // Temporarily disable debouncing. - this.debounceNextUpdate = false; - - this.debounceNextUpdate - ? this.updateDebounced(json) - : this.update(json); - + const shouldDebounce = this.debounceNextUpdate; this.debounceNextUpdate = true; + + if (shouldDebounce) { + this.updateDebounced(json); + } else { + this.updateDebounced.cancel(); + this.update(json); + } }, value(value, oldValue) { + if (!this.editor) return; + const oldContent = this.editor.getJSON(); const content = this.valueToContent(value); diff --git a/resources/js/util/debounce.js b/resources/js/util/debounce.js index 0165d75d65..1dc726d303 100644 --- a/resources/js/util/debounce.js +++ b/resources/js/util/debounce.js @@ -1,7 +1,7 @@ export default function (func, wait, immediate) { let timeout; - return function () { + const debounced = function () { const context = this, args = arguments; @@ -14,4 +14,11 @@ export default function (func, wait, immediate) { if (!immediate) func.apply(context, args); }, wait); }; + + debounced.cancel = function () { + clearTimeout(timeout); + timeout = null; + }; + + return debounced; }