docs: consolidate docs (#2182)

Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Philip Molares 2022-07-21 22:36:46 +02:00 committed by GitHub
parent 8d46d7e39e
commit ecffebc43c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
307 changed files with 1474 additions and 487 deletions

View file

@ -1,8 +1,11 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
/**
* Custom {@link Error} class for the {@link ApplicationLoader}.
*/
export class ApplicationLoaderError extends Error {
constructor(taskName: string) {
super(`The task ${taskName} failed`)

View file

@ -14,7 +14,13 @@ import { ApplicationLoaderError } from './application-loader-error'
const log = new Logger('ApplicationLoader')
export const ApplicationLoader: React.FC<PropsWithChildren<unknown>> = ({ children }) => {
/**
* Initializes the application and executes all the setup tasks.
* It renders a {@link LoadingScreen} while this is happening. If there are any error, they will be displayed in the {@link LoadingScreen}.
*
* @param children The children in the React dom that should be shown once the application is loaded.
*/
export const ApplicationLoader: React.FC<PropsWithChildren> = ({ children }) => {
const { error, loading } = useAsync(async () => {
const initTasks = createSetUpTaskList()
for (const task of initTasks) {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -7,6 +7,9 @@
import { getConfig } from '../../../api/config'
import { setConfig } from '../../../redux/config/methods'
/**
* Get the {@link Config frontend config} and save it in the global application state.
*/
export const fetchFrontendConfig = async (): Promise<void> => {
const config = await getConfig()
if (!config) {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -12,6 +12,9 @@ import { fetchFrontendConfig } from './fetch-frontend-config'
import { loadDarkMode } from './load-dark-mode'
import { isDevMode, isTestMode } from '../../../utils/test-modes'
/**
* Create a custom delay in the loading of the application.
*/
const customDelay: () => Promise<void> = async () => {
if (
(isDevMode || isTestMode) &&
@ -30,6 +33,9 @@ export interface InitTask {
task: () => Promise<void>
}
/**
* Create a list of tasks, that need to be fulfilled on startup.
*/
export const createSetUpTaskList = (): InitTask[] => {
return [
{

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -31,7 +31,8 @@ export const loadDarkMode = async (): Promise<void> => {
/**
* Tries to read the saved dark mode settings from the browser local storage.
*
* @return {@code true} if the local storage has saved that the user prefers dark mode. {@code false} if the user doesn't or if the value could be read from local storage.
* @return {@link true} if the local storage has saved that the user prefers dark mode.
* {@link false} if the user doesn't prefer dark mode or if the value couldn't be read from local storage.
*/
const fetchDarkModeFromLocalStorage = (): boolean => {
if (!isClientSideRendering()) {
@ -48,7 +49,9 @@ const fetchDarkModeFromLocalStorage = (): boolean => {
/**
* Tries to read the preferred dark mode setting from the browser settings.
*
* @return {@code true} if the browser has reported that the user prefers dark mode. {@code false} if the user doesn't or if the browser doesn't support the `prefers-color-scheme` media query.
* @return {@link true} if the browser has reported that the user prefers dark mode.
* {@link false} if the browser doesn't prefer dark mode.
* {@link undefined} if the browser doesn't support the `prefers-color-scheme` media query.
*/
const determineDarkModeBrowserSettings = (): DarkModeConfig | undefined => {
if (!isClientSideRendering()) {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -12,6 +12,9 @@ import { Settings } from 'luxon'
import { initReactI18next } from 'react-i18next'
import { isDevMode } from '../../../utils/test-modes'
/**
* Set up the internationalisation framework i18n.
*/
export const setUpI18n = async (): Promise<void> => {
await i18nUse(
resourcesToBackend((language, namespace, callback) => {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -14,6 +14,13 @@ export interface BrandingProps {
delimiter?: boolean
}
/**
* Show the branding of the HedgeDoc instance.
* This branding can either be a text, a logo or both (in that case the text is used as the title and alt of the image).
*
* @param inline If the logo should be using the inline-size or the regular-size css class.
* @param delimiter If the delimiter between the HedgeDoc logo and the branding should be shown.
*/
export const Branding: React.FC<BrandingProps> = ({ inline = false, delimiter = true }) => {
const branding = useApplicationState((state) => state.config.branding)
const showBranding = !!branding.name || !!branding.logo

View file

@ -1,91 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Cache } from './cache'
describe('Test caching functionality', () => {
let testCache: Cache<string, number>
beforeAll(() => {
jest.useFakeTimers()
})
afterAll(() => {
jest.useRealTimers()
})
beforeEach(() => {
testCache = new Cache<string, number>(1000)
})
it('initialize with right lifetime, no entry limit', () => {
const lifetime = 1000
const lifetimedCache = new Cache<string, string>(lifetime)
expect(lifetimedCache.entryLifetime).toEqual(lifetime)
expect(lifetimedCache.maxEntries).toEqual(0)
})
it('initialize with right lifetime, given entry limit', () => {
const lifetime = 1000
const maxEntries = 10
const limitedCache = new Cache<string, string>(lifetime, maxEntries)
expect(limitedCache.entryLifetime).toEqual(lifetime)
expect(limitedCache.maxEntries).toEqual(maxEntries)
})
it('entry exists after inserting', () => {
testCache.put('test', 123)
expect(testCache.has('test')).toBe(true)
})
it('entry does not exist prior inserting', () => {
expect(testCache.has('test')).toBe(false)
})
it('entry does expire', () => {
const shortLivingCache = new Cache<string, number>(2)
shortLivingCache.put('test', 123)
expect(shortLivingCache.has('test')).toBe(true)
setTimeout(() => {
expect(shortLivingCache.has('test')).toBe(false)
}, 2000)
})
it('entry value does not change', () => {
const testValue = Date.now()
testCache.put('test', testValue)
expect(testCache.get('test')).toEqual(testValue)
})
it('error is thrown on non-existent entry', () => {
const accessNonExistentEntry = () => {
testCache.get('test')
}
expect(accessNonExistentEntry).toThrow(Error)
})
it('newer item replaces older item', () => {
testCache.put('test', 123)
testCache.put('test', 456)
expect(testCache.get('test')).toEqual(456)
})
it('entry limit is respected', () => {
const limitedCache = new Cache<string, number>(1000, 2)
limitedCache.put('first', 1)
expect(limitedCache.has('first')).toBe(true)
expect(limitedCache.has('second')).toBe(false)
expect(limitedCache.has('third')).toBe(false)
limitedCache.put('second', 2)
expect(limitedCache.has('first')).toBe(true)
expect(limitedCache.has('second')).toBe(true)
expect(limitedCache.has('third')).toBe(false)
limitedCache.put('third', 3)
expect(limitedCache.has('first')).toBe(false)
expect(limitedCache.has('second')).toBe(true)
expect(limitedCache.has('third')).toBe(true)
})
})

View file

@ -1,50 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
export interface CacheEntry<T> {
entryCreated: number
data: T
}
export class Cache<K, V> {
readonly entryLifetime: number
readonly maxEntries: number
private store = new Map<K, CacheEntry<V>>()
constructor(lifetime: number, maxEntries = 0) {
if (lifetime < 0) {
throw new Error('Cache entry lifetime can not be less than 0 seconds.')
}
this.entryLifetime = lifetime
this.maxEntries = maxEntries
}
has(key: K): boolean {
if (!this.store.has(key)) {
return false
}
const entry = this.store.get(key)
return !!entry && entry.entryCreated >= Date.now() - this.entryLifetime * 1000
}
get(key: K): V {
const entry = this.store.get(key)
if (!entry) {
throw new Error('This cache entry does not exist. Check with ".has()" before using ".get()".')
}
return entry.data
}
put(key: K, value: V): void {
if (this.maxEntries > 0 && this.store.size === this.maxEntries) {
this.store.delete(this.store.keys().next().value as K)
}
this.store.set(key, {
entryCreated: Date.now(),
data: value
})
}
}

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -21,7 +21,7 @@ export interface CopyableFieldProps {
const log = new Logger('CopyableField')
/**
* Provides an input field with an attached copy button and a share button (if supported by the browser)
* Provides an input field with an attached copy button and a share button (if supported by the browser).
*
* @param content The content to present
* @param shareOriginUrl The URL of the page to which the shared content should be linked. If this value is omitted then the share button won't be shown.

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -15,7 +15,10 @@ export interface CountdownButtonProps extends ButtonProps {
/**
* Button that starts a countdown on render and is only clickable after the countdown has finished.
*
* @param countdownStartSeconds The initial amount of seconds for the countdown.
* @param children The children that should be displayed after the countdown has elapsed.
* @param props Additional props given to the {@link Button}.
*/
export const CountdownButton: React.FC<CountdownButtonProps> = ({ countdownStartSeconds, children, ...props }) => {
const [secondsRemaining, setSecondsRemaining] = useState(countdownStartSeconds)

View file

@ -1,15 +1,22 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
/**
* Download a given {@link BlobPart file} from memory<.
*
* @param data The file to download.
* @param fileName Which filename does the file have.
* @param mimeType What is the files mimetype.
*/
export const download = (data: BlobPart, fileName: string, mimeType: string): void => {
const file = new Blob([data], { type: mimeType })
downloadLink(URL.createObjectURL(file), fileName)
}
export const downloadLink = (url: string, fileName: string): void => {
const downloadLink = (url: string, fileName: string): void => {
const helperElement = document.createElement('a')
helperElement.href = url
helperElement.download = fileName

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,6 +11,7 @@ import { Trans, useTranslation } from 'react-i18next'
/**
* Renders an input field for the current password when changing passwords.
*
* @param onChange Hook that is called when the entered password changes.
*/
export const CurrentPasswordField: React.FC<CommonFieldProps> = ({ onChange }) => {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -15,6 +15,7 @@ interface DisplayNameFieldProps extends CommonFieldProps {
/**
* Renders an input field for the display name when registering.
*
* @param onChange Hook that is called when the entered display name changes.
* @param value The currently entered display name.
* @param initialValue The initial input field value.

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,6 +11,7 @@ import { Trans, useTranslation } from 'react-i18next'
/**
* Renders an input field for the new password when registering.
*
* @param onChange Hook that is called when the entered password changes.
* @param value The currently entered password.
*/

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -15,6 +15,7 @@ interface PasswordAgainFieldProps extends CommonFieldProps {
/**
* Renders an input field for typing the new password again when registering.
*
* @param onChange Hook that is called when the entered retype of the password changes.
* @param value The currently entered retype of the password.
* @param password The password entered into the password input field.

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,6 +11,7 @@ import { Trans, useTranslation } from 'react-i18next'
/**
* Renders an input field for the username when registering.
*
* @param onChange Hook that is called when the entered username changes.
* @param value The currently entered username.
*/

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -15,6 +15,16 @@ export interface ForkAwesomeIconProps {
stacked?: boolean
}
/**
* Renders a fork awesome icon.
*
* @param icon The icon that should be rendered.
* @param fixedWidth If the icon should be rendered with a fixed width.
* @param size The size class the icon should be rendered in.
* @param className Additional classes the icon should get.
* @param stacked If the icon is part of a {@link ForkAwesomeStack stack}.
* @see https://forkaweso.me
*/
export const ForkAwesomeIcon: React.FC<ForkAwesomeIconProps> = ({
icon,
fixedWidth = false,

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -15,6 +15,12 @@ export interface ForkAwesomeStackProps {
children: ReactElement<ForkAwesomeIconProps> | Array<ReactElement<ForkAwesomeIconProps>>
}
/**
* A stack of {@link ForkAwesomeIcon ForkAwesomeIcons}.
*
* @param size Which size the stack should have.
* @param children One or more {@link ForkAwesomeIcon ForkAwesomeIcons} to be stacked.
*/
export const ForkAwesomeStack: React.FC<ForkAwesomeStackProps> = ({ size, children }) => {
return (
<span className={`fa-stack ${size ? 'fa-' : ''}${size ?? ''}`}>

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -27,6 +27,12 @@ export enum HedgeDocLogoType {
WB_HORIZONTAL
}
/**
* Renders the HedgeDoc logo with the app name in different types.
*
* @param size The size the logo should have.
* @param logoType The logo type to be used.
*/
export const HedgeDocLogoWithText: React.FC<HedgeDocLogoProps> = ({ size = HedgeDocLogoSize.MEDIUM, logoType }) => {
const { t } = useTranslation()
const altText = useMemo(() => t('app.icon'), [t])

View file

@ -20,6 +20,16 @@ export interface IconButtonProps extends ButtonProps {
iconFixedWidth?: boolean
}
/**
* A generic {@link Button button} with an {@link ForkAwesomeIcon icon} in it.
*
* @param icon Which icon should be used
* @param children The children that will be added as the content of the button.
* @param iconFixedWidth If the icon should be of fixed width.
* @param border Should the button have a border.
* @param className Additional class names added to the button.
* @param props Additional props for the button.
*/
export const IconButton: React.FC<IconButtonProps> = ({
icon,
children,

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -13,6 +13,12 @@ export interface TranslatedIconButtonProps extends IconButtonProps {
i18nKey: string
}
/**
* Renders an {@link IconButton icon button} with a translation inside.
*
* @param i18nKey The key for the translated string.
* @param props Additional props directly given to the {@link IconButton}.
*/
export const TranslatedIconButton: React.FC<TranslatedIconButtonProps> = ({ i18nKey, ...props }) => {
return (
<IconButton {...props}>

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -10,6 +10,18 @@ import type { IconName } from '../fork-awesome/types'
import { ShowIf } from '../show-if/show-if'
import type { LinkWithTextProps } from './types'
/**
* An external link.
* This should be used for linking pages that are not part of the HedgeDoc instance.
* The links will be opened in a new tab.
*
* @param href The links location
* @param text The links text
* @param icon An optional icon to be shown before the links text
* @param id An id for the link object
* @param className Additional class names added to the link object
* @param title The title of the link
*/
export const ExternalLink: React.FC<LinkWithTextProps> = ({
href,
text,

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,6 +11,17 @@ import type { IconName } from '../fork-awesome/types'
import { ShowIf } from '../show-if/show-if'
import type { LinkWithTextProps } from './types'
/**
* An internal link.
* This should be used for linking pages of the HedgeDoc instance.
*
* @param href The links location
* @param text The links text
* @param icon An optional icon to be shown before the links text
* @param id An id for the link object
* @param className Additional class names added to the link object
* @param title The title of the link
*/
export const InternalLink: React.FC<LinkWithTextProps> = ({
href,
text,

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -9,6 +9,13 @@ import { useTranslation } from 'react-i18next'
import { ExternalLink } from './external-link'
import type { TranslatedLinkProps } from './types'
/**
* An {@link ExternalLink external link} with translated text.
*
* @param i18nKey The key of the translation
* @param i18nOption The translation options
* @param props Additional props directly given to the {@link ExternalLink}
*/
export const TranslatedExternalLink: React.FC<TranslatedLinkProps> = ({ i18nKey, i18nOption, ...props }) => {
const { t } = useTranslation()
return <ExternalLink text={t(i18nKey, i18nOption)} {...props} />

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -8,7 +8,13 @@ import React from 'react'
import { useTranslation } from 'react-i18next'
import { InternalLink } from './internal-link'
import type { TranslatedLinkProps } from './types'
/**
* An {@link InternalLink internal link} with translated text.
*
* @param i18nKey The key of the translation
* @param i18nOption The translation options
* @param props Additional props directly given to the {@link InternalLink}
*/
export const TranslatedInternalLink: React.FC<TranslatedLinkProps> = ({ i18nKey, i18nOption, ...props }) => {
const { t } = useTranslation()
return <InternalLink text={t(i18nKey, i18nOption)} {...props} />

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -14,6 +14,13 @@ export interface LockButtonProps {
title: string
}
/**
* A button with a lock icon.
*
* @param locked If the button should be shown as locked or not
* @param onLockedChanged The callback to change the state from locked to unlocked and vise-versa.
* @param title The title for the button.
*/
export const LockButton: React.FC<LockButtonProps> = ({ locked, onLockedChanged, title }) => {
return (
<Button variant='dark' size='sm' onClick={() => onLockedChanged(!locked)} title={title}>

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -30,6 +30,20 @@ export interface ModalContentProps {
export type CommonModalProps = PropsWithDataCypressId & ModalVisibilityProps & ModalContentProps
/**
* Renders a generic modal.
*
* @param show If the modal should be shown or not.
* @param onHide The callback to hide the modal again
* @param title The title in the header of the modal
* @param showCloseButton If a close button should be shown
* @param titleIcon An optional title icon
* @param additionalClasses Additional class names for the modal
* @param modalSize The modal size
* @param children The children to render into the modal.
* @param titleIsI18nKey If the title is a i18n key and should be used as such
* @param props Additional props directly given to the modal
*/
export const CommonModal: React.FC<PropsWithChildren<CommonModalProps>> = ({
show,
onHide,

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -17,6 +17,19 @@ export interface DeletionModalProps extends CommonModalProps {
deletionButtonI18nKey: string
}
/**
* Renders a generic modal for deletion.
* This means in addition to most things for the {@link CommonModal} there is also a button to confirm the deletion and a corresponding callback.
*
* @param show If the modal should be shown or not.
* @param onHide The callback to hide the modal again
* @param title The title in the header of the modal
* @param onConfirm The callback for the delete button.
* @param deletionButtonI18nKey The i18n key for the deletion button.
* @param titleIcon An optional title icon
* @param children The children to render into the modal.
* @param props Additional props directly given to the modal
*/
export const DeletionModal: React.FC<PropsWithChildren<DeletionModalProps>> = ({
show,
onHide,

View file

@ -1,9 +1,15 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
/**
* Create an array with numbers from 1 to n.
*
* @param length The length of the array (or the number n)
* @return An array of numbers from 1 to n
*/
export const createNumberRangeArray = (length: number): number[] => {
return Array.from(Array(length).keys())
}

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,6 +11,12 @@ export interface PageItemProps {
index: number
}
/**
* Renders a number and adds an onClick handler to it.
*
* @param index The number to render
* @param onClick The onClick Handler
*/
export const PagerItem: React.FC<PageItemProps> = ({ index, onClick }) => {
return (
<li className='page-item'>

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -15,6 +15,13 @@ export interface PaginationProps {
lastPageIndex: number
}
/**
* Renders a pagination menu to move back and forth between pages.
*
* @param numberOfPageButtonsToShowAfterAndBeforeCurrent The number of buttons that should be shown before and after the current button.
* @param onPageChange The callback when one of the buttons is clicked
* @param lastPageIndex The index of the last page
*/
export const PagerPagination: React.FC<PaginationProps> = ({
numberOfPageButtonsToShowAfterAndBeforeCurrent,
onPageChange,

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -13,6 +13,14 @@ export interface PagerPageProps {
onLastPageIndexChange: (lastPageIndex: number) => void
}
/**
* Renders a limited number of the given children.
*
* @param children The children to render
* @param numberOfElementsPerPage The number of elements per page
* @param pageIndex Which page of the children to render
* @param onLastPageIndexChange A callback to notify about changes to the maximal page number
*/
export const Pager: React.FC<PropsWithChildren<PagerPageProps>> = ({
children,
numberOfElementsPerPage,

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,6 +11,12 @@ export interface ShowIfProps {
condition: boolean
}
/**
* Renders the children if the condition is met.
*
* @param children The children to show if the condition is met.
* @param condition If the children should be shown
*/
export const ShowIf: React.FC<PropsWithChildren<ShowIfProps>> = ({ children, condition }) => {
return condition ? <Fragment>{children}</Fragment> : null
}

View file

@ -21,9 +21,10 @@ export interface UserAvatarForUsernameProps extends Omit<UserAvatarProps, 'user'
* Renders the user avatar for a given username.
* When no username is given, the guest user will be used as fallback.
*
* @see {UserAvatar}
* @see UserAvatar
*
* @param username The username for which to show the avatar or null to show the guest user avatar.
* @param props Additional props directly given to the {@link UserAvatar}
*/
export const UserAvatarForUsername: React.FC<UserAvatarForUsernameProps> = ({ username, ...props }) => {
const { t } = useTranslation()

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -7,6 +7,9 @@
import React from 'react'
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon'
/**
* Renders a indefinitely spinning spinner.
*/
export const WaitSpinner: React.FC = () => {
return (
<div className={'m-3 d-flex align-items-center justify-content-center'}>

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -13,7 +13,7 @@ import { NoteInfoLineCreated } from '../editor-page/document-bar/note-info/note-
import { NoteInfoLineUpdated } from '../editor-page/document-bar/note-info/note-info-line-updated'
/**
* Renders an infobar with metadata about the current note.
* Renders an info bar with metadata about the current note.
*/
export const DocumentInfobar: React.FC = () => {
const { t } = useTranslation()

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -29,6 +29,11 @@ export interface AppBarProps {
mode: AppBarMode
}
/**
* Renders the app bar.
*
* @param mode Which mode the app bar should be rendered in. This mainly adds / removes buttons for the editor.
*/
export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
const userExists = useApplicationState((state) => !!state.user)
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -16,6 +16,9 @@ enum DarkModeState {
LIGHT
}
/**
* Renders a button group to activate / deactivate the dark mode.
*/
const DarkModeButton: React.FC = () => {
const { t } = useTranslation()
const darkModeEnabled = useIsDarkModeActivated() ? DarkModeState.DARK : DarkModeState.LIGHT

View file

@ -18,6 +18,10 @@ export enum EditorMode {
EDITOR = 'edit'
}
/**
* Renders the button group to set the editor mode.
* @see EditorMode
*/
export const EditorViewMode: React.FC = () => {
const { t } = useTranslation()
const editorMode = useApplicationState((state) => state.editorConfig.editorMode)

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -17,6 +17,13 @@ const HighlightedCode = React.lazy(
)
const DocumentMarkdownRenderer = React.lazy(() => import('../../../markdown-renderer/document-markdown-renderer'))
/**
* Renders one line in the {@link CheatsheetTabContent cheat sheet}.
* This line shows an minimal markdown example and how it would be rendered.
*
* @param markdown The markdown to be shown and rendered
* @param onTaskCheckedChange A callback to call if a task would be clicked
*/
export const CheatsheetLine: React.FC<CheatsheetLineProps> = ({ markdown, onTaskCheckedChange }) => {
const lines = useMemo(() => markdown.split('\n'), [markdown])
const checkboxClick = useCallback(

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -10,6 +10,9 @@ import { Trans, useTranslation } from 'react-i18next'
import { CheatsheetLine } from './cheatsheet-line'
import styles from './cheatsheet.module.scss'
/**
* Renders the content of the cheat sheet for the {@link HelpModal}.
*/
export const CheatsheetTabContent: React.FC = () => {
const { t } = useTranslation()
const [checked, setChecked] = useState<boolean>(false)

View file

@ -12,6 +12,9 @@ import { HelpModal } from './help-modal'
import { cypressId } from '../../../../utils/cypress-attribute'
import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
/**
* Renders the button to open the {@link HelpModal}.
*/
export const HelpButton: React.FC = () => {
const { t } = useTranslation()
const [modalVisibility, showModal, closeModal] = useBooleanState()

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -19,6 +19,17 @@ export enum HelpTabStatus {
Links = 'links.title'
}
/**
* Renders the help modal.
* This modal shows the user the markdown cheatsheet, shortcuts and different links with further help.
*
* @see CheatsheetTabContent
* @see ShortcutTabContent
* @see LinksTabContent
*
* @param show If the modal should be shown
* @param onHide A callback when the modal should be closed again
*/
export const HelpModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) => {
const [tab, setTab] = useState<HelpTabStatus>(HelpTabStatus.Cheatsheet)
const { t } = useTranslation()

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,6 +11,9 @@ import links from '../../../../links.json'
import { TranslatedExternalLink } from '../../../common/links/translated-external-link'
import { TranslatedInternalLink } from '../../../common/links/translated-internal-link'
/**
* Renders a bunch of links, where further help can be requested.
*/
export const LinksTabContent: React.FC = () => {
useTranslation()

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -9,6 +9,9 @@ import { Card, ListGroup, Row } from 'react-bootstrap'
import { Trans } from 'react-i18next'
import { isMac } from '../../utils'
/**
* Renders a list of shortcuts usable in HedgeDoc.
*/
export const ShortcutTabContent: React.FC = () => {
const modifierKey = useMemo(() => (isMac() ? <kbd></kbd> : <kbd>Ctrl</kbd>), [])
const altKey = useMemo(() => (isMac() ? <kbd></kbd> : <kbd>Alt</kbd>), [])

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -15,6 +15,9 @@ import {
HedgeDocLogoWithText
} from '../../common/hedge-doc-logo/hedge-doc-logo-with-text'
/**
* Renders the branding for the {@link AppBar}
*/
export const NavbarBranding: React.FC = () => {
const darkModeActivated = useIsDarkModeActivated()

View file

@ -10,6 +10,9 @@ import { Trans, useTranslation } from 'react-i18next'
import { Button } from 'react-bootstrap'
import Link from 'next/link'
/**
* Renders a button to create a new note.
*/
export const NewNoteButton: React.FC = () => {
useTranslation()

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -18,6 +18,10 @@ enum SyncScrollState {
UNSYNCED
}
/**
* Renders a button group with two states for the sync scroll buttons.
* This makes it possible to activate or deactivate sync scrolling.
*/
export const SyncScrollButtons: React.FC = () => {
const syncScrollEnabled = useApplicationState((state) => state.editorConfig.syncScroll)
? SyncScrollState.SYNCED

View file

@ -24,7 +24,9 @@ type ChangeEditorContentContext = [CodeMirrorReference, SetCodeMirrorReference]
const changeEditorContentContext = createContext<ChangeEditorContentContext | undefined>(undefined)
/**
* Extracts the code mirror reference from the parent context
* Extracts the {@link CodeMirrorReference code mirror reference} from the parent context.
*
* @return The {@link CodeMirrorReference} from the parent context.
*/
export const useCodeMirrorReference = (): CodeMirrorReference => {
const contextContent = Optional.ofNullable(useContext(changeEditorContentContext)).orElseThrow(
@ -34,7 +36,9 @@ export const useCodeMirrorReference = (): CodeMirrorReference => {
}
/**
* Extracts the code mirror reference from the parent context
* Provides a function to set the {@link CodeMirrorReference code mirror reference} in the current context.
*
* @return A function to set a {@link CodeMirrorReference code mirror reference}.
*/
export const useSetCodeMirrorReference = (): SetCodeMirrorReference => {
const contextContent = Optional.ofNullable(useContext(changeEditorContentContext)).orElseThrow(

View file

@ -13,6 +13,7 @@ import { DateTime } from 'luxon'
/**
* Renders an info line about the creation of the current note.
*
* @param size The size in which the line should be displayed.
*/
export const NoteInfoLineCreated: React.FC<NoteInfoTimeLineProps> = ({ size }) => {

View file

@ -15,6 +15,7 @@ import { DateTime } from 'luxon'
/**
* Renders an info line about the last update of the current note.
*
* @param size The size in which line and user avatar should be displayed.
*/
export const NoteInfoLineUpdated: React.FC<NoteInfoTimeLineProps> = ({ size }) => {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -14,6 +14,14 @@ export interface NoteInfoLineProps {
size?: '2x' | '3x' | '4x' | '5x' | undefined
}
/**
* This is the base component for all note info lines.
* It renders an icon and some children in italic.
*
* @param icon The icon be shown
* @param size Which size the icon should be
* @param children The children to render
*/
export const NoteInfoLine: React.FC<PropsWithChildren<NoteInfoLineProps>> = ({ icon, size, children }) => {
return (
<span className={'d-flex align-items-center'}>

View file

@ -17,6 +17,7 @@ import { NoteInfoLineContributors } from './note-info-line-contributors'
/**
* Modal that shows informational data about the current note.
*
* @param show true when the modal should be visible, false otherwise.
* @param onHide Callback that is fired when the modal is going to be closed.
*/

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -12,6 +12,11 @@ export interface TimeFromNowProps {
time: DateTime
}
/**
* Renders a given time relative to the current time.
*
* @param time The time to be rendered.
*/
export const TimeFromNow: React.FC<TimeFromNowProps> = ({ time }) => {
return (
<time className={'mx-1'} title={time.toFormat('DDDD T')} dateTime={time.toString()}>

View file

@ -6,15 +6,19 @@
import type { PropsWithChildren } from 'react'
import React from 'react'
import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface UnitalicBoldContentProps {
export interface UnitalicBoldContentProps extends PropsWithDataCypressId {
text?: string | number
}
/**
* Renders the children elements in a non-italic but bold style.
*
* @param text Optional text content that should be rendered.
* @param children Children that may be rendered.
* @param props Additional props for cypressId
*/
export const UnitalicBoldContent: React.FC<PropsWithChildren<UnitalicBoldContentProps>> = ({
text,

View file

@ -17,6 +17,7 @@ export interface PermissionAddEntryFieldProps {
/**
* Permission entry row containing a field for adding new user permission entries.
*
* @param onAddEntry Callback that is fired with the entered username as identifier of the entry to add.
* @param i18nKey The localization key for the submit button.
*/

View file

@ -31,6 +31,7 @@ export interface PermissionEntryButtonsProps {
/**
* Buttons next to a user or group permission entry to change the permissions or remove the entry.
*
* @param name The name of the user or group.
* @param type The type of the entry. Either {@link PermissionType.USER} or {@link PermissionType.GROUP}.
* @param currentSetting How the permission is currently set.

View file

@ -20,6 +20,7 @@ export interface PermissionEntrySpecialGroupProps {
/**
* Permission entry that represents one of the built-in special groups.
*
* @param level The access level that is currently set for the group.
* @param type The type of the special group. Must be either {@link SpecialGroup.EVERYONE} or {@link SpecialGroup.LOGGED_IN}.
*/

View file

@ -22,6 +22,7 @@ export interface PermissionEntryUserProps {
/**
* Permission entry for a user that can be set to read-only or writeable and can be removed.
*
* @param entry The permission entry.
*/
export const PermissionEntryUser: React.FC<PermissionEntryUserProps> = ({ entry }) => {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -14,6 +14,7 @@ import { PermissionSectionSpecialGroups } from './permission-section-special-gro
/**
* Modal for viewing and managing the permissions of the note.
*
* @param show true to show the modal, false otherwise.
* @param onHide Callback that is fired when the modal is about to be closed.
*/

View file

@ -14,6 +14,11 @@ export interface PermissionOwnerChangeProps {
onConfirmOwnerChange: (newOwner: string) => void
}
/**
* Renders an input group to change the permission owner.
*
* @param onConfirmOwnerChange The callback to call if the owner was changed.
*/
export const PermissionOwnerChange: React.FC<PermissionOwnerChangeProps> = ({ onConfirmOwnerChange }) => {
const { t } = useTranslation()
const [ownerFieldValue, setOwnerFieldValue] = useState('')

View file

@ -17,6 +17,7 @@ export interface PermissionOwnerInfoProps {
/**
* Content for the owner section of the permission modal that shows the current note owner.
*
* @param onEditOwner Callback that is fired when the user chooses to change the note owner.
*/
export const PermissionOwnerInfo: React.FC<PermissionOwnerInfoProps> = ({ onEditOwner }) => {

View file

@ -19,6 +19,7 @@ export interface RevisionModalFooterProps {
/**
* Renders the footer of the revision modal that includes buttons to download the currently selected revision or to
* revert the note content back to that revision.
*
* @param selectedRevisionId The currently selected revision id or undefined if no revision was selected.
* @param onHide Callback that is fired when the modal is about to be closed.
*/

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -13,6 +13,7 @@ const DISPLAY_MAX_USERS_PER_REVISION = 9
/**
* Downloads a given revision's content as markdown document in the browser.
*
* @param noteId The id of the note from which to download the revision.
* @param revision The revision details object containing the content to download.
*/
@ -25,6 +26,7 @@ export const downloadRevision = (noteId: string, revision: RevisionDetails | nul
/**
* Fetches user details for the given usernames while returning a maximum of 9 users.
*
* @param usernames The list of usernames to fetch.
* @throws {Error} in case the user-data request failed.
* @return An array of user details.

View file

@ -15,6 +15,12 @@ import { useApplicationState } from '../../../../hooks/common/use-application-st
import { NoteType } from '../../../../redux/note-details/types/note-details'
import { useFrontendBaseUrl } from '../../../../hooks/common/use-frontend-base-url'
/**
* Renders a modal which provides shareable URLs of this note.
*
* @param show If the modal should be shown
* @param onHide The callback when the modal should be closed
*/
export const ShareModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) => {
useTranslation()
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)

View file

@ -37,6 +37,15 @@ import { useOnFirstEditorUpdateExtension } from './hooks/yjs/use-on-first-editor
import { useIsConnectionSynced } from './hooks/yjs/use-is-connection-synced'
import { useMarkdownContentYText } from './hooks/yjs/use-markdown-content-y-text'
/**
* Renders the text editor pane of the editor.
* The used editor is {@link ReactCodeMirror code mirror}.
*
* @param scrollState The current {@link ScrollState}
* @param onScroll The callback to update the {@link ScrollState}
* @param onMakeScrollSource The callback to request to become the scroll source.
* @external {ReactCodeMirror} https://npmjs.com/@uiw/react-codemirror
*/
export const EditorPane: React.FC<ScrollProps> = ({ scrollState, onScroll, onMakeScrollSource }) => {
const ligaturesEnabled = useApplicationState((state) => state.editorConfig.ligatures)

View file

@ -9,7 +9,7 @@
*
* @param markdownContent The markdown content whose content should be checked
* @param cursorPosition The cursor position that may or may not be in a code fence
* @return {@code true} if the given cursor position is in a code fence
* @return {@link true} if the given cursor position is in a code fence
*/
export const isCursorInCodeFence = (markdownContent: string, cursorPosition: number): boolean => {
const lines = markdownContent.slice(0, cursorPosition).split('\n')

View file

@ -8,7 +8,9 @@ import { createNumberRangeArray } from '../../../../common/number-range/number-r
/**
* Checks if the given text is a tab-and-new-line-separated table.
*
* @param text The text to check
* @return If the text is a table or not
*/
export const isTable = (text: string): boolean => {
// Tables must consist of multiple rows and columns
@ -32,7 +34,8 @@ export const isTable = (text: string): boolean => {
}
/**
* Reformat the given text as Markdown table
* Reformat the given text as Markdown table.
*
* @param pasteData The plain text table separated by tabs and new-lines
* @return the formatted Markdown table
*/

View file

@ -34,7 +34,6 @@ const applyScrollState = (view: EditorView, scrollState: ScrollState): void => {
/**
* Monitors the given scroll state and scrolls the editor to the state if changed.
*
* @param editorRef The editor that should be manipulated
* @param scrollState The scroll state that should be monitored
*/
export const useApplyScrollState = (scrollState?: ScrollState): void => {

View file

@ -11,7 +11,7 @@ import { EditorView } from '@codemirror/view'
import type { Extension, SelectionRange } from '@codemirror/state'
/**
* Provides a callback for codemirror that handles cursor changes
* Provides a callback for codemirror that handles cursor changes.
*
* @return the generated callback
*/

View file

@ -19,7 +19,7 @@ import type { ContentFormatter } from '../../change-content-context/change-conte
import { useCodeMirrorReference } from '../../change-content-context/change-content-context'
/**
* Processes the upload of the given file and inserts the correct Markdown code
* Processes the upload of the given file and inserts the correct Markdown code.
*
* @param view the codemirror instance that is used to insert the Markdown code
* @param file The file to upload

View file

@ -22,7 +22,7 @@ const calculateLineBasedPosition = (absolutePosition: number, lineStartIndexes:
}
/**
* Returns the line+character based position of the to cursor, if available.
* Returns the line+character based position of the to-cursor, if available.
*/
export const useLineBasedToPosition = (): LineBasedPosition | undefined => {
const lineStartIndexes = useApplicationState((state) => state.noteDetails.markdownContent.lineStartIndexes)
@ -38,7 +38,7 @@ export const useLineBasedToPosition = (): LineBasedPosition | undefined => {
}
/**
* Returns the line+character based position of the from cursor.
* Returns the line+character based position of the from-cursor.
*/
export const useLineBasedFromPosition = (): LineBasedPosition => {
const lineStartIndexes = useApplicationState((state) => state.noteDetails.markdownContent.lineStartIndexes)

View file

@ -10,7 +10,7 @@ import type { YText } from 'yjs/dist/src/types/YText'
/**
* One-Way-synchronizes the text of the given {@link YText y-text} into the global application state.
*4
*
* @param yText The source text
*/
export const useBindYTextToRedux = (yText: YText): void => {

View file

@ -11,6 +11,7 @@ import type { YDocMessageTransporter } from '@hedgedoc/realtime'
* Checks if the given message transporter has received at least one full synchronisation.
*
* @param connection The connection whose sync status should be checked
* @return If at least one full synchronisation is occurred.
*/
export const useIsConnectionSynced = (connection: YDocMessageTransporter): boolean => {
const [editorEnabled, setEditorEnabled] = useState<boolean>(false)

View file

@ -11,7 +11,7 @@ import type { Extension } from '@codemirror/state'
/**
* Provides an extension that checks when the code mirror, that loads the extension, has its first update.
*
* @return [Extension, boolean] The extension that listens for editor updates and a boolean that defines if the first update already happened
* @return The extension that listens for editor updates and a boolean that defines if the first update already happened
*/
export const useOnFirstEditorUpdateExtension = (): [Extension, boolean] => {
const [firstUpdateHappened, setFirstUpdateHappened] = useState<boolean>(false)

View file

@ -14,7 +14,7 @@ import type { Doc } from 'yjs'
import type { Awareness } from 'y-protocols/awareness'
/**
* Handles the communication with the realtime endpoint of the backend and synchronizes the given y-doc and awareness with other clients..
* Handles the communication with the realtime endpoint of the backend and synchronizes the given y-doc and awareness with other clients.
*/
export class WebsocketConnection extends WebsocketTransporter {
constructor(url: URL, doc: Doc, awareness: Awareness) {

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -10,8 +10,6 @@ import { useApplicationState } from '../../../../hooks/common/use-application-st
/**
* Renders a translated text that shows the number of lines in the document.
*
* @param linesInDocument The number of lines in the document
*/
export const NumberOfLinesInDocumentInfo: React.FC = () => {
useTranslation()

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,9 +11,6 @@ import { useApplicationState } from '../../../../hooks/common/use-application-st
/**
* Renders a translated text that shows the number of remaining characters.
*
* @param remainingCharacters The number of characters that are still available in this document
* @param charactersInDocument The total number of characters in the document
*/
export const RemainingCharactersInfo: React.FC = () => {
const { t } = useTranslation()

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import { wrapSelection } from '../formatters/wrap-selection'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
/**
* Renders a button to make the selection in the {@link Editor editor} bold.
*/
export const BoldButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '**', '**')

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
/**
* Renders a button to create a checklist in the {@link Editor editor}.
*/
export const CheckListButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(markdownContent, currentSelection, () => `- [ ] `)

View file

@ -10,6 +10,9 @@ import { wrapSelection } from '../formatters/wrap-selection'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { changeCursorsToWholeLineIfNoToCursor } from '../formatters/utils/change-cursors-to-whole-line-if-no-to-cursor'
/**
* Renders a button to create a code fence in the {@link Editor editor}.
*/
export const CodeFenceButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return wrapSelection(changeCursorsToWholeLineIfNoToCursor(markdownContent, currentSelection), '```\n', '\n```')

View file

@ -10,6 +10,9 @@ import { wrapSelection } from '../formatters/wrap-selection'
import { changeCursorsToWholeLineIfNoToCursor } from '../formatters/utils/change-cursors-to-whole-line-if-no-to-cursor'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
/**
* Renders a button to create a spoiler section in the {@link Editor editor}.
*/
export const CollapsibleBlockButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return wrapSelection(

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { replaceSelection } from '../formatters/replace-selection'
/**
* Renders a button to create a comment in the {@link Editor editor}.
*/
export const CommentButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return replaceSelection({ from: currentSelection.to ?? currentSelection.from }, '> []', true)

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
/**
* Renders a button to add a header in the {@link Editor editor}.
*/
export const HeaderLevelButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(markdownContent, currentSelection, (line) => (line.startsWith('#') ? `#` : `# `))

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import { wrapSelection } from '../formatters/wrap-selection'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
/**
* Renders a button that highlights the selection in the {@link Editor editor}.
*/
export const HighlightButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '==', '==')

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { replaceSelection } from '../formatters/replace-selection'
/**
* Renders a button to insert a horizontal line in the {@link Editor editor}.
*/
export const HorizontalLineButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return replaceSelection({ from: currentSelection.to ?? currentSelection.from }, '----\n', true)

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { addLink } from '../formatters/add-link'
/**
* Renders a button to insert an image in the {@link Editor editor}.
*/
export const ImageLinkButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return addLink(markdownContent, currentSelection, '!')

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import { wrapSelection } from '../formatters/wrap-selection'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
/**
* Renders a button to make the selection in the {@link Editor editor} italic.
*/
export const ItalicButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '*', '*')

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { addLink } from '../formatters/add-link'
/**
* Renders a button to insert a link in the {@link Editor editor}.
*/
export const LinkButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return addLink(markdownContent, currentSelection)

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
/**
* Renders a button to insert an ordered list in the {@link Editor editor}.
*/
export const OrderedListButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
/**
* Renders a button to insert a quotation in the {@link Editor editor}.
*/
export const QuotesButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(markdownContent, currentSelection, () => `> `)

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import { wrapSelection } from '../formatters/wrap-selection'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
/**
* Renders a button to strike through the selection in the {@link Editor editor}.
*/
export const StrikethroughButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '~~', '~~')

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import { wrapSelection } from '../formatters/wrap-selection'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
/**
* Renders a button to format the selection in the {@link Editor editor} as subscript.
*/
export const SubscriptButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '~', '~')

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import { wrapSelection } from '../formatters/wrap-selection'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
/**
* Renders a button to format the selection in the {@link Editor editor} as superscript.
*/
export const SuperscriptButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '^', '^')

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import { wrapSelection } from '../formatters/wrap-selection'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
/**
* Renders a button to underline the selection in the {@link Editor editor}.
*/
export const UnderlineButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '++', '++')

View file

@ -9,6 +9,9 @@ import { ToolbarButton } from '../toolbar-button'
import type { ContentFormatter } from '../../../change-content-context/change-content-context'
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
/**
* Renders a button to insert an unordered list in the {@link Editor editor}.
*/
export const UnorderedListButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(markdownContent, currentSelection, () => `- `)

View file

@ -16,6 +16,10 @@ import { useChangeEditorContentCallback } from '../../../change-content-context/
import { replaceSelection } from '../formatters/replace-selection'
import { extractEmojiShortCode } from './extract-emoji-short-code'
/**
* Renders a button to open the emoji picker.
* @see EmojiPicker
*/
export const EmojiPickerButton: React.FC = () => {
const { t } = useTranslation()
const [showEmojiPicker, setShowEmojiPicker] = useState(false)

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -14,22 +14,16 @@ import forkawesomeIcon from './forkawesome.png'
import { ForkAwesomeIcons } from '../../../../common/fork-awesome/fork-awesome-icons'
import fontStyles from '../../../../../../global-styles/variables.module.scss'
export interface EmojiPickerProps {
show: boolean
onEmojiSelected: (emoji: EmojiClickEventDetail) => void
onDismiss: () => void
}
export const customEmojis: CustomEmoji[] = ForkAwesomeIcons.map((name) => ({
const customEmojis: CustomEmoji[] = ForkAwesomeIcons.map((name) => ({
name: `fa-${name}`,
shortcodes: [`fa-${name.toLowerCase()}`],
url: forkawesomeIcon.src,
category: 'ForkAwesome'
}))
export const EMOJI_DATA_PATH = '/_next/static/js/emoji-data.json'
const EMOJI_DATA_PATH = '/_next/static/js/emoji-data.json'
export const emojiPickerConfig = {
const emojiPickerConfig = {
customEmoji: customEmojis,
dataSource: EMOJI_DATA_PATH
}
@ -40,6 +34,20 @@ const twemojiStyle = (): HTMLStyleElement => {
return style
}
export interface EmojiPickerProps {
show: boolean
onEmojiSelected: (emoji: EmojiClickEventDetail) => void
onDismiss: () => void
}
/**
* Renders the emoji picker.
*
* @param show If the emoji picker should be shown
* @param onEmojiSelected The callback, that will be called if an emoji is selected
* @param onDismiss The callback, that will be called if the picker should be closed.
* @external {Picker} https://www.npmjs.com/package/emoji-picker-element
*/
export const EmojiPicker: React.FC<EmojiPickerProps> = ({ show, onEmojiSelected, onDismiss }) => {
const darkModeEnabled = useIsDarkModeActivated()
const pickerContainerRef = useRef<HTMLDivElement>(null)

View file

@ -16,8 +16,8 @@ const afterLink = ')'
* Creates a copy of the given markdown content lines but inserts a new link tag.
*
* @param markdownContent The content of the document to modify
* @param selection If the selection has no to cursor then the tag will be inserted at this position.
* If the selection has a to cursor then the selected text will be inserted into the description or the URL part.
* @param selection If the selection has no to-cursor then the tag will be inserted at this position.
* If the selection has a to-cursor then the selected text will be inserted into the description or the URL part.
* @param prefix An optional prefix for the link
* @return the modified copy of lines
*/

Some files were not shown because too many files have changed in this diff Show more