@wordpress/block-library 8.23.0 → 8.24.1

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.
Files changed (136) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/block/edit.js +8 -11
  3. package/build/block/edit.js.map +1 -1
  4. package/build/block/edit.native.js +3 -4
  5. package/build/block/edit.native.js.map +1 -1
  6. package/build/button/edit.js +4 -7
  7. package/build/button/edit.js.map +1 -1
  8. package/build/column/edit.js +1 -1
  9. package/build/column/edit.js.map +1 -1
  10. package/build/column/edit.native.js +1 -1
  11. package/build/column/edit.native.js.map +1 -1
  12. package/build/cover/edit/index.js +2 -1
  13. package/build/cover/edit/index.js.map +1 -1
  14. package/build/file/edit.js +8 -9
  15. package/build/file/edit.js.map +1 -1
  16. package/build/file/view.js +4 -6
  17. package/build/file/view.js.map +1 -1
  18. package/build/group/edit.js +3 -7
  19. package/build/group/edit.js.map +1 -1
  20. package/build/image/edit.js +17 -18
  21. package/build/image/edit.js.map +1 -1
  22. package/build/image/edit.native.js +22 -15
  23. package/build/image/edit.native.js.map +1 -1
  24. package/build/image/image.js +35 -27
  25. package/build/image/image.js.map +1 -1
  26. package/build/image/index.js +2 -3
  27. package/build/image/index.js.map +1 -1
  28. package/build/image/view.js +233 -268
  29. package/build/image/view.js.map +1 -1
  30. package/build/navigation/view.js +153 -176
  31. package/build/navigation/view.js.map +1 -1
  32. package/build/navigation-link/index.js +2 -1
  33. package/build/navigation-link/index.js.map +1 -1
  34. package/build/pattern/edit.js +1 -3
  35. package/build/pattern/edit.js.map +1 -1
  36. package/build/post-template/edit.js +1 -1
  37. package/build/post-template/edit.js.map +1 -1
  38. package/build/query/view.js +52 -60
  39. package/build/query/view.js.map +1 -1
  40. package/build/search/view.js +66 -74
  41. package/build/search/view.js.map +1 -1
  42. package/build/utils/remove-anchor-tag.js +17 -0
  43. package/build/utils/remove-anchor-tag.js.map +1 -0
  44. package/build-module/block/edit.js +8 -11
  45. package/build-module/block/edit.js.map +1 -1
  46. package/build-module/block/edit.native.js +3 -4
  47. package/build-module/block/edit.native.js.map +1 -1
  48. package/build-module/button/edit.js +4 -7
  49. package/build-module/button/edit.js.map +1 -1
  50. package/build-module/column/edit.js +1 -1
  51. package/build-module/column/edit.js.map +1 -1
  52. package/build-module/column/edit.native.js +1 -1
  53. package/build-module/column/edit.native.js.map +1 -1
  54. package/build-module/cover/edit/index.js +2 -1
  55. package/build-module/cover/edit/index.js.map +1 -1
  56. package/build-module/file/edit.js +8 -9
  57. package/build-module/file/edit.js.map +1 -1
  58. package/build-module/file/view.js +5 -7
  59. package/build-module/file/view.js.map +1 -1
  60. package/build-module/group/edit.js +3 -7
  61. package/build-module/group/edit.js.map +1 -1
  62. package/build-module/image/edit.js +18 -19
  63. package/build-module/image/edit.js.map +1 -1
  64. package/build-module/image/edit.native.js +23 -16
  65. package/build-module/image/edit.native.js.map +1 -1
  66. package/build-module/image/image.js +36 -28
  67. package/build-module/image/image.js.map +1 -1
  68. package/build-module/image/index.js +2 -3
  69. package/build-module/image/index.js.map +1 -1
  70. package/build-module/image/view.js +234 -269
  71. package/build-module/image/view.js.map +1 -1
  72. package/build-module/navigation/view.js +154 -177
  73. package/build-module/navigation/view.js.map +1 -1
  74. package/build-module/navigation-link/index.js +2 -1
  75. package/build-module/navigation-link/index.js.map +1 -1
  76. package/build-module/pattern/edit.js +1 -3
  77. package/build-module/pattern/edit.js.map +1 -1
  78. package/build-module/post-template/edit.js +1 -1
  79. package/build-module/post-template/edit.js.map +1 -1
  80. package/build-module/query/view.js +53 -61
  81. package/build-module/query/view.js.map +1 -1
  82. package/build-module/search/view.js +67 -75
  83. package/build-module/search/view.js.map +1 -1
  84. package/build-module/utils/remove-anchor-tag.js +11 -0
  85. package/build-module/utils/remove-anchor-tag.js.map +1 -0
  86. package/build-style/cover/style-rtl.css +14 -14
  87. package/build-style/cover/style.css +14 -14
  88. package/build-style/editor-rtl.css +6 -9
  89. package/build-style/editor.css +6 -9
  90. package/build-style/gallery/style-rtl.css +28 -0
  91. package/build-style/gallery/style.css +28 -0
  92. package/build-style/image/editor-rtl.css +0 -3
  93. package/build-style/image/editor.css +0 -3
  94. package/build-style/style-rtl.css +42 -14
  95. package/build-style/style.css +42 -14
  96. package/package.json +32 -32
  97. package/src/block/edit.js +18 -19
  98. package/src/block/edit.native.js +5 -13
  99. package/src/button/edit.js +6 -6
  100. package/src/buttons/test/__snapshots__/edit.native.js.snap +0 -6
  101. package/src/buttons/test/edit.native.js +0 -27
  102. package/src/column/edit.js +1 -1
  103. package/src/column/edit.native.js +1 -1
  104. package/src/cover/edit/index.js +1 -0
  105. package/src/cover/style.scss +1 -1
  106. package/src/cover/test/edit.js +1 -1
  107. package/src/editor.scss +6 -6
  108. package/src/file/edit.js +11 -10
  109. package/src/file/index.php +30 -11
  110. package/src/file/view.js +5 -7
  111. package/src/gallery/style.scss +1 -0
  112. package/src/group/edit.js +3 -11
  113. package/src/heading/test/__snapshots__/index.native.js.snap +6 -0
  114. package/src/heading/test/index.native.js +40 -0
  115. package/src/image/block.json +2 -3
  116. package/src/image/edit.js +16 -21
  117. package/src/image/edit.native.js +17 -18
  118. package/src/image/editor.scss +0 -7
  119. package/src/image/image.js +48 -51
  120. package/src/image/index.php +54 -45
  121. package/src/image/view.js +278 -334
  122. package/src/navigation/index.php +19 -10
  123. package/src/navigation/view.js +159 -192
  124. package/src/navigation-link/block.json +2 -1
  125. package/src/paragraph/test/edit.native.js +37 -1
  126. package/src/pattern/edit.js +5 -3
  127. package/src/post-template/edit.js +1 -1
  128. package/src/query/index.php +36 -22
  129. package/src/query/view.js +58 -65
  130. package/src/query-pagination-next/index.php +3 -3
  131. package/src/query-pagination-numbers/index.php +1 -1
  132. package/src/query-pagination-previous/index.php +3 -3
  133. package/src/search/index.php +40 -40
  134. package/src/search/view.js +58 -63
  135. package/src/utils/remove-anchor-tag.js +10 -0
  136. package/tsconfig.json +1 -0
