mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-25 04:24:43 -04:00
docs: consolidate docs (#2182)
Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
8d46d7e39e
commit
ecffebc43c
307 changed files with 1474 additions and 487 deletions
|
@ -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`)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 [
|
||||
{
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
|
|
91
src/components/common/cache/cache.test.ts
vendored
91
src/components/common/cache/cache.test.ts
vendored
|
@ -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)
|
||||
})
|
||||
})
|
50
src/components/common/cache/cache.ts
vendored
50
src/components/common/cache/cache.ts
vendored
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 ?? ''}`}>
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'}>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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>), [])
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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'}>
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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()}>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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}.
|
||||
*/
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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('')
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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, '**', '**')
|
||||
|
|
|
@ -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, () => `- [ ] `)
|
||||
|
|
|
@ -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```')
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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('#') ? `#` : `# `))
|
||||
|
|
|
@ -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, '==', '==')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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, '!')
|
||||
|
|
|
@ -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, '*', '*')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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, () => `> `)
|
||||
|
|
|
@ -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, '~~', '~~')
|
||||
|
|
|
@ -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, '~', '~')
|
||||
|
|
|
@ -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, '^', '^')
|
||||
|
|
|
@ -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, '++', '++')
|
||||
|
|
|
@ -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, () => `- `)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue