177 lines
8.5 KiB
TypeScript
177 lines
8.5 KiB
TypeScript
import { dom, icon, type NodeChildren, type Node, div, type Class } from "#shared/dom.util";
|
|
import { parseURL } from 'ufo';
|
|
import render from "#shared/markdown.util";
|
|
import { popper } from "#shared/floating.util";
|
|
import { Canvas } from "#shared/canvas.util";
|
|
import { Content, iconByType, type LocalContent } from "#shared/content.util";
|
|
import { parsePath, unifySlug } from "#shared/general.util";
|
|
import { async, floater, loading } from "./components.util";
|
|
|
|
|
|
export type CustomProse = (properties: any, children: NodeChildren) => Node;
|
|
export type Prose = { class: string } | { custom: CustomProse };
|
|
export const a: Prose = {
|
|
custom(properties, children) {
|
|
const href = decodeURIComponent(properties.href) as string;
|
|
const { hash, pathname } = parseURL(href);
|
|
const router = useRouter();
|
|
|
|
const overview = Content.getFromPath(pathname === '' && hash.length > 0 ? unifySlug(router.currentRoute.value.params.path ?? '') : pathname);
|
|
|
|
const link = overview ? { name: 'explore-path', params: { path: overview.path }, hash: hash } : href, nav = router.resolve(link);
|
|
|
|
const element = dom('a', { class: ['text-accent-blue inline-flex items-center', properties?.class], attributes: { href: nav.href }, listeners: {
|
|
'click': (e) => {
|
|
e.preventDefault();
|
|
router.push(link);
|
|
}
|
|
}}, [
|
|
dom('span', {}, [
|
|
...(children ?? []),
|
|
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
|
])
|
|
]);
|
|
|
|
return !!overview ? floater(element, () => [async('large', Content.getContent(overview.id).then((_content) => {
|
|
if(_content?.type === 'markdown')
|
|
{
|
|
return render((_content as LocalContent<'markdown'>).content ?? '', hash.length > 0 ? hash.substring(1) : undefined, { class: 'w-full max-h-full overflow-auto py-4 px-6' });
|
|
}
|
|
if(_content?.type === 'canvas')
|
|
{
|
|
const canvas = new Canvas((_content as LocalContent<'canvas'>).content);
|
|
queueMicrotask(() => canvas.mount());
|
|
return dom('div', { class: 'w-[600px] h-[600px] group-data-[pinned]:h-full group-data-[pinned]:w-full h-[600px] relative w-[600px] relative' }, [canvas.container]);
|
|
}
|
|
return div('');
|
|
})).current], { position: 'bottom-start', pinned: false }) : element;
|
|
}
|
|
}
|
|
export const preview: Prose = {
|
|
custom(properties: { href: string, class?: Class, size?: 'small' | 'large' }, children) {
|
|
const href = properties.href as string;
|
|
const { hash, pathname } = parseURL(href);
|
|
const router = useRouter();
|
|
|
|
const overview = Content.getFromPath(pathname === '' && hash.length > 0 ? unifySlug(router.currentRoute.value.params.path ?? '') : pathname);
|
|
|
|
const element = dom('span', { class: ['cursor-pointer text-accent-blue inline-flex items-center', properties?.class] }, [
|
|
...(children ?? []),
|
|
overview && overview.type !== 'markdown' ? icon(iconByType[overview.type], { class: 'w-4 h-4 inline-block', inline: true }) : undefined
|
|
]);
|
|
|
|
const magicKeys = useMagicKeys();
|
|
return !!overview ? floater(element, () => [async('large', Content.getContent(overview.id).then((_content) => {
|
|
if(_content?.type === 'markdown')
|
|
{
|
|
return render((_content as LocalContent<'markdown'>).content ?? '', hash.length > 0 ? hash.substring(1) : undefined, { class: 'w-full max-h-full overflow-auto py-4 px-6' });
|
|
}
|
|
if(_content?.type === 'canvas')
|
|
{
|
|
const canvas = new Canvas((_content as LocalContent<'canvas'>).content);
|
|
queueMicrotask(() => canvas.mount());
|
|
return dom('div', { class: 'w-[600px] h-[600px] group-data-[pinned]:h-full group-data-[pinned]:w-full h-[600px] relative w-[600px] relative' }, [canvas.container]);
|
|
}
|
|
return div('');
|
|
})).current], { position: 'bottom-start', pinned: false,
|
|
events: {
|
|
show: ['mouseenter', 'mousemove'],
|
|
hide: ['mouseleave'],
|
|
onshow() {
|
|
return !magicKeys.current.has('control') || !magicKeys.current.has('meta');
|
|
}
|
|
}, }) : element;
|
|
}
|
|
}
|
|
export const callout: Prose = {
|
|
custom(properties, children) {
|
|
const calloutIconByType: Record<string, string> = {
|
|
note: 'radix-icons:pencil-1',
|
|
abstract: 'radix-icons:file-text',
|
|
info: 'radix-icons:info-circled',
|
|
todo: 'radix-icons:check-circled',
|
|
tip: 'radix-icons:star',
|
|
success: 'radix-icons:check',
|
|
question: 'radix-icons:question-mark-circled',
|
|
warning: 'radix-icons:exclamation-triangle',
|
|
failure: 'radix-icons:cross-circled',
|
|
danger: 'radix-icons:circle-backslash',
|
|
bug: 'solar:bug-linear',
|
|
example: 'radix-icons:list-bullet',
|
|
quote: 'radix-icons:quote',
|
|
};
|
|
const defaultCalloutIcon = 'radix-icons:info-circled';
|
|
|
|
const { type, title, fold }: {
|
|
type: string;
|
|
title?: string;
|
|
fold?: boolean;
|
|
} = properties;
|
|
|
|
let open = fold;
|
|
const container = dom('div', { class: 'callout group overflow-hidden my-4 p-3 ps-4 bg-blend-lighten !bg-opacity-25 border-l-4 inline-block pe-8 bg-light-blue dark:bg-dark-blue', attributes: { 'data-state': fold !== false ? 'closed' : 'open', 'data-type': type } }, [
|
|
dom('div', { class: [{'cursor-pointer': fold !== undefined}, 'flex flex-row items-center justify-start ps-2'], listeners: { click: e => {
|
|
container.setAttribute('data-state', open ? 'open' : 'closed');
|
|
open = !open;
|
|
}}},
|
|
[icon(calloutIconByType[type] ?? defaultCalloutIcon, { inline: true, width: 24, height: 24, class: 'w-6 h-6 stroke-2 float-start me-2 flex-shrink-0' }), !!title ? dom('span', { class: 'block font-bold text-start', text: title }) : undefined, fold !== undefined ? icon('radix-icons:caret-right', { height: 24, width: 24, class: 'transition-transform group-data-[state=open]:rotate-90 w-6 h-6 mx-6' }) : undefined
|
|
]),
|
|
dom('div', { class: {'overflow-hidden': true, 'group-data-[state=closed]:animate-[collapseClose_0.2s_ease-in-out] group-data-[state=open]:animate-[collapseOpen_0.2s_ease-in-out] group-data-[state=closed]:h-0': fold !== undefined } }, [
|
|
dom('div', { class: 'px-2' }, children),
|
|
])
|
|
]);
|
|
|
|
return container;
|
|
},
|
|
}
|
|
export const tag: Prose = {
|
|
class: "before:content-['#'] cursor-default bg-accent-blue bg-opacity-10 hover:bg-opacity-20 text-accent-blue text-sm px-1 ms-1 pb-0.5 rounded-full rounded-se-none border border-accent-blue border-opacity-30",
|
|
}
|
|
export const blockquote: Prose = {
|
|
class: 'empty:before:hidden ps-4 my-4 relative before:absolute before:-top-1 before:-bottom-1 before:left-0 before:w-1 before:bg-light-30 dark:before:bg-dark-30',
|
|
}
|
|
export const h1: Prose = {
|
|
class: 'text-5xl font-thin mt-3 mb-8 first:pt-0 pt-2',
|
|
}
|
|
export const h2: Prose = {
|
|
class: 'text-4xl font-semibold mt-3 mb-6 ms-1 first:pt-0 pt-2',
|
|
}
|
|
export const h3: Prose = {
|
|
class: 'text-2xl font-bold mt-2 mb-4',
|
|
}
|
|
export const h4: Prose = {
|
|
class: 'text-xl font-semibold my-2',
|
|
}
|
|
export const h5: Prose = {
|
|
class: 'text-lg font-semibold my-1',
|
|
}
|
|
export const hr: Prose = {
|
|
class: 'border-b border-light-35 dark:border-dark-35 m-4',
|
|
}
|
|
export const li: Prose = {
|
|
class: 'before:absolute before:top-2 before:left-0 before:inline-block before:w-2 before:h-2 before:rounded before:bg-light-40 dark:before:bg-dark-40 relative ps-4',
|
|
}
|
|
export const small: Prose = {
|
|
class: 'text-light-60 dark:text-dark-60 text-sm italic',
|
|
}
|
|
export const table: Prose = {
|
|
class: 'mx-4 my-8 border-collapse border border-light-35 dark:border-dark-35',
|
|
}
|
|
export const td: Prose = {
|
|
class: 'border border-light-35 dark:border-dark-35 py-1 px-2',
|
|
}
|
|
export const th: Prose = {
|
|
class: 'border border-light-35 dark:border-dark-35 px-4 first:pt-0',
|
|
}
|
|
|
|
export default function(tag: string, prose: Prose, children?: NodeChildren, properties?: any): Node
|
|
{
|
|
if('class' in prose)
|
|
{
|
|
return dom(tag as keyof HTMLElementTagNameMap, { class: [properties?.class, prose.class] }, children ?? []);
|
|
}
|
|
else
|
|
{
|
|
return prose.custom(properties, children ?? []);
|
|
}
|
|
} |