mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-15 07:34:42 -04:00
fix(cheatsheet): refactor cheatsheet to use app extensions as source
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
9d49401b4d
commit
24b0070909
53 changed files with 1164 additions and 275 deletions
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { Linter } from '../../components/editor-page/editor-pane/linter/linter'
|
||||
import type { MarkdownRendererExtension } from '../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import type { EventEmitter2 } from 'eventemitter2'
|
||||
|
@ -22,4 +23,8 @@ export abstract class AppExtension {
|
|||
public buildEditorExtensionComponent(): React.FC {
|
||||
return Fragment
|
||||
}
|
||||
|
||||
public buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { AbcjsMarkdownExtension } from './abcjs-markdown-extension'
|
||||
|
@ -11,4 +12,8 @@ export class AbcjsAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new AbcjsMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'abcjs', categoryI18nKey: 'charts', readMoreUrl: new URL('https://www.abcjs.net/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { AlertMarkdownExtension } from './alert-markdown-extension'
|
||||
|
@ -14,4 +15,8 @@ export class AlertAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new AlertMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'alert' }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { AsciinemaMarkdownExtension } from './asciinema-markdown-extension'
|
||||
|
@ -16,4 +17,8 @@ export class AsciinemaAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new AsciinemaMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'asciinema', categoryI18nKey: 'embedding', readMoreUrl: new URL('https://asciinema.org/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { BlockquoteExtraTagMarkdownExtension } from './blockquote-extra-tag-markdown-extension'
|
||||
|
@ -14,4 +15,8 @@ export class BlockquoteAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new BlockquoteExtraTagMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'blockquoteTags', entries: [{ i18nKey: 'name' }, { i18nKey: 'color' }, { i18nKey: 'time' }] }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { CsvTableMarkdownExtension } from './csv-table-markdown-extension'
|
||||
|
@ -14,4 +15,8 @@ export class CsvTableAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new CsvTableMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'csv', entries: [{ i18nKey: 'table' }, { i18nKey: 'header' }] }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { FlowchartMarkdownExtension } from './flowchart-markdown-extension'
|
||||
|
@ -14,4 +15,8 @@ export class FlowchartAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new FlowchartMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'flowchart', categoryI18nKey: 'charts', readMoreUrl: new URL('https://flowchart.js.org/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { GistMarkdownExtension } from './gist-markdown-extension'
|
||||
|
@ -14,4 +15,8 @@ export class GistAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new GistMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'gist', categoryI18nKey: 'embedding', readMoreUrl: new URL('https://gist.github.com/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { GraphvizMarkdownExtension } from './graphviz-markdown-extension'
|
||||
|
@ -14,4 +15,8 @@ export class GraphvizAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new GraphvizMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'graphviz', categoryI18nKey: 'charts', readMoreUrl: new URL('https://graphviz.org/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { HighlightedCodeMarkdownExtension } from './highlighted-code-markdown-extension'
|
||||
|
@ -14,4 +15,13 @@ export class HighlightedCodeFenceAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new HighlightedCodeMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [
|
||||
{
|
||||
i18nKey: 'codeHighlighting',
|
||||
entries: [{ i18nKey: 'language' }, { i18nKey: 'lineNumbers' }, { i18nKey: 'lineWrapping' }]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { KatexMarkdownExtension } from './katex-markdown-extension'
|
||||
|
@ -16,4 +17,8 @@ export class KatexAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new KatexMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'katex', readMoreUrl: new URL('https://katex.org/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { HtmlToReact } from '../../../components/common/html-to-react/html-to-react'
|
||||
import { testId } from '../../../utils/test-id'
|
||||
import convertHtmlToReact from '@hedgedoc/html-to-react'
|
||||
import { sanitize } from 'dompurify'
|
||||
import KaTeX from 'katex'
|
||||
import 'katex/dist/katex.min.css'
|
||||
import React, { useMemo } from 'react'
|
||||
|
@ -26,10 +25,12 @@ export const KatexFrame: React.FC<KatexFrameProps> = ({ expression, block = fals
|
|||
const dom = useMemo(() => {
|
||||
try {
|
||||
const katexHtml = KaTeX.renderToString(expression, {
|
||||
displayMode: block === true,
|
||||
displayMode: block,
|
||||
throwOnError: true
|
||||
})
|
||||
return convertHtmlToReact(sanitize(katexHtml, { ADD_TAGS: ['semantics', 'annotation'] }))
|
||||
return (
|
||||
<HtmlToReact htmlCode={katexHtml} domPurifyConfig={{ ADD_TAGS: ['semantics', 'annotation'] }}></HtmlToReact>
|
||||
)
|
||||
} catch (error) {
|
||||
return (
|
||||
<Alert className={block ? '' : 'd-inline-block'} variant={'danger'}>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { MermaidMarkdownExtension } from './mermaid-markdown-extension'
|
||||
|
@ -14,4 +15,8 @@ export class MermaidAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new MermaidMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'mermaid', categoryI18nKey: 'charts', readMoreUrl: new URL('https://mermaid.js.org/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { BasicMarkdownSyntaxAppExtension } from '../../components/markdown-renderer/extensions/basic-markdown-syntax/basic-markdown-syntax-app-extension'
|
||||
import { BootstrapIconAppExtension } from '../../components/markdown-renderer/extensions/bootstrap-icons/bootstrap-icon-app-extension'
|
||||
import { EmojiAppExtension } from '../../components/markdown-renderer/extensions/emoji/emoji-app-extension'
|
||||
import { IframeCapsuleAppExtension } from '../../components/markdown-renderer/extensions/iframe-capsule/iframe-capsule-app-extension'
|
||||
import { ImagePlaceholderAppExtension } from '../../components/markdown-renderer/extensions/image-placeholder/image-placeholder-app-extension'
|
||||
import { TableOfContentsAppExtension } from '../../components/markdown-renderer/extensions/table-of-contents/table-of-contents-app-extension'
|
||||
import type { AppExtension } from '../base/app-extension'
|
||||
import { AbcjsAppExtension } from './abcjs/abcjs-app-extension'
|
||||
import { AlertAppExtension } from './alert/alert-app-extension'
|
||||
|
@ -48,5 +54,11 @@ export const optionalAppExtensions: AppExtension[] = [
|
|||
new YoutubeAppExtension(),
|
||||
new TaskListCheckboxAppExtension(),
|
||||
new HighlightedCodeFenceAppExtension(),
|
||||
new ForkAwesomeHtmlTagAppExtension()
|
||||
new ForkAwesomeHtmlTagAppExtension(),
|
||||
new BootstrapIconAppExtension(),
|
||||
new EmojiAppExtension(),
|
||||
new TableOfContentsAppExtension(),
|
||||
new ImagePlaceholderAppExtension(),
|
||||
new IframeCapsuleAppExtension(),
|
||||
new BasicMarkdownSyntaxAppExtension()
|
||||
]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { PlantumlMarkdownExtension } from './plantuml-markdown-extension'
|
||||
|
@ -16,4 +17,8 @@ export class PlantumlAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new PlantumlMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'plantuml', categoryI18nKey: 'charts', readMoreUrl: new URL('https://plantuml.com/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { SpoilerMarkdownExtension } from './spoiler-markdown-extension'
|
||||
|
@ -16,4 +17,8 @@ export class SpoilerAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new SpoilerMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'spoiler' }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the markdown line prefix for a task list checkbox.
|
||||
*
|
||||
* @param state The check state of the checkbox.
|
||||
*/
|
||||
export const createCheckboxContent = (state: boolean) => {
|
||||
return `[${state ? 'x' : ' '}]`
|
||||
}
|
|
@ -13,7 +13,7 @@ type EventEmittingTaskListCheckboxProps = Omit<TaskListProps, 'onTaskCheckedChan
|
|||
|
||||
export interface TaskCheckedEventPayload {
|
||||
lineInMarkdown: number
|
||||
checked: boolean
|
||||
newCheckedState: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,10 @@ export const EventEmittingTaskListCheckbox: React.FC<EventEmittingTaskListCheckb
|
|||
const emitter = useExtensionEventEmitter()
|
||||
const sendEvent: TaskCheckedChangeHandler = useCallback(
|
||||
(lineInMarkdown: number, checked: boolean) => {
|
||||
emitter?.emit(TaskListCheckboxAppExtension.EVENT_NAME, { lineInMarkdown, checked } as TaskCheckedEventPayload)
|
||||
emitter?.emit(TaskListCheckboxAppExtension.EVENT_NAME, {
|
||||
lineInMarkdown,
|
||||
newCheckedState: checked
|
||||
} as TaskCheckedEventPayload)
|
||||
},
|
||||
[emitter]
|
||||
)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Optional } from '@mrdrogdrog/optional'
|
||||
|
||||
const TASK_REGEX = /^(\s*(?:[-*+]|\d+[.)]) )(\[[ xX]?])/
|
||||
|
||||
/**
|
||||
* Checks if the given markdown content contains a task list checkbox at the given line index.
|
||||
*
|
||||
* @param markdownContent The content that should be checked
|
||||
* @param lineIndex The index of the line that should be checked for a task list checkbox
|
||||
* @return An {@link Optional} that contains the start and end index of the found checkbox
|
||||
*/
|
||||
export const findCheckBox = (
|
||||
markdownContent: string,
|
||||
lineIndex: number
|
||||
): Optional<[startIndex: number, endIndex: number]> => {
|
||||
const lines = markdownContent.split('\n')
|
||||
const lineStartIndex = findStartIndexOfLine(lines, lineIndex)
|
||||
return Optional.ofNullable(TASK_REGEX.exec(lines[lineIndex])).map(([, beforeCheckbox, oldCheckbox]) => [
|
||||
lineStartIndex + beforeCheckbox.length,
|
||||
lineStartIndex + beforeCheckbox.length + oldCheckbox.length
|
||||
])
|
||||
}
|
||||
|
||||
const findStartIndexOfLine = (lines: string[], wantedLineIndex: number): number => {
|
||||
return lines
|
||||
.map((value) => value.length)
|
||||
.filter((value, index) => index < wantedLineIndex)
|
||||
.reduce((state, lineLength) => state + lineLength + 1, 0)
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtensionComponentProps } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import { useExtensionEventEmitterHandler } from '../../../components/markdown-renderer/hooks/use-extension-event-emitter'
|
||||
import { createCheckboxContent } from './create-checkbox-content'
|
||||
import type { TaskCheckedEventPayload } from './event-emitting-task-list-checkbox'
|
||||
import { findCheckBox } from './find-check-box'
|
||||
import { TaskListCheckboxAppExtension } from './task-list-checkbox-app-extension'
|
||||
import type React from 'react'
|
||||
|
||||
/**
|
||||
* Receives task-checkbox-change events and modify the current editor content.
|
||||
*/
|
||||
export const SetCheckboxInCheatsheet: React.FC<CheatsheetExtensionComponentProps> = ({ setContent }) => {
|
||||
useExtensionEventEmitterHandler(TaskListCheckboxAppExtension.EVENT_NAME, (event: TaskCheckedEventPayload) => {
|
||||
setContent((previousContent) => {
|
||||
return findCheckBox(previousContent, event.lineInMarkdown)
|
||||
.map(
|
||||
([startIndex, endIndex]) =>
|
||||
previousContent.slice(0, startIndex) +
|
||||
createCheckboxContent(event.newCheckedState) +
|
||||
previousContent.slice(endIndex)
|
||||
)
|
||||
.orElse(previousContent)
|
||||
})
|
||||
})
|
||||
return null
|
||||
}
|
|
@ -3,10 +3,16 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useChangeEditorContentCallback } from '../../../components/editor-page/change-content-context/use-change-editor-content-callback'
|
||||
import type { ContentEdits } from '../../../components/editor-page/editor-pane/tool-bar/formatters/types/changes'
|
||||
import { useExtensionEventEmitterHandler } from '../../../components/markdown-renderer/hooks/use-extension-event-emitter'
|
||||
import { store } from '../../../redux'
|
||||
import { createCheckboxContent } from './create-checkbox-content'
|
||||
import type { TaskCheckedEventPayload } from './event-emitting-task-list-checkbox'
|
||||
import { findCheckBox } from './find-check-box'
|
||||
import { TaskListCheckboxAppExtension } from './task-list-checkbox-app-extension'
|
||||
import { useSetCheckboxInEditor } from './use-set-checkbox-in-editor'
|
||||
import type React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
/**
|
||||
* Receives task-checkbox-change events and modify the current editor content.
|
||||
|
@ -16,3 +22,45 @@ export const SetCheckboxInEditor: React.FC = () => {
|
|||
useExtensionEventEmitterHandler(TaskListCheckboxAppExtension.EVENT_NAME, changeCallback)
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a callback that changes the state of a checkbox in a given line in the current codemirror instance.
|
||||
*/
|
||||
export const useSetCheckboxInEditor = () => {
|
||||
const changeEditorContent = useChangeEditorContentCallback()
|
||||
|
||||
return useCallback(
|
||||
({ lineInMarkdown, newCheckedState }: TaskCheckedEventPayload): void => {
|
||||
changeEditorContent?.(({ markdownContent }) => {
|
||||
const correctedLineIndex = lineInMarkdown + store.getState().noteDetails.frontmatterRendererInfo.lineOffset
|
||||
const edits = findCheckBox(markdownContent, correctedLineIndex)
|
||||
.map(([startIndex, endIndex]) => createCheckboxContentEdit(startIndex, endIndex, newCheckedState))
|
||||
.orElse([])
|
||||
return [edits, undefined]
|
||||
})
|
||||
},
|
||||
[changeEditorContent]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ContentEdits content edit} for the change of a checkbox at a given position.
|
||||
*
|
||||
* @param checkboxStartIndex The start index of the old checkbox code
|
||||
* @param checkboxEndIndex The end index of the old checkbox code
|
||||
* @param newCheckboxState The new status of the checkbox
|
||||
* @return the created {@link ContentEdits edit}
|
||||
*/
|
||||
const createCheckboxContentEdit = (
|
||||
checkboxStartIndex: number,
|
||||
checkboxEndIndex: number,
|
||||
newCheckboxState: boolean
|
||||
): ContentEdits => {
|
||||
return [
|
||||
{
|
||||
from: checkboxStartIndex,
|
||||
to: checkboxEndIndex,
|
||||
insert: createCheckboxContent(newCheckboxState)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { SetCheckboxInCheatsheet } from './set-checkbox-in-cheatsheet'
|
||||
import { SetCheckboxInEditor } from './set-checkbox-in-editor'
|
||||
import { TaskListMarkdownExtension } from './task-list-markdown-extension'
|
||||
import type { EventEmitter2 } from 'eventemitter2'
|
||||
|
@ -22,4 +24,8 @@ export class TaskListCheckboxAppExtension extends AppExtension {
|
|||
buildEditorExtensionComponent(): React.FC {
|
||||
return SetCheckboxInEditor
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'taskList', cheatsheetExtensionComponent: SetCheckboxInCheatsheet }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useChangeEditorContentCallback } from '../../../components/editor-page/change-content-context/use-change-editor-content-callback'
|
||||
import type { ContentEdits } from '../../../components/editor-page/editor-pane/tool-bar/formatters/types/changes'
|
||||
import { store } from '../../../redux'
|
||||
import type { TaskCheckedEventPayload } from './event-emitting-task-list-checkbox'
|
||||
import { Optional } from '@mrdrogdrog/optional'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
const TASK_REGEX = /(\s*(?:[-*+]|\d+[.)]) )(\[[ xX]?])/
|
||||
|
||||
/**
|
||||
* Provides a callback that changes the state of a checkbox in a given line in the current codemirror instance.
|
||||
*/
|
||||
export const useSetCheckboxInEditor = () => {
|
||||
const changeEditorContent = useChangeEditorContentCallback()
|
||||
|
||||
return useCallback(
|
||||
({ lineInMarkdown, checked }: TaskCheckedEventPayload): void => {
|
||||
changeEditorContent?.(({ markdownContent }) => {
|
||||
const lines = markdownContent.split('\n')
|
||||
const correctedLineIndex = lineInMarkdown + store.getState().noteDetails.frontmatterRendererInfo.lineOffset
|
||||
const lineStartIndex = findStartIndexOfLine(lines, correctedLineIndex)
|
||||
const edits = Optional.ofNullable(TASK_REGEX.exec(lines[correctedLineIndex]))
|
||||
.map(([, beforeCheckbox, oldCheckbox]) => {
|
||||
const checkboxStartIndex = lineStartIndex + beforeCheckbox.length
|
||||
return createCheckboxContentEdit(checkboxStartIndex, oldCheckbox, checked)
|
||||
})
|
||||
.orElse([])
|
||||
return [edits, undefined]
|
||||
})
|
||||
},
|
||||
[changeEditorContent]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the start position of the wanted line index if the given lines would be concat with new-line-characters.
|
||||
*
|
||||
* @param lines The lines to search through
|
||||
* @param wantedLineIndex The index of the line whose start position should be found
|
||||
* @return the found start position
|
||||
*/
|
||||
const findStartIndexOfLine = (lines: string[], wantedLineIndex: number): number => {
|
||||
return lines
|
||||
.map((value) => value.length)
|
||||
.filter((value, index) => index < wantedLineIndex)
|
||||
.reduce((state, lineLength) => state + lineLength + 1, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ContentEdits content edit} for the change of a checkbox at a given position.
|
||||
*
|
||||
* @param checkboxStartIndex The start index of the checkbox
|
||||
* @param oldCheckbox The old checkbox that should be replaced
|
||||
* @param newCheckboxState The new status of the checkbox
|
||||
* @return the created {@link ContentEdits edit}
|
||||
*/
|
||||
const createCheckboxContentEdit = (
|
||||
checkboxStartIndex: number,
|
||||
oldCheckbox: string,
|
||||
newCheckboxState: boolean
|
||||
): ContentEdits => {
|
||||
return [
|
||||
{
|
||||
from: checkboxStartIndex,
|
||||
to: checkboxStartIndex + oldCheckbox.length,
|
||||
insert: `[${newCheckboxState ? 'x' : ' '}]`
|
||||
}
|
||||
]
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
import { AppExtension } from '../../base/app-extension'
|
||||
import { VegaLiteMarkdownExtension } from './vega-lite-markdown-extension'
|
||||
|
@ -14,4 +15,10 @@ export class VegaLiteAppExtension extends AppExtension {
|
|||
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
|
||||
return [new VegaLiteMarkdownExtension()]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [
|
||||
{ i18nKey: 'vegaLite', categoryI18nKey: 'charts', readMoreUrl: new URL('https://vega.github.io/vega-lite/') }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { Linter } from '../../../components/editor-page/editor-pane/linter/linter'
|
||||
import { SingleLineRegexLinter } from '../../../components/editor-page/editor-pane/linter/single-line-regex-linter'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
|
@ -28,4 +29,8 @@ export class VimeoAppExtension extends AppExtension {
|
|||
)
|
||||
]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'vimeo', categoryI18nKey: 'embedding', readMoreUrl: new URL('https://vimeo.com/') }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension'
|
||||
import type { Linter } from '../../../components/editor-page/editor-pane/linter/linter'
|
||||
import { SingleLineRegexLinter } from '../../../components/editor-page/editor-pane/linter/single-line-regex-linter'
|
||||
import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
|
||||
|
@ -28,4 +29,8 @@ export class YoutubeAppExtension extends AppExtension {
|
|||
)
|
||||
]
|
||||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'youtube', categoryI18nKey: 'embedding', readMoreUrl: new URL('https://youtube.com/') }]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue