PluginProbe
TablePress – Tables in WordPress made easy / 3.3
TablePress – Tables in WordPress made easy v3.3
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
tablepress / admin / js / list / screen.jsx

screen.jsx in TablePress – Tables in WordPress made easy 3.3, at admin/js/list/screen.jsx

288 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * JavaScript code for the "List of Tables Screen" component.
3 *
4 * @package TablePress
5 * @subpackage List of Tables Screen
6 * @author Tobias Bäthge
7 * @since 3.1.0
8 */
9
10 /**
11 * WordPress dependencies.
12 */
13 import { useCallback, useEffect, useRef, useState } from 'react';
14 import {
15 Button,
16 Icon,
17 __experimentalHStack as HStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis
18 __experimentalInputControl as InputControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis
19 __experimentalInputControlSuffixWrapper as InputControlSuffixWrapper, // eslint-disable-line @wordpress/no-unsafe-wp-apis
20 Modal,
21 __experimentalVStack as VStack, // eslint-disable-line @wordpress/no-unsafe-wp-apis
22 } from '@wordpress/components';
23 import { useCopyToClipboard } from '@wordpress/compose';
24 import { useDispatch } from '@wordpress/data';
25 import { copySmall, external } from '@wordpress/icons';
26 import { __, _n } from '@wordpress/i18n';
27 import { store as noticesStore } from '@wordpress/notices';
28
29 /**
30 * Internal dependencies.
31 */
32 import { TablePressIcon, TablePressIconSimple } from '../../img/tablepress-icon';
33 import { Notifications } from '../common/notifications';
34
35 /**
36 * Returns the Table Preview component's JSX markup.
37 *
38 * @param {Object} props Component props.
39 * @param {string} props.title Title of the preview.
40 * @param {string} props.url URL of the preview.
41 * @param {Function} props.onRequestClose Callback to close the preview.
42 * @return {Object} Table Preview component.
43 */
44 const TablePreview = ( { title, url, onRequestClose } ) => {
45 return (
46 <Modal
47 icon={ <Icon icon={ TablePressIcon } size="36" style={ { display: 'flex', marginRight: '1rem' } } /> }
48 title={ title }
49 className="table-preview-modal"
50 onRequestClose={ onRequestClose }
51 size="fill"
52 headerActions={
53 <Button
54 icon={ external }
55 size="compact"
56 label={ __( 'Open the preview in a new tab', 'tablepress' ) }
57 href={ url }
58 target="_blank"
59 />
60 }
61 >
62 <iframe
63 title={ title }
64 src={ url }
65 />
66 </Modal>
67 );
68 };
69
70 /**
71 * Returns the Shortcode Modal component's JSX markup.
72 *
73 * @param {Object} props Component props.
74 * @param {Object} props.shortcode Table Shortcode.
75 * @param {Function} props.onRequestClose Callback to close the Shortcode Modal.
76 * @return {Object} Shortcode Modal component.
77 */
78 const ShortcodeModal = ( { shortcode, onRequestClose } ) => {
79 const { createSuccessNotice } = useDispatch( noticesStore );
80 const copyShortcodeButtonRef = useCopyToClipboard( shortcode, () => {
81 onRequestClose();
82 createSuccessNotice(
83 __( 'Copied Shortcode to clipboard.', 'tablepress' ),
84 {
85 type: 'snackbar',
86 icon: <Icon icon={ TablePressIconSimple } />,
87 }
88 );
89 } );
90
91 return (
92 <Modal
93 icon={ <Icon icon={ TablePressIcon } size="36" style={ { display: 'flex', marginRight: '1rem' } } /> }
94 title={ __( 'Table Shortcode', 'tablepress' ) }
95 onRequestClose={ onRequestClose }
96 >
97 <VStack>
98 <span>
99 { __( 'To embed this table into a post or page, use this Shortcode:', 'tablepress' ) }
100 </span>
101 <InputControl
102 __next40pxDefaultSize
103 type="text"
104 id="table-information-shortcode"
105 value={ shortcode }
106 onFocus={ ( event ) => event.target.select() }
107 readOnly={ true }
108 suffix={
109 <InputControlSuffixWrapper variant="control">
110 <Button
111 icon={ copySmall }
112 ref={ copyShortcodeButtonRef }
113 size="small"
114 label={ __( 'Copy Shortcode to clipboard', 'tablepress' ) }
115 />
116 </InputControlSuffixWrapper>
117 }
118 />
119 </VStack>
120 </Modal>
121 );
122 };
123
124 /**
125 * Returns the Confirm Delete Modal component's JSX markup.
126 *
127 * @param {Object} props Component props.
128 * @param {string} props.title Title of the modal.
129 * @param {string} props.deleteUrl URL to delete the table.
130 * @param {Function} props.closeConfirmDeleteModal Callback to close the Confirm Delete modal.
131 * @return {Object} Confirm Delete Modal component.
132 */
133 const ConfirmDeleteModal = ( { title, deleteUrl, closeConfirmDeleteModal } ) => {
134 const cancelButtonRef = useRef();
135 const deleteButtonRef = useRef();
136
137 const handleEnter = useCallback(
138 ( event ) => {
139 // Avoid triggering the action when a button is focused, as this can cause a double submission.
140 const isCancelOrDeleteButton =
141 event.target === cancelButtonRef.current ||
142 event.target === deleteButtonRef.current;
143
144 if ( ! isCancelOrDeleteButton && 'Enter' === event.key ) {
145 deleteButtonRef.current.click();
146 }
147 },
148 [],
149 );
150
151 return (
152 <Modal
153 size="medium"
154 icon={ <Icon icon={ TablePressIcon } size="36" style={ { display: 'flex', marginRight: '1rem' } } /> }
155 title={ title }
156 isDismissible={ false }
157 onKeyDown={ handleEnter }
158 onRequestClose={ closeConfirmDeleteModal }
159 >
160 <VStack spacing={ 8 }>
161 <span>
162 { _n( 'Do you really want to delete this table?', 'Do you really want to delete these tables?', 1, 'tablepress' ) }
163 <br />
164 { __( 'Deleting a table is permanent and can not be undone!', 'tablepress' ) }
165 </span>
166 <HStack alignment="right">
167 <Button
168 ref={ cancelButtonRef }
169 variant="tertiary"
170 text={ __( 'Cancel', 'tablepress' ) }
171 onClick={ closeConfirmDeleteModal }
172 />
173 <Button
174 ref={ deleteButtonRef }
175 variant="primary"
176 isDestructive={ true }
177 href={ deleteUrl }
178 text={ __( 'Delete', 'tablepress' ) }
179 onClick={ closeConfirmDeleteModal }
180 />
181 </HStack>
182 </VStack>
183 </Modal>
184 );
185 };
186
187 /**
188 * Returns the "List of Tables Screen" component's JSX markup.
189 *
190 * @return {Object} List of Tables Screen component.
191 */
192 const Screen = () => {
193 const [ screenData, setScreenData ] = useState( {
194 previewIsOpen: false,
195 previewUrl: '',
196 previewTitle: '',
197 shortcode: '',
198 shortcodeModalIsOpen: false,
199 } );
200
201 const updateScreenData = ( updatedScreenData ) => {
202 // Use an updater function to ensure that the current state is used when updating screen data.
203 setScreenData( ( currentScreenData ) => ( {
204 ...currentScreenData,
205 ...updatedScreenData,
206 } ) );
207 };
208
209 useEffect( () => {
210 const handleClick = ( event ) => {
211 if ( ! event.target ) {
212 return;
213 }
214
215 // Open the Preview Modal if the link is clicked while no modifier key is pressed.
216 if ( event.target.matches( '.table-preview a' ) && ! ( event.ctrlKey || event.metaKey || event.shiftKey ) ) {
217 updateScreenData( {
218 previewIsOpen: true,
219 previewUrl: event.target.href,
220 previewTitle: event.target.title,
221 } );
222 event.preventDefault();
223 return;
224 }
225
226 /**
227 * Show a Modal with the Shortcode.
228 */
229 if ( event.target.matches( '.shortcode a' ) ) {
230 updateScreenData( {
231 shortcode: event.target.title,
232 shortcodeModalIsOpen: true,
233 } );
234 event.preventDefault();
235 return;
236 }
237
238 /**
239 * Show a Confirm Delete Modal.
240 */
241 if ( event.target.matches( '.delete a' ) ) {
242 updateScreenData( {
243 confirmDeleteUrl: event.target.href,
244 confirmDeleteTitle: event.target.title,
245 confirmDeleteModalIsOpen: true,
246 } );
247 event.preventDefault();
248 return;
249 }
250 };
251
252 document.querySelector( '.tablepress-all-tables' ).addEventListener( 'click', handleClick );
253
254 // Clean-up function for the effect.
255 return () => {
256 document.querySelector( '.tablepress-all-tables' ).removeEventListener( 'click', handleClick );
257 };
258 }, [] ); // eslint-disable-line react-hooks/exhaustive-deps -- This should only run on the initial render, so no dependencies are needed.
259
260 return (
261 <>
262 { screenData.previewIsOpen && (
263 <TablePreview
264 title={ screenData.previewTitle }
265 url={ screenData.previewUrl }
266 onRequestClose={ () => updateScreenData( { previewIsOpen: false } ) }
267 />
268 ) }
269 { screenData.shortcodeModalIsOpen && (
270 <ShortcodeModal
271 shortcode={ screenData.shortcode }
272 onRequestClose={ () => updateScreenData( { shortcodeModalIsOpen: false } ) }
273 />
274 ) }
275 { screenData.confirmDeleteModalIsOpen && (
276 <ConfirmDeleteModal
277 title={ screenData.confirmDeleteTitle }
278 deleteUrl={ screenData.confirmDeleteUrl }
279 closeConfirmDeleteModal={ () => updateScreenData( { confirmDeleteModalIsOpen: false } ) }
280 />
281 ) }
282 <Notifications />
283 </>
284 );
285 };
286
287 export default Screen;
288