Move and rename files (2/4) (#987)

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
Tilman Vatteroth 2021-02-02 00:03:47 +01:00 committed by GitHub
parent 1b7abf9f27
commit 123f959fb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
145 changed files with 586 additions and 301 deletions

View file

@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { DeletionModal } from '../../common/modals/deletion-modal'
import { SidebarButton } from './sidebar-button'
import { SpecificSidebarEntryProps } from './types'
export const DeleteNoteSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ hide, className }) => {
useTranslation()
const [showDialog, setShowDialog] = useState(false)
return (
<Fragment>
<SidebarButton icon={"trash"} className={className} hide={hide} onClick={() => setShowDialog(true)}>
<Trans i18nKey={'landing.history.menu.deleteNote'}/>
</SidebarButton>
<DeletionModal
onConfirm={() => setShowDialog(false)}
deletionButtonI18nKey={'editor.modal.deleteNote.button'}
show={showDialog}
onHide={() => setShowDialog(false)}
titleI18nKey={'editor.modal.deleteNote.title'}>
<h5><Trans i18nKey={'editor.modal.deleteNote.question'}/></h5>
<ul>
<li> noteTitle</li>
</ul>
<h6>
<Trans i18nKey={'editor.modal.deleteNote.warning'}/>
</h6>
</DeletionModal>
</Fragment>
)
}

View file

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { DocumentInfoModal } from '../document-bar/document-info/document-info-modal'
import { SidebarButton } from './sidebar-button'
import { SpecificSidebarEntryProps } from './types'
export const DocumentInfoSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({className, hide}) => {
const [showModal, setShowModal] = useState(false)
useTranslation()
return (
<Fragment>
<SidebarButton hide={hide} className={className} icon={"line-chart"} onClick={() => setShowModal(true)}>
<Trans i18nKey={'editor.modal.documentInfo.title'} />
</SidebarButton>
<DocumentInfoModal show={showModal} onHide={() => setShowModal(false)}/>
</Fragment>
)
}

View file

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useCallback } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { useNoteMarkdownContent } from '../../../hooks/common/use-note-markdown-content'
import { download } from '../../common/download/download'
import { SidebarButton } from './sidebar-button'
export const ExportMarkdownSidebarEntry: React.FC = () => {
useTranslation()
const markdownContent = useNoteMarkdownContent()
const onClick = useCallback(() => {
download(markdownContent, `title.md`, 'text/markdown') //todo: replace hard coded title with redux
}, [markdownContent])
return (
<SidebarButton data-cy={"menu-export-markdown"} onClick={onClick} icon={'file-text'}>
<Trans i18nKey={'editor.export.markdown-file'}/>
</SidebarButton>
)
}

View file

@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import links from '../../../links.json'
import { ExportMarkdownSidebarEntry } from './export-markdown-sidebar-entry'
import { SidebarButton } from './sidebar-button'
import { SidebarMenu } from './sidebar-menu'
import { DocumentSidebarMenuSelection, SpecificSidebarMenuProps } from './types'
export const ExportMenuSidebarMenu: React.FC<SpecificSidebarMenuProps> = (
{
className,
menuId,
onClick,
selectedMenuId
}) => {
useTranslation()
const hide = selectedMenuId !== DocumentSidebarMenuSelection.NONE && selectedMenuId !== menuId
const expand = selectedMenuId === menuId
const onClickHandler = useCallback(() => {
onClick(menuId)
}, [menuId, onClick])
return (
<Fragment>
<SidebarButton data-cy={"menu-export"} hide={hide} icon={expand ? "arrow-left" : "cloud-download"}
className={className} onClick={onClickHandler}>
<Trans i18nKey={'editor.documentBar.export'}/>
</SidebarButton>
<SidebarMenu expand={expand}>
<SidebarButton icon={"github"}>
Gist
</SidebarButton>
<SidebarButton icon={"gitlab"}>
Gitlab Snippet
</SidebarButton>
<ExportMarkdownSidebarEntry/>
<SidebarButton icon={"file-code-o"}>
HTML
</SidebarButton>
<SidebarButton icon={"file-code-o"}>
<Trans i18nKey='editor.export.rawHtml'/>
</SidebarButton>
<SidebarButton icon={"file-pdf-o"}>
<a className='small text-muted' dir={'auto'} href={links.faq} target={'_blank'} rel='noopener noreferrer'>
<Trans i18nKey={'editor.export.pdf'}/>
&nbsp;
<span className={'text-primary'}>
<Trans i18nKey={'common.why'}/>
</span>
</a>
</SidebarButton>
</SidebarMenu>
</Fragment>
)
}

