PluginProbe
Code Snippets / 3.10.0-beta.1
Code Snippets v3.10.0-beta.1
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 / ManageSnippetCard.tsx

ManageSnippetCard.tsx in Code Snippets 3.10.0-beta.1, at js/components/ManageMenu/SnippetsTable/ManageSnippetCard.tsx

234 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { humanTimeDiff } from '@wordpress/date'
2 import { RawHTML } from '@wordpress/element'
3 import { __, sprintf } from '@wordpress/i18n'
4 import React, { useState } from 'react'
5 import { useSnippetsAPI } from '../../../hooks/useSnippetsAPI'
6 import { useSnippetsList } from '../../../hooks/useSnippetsList'
7 import { handleUnknownError } from '../../../utils/errors'
8 import { downloadSnippetExportFile } from '../../../utils/files'
9 import { canModifySnippet, cloneSnippetObject, getSnippetDisplayName, getSnippetEditUrl, getSnippetType, isNetworkOnlySnippet, isSnippetActive } from '../../../utils/snippets/snippets'
10 import { Button } from '../../common/Button'
11 import { useDeleteSnippet } from '../../common/DeleteButton'
12 import { KebabMenu, KebabMenuDivider, KebabMenuItem, KebabMenuRow } from '../../common/KebabMenu'
13 import { SnippetCard } from '../../common/SnippetCard'
14 import { SnippetPreviewModal } from '../../common/SnippetPreviewModal'
15 import { SnippetPriorityInput } from '../../common/SnippetPriorityInput'
16 import { useFilteredSnippets } from './WithFilteredSnippetsContext'
17 import { ActivateColumn, SnippetExtraIcons, SnippetName, TagsColumn, TypeColumn } from './TableColumns'
18 import type { Snippet } from '../../../types/Snippet'
19
20 interface SnippetCardActionsProps {
21 snippet: Snippet
22 }
23
24 const CardPreviewButton: React.FC<SnippetCardActionsProps> = ({ snippet }) => {
25 const [isPreviewOpen, setIsPreviewOpen] = useState(false)
26
27 return (
28 <>
29 <Button secondary onClick={() => setIsPreviewOpen(true)}>
30 {__('Preview', 'code-snippets')}
31 </Button>
32
33 <SnippetPreviewModal
34 title={getSnippetDisplayName(snippet)}
35 code={snippet.code}
36 type={getSnippetType(snippet)}
37 isOpen={isPreviewOpen}
38 setIsOpen={setIsPreviewOpen}
39 snippet={snippet}
40 />
41 </>
42 )
43 }
44
45 const CloneExportMenuItems: React.FC<SnippetCardActionsProps> = ({ snippet }) => {
46 const api = useSnippetsAPI()
47 const { refreshSnippetsList } = useSnippetsList()
48
49 return (
50 <>
51 <KebabMenuItem
52 onSelect={() => {
53 api.create(cloneSnippetObject(snippet))
54 .then(refreshSnippetsList)
55 .catch(handleUnknownError)
56 }}
57 >
58 {__('Clone', 'code-snippets')}
59 </KebabMenuItem>
60
61 <KebabMenuItem
62 onSelect={() => {
63 api.export(snippet)
64 .then(response => downloadSnippetExportFile(response, snippet))
65 .catch(handleUnknownError)
66 }}
67 >
68 {__('Export', 'code-snippets')}
69 </KebabMenuItem>
70
71 <KebabMenuDivider />
72 </>
73 )
74 }
75
76 interface RestoreDeleteMenuItemsProps extends SnippetCardActionsProps {
77 requestDelete: () => void
78 }
79
80 const RestoreDeleteMenuItems: React.FC<RestoreDeleteMenuItemsProps> = ({
81 snippet,
82 requestDelete
83 }) => {
84 const api = useSnippetsAPI()
85 const { refreshSnippetsList } = useSnippetsList()
86
87 return (
88 <>
89 <KebabMenuDivider />
90
91 {snippet.trashed
92 ? <KebabMenuItem
93 onSelect={() => {
94 api.restore(snippet)
95 .then(refreshSnippetsList)
96 .catch(handleUnknownError)
97 }}
98 >
99 {__('Restore', 'code-snippets')}
100 </KebabMenuItem>
101 : null}
102
103 <KebabMenuItem destructive onSelect={requestDelete}>
104 {snippet.trashed ? __('Delete Permanently', 'code-snippets') : __('Trash', 'code-snippets')}
105 </KebabMenuItem>
106 </>
107 )
108 }
109
110 const CardActionsMenu: React.FC<SnippetCardActionsProps> = ({ snippet }) => {
111 const { refreshSnippetsList } = useSnippetsList()
112 const canModify = canModifySnippet(snippet)
113 const { requestDelete, confirmDialog } = useDeleteSnippet({
114 snippet,
115 onSuccess: refreshSnippetsList,
116 onError: handleUnknownError
117 })
118
119 return (
120 <>
121 <KebabMenu
122 label={sprintf(
123 /* translators: %s: name of the snippet. */
124 __('Actions for %s', 'code-snippets'),
125 getSnippetDisplayName(snippet)
126 )}
127 >
128 {canModify && !snippet.trashed ? <CloneExportMenuItems snippet={snippet} /> : null}
129
130 <KebabMenuRow className="kebab-menu-priority">
131 <label htmlFor={`snippet-${snippet.id}-priority`}>{__('Priority', 'code-snippets')}</label>
132 <SnippetPriorityInput snippet={snippet} />
133 </KebabMenuRow>
134
135 {canModify && (snippet.trashed || !snippet.locked)
136 ? <RestoreDeleteMenuItems snippet={snippet} requestDelete={requestDelete} />
137 : null}
138 </KebabMenu>
139
140 {confirmDialog}
141 </>
142 )
143 }
144
145 const CardFooterActions: React.FC<SnippetCardActionsProps> = ({ snippet }) =>
146 <>
147 {isNetworkOnlySnippet(snippet)
148 ? snippet.active
149 ? <span className="network-active">{__('Network Active', 'code-snippets')}</span>
150 : <span className="network-only">{__('Network Only', 'code-snippets')}</span>
151 : null}
152
153 {!snippet.trashed ? <CardPreviewButton snippet={snippet} /> : null}
154
155 {!snippet.trashed && canModifySnippet(snippet)
156 ? <a className="button button-primary" href={getSnippetEditUrl(snippet)}>
157 {snippet.locked ? __('View', 'code-snippets') : __('Edit', 'code-snippets')}
158 </a>
159 : null}
160
161 <CardActionsMenu snippet={snippet} />
162 </>
163
164 const CardModifiedDate: React.FC<SnippetCardActionsProps> = ({ snippet }) =>
165 snippet.modified
166 ? <time className="snippet-card-modified" dateTime={snippet.modified} title={snippet.modified}>
167 {sprintf(
168 /* translators: %s: human-readable time difference, including "ago" suffix. */
169 __('Modified %s', 'code-snippets'),
170 humanTimeDiff(snippet.modified, undefined)
171 )}
172 </time>
173 : null
174
175 export interface ManageSnippetCardProps {
176 snippet: Snippet
177 isSelected: boolean
178 onSelectedChange: (isSelected: boolean) => void
179 }
180
181 export const ManageSnippetCard: React.FC<ManageSnippetCardProps> = ({
182 snippet,
183 isSelected,
184 onSelectedChange
185 }) => {
186 const { activeByCondition } = useFilteredSnippets()
187
188 return (
189 <SnippetCard
190 className={[
191 'snippet',
192 `${isSnippetActive(snippet, activeByCondition) ? 'active' : 'inactive'}-snippet`,
193 `${getSnippetType(snippet)}-snippet`,
194 `${snippet.scope}-snippet`
195 ].join(' ')}
196 isSelected={isSelected}
197 onSelectedChange={onSelectedChange}
198 selectionLabel={sprintf(
199 /* translators: %s: name of the snippet. */
200 __('Select %s', 'code-snippets'),
201 getSnippetDisplayName(snippet)
202 )}
203 footer={<CardFooterActions snippet={snippet} />}
204 >
205 <div className="card-inner">
206 <div className="snippet-card-header">
207 <ActivateColumn snippet={snippet} />
208 <TypeColumn snippet={snippet} />
209 <h3><SnippetName snippet={snippet} /></h3>
210 <SnippetExtraIcons snippet={snippet} />
211 </div>
212
213 {0 < snippet.tags.length || snippet.modified
214 ? <div className="snippet-card-meta">
215 {0 < snippet.tags.length
216 ? <span className="snippet-card-tags">
217 <span className="snippet-card-tags-label">{__('Tags:', 'code-snippets')}</span>
218 {' '}
219 <TagsColumn snippet={snippet} />
220 </span>
221 : null}
222
223 <CardModifiedDate snippet={snippet} />
224 </div>
225 : null}
226
227 {snippet.desc
228 ? <div className="snippet-description-content"><RawHTML>{snippet.desc}</RawHTML></div>
229 : null}
230 </div>
231 </SnippetCard>
232 )
233 }
234