PluginProbe
Code Snippets / 3.10.0
Code Snippets v3.10.0
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, at js/components/ManageMenu/SnippetsTable/ManageSnippetCard.tsx

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