View file

@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback, useRef } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { useNoteMarkdownContent } from '../../../hooks/common/use-note-markdown-content'
import { setNoteMarkdownContent } from '../../../redux/note-details/methods'
import { SidebarButton } from './sidebar-button'
import { UploadInput } from './upload-input'
export const ImportMarkdownSidebarEntry: React.FC = () => {
const markdownContent = useNoteMarkdownContent()
useTranslation()
const onImportMarkdown = useCallback((file: File) => {
return new Promise<void>((resolve, reject) => {
const fileReader = new FileReader()
fileReader.addEventListener('load', () => {
const newContent = fileReader.result as string
setNoteMarkdownContent(markdownContent.length === 0 ? newContent : `${markdownContent}\n${newContent}`)
})
fileReader.addEventListener('loadend', () => {
resolve()
})
fileReader.addEventListener('error', (error) => {
reject(error)
})
fileReader.readAsText(file)
})
}, [markdownContent])
const clickRef = useRef<(() => void)>()
const buttonClick = useCallback(() => {
clickRef.current?.()
}, [])
return (
<Fragment>
<SidebarButton data-cy={"menu-import-markdown"} icon={"file-text-o"} onClick={buttonClick}>
<Trans i18nKey={'editor.import.file'}/>
</SidebarButton>
<UploadInput onLoad={onImportMarkdown} data-cy={"menu-import-markdown-input"}
acceptedFiles={'.md, text/markdown, text/plain'} onClickRef={clickRef}/>
</Fragment>
)
}

View file

@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { ImportMarkdownSidebarEntry } from './import-markdown-sidebar-entry'
import { SidebarButton } from './sidebar-button'
import { SidebarMenu } from './sidebar-menu'
import { DocumentSidebarMenuSelection, SpecificSidebarMenuProps } from './types'
export const ImportMenuSidebarMenu: React.FC<SpecificSidebarMenuProps> = (
{
className,
menuId,
onClick,
selectedMenuId
}) => {
useTranslation()
const hide = selectedMenuId !== DocumentSidebarMenuSelection.NONE && selectedMenuId !== menuId
const expand = selectedMenuId === menuId
const onClickHandler = useCallback(() => {
onClick(menuId)
}, [menuId, onClick])
return (
<Fragment>
<SidebarButton data-cy={"menu-import"} hide={hide} icon={expand ? "arrow-left" : "cloud-upload"}
className={className} onClick={onClickHandler}>
<Trans i18nKey={'editor.documentBar.import'}/>
</SidebarButton>
<SidebarMenu expand={expand}>
<SidebarButton icon={"github"}>
Gist
</SidebarButton>
<SidebarButton icon={"gitlab"}>
Gitlab Snippet
</SidebarButton>
<SidebarButton icon={"clipboard"}>
<Trans i18nKey={'editor.import.clipboard'}/>
</SidebarButton>
<ImportMarkdownSidebarEntry/>
</SidebarMenu>
</Fragment>
)
}

View file

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { PermissionModal } from '../document-bar/permissions/permission-modal'
import { SidebarButton } from './sidebar-button'
import { SpecificSidebarEntryProps } from './types'
export const PermissionsSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({className, hide}) => {
const [showModal, setShowModal] = useState(false)
useTranslation()
return (
<Fragment>
<SidebarButton hide={hide} className={className} icon={"lock"} onClick={() => setShowModal(true)}>
<Trans i18nKey={'editor.modal.permissions.title'}/>
</SidebarButton>
<PermissionModal show={showModal} onHide={() => setShowModal(false)}/>
</Fragment>
)
}

