PluginProbe
Code Snippets / trunk
Code Snippets vtrunk
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / js / components / ManageMenu / SnippetsTable / RowActions.tsx

RowActions.tsx in Code Snippets trunk, at js/components/ManageMenu/SnippetsTable/RowActions.tsx

197 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { Spinner } from '@wordpress/components'
2 import { __ } from '@wordpress/i18n'
3 import classnames from 'classnames'
4 import React, { useState } from 'react'
5 import { useSnippetsAPI } from '../../../hooks/useSnippetsAPI'
6 import { useSnippetsList } from '../../../hooks/useSnippetsList'
7 import { downloadSnippetExportFile } from '../../../utils/files'
8 import { isNetworkAdmin } from '../../../utils/screen'
9 import { cloneSnippetObject, getSnippetEditUrl } from '../../../utils/snippets/snippets'
10 import { Button } from '../../common/Button'
11 import { SnippetPreviewModal } from '../../common/snippets/SnippetPreviewModal'
12 import { ConfirmDeleteDialog, useDeleteSnippet } from '../../common/snippets/ConfirmDeleteDialog'
13 import type { ReactNode } from 'react'
14 import type { Snippet } from '../../../types/Snippet'
15
16 interface RowActionsProps {
17 snippet: Snippet
18 }
19
20 const PreviewButton: React.FC<RowActionsProps> = ({ snippet }) => {
21 const [isPreviewOpen, setIsPreviewOpen] = useState(false)
22
23 return (
24 <>
25 <Button link onClick={() => setIsPreviewOpen(true)}>
26 {__('Preview', 'code-snippets')}
27 </Button>
28
29 {isPreviewOpen && (
30 <SnippetPreviewModal
31 snippet={snippet}
32 setIsOpen={setIsPreviewOpen}
33 />)}
34 </>
35 )
36 }
37
38 interface SnippetActionButtonProps {
39 action: () => Promise<unknown>
40 label: ReactNode
41 workingLabel?: string
42 failedLabel?: string
43 className?: string
44 }
45
46 enum SnippetActionStatus { Ready, Working, Errored }
47
48 const SnippetActionButton: React.FC<SnippetActionButtonProps> = ({ action, label, workingLabel, failedLabel, className }) => {
49 const [status, setStatus] = useState(SnippetActionStatus.Ready)
50
51 return (
52 <Button
53 link
54 className={classnames(className, { 'is-busy': status === SnippetActionStatus.Working })}
55 disabled={status === SnippetActionStatus.Working}
56 onClick={() => {
57 switch (status) {
58 case SnippetActionStatus.Errored:
59 setStatus(SnippetActionStatus.Ready)
60 break
61
62 case SnippetActionStatus.Working:
63 break
64
65 case SnippetActionStatus.Ready: {
66 setStatus(SnippetActionStatus.Working)
67
68 action()
69 .then(() => setStatus(SnippetActionStatus.Ready))
70 .catch(() => {
71 setStatus(SnippetActionStatus.Errored)
72 })
73
74 break
75 }
76 }
77 }}
78 >
79 {(() => {
80 switch (status) {
81 case SnippetActionStatus.Working:
82 return (
83 <span className="snippet-row-action-feedback">
84 <Spinner /> {workingLabel ?? __('Loading…', 'code-snippets')}
85 </span>
86 )
87
88 case SnippetActionStatus.Errored:
89 return (
90 <span className="snippet-row-action-error">
91 <span className="dashicons dashicons-warning"></span>
92 {failedLabel ?? __('Failed', 'code-snippets')}
93 </span>
94 )
95
96 default:
97 return label
98 }
99 })()}
100 </Button>
101 )
102 }
103 const DeleteActionLink: React.FC<RowActionsProps> = ({ snippet }) => {
104 const { refreshSnippetsList } = useSnippetsList()
105 const { requestDelete, deleteDialogProps } = useDeleteSnippet({ snippet, onSuccess: refreshSnippetsList })
106
107 return (
108 <>
109 <SnippetActionButton
110 action={requestDelete}
111 className="delete"
112 label={snippet.trashed ? __('Delete Permanently', 'code-snippets') : __('Trash', 'code-snippets')}
113 workingLabel={snippet.trashed ? __('Deleting…', 'code-snippets') : __('Trashing…', 'code-snippets')}
114 failedLabel={__('Failed to delete', 'code-snippets')}
115 />
116
117 <ConfirmDeleteDialog {...deleteDialogProps} />
118 </>
119 )
120 }
121
122 const ActionLinks: React.FC<RowActionsProps> = ({ snippet }) => {
123 const api = useSnippetsAPI()
124 const { refreshSnippetsList } = useSnippetsList()
125
126 const previewButton = !snippet.trashed ? <PreviewButton snippet={snippet} /> : null
127
128 const editLink = !snippet.trashed
129 ? <a href={getSnippetEditUrl(snippet)}>
130 {snippet.locked ? __('View', 'code-snippets') : __('Edit', 'code-snippets')}
131 </a>
132 : null
133
134 const cloneButton = !snippet.trashed
135 ? <SnippetActionButton
136 label={__('Clone', 'code-snippets')}
137 workingLabel={__('Cloning…', 'code-snippets')}
138 action={() => api.create(cloneSnippetObject(snippet)).then(refreshSnippetsList)}
139 />
140 : null
141
142 const exportButton = !snippet.trashed
143 ? <SnippetActionButton
144 label={__('Export', 'code-snippets')}
145 workingLabel={__('Exporting…', 'code-snippets')}
146 action={() =>
147 api.export(snippet)
148 .then(response => downloadSnippetExportFile(response, snippet))}
149 />
150 : null
151
152 const restoreButton = snippet.trashed
153 ? <SnippetActionButton
154 label={__('Restore', 'code-snippets')}
155 workingLabel={__('Restoring…', 'code-snippets')}
156 action={() => api.restore(snippet).then(refreshSnippetsList)}
157 />
158 : null
159
160 const deleteButton = !snippet.locked || snippet.trashed
161 ? <DeleteActionLink snippet={snippet} />
162 : null
163
164 return (
165 <>
166 {[previewButton, editLink, cloneButton, restoreButton, exportButton, deleteButton]
167 .filter(action => action)
168 .reduce<ReactNode>(
169 (actions, action) =>
170 null === actions ? <>{action}</> : <>{actions} | {action}</>,
171 null)}
172 </>
173 )
174 }
175
176 export const RowActions: React.FC<RowActionsProps> = ({ snippet }) => {
177 if (!isNetworkAdmin() && snippet.network && !snippet.shared_network) {
178 return (
179 <div className="row-actions visible">
180 {snippet.active
181 ? <span className="network-active">{__('Network Active', 'code-snippets')}</span>
182 : <span className="network-only">{__('Network Only', 'code-snippets')}</span>}
183 </div>
184 )
185 }
186
187 if (snippet.shared_network && !window.CODE_SNIPPETS_MANAGE?.hasNetworkCap) {
188 return null
189 }
190
191 return (
192 <div className={classnames('row-actions', { visible: !snippet.trashed })}>
193 <ActionLinks snippet={snippet} />
194 </div>
195 )
196 }
197