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 / CommunityCloud / CloudSnippetsTable.tsx

CloudSnippetsTable.tsx in Code Snippets 4.0.0-beta.2, at js/components/ManageMenu/CommunityCloud/CloudSnippetsTable.tsx

187 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __, sprintf } from '@wordpress/i18n'
2 import React, { useState } from 'react'
3 import { getSnippetType, isCloudSnippetDownloadable } from '../../../utils/snippets/snippets'
4 import { stripTags, truncateChars } from '../../../utils/text'
5 import { Badge } from '../../common/Badge'
6 import { Button } from '../../common/Button'
7 import { CloudSnippetDownloadButton } from '../../common/cloud/CloudSnippetDownloadButton'
8 import { CloudStatusBadge } from '../../common/cloud/CloudStatusBadge'
9 import { CloudSnippetPreviewModal } from '../../common/snippets/SnippetPreviewModal'
10 import { useCloudSearch } from './WithCloudSearchContext'
11 import type { CloudSnippetSchema } from '../../../types/schema/CloudSnippetSchema'
12 import type { Dispatch, SetStateAction } from 'react'
13
14 interface CloudSnippetRowProps {
15 snippet: CloudSnippetSchema
16 }
17
18 const CloudSnippetRow: React.FC<CloudSnippetRowProps> = ({ snippet }) => {
19 const { doSearch } = useCloudSearch()
20 const [isPreviewOpen, setIsPreviewOpen] = useState(false)
21
22 return (
23 <>
24 <td className="column-name column-primary">
25 <strong>
26 <button
27 type="button"
28 className="cloud-table-name-button"
29 title={__('Preview this snippet', 'code-snippets')}
30 onClick={() => setIsPreviewOpen(true)}
31 >
32 {snippet.name}
33 </button>
34 </strong>
35 </td>
36
37 <td className="column-type"><Badge name={getSnippetType(snippet)} /></td>
38
39 <td className="column-status"><CloudStatusBadge status={snippet.status} /></td>
40
41 <td className="column-desc">
42 <div className="cloud-table-description">{truncateChars(stripTags(snippet.description))}</div>
43 </td>
44
45 <td className="column-actions">
46 <div className="cloud-snippet-action-buttons">
47 <Button secondary onClick={() => setIsPreviewOpen(true)}>
48 {__('Preview', 'code-snippets')}
49 </Button>
50
51 <CloudSnippetDownloadButton snippet={snippet} onDownloaded={doSearch} />
52 </div>
53
54 {isPreviewOpen && (
55 <CloudSnippetPreviewModal snippet={snippet} setIsOpen={setIsPreviewOpen} onDownloaded={doSearch} />)}
56 </td>
57 </>
58 )
59 }
60
61 interface TableHeadingCheckboxProps {
62 availableIds: CloudSnippetSchema['id'][]
63 selectedIds: Set<CloudSnippetSchema['id']>
64 setSelectedIds: Dispatch<SetStateAction<Set<CloudSnippetSchema['id']>>>
65 }
66
67 /**
68 * Selects every snippet on the page that can be downloaded. With nothing
69 * downloadable the box is disabled and unticked: "all of nothing" must not
70 * read as a selection.
71 */
72 const TableHeadingCheckbox: React.FC<TableHeadingCheckboxProps> = ({ availableIds, selectedIds, setSelectedIds }) =>
73 <td className="column-cb check-column">
74 <input
75 id="cb-select-all-cloud-snippets"
76 type="checkbox"
77 checked={0 < availableIds.length && availableIds.every(snippetId => selectedIds.has(snippetId))}
78 disabled={0 === availableIds.length}
79 title={0 === availableIds.length ? __('Nothing on this page can be downloaded.', 'code-snippets') : undefined}
80 onChange={event =>
81 setSelectedIds(previous =>
82 new Set(event.target.checked
83 ? [...previous, ...availableIds]
84 : [...previous].filter(snippetId => !availableIds.includes(snippetId)))
85 )}
86 aria-label={__('Select all downloadable snippets', 'code-snippets')}
87 />
88 </td>
89
90 /** Why a snippet cannot be selected for download, or undefined when it can. */
91 const unavailableReason = (snippet: CloudSnippetSchema): string | undefined => {
92 if (snippet.local_id) {
93 return __('Already in your library.', 'code-snippets')
94 }
95
96 return isCloudSnippetDownloadable(snippet)
97 ? undefined
98 : __('Requires Code Snippets Pro.', 'code-snippets')
99 }
100
101 interface TableRowCheckboxProps {
102 snippet: CloudSnippetSchema
103 selected: Set<CloudSnippetSchema['id']>
104 setSelected: Dispatch<SetStateAction<Set<CloudSnippetSchema['id']>>>
105 }
106
107 /**
108 * Every row shows a box, so the column reads as one control: a snippet that
109 * cannot be downloaded gets a disabled box that says why.
110 */
111 const TableRowCheckbox: React.FC<TableRowCheckboxProps> = ({ snippet, selected, setSelected }) => {
112 const reason = unavailableReason(snippet)
113
114 return (
115 <th scope="row" className="check-column">
116 <input
117 id={`cb-select-${snippet.id}`}
118 type="checkbox"
119 name="checked[]"
120 checked={!reason && selected.has(snippet.id)}
121 disabled={!!reason}
122 title={reason}
123 aria-label={reason
124 // translators: 1: snippet name, 2: why it cannot be selected.
125 ? sprintf(__('%1$s cannot be selected: %2$s', 'code-snippets'), snippet.name, reason)
126 // translators: %s: snippet name.
127 : sprintf(__('Select %s', 'code-snippets'), snippet.name)}
128 onChange={event =>
129 setSelected(previous =>
130 new Set(event.target.checked
131 ? [...previous, snippet.id]
132 : [...previous].filter(snippetId => snippetId !== snippet.id))
133 )}
134 />
135 </th>
136 )
137 }
138
139 export interface CloudSnippetsTableProps {
140 snippets: CloudSnippetSchema[]
141 selected?: Set<CloudSnippetSchema['id']>
142 setSelected?: Dispatch<SetStateAction<Set<CloudSnippetSchema['id']>>>
143 }
144
145 /**
146 * Table view for lists of cloud snippets (community search results and
147 * bundle contents), mirroring the card actions. Descriptions are clamped
148 * so all rows stay the same height. Selection is only offered when the
149 * containing view provides selection state, as bundle contents have no
150 * bulk actions.
151 */
152 export const CloudSnippetsTable: React.FC<CloudSnippetsTableProps> = ({
153 snippets,
154 selected,
155 setSelected
156 }) =>
157 <table className="wp-list-table widefat fixed striped cloud-snippets-table">
158 <thead>
159 <tr>
160 {selected && setSelected && (
161 <TableHeadingCheckbox
162 selectedIds={selected}
163 setSelectedIds={setSelected}
164 availableIds={snippets
165 .filter(snippet => isCloudSnippetDownloadable(snippet))
166 .map(snippet => snippet.id)}
167 />)}
168
169 <th scope="col" className="column-name column-primary">{__('Name', 'code-snippets')}</th>
170 <th scope="col" className="column-type">{__('Type', 'code-snippets')}</th>
171 <th scope="col" className="column-status">{__('Status', 'code-snippets')}</th>
172 <th scope="col" className="column-desc">{__('Description', 'code-snippets')}</th>
173 <th scope="col" className="column-actions">
174 <span className="screen-reader-text">{__('Actions', 'code-snippets')}</span>
175 </th>
176 </tr>
177 </thead>
178 <tbody>
179 {snippets.map(snippet =>
180 <tr key={snippet.id}>
181 {selected && setSelected && <TableRowCheckbox {...{ snippet, selected, setSelected }} />}
182 <CloudSnippetRow snippet={snippet} />
183 </tr>
184 )}
185 </tbody>
186 </table>
187