View file

@ -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 { Trans, useTranslation } from 'react-i18next'
import { SidebarButton } from './sidebar-button'
import { SpecificSidebarEntryProps } from './types'
export const PinNoteSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ className, hide }) => {
useTranslation()
const isPinned = true
const i18nKey = isPinned ? 'editor.documentBar.pinNoteToHistory' : 'editor.documentBar.pinnedToHistory'
return (
<SidebarButton icon={'thumb-tack'} className={className} hide={hide}>
<Trans i18nKey={i18nKey}/>
</SidebarButton>
)
}

View file

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useState } from 'react'
import { Trans } from 'react-i18next'
import { RevisionModal } from '../document-bar/revisions/revision-modal'
import { SidebarButton } from './sidebar-button'
import { SpecificSidebarEntryProps } from './types'
export const RevisionSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({className, hide}) => {
const [showModal, setShowModal] = useState(false)
return (
<Fragment>
<SidebarButton hide={hide} className={className} icon={"history"} onClick={() => setShowModal(true)}>
<Trans i18nKey={'editor.modal.revision.title'}/>
</SidebarButton>
<RevisionModal show={showModal} onHide={() => setShowModal(false)}/>
</Fragment>
)
}

View file

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { ShareModal } from '../document-bar/share/share-modal'
import { SidebarButton } from './sidebar-button'
import { SpecificSidebarEntryProps } from './types'
export const ShareSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({className, hide}) => {
const [showModal, setShowModal] = useState(false)
useTranslation()
return (
<Fragment>
<SidebarButton hide={hide} className={className} icon={"share"} onClick={() => setShowModal(true)}>
<Trans i18nKey={'editor.modal.shareLink.title'}/>
</SidebarButton>
<ShareModal show={showModal} onHide={() => setShowModal(false)}/>
</Fragment>
)
}

View file

@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
import { IconName } from '../../common/fork-awesome/types'
import { ShowIf } from '../../common/show-if/show-if'
import { SidebarEntryProps } from './types'
export type SidebarEntryVariant = "primary"
export const SidebarButton: React.FC<SidebarEntryProps> = ({children, icon, className, variant,buttonRef, hide, ...props }) => {
return (
<button ref={buttonRef}
className={`sidebar-entry ${hide ? 'hide' : ''} ${variant ? `sidebar-entry-${variant}` : ''} ${className ?? ''}`} {...props} >
<ShowIf condition={!!icon}>
<span className={'sidebar-icon'}>
<ForkAwesomeIcon icon={icon as IconName}/>
</span>
</ShowIf>
<span className={'sidebar-text'}>
{children}
</span>
</button>
)
}

View file

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { SidebarMenuProps } from './types'
export const SidebarMenu: React.FC<SidebarMenuProps> = ({children, expand}) => {
return (
<div className={`sidebar-menu ${expand ? 'show' : ''}`}>
<div className={`d-flex flex-column`}>
{children}
</div>
</div>
);
}

