feat: entry list at /content/:key
This commit is contained in:
1 parent
b4cbcb0395
commit
91d916fb74
44 files changed
+4984
-32
No files matched your search
@@ -0,0 +1,95 @@
|
||||
import { formatNumber, t } from '@/i18n'
|
||||
import type { BuiltinField } from '@/lib/contentTypes'
|
||||
import { formatAbsoluteDate, formatAbsoluteDateFromIso } from '@/lib/relativeDate'
|
||||
|
||||
export type CellKind = 'empty' | 'text' | 'richtext' | 'number' | 'boolean' | 'date' | 'choice'
|
||||
|
||||
export interface CellView {
|
||||
kind: CellKind
|
||||
text: string
|
||||
yes?: boolean
|
||||
}
|
||||
|
||||
export const EMPTY_TEXT = '–'
|
||||
|
||||
const EMPTY: CellView = { kind: 'empty', text: '' }
|
||||
const ISO_DAY = /^(\d{4})-(\d{2})-(\d{2})/
|
||||
const LINE_PREFIX = /^\s{0,3}(?:#{1,6}(?:\s+|$)|>\s?|[-*+]\s+|\d+[.)]\s+)+/
|
||||
const IMAGE_OR_LINK = /!?\[([^\]]*)\]\([^)]*\)/g
|
||||
const EMPHASIS = /(\*\*|__|\*|`|~~)/g
|
||||
|
||||
export function richTextFirstLine(markdown: string) {
|
||||
for (const line of markdown.split(/\r?\n/)) {
|
||||
const plain = line
|
||||
.replace(LINE_PREFIX, '')
|
||||
.replace(IMAGE_OR_LINK, '$1')
|
||||
.replace(EMPHASIS, '')
|
||||
.trim()
|
||||
if (plain) {
|
||||
return plain
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function formatDayValue(value: string) {
|
||||
const match = ISO_DAY.exec(value)
|
||||
if (!match) {
|
||||
return value
|
||||
}
|
||||
return formatAbsoluteDate(new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])))
|
||||
}
|
||||
|
||||
export function describeCell(field: BuiltinField, value: unknown): CellView {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return EMPTY
|
||||
}
|
||||
switch (field.type) {
|
||||
case 'boolean':
|
||||
return { kind: 'boolean', text: '', yes: value === true }
|
||||
case 'number':
|
||||
return typeof value === 'number'
|
||||
? { kind: 'number', text: formatNumber(value) }
|
||||
: { kind: 'number', text: String(value) }
|
||||
case 'date':
|
||||
return { kind: 'date', text: formatDayValue(String(value)) }
|
||||
case 'select':
|
||||
return { kind: 'choice', text: String(value) }
|
||||
case 'richtext': {
|
||||
const line = richTextFirstLine(String(value))
|
||||
return line ? { kind: 'richtext', text: line } : EMPTY
|
||||
}
|
||||
default:
|
||||
return { kind: 'text', text: String(value) }
|
||||
}
|
||||
}
|
||||
|
||||
export function formatEntryDate(iso: string) {
|
||||
return formatAbsoluteDateFromIso(iso)
|
||||
}
|
||||
|
||||
export function isEmptyCell(cell: CellView) {
|
||||
return cell.kind === 'empty'
|
||||
}
|
||||
|
||||
export function plainCellText(cell: CellView) {
|
||||
if (cell.kind === 'empty') {
|
||||
return EMPTY_TEXT
|
||||
}
|
||||
if (cell.kind === 'boolean') {
|
||||
return cell.yes ? t('Yes') : t('No')
|
||||
}
|
||||
return cell.text
|
||||
}
|
||||
|
||||
export interface CardFields {
|
||||
first?: BuiltinField
|
||||
second?: BuiltinField
|
||||
choice?: BuiltinField
|
||||
}
|
||||
|
||||
export function cardFields(fields: BuiltinField[]): CardFields {
|
||||
const [first, second] = fields
|
||||
const choice = fields.find((field) => field.type === 'select')
|
||||
return { first, second, choice: choice === first ? undefined : choice }
|
||||
}
|
||||
Reference in new issue
Block a user