package/src/image/view.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { store } from '@wordpress/interactivity';
4
+ import { store, getContext, getElement } from '@wordpress/interactivity';
5
5
 
6
6
  const focusableSelectors = [
7
7
  'a[href]',
@@ -17,7 +17,7 @@ const focusableSelectors = [
17
17
  '[tabindex]:not([tabindex^="-"])',
18
18
  ];
19
19
 
20
- /*
20
+ /**
21
21
  * Stores a context-bound scroll handler.
22
22
  *
23
23
  * This callback could be defined inline inside of the store
@@ -32,7 +32,7 @@ const focusableSelectors = [
32
32
  */
33
33
  let scrollCallback;
34
34
 
35
- /*
35
+ /**
36
36
  * Tracks whether user is touching screen; used to
37
37
  * differentiate behavior for touch and mouse input.
38
38
  *
@@ -40,7 +40,7 @@ let scrollCallback;
40
40
  */
41
41
  let isTouching = false;
42
42
 
43
- /*
43
+ /**
44
44
  * Tracks the last time the screen was touched; used to
45
45
  * differentiate behavior for touch and mouse input.
46
46
  *
@@ -48,7 +48,7 @@ let isTouching = false;
48
48
  */
49
49
  let lastTouchTime = 0;
50
50
 
51
- /*
51
+ /**
52
52
  * Lightbox page-scroll handler: prevents scrolling.
53
53
  *
54
54
  * This handler is added to prevent scrolling behaviors that
@@ -64,348 +64,296 @@ let lastTouchTime = 0;
64
64
  * instead to not rely on JavaScript, but this seems to be the best approach
65
65
  * for now that provides the best visual experience.
66
66
  *
67
- * @param {Object} context Interactivity page context?
67
+ * @param {Object} ctx Context object with the `core/image` namespace.
68
68
  */
69
- function handleScroll( context ) {
69
+ function handleScroll( ctx ) {
70
70
  // We can't override the scroll behavior on mobile devices
71
71
  // because doing so breaks the pinch to zoom functionality, and we
72
72
  // want to allow users to zoom in further on the high-res image.
73
73
  if ( ! isTouching && Date.now() - lastTouchTime > 450 ) {
74
74
  // We are unable to use event.preventDefault() to prevent scrolling
75
75
  // because the scroll event can't be canceled, so we reset the position instead.
76
- window.scrollTo(
77
- context.core.image.scrollLeftReset,
78
- context.core.image.scrollTopReset
79
- );
76
+ window.scrollTo( ctx.scrollLeftReset, ctx.scrollTopReset );
80
77
  }
81
78
  }
82
79
 
83
- store(
84
- {
85
- state: {
86
- core: {
87
- image: {
88
- windowWidth: window.innerWidth,
89
- windowHeight: window.innerHeight,
90
- },
91
- },
80
+ const { state, actions, callbacks } = store( 'core/image', {
81
+ state: {
82
+ windowWidth: window.innerWidth,
83
+ windowHeight: window.innerHeight,
84
+ get roleAttribute() {
85
+ const ctx = getContext();
86
+ return ctx.lightboxEnabled ? 'dialog' : null;
92
87
  },
93
- actions: {
94
- core: {
95
- image: {
96
- showLightbox: ( { context, event } ) => {
97
- // We can't initialize the lightbox until the reference
98
- // image is loaded, otherwise the UX is broken.
99
- if ( ! context.core.image.imageLoaded ) {
100
- return;
101
- }
102
- context.core.image.initialized = true;
103
- context.core.image.lastFocusedElement =
104
- window.document.activeElement;
105
- context.core.image.scrollDelta = 0;
106
- context.core.image.pointerType = event.pointerType;
107
-
108
- context.core.image.lightboxEnabled = true;
109
- setStyles( context, context.core.image.imageRef );
110
-
111
- context.core.image.scrollTopReset =
112
- window.pageYOffset ||
113
- document.documentElement.scrollTop;
114
-
115
- // In most cases, this value will be 0, but this is included
116
- // in case a user has created a page with horizontal scrolling.
117
- context.core.image.scrollLeftReset =
118
- window.pageXOffset ||
119
- document.documentElement.scrollLeft;
120
-
121
- // We define and bind the scroll callback here so
122
- // that we can pass the context and as an argument.
123
- // We may be able to change this in the future if we
124
- // define the scroll callback in the store instead, but
125
- // this approach seems to tbe clearest for now.
126
- scrollCallback = handleScroll.bind( null, context );
127
-
128
- // We need to add a scroll event listener to the window
129
- // here because we are unable to otherwise access it via
130
- // the Interactivity API directives. If we add a native way
131
- // to access the window, we can remove this.
132
- window.addEventListener(
133
- 'scroll',
134
- scrollCallback,
135
- false
136
- );
137
- },
138
- hideLightbox: async ( { context } ) => {
139
- context.core.image.hideAnimationEnabled = true;
140
- if ( context.core.image.lightboxEnabled ) {
141
- // We want to wait until the close animation is completed
142
- // before allowing a user to scroll again. The duration of this
143
- // animation is defined in the styles.scss and depends on if the
144
- // animation is 'zoom' or 'fade', but in any case we should wait
145
- // a few milliseconds longer than the duration, otherwise a user
146
- // may scroll too soon and cause the animation to look sloppy.
147
- setTimeout( function () {
148
- window.removeEventListener(
149
- 'scroll',
150
- scrollCallback
151
- );
152
- // If we don't delay before changing the focus,
153
- // the focus ring will appear on Firefox before
154
- // the image has finished animating, which looks broken.
155
- context.core.image.lightboxTriggerRef.focus( {
156
- preventScroll: true,
157
- } );
158
- }, 450 );
159
-
160
- context.core.image.lightboxEnabled = false;
161
- }
162
- },
163
- handleKeydown: ( { context, actions, event } ) => {
164
- if ( context.core.image.lightboxEnabled ) {
165
- if ( event.key === 'Tab' || event.keyCode === 9 ) {
166
- // If shift + tab it change the direction
167
- if (
168
- event.shiftKey &&
169
- window.document.activeElement ===
170
- context.core.image.firstFocusableElement
171
- ) {
172
- event.preventDefault();
173
- context.core.image.lastFocusableElement.focus();
174
- } else if (
175
- ! event.shiftKey &&
176
- window.document.activeElement ===
177
- context.core.image.lastFocusableElement
178
- ) {
179
- event.preventDefault();
180
- context.core.image.firstFocusableElement.focus();
181
- }
182
- }
183
-
184
- if (
185
- event.key === 'Escape' ||
186
- event.keyCode === 27
187
- ) {
188
- actions.core.image.hideLightbox( {
189
- context,
190
- event,
191
- } );
192
- }
193
- }
194
- },
195
- // This is fired just by lazily loaded
196
- // images on the page, not all images.
197
- handleLoad: ( { context, effects, ref } ) => {
198
- context.core.image.imageLoaded = true;
199
- context.core.image.imageCurrentSrc = ref.currentSrc;
200
- effects.core.image.setButtonStyles( {
201
- context,
202
- ref,
203
- } );
204
- },
205
- handleTouchStart: () => {
206
- isTouching = true;
207
- },
208
- handleTouchMove: ( { context, event } ) => {
209
- // On mobile devices, we want to prevent triggering the
210
- // scroll event because otherwise the page jumps around as
211
- // we reset the scroll position. This also means that closing
212
- // the lightbox requires that a user perform a simple tap. This
213
- // may be changed in the future if we find a better alternative
214
- // to override or reset the scroll position during swipe actions.
215
- if ( context.core.image.lightboxEnabled ) {
216
- event.preventDefault();
217
- }
218
- },
219
- handleTouchEnd: () => {
220
- // We need to wait a few milliseconds before resetting
221
- // to ensure that pinch to zoom works consistently
222
- // on mobile devices when the lightbox is open.
223
- lastTouchTime = Date.now();
224
- isTouching = false;
225
- },
226
- },
227
- },
88
+ get ariaModal() {
89
+ const ctx = getContext();
90
+ return ctx.lightboxEnabled ? 'true' : null;
228
91
  },
229
- selectors: {
230
- core: {
231
- image: {
232
- roleAttribute: ( { context } ) => {
233
- return context.core.image.lightboxEnabled
234
- ? 'dialog'
235
- : null;
236
- },
237
- ariaModal: ( { context } ) => {
238
- return context.core.image.lightboxEnabled
239
- ? 'true'
240
- : null;
241
- },
242
- dialogLabel: ( { context } ) => {
243
- return context.core.image.lightboxEnabled
244
- ? context.core.image.dialogLabel
245
- : null;
246
- },
247
- lightboxObjectFit: ( { context } ) => {
248
- if ( context.core.image.initialized ) {
249
- return 'cover';
250
- }
251
- },
252
- enlargedImgSrc: ( { context } ) => {
253
- return context.core.image.initialized
254
- ? context.core.image.imageUploadedSrc
255
- : 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
256
- },
257
- },
258
- },
92
+ get dialogLabel() {
93
+ const ctx = getContext();
94
+ return ctx.lightboxEnabled ? ctx.dialogLabel : null;
259
95
  },
260
- effects: {
261
- core: {
262
- image: {
263
- initOriginImage: ( { context, ref } ) => {
264
- context.core.image.imageRef = ref;
265
- context.core.image.lightboxTriggerRef =
266
- ref.parentElement.querySelector(
267
- '.lightbox-trigger'
268
- );
269
- if ( ref.complete ) {
270
- context.core.image.imageLoaded = true;
271
- context.core.image.imageCurrentSrc = ref.currentSrc;
272
- }
273
- },
274
- initLightbox: async ( { context, ref } ) => {
275
- if ( context.core.image.lightboxEnabled ) {
276
- const focusableElements =
277
- ref.querySelectorAll( focusableSelectors );
278
- context.core.image.firstFocusableElement =
279
- focusableElements[ 0 ];
280
- context.core.image.lastFocusableElement =
281
- focusableElements[
282
- focusableElements.length - 1
283
- ];
284
-
285
- // Move focus to the dialog when opening it.
286
- ref.focus();
287
- }
288
- },
289
- setButtonStyles: ( { context, ref } ) => {
290
- const {
291
- naturalWidth,
292
- naturalHeight,
293
- offsetWidth,
294
- offsetHeight,
295
- } = ref;
296
-
297
- // If the image isn't loaded yet, we can't
298
- // calculate where the button should be.
299
- if ( naturalWidth === 0 || naturalHeight === 0 ) {
300
- return;
301
- }
302
-
303
- const figure = ref.parentElement;
304
- const figureWidth = ref.parentElement.clientWidth;
305
-
306
- // We need special handling for the height because
307
- // a caption will cause the figure to be taller than
308
- // the image, which means we need to account for that
309
- // when calculating the placement of the button in the
310
- // top right corner of the image.
311
- let figureHeight = ref.parentElement.clientHeight;
312
- const caption = figure.querySelector( 'figcaption' );
313
- if ( caption ) {
314
- const captionComputedStyle =
315
- window.getComputedStyle( caption );
316
- if (
317
- ! [ 'absolute', 'fixed' ].includes(
318
- captionComputedStyle.position
319
- )
320
- ) {
321
- figureHeight =
322
- figureHeight -
323
- caption.offsetHeight -
324
- parseFloat(
325
- captionComputedStyle.marginTop
326
- ) -
327
- parseFloat(
328
- captionComputedStyle.marginBottom
329
- );
330
- }
331
- }
332
-
333
- const buttonOffsetTop = figureHeight - offsetHeight;
334
- const buttonOffsetRight = figureWidth - offsetWidth;
335
-
336
- // In the case of an image with object-fit: contain, the
337
- // size of the <img> element can be larger than the image itself,
338
- // so we need to calculate where to place the button.
339
- if ( context.core.image.scaleAttr === 'contain' ) {
340
- // Natural ratio of the image.
341
- const naturalRatio = naturalWidth / naturalHeight;
342
- // Offset ratio of the image.
343
- const offsetRatio = offsetWidth / offsetHeight;
344
-
345
- if ( naturalRatio >= offsetRatio ) {
346
- // If it reaches the width first, keep
347
- // the width and compute the height.
348
- const referenceHeight =
349
- offsetWidth / naturalRatio;
350
- context.core.image.imageButtonTop =
351
- ( offsetHeight - referenceHeight ) / 2 +
352
- buttonOffsetTop +
353
- 16;
354
- context.core.image.imageButtonRight =
355
- buttonOffsetRight + 16;
356
- } else {
357
- // If it reaches the height first, keep
358
- // the height and compute the width.
359
- const referenceWidth =
360
- offsetHeight * naturalRatio;
361
- context.core.image.imageButtonTop =
362
- buttonOffsetTop + 16;
363
- context.core.image.imageButtonRight =
364
- ( offsetWidth - referenceWidth ) / 2 +
365
- buttonOffsetRight +
366
- 16;
367
- }
368
- } else {
369
- context.core.image.imageButtonTop =
370
- buttonOffsetTop + 16;
371
- context.core.image.imageButtonRight =
372
- buttonOffsetRight + 16;
373
- }
374
- },
375
- setStylesOnResize: ( { state, context, ref } ) => {
376
- if (
377
- context.core.image.lightboxEnabled &&
378
- ( state.core.image.windowWidth ||
379
- state.core.image.windowHeight )
380
- ) {
381
- setStyles( context, ref );
382
- }
383
- },
384
- },
385
- },
96
+ get lightboxObjectFit() {
97
+ const ctx = getContext();
98
+ if ( ctx.initialized ) {
99
+ return 'cover';
100
+ }
101
+ },
102
+ get enlargedImgSrc() {
103
+ const ctx = getContext();
104
+ return ctx.initialized
105
+ ? ctx.imageUploadedSrc
106
+ : 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
386
107
  },
387
108
  },
388
- {
389
- afterLoad: ( { state } ) => {
390
- window.addEventListener(
391
- 'resize',
392
- debounce( () => {
393
- state.core.image.windowWidth = window.innerWidth;
394
- state.core.image.windowHeight = window.innerHeight;
395
- } )
396
- );
109
+ actions: {
110
+ showLightbox( event ) {
111
+ const ctx = getContext();
112
+ // We can't initialize the lightbox until the reference
113
+ // image is loaded, otherwise the UX is broken.
114
+ if ( ! ctx.imageLoaded ) {
115
+ return;
116
+ }
117
+ ctx.initialized = true;
118
+ ctx.lastFocusedElement = window.document.activeElement;
119
+ ctx.scrollDelta = 0;
120
+ ctx.pointerType = event.pointerType;
121
+
122
+ ctx.lightboxEnabled = true;
123
+ setStyles( ctx, ctx.imageRef );
124
+
125
+ ctx.scrollTopReset =
126
+ window.pageYOffset || document.documentElement.scrollTop;
127
+
128
+ // In most cases, this value will be 0, but this is included
129
+ // in case a user has created a page with horizontal scrolling.
130
+ ctx.scrollLeftReset =
131
+ window.pageXOffset || document.documentElement.scrollLeft;
132
+
133
+ // We define and bind the scroll callback here so
134
+ // that we can pass the context and as an argument.
135
+ // We may be able to change this in the future if we
136
+ // define the scroll callback in the store instead, but
137
+ // this approach seems to tbe clearest for now.
138
+ scrollCallback = handleScroll.bind( null, ctx );
139
+
140
+ // We need to add a scroll event listener to the window
141
+ // here because we are unable to otherwise access it via
142
+ // the Interactivity API directives. If we add a native way
143
+ // to access the window, we can remove this.
144
+ window.addEventListener( 'scroll', scrollCallback, false );
397
145
  },
398
- }
146
+ hideLightbox() {
147
+ const ctx = getContext();
148
+ ctx.hideAnimationEnabled = true;
149
+ if ( ctx.lightboxEnabled ) {
150
+ // We want to wait until the close animation is completed
151
+ // before allowing a user to scroll again. The duration of this
152
+ // animation is defined in the styles.scss and depends on if the
153
+ // animation is 'zoom' or 'fade', but in any case we should wait
154
+ // a few milliseconds longer than the duration, otherwise a user
155
+ // may scroll too soon and cause the animation to look sloppy.
156
+ setTimeout( function () {
157
+ window.removeEventListener( 'scroll', scrollCallback );
158
+ // If we don't delay before changing the focus,
159
+ // the focus ring will appear on Firefox before
160
+ // the image has finished animating, which looks broken.
161
+ ctx.lightboxTriggerRef.focus( {
162
+ preventScroll: true,
163
+ } );
164
+ }, 450 );
165
+
166
+ ctx.lightboxEnabled = false;
167
+ }
168
+ },
169
+ handleKeydown( event ) {
170
+ const ctx = getContext();
171
+ if ( ctx.lightboxEnabled ) {
172
+ if ( event.key === 'Tab' || event.keyCode === 9 ) {
173
+ // If shift + tab it change the direction
174
+ if (
175
+ event.shiftKey &&
176
+ window.document.activeElement ===
177
+ ctx.firstFocusableElement
178
+ ) {
179
+ event.preventDefault();
180
+ ctx.lastFocusableElement.focus();
181
+ } else if (
182
+ ! event.shiftKey &&
183
+ window.document.activeElement ===
184
+ ctx.lastFocusableElement
185
+ ) {
186
+ event.preventDefault();
187
+ ctx.firstFocusableElement.focus();
188
+ }
189
+ }
190
+
191
+ if ( event.key === 'Escape' || event.keyCode === 27 ) {
192
+ actions.hideLightbox( event );
193
+ }
194
+ }
195
+ },
196
+ // This is fired just by lazily loaded
197
+ // images on the page, not all images.
198
+ handleLoad() {
199
+ const ctx = getContext();
200
+ const { ref } = getElement();
201
+ ctx.imageLoaded = true;
202
+ ctx.imageCurrentSrc = ref.currentSrc;
203
+ callbacks.setButtonStyles();
204
+ },
205
+ handleTouchStart() {
206
+ isTouching = true;
207
+ },
208
+ handleTouchMove( event ) {
209
+ const ctx = getContext();
210
+ // On mobile devices, we want to prevent triggering the
211
+ // scroll event because otherwise the page jumps around as
212
+ // we reset the scroll position. This also means that closing
213
+ // the lightbox requires that a user perform a simple tap. This
214
+ // may be changed in the future if we find a better alternative
215
+ // to override or reset the scroll position during swipe actions.
216
+ if ( ctx.lightboxEnabled ) {
217
+ event.preventDefault();
218
+ }
219
+ },
220
+ handleTouchEnd() {
221
+ // We need to wait a few milliseconds before resetting
222
+ // to ensure that pinch to zoom works consistently
223
+ // on mobile devices when the lightbox is open.
224
+ lastTouchTime = Date.now();
225
+ isTouching = false;
226
+ },
227
+ },
228
+ callbacks: {
229
+ initOriginImage() {
230
+ const ctx = getContext();
231
+ const { ref } = getElement();
232
+ ctx.imageRef = ref;
233
+ ctx.lightboxTriggerRef =
234
+ ref.parentElement.querySelector( '.lightbox-trigger' );
235
+ if ( ref.complete ) {
236
+ ctx.imageLoaded = true;
237
+ ctx.imageCurrentSrc = ref.currentSrc;
238
+ }
239
+ },
240
+ initLightbox() {
241
+ const ctx = getContext();
242
+ const { ref } = getElement();
243
+ if ( ctx.lightboxEnabled ) {
244
+ const focusableElements =
245
+ ref.querySelectorAll( focusableSelectors );
246
+ ctx.firstFocusableElement = focusableElements[ 0 ];
247
+ ctx.lastFocusableElement =
248
+ focusableElements[ focusableElements.length - 1 ];
249
+
250
+ // Move focus to the dialog when opening it.
251
+ ref.focus();
252
+ }
253
+ },
254
+ setButtonStyles() {
255
+ const { ref } = getElement();
256
+ const { naturalWidth, naturalHeight, offsetWidth, offsetHeight } =
257
+ ref;
258
+
259
+ // If the image isn't loaded yet, we can't
260
+ // calculate where the button should be.
261
+ if ( naturalWidth === 0 || naturalHeight === 0 ) {
262
+ return;
263
+ }
264
+
265
+ const figure = ref.parentElement;
266
+ const figureWidth = ref.parentElement.clientWidth;
267
+
268
+ // We need special handling for the height because
269
+ // a caption will cause the figure to be taller than
270
+ // the image, which means we need to account for that
271
+ // when calculating the placement of the button in the
272
+ // top right corner of the image.
273
+ let figureHeight = ref.parentElement.clientHeight;
274
+ const caption = figure.querySelector( 'figcaption' );
275
+ if ( caption ) {
276
+ const captionComputedStyle = window.getComputedStyle( caption );
277
+ if (
278
+ ! [ 'absolute', 'fixed' ].includes(
279
+ captionComputedStyle.position
280
+ )
281
+ ) {
282
+ figureHeight =
283
+ figureHeight -
284
+ caption.offsetHeight -
285
+ parseFloat( captionComputedStyle.marginTop ) -
286
+ parseFloat( captionComputedStyle.marginBottom );
287
+ }
288
+ }
289
+
290
+ const buttonOffsetTop = figureHeight - offsetHeight;
291
+ const buttonOffsetRight = figureWidth - offsetWidth;
292
+
293
+ const ctx = getContext();
294
+
295
+ // In the case of an image with object-fit: contain, the
296
+ // size of the <img> element can be larger than the image itself,
297
+ // so we need to calculate where to place the button.
298
+ if ( ctx.scaleAttr === 'contain' ) {
299
+ // Natural ratio of the image.
300
+ const naturalRatio = naturalWidth / naturalHeight;
301
+ // Offset ratio of the image.
302
+ const offsetRatio = offsetWidth / offsetHeight;
303
+
304
+ if ( naturalRatio >= offsetRatio ) {
305
+ // If it reaches the width first, keep
306
+ // the width and compute the height.
307
+ const referenceHeight = offsetWidth / naturalRatio;
308
+ ctx.imageButtonTop =
309
+ ( offsetHeight - referenceHeight ) / 2 +
310
+ buttonOffsetTop +
311
+ 16;
312
+ ctx.imageButtonRight = buttonOffsetRight + 16;
313
+ } else {
314
+ // If it reaches the height first, keep
315
+ // the height and compute the width.
316
+ const referenceWidth = offsetHeight * naturalRatio;
317
+ ctx.imageButtonTop = buttonOffsetTop + 16;
318
+ ctx.imageButtonRight =
319
+ ( offsetWidth - referenceWidth ) / 2 +
320
+ buttonOffsetRight +
321
+ 16;
322
+ }
323
+ } else {
324
+ ctx.imageButtonTop = buttonOffsetTop + 16;
325
+ ctx.imageButtonRight = buttonOffsetRight + 16;
326
+ }
327
+ },
328
+ setStylesOnResize() {
329
+ const ctx = getContext();
330
+ const { ref } = getElement();
331
+ if (
332
+ ctx.lightboxEnabled &&
333
+ ( state.windowWidth || state.windowHeight )
334
+ ) {
335
+ setStyles( ctx, ref );
336
+ }
337
+ },
338
+ },
339
+ } );
340
+
341
+ window.addEventListener(
342
+ 'resize',
343
+ debounce( () => {
344
+ state.windowWidth = window.innerWidth;
345
+ state.windowHeight = window.innerHeight;
346
+ } )
399
347
  );
400
348
 
401
- /*
349
+ /**
402
350
  * Computes styles for the lightbox and adds them to the document.
403
351
  *
404
352
  * @function
405
- * @param {Object} context - An Interactivity API context
406
- * @param {Object} event - A triggering event
353
+ * @param {Object} ctx - Context for the `core/image` namespace.
354
+ * @param {Object} ref - The element reference.
407
355
  */
408
- function setStyles( context, ref ) {
356
+ function setStyles( ctx, ref ) {
409
357
  // The reference img element lies adjacent
410
358
  // to the event target button in the DOM.
411
359
  let {
@@ -423,7 +371,7 @@ function setStyles( context, ref ) {
423
371
 
424
372
  // If it has object-fit: contain, recalculate the original sizes
425
373
  // and the screen position without the blank spaces.
426
- if ( context.core.image.scaleAttr === 'contain' ) {
374
+ if ( ctx.scaleAttr === 'contain' ) {
427
375
  if ( naturalRatio > originalRatio ) {
428
376
  const heightWithoutSpace = originalWidth / naturalRatio;
429
377
  // Recalculate screen position without the top space.
@@ -443,14 +391,10 @@ function setStyles( context, ref ) {
443
391
  // the image's dimensions in the lightbox are the same
444
392
  // as those of the image in the content.
445
393
  let imgMaxWidth = parseFloat(
446
- context.core.image.targetWidth !== 'none'
447
- ? context.core.image.targetWidth
448
- : naturalWidth
394
+ ctx.targetWidth !== 'none' ? ctx.targetWidth : naturalWidth
449
395
  );
450
396
  let imgMaxHeight = parseFloat(
451
- context.core.image.targetHeight !== 'none'
452
- ? context.core.image.targetHeight
453
- : naturalHeight
397
+ ctx.targetHeight !== 'none' ? ctx.targetHeight : naturalHeight
454
398
  );
455
399
 
456
400
  // Ratio of the biggest image stored in the database.
@@ -575,12 +519,12 @@ function setStyles( context, ref ) {
575
519
  `;
576
520
  }
577
521
 
578
- /*
522
+ /**
579
523
  * Debounces a function call.
580
524
  *
581
525
  * @function
582
526
  * @param {Function} func - A function to be called
583
- * @param {number} wait - The time to wait before calling the function
527
+ * @param {number} wait - The time to wait before calling the function
584
528
  */
585
529
  function debounce( func, wait = 50 ) {
586
530
  let timeout;