View file

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useCallback, useRef, useState } from 'react'
import { useClickAway } from 'react-use'
import { DeleteNoteSidebarEntry } from './delete-note-sidebar-entry'
import { DocumentInfoSidebarEntry } from './document-info-sidebar-entry'
import { ExportMenuSidebarMenu } from './export-menu-sidebar-menu'
import { ImportMenuSidebarMenu } from './import-menu-sidebar-menu'
import { PermissionsSidebarEntry } from './permissions-sidebar-entry'
import { PinNoteSidebarEntry } from './pin-note-sidebar-entry'
import { RevisionSidebarEntry } from './revision-sidebar-entry'
import { ShareSidebarEntry } from './share-sidebar-entry'
import "./style/theme.scss"
import { DocumentSidebarMenuSelection } from './types'
import { UsersOnlineSidebarMenu } from './users-online-sidebar-menu/users-online-sidebar-menu'
export const Sidebar: React.FC = () => {
const sideBarRef = useRef<HTMLDivElement>(null)
const [selectedMenu, setSelectedMenu] = useState<DocumentSidebarMenuSelection>(DocumentSidebarMenuSelection.NONE)
useClickAway(sideBarRef, () => {
setSelectedMenu(DocumentSidebarMenuSelection.NONE)
})
const toggleValue = useCallback((toggleValue: DocumentSidebarMenuSelection): void => {
const newValue = selectedMenu === toggleValue ? DocumentSidebarMenuSelection.NONE : toggleValue
setSelectedMenu(newValue)
}, [selectedMenu])
const selectionIsNotNone = selectedMenu !== DocumentSidebarMenuSelection.NONE
return (
<div className="slide-sidebar">
<div ref={sideBarRef} className={`sidebar-inner ${selectionIsNotNone ? 'show' : ''}`}>
<UsersOnlineSidebarMenu menuId={DocumentSidebarMenuSelection.USERS_ONLINE}
selectedMenuId={selectedMenu} onClick={toggleValue}/>
<DocumentInfoSidebarEntry hide={selectionIsNotNone}/>
<RevisionSidebarEntry hide={selectionIsNotNone}/>
<PermissionsSidebarEntry hide={selectionIsNotNone}/>
<ImportMenuSidebarMenu menuId={DocumentSidebarMenuSelection.IMPORT}
selectedMenuId={selectedMenu} onClick={toggleValue}/>
<ExportMenuSidebarMenu menuId={DocumentSidebarMenuSelection.EXPORT}
selectedMenuId={selectedMenu} onClick={toggleValue}/>
<ShareSidebarEntry hide={selectionIsNotNone}/>
<DeleteNoteSidebarEntry hide={selectionIsNotNone}/>
<PinNoteSidebarEntry hide={selectionIsNotNone}/>
</div>
</div>
)
}

View file

