@@ -37,11 +37,22 @@ import { scopeSelector } from '../components/global-styles/utils';
37
37
import { useBlockSettings } from './utils' ;
38
38
import { default as StylesFiltersPanel } from '../components/global-styles/filters-panel' ;
39
39
import { useBlockEditingMode } from '../components/block-editing-mode' ;
40
+ import { __unstableUseBlockElement as useBlockElement } from '../components/block-list/use-block-props/use-block-refs' ;
40
41
import { store as blockEditorStore } from '../store' ;
41
42
import { unlock } from '../lock-unlock' ;
42
43
43
44
const EMPTY_ARRAY = [ ] ;
44
45
46
+ // Safari does not always update the duotone filter when the duotone colors
47
+ // are changed. This browser check is later used to force a re-render of the block
48
+ // element to ensure the duotone filter is updated. The check is included at the
49
+ // root of this file as it only needs to be run once per page load.
50
+ const isSafari =
51
+ window ?. navigator . userAgent &&
52
+ window . navigator . userAgent . includes ( 'Safari' ) &&
53
+ ! window . navigator . userAgent . includes ( 'Chrome' ) &&
54
+ ! window . navigator . userAgent . includes ( 'Chromium' ) ;
55
+
45
56
extend ( [ namesPlugin ] ) ;
46
57
47
58
function useMultiOriginPresets ( { presetSetting, defaultSetting } ) {
@@ -223,6 +234,7 @@ const withDuotoneControls = createHigherOrderComponent(
223
234
) ;
224
235
225
236
function DuotoneStyles ( {
237
+ clientId,
226
238
id : filterId ,
227
239
selector : duotoneSelector ,
228
240
attribute : duotoneAttr ,
@@ -278,6 +290,8 @@ function DuotoneStyles( {
278
290
useDispatch ( blockEditorStore )
279
291
) ;
280
292
293
+ const blockElement = useBlockElement ( clientId ) ;
294
+
281
295
useEffect ( ( ) => {
282
296
if ( ! isValidFilter ) return ;
283
297
@@ -294,12 +308,30 @@ function DuotoneStyles( {
294
308
__unstableType : 'svgs' ,
295
309
} ) ;
296
310
311
+ // Safari does not always update the duotone filter when the duotone colors
312
+ // are changed. When using Safari, force the block element to be repainted by
313
+ // the browser to ensure any changes are reflected visually. This logic matches
314
+ // that used on the site frontend in `block-supports/duotone.php`.
315
+ if ( blockElement && isSafari ) {
316
+ const display = blockElement . style . display ;
317
+ // Switch to `inline-block` to force a repaint. In the editor, `inline-block`
318
+ // is used instead of `none` to ensure that scroll position is not affected,
319
+ // as `none` results in the editor scrolling to the top of the block.
320
+ blockElement . style . display = 'inline-block' ;
321
+ // Simply accessing el.offsetHeight flushes layout and style
322
+ // changes in WebKit without having to wait for setTimeout.
323
+ // eslint-disable-next-line no-unused-expressions
324
+ blockElement . offsetHeight ;
325
+ blockElement . style . display = display ;
326
+ }
327
+
297
328
return ( ) => {
298
329
deleteStyleOverride ( filterId ) ;
299
330
deleteStyleOverride ( `duotone-${ filterId } ` ) ;
300
331
} ;
301
332
} , [
302
333
isValidFilter ,
334
+ blockElement ,
303
335
colors ,
304
336
selector ,
305
337
filterId ,
@@ -378,6 +410,7 @@ const withDuotoneStyles = createHigherOrderComponent(
378
410
< >
379
411
{ shouldRender && (
380
412
< DuotoneStyles
413
+ clientId = { props . clientId }
381
414
id = { filterClass }
382
415
selector = { selector }
383
416
attribute = { attribute }
0 commit comments