ajax-request.js000064400000013114150212644410007512 0ustar00/** * JavaScript code for AJAX requests with Notices functionality on admin screens. * * @package TablePress * @subpackage Views JavaScript * @author Tobias Bäthge * @since 3.1.0 */ /** * WordPress dependencies. */ import { Icon, } from '@wordpress/components'; import { RawHTML } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { buildQueryString } from '@wordpress/url'; import { TablePressIconSimple } from '../../img/tablepress-icon'; /** * Default callback for handling specifics of a successful request. * * @param {Object} data */ const onSuccessfulRequestDefault = ( data ) => { const actionMessages = { success_save: __( 'The changes were saved successfully.', 'tablepress' ), }; const notice = { status: ( data.message.includes( 'error' ) ) ? 'error' : 'success', content: actionMessages[ data.message ], type: ( data.message.includes( 'error' ) ) ? 'notice' : 'snackbar', }; return { notice }; }; /** * Processes an AJAX request with Notices functionality. * * @param {Object} props Function parameters. * @param {Object} props.requestData Request data. * @param {Function} props.onSuccessfulRequest Callback for handling a successful save. * @param {Function} props.setBusyState Callback for setting the busy state. * @param {Function} props.noticeOperations Callbacks for working with notices. * @param {Function} props.noticesStoreDispatch Dispatch function for notices store. (Optional, only for "success" Snackbar notices.) */ const processAjaxRequest = ( { requestData, onSuccessfulRequest = onSuccessfulRequestDefault, setBusyState, noticeOperations, noticesStoreDispatch } ) => { /** * Shows a notice to the user. * * @param {Object} notice Notice data. * @param {string} notice.status Status of the notice (error, success, warning, info). * @param {string} notice.content Content of the notice. */ const showNotice = ( { status, content } ) => { const id = `notice-${ Date.now() }`; content = <> { /* Notices don't have a DOM ID, so add a custom span which has one, for the fade-out and removal. */ } { content } ; noticeOperations.createNotice( { id, status, content, isDismissible: ( 'error' === status ) } ); if ( 'error' !== status ) { // Fade out non-error notices after 5 seconds and then remove them. setTimeout( () => { const notice = document.getElementById( id ).closest( '.components-notice' ); notice.addEventListener( 'transitionend', () => noticeOperations.removeNotice( id ) ); notice.style.opacity = 0; }, 5000 ); } else { // Scroll error notices into view. setTimeout( () => { const notice = document.getElementById( id ).closest( '.components-notice' ); if ( notice.getBoundingClientRect().bottom > ( window.innerHeight || document.documentElement.clientHeight ) ) { notice.scrollIntoView( { behavior: 'smooth', block: 'end', inline: 'nearest' } ); } }, 1 ); } }; // Put the screen into "is busy" mode. setBusyState( true ); document.body.classList.add( 'wait' ); // Save the table data to the server via an AJAX request. fetch( ajaxurl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json', }, body: buildQueryString( requestData ), } ) // Check for HTTP connection problems. .then( ( response ) => { if ( ! response.ok ) { throw new Error( sprintf( __( 'There was a problem with the server, HTTP response code %1$d (%2$s).', 'tablepress' ), response.status, response.statusText ) ); } return response.json(); } ) // Check for problems with the transmitted data. .then( ( data ) => { if ( 'undefined' === typeof data || null === data || '-1' === data || 'undefined' === typeof data.success ) { throw new Error( __( 'The JSON data returned from the server is unclear or incomplete.', 'tablepress' ) ); } if ( true !== data.success ) { const debugHtml = data.error_details ? `

${ __( 'These errors were encountered:', 'tablepress' ) }

${ data.error_details }
` : ''; throw new Error( `

${ __( 'There was a problem with the request.', 'tablepress' ) }

${ debugHtml }` ); } handleRequestSuccess( data ); } ) // Handle errors. .catch( ( error ) => { handleRequestError( error.message ); } ) // Clean up. .finally( () => { // Reset the screen from "is busy" mode. setBusyState( false ); document.body.classList.remove( 'wait' ); } ); /** * Handles a successful AJAX request. * * @param {Object} data Request response data. */ const handleRequestSuccess = ( data ) => { const { notice } = onSuccessfulRequest( data ); if ( notice ) { if ( 'snackbar' === notice.type && 'undefined' !== typeof noticesStoreDispatch ) { // Dispatch a Snackbar notice. noticesStoreDispatch.createSuccessNotice( notice.content, { type: 'snackbar', icon: , } ); } else { // Show a normal notice. showNotice( notice ); } } }; /** * Handles an error during the AJAX request. * * @param {string} message Error message. */ const handleRequestError = ( message ) => { message = __( 'Attention: Unfortunately, an error occurred.', 'tablepress' ) + ' ' + message + '
' + sprintf( __( 'Please see the TablePress FAQ page for suggestions.', 'tablepress' ), 'https://tablepress.org/faq/common-errors/' ); const notice = { status: 'error', content: message, }; showNotice( notice ); }; }; export default processAjaxRequest; react-loader.js000064400000003152150212644410007444 0ustar00/** * Common functions for loading React components in TablePress JS. * * @package TablePress * @subpackage Views JavaScript * @author Tobias Bäthge * @since 2.2.0 */ /** * WordPress dependencies. */ import { StrictMode } from 'react'; import { createPortal, createRoot } from 'react-dom'; import { addFilter } from '@wordpress/hooks'; /** * Initializes a React component on the page. * * @param {string} rootId HTML ID of the root element for the component. * @param {Component} Component JSX component. */ export const initializeReactComponent = ( rootId, Component ) => { if ( process.env.DEVELOP ) { Component = { Component }; } const root = document.getElementById( rootId ); if ( root ) { createRoot( root ).render( Component ); } }; /** * Initializes a React component on the page, in a React Portal, and registers its meta box. * * @param {string} slug Slug of the component/feature module. * @param {string} screen Slug/action of the screen. * @param {Component} Component JSX component. */ export const initializeReactComponentInPortal = ( slug, screen, Component ) => { addFilter( `tablepress.${screen}ScreenFeatures`, `tp/${slug}/${screen}-screen-feature`, ( features ) => ( [ ...features, slug ] ), ); addFilter( `tablepress.${screen}ScreenPortals`, `tp/${slug}/${screen}-screen-portal`, ( Portals ) => { return ( props ) => ( <> { createPortal( , document.getElementById( `tablepress-${slug}-section` ), ) } ); }, ); }; functions.js000064400000001256150212644410007115 0ustar00/** * Common functions that are used in TablePress JS. * * @package TablePress * @subpackage Views JavaScript * @author Tobias Bäthge * @since 2.0.0 */ /** * Alias for document.getElementById and document.querySelectorAll, depending on the first character of the passed selector string. Resembles jQuery. * * @param {string} selector Selector string. If it starts with #, a single ID is selected, all matching selectors otherwise. * @return {Element|NodeList} A single DOM Element or a DOM NodeList matching the selector. */ export const $ = ( selector ) => ( '#' === selector[0] ? document.getElementById( selector.slice( 1 ) ) : document.querySelectorAll( selector ) ); help.jsx000064400000005440150212644410006224 0ustar00/** * JavaScript code for the HelpBox and Help components. * * @package TablePress * @subpackage Edit Screen * @author Tobias Bäthge * @since 3.1.0 */ /** * WordPress dependencies. */ import { useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { Button, Icon, Modal, } from '@wordpress/components'; import { help } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; /** * Returns the HelpBox component's JSX markup. * * @param {Object} props Function parameters. * @param {string} props.title The title of the HelpBox. * @param {Object} props.buttonProps Additional props for the Button. * @param {Object} props.modalProps Additional props for the Modal. * @param {Object} props.children The Help content. * @return {Object} HelpBox component. */ export const HelpBox = ( { title, buttonProps= {}, modalProps = {}, children } ) => { const [ modalOpen, setModalOpen ] = useState( false ); const openModal = () => setModalOpen( true ); const closeModal = () => setModalOpen( false ); return ( <>