@wordpress/block-library 9.40.2-next.v.202602271551.0 → 9.41.0
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.
- package/CHANGELOG.md +2 -0
- package/build/button/block.json +11 -3
- package/build/button/deprecated.cjs +246 -13
- package/build/button/deprecated.cjs.map +2 -2
- package/build/button/edit.cjs +45 -58
- package/build/button/edit.cjs.map +3 -3
- package/build/button/save.cjs +3 -7
- package/build/button/save.cjs.map +2 -2
- package/build/button/utils.cjs +59 -0
- package/build/button/utils.cjs.map +7 -0
- package/build/image/image.cjs +1 -1
- package/build/image/image.cjs.map +2 -2
- package/build/navigation-link/shared/use-link-preview.cjs +2 -2
- package/build/navigation-link/shared/use-link-preview.cjs.map +2 -2
- package/build/playlist/edit.cjs +43 -136
- package/build/playlist/edit.cjs.map +3 -3
- package/build/playlist/view.cjs +56 -38
- package/build/playlist/view.cjs.map +2 -2
- package/build/playlist-track/edit.cjs +0 -1
- package/build/playlist-track/edit.cjs.map +2 -2
- package/build/post-title/block.json +3 -0
- package/build/post-title/edit.cjs +2 -2
- package/build/post-title/edit.cjs.map +2 -2
- package/build/utils/waveform-player.cjs +68 -0
- package/build/utils/waveform-player.cjs.map +7 -0
- package/build/utils/waveform-utils.cjs +171 -0
- package/build/utils/waveform-utils.cjs.map +7 -0
- package/build-module/button/block.json +11 -3
- package/build-module/button/deprecated.mjs +246 -13
- package/build-module/button/deprecated.mjs.map +2 -2
- package/build-module/button/edit.mjs +47 -63
- package/build-module/button/edit.mjs.map +2 -2
- package/build-module/button/save.mjs +3 -7
- package/build-module/button/save.mjs.map +2 -2
- package/build-module/button/utils.mjs +33 -0
- package/build-module/button/utils.mjs.map +7 -0
- package/build-module/image/image.mjs +1 -1
- package/build-module/image/image.mjs.map +2 -2
- package/build-module/navigation-link/shared/use-link-preview.mjs +2 -2
- package/build-module/navigation-link/shared/use-link-preview.mjs.map +2 -2
- package/build-module/playlist/edit.mjs +41 -139
- package/build-module/playlist/edit.mjs.map +2 -2
- package/build-module/playlist/view.mjs +56 -38
- package/build-module/playlist/view.mjs.map +2 -2
- package/build-module/playlist-track/edit.mjs +0 -1
- package/build-module/playlist-track/edit.mjs.map +2 -2
- package/build-module/post-title/block.json +3 -0
- package/build-module/post-title/edit.mjs +2 -2
- package/build-module/post-title/edit.mjs.map +2 -2
- package/build-module/utils/waveform-player.mjs +43 -0
- package/build-module/utils/waveform-player.mjs.map +7 -0
- package/build-module/utils/waveform-utils.mjs +131 -0
- package/build-module/utils/waveform-utils.mjs.map +7 -0
- package/build-style/button/style-rtl.css +6 -0
- package/build-style/button/style.css +6 -0
- package/build-style/editor-rtl.css +3 -3
- package/build-style/editor.css +3 -3
- package/build-style/playlist/editor-rtl.css +3 -3
- package/build-style/playlist/editor.css +3 -3
- package/build-style/playlist/style-rtl.css +351 -17
- package/build-style/playlist/style.css +351 -17
- package/build-style/style-rtl.css +357 -17
- package/build-style/style.css +357 -17
- package/package.json +39 -38
- package/src/button/block.json +11 -3
- package/src/button/deprecated.js +254 -16
- package/src/button/edit.js +50 -61
- package/src/button/index.php +68 -0
- package/src/button/save.js +2 -8
- package/src/button/style.scss +49 -7
- package/src/button/test/utils.js +84 -0
- package/src/button/utils.js +42 -0
- package/src/image/image.js +14 -15
- package/src/image/index.php +3 -1
- package/src/navigation-link/shared/test/use-link-preview.test.js +9 -0
- package/src/navigation-link/shared/use-link-preview.js +6 -9
- package/src/playlist/edit.js +60 -154
- package/src/playlist/editor.scss +3 -3
- package/src/playlist/index.php +15 -40
- package/src/playlist/style.scss +34 -27
- package/src/playlist/test/edit.js +137 -0
- package/src/playlist/view.js +97 -40
- package/src/playlist-track/edit.js +0 -1
- package/src/post-title/block.json +3 -0
- package/src/post-title/edit.js +4 -2
- package/src/search/index.php +1 -1
- package/src/utils/test/waveform-utils.js +328 -0
- package/src/utils/waveform-player.js +77 -0
- package/src/utils/waveform-utils.js +232 -0
package/src/button/style.scss
CHANGED
|
@@ -55,16 +55,41 @@ $blocks-block__margin: 0.5em;
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
&[class*="wp-block-button__width"] {
|
|
59
|
+
width:
|
|
60
|
+
calc(var(--wp--block-button--width) * 1% -
|
|
61
|
+
(
|
|
62
|
+
var(--wp--style--block-gap, #{$blocks-block__margin}) *
|
|
63
|
+
( 1 - var(--wp--block-button--width) / 100 )
|
|
64
|
+
));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Legacy width classes for backwards compatibility.
|
|
58
68
|
&.wp-block-button__width-25 {
|
|
59
|
-
width:
|
|
69
|
+
width:
|
|
70
|
+
calc(25% -
|
|
71
|
+
(
|
|
72
|
+
var(--wp--style--block-gap, #{$blocks-block__margin}) *
|
|
73
|
+
0.75
|
|
74
|
+
));
|
|
60
75
|
}
|
|
61
76
|
|
|
62
77
|
&.wp-block-button__width-50 {
|
|
63
|
-
width:
|
|
78
|
+
width:
|
|
79
|
+
calc(50% -
|
|
80
|
+
(
|
|
81
|
+
var(--wp--style--block-gap, #{$blocks-block__margin}) *
|
|
82
|
+
0.5
|
|
83
|
+
));
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
&.wp-block-button__width-75 {
|
|
67
|
-
width:
|
|
87
|
+
width:
|
|
88
|
+
calc(75% -
|
|
89
|
+
(
|
|
90
|
+
var(--wp--style--block-gap, #{$blocks-block__margin}) *
|
|
91
|
+
0.25
|
|
92
|
+
));
|
|
68
93
|
}
|
|
69
94
|
|
|
70
95
|
&.wp-block-button__width-100 {
|
|
@@ -75,6 +100,11 @@ $blocks-block__margin: 0.5em;
|
|
|
75
100
|
|
|
76
101
|
// For vertical buttons, gap is not factored into width calculations.
|
|
77
102
|
.wp-block-buttons.is-vertical > .wp-block-button {
|
|
103
|
+
&[class*="wp-block-button__width"] {
|
|
104
|
+
width: calc(var(--wp--block-button--width) * 1%);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Legacy width classes for backwards compatibility.
|
|
78
108
|
&.wp-block-button__width-25 {
|
|
79
109
|
width: 25%;
|
|
80
110
|
}
|
|
@@ -110,13 +140,25 @@ $blocks-block__margin: 0.5em;
|
|
|
110
140
|
padding: 0.667em 1.333em;
|
|
111
141
|
}
|
|
112
142
|
|
|
113
|
-
:where(
|
|
114
|
-
|
|
143
|
+
:where(
|
|
144
|
+
.wp-block-button.is-style-outline
|
|
145
|
+
> .wp-block-button__link:not(.has-text-color)
|
|
146
|
+
),
|
|
147
|
+
:where(
|
|
148
|
+
.wp-block-button
|
|
149
|
+
.wp-block-button__link.is-style-outline:not(.has-text-color)
|
|
150
|
+
) {
|
|
115
151
|
color: currentColor;
|
|
116
152
|
}
|
|
117
153
|
|
|
118
|
-
:where(
|
|
119
|
-
|
|
154
|
+
:where(
|
|
155
|
+
.wp-block-button.is-style-outline
|
|
156
|
+
> .wp-block-button__link:not(.has-background)
|
|
157
|
+
),
|
|
158
|
+
:where(
|
|
159
|
+
.wp-block-button
|
|
160
|
+
.wp-block-button__link.is-style-outline:not(.has-background)
|
|
161
|
+
) {
|
|
120
162
|
background-color: transparent;
|
|
121
163
|
// background-image is required to overwrite a gradient background
|
|
122
164
|
background-image: none;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { getWidthClasses, isPercentageWidth } from '../utils';
|
|
5
|
+
|
|
6
|
+
describe( 'isPercentageWidth', () => {
|
|
7
|
+
it( 'should return true for percentage values', () => {
|
|
8
|
+
expect( isPercentageWidth( '50%' ) ).toBe( true );
|
|
9
|
+
expect( isPercentageWidth( '100%' ) ).toBe( true );
|
|
10
|
+
expect( isPercentageWidth( '33.5%' ) ).toBe( true );
|
|
11
|
+
} );
|
|
12
|
+
|
|
13
|
+
it( 'should return false for non-percentage values', () => {
|
|
14
|
+
expect( isPercentageWidth( '200px' ) ).toBe( false );
|
|
15
|
+
expect( isPercentageWidth( '10em' ) ).toBe( false );
|
|
16
|
+
expect( isPercentageWidth( undefined ) ).toBe( false );
|
|
17
|
+
expect( isPercentageWidth( null ) ).toBe( false );
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
it( 'should return false for preset strings', () => {
|
|
21
|
+
expect( isPercentageWidth( 'var:preset|dimension|custom-width' ) ).toBe(
|
|
22
|
+
false
|
|
23
|
+
);
|
|
24
|
+
} );
|
|
25
|
+
} );
|
|
26
|
+
|
|
27
|
+
describe( 'getWidthClasses', () => {
|
|
28
|
+
it( 'should return empty object when no width is provided', () => {
|
|
29
|
+
expect( getWidthClasses( undefined ) ).toEqual( {} );
|
|
30
|
+
expect( getWidthClasses( '' ) ).toEqual( {} );
|
|
31
|
+
expect( getWidthClasses( null ) ).toEqual( {} );
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
it( 'should return percentage classes for standard percentage widths', () => {
|
|
35
|
+
expect( getWidthClasses( '25%' ) ).toEqual( {
|
|
36
|
+
'has-custom-width': true,
|
|
37
|
+
'wp-block-button__width': true,
|
|
38
|
+
'wp-block-button__width-25': true,
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
expect( getWidthClasses( '50%' ) ).toEqual( {
|
|
42
|
+
'has-custom-width': true,
|
|
43
|
+
'wp-block-button__width': true,
|
|
44
|
+
'wp-block-button__width-50': true,
|
|
45
|
+
} );
|
|
46
|
+
|
|
47
|
+
expect( getWidthClasses( '75%' ) ).toEqual( {
|
|
48
|
+
'has-custom-width': true,
|
|
49
|
+
'wp-block-button__width': true,
|
|
50
|
+
'wp-block-button__width-75': true,
|
|
51
|
+
} );
|
|
52
|
+
|
|
53
|
+
expect( getWidthClasses( '100%' ) ).toEqual( {
|
|
54
|
+
'has-custom-width': true,
|
|
55
|
+
'wp-block-button__width': true,
|
|
56
|
+
'wp-block-button__width-100': true,
|
|
57
|
+
} );
|
|
58
|
+
} );
|
|
59
|
+
|
|
60
|
+
it( 'should return generic percentage classes for non-standard percentage widths', () => {
|
|
61
|
+
expect( getWidthClasses( '33%' ) ).toEqual( {
|
|
62
|
+
'has-custom-width': true,
|
|
63
|
+
'wp-block-button__width': true,
|
|
64
|
+
} );
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
it( 'should return only has-custom-width for non-percentage values', () => {
|
|
68
|
+
expect( getWidthClasses( '200px' ) ).toEqual( {
|
|
69
|
+
'has-custom-width': true,
|
|
70
|
+
} );
|
|
71
|
+
|
|
72
|
+
expect( getWidthClasses( '10em' ) ).toEqual( {
|
|
73
|
+
'has-custom-width': true,
|
|
74
|
+
} );
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
it( 'should return only has-custom-width for resolved non-percentage preset values', () => {
|
|
78
|
+
// When a preset resolves to a non-percentage value (e.g., 200px),
|
|
79
|
+
// the resolved value is passed to getWidthClasses, not the preset string.
|
|
80
|
+
expect( getWidthClasses( '300px' ) ).toEqual( {
|
|
81
|
+
'has-custom-width': true,
|
|
82
|
+
} );
|
|
83
|
+
} );
|
|
84
|
+
} );
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns whether the given width value is a percentage.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} width - The width value.
|
|
5
|
+
* @return {boolean} True if the width is a percentage value.
|
|
6
|
+
*/
|
|
7
|
+
export function isPercentageWidth( width ) {
|
|
8
|
+
return typeof width === 'string' && width.endsWith( '%' );
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Returns the width classes for the button based on the width attribute.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} width - The width value (e.g., '25%', '50%', '75%', '100%', or custom value).
|
|
15
|
+
* @return {Object} Object with width-related class names as keys and true as values.
|
|
16
|
+
*/
|
|
17
|
+
export function getWidthClasses( width ) {
|
|
18
|
+
if ( ! width ) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if ( isPercentageWidth( width ) ) {
|
|
23
|
+
const legacyWidthClasses = {
|
|
24
|
+
'25%': 'wp-block-button__width-25',
|
|
25
|
+
'50%': 'wp-block-button__width-50',
|
|
26
|
+
'75%': 'wp-block-button__width-75',
|
|
27
|
+
'100%': 'wp-block-button__width-100',
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
'has-custom-width': true,
|
|
31
|
+
'wp-block-button__width': true,
|
|
32
|
+
// Maintain legacy class for backwards compatibility.
|
|
33
|
+
...( legacyWidthClasses[ width ] && {
|
|
34
|
+
[ legacyWidthClasses[ width ] ]: true,
|
|
35
|
+
} ),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
'has-custom-width': true,
|
|
41
|
+
};
|
|
42
|
+
}
|
package/src/image/image.js
CHANGED
|
@@ -1241,21 +1241,20 @@ export default function Image( {
|
|
|
1241
1241
|
} );
|
|
1242
1242
|
};
|
|
1243
1243
|
|
|
1244
|
-
const featuredImageControl =
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
selectedClientIds
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
);
|
|
1244
|
+
const featuredImageControl =
|
|
1245
|
+
! isDescendentOfQueryLoop && postId && id ? (
|
|
1246
|
+
<BlockSettingsMenuControls>
|
|
1247
|
+
{ ( { canEdit, selectedClientIds } ) =>
|
|
1248
|
+
canEdit &&
|
|
1249
|
+
selectedClientIds.length === 1 &&
|
|
1250
|
+
clientId === selectedClientIds[ 0 ] && (
|
|
1251
|
+
<MenuItem onClick={ setPostFeatureImage }>
|
|
1252
|
+
{ __( 'Set as featured image' ) }
|
|
1253
|
+
</MenuItem>
|
|
1254
|
+
)
|
|
1255
|
+
}
|
|
1256
|
+
</BlockSettingsMenuControls>
|
|
1257
|
+
) : null;
|
|
1259
1258
|
|
|
1260
1259
|
return (
|
|
1261
1260
|
<>
|
package/src/image/index.php
CHANGED
|
@@ -218,7 +218,9 @@ function block_core_image_render_lightbox( $block_content, $block, $block_instan
|
|
|
218
218
|
if ( isset( $block['attrs']['id'] ) ) {
|
|
219
219
|
$img_uploaded_src = wp_get_attachment_url( $block['attrs']['id'] );
|
|
220
220
|
$img_metadata = wp_get_attachment_metadata( $block['attrs']['id'] );
|
|
221
|
-
$
|
|
221
|
+
$has_dimensions = ( $img_metadata['width'] ?? '' ) && ( $img_metadata['height'] ?? '' );
|
|
222
|
+
$srcset_size = $has_dimensions ? array( $img_metadata['width'], $img_metadata['height'] ) : 'large';
|
|
223
|
+
$img_srcset = wp_get_attachment_image_srcset( $block['attrs']['id'], $srcset_size );
|
|
222
224
|
$img_width = $img_metadata['width'] ?? 'none';
|
|
223
225
|
$img_height = $img_metadata['height'] ?? 'none';
|
|
224
226
|
}
|
|
@@ -105,6 +105,15 @@ describe( 'computeDisplayUrl', () => {
|
|
|
105
105
|
expect( result.isExternal ).toBe( false );
|
|
106
106
|
expect( result.displayUrl ).toBe( '/my-page' );
|
|
107
107
|
} );
|
|
108
|
+
|
|
109
|
+
it( 'should treat http and https to same host as internal (compare by host, not origin)', () => {
|
|
110
|
+
const result = computeDisplayUrl( {
|
|
111
|
+
linkUrl: 'http://example.com/my-page',
|
|
112
|
+
homeUrl: 'https://example.com',
|
|
113
|
+
} );
|
|
114
|
+
expect( result.isExternal ).toBe( false );
|
|
115
|
+
expect( result.displayUrl ).toBe( '/my-page' );
|
|
116
|
+
} );
|
|
108
117
|
} );
|
|
109
118
|
|
|
110
119
|
describe( 'special protocols and edge cases', () => {
|
|
@@ -31,7 +31,7 @@ function capitalize( str ) {
|
|
|
31
31
|
*
|
|
32
32
|
* @param {Object} options - Parameters object
|
|
33
33
|
* @param {string} options.linkUrl - The URL to process
|
|
34
|
-
* @param {string} options.homeUrl - The WordPress site URL (
|
|
34
|
+
* @param {string} options.homeUrl - The WordPress site URL (required for internal/external detection)
|
|
35
35
|
* @return {Object} Object with displayUrl and isExternal flag
|
|
36
36
|
*/
|
|
37
37
|
export function computeDisplayUrl( { linkUrl, homeUrl } = {} ) {
|
|
@@ -51,12 +51,10 @@ export function computeDisplayUrl( { linkUrl, homeUrl } = {} ) {
|
|
|
51
51
|
// This must happen before trusting the type attribute
|
|
52
52
|
try {
|
|
53
53
|
const parsedUrl = new URL( linkUrl );
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
? new URL( homeUrl ).origin
|
|
57
|
-
: window.location.origin;
|
|
54
|
+
// Compare by host (not origin) so http/https to same site both count as internal
|
|
55
|
+
const siteHost = new URL( homeUrl ).host;
|
|
58
56
|
|
|
59
|
-
if ( parsedUrl.
|
|
57
|
+
if ( parsedUrl.host === siteHost ) {
|
|
60
58
|
// Show only the pathname (and search/hash if present)
|
|
61
59
|
let path = parsedUrl.pathname + parsedUrl.search + parsedUrl.hash;
|
|
62
60
|
// Remove trailing slash
|
|
@@ -65,12 +63,11 @@ export function computeDisplayUrl( { linkUrl, homeUrl } = {} ) {
|
|
|
65
63
|
}
|
|
66
64
|
displayUrl = path;
|
|
67
65
|
} else {
|
|
68
|
-
// Different
|
|
66
|
+
// Different host - this is an external link
|
|
69
67
|
isExternal = true;
|
|
70
68
|
}
|
|
71
69
|
} catch ( e ) {
|
|
72
|
-
// URL parsing failed -
|
|
73
|
-
// Since we already checked for relative paths and hash links above, treat as external
|
|
70
|
+
// URL parsing failed - treat as external (e.g. no homeUrl, or URL without protocol)
|
|
74
71
|
isExternal = true;
|
|
75
72
|
}
|
|
76
73
|
|
package/src/playlist/edit.js
CHANGED
|
@@ -7,7 +7,7 @@ import { v4 as uuid } from 'uuid';
|
|
|
7
7
|
/**
|
|
8
8
|
* WordPress dependencies
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { useCallback, useEffect } from '@wordpress/element';
|
|
11
11
|
import {
|
|
12
12
|
store as blockEditorStore,
|
|
13
13
|
MediaPlaceholder,
|
|
@@ -23,15 +23,13 @@ import {
|
|
|
23
23
|
ToggleControl,
|
|
24
24
|
Disabled,
|
|
25
25
|
SelectControl,
|
|
26
|
-
Spinner,
|
|
27
26
|
__experimentalToolsPanel as ToolsPanel,
|
|
28
27
|
__experimentalToolsPanelItem as ToolsPanelItem,
|
|
29
28
|
} from '@wordpress/components';
|
|
30
29
|
import { useSelect, useDispatch } from '@wordpress/data';
|
|
31
30
|
import { store as noticesStore } from '@wordpress/notices';
|
|
32
|
-
import { __
|
|
31
|
+
import { __ } from '@wordpress/i18n';
|
|
33
32
|
import { audio as icon } from '@wordpress/icons';
|
|
34
|
-
import { safeHTML, __unstableStripHTML as stripHTML } from '@wordpress/dom';
|
|
35
33
|
import { createBlock } from '@wordpress/blocks';
|
|
36
34
|
|
|
37
35
|
/**
|
|
@@ -39,99 +37,42 @@ import { createBlock } from '@wordpress/blocks';
|
|
|
39
37
|
*/
|
|
40
38
|
import { Caption } from '../utils/caption';
|
|
41
39
|
import { useToolsPanelDropdownMenuProps } from '../utils/hooks';
|
|
40
|
+
import { WaveformPlayer } from '../utils/waveform-player';
|
|
42
41
|
|
|
43
42
|
const ALLOWED_MEDIA_TYPES = [ 'audio' ];
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
),
|
|
67
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Transform media library data into track block attributes.
|
|
46
|
+
*
|
|
47
|
+
* @param {Object} media - Media object from the media library.
|
|
48
|
+
* @return {Object} Track attributes for the playlist-track block.
|
|
49
|
+
*/
|
|
50
|
+
function getTrackAttributes( media ) {
|
|
51
|
+
return {
|
|
52
|
+
id: media.id || media.url, // Attachment ID or URL.
|
|
53
|
+
uniqueId: uuid(), // Unique ID for the track.
|
|
54
|
+
src: media.url,
|
|
55
|
+
title: media.title,
|
|
56
|
+
artist:
|
|
57
|
+
media.artist ||
|
|
58
|
+
media?.meta?.artist ||
|
|
59
|
+
media?.media_details?.artist ||
|
|
60
|
+
__( 'Unknown artist' ),
|
|
61
|
+
album:
|
|
62
|
+
media.album ||
|
|
63
|
+
media?.meta?.album ||
|
|
64
|
+
media?.media_details?.album ||
|
|
65
|
+
__( 'Unknown album' ),
|
|
66
|
+
length: media?.fileLength || media?.media_details?.length_formatted,
|
|
67
|
+
// Prevent using the default media attachment icon as the track image.
|
|
68
|
+
// Note: Image is not available when a new track is uploaded.
|
|
69
|
+
image:
|
|
70
|
+
media?.image?.src &&
|
|
71
|
+
media?.image?.src.endsWith( '/images/media/audio.svg' )
|
|
72
|
+
? ''
|
|
73
|
+
: media?.image?.src,
|
|
68
74
|
};
|
|
69
|
-
|
|
70
|
-
let ariaLabel;
|
|
71
|
-
if ( track?.title && track?.artist && track?.album ) {
|
|
72
|
-
ariaLabel = stripHTML(
|
|
73
|
-
sprintf(
|
|
74
|
-
/* translators: %1$s: track title, %2$s artist name, %3$s: album name. */
|
|
75
|
-
_x(
|
|
76
|
-
'%1$s by %2$s from the album %3$s',
|
|
77
|
-
'track title, artist name, album name'
|
|
78
|
-
),
|
|
79
|
-
track?.title,
|
|
80
|
-
track?.artist,
|
|
81
|
-
track?.album
|
|
82
|
-
)
|
|
83
|
-
);
|
|
84
|
-
} else if ( track?.title ) {
|
|
85
|
-
ariaLabel = stripHTML( track.title );
|
|
86
|
-
} else {
|
|
87
|
-
ariaLabel = stripHTML( __( 'Untitled' ) );
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<>
|
|
92
|
-
<div className="wp-block-playlist__current-item">
|
|
93
|
-
{ showImages && track?.image && (
|
|
94
|
-
<img
|
|
95
|
-
className="wp-block-playlist__item-image"
|
|
96
|
-
src={ track.image }
|
|
97
|
-
alt=""
|
|
98
|
-
width="70px"
|
|
99
|
-
height="70px"
|
|
100
|
-
/>
|
|
101
|
-
) }
|
|
102
|
-
<div>
|
|
103
|
-
{ ! track?.title ? (
|
|
104
|
-
<span className="wp-block-playlist__item-title">
|
|
105
|
-
<Spinner />
|
|
106
|
-
</span>
|
|
107
|
-
) : (
|
|
108
|
-
<span
|
|
109
|
-
className="wp-block-playlist__item-title"
|
|
110
|
-
{ ...trackTitle }
|
|
111
|
-
/>
|
|
112
|
-
) }
|
|
113
|
-
<div className="wp-block-playlist__current-item-artist-album">
|
|
114
|
-
<span
|
|
115
|
-
className="wp-block-playlist__item-artist"
|
|
116
|
-
{ ...trackArtist }
|
|
117
|
-
/>
|
|
118
|
-
<span
|
|
119
|
-
className="wp-block-playlist__item-album"
|
|
120
|
-
{ ...trackAlbum }
|
|
121
|
-
/>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
<audio
|
|
126
|
-
controls="controls"
|
|
127
|
-
src={ track?.url ? track.url : '' }
|
|
128
|
-
onEnded={ onTrackEnd }
|
|
129
|
-
aria-label={ ariaLabel }
|
|
130
|
-
tabIndex={ 0 }
|
|
131
|
-
/>
|
|
132
|
-
</>
|
|
133
|
-
);
|
|
134
|
-
};
|
|
75
|
+
}
|
|
135
76
|
|
|
136
77
|
const PlaylistEdit = ( {
|
|
137
78
|
attributes,
|
|
@@ -148,7 +89,6 @@ const PlaylistEdit = ( {
|
|
|
148
89
|
showArtists,
|
|
149
90
|
currentTrack,
|
|
150
91
|
} = attributes;
|
|
151
|
-
const [ trackListIndex, setTrackListIndex ] = useState( 0 );
|
|
152
92
|
const blockProps = useBlockProps();
|
|
153
93
|
const { replaceInnerBlocks, __unstableMarkNextChangeAsNotPersistent } =
|
|
154
94
|
useDispatch( blockEditorStore );
|
|
@@ -235,33 +175,7 @@ const PlaylistEdit = ( {
|
|
|
235
175
|
media = [ media ];
|
|
236
176
|
}
|
|
237
177
|
|
|
238
|
-
const
|
|
239
|
-
id: track.id || track.url, // Attachment ID or URL.
|
|
240
|
-
uniqueId: uuid(), // Unique ID for the track.
|
|
241
|
-
src: track.url,
|
|
242
|
-
title: track.title,
|
|
243
|
-
artist:
|
|
244
|
-
track.artist ||
|
|
245
|
-
track?.meta?.artist ||
|
|
246
|
-
track?.media_details?.artist ||
|
|
247
|
-
__( 'Unknown artist' ),
|
|
248
|
-
album:
|
|
249
|
-
track.album ||
|
|
250
|
-
track?.meta?.album ||
|
|
251
|
-
track?.media_details?.album ||
|
|
252
|
-
__( 'Unknown album' ),
|
|
253
|
-
length:
|
|
254
|
-
track?.fileLength || track?.media_details?.length_formatted,
|
|
255
|
-
// Prevent using the default media attachment icon as the track image.
|
|
256
|
-
// Note: Image is not available when a new track is uploaded.
|
|
257
|
-
image:
|
|
258
|
-
track?.image?.src &&
|
|
259
|
-
track?.image?.src.endsWith( '/images/media/audio.svg' )
|
|
260
|
-
? ''
|
|
261
|
-
: track?.image?.src,
|
|
262
|
-
} );
|
|
263
|
-
|
|
264
|
-
const trackList = media.map( trackAttributes );
|
|
178
|
+
const trackList = media.map( getTrackAttributes );
|
|
265
179
|
__unstableMarkNextChangeAsNotPersistent();
|
|
266
180
|
setAttributes( {
|
|
267
181
|
currentTrack:
|
|
@@ -282,29 +196,21 @@ const PlaylistEdit = ( {
|
|
|
282
196
|
]
|
|
283
197
|
);
|
|
284
198
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
} else if ( tracks.length > 0 ) {
|
|
299
|
-
const validTrack = tracks.find(
|
|
300
|
-
( track ) => track.uniqueId !== undefined
|
|
301
|
-
);
|
|
302
|
-
if ( validTrack ) {
|
|
303
|
-
setAttributes( { currentTrack: validTrack.uniqueId } );
|
|
304
|
-
}
|
|
305
|
-
}
|
|
199
|
+
// Get current track data by finding the track with matching uniqueId.
|
|
200
|
+
const currentTrackData = tracks.find(
|
|
201
|
+
( track ) => track.uniqueId === currentTrack
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// Handle track end - advance to next track or loop to first.
|
|
205
|
+
const onTrackEnded = useCallback( () => {
|
|
206
|
+
const currentIndex = tracks.findIndex(
|
|
207
|
+
( track ) => track.uniqueId === currentTrack
|
|
208
|
+
);
|
|
209
|
+
const nextTrack = tracks[ currentIndex + 1 ] || tracks[ 0 ];
|
|
210
|
+
if ( nextTrack?.uniqueId ) {
|
|
211
|
+
setAttributes( { currentTrack: nextTrack.uniqueId } );
|
|
306
212
|
}
|
|
307
|
-
}, [
|
|
213
|
+
}, [ currentTrack, tracks, setAttributes ] );
|
|
308
214
|
|
|
309
215
|
const onChangeOrder = useCallback(
|
|
310
216
|
( trackOrder ) => {
|
|
@@ -317,16 +223,13 @@ const PlaylistEdit = ( {
|
|
|
317
223
|
}
|
|
318
224
|
return titleB.localeCompare( titleA );
|
|
319
225
|
} );
|
|
320
|
-
const
|
|
321
|
-
( block ) => block.attributes
|
|
322
|
-
);
|
|
226
|
+
const firstUniqueId = sortedBlocks[ 0 ]?.attributes?.uniqueId;
|
|
323
227
|
replaceInnerBlocks( clientId, sortedBlocks );
|
|
324
228
|
setAttributes( {
|
|
325
229
|
order: trackOrder,
|
|
326
230
|
currentTrack:
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
? sortedTracks[ 0 ].uniqueId
|
|
231
|
+
firstUniqueId && firstUniqueId !== currentTrack
|
|
232
|
+
? firstUniqueId
|
|
330
233
|
: currentTrack,
|
|
331
234
|
} );
|
|
332
235
|
},
|
|
@@ -358,7 +261,7 @@ const PlaylistEdit = ( {
|
|
|
358
261
|
renderAppender: hasAnySelected && InnerBlocks.ButtonBlockAppender,
|
|
359
262
|
} );
|
|
360
263
|
|
|
361
|
-
if (
|
|
264
|
+
if ( tracks.length === 0 ) {
|
|
362
265
|
return (
|
|
363
266
|
<div
|
|
364
267
|
{ ...blockProps }
|
|
@@ -498,10 +401,12 @@ const PlaylistEdit = ( {
|
|
|
498
401
|
</InspectorControls>
|
|
499
402
|
<figure { ...blockProps }>
|
|
500
403
|
<Disabled isDisabled={ ! isSelected }>
|
|
501
|
-
<
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
404
|
+
<WaveformPlayer
|
|
405
|
+
src={ currentTrackData?.src }
|
|
406
|
+
title={ currentTrackData?.title }
|
|
407
|
+
artist={ currentTrackData?.artist }
|
|
408
|
+
image={ currentTrackData?.image }
|
|
409
|
+
onEnded={ onTrackEnded }
|
|
505
410
|
/>
|
|
506
411
|
</Disabled>
|
|
507
412
|
{ showTracklist && (
|
|
@@ -529,3 +434,4 @@ const PlaylistEdit = ( {
|
|
|
529
434
|
};
|
|
530
435
|
|
|
531
436
|
export default PlaylistEdit;
|
|
437
|
+
export { getTrackAttributes };
|
package/src/playlist/editor.scss
CHANGED