PluginProbe
Code Snippets / 4.0.0-beta.2
Code Snippets v4.0.0-beta.2
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 4.0.0-beta.2, at js/components/ManageMenu/SnippetsTable/ManageSnippetCard.tsx

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