mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-23 11:37:02 -04:00
Introduce Markdown extensions (#1614)
* Introduce markdown extensions Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
e9defd60dc
commit
8a8bacc0aa
148 changed files with 1878 additions and 1128 deletions
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
export interface TaskListProps {
|
||||
onTaskCheckedChange?: (lineInMarkdown: number, checked: boolean) => void
|
||||
checked: boolean
|
||||
lineInMarkdown?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a task list checkbox.
|
||||
*
|
||||
* @param onTaskCheckedChange A callback that is executed if the checkbox was clicked. If this prop is omitted then the checkbox will be disabled.
|
||||
* @param checked Determines if the checkbox should be rendered as checked
|
||||
* @param lineInMarkdown Defines the line in the markdown code this checkbox is mapped to. The information is send with the onTaskCheckedChange callback.
|
||||
*/
|
||||
export const TaskListCheckbox: React.FC<TaskListProps> = ({ onTaskCheckedChange, checked, lineInMarkdown }) => {
|
||||
const onChange = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
if (onTaskCheckedChange && lineInMarkdown !== undefined) {
|
||||
onTaskCheckedChange(lineInMarkdown, event.currentTarget.checked)
|
||||
}
|
||||
},
|
||||
[lineInMarkdown, onTaskCheckedChange]
|
||||
)
|
||||
|
||||
return (
|
||||
<input
|
||||
disabled={onTaskCheckedChange === undefined}
|
||||
className='task-list-item-checkbox'
|
||||
type='checkbox'
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { MarkdownExtension } from '../markdown-extension'
|
||||
import type MarkdownIt from 'markdown-it'
|
||||
import type { ComponentReplacer } from '../../replace-components/component-replacer'
|
||||
import type { TaskCheckedChangeHandler } from './task-list-replacer'
|
||||
import { TaskListReplacer } from './task-list-replacer'
|
||||
import markdownItTaskLists from '@hedgedoc/markdown-it-task-lists'
|
||||
|
||||
/**
|
||||
* Adds support for interactive checkbox lists to the markdown rendering using the github checklist syntax.
|
||||
*/
|
||||
export class TaskListMarkdownExtension extends MarkdownExtension {
|
||||
constructor(private frontmatterLinesToSkip?: number, private onTaskCheckedChange?: TaskCheckedChangeHandler) {
|
||||
super()
|
||||
}
|
||||
|
||||
public configureMarkdownIt(markdownIt: MarkdownIt): void {
|
||||
markdownItTaskLists(markdownIt, {
|
||||
enabled: true,
|
||||
label: true,
|
||||
lineNumber: true
|
||||
})
|
||||
}
|
||||
|
||||
public buildReplacers(): ComponentReplacer[] {
|
||||
return [new TaskListReplacer(this.frontmatterLinesToSkip, this.onTaskCheckedChange)]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { Element } from 'domhandler'
|
||||
import type { ReactElement } from 'react'
|
||||
import React from 'react'
|
||||
import { ComponentReplacer } from '../../replace-components/component-replacer'
|
||||
import { TaskListCheckbox } from './task-list-checkbox'
|
||||
|
||||
export type TaskCheckedChangeHandler = (lineInMarkdown: number, checked: boolean) => void
|
||||
|
||||
/**
|
||||
* Detects task lists and renders them as checkboxes that execute a callback if clicked.
|
||||
*/
|
||||
export class TaskListReplacer extends ComponentReplacer {
|
||||
onTaskCheckedChange?: (lineInMarkdown: number, checked: boolean) => void
|
||||
|
||||
constructor(frontmatterLinesToSkip?: number, onTaskCheckedChange?: TaskCheckedChangeHandler) {
|
||||
super()
|
||||
this.onTaskCheckedChange = (lineInMarkdown, checked) => {
|
||||
if (onTaskCheckedChange === undefined || frontmatterLinesToSkip === undefined) {
|
||||
return
|
||||
}
|
||||
onTaskCheckedChange(frontmatterLinesToSkip + lineInMarkdown, checked)
|
||||
}
|
||||
}
|
||||
|
||||
public replace(node: Element): ReactElement | undefined {
|
||||
if (node.attribs?.class !== 'task-list-item-checkbox') {
|
||||
return
|
||||
}
|
||||
const lineInMarkdown = Number(node.attribs['data-line'])
|
||||
if (isNaN(lineInMarkdown)) {
|
||||
return undefined
|
||||
}
|
||||
return (
|
||||
<TaskListCheckbox
|
||||
onTaskCheckedChange={this.onTaskCheckedChange}
|
||||
checked={node.attribs.checked !== undefined}
|
||||
lineInMarkdown={lineInMarkdown}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue