| 1 |
import React, { Fragment } from 'react' |
| 2 |
import { __ } from '@wordpress/i18n' |
| 3 |
import { humanTimeDiff } from '@wordpress/date' |
| 4 |
import classnames from 'classnames' |
| 5 |
import { getSnippetDisplayName, getSnippetType } from '../../../utils/snippets/snippets' |
| 6 |
import { stripTags, truncateChars } from '../../../utils/text' |
| 7 |
import { Badge } from '../../common/Badge' |
| 8 |
import { Button } from '../../common/Button' |
| 9 |
import { CloudStatusBadge } from '../../common/cloud/CloudStatusBadge' |
| 10 |
import { Tooltip } from '../../common/Tooltip' |
| 11 |
import { CloudAvailableIcon } from '../../common/icons/cloud/CloudAvailableIcon' |
| 12 |
import { CloudSyncedIcon } from '../../common/icons/cloud/CloudSyncedIcon' |
| 13 |
import { FEATURED_SNIPPET_ID } from './demoCloudSnippets' |
| 14 |
import { hasReached } from './types' |
| 15 |
import type { DemoStage } from './types' |
| 16 |
import type { ListTableColumn } from '../../common/ListTable' |
| 17 |
import type { CloudSnippetSchema } from '../../../types/schema/CloudSnippetSchema' |
| 18 |
|
| 19 |
/** |
| 20 |
* Whether a row should render as already downloaded. Only the featured snippet |
| 21 |
* changes state, and only once the walkthrough has downloaded it. |
| 22 |
*/ |
| 23 |
const isSynced = (snippet: CloudSnippetSchema, stage: DemoStage): boolean => |
| 24 |
FEATURED_SNIPPET_ID === snippet.id && hasReached(stage, 'synced') |
| 25 |
|
| 26 |
const StateColumn: React.FC<{ snippet: CloudSnippetSchema, stage: DemoStage }> = ({ snippet, stage }) => { |
| 27 |
const synced = isSynced(snippet, stage) |
| 28 |
const label = synced |
| 29 |
? __('Synced to Cloud', 'code-snippets') |
| 30 |
: __('Available in Cloud', 'code-snippets') |
| 31 |
|
| 32 |
const Icon = () => |
| 33 |
<span className="cloud-sync-indicator" role="img" aria-label={label}> |
| 34 |
{synced ? <CloudSyncedIcon /> : <CloudAvailableIcon />} |
| 35 |
</span> |
| 36 |
|
| 37 |
return <Tooltip inline label={label} icon={<Icon />}>{label}</Tooltip> |
| 38 |
} |
| 39 |
|
| 40 |
const TagsColumn: React.FC<{ snippet: CloudSnippetSchema }> = ({ snippet }) => |
| 41 |
snippet.tags.map((tag, index) => |
| 42 |
<Fragment key={tag}>{tag}{index < snippet.tags.length - 1 ? ', ' : ''}</Fragment>) |
| 43 |
|
| 44 |
interface ActionsColumnProps { |
| 45 |
snippet: CloudSnippetSchema |
| 46 |
stage: DemoStage |
| 47 |
onPreview: (snippet: CloudSnippetSchema) => void |
| 48 |
} |
| 49 |
|
| 50 |
const ActionsColumn: React.FC<ActionsColumnProps> = ({ snippet, stage, onPreview }) => { |
| 51 |
const featured = FEATURED_SNIPPET_ID === snippet.id |
| 52 |
|
| 53 |
return ( |
| 54 |
<div className="cloud-snippet-action-buttons"> |
| 55 |
<Button |
| 56 |
secondary |
| 57 |
className={classnames('demo-preview-button', { 'demo-click': featured && 'preview' === stage })} |
| 58 |
onClick={() => onPreview(snippet)} |
| 59 |
> |
| 60 |
{__('Preview', 'code-snippets')} |
| 61 |
</Button> |
| 62 |
|
| 63 |
{isSynced(snippet, stage) |
| 64 |
? <Button secondary className="demo-action-button" aria-hidden="true" tabIndex={-1}> |
| 65 |
{__('Edit', 'code-snippets')} |
| 66 |
</Button> |
| 67 |
: <Button |
| 68 |
primary |
| 69 |
aria-hidden="true" |
| 70 |
tabIndex={-1} |
| 71 |
className={classnames('cloud-snippet-download demo-action-button', { |
| 72 |
'demo-click': featured && 'download' === stage |
| 73 |
})} |
| 74 |
> |
| 75 |
{__('Download', 'code-snippets')} |
| 76 |
</Button>} |
| 77 |
</div> |
| 78 |
) |
| 79 |
} |
| 80 |
|
| 81 |
interface ColumnParams { |
| 82 |
stage: DemoStage |
| 83 |
onPreview: (snippet: CloudSnippetSchema) => void |
| 84 |
} |
| 85 |
|
| 86 |
// eslint-disable-next-line max-lines-per-function -- the column set reads as one table definition. |
| 87 |
export const cloudLibraryDemoColumns = ( |
| 88 |
{ stage, onPreview }: ColumnParams |
| 89 |
): ListTableColumn<CloudSnippetSchema>[] => [ |
| 90 |
{ |
| 91 |
id: 'state', |
| 92 |
title: <CloudAvailableIcon role="img" aria-label={__('Sync Status', 'code-snippets')} />, |
| 93 |
render: snippet => <StateColumn snippet={snippet} stage={stage} /> |
| 94 |
}, |
| 95 |
{ |
| 96 |
id: 'name', |
| 97 |
title: __('Snippet Name', 'code-snippets'), |
| 98 |
isPrimary: true, |
| 99 |
sortedValue: snippet => getSnippetDisplayName(snippet).toLowerCase(), |
| 100 |
render: snippet => <strong>{snippet.name}</strong> |
| 101 |
}, |
| 102 |
{ |
| 103 |
id: 'type', |
| 104 |
title: __('Type', 'code-snippets'), |
| 105 |
sortedValue: snippet => getSnippetType(snippet), |
| 106 |
render: snippet => <Badge name={getSnippetType(snippet)} /> |
| 107 |
}, |
| 108 |
{ |
| 109 |
id: 'status', |
| 110 |
title: __('Status', 'code-snippets'), |
| 111 |
sortedValue: snippet => snippet.status, |
| 112 |
render: snippet => <CloudStatusBadge status={snippet.status} /> |
| 113 |
}, |
| 114 |
{ |
| 115 |
id: 'desc', |
| 116 |
title: __('Description', 'code-snippets'), |
| 117 |
render: snippet => |
| 118 |
<div className="snippet-description-content"> |
| 119 |
{truncateChars(stripTags(snippet.description))} |
| 120 |
</div> |
| 121 |
}, |
| 122 |
{ |
| 123 |
id: 'tags', |
| 124 |
title: __('Tags', 'code-snippets'), |
| 125 |
render: snippet => <TagsColumn snippet={snippet} /> |
| 126 |
}, |
| 127 |
{ |
| 128 |
id: 'date', |
| 129 |
title: __('Updated', 'code-snippets'), |
| 130 |
sortedValue: snippet => new Date(snippet.updated).toISOString(), |
| 131 |
render: snippet => |
| 132 |
<span className="modified-column-content" title={snippet.updated}> |
| 133 |
<time dateTime={snippet.updated}>{humanTimeDiff(snippet.updated, undefined)}</time> |
| 134 |
</span> |
| 135 |
}, |
| 136 |
{ |
| 137 |
id: 'actions', |
| 138 |
title: <span className="screen-reader-text">{__('Actions', 'code-snippets')}</span>, |
| 139 |
render: snippet => <ActionsColumn snippet={snippet} stage={stage} onPreview={onPreview} /> |
| 140 |
} |
| 141 |
] |
| 142 |
|