UNPKG

@wordpress/block-library

Version:
495 lines (494 loc) 15.5 kB
// packages/block-library/src/playlist/edit.js import clsx from "clsx"; import { v4 as uuid } from "uuid"; import { useState, useCallback, useEffect } from "@wordpress/element"; import { store as blockEditorStore, MediaPlaceholder, MediaReplaceFlow, BlockIcon, useBlockProps, useInnerBlocksProps, BlockControls, InspectorControls, InnerBlocks } from "@wordpress/block-editor"; import { ToggleControl, Disabled, SelectControl, Spinner, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem } from "@wordpress/components"; import { useSelect, useDispatch } from "@wordpress/data"; import { store as noticesStore } from "@wordpress/notices"; import { __, _x, sprintf } from "@wordpress/i18n"; import { audio as icon } from "@wordpress/icons"; import { safeHTML, __unstableStripHTML as stripHTML } from "@wordpress/dom"; import { createBlock } from "@wordpress/blocks"; import { Caption } from "../utils/caption.mjs"; import { useToolsPanelDropdownMenuProps } from "../utils/hooks.mjs"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var ALLOWED_MEDIA_TYPES = ["audio"]; var CurrentTrack = ({ track, showImages, onTrackEnd }) => { const trackTitle = { dangerouslySetInnerHTML: { __html: safeHTML(track?.title ? track.title : __("Untitled")) } }; const trackArtist = { dangerouslySetInnerHTML: { __html: safeHTML( track?.artist ? track.artist : __("Unknown artist") ) } }; const trackAlbum = { dangerouslySetInnerHTML: { __html: safeHTML( track?.album ? track.album : __("Unknown album") ) } }; let ariaLabel; if (track?.title && track?.artist && track?.album) { ariaLabel = stripHTML( sprintf( /* translators: %1$s: track title, %2$s artist name, %3$s: album name. */ _x( "%1$s by %2$s from the album %3$s", "track title, artist name, album name" ), track?.title, track?.artist, track?.album ) ); } else if (track?.title) { ariaLabel = stripHTML(track.title); } else { ariaLabel = stripHTML(__("Untitled")); } return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsxs("div", { className: "wp-block-playlist__current-item", children: [ showImages && track?.image && /* @__PURE__ */ jsx( "img", { className: "wp-block-playlist__item-image", src: track.image, alt: "", width: "70px", height: "70px" } ), /* @__PURE__ */ jsxs("div", { children: [ !track?.title ? /* @__PURE__ */ jsx("span", { className: "wp-block-playlist__item-title", children: /* @__PURE__ */ jsx(Spinner, {}) }) : /* @__PURE__ */ jsx( "span", { className: "wp-block-playlist__item-title", ...trackTitle } ), /* @__PURE__ */ jsxs("div", { className: "wp-block-playlist__current-item-artist-album", children: [ /* @__PURE__ */ jsx( "span", { className: "wp-block-playlist__item-artist", ...trackArtist } ), /* @__PURE__ */ jsx( "span", { className: "wp-block-playlist__item-album", ...trackAlbum } ) ] }) ] }) ] }), /* @__PURE__ */ jsx( "audio", { controls: "controls", src: track?.url ? track.url : "", onEnded: onTrackEnd, "aria-label": ariaLabel, tabIndex: 0 } ) ] }); }; var PlaylistEdit = ({ attributes, setAttributes, isSelected, insertBlocksAfter, clientId }) => { const { order, showTracklist, showNumbers, showImages, showArtists, currentTrack } = attributes; const [trackListIndex, setTrackListIndex] = useState(0); const blockProps = useBlockProps(); const { replaceInnerBlocks, __unstableMarkNextChangeAsNotPersistent } = useDispatch(blockEditorStore); const { createErrorNotice } = useDispatch(noticesStore); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); function onUploadError(message) { createErrorNotice(message, { type: "snackbar" }); } const { updateBlockAttributes } = useDispatch(blockEditorStore); const { innerBlockTracks } = useSelect( (select) => { const { getBlock: _getBlock } = select(blockEditorStore); return { innerBlockTracks: _getBlock(clientId)?.innerBlocks ?? [] }; }, [clientId] ); useEffect(() => { const seen = /* @__PURE__ */ new Set(); let hasDuplicates = false; const updatedBlocks = innerBlockTracks.map((block) => { if (seen.has(block.attributes.uniqueId)) { hasDuplicates = true; return { ...block, attributes: { ...block.attributes, uniqueId: uuid() } }; } seen.add(block.attributes.uniqueId); return block; }); if (hasDuplicates) { replaceInnerBlocks(clientId, updatedBlocks); } }, [innerBlockTracks, clientId, replaceInnerBlocks]); const validTracks = innerBlockTracks.filter( (block) => !!block.attributes.uniqueId ); const tracks = validTracks.map((block) => block.attributes); const firstTrackId = validTracks[0]?.attributes?.uniqueId; useEffect(() => { if (tracks.length === 0) { if (currentTrack !== null) { updateBlockAttributes(clientId, { currentTrack: null }); } } else if ( // If the currentTrack is not the first track, update it to the first track. firstTrackId && firstTrackId !== currentTrack ) { updateBlockAttributes(clientId, { currentTrack: firstTrackId }); } }, [ tracks, currentTrack, firstTrackId, clientId, updateBlockAttributes ]); const onSelectTracks = useCallback( (media) => { if (!media) { return; } if (!Array.isArray(media)) { media = [media]; } const trackAttributes = (track) => ({ id: track.id || track.url, // Attachment ID or URL. uniqueId: uuid(), // Unique ID for the track. src: track.url, title: track.title, artist: track.artist || track?.meta?.artist || track?.media_details?.artist || __("Unknown artist"), album: track.album || track?.meta?.album || track?.media_details?.album || __("Unknown album"), length: track?.fileLength || track?.media_details?.length_formatted, // Prevent using the default media attachment icon as the track image. // Note: Image is not available when a new track is uploaded. image: track?.image?.src && track?.image?.src.endsWith("/images/media/audio.svg") ? "" : track?.image?.src }); const trackList = media.map(trackAttributes); __unstableMarkNextChangeAsNotPersistent(); setAttributes({ currentTrack: trackList.length > 0 ? trackList[0].uniqueId : null }); const newBlocks = trackList.map( (track) => createBlock("core/playlist-track", track) ); replaceInnerBlocks(clientId, newBlocks); }, [ __unstableMarkNextChangeAsNotPersistent, setAttributes, replaceInnerBlocks, clientId ] ); const onTrackEnd = useCallback(() => { if (trackListIndex < tracks.length - 1) { if (tracks[trackListIndex + 1]?.uniqueId) { setTrackListIndex(trackListIndex + 1); setAttributes({ currentTrack: tracks[trackListIndex + 1].uniqueId }); } } else { setTrackListIndex(0); if (tracks[0].uniqueId) { setAttributes({ currentTrack: tracks[0].uniqueId }); } else if (tracks.length > 0) { const validTrack = tracks.find( (track) => track.uniqueId !== void 0 ); if (validTrack) { setAttributes({ currentTrack: validTrack.uniqueId }); } } } }, [setAttributes, trackListIndex, tracks]); const onChangeOrder = useCallback( (trackOrder) => { const sortedBlocks = [...innerBlockTracks].sort((a, b) => { const titleA = a.attributes.title || ""; const titleB = b.attributes.title || ""; if (trackOrder === "asc") { return titleA.localeCompare(titleB); } return titleB.localeCompare(titleA); }); const sortedTracks = sortedBlocks.map( (block) => block.attributes ); replaceInnerBlocks(clientId, sortedBlocks); setAttributes({ order: trackOrder, currentTrack: sortedTracks.length > 0 && sortedTracks[0].uniqueId !== currentTrack ? sortedTracks[0].uniqueId : currentTrack }); }, [ clientId, currentTrack, innerBlockTracks, replaceInnerBlocks, setAttributes ] ); function toggleAttribute(attribute) { return (newValue) => { setAttributes({ [attribute]: newValue }); }; } const hasSelectedChild = useSelect( (select) => select(blockEditorStore).hasSelectedInnerBlock(clientId), [clientId] ); const hasAnySelected = isSelected || hasSelectedChild; const innerBlocksProps = useInnerBlocksProps(blockProps, { __experimentalAppenderTagName: "li", renderAppender: hasAnySelected && InnerBlocks.ButtonBlockAppender }); if (!tracks || Array.isArray(tracks) && tracks.length === 0) { return /* @__PURE__ */ jsx( "div", { ...blockProps, className: clsx("is-placeholder", blockProps.className), children: /* @__PURE__ */ jsx( MediaPlaceholder, { icon: /* @__PURE__ */ jsx(BlockIcon, { icon }), labels: { title: __("Playlist"), instructions: __( "Upload an audio file or pick one from your media library." ) }, onSelect: onSelectTracks, accept: "audio/*", multiple: true, allowedTypes: ALLOWED_MEDIA_TYPES, onError: onUploadError } ) } ); } return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(BlockControls, { group: "other", children: /* @__PURE__ */ jsx( MediaReplaceFlow, { name: __("Edit"), onSelect: onSelectTracks, accept: "audio/*", multiple: true, mediaIds: tracks.filter((track) => track.id).map((track) => track.id), allowedTypes: ALLOWED_MEDIA_TYPES, onError: onUploadError } ) }), /* @__PURE__ */ jsx(InspectorControls, { children: /* @__PURE__ */ jsxs( ToolsPanel, { label: __("Settings"), resetAll: () => { setAttributes({ showTracklist: true, showArtists: true, showNumbers: true, showImages: true, order: "asc" }); }, dropdownMenuProps, children: [ /* @__PURE__ */ jsx( ToolsPanelItem, { label: __("Show Tracklist"), isShownByDefault: true, hasValue: () => showTracklist !== true, onDeselect: () => setAttributes({ showTracklist: true }), children: /* @__PURE__ */ jsx( ToggleControl, { label: __("Show Tracklist"), onChange: toggleAttribute("showTracklist"), checked: showTracklist } ) } ), showTracklist && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx( ToolsPanelItem, { label: __("Show artist name in Tracklist"), isShownByDefault: true, hasValue: () => showArtists !== true, onDeselect: () => setAttributes({ showArtists: true }), children: /* @__PURE__ */ jsx( ToggleControl, { label: __( "Show artist name in Tracklist" ), onChange: toggleAttribute( "showArtists" ), checked: showArtists } ) } ), /* @__PURE__ */ jsx( ToolsPanelItem, { label: __("Show number in Tracklist"), isShownByDefault: true, hasValue: () => showNumbers !== true, onDeselect: () => setAttributes({ showNumbers: true }), children: /* @__PURE__ */ jsx( ToggleControl, { label: __("Show number in Tracklist"), onChange: toggleAttribute( "showNumbers" ), checked: showNumbers } ) } ) ] }), /* @__PURE__ */ jsx( ToolsPanelItem, { label: __("Show images"), isShownByDefault: true, hasValue: () => showImages !== true, onDeselect: () => setAttributes({ showImages: true }), children: /* @__PURE__ */ jsx( ToggleControl, { label: __("Show images"), onChange: toggleAttribute("showImages"), checked: showImages } ) } ), /* @__PURE__ */ jsx( ToolsPanelItem, { label: __("Order"), isShownByDefault: true, hasValue: () => order !== "asc", onDeselect: () => setAttributes({ order: "asc" }), children: /* @__PURE__ */ jsx( SelectControl, { __next40pxDefaultSize: true, label: __("Order"), value: order, options: [ { label: __("Descending"), value: "desc" }, { label: __("Ascending"), value: "asc" } ], onChange: (value) => onChangeOrder(value) } ) } ) ] } ) }), /* @__PURE__ */ jsxs("figure", { ...blockProps, children: [ /* @__PURE__ */ jsx(Disabled, { isDisabled: !isSelected, children: /* @__PURE__ */ jsx( CurrentTrack, { track: tracks[trackListIndex], showImages, onTrackEnd } ) }), showTracklist && /* @__PURE__ */ jsx( "ol", { className: clsx("wp-block-playlist__tracklist", { "wp-block-playlist__tracklist-show-numbers": showNumbers }), children: innerBlocksProps.children } ), /* @__PURE__ */ jsx( Caption, { attributes, setAttributes, isSelected, insertBlocksAfter, label: __("Playlist caption text"), showToolbarButton: isSelected, style: { marginTop: 16 } } ) ] }) ] }); }; var edit_default = PlaylistEdit; export { edit_default as default }; //# sourceMappingURL=edit.mjs.map