Files
RenewCMS/web/src/lib/editorOverflow.ts
T

119 lines
3.0 KiB
TypeScript

import { t } from '@/i18n'
import type { ArticleCapability, CoverageEntry } from '@/lib/articleTypes'
import { holds } from '@/lib/editorCapabilities'
import { unpublishLabel } from '@/lib/editorLabels'
export interface OverflowItem {
key: 'unpublish' | 'editSlug' | 'move' | 'deleteVersion' | 'deleteArticle'
label: string
icon: string
danger: boolean
disabled: boolean
reason: string
onSelect: () => void
}
export interface OverflowContext {
capabilities: ArticleCapability[]
siblings: CoverageEntry[]
language: string | null
unpublishInMenu: boolean
handlers: Partial<Record<OverflowItem['key'], () => void>>
}
function ignore() {
return undefined
}
function unavailable(key: OverflowItem['key'], context: OverflowContext): Partial<OverflowItem> {
return context.handlers[key] ? {} : { disabled: true, reason: t('Not available yet.') }
}
function item(
key: OverflowItem['key'],
label: string,
icon: string,
context: OverflowContext,
extra: Partial<OverflowItem> = {},
): OverflowItem {
return {
key,
label,
icon,
danger: false,
disabled: false,
reason: '',
onSelect: context.handlers[key] ?? ignore,
...unavailable(key, context),
...extra,
}
}
function deleteVersionLabel(language: string | null) {
return language ? t('Delete {0} version', [language]) : t('Delete article')
}
function blocksWholeArticle(context: OverflowContext) {
return context.siblings.some((sibling) => !holds(sibling.capabilities, 'delete'))
}
function blockedExtra(blocked: boolean, reason: string): Partial<OverflowItem> {
return blocked ? { disabled: true, reason } : {}
}
function editItems(context: OverflowContext): OverflowItem[] {
const blocked = blocksWholeArticle(context)
return [
item('editSlug', t('Edit slug'), 'i-lucide-link', context),
item(
'move',
t('Move article'),
'i-lucide-arrow-right-left',
context,
blockedExtra(blocked, t("You can't delete this article.")),
),
]
}
function deleteItems(context: OverflowContext): OverflowItem[] {
if (context.language && context.siblings.length === 0) {
return [
item('deleteArticle', t('Delete article'), 'i-lucide-trash-2', context, {
danger: true,
}),
]
}
const versionItem = item(
'deleteVersion',
deleteVersionLabel(context.language),
'i-lucide-trash-2',
context,
{ danger: true },
)
if (!context.language) {
return [versionItem]
}
const blocked = blocksWholeArticle(context)
return [
versionItem,
item('deleteArticle', t('Delete article'), 'i-lucide-trash-2', context, {
danger: !blocked,
...blockedExtra(blocked, t("You can't delete this article.")),
}),
]
}
export function overflowItems(context: OverflowContext): OverflowItem[] {
const items: OverflowItem[] = []
if (context.unpublishInMenu) {
items.push(item('unpublish', unpublishLabel(context.language), 'i-lucide-eye-off', context))
}
if (holds(context.capabilities, 'edit')) {
items.push(...editItems(context))
}
if (holds(context.capabilities, 'delete')) {
items.push(...deleteItems(context))
}
return items
}