@ -0,0 +1,49 @@
/*!
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.slide-sidebar {
background: $body-bg;
.sidebar-menu>div {
background: $sidebar-menu-bg;
box-shadow: inset 0 7px 7px -6px $sidebar-menu-shadow;
}
.sidebar-inner {
background: $body-bg;
}
.sidebar-entry {
color: $dark;
&:hover {
background: $entry-hover-bg;
color: $dark;
}
&.sidebar-entry-primary {
background: $primary;
color: $white;
&:hover {
color: $primary;
background: $entry-hover-bg;
}
}
}
}
@mixin text-stroke($shadow-color) {
text-shadow: 0px 0px 1px $shadow-color,
1px 0px 1px $shadow-color,
0px 1px 1px $shadow-color,
-1px 0px 1px $shadow-color,
0px -1px 1px $shadow-color,
1px 1px 1px $shadow-color,
-1px -1px 1px $shadow-color,
-1px 1px 1px $shadow-color,
1px -1px 1px $shadow-color;
}

View file

@ -0,0 +1,100 @@
/*!
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
$height: 40px;
$menu-width: 175px;
.slide-sidebar {
flex: 0 0 $height;
position: relative;
.sidebar-inner {
height: 100%;
display: flex;
overflow-y: auto;
flex-direction: column;
position: absolute;
z-index: 999;
width: $menu-width;
top: 0;
left: 0;
transition: left 0.3s;
box-shadow: 0 0 0px rgba(0, 0, 0, 0.15);
&:hover, &.show {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
left: (-$menu-width + $height);
}
}
.sidebar-menu {
transition: height 0.2s, flex-basis 0.2s;
display: flex;
flex-direction: column;
overflow: hidden;
flex: 0 1 0;
height: 0;
&.show {
height: 100%;
flex-basis: 100%;
overflow-y: auto;
}
}
.sidebar-entry {
height: $height;
flex: 0 0 $height;
width: 100%;
display: flex;
align-items: center;
border: solid 1px rgba(0, 0, 0, 0.15);
user-select: none;
cursor: pointer;
background: transparent;
padding: 0;
transition: height 0.2s, flex-basis 0.2s;
overflow: hidden;
&.hide {
flex-basis: 0;
height: 0px;
border-width: 0px;
.sidebar-icon {
opacity: 0;
}
.sidebar-text {
opacity: 0;
}
}
.sidebar-icon {
transition: opacity 0.2s;
opacity: 1;
height: $height;
width: $height;
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
flex: 0 0 40px;
}
.sidebar-text {
height: 100%;
display: flex;
align-items: center;
opacity: 1;
transition: opacity 0.2s;
text-align: left;
flex: 1 1 0;
width: 0;
}
}
}

View file

@ -0,0 +1,21 @@
/*!
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
body.dark {
@import "../../../../style/variables.dark";
$entry-hover-bg: lighten($body-bg, 10%);
$sidebar-menu-bg: $body-bg;
$sidebar-menu-shadow: #1d1d1d;
@import "colors";
}
@import "../../../../style/variables.light";
$entry-hover-bg: darken($body-bg, 10%);
$sidebar-menu-bg: $body-bg;
$sidebar-menu-shadow: #bbbbbb;
@import "colors";
@import "layout";

View file

@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { RefObject } from 'react'
import { IconName } from '../../common/fork-awesome/types'
import { SidebarEntryVariant } from './sidebar-button'
export interface SpecificSidebarEntryProps {
className?: string
hide?: boolean
onClick?: () => void
}
export interface SidebarEntryProps {
icon?: IconName
variant?: SidebarEntryVariant
buttonRef?: RefObject<HTMLButtonElement>
hide?: boolean
className?: string
onClick?: () => void
"data-cy"?: string
}
export interface SidebarMenuProps {
expand?: boolean
}
export enum DocumentSidebarMenuSelection {
NONE,
USERS_ONLINE,
IMPORT,
EXPORT
}
export interface SpecificSidebarMenuProps {
className?: string
onClick: (menuId: DocumentSidebarMenuSelection) => void
selectedMenuId: DocumentSidebarMenuSelection
menuId: DocumentSidebarMenuSelection
}

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { MutableRefObject, useCallback, useEffect, useRef } from 'react'
export interface UploadInputProps {
onLoad: (file: File) => Promise<void>
acceptedFiles: string
onClickRef: MutableRefObject<(() => void) | undefined>
"data-cy"?: string
}
export const UploadInput: React.FC<UploadInputProps> = ({ onLoad, acceptedFiles, onClickRef, ...props }) => {
const fileInputReference = useRef<HTMLInputElement>(null)
const onClick = useCallback(() => {
const fileInput = fileInputReference.current
if (!fileInput) {
return
}
fileInput.addEventListener('change', () => {
if (!fileInput.files || fileInput.files.length < 1) {
return
}
const file = fileInput.files[0]
onLoad(file).then(() => {
fileInput.value = ''
}).catch((error) => {
console.error(error)
})
})
fileInput.click()
}, [onLoad])
useEffect(() => {
onClickRef.current = onClick
})
return (
<input data-cy={props["data-cy"]} type='file' ref={fileInputReference} className='d-none' accept={acceptedFiles}/>
)
}

View file

@ -0,0 +1,20 @@
/*!
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.activeIndicator {
$indicator-size: 12px;
border-radius: $indicator-size;
height: $indicator-size;
width: $indicator-size;
&.active {
background-color: #5cb85c;
}
&.inactive {
background-color: #d20000;
}
}

View file

@ -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 './active-indicator.scss'
export enum ActiveIndicatorStatus {
ACTIVE = 'active',
INACTIVE = 'inactive'
}
export interface ActiveIndicatorProps {
status: ActiveIndicatorStatus;
}
export const ActiveIndicator: React.FC<ActiveIndicatorProps> = ({ status }) => {
return (
<span className={`activeIndicator ${status}`}/>
)
}

View file

@ -0,0 +1,42 @@
/*!
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
body.dark {
@import "../../../../style/variables.dark";
$entry-hover-bg: lighten($body-bg, 10%);
$sidebar-menu-bg: $body-bg;
$sidebar-menu-shadow: #1d1d1d;
@import "../style/colors";
}
@import "../../../../style/variables.light";
$entry-hover-bg: darken($body-bg, 10%);
$sidebar-menu-bg: $body-bg;
$sidebar-menu-shadow: #bbbbbb;
@import "../style/colors";
.slide-sidebar .sidebar-entry.online-entry {
&:hover {
.sidebar-icon:after {
color: $primary;
$shadow-color: #ffffff;
@include text-stroke(#ffffff);
}
}
--users-online: '0';
.sidebar-icon:after {
@include text-stroke($primary);
content: var(--users-online);
position: absolute;
right: 5px;
bottom: 3px;
font-size: 0.9rem;
color: #ffffff;
line-height: 1;
}
}

View file

@ -0,0 +1,30 @@
/*!
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.user-line-color-indicator {
border-left: 3px solid;
min-height: 30px;
height: 100%;
flex: 0 0 3px;
}
.user-avatar {
flex: 0 0 20px;
}
.user-line-name {
text-overflow: ellipsis;
flex: 1 1 0;
overflow: hidden;
}
.active-indicator-container {
height: 100%;
display: flex;
flex: 0 0 20px;
align-items: center;
justify-content: center;
}

View file

@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { UserAvatar } from '../../../common/user-avatar/user-avatar'
import { ActiveIndicator, ActiveIndicatorStatus } from './active-indicator'
import './user-line.scss'
export interface UserLineProps {
name: string;
photo: string;
color: string;
status: ActiveIndicatorStatus;
}
export const UserLine: React.FC<UserLineProps> = ({ name, photo, color, status }) => {
return (
<div className={'d-flex align-items-center h-100 w-100'}>
<div className='d-inline-flex align-items-bottom user-line-color-indicator' style={{ borderLeftColor: color }}/>
<UserAvatar photo={photo} name={name} additionalClasses={'flex-fill overflow-hidden px-2 text-nowrap w-100'}/>
<div className={"active-indicator-container"}>
<ActiveIndicator status={status} />
</div>
</div>
)
}

View file

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { SidebarButton } from '../sidebar-button'
import { SidebarMenu } from '../sidebar-menu'
import { DocumentSidebarMenuSelection, SpecificSidebarMenuProps } from '../types'
import { ActiveIndicatorStatus } from './active-indicator'
import './online-counter.scss'
import { UserLine } from './user-line'
export const UsersOnlineSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
className,
menuId,
onClick,
selectedMenuId
}) => {
const buttonRef = useRef<HTMLButtonElement>(null)
const [counter] = useState(2)
useTranslation()
useEffect(() => {
const value = `${counter}`
buttonRef.current?.style.setProperty('--users-online', `"${value}"`)
}, [counter])
const hide = selectedMenuId !== DocumentSidebarMenuSelection.NONE && selectedMenuId !== menuId
const expand = selectedMenuId === menuId
const onClickHandler = useCallback(() => {
onClick(menuId)
}, [menuId, onClick])
return (
<Fragment>
<SidebarButton hide={hide} buttonRef={buttonRef} onClick={onClickHandler} icon={expand ? "arrow-left" : "users"}
variant={'primary'} className={`online-entry ${className ?? ''}`}>
<Trans i18nKey={'editor.onlineStatus.online'}/>
</SidebarButton>
<SidebarMenu expand={expand}>
<SidebarButton>
<UserLine name="Philip Molares" photo="/img/avatar.png" color="red" status={ActiveIndicatorStatus.INACTIVE}/>
</SidebarButton>
<SidebarButton>
<UserLine name="Tilman Vatteroth" photo="/img/avatar.png" color="blue" status={ActiveIndicatorStatus.ACTIVE}/>
</SidebarButton>
</SidebarMenu>
</Fragment>
)
}