diff --git a/package.json b/package.json index 2170835287..16e97ba1fd 100644 --- a/package.json +++ b/package.json @@ -48,26 +48,27 @@ "eslint-plugin-flowtype": "5.2.0", "eslint-plugin-import": "2.22.1", "eslint-plugin-jsx-a11y": "6.4.1", - "eslint-plugin-node": "11.1.0", - "eslint-plugin-promise": "4.2.1", - "fast-deep-equal": "3.1.3", - "firacode": "5.2.0", - "flowchart.js": "1.15.0", - "fork-awesome": "1.1.7", - "highlight.js": "10.5.0", - "i18next": "19.8.7", - "i18next-browser-languagedetector": "6.0.1", - "i18next-http-backend": "1.1.0", - "js-yaml": "4.0.0", - "katex": "0.12.0", - "luxon": "1.25.0", - "markdown-it": "12.0.4", - "markdown-it-abbr": "1.0.4", - "markdown-it-anchor": "7.0.2", - "markdown-it-container": "3.0.0", - "markdown-it-deflist": "2.1.0", - "markdown-it-emoji": "2.0.0", - "markdown-it-footnote": "3.0.2", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-promise": "4.2.1", + "fast-deep-equal": "3.1.3", + "firacode": "5.2.0", + "flowchart.js": "1.15.0", + "fork-awesome": "1.1.7", + "highlight.js": "10.5.0", + "i18next": "19.8.7", + "i18next-browser-languagedetector": "6.0.1", + "i18next-http-backend": "1.1.0", + "iso-639-1": "2.1.8", + "js-yaml": "4.0.0", + "katex": "0.12.0", + "luxon": "1.25.0", + "markdown-it": "12.0.4", + "markdown-it-abbr": "1.0.4", + "markdown-it-anchor": "7.0.2", + "markdown-it-container": "3.0.0", + "markdown-it-deflist": "2.1.0", + "markdown-it-emoji": "2.0.0", + "markdown-it-footnote": "3.0.2", "markdown-it-front-matter": "0.2.3", "markdown-it-imsize": "2.0.1", "markdown-it-ins": "3.0.1", diff --git a/public/locales/en.json b/public/locales/en.json index c7a726851d..5bf88bf46e 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -338,26 +338,45 @@ "modal": { "snippetImport": { "title": "Import from Snippet", - "selectProject": "Select From Available Projects", - "selectSnippet": "Select From Available Snippets" + "selectProject": "Select From Available Projects", + "selectSnippet": "Select From Available Snippets" }, - "documentInfo": { - "title": "Document info", - "created": "<0> created this note <1>", - "edited": "<0> was the last editor <1>", - "usersContributed": "<0> users contributed to this document", - "revisions": "<0> revisions are saved" - }, - "gistImport": { - "title": "Import from Gist", - "insertGistUrl": "Paste your gist url here…" - }, - "snippetExport": { - "title": "Export to Snippet", - "visibilityLevel": "Select Visibility Level" - }, - "revision": { - "title": "Revisions", + "documentInfo": { + "title": "Document info", + "created": "<0> created this note <1>", + "edited": "<0> was the last editor <1>", + "usersContributed": "<0> users contributed to this document", + "revisions": "<0> revisions are saved" + }, + "metadataEditor": { + "title": "Edit Metadata", + "labels": { + "title": "Title", + "type": "Document type", + "description": "Description", + "tags": "Tags", + "lang": "Language", + "dir": "Text direction", + "breaks": "New line style", + "robots": "Robots", + "GA": "Google Analytics", + "disqus": "Disqus", + "LTR": "left to right", + "RTL": "right to left", + "breaksOn": "hedgedoc style", + "breaksOff": "markdown style" + } + }, + "gistImport": { + "title": "Import from Gist", + "insertGistUrl": "Paste your gist url here…" + }, + "snippetExport": { + "title": "Export to Snippet", + "visibilityLevel": "Select Visibility Level" + }, + "revision": { + "title": "Revisions", "revertButton": "Revert", "error": "An error occurred while fetching the revisions of this note.", "length": "Length", diff --git a/src/components/common/fork-awesome/fork-awesome-icon.tsx b/src/components/common/fork-awesome/fork-awesome-icon.tsx index 4b7c5ab7dd..9c4a306aa6 100644 --- a/src/components/common/fork-awesome/fork-awesome-icon.tsx +++ b/src/components/common/fork-awesome/fork-awesome-icon.tsx @@ -1,10 +1,10 @@ /* - SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - - SPDX-License-Identifier: AGPL-3.0-only + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only */ -import React from 'react' +import React, { MouseEventHandler } from 'react' import { IconName, IconSize } from './types' export interface ForkAwesomeIconProps { @@ -13,14 +13,15 @@ export interface ForkAwesomeIconProps { fixedWidth?: boolean size?: IconSize stacked?: boolean + onClick?: MouseEventHandler } -export const ForkAwesomeIcon: React.FC = ({ icon, fixedWidth = false, size, className, stacked = false }) => { +export const ForkAwesomeIcon: React.FC = ({ icon, fixedWidth = false, size, className, stacked = false, onClick }) => { const fixedWithClass = fixedWidth ? 'fa-fw' : '' const sizeClass = size ? `-${ size }` : (stacked ? '-1x' : '') const stackClass = stacked ? '-stack' : '' const extraClasses = `${ className ?? '' } ${ sizeClass || stackClass ? `fa${ stackClass }${ sizeClass }` : '' }` return ( - + ) } diff --git a/src/components/editor-page/document-bar/share/share-modal.tsx b/src/components/editor-page/document-bar/share/share-modal.tsx index 043376d65f..8c8f647a65 100644 --- a/src/components/editor-page/document-bar/share/share-modal.tsx +++ b/src/components/editor-page/document-bar/share/share-modal.tsx @@ -16,6 +16,7 @@ import { CopyableField } from '../../../common/copyable/copyable-field/copyable- import { CommonModal } from '../../../common/modals/common-modal' import { ShowIf } from '../../../common/show-if/show-if' import { EditorPagePathParams } from '../../editor-page' +import { NoteType } from '../../note-frontmatter/note-frontmatter' export interface ShareModalProps { show: boolean, @@ -44,7 +45,7 @@ export const ShareModal: React.FC = ({ show, onHide }) => { - + diff --git a/src/components/editor-page/metadata-editor/breaks-metadata-input.tsx b/src/components/editor-page/metadata-editor/breaks-metadata-input.tsx new file mode 100644 index 0000000000..d8fb206786 --- /dev/null +++ b/src/components/editor-page/metadata-editor/breaks-metadata-input.tsx @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import React from 'react' +import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap' +import { Trans, useTranslation } from 'react-i18next' +import { MetadataInputFieldProps } from './metadata-editor' + +enum ButtonState { + ON, + OFF +} + +export const BreaksMetadataInput: React.FC> = ({ id, content, onContentChange }) => { + const { t } = useTranslation() + + return ( + + onContentChange(true) }> + + + onContentChange(false) }> + + + + ) +} diff --git a/src/components/editor-page/metadata-editor/datalist-metadata-input.tsx b/src/components/editor-page/metadata-editor/datalist-metadata-input.tsx new file mode 100644 index 0000000000..b1a89b9e98 --- /dev/null +++ b/src/components/editor-page/metadata-editor/datalist-metadata-input.tsx @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import React, { Fragment, useCallback } from 'react' +import { MetadataInputFieldProps, SelectMetadataOptions } from './metadata-editor' + +export const DatalistMetadataInput: React.FC & SelectMetadataOptions> = ({ id, content, onContentChange, options }) => { + const onChange = useCallback((event: React.ChangeEvent) => { + onContentChange(event.currentTarget.value) + }, [onContentChange]) + + return ( + + + + { options.map(option => { + return ( + + ) + }) } + + + ) +} diff --git a/src/components/editor-page/metadata-editor/input-label.scss b/src/components/editor-page/metadata-editor/input-label.scss new file mode 100644 index 0000000000..3e087ae3a7 --- /dev/null +++ b/src/components/editor-page/metadata-editor/input-label.scss @@ -0,0 +1,9 @@ +/*! + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +.tighter { + margin-bottom: -0.5px !important; +} diff --git a/src/components/editor-page/metadata-editor/input-label.tsx b/src/components/editor-page/metadata-editor/input-label.tsx new file mode 100644 index 0000000000..0ba42acb8f --- /dev/null +++ b/src/components/editor-page/metadata-editor/input-label.tsx @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import React from 'react' +import './input-label.scss' +import { Form } from 'react-bootstrap' + +export interface InputLabelProps { + id: string + label: string +} + +export const InputLabel: React.FC = ({ id, label, children }) => { + return ( + + + { children } + + ) +} diff --git a/src/components/editor-page/metadata-editor/metadata-editor.tsx b/src/components/editor-page/metadata-editor/metadata-editor.tsx new file mode 100644 index 0000000000..9d4756d12b --- /dev/null +++ b/src/components/editor-page/metadata-editor/metadata-editor.tsx @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ISO from 'iso-639-1' +import React, { useCallback } from 'react' +import { Col, Modal, Row } from 'react-bootstrap' +import { useTranslation } from 'react-i18next' +import { useSelector } from 'react-redux' +import { ApplicationState } from '../../../redux' +import { setNoteFrontmatter } from '../../../redux/note-details/methods' +import { CommonModal } from '../../common/modals/common-modal' +import { NoteFrontmatter, NoteType } from '../note-frontmatter/note-frontmatter' +import { BreaksMetadataInput } from './breaks-metadata-input' +import { DatalistMetadataInput } from './datalist-metadata-input' +import { InputLabel } from './input-label' +import { StringMetadataInput } from './string-metadata-input' +import { StringMetadataTextarea } from './string-metadata-textarea' +import { TagsMetadataInput } from './tags-metadata-input' +import { TextDirectionMetadataInput } from './text-direction-metadata-input' + +export interface MetadataEditorProps { + show: boolean, + onHide: () => void +} + +export interface MetadataInputFieldProps { + id: string + content: T + onContentChange: (newContent: T) => void +} + +export interface SelectMetadataOptions { + options: T[] +} + +export const MetadataEditor: React.FC = ({ show, onHide }) => { + const { t } = useTranslation() + const yamlMetadata = useSelector((state: ApplicationState) => state.noteDetails.frontmatter) + const noteDetails = useSelector((state: ApplicationState) => state.noteDetails.markdownContent) + /*const [yamlMetadata, setNoteFrontmatter] = useState>({ + title: "Test Title", + description: "Test Description\nwith two lines", + tags: ["tag1", "tag2"], + robots: "", + lang: "de-at", + dir: TextDirection.LTR, + breaks: false, + GA: "test GA string", + disqus: "test disqus string", + type: '', + deprecatedTagsSyntax: false + })*/ + + const setMarkdown = useCallback((changes: Partial) => { + const newMetadata = Object.assign(yamlMetadata, changes) + +// setnoteDetails(noteDetails) + }, [noteDetails]) + + return ( + + + + + + setNoteFrontmatter({ ...yamlMetadata, title }) }/> + + + setNoteFrontmatter({ ...yamlMetadata, type: (type as NoteType) }) }/> + + + setNoteFrontmatter({ ...yamlMetadata, dir }) }/> + + + setNoteFrontmatter({ ...yamlMetadata, description }) }/> + + + setNoteFrontmatter({ ...yamlMetadata, disqus }) }/> + + + + + setNoteFrontmatter({ ...yamlMetadata, lang }) }/> + + + setNoteFrontmatter({ ...yamlMetadata, robots }) }/> + + + setNoteFrontmatter({ ...yamlMetadata, breaks }) }/> + + + setNoteFrontmatter({ ...yamlMetadata, tags }) }/> + + + setNoteFrontmatter({ ...yamlMetadata, GA }) }/> + + + + + + ) +} diff --git a/src/components/editor-page/metadata-editor/string-metadata-input.tsx b/src/components/editor-page/metadata-editor/string-metadata-input.tsx new file mode 100644 index 0000000000..5d556c9505 --- /dev/null +++ b/src/components/editor-page/metadata-editor/string-metadata-input.tsx @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import React, { useCallback } from 'react' +import { MetadataInputFieldProps } from './metadata-editor' + +export const StringMetadataInput: React.FC> = ({ id, content, onContentChange }) => { + const onChange = useCallback((event: React.ChangeEvent) => { + onContentChange(event.currentTarget.value) + }, [onContentChange]) + + return ( + + ) +} diff --git a/src/components/editor-page/metadata-editor/string-metadata-textarea.tsx b/src/components/editor-page/metadata-editor/string-metadata-textarea.tsx new file mode 100644 index 0000000000..5e7f2268a6 --- /dev/null +++ b/src/components/editor-page/metadata-editor/string-metadata-textarea.tsx @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import React, { useCallback } from 'react' +import { MetadataInputFieldProps } from './metadata-editor' + +export const StringMetadataTextarea: React.FC> = ({ id, content, onContentChange }) => { + const onChange = useCallback((event: React.ChangeEvent) => { + onContentChange(event.currentTarget.value) + }, [onContentChange]) + + return ( +