mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-06-04 00:48:51 -04:00
Switch the base framework from Create React App to Next.JS
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
a979b6ffdd
commit
77a60c6c48
361 changed files with 5130 additions and 9605 deletions
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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 type { IconName } from '../../common/fork-awesome/types'
|
||||
import { ShowIf } from '../../common/show-if/show-if'
|
||||
import type { 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>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
.sidebar-button {
|
||||
@import '../style/variables.scss';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@mixin colors {
|
||||
color: $dark;
|
||||
|
||||
|
||||
&.sidebar-button-primary {
|
||||
background: $primary;
|
||||
color: $white;
|
||||
|
||||
&:hover {
|
||||
color: $primary;
|
||||
background: $entry-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $entry-hover-bg;
|
||||
color: $dark;
|
||||
}
|
||||
}
|
||||
|
||||
@import "../../../../../global-styles/variables.light";
|
||||
$entry-hover-bg: darken($body-bg, 10%);
|
||||
@include colors;
|
||||
|
||||
:global(body.dark) & {
|
||||
@import "../../../../../global-styles/variables.dark";
|
||||
$entry-hover-bg: darken($body-bg, 10%);
|
||||
@include colors;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react'
|
||||
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
|
||||
import type { IconName } from '../../../common/fork-awesome/types'
|
||||
import { ShowIf } from '../../../common/show-if/show-if'
|
||||
import type { SidebarEntryProps } from '../types'
|
||||
import styles from './sidebar-button.module.scss'
|
||||
|
||||
export interface SidebarButton extends SidebarEntryProps {
|
||||
variant?: 'primary'
|
||||
}
|
||||
|
||||
/**
|
||||
* A button that should be rendered in the sidebar.
|
||||
*
|
||||
* @param children The react elements in the button
|
||||
* @param icon The icon at the left side of the button
|
||||
* @param className Additional css class names
|
||||
* @param buttonRef A reference to the button
|
||||
* @param hide Should be {@code true} if the button should be invisible
|
||||
* @param variant An alternative theme for the button
|
||||
* @param props Other button props
|
||||
*/
|
||||
export const SidebarButton: React.FC<SidebarButton> = ({
|
||||
children,
|
||||
icon,
|
||||
className,
|
||||
buttonRef,
|
||||
hide,
|
||||
variant,
|
||||
...props
|
||||
}) => {
|
||||
const variantClass = useMemo(() => {
|
||||
return variant !== undefined ? styles['sidebar-button-' + variant] : ''
|
||||
}, [variant])
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={buttonRef}
|
||||
className={`${styles['sidebar-button']} ${variantClass} ${hide ? styles['hide'] : ''} ${className ?? ''}`}
|
||||
{...props}>
|
||||
<ShowIf condition={!!icon}>
|
||||
<span className={`sidebar-button-icon ${styles['sidebar-icon']}`}>
|
||||
<ForkAwesomeIcon icon={icon as IconName} />
|
||||
</span>
|
||||
</ShowIf>
|
||||
<span className={styles['sidebar-text']}>{children}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@import "../../../../../global-styles/variables.light";
|
||||
& > div {
|
||||
background: $body-bg;
|
||||
box-shadow: inset 0 7px 7px -6px #bbbbbb;
|
||||
}
|
||||
|
||||
:global(body.dark) & {
|
||||
@import "../../../../../global-styles/variables.dark";
|
||||
& > div {
|
||||
background: $body-bg;
|
||||
box-shadow: inset 0 7px 7px -6px #1d1d1d;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,11 +5,12 @@
|
|||
*/
|
||||
|
||||
import React from 'react'
|
||||
import type { SidebarMenuProps } from './types'
|
||||
import type { SidebarMenuProps } from '../types'
|
||||
import styles from './sidebar-menu.module.scss'
|
||||
|
||||
export const SidebarMenu: React.FC<SidebarMenuProps> = ({ children, expand }) => {
|
||||
return (
|
||||
<div className={`sidebar-menu ${expand ? 'show' : ''}`}>
|
||||
<div className={`${styles['sidebar-menu']} ${expand ? styles['show'] : ''}`}>
|
||||
<div className={`d-flex flex-column`}>{children}</div>
|
||||
</div>
|
||||
)
|
|
@ -6,15 +6,15 @@
|
|||
|
||||
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 { DeleteNoteSidebarEntry } from './specific-sidebar-entries/delete-note-sidebar-entry'
|
||||
import { DocumentInfoSidebarEntry } from './specific-sidebar-entries/document-info-sidebar-entry'
|
||||
import { ExportMenuSidebarMenu } from './specific-sidebar-entries/export-menu-sidebar-menu'
|
||||
import { ImportMenuSidebarMenu } from './specific-sidebar-entries/import-menu-sidebar-menu'
|
||||
import { PermissionsSidebarEntry } from './specific-sidebar-entries/permissions-sidebar-entry'
|
||||
import { PinNoteSidebarEntry } from './specific-sidebar-entries/pin-note-sidebar-entry'
|
||||
import { RevisionSidebarEntry } from './specific-sidebar-entries/revision-sidebar-entry'
|
||||
import { ShareSidebarEntry } from './specific-sidebar-entries/share-sidebar-entry'
|
||||
import styles from './style/sidebar.module.scss'
|
||||
import { DocumentSidebarMenuSelection } from './types'
|
||||
import { UsersOnlineSidebarMenu } from './users-online-sidebar-menu/users-online-sidebar-menu'
|
||||
|
||||
|
@ -37,8 +37,8 @@ export const Sidebar: React.FC = () => {
|
|||
const selectionIsNotNone = selectedMenu !== DocumentSidebarMenuSelection.NONE
|
||||
|
||||
return (
|
||||
<div className='slide-sidebar'>
|
||||
<div ref={sideBarRef} className={`sidebar-inner ${selectionIsNotNone ? 'show' : ''}`}>
|
||||
<div className={styles['slide-sidebar']}>
|
||||
<div ref={sideBarRef} className={`${styles['sidebar-inner']} ${selectionIsNotNone ? styles['show'] : ''}`}>
|
||||
<UsersOnlineSidebarMenu
|
||||
menuId={DocumentSidebarMenuSelection.USERS_ONLINE}
|
||||
selectedMenuId={selectedMenu}
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
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 type { SpecificSidebarEntryProps } from './types'
|
||||
import { DeletionModal } from '../../../common/modals/deletion-modal'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import type { SpecificSidebarEntryProps } from '../types'
|
||||
|
||||
export const DeleteNoteSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ hide, className }) => {
|
||||
useTranslation()
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
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 type { SpecificSidebarEntryProps } from './types'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { DocumentInfoModal } from '../../document-bar/document-info/document-info-modal'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import type { SpecificSidebarEntryProps } from '../types'
|
||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||
|
||||
export const DocumentInfoSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ className, hide }) => {
|
||||
const [showModal, setShowModal] = useState(false)
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
import React, { useCallback } from 'react'
|
||||
import sanitize from 'sanitize-filename'
|
||||
import { store } from '../../../redux'
|
||||
import { store } from '../../../../redux'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { download } from '../../common/download/download'
|
||||
import { SidebarButton } from './sidebar-button'
|
||||
import { useNoteMarkdownContent } from '../../../hooks/common/use-note-markdown-content'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { download } from '../../../common/download/download'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import { useNoteMarkdownContent } from '../../../../hooks/common/use-note-markdown-content'
|
||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||
|
||||
export const ExportMarkdownSidebarEntry: React.FC = () => {
|
||||
const { t } = useTranslation()
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
import React, { Fragment, useCallback } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import links from '../../../links.json'
|
||||
import links from '../../../../links.json'
|
||||
import { ExportMarkdownSidebarEntry } from './export-markdown-sidebar-entry'
|
||||
import { SidebarButton } from './sidebar-button'
|
||||
import { SidebarMenu } from './sidebar-menu'
|
||||
import type { SpecificSidebarMenuProps } from './types'
|
||||
import { DocumentSidebarMenuSelection } from './types'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import { SidebarMenu } from '../sidebar-menu/sidebar-menu'
|
||||
import type { SpecificSidebarMenuProps } from '../types'
|
||||
import { DocumentSidebarMenuSelection } from '../types'
|
||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||
|
||||
export const ExportMenuSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
|
||||
className,
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
import React, { Fragment, useCallback, useRef } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useNoteMarkdownContent } from '../../../hooks/common/use-note-markdown-content'
|
||||
import { setNoteContent } from '../../../redux/note-details/methods'
|
||||
import { SidebarButton } from './sidebar-button'
|
||||
import { UploadInput } from './upload-input'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { useNoteMarkdownContent } from '../../../../hooks/common/use-note-markdown-content'
|
||||
import { setNoteContent } from '../../../../redux/note-details/methods'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import { UploadInput } from '../upload-input'
|
||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||
|
||||
export const ImportMarkdownSidebarEntry: React.FC = () => {
|
||||
const markdownContent = useNoteMarkdownContent()
|
|
@ -7,11 +7,11 @@
|
|||
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 type { SpecificSidebarMenuProps } from './types'
|
||||
import { DocumentSidebarMenuSelection } from './types'
|
||||
import { cypressId } from '../../../utils/cypress-attribute'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import { SidebarMenu } from '../sidebar-menu/sidebar-menu'
|
||||
import type { SpecificSidebarMenuProps } from '../types'
|
||||
import { DocumentSidebarMenuSelection } from '../types'
|
||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||
|
||||
export const ImportMenuSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
|
||||
className,
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
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 type { SpecificSidebarEntryProps } from './types'
|
||||
import { PermissionModal } from '../../document-bar/permissions/permission-modal'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import type { SpecificSidebarEntryProps } from '../types'
|
||||
|
||||
export const PermissionsSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ className, hide }) => {
|
||||
const [showModal, setShowModal] = useState(false)
|
|
@ -6,17 +6,15 @@
|
|||
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { SidebarButton } from './sidebar-button'
|
||||
import type { SpecificSidebarEntryProps } from './types'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import type { EditorPagePathParams } from '../editor-page'
|
||||
import { toggleHistoryEntryPinning } from '../../../redux/history/methods'
|
||||
import { showErrorNotification } from '../../../redux/ui-notifications/methods'
|
||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import type { SpecificSidebarEntryProps } from '../types'
|
||||
import { toggleHistoryEntryPinning } from '../../../../redux/history/methods'
|
||||
import { showErrorNotification } from '../../../../redux/ui-notifications/methods'
|
||||
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||
|
||||
export const PinNoteSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ className, hide }) => {
|
||||
useTranslation()
|
||||
const { id } = useParams<EditorPagePathParams>()
|
||||
const id = useApplicationState((state) => state.noteDetails.id)
|
||||
const history = useApplicationState((state) => state.history)
|
||||
|
||||
const isPinned = useMemo(() => {
|
|
@ -4,21 +4,27 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React, { Fragment, useState } from 'react'
|
||||
import React, { Fragment, useCallback, useState } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { RevisionModal } from '../document-bar/revisions/revision-modal'
|
||||
import { SidebarButton } from './sidebar-button'
|
||||
import type { SpecificSidebarEntryProps } from './types'
|
||||
import { RevisionModal } from '../../document-bar/revisions/revision-modal'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import type { SpecificSidebarEntryProps } from '../types'
|
||||
|
||||
export const RevisionSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ className, hide }) => {
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const onHide = useCallback(() => {
|
||||
setShowModal(false)
|
||||
}, [])
|
||||
const onShow = useCallback(() => {
|
||||
setShowModal(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<SidebarButton hide={hide} className={className} icon={'history'} onClick={() => setShowModal(true)}>
|
||||
<SidebarButton hide={hide} className={className} icon={'history'} onClick={onShow}>
|
||||
<Trans i18nKey={'editor.modal.revision.title'} />
|
||||
</SidebarButton>
|
||||
<RevisionModal show={showModal} onHide={() => setShowModal(false)} />
|
||||
<RevisionModal show={showModal} onHide={onHide} />
|
||||
</Fragment>
|
||||
)
|
||||
}
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
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 type { SpecificSidebarEntryProps } from './types'
|
||||
import { ShareModal } from '../../document-bar/share/share-modal'
|
||||
import { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import type { SpecificSidebarEntryProps } from '../types'
|
||||
|
||||
export const ShareSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ className, hide }) => {
|
||||
const [showModal, setShowModal] = useState(false)
|
|
@ -1,53 +0,0 @@
|
|||
/*!
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
&.icon-highlighted > .sidebar-icon {
|
||||
color: $orange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*!
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
40
src/components/editor-page/sidebar/style/sidebar.module.scss
Normal file
40
src/components/editor-page/sidebar/style/sidebar.module.scss
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
.slide-sidebar {
|
||||
@import "./variables.scss";
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
:global(body.dark) & {
|
||||
@import "../../../../../global-styles/variables.dark";
|
||||
background: $body-bg;
|
||||
}
|
||||
|
||||
@import "../../../../../global-styles/variables.light";
|
||||
background: $body-bg;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*!
|
||||
* 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";
|
8
src/components/editor-page/sidebar/style/variables.scss
Normal file
8
src/components/editor-page/sidebar/style/variables.scss
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
$height: 40px;
|
||||
$menu-width: 175px;
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import type { RefObject } from 'react'
|
||||
import type { IconName } from '../../common/fork-awesome/types'
|
||||
import type { SidebarEntryVariant } from './sidebar-button'
|
||||
import type { PropsWithDataCypressId } from '../../../utils/cypress-attribute'
|
||||
|
||||
export interface SpecificSidebarEntryProps {
|
||||
|
@ -17,7 +16,6 @@ export interface SpecificSidebarEntryProps {
|
|||
|
||||
export interface SidebarEntryProps extends PropsWithDataCypressId {
|
||||
icon?: IconName
|
||||
variant?: SidebarEntryVariant
|
||||
buttonRef?: RefObject<HTMLButtonElement>
|
||||
hide?: boolean
|
||||
className?: string
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*!
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
import React from 'react'
|
||||
import { UserAvatar } from '../../../common/user-avatar/user-avatar'
|
||||
import type { ActiveIndicatorStatus } from './active-indicator'
|
||||
import { ActiveIndicator } from './active-indicator'
|
||||
import './user-line.scss'
|
||||
import type { ActiveIndicatorStatus } from '../users-online-sidebar-menu/active-indicator'
|
||||
import { ActiveIndicator } from '../users-online-sidebar-menu/active-indicator'
|
||||
import styles from './user-line.module.scss'
|
||||
|
||||
export interface UserLineProps {
|
||||
name: string
|
||||
|
@ -20,9 +20,12 @@ export interface UserLineProps {
|
|||
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 }} />
|
||||
<div
|
||||
className={`d-inline-flex align-items-bottom ${styles['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'}>
|
||||
<div className={styles['active-indicator-container']}>
|
||||
<ActiveIndicator status={status} />
|
||||
</div>
|
||||
</div>
|
|
@ -1,4 +1,4 @@
|
|||
/*!
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react'
|
||||
import './active-indicator.scss'
|
||||
import styles from './active-indicator.module.scss'
|
||||
|
||||
export enum ActiveIndicatorStatus {
|
||||
ACTIVE = 'active',
|
||||
|
@ -17,5 +17,5 @@ export interface ActiveIndicatorProps {
|
|||
}
|
||||
|
||||
export const ActiveIndicator: React.FC<ActiveIndicatorProps> = ({ status }) => {
|
||||
return <span className={`activeIndicator ${status}`} />
|
||||
return <span className={`${styles['activeIndicator']} ${status}`} />
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
.online-entry {
|
||||
@import "../../../../../global-styles/variables.light";
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
:global(.sidebar-button-icon):after {
|
||||
color: $primary;
|
||||
$shadow-color: #ffffff;
|
||||
@include text-stroke(#ffffff);
|
||||
}
|
||||
}
|
||||
|
||||
--users-online: '0';
|
||||
|
||||
:global(.sidebar-button-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;
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*!
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -6,13 +6,14 @@
|
|||
|
||||
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 { SidebarButton } from '../sidebar-button/sidebar-button'
|
||||
import { SidebarMenu } from '../sidebar-menu/sidebar-menu'
|
||||
import type { SpecificSidebarMenuProps } from '../types'
|
||||
import { DocumentSidebarMenuSelection } from '../types'
|
||||
import { ActiveIndicatorStatus } from './active-indicator'
|
||||
import './online-counter.scss'
|
||||
import { UserLine } from './user-line'
|
||||
import styles from './online-counter.module.scss'
|
||||
import { UserLine } from '../user-line/user-line'
|
||||
import { useCustomizeAssetsUrl } from '../../../../hooks/common/use-customize-assets-url'
|
||||
|
||||
export const UsersOnlineSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
|
||||
className,
|
||||
|
@ -35,6 +36,8 @@ export const UsersOnlineSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
|
|||
onClick(menuId)
|
||||
}, [menuId, onClick])
|
||||
|
||||
const avatarUrl = useCustomizeAssetsUrl() + 'img/avatar.png'
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<SidebarButton
|
||||
|
@ -42,21 +45,16 @@ export const UsersOnlineSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
|
|||
buttonRef={buttonRef}
|
||||
onClick={onClickHandler}
|
||||
icon={expand ? 'arrow-left' : 'users'}
|
||||
variant={'primary'}
|
||||
className={`online-entry ${className ?? ''}`}>
|
||||
className={`${styles['online-entry']} ${className ?? ''}`}
|
||||
variant={'primary'}>
|
||||
<Trans i18nKey={'editor.onlineStatus.online'} />
|
||||
</SidebarButton>
|
||||
<SidebarMenu expand={expand}>
|
||||
<SidebarButton>
|
||||
<UserLine name='Philip Molares' photo='/img/avatar.png' color='red' status={ActiveIndicatorStatus.INACTIVE} />
|
||||
<UserLine name='Philip Molares' photo={avatarUrl} color='red' status={ActiveIndicatorStatus.INACTIVE} />
|
||||
</SidebarButton>
|
||||
<SidebarButton>
|
||||
<UserLine
|
||||
name='Tilman Vatteroth'
|
||||
photo='/img/avatar.png'
|
||||
color='blue'
|
||||
status={ActiveIndicatorStatus.ACTIVE}
|
||||
/>
|
||||
<UserLine name='Tilman Vatteroth' photo={avatarUrl} color='blue' status={ActiveIndicatorStatus.ACTIVE} />
|
||||
</SidebarButton>
|
||||
</SidebarMenu>
|
||||
</Fragment>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue