mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-24 10:46:30 -05:00
refactor(frontend): make terminology of cheatsheet more clear
Also add additional documentation to explain how cheatsheets work Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
81927b88f2
commit
7a365acdb9
11 changed files with 61 additions and 45 deletions
|
@ -3,8 +3,8 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetEntry, CheatsheetExtension } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { isCheatsheetGroup } from '../../cheatsheet/cheatsheet-extension'
|
||||
import type { CheatsheetSingleEntry, CheatsheetExtension } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { hasCheatsheetTopics } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { CategoryAccordion } from './category-accordion'
|
||||
import { CheatsheetEntryPane } from './cheatsheet-entry-pane'
|
||||
import { CheatsheetSearch } from './cheatsheet-search'
|
||||
|
@ -20,11 +20,11 @@ import { Trans } from 'react-i18next'
|
|||
export const CheatsheetContent: React.FC = () => {
|
||||
const [visibleExtensions, setVisibleExtensions] = useState<CheatsheetExtension[]>([])
|
||||
const [selectedExtension, setSelectedExtension] = useState<CheatsheetExtension>()
|
||||
const [selectedEntry, setSelectedEntry] = useState<CheatsheetEntry>()
|
||||
const [selectedEntry, setSelectedEntry] = useState<CheatsheetSingleEntry>()
|
||||
|
||||
const changeExtension = useCallback((value: CheatsheetExtension) => {
|
||||
setSelectedExtension(value)
|
||||
setSelectedEntry(isCheatsheetGroup(value) ? value.entries[0] : value)
|
||||
setSelectedEntry(hasCheatsheetTopics(value) ? value.topics[0] : value)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
@ -46,7 +46,7 @@ export const CheatsheetContent: React.FC = () => {
|
|||
/>
|
||||
{selectedEntry !== undefined ? (
|
||||
<CheatsheetEntryPane
|
||||
rootI18nKey={isCheatsheetGroup(selectedExtension) ? selectedExtension.i18nKey : undefined}
|
||||
rootI18nKey={hasCheatsheetTopics(selectedExtension) ? selectedExtension.i18nKey : undefined}
|
||||
extension={selectedEntry}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
@ -8,7 +8,7 @@ import { HtmlToReact } from '../../../common/html-to-react/html-to-react'
|
|||
import { RendererIframe } from '../../../common/renderer-iframe/renderer-iframe'
|
||||
import { ExtensionEventEmitterProvider } from '../../../markdown-renderer/hooks/use-extension-event-emitter'
|
||||
import { RendererType } from '../../../render-page/window-post-message-communicator/rendering-message'
|
||||
import type { CheatsheetEntry } from '../../cheatsheet/cheatsheet-extension'
|
||||
import type { CheatsheetSingleEntry } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { EditorToRendererCommunicatorContextProvider } from '../../render-context/editor-to-renderer-communicator-context-provider'
|
||||
import { ReadMoreLinkItem } from './read-more-link-item'
|
||||
import { useComponentsFromAppExtensions } from './use-components-from-app-extensions'
|
||||
|
@ -19,7 +19,7 @@ import { Trans, useTranslation } from 'react-i18next'
|
|||
|
||||
interface CheatsheetRendererProps {
|
||||
rootI18nKey?: string
|
||||
extension: CheatsheetEntry
|
||||
extension: CheatsheetSingleEntry
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,7 @@ import { allAppExtensions } from '../../../../extensions/all-app-extensions'
|
|||
import type { SearchIndexEntry } from '../../../../hooks/common/use-document-search'
|
||||
import { useDocumentSearch } from '../../../../hooks/common/use-document-search'
|
||||
import { useOnInputChange } from '../../../../hooks/common/use-on-input-change'
|
||||
import { useTranslatedText } from '../../../../hooks/common/use-translated-text'
|
||||
import { UiIcon } from '../../../common/icons/ui-icon'
|
||||
import type { CheatsheetSingleEntry, CheatsheetExtension } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { hasCheatsheetTopics } from '../../cheatsheet/cheatsheet-extension'
|
||||
|
@ -47,8 +48,8 @@ export const CheatsheetSearch: React.FC<CheatsheetSearchProps> = ({ setVisibleEx
|
|||
() => allAppExtensions.flatMap((extension) => extension.buildCheatsheetExtensions()),
|
||||
[]
|
||||
)
|
||||
const buildSearchIndexDocument = useCallback(
|
||||
(entry: CheatsheetEntry, rootI18nKey: string | undefined = undefined): CheatsheetSearchIndexEntry => {
|
||||
const buildSearchIndexEntry = useCallback(
|
||||
(entry: CheatsheetSingleEntry, rootI18nKey: string | undefined = undefined): CheatsheetSearchIndexEntry => {
|
||||
const rootI18nKeyWithDot = rootI18nKey ? `${rootI18nKey}.` : ''
|
||||
return {
|
||||
id: rootI18nKey ? rootI18nKey : entry.i18nKey,
|
||||
|
@ -59,15 +60,16 @@ export const CheatsheetSearch: React.FC<CheatsheetSearchProps> = ({ setVisibleEx
|
|||
},
|
||||
[t]
|
||||
)
|
||||
const placeholderText = useTranslatedText('cheatsheet.search')
|
||||
const cheatsheetSearchIndexEntries = useMemo(
|
||||
() =>
|
||||
allCheatsheetExtensions.flatMap((entry) => {
|
||||
if (hasCheatsheetTopics(entry)) {
|
||||
return entry.topics.map((innerEntry) => buildSearchIndexEntry(innerEntry, entry.i18nKey))
|
||||
}
|
||||
return buildSearchIndexDocument(entry)
|
||||
return buildSearchIndexEntry(entry)
|
||||
}),
|
||||
[buildSearchIndexDocument, allCheatsheetExtensions]
|
||||
[buildSearchIndexEntry, allCheatsheetExtensions]
|
||||
)
|
||||
const searchResults = useDocumentSearch(cheatsheetSearchIndexEntries, searchOptions, searchTerm)
|
||||
useEffect(() => {
|
||||
|
@ -81,21 +83,14 @@ export const CheatsheetSearch: React.FC<CheatsheetSearchProps> = ({ setVisibleEx
|
|||
})
|
||||
setVisibleExtensions(extensionResults)
|
||||
}, [allCheatsheetExtensions, searchResults, searchTerm, setVisibleExtensions])
|
||||
const onChange = useOnInputChange((search) => {
|
||||
setSearchTerm(search)
|
||||
})
|
||||
const onChange = useOnInputChange(setSearchTerm)
|
||||
const clearSearch = useCallback(() => {
|
||||
setSearchTerm('')
|
||||
}, [setSearchTerm])
|
||||
|
||||
return (
|
||||
<InputGroup className='mb-3'>
|
||||
<FormControl
|
||||
placeholder={t('cheatsheet.search') ?? undefined}
|
||||
aria-label={t('cheatsheet.search') ?? undefined}
|
||||
onChange={onChange}
|
||||
value={searchTerm}
|
||||
/>
|
||||
<FormControl placeholder={placeholderText} aria-label={placeholderText} onChange={onChange} value={searchTerm} />
|
||||
<button className={styles.innerBtn} onClick={clearSearch}>
|
||||
<UiIcon icon={X} />
|
||||
</button>
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { CheatsheetEntry, CheatsheetExtension } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { isCheatsheetGroup } from '../../cheatsheet/cheatsheet-extension'
|
||||
import type { CheatsheetSingleEntry, CheatsheetExtension } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { hasCheatsheetTopics } from '../../cheatsheet/cheatsheet-extension'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Button, ButtonGroup, ListGroupItem } from 'react-bootstrap'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
interface EntrySelectionProps {
|
||||
extension: CheatsheetExtension | undefined
|
||||
selectedEntry: CheatsheetEntry | undefined
|
||||
setSelectedEntry: (value: CheatsheetEntry) => void
|
||||
selectedEntry: CheatsheetSingleEntry | undefined
|
||||
setSelectedEntry: (value: CheatsheetSingleEntry) => void
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,10 +25,10 @@ interface EntrySelectionProps {
|
|||
*/
|
||||
export const TopicSelection: React.FC<EntrySelectionProps> = ({ extension, selectedEntry, setSelectedEntry }) => {
|
||||
const listItems = useMemo(() => {
|
||||
if (!isCheatsheetGroup(extension)) {
|
||||
if (!hasCheatsheetTopics(extension)) {
|
||||
return null
|
||||
}
|
||||
return extension.entries.map((entry) => (
|
||||
return extension.topics.map((entry) => (
|
||||
<Button
|
||||
key={entry.i18nKey}
|
||||
variant={selectedEntry?.i18nKey === entry.i18nKey ? 'primary' : 'outline-primary'}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import { allAppExtensions } from '../../../../extensions/all-app-extensions'
|
||||
import type { CheatsheetExtensionComponentProps } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { isCheatsheetGroup } from '../../cheatsheet/cheatsheet-extension'
|
||||
import { hasCheatsheetTopics } from '../../cheatsheet/cheatsheet-extension'
|
||||
import type { ReactElement } from 'react'
|
||||
import React, { Fragment, useMemo } from 'react'
|
||||
|
||||
|
@ -20,7 +20,7 @@ export const useComponentsFromAppExtensions = (
|
|||
<Fragment key={'app-extensions'}>
|
||||
{allAppExtensions
|
||||
.flatMap((extension) => extension.buildCheatsheetExtensions())
|
||||
.flatMap((extension) => (isCheatsheetGroup(extension) ? extension.entries : extension))
|
||||
.flatMap((extension) => (hasCheatsheetTopics(extension) ? extension.topics : extension))
|
||||
.map((extension) => {
|
||||
if (extension.cheatsheetExtensionComponent) {
|
||||
return React.createElement(extension.cheatsheetExtensionComponent, { key: extension.i18nKey, setContent })
|
||||
|
|
|
@ -9,19 +9,40 @@ export interface CheatsheetExtensionComponentProps {
|
|||
setContent: (dispatcher: string | ((prevState: string) => string)) => void
|
||||
}
|
||||
|
||||
export type CheatsheetExtension = CheatsheetEntry | CheatsheetGroup
|
||||
export type CheatsheetExtension = CheatsheetSingleEntry | CheatsheetEntryWithTopics
|
||||
|
||||
export const isCheatsheetGroup = (extension: CheatsheetExtension | undefined): extension is CheatsheetGroup => {
|
||||
return (extension as CheatsheetGroup)?.entries !== undefined
|
||||
/**
|
||||
* Determine if a given {@link CheatsheetExtension} is a {@link CheatsheetEntryWithTopics} or just a {@link CheatsheetSingleEntry}.
|
||||
*
|
||||
* @param extension The extension in question
|
||||
* @return boolean
|
||||
*/
|
||||
export const hasCheatsheetTopics = (
|
||||
extension: CheatsheetExtension | undefined
|
||||
): extension is CheatsheetEntryWithTopics => {
|
||||
return (extension as CheatsheetEntryWithTopics)?.topics !== undefined
|
||||
}
|
||||
|
||||
export interface CheatsheetGroup {
|
||||
/**
|
||||
* This is an entry with just a name and a bunch of different topics to discuss.
|
||||
*
|
||||
* e.g 'basics.headlines' with the topics 'hashtag' and 'equal'
|
||||
*/
|
||||
export interface CheatsheetEntryWithTopics {
|
||||
i18nKey: string
|
||||
categoryI18nKey?: string
|
||||
entries: CheatsheetEntry[]
|
||||
topics: CheatsheetSingleEntry[]
|
||||
}
|
||||
|
||||
export interface CheatsheetEntry {
|
||||
/**
|
||||
* This is an entry that describes something completely.
|
||||
*
|
||||
* In the translations you'll find both 'description' containing an explanation and 'example' containing a demonstration in markdown under the i18nKey.
|
||||
* If this entry is a topic of some other entry the i18nKey needs to be prefixed with the i18nKey of the other entry.
|
||||
*
|
||||
* e.g 'basics.basicFormatting'
|
||||
*/
|
||||
export interface CheatsheetSingleEntry {
|
||||
i18nKey: string
|
||||
categoryI18nKey?: string
|
||||
cheatsheetExtensionComponent?: React.FC<CheatsheetExtensionComponentProps>
|
||||
|
|
|
@ -33,7 +33,7 @@ export class BasicMarkdownSyntaxAppExtension extends AppExtension {
|
|||
{
|
||||
i18nKey: 'basics.headlines',
|
||||
categoryI18nKey: 'basic',
|
||||
entries: [
|
||||
topics: [
|
||||
{
|
||||
i18nKey: 'hashtag'
|
||||
},
|
||||
|
@ -45,17 +45,17 @@ export class BasicMarkdownSyntaxAppExtension extends AppExtension {
|
|||
{
|
||||
i18nKey: 'basics.code',
|
||||
categoryI18nKey: 'basic',
|
||||
entries: [{ i18nKey: 'inline' }, { i18nKey: 'block' }]
|
||||
topics: [{ i18nKey: 'inline' }, { i18nKey: 'block' }]
|
||||
},
|
||||
{
|
||||
i18nKey: 'basics.lists',
|
||||
categoryI18nKey: 'basic',
|
||||
entries: [{ i18nKey: 'unordered' }, { i18nKey: 'ordered' }]
|
||||
topics: [{ i18nKey: 'unordered' }, { i18nKey: 'ordered' }]
|
||||
},
|
||||
{
|
||||
i18nKey: 'basics.images',
|
||||
categoryI18nKey: 'basic',
|
||||
entries: [{ i18nKey: 'basic' }, { i18nKey: 'size' }]
|
||||
topics: [{ i18nKey: 'basic' }, { i18nKey: 'size' }]
|
||||
},
|
||||
{
|
||||
i18nKey: 'basics.links',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -22,7 +22,7 @@ export class BlockquoteAppExtension extends AppExtension {
|
|||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'blockquoteTags', entries: [{ i18nKey: 'name' }, { i18nKey: 'color' }, { i18nKey: 'time' }] }]
|
||||
return [{ i18nKey: 'blockquoteTags', topics: [{ i18nKey: 'name' }, { i18nKey: 'color' }, { i18nKey: 'time' }] }]
|
||||
}
|
||||
|
||||
buildAutocompletion(): CompletionSource[] {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -22,7 +22,7 @@ export class CsvTableAppExtension extends AppExtension {
|
|||
}
|
||||
|
||||
buildCheatsheetExtensions(): CheatsheetExtension[] {
|
||||
return [{ i18nKey: 'csv', entries: [{ i18nKey: 'table' }, { i18nKey: 'header' }] }]
|
||||
return [{ i18nKey: 'csv', topics: [{ i18nKey: 'table' }, { i18nKey: 'header' }] }]
|
||||
}
|
||||
|
||||
buildAutocompletion(): CompletionSource[] {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -23,7 +23,7 @@ export class HighlightedCodeFenceAppExtension extends AppExtension {
|
|||
return [
|
||||
{
|
||||
i18nKey: 'codeHighlighting',
|
||||
entries: [{ i18nKey: 'language' }, { i18nKey: 'lineNumbers' }, { i18nKey: 'lineWrapping' }]
|
||||
topics: [{ i18nKey: 'language' }, { i18nKey: 'lineNumbers' }, { i18nKey: 'lineWrapping' }]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export class TableOfContentsAppExtension extends AppExtension {
|
|||
return [
|
||||
{
|
||||
i18nKey: 'toc',
|
||||
entries: [
|
||||
topics: [
|
||||
{
|
||||
i18nKey: 'basic'
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue