@wordpress/block-library
Version:
Block library for the WordPress editor.
228 lines (218 loc) • 6.24 kB
JavaScript
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import {
Modal,
Button,
Flex,
Notice,
privateApis as componentsPrivateApis,
__experimentalVStack as VStack,
__experimentalHStack as HStack,
} from '@wordpress/components';
import { PlainText, store as blockEditorStore } from '@wordpress/block-editor';
import { fullscreen, square } from '@wordpress/icons';
import { useViewportMatch } from '@wordpress/compose';
/**
* Internal dependencies
*/
import { unlock } from '../lock-unlock';
import Preview from './preview';
import { parseContent, serializeContent } from './utils';
const { Tabs } = unlock( componentsPrivateApis );
export default function HTMLEditModal( {
isOpen,
onRequestClose,
content,
setAttributes,
} ) {
// Parse content into separate sections and use as initial state
const { html, css, js } = parseContent( content );
const [ editedHtml, setEditedHtml ] = useState( html );
const [ editedCss, setEditedCss ] = useState( css );
const [ editedJs, setEditedJs ] = useState( js );
const [ isFullscreen, setIsFullscreen ] = useState( false );
const isMobileViewport = useViewportMatch( 'small', '<' );
// Check if user has permission to save scripts and get editor styles
const { canUserUseUnfilteredHTML } = useSelect( ( select ) => {
const settings = select( blockEditorStore ).getSettings();
return {
canUserUseUnfilteredHTML:
settings.__experimentalCanUserUseUnfilteredHTML,
};
}, [] );
// Determine if we should show a warning about CSS/JS content being stripped
const hasRestrictedContent =
! canUserUseUnfilteredHTML && ( css.trim() || js.trim() );
if ( ! isOpen ) {
return null;
}
const handleUpdate = () => {
// For users without unfiltered_html capability, strip CSS and JS content
// to prevent kses from leaving broken content
setAttributes( {
content: serializeContent( {
html: editedHtml,
css: canUserUseUnfilteredHTML ? editedCss : '',
js: canUserUseUnfilteredHTML ? editedJs : '',
} ),
} );
};
const handleUpdateAndClose = () => {
handleUpdate();
onRequestClose();
};
const toggleFullscreen = () => {
setIsFullscreen( ( prevState ) => ! prevState );
};
return (
<>
<Modal
title={ __( 'Edit HTML' ) }
onRequestClose={ onRequestClose }
className="block-library-html__modal"
size="large"
isDismissible={ false }
shouldCloseOnClickOutside={ false }
isFullScreen={ isFullscreen }
__experimentalHideHeader
>
<Tabs orientation="horizontal" defaultTabId="html">
<VStack expanded>
<HStack
justify="space-between"
className="block-library-html__modal-header"
>
<div>
<Tabs.TabList>
<Tabs.Tab tabId="html">HTML</Tabs.Tab>
{ canUserUseUnfilteredHTML && (
<Tabs.Tab tabId="css">CSS</Tabs.Tab>
) }
{ canUserUseUnfilteredHTML && (
<Tabs.Tab tabId="js">
{ __( 'JavaScript' ) }
</Tabs.Tab>
) }
</Tabs.TabList>
</div>
{ ! isMobileViewport && (
<div>
<Button
__next40pxDefaultSize
icon={
isFullscreen ? square : fullscreen
}
label={ __(
'Enable/disable fullscreen'
) }
onClick={ toggleFullscreen }
variant="tertiary"
/>
</div>
) }
</HStack>
{ hasRestrictedContent && (
<Notice
status="warning"
isDismissible={ false }
className="block-library-html__modal-notice"
>
{ __(
'This block contains CSS or JavaScript that will be removed when you save because you do not have permission to use unfiltered HTML.'
) }
</Notice>
) }
<Flex
direction={ isMobileViewport ? 'column' : 'row' }
className="block-library-html__modal-tabs"
align="stretch"
gap={ 8 }
>
<div className="block-library-html__modal-content">
<Tabs.TabPanel
tabId="html"
focusable={ false }
className="block-library-html__modal-tab"
>
<PlainText
value={ editedHtml }
onChange={ setEditedHtml }
placeholder={ __( 'Write HTML…' ) }
aria-label={ __( 'HTML' ) }
className="block-library-html__modal-editor"
/>
</Tabs.TabPanel>
{ canUserUseUnfilteredHTML && (
<Tabs.TabPanel
tabId="css"
focusable={ false }
className="block-library-html__modal-tab"
>
<PlainText
value={ editedCss }
onChange={ setEditedCss }
placeholder={ __( 'Write CSS…' ) }
aria-label={ __( 'CSS' ) }
className="block-library-html__modal-editor"
/>
</Tabs.TabPanel>
) }
{ canUserUseUnfilteredHTML && (
<Tabs.TabPanel
tabId="js"
focusable={ false }
className="block-library-html__modal-tab"
>
<PlainText
value={ editedJs }
onChange={ setEditedJs }
placeholder={ __(
'Write JavaScript…'
) }
aria-label={ __( 'JavaScript' ) }
className="block-library-html__modal-editor"
/>
</Tabs.TabPanel>
) }
</div>
<div className="block-library-html__preview">
<Preview
content={ serializeContent( {
html: editedHtml,
css: editedCss,
js: editedJs,
} ) }
/>
</div>
</Flex>
<HStack
alignment="center"
justify="flex-end"
spacing={ 4 }
className="block-library-html__modal-footer"
>
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ onRequestClose }
>
{ __( 'Cancel' ) }
</Button>
<Button
__next40pxDefaultSize
variant="primary"
onClick={ handleUpdateAndClose }
>
{ __( 'Update' ) }
</Button>
</HStack>
</VStack>
</Tabs>
</Modal>
</>
);
}