Skip to content
This repository has been archived by the owner on Nov 23, 2022. It is now read-only.

Commit

Permalink
finished the GUI Metadata
Browse files Browse the repository at this point in the history
Signed-off-by: Philip Molares <[email protected]>
  • Loading branch information
DerMolly committed Jan 30, 2021
1 parent 2a63a01 commit 925d4a5
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 74 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"i18next": "19.8.4",
"i18next-browser-languagedetector": "6.0.1",
"i18next-http-backend": "1.0.22",
"iso-639-1": "^2.1.7",
"js-yaml": "4.0.0",
"katex": "0.12.0",
"luxon": "1.25.0",
Expand Down
18 changes: 17 additions & 1 deletion public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,23 @@
"revisions": "<0></0> revisions are saved"
},
"metadataEditor": {
"title": "Metadata"
"title": "Edit Metadata",
"labels": {
"title": "Title",
"type": "Document type",
"description": "Description",
"tags": "Tags",
"lang": "Language",
"dir": "Text direction",
"breaks": "Breaks",
"robots": "Robots",
"GA": "Google Analytics",
"disqus": "Disqus",
"LTR": "left to right",
"RTL": "right to left",
"breaksOn": "hedgedoc newline style",
"breaksOff": "markdown newline style"
}
},
"gistImport": {
"title": "Import from Gist",
Expand Down
5 changes: 3 additions & 2 deletions src/components/common/fork-awesome/fork-awesome-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ export interface ForkAwesomeIconProps {
fixedWidth?: boolean
size?: IconSize
stacked?: boolean
onClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void
}

export const ForkAwesomeIcon: React.FC<ForkAwesomeIconProps> = ({ icon, fixedWidth = false, size, className, stacked = false }) => {
export const ForkAwesomeIcon: React.FC<ForkAwesomeIconProps> = ({ 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 (
<i className={`fa ${fixedWithClass} fa-${icon} ${extraClasses}`}/>
<i className={`fa ${fixedWithClass} fa-${icon} ${extraClasses}`} onClick={onClick}/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,45 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import React, { useCallback } from 'react'
import { InputLabelProps } from './input-label'
import React from 'react'
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { ForkAwesomeStack } from '../../../common/fork-awesome/fork-awesome-stack'
import { MetadataInputFieldProps } from './metadata-editor'

export const BooleanMetadataInput: React.FC<MetadataInputFieldProps<boolean> & InputLabelProps> = ({ label, content, onContentChange }) => {
const onChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
onContentChange(event.currentTarget.checked)
}, [onContentChange])
enum ButtonState {
ON,
OFF
}

export const BooleanMetadataInput: React.FC<MetadataInputFieldProps<boolean>> = ({ id, content, onContentChange }) => {
const {t} = useTranslation();

return (
<div className='d-flex flex-row'>
<input type="checkbox"
className="left"
checked={content}
onChange={onChange}
/>
&nbsp;
{label}
</div>
<ToggleButtonGroup
type="radio"
name={id}
id={id}
value={content ? ButtonState.ON : ButtonState.OFF}
className={'ml-2'}
>
<ToggleButton
value={ButtonState.ON}
variant="outline-secondary"
title={t('editor.modal.metadataEditor.labels.breaksOn')}
onChange={() => onContentChange(true)}
>
{t('editor.modal.metadataEditor.labels.breaksOn')}
</ToggleButton>
<ToggleButton
value={ButtonState.OFF}
variant="outline-secondary"
title={t('editor.modal.metadataEditor.labels.breaksOff')}
onChange={() => onContentChange(false)}
>
{t('editor.modal.metadataEditor.labels.breaksOff')}
</ToggleButton>
</ToggleButtonGroup>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@
*/

import React, { Fragment, useCallback } from 'react'
import { iso6391 } from '../../yaml-metadata/yaml-metadata'
import { MetadataInputFieldProps, SelectMetadataOptions } from './metadata-editor'

export const DatalistMetadataInput: React.FC<MetadataInputFieldProps<string> & SelectMetadataOptions<iso6391>> = ({ id, content, onContentChange, options }) => {
export const DatalistMetadataInput: React.FC<MetadataInputFieldProps<string> & SelectMetadataOptions<string>> = ({ id, content, onContentChange, options }) => {
const onChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
onContentChange(event.currentTarget.value)
}, [onContentChange])

return (
<Fragment>
<input list={id} onChange={onChange} value={content}/>
<input list={id} onChange={onChange} value={content} className={'ml-2'}/>
<datalist id={id}>
{options.map((option: iso6391) => {
{options.map(option => {
return (
<option value={option}>
{option}
Expand Down
100 changes: 62 additions & 38 deletions src/components/editor/document-bar/metadata-editor/metadata-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import React, { useState } from 'react'
import { Modal } from 'react-bootstrap'
import ISO from 'iso-639-1'
import React, { useCallback } from 'react'
import { Modal, Row } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../redux'
import { setDocumentContent } from '../../../../redux/document-content/methods'
import { CommonModal } from '../../../common/modals/common-modal'
import { TextDirection, YAMLMetaData } from '../../yaml-metadata/yaml-metadata'
import { supportedDocumentTypes, YAMLMetaData } from '../../yaml-metadata/yaml-metadata'
import { BooleanMetadataInput } from './boolean-metadata-input'
import { InputLabel } from './input-label'
import { DatalistMetadataInput } from './datalist-metadata-input'
import { TextDirectionMetadataInput } from './text-direction-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,
Expand All @@ -32,7 +37,10 @@ export interface SelectMetadataOptions<T> {
}

export const MetadataEditor: React.FC<MetadataEditorProps> = ({ show, onHide }) => {
const [yamlMetadata, setYamlMetadata] = useState<Omit<YAMLMetaData, 'opengraph'>>({
const { t } = useTranslation();
const yamlMetadata = useSelector((state: ApplicationState) => state.documentContent.metadata);
const documentContent = useSelector((state: ApplicationState) => state.documentContent.content);
/*const [yamlMetadata, setYamlMetadata] = useState<Omit<YAMLMetaData, 'opengraph'>>({
title: "Test Title",
description: "Test Description\nwith two lines",
tags: ["tag1", "tag2"],
Expand All @@ -44,46 +52,62 @@ export const MetadataEditor: React.FC<MetadataEditorProps> = ({ show, onHide })
disqus: "test disqus string",
type: '',
deprecatedTagsSyntax: false
})
})*/

const setMarkdown = useCallback((changes: Partial<YAMLMetaData>) => {
const newMetadata = Object.assign(yamlMetadata, changes);

setDocumentContent(documentContent);
}, [documentContent])

return (
<CommonModal
size='xl'
show={show}
onHide={onHide}
closeButton={true}
titleI18nKey={'editor.modal.metadataEditor.title'}>
<Modal.Body>
<InputLabel id={'title'} label={"title"}>
<StringMetadataInput id={'title'} content={yamlMetadata.title} onContentChange={title => setYamlMetadata({...yamlMetadata, title})}/>
</InputLabel>
<InputLabel id={'description'} label={"description"}>
<StringMetadataTextarea id={'description'} content={yamlMetadata.description} onContentChange={description => setYamlMetadata({...yamlMetadata, description})}/>
</InputLabel>
<InputLabel id={'tags'} label={"tags"}>
<TagsMetadataInput id={'tags'} content={yamlMetadata.tags} onContentChange={tags => setYamlMetadata({...yamlMetadata, tags})}/>
</InputLabel>
<InputLabel id={'robots'} label={"robots"}>
<StringMetadataInput id={'robots'} content={yamlMetadata.robots} onContentChange={robots => setYamlMetadata({...yamlMetadata, robots})}/>
</InputLabel>
<InputLabel id={'lang'} label={"lang"}>
<DatalistMetadataInput id={'lang'} options={['aa', 'ab', 'af', 'de-at']} content={yamlMetadata.lang} onContentChange={lang => setYamlMetadata({...yamlMetadata, lang})}/>
</InputLabel>
<InputLabel id={'dir'} label={"dir"}>
<TextDirectionMetadataInput
id={'dir'}
content={yamlMetadata.dir}
onContentChange={dir => setYamlMetadata({...yamlMetadata, dir })}
/>
</InputLabel>
<InputLabel id={'breaks'} label={"breaks"}>
<BooleanMetadataInput id={'breaks'} label={"breaks"} content={yamlMetadata.breaks} onContentChange={breaks => setYamlMetadata({...yamlMetadata, breaks})}/>
</InputLabel>
<InputLabel id={'GA'} label={"GA"}>
<StringMetadataInput id={'GA'} content={yamlMetadata.GA} onContentChange={GA => setYamlMetadata({...yamlMetadata, GA})}/>
</InputLabel>
<InputLabel id={'disqus'} label={"disqus"}>
<StringMetadataInput id={'disqus'} content={yamlMetadata.disqus} onContentChange={disqus => setYamlMetadata({...yamlMetadata, disqus})}/>
</InputLabel>
<Row>
<div className='col-6'>
<InputLabel id={'title'} label={t('editor.modal.metadataEditor.labels.title')}>
<StringMetadataInput id={'title'} content={yamlMetadata.title} onContentChange={title => setYamlMetadata({...yamlMetadata, title})}/>
</InputLabel>
<InputLabel id={'type'} label={t('editor.modal.metadataEditor.labels.type')}>
<DatalistMetadataInput id={'type'} options={supportedDocumentTypes} content={yamlMetadata.type} onContentChange={type => setYamlMetadata({...yamlMetadata, type})}/>
</InputLabel>
<InputLabel id={'dir'} label={t('editor.modal.metadataEditor.labels.dir')}>
<TextDirectionMetadataInput
id={'dir'}
content={yamlMetadata.dir}
onContentChange={dir => setYamlMetadata({...yamlMetadata, dir })}
/>
</InputLabel>
<InputLabel id={'description'} label={t('editor.modal.metadataEditor.labels.description')}>
<StringMetadataTextarea id={'description'} content={yamlMetadata.description} onContentChange={description => setYamlMetadata({...yamlMetadata, description})}/>
</InputLabel>
<InputLabel id={'disqus'} label={t('editor.modal.metadataEditor.labels.disqus')}>
<StringMetadataInput id={'disqus'} content={yamlMetadata.disqus} onContentChange={disqus => setYamlMetadata({...yamlMetadata, disqus})}/>
</InputLabel>
</div>
<div className='col-6'>
<InputLabel id={'lang'} label={t('editor.modal.metadataEditor.labels.lang')}>
<DatalistMetadataInput id={'lang'} options={ISO.getAllCodes()} content={yamlMetadata.lang} onContentChange={lang => setYamlMetadata({...yamlMetadata, lang})}/>
</InputLabel>
<InputLabel id={'robots'} label={t('editor.modal.metadataEditor.labels.robots')}>
<StringMetadataInput id={'robots'} content={yamlMetadata.robots} onContentChange={robots => setYamlMetadata({...yamlMetadata, robots})}/>
</InputLabel>
<InputLabel id={'breaks'} label={t('editor.modal.metadataEditor.labels.breaks')}>
<BooleanMetadataInput id={'breaks'} content={yamlMetadata.breaks} onContentChange={breaks => setYamlMetadata({...yamlMetadata, breaks})}/>
</InputLabel>
<InputLabel id={'tags'} label={t('editor.modal.metadataEditor.labels.tags')}>
<TagsMetadataInput id={'tags'} content={yamlMetadata.tags} onContentChange={tags => setYamlMetadata({...yamlMetadata, tags})}/>
</InputLabel>
<InputLabel id={'GA'} label={t('editor.modal.metadataEditor.labels.GA')}>
<StringMetadataInput id={'GA'} content={yamlMetadata.GA} onContentChange={GA => setYamlMetadata({...yamlMetadata, GA})}/>
</InputLabel>
</div>
</Row>
</Modal.Body>
</CommonModal>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const StringMetadataInput: React.FC<MetadataInputFieldProps<string>> = ({
return (
<input id={id}
type="text"
className={'ml-2'}
value={content}
onChange={onChange}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const StringMetadataTextarea: React.FC<MetadataInputFieldProps<string>> =
return (
<textarea
id={id}
className={'ml-2'}
value={content}
onChange={onChange}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,27 @@ export const TagsMetadataInput: React.FC<MetadataInputFieldProps<string[]>> = ({
setNewTag(event.currentTarget.value);
}, [])

const dismissTag = useCallback((clickedTag: string) => {
onContentChange(content.filter(tag => tag !== clickedTag))
}, [content, onContentChange])

return (
<Fragment>
<div className='pl-1 d-flex flex-row mb-2 mt-1 overflow-x-scroll'>
<div className='pl-1 d-flex flex-row mb-2 mt-1 overflow-x-scroll ml-2'>
{
content.map(tag => {
return (
<div className='rounded-pill mr-1 px-2 bg-primary tag-bubble' key={tag}>
{tag}
<ForkAwesomeIcon icon={'times'} className='pl-1'/>
<ForkAwesomeIcon icon={'times'} className='pl-1' onClick={() => dismissTag(tag)}/>
</div>
)
})
}
</div>
<input type="text"
id={id}
className='w-100 px-2'
className='w-100 px-2 ml-2'
value={newTag}
onKeyDown={onKeyDown}
onChange={onChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,42 @@ SPDX-License-Identifier: AGPL-3.0-only

import React from 'react'
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { TextDirection } from '../../yaml-metadata/yaml-metadata'
import { MetadataInputFieldProps } from './metadata-editor'


export const TextDirectionMetadataInput: React.FC<MetadataInputFieldProps<TextDirection>> = ({ id, content, onContentChange }) => {
const {t} = useTranslation();

return (
<ToggleButtonGroup
type="radio"
name={id}
id={id}
value={content}
className="ml-2"
className={'ml-2'}
>
<ToggleButton
value={TextDirection.LTR}
variant="outline-secondary"
title={'ltr'}
title={t('editor.modal.metadataEditor.labels.LTR')}
onChange={() => onContentChange(TextDirection.LTR)}
>
<ForkAwesomeIcon icon={'align-left'}/>
&nbsp;
left to right
{t('editor.modal.metadataEditor.labels.LTR')}
</ToggleButton>
<ToggleButton
value={TextDirection.RTL}
variant="outline-secondary"
title={'ltr'}
title={t('editor.modal.metadataEditor.labels.RTL')}
onChange={() => onContentChange(TextDirection.RTL)}
>
<ForkAwesomeIcon icon={'align-right'}/>
&nbsp;
right to left
{t('editor.modal.metadataEditor.labels.RTL')}
</ToggleButton>
</ToggleButtonGroup>
)
Expand Down
Loading

0 comments on commit 925d4a5

Please sign in to comment.