Fixes
- fix(ui): every directive guards its context again in
updated/beforeUnmount-> Vue defers a directive'supdatedto the post-flush queue but runsbeforeUnmountsynchronously, so a queuedupdatedcan outlive the element's unmount and find its context already gone. v2.30.0 dropped the early return that covered this, and the hook threw aTypeError. In production Vue logs it; in development it rethrows,flushJobsnever clearscurrentFlushPromiseand nothing reactive is ever flushed again, freezing the app on whatever was painted. Reported for the four touch directives (a QScrollArea inside<keep-alive :include>, dropped from the cache in the same tick it re-renders), the guard is restored across all eleven:v-close-popup,v-intersection,v-morph,v-mutation,v-ripple,v-scroll,v-scroll-fireandv-touch-hold/v-touch-pan/v-touch-repeat/v-touch-swipe(#18543) - fix(QInfiniteScroll): decide the first load at mount, not from the observer's opening report -> since the sentinel took over the trigger condition in v2.30.0, the first load waited for the observer's opening report, which lands a frame after mount. An app that fetches its first page from
@loadand renders a loading placeholder taller thanoffsetwhile that runs (from its ownmountedhook, anextTickor an async component) had already grown the content by then, so that report read "out of reach" and an observer never re-reports a state that merely holds:@loadnever fired and the placeholder waited for data nobody had asked for. The first load is decided on mount again, off the DOM as it stands, by the same reach test the observer applies, and it is no longer routed through the debounced poll, so a paused fake clock (Playwright'spage.clock) does not hold it back either. A component mounted out of reach stays quiet as before, and the observer still owns every load after the first (#18542) - fix(Ripple): the argument and the modifiers follow runtime changes ->
v-ripplere-read its argument and modifiers only when the value reference changed and only for an object value, so three things were frozen at mount:v-ripple:[color]with no value never picked up a new color, a modifiers object that changed while the value did not was ignored (QBtn hits this on its own, so toggling itsroundprop left the ripple starting from the press point instead of the center, or the other way round), and setting the value back totrueafter an object kept that object's options. All three now re-derive (#18541) - fix(utils/is):
deepEqual()handles null-prototype objects -> comparing two objects created withObject.create(null)threw, because thevalueOffallback saw a missing method as an override ofObject.prototype's and called it. Both conversion fallbacks now require an actual callable on both sides, so null-prototype objects, and objects whose ownvalueOf/toStringis not a function, compare by their keys. Two objects of the same constructor where only one carries such an override are compared key by key too, instead of through that one side's conversion (#18540) - fix(QTree): let header slot content size its own icons -> the
header-<name>slot defaults styled.q-iconand.q-avatarinside the node header at a specificity that tied with each component's own icon sizing and won on source order, so a QBtn placed in a header slot rendered a fixed 21px icon whatever itssizeprop said. The defaults are scoped through:where()and lose every tie, while still applying to plain slot icons (#16753)
Donations
Quasar Framework is an open-source MIT-licensed project made possible due to the generous contributions by sponsors and backers. If Quasar is useful in your workflow and you want to support ongoing maintenance, please consider the following: