mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-22 09:46:30 -05:00
fix: extract app bar into layout slot
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
18a1e79d9f
commit
b3fb1bbf30
35 changed files with 258 additions and 207 deletions
9
frontend/src/app/(editor)/@appBar/cheatsheet/page.tsx
Normal file
9
frontend/src/app/(editor)/@appBar/cheatsheet/page.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return null
|
||||||
|
}
|
11
frontend/src/app/(editor)/@appBar/default.tsx
Normal file
11
frontend/src/app/(editor)/@appBar/default.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { BaseAppBar } from '../../../components/layout/app-bar/base-app-bar'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return <BaseAppBar />
|
||||||
|
}
|
11
frontend/src/app/(editor)/@appBar/history/page.tsx
Normal file
11
frontend/src/app/(editor)/@appBar/history/page.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
import { BaseAppBar } from '../../../../components/layout/app-bar/base-app-bar'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return <BaseAppBar>History</BaseAppBar>
|
||||||
|
}
|
11
frontend/src/app/(editor)/@appBar/intro/page.tsx
Normal file
11
frontend/src/app/(editor)/@appBar/intro/page.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
import { BaseAppBar } from '../../../../components/layout/app-bar/base-app-bar'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return <BaseAppBar>Landing</BaseAppBar>
|
||||||
|
}
|
11
frontend/src/app/(editor)/@appBar/login/page.tsx
Normal file
11
frontend/src/app/(editor)/@appBar/login/page.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
import { BaseAppBar } from '../../../../components/layout/app-bar/base-app-bar'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return <BaseAppBar>Login</BaseAppBar>
|
||||||
|
}
|
|
@ -29,14 +29,14 @@ exports[`app bar contains note title and read-only marker when having only read
|
||||||
first part
|
first part
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
|
<span
|
||||||
|
class="m-0 text-truncate"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
class="text-secondary me-2"
|
class="text-secondary me-2"
|
||||||
>
|
>
|
||||||
BootstrapIconMock_Lock
|
BootstrapIconMock_Lock
|
||||||
</span>
|
</span>
|
||||||
<span
|
|
||||||
class="text-truncate mw-100"
|
|
||||||
>
|
|
||||||
Note Title Test
|
Note Title Test
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,7 +55,7 @@ exports[`app bar contains note title when editor is synced 1`] = `
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="text-truncate mw-100"
|
class="m-0 text-truncate"
|
||||||
>
|
>
|
||||||
Note Title Test
|
Note Title Test
|
||||||
</span>
|
</span>
|
|
@ -3,16 +3,16 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import * as UseApplicationStateModule from '../../../hooks/common/use-application-state'
|
import * as UseApplicationStateModule from '../../../../../hooks/common/use-application-state'
|
||||||
import type { ApplicationState } from '../../../redux/application-state'
|
import type { ApplicationState } from '../../../../../redux/application-state'
|
||||||
import { mockI18n } from '../../../test-utils/mock-i18n'
|
import { mockI18n } from '../../../../../test-utils/mock-i18n'
|
||||||
import { EditorAppBar } from './editor-app-bar'
|
import { EditorAppBar } from './editor-app-bar'
|
||||||
import type { NoteGroupPermissionEntry, NoteUserPermissionEntry } from '@hedgedoc/commons'
|
import type { NoteGroupPermissionEntry, NoteUserPermissionEntry } from '@hedgedoc/commons'
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
jest.mock('./base-app-bar', () => ({
|
jest.mock('../../../../../components/layout/app-bar/base-app-bar', () => ({
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
BaseAppBar: ({ children }: PropsWithChildren) => (
|
BaseAppBar: ({ children }: PropsWithChildren) => (
|
||||||
<div>
|
<div>
|
||||||
|
@ -22,7 +22,7 @@ jest.mock('./base-app-bar', () => ({
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
jest.mock('../../../hooks/common/use-application-state')
|
jest.mock('../../../../../hooks/common/use-application-state')
|
||||||
|
|
||||||
const mockedCommonAppState = {
|
const mockedCommonAppState = {
|
||||||
noteDetails: {
|
noteDetails: {
|
|
@ -1,12 +1,14 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
import { RealtimeConnectionAlert } from '../../../../../components/editor-page/realtime-connection-alert/realtime-connection-alert'
|
||||||
import { RealtimeConnectionAlert } from '../../editor-page/realtime-connection-alert/realtime-connection-alert'
|
import { NoteTitleElement } from '../../../../../components/layout/app-bar/app-bar-elements/note-title-element/note-title-element'
|
||||||
import { NoteTitleElement } from './app-bar-elements/note-title-element/note-title-element'
|
import { BaseAppBar } from '../../../../../components/layout/app-bar/base-app-bar'
|
||||||
import { BaseAppBar } from './base-app-bar'
|
import { useApplicationState } from '../../../../../hooks/common/use-application-state'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
/**
|
/**
|
11
frontend/src/app/(editor)/@appBar/n/[noteId]/page.tsx
Normal file
11
frontend/src/app/(editor)/@appBar/n/[noteId]/page.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { EditorAppBar } from './editor-app-bar'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return <EditorAppBar />
|
||||||
|
}
|
11
frontend/src/app/(editor)/@appBar/not-found.tsx
Normal file
11
frontend/src/app/(editor)/@appBar/not-found.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { BaseAppBar } from '../../../components/layout/app-bar/base-app-bar'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return <BaseAppBar />
|
||||||
|
}
|
9
frontend/src/app/(editor)/@appBar/p/[noteId]/page.tsx
Normal file
9
frontend/src/app/(editor)/@appBar/p/[noteId]/page.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return null
|
||||||
|
}
|
11
frontend/src/app/(editor)/@appBar/profile/page.tsx
Normal file
11
frontend/src/app/(editor)/@appBar/profile/page.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
import { BaseAppBar } from '../../../../components/layout/app-bar/base-app-bar'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return <BaseAppBar>Profile</BaseAppBar>
|
||||||
|
}
|
11
frontend/src/app/(editor)/@appBar/register/page.tsx
Normal file
11
frontend/src/app/(editor)/@appBar/register/page.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
import { BaseAppBar } from '../../../../components/layout/app-bar/base-app-bar'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return <BaseAppBar>Register</BaseAppBar>
|
||||||
|
}
|
16
frontend/src/app/(editor)/@appBar/s/[noteId]/page.tsx
Normal file
16
frontend/src/app/(editor)/@appBar/s/[noteId]/page.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { NoteTitleElement } from '../../../../../components/layout/app-bar/app-bar-elements/note-title-element/note-title-element'
|
||||||
|
import { BaseAppBar } from '../../../../../components/layout/app-bar/base-app-bar'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function AppBar() {
|
||||||
|
return (
|
||||||
|
<BaseAppBar>
|
||||||
|
<NoteTitleElement />
|
||||||
|
</BaseAppBar>
|
||||||
|
)
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ import { LandingLayout } from '../../../components/landing-layout/landing-layout
|
||||||
import type { NextPage } from 'next'
|
import type { NextPage } from 'next'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { Row } from 'react-bootstrap'
|
import { Row } from 'react-bootstrap'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The page that shows the local and remote note history.
|
* The page that shows the local and remote note history.
|
||||||
|
@ -29,9 +29,6 @@ const HistoryPage: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
<LandingLayout>
|
<LandingLayout>
|
||||||
<HistoryToolbarStateContextProvider>
|
<HistoryToolbarStateContextProvider>
|
||||||
<h1 className='mb-4'>
|
|
||||||
<Trans i18nKey={'landing.navigation.history'} />
|
|
||||||
</h1>
|
|
||||||
<Row className={'justify-content-center mt-5 mb-3'}>
|
<Row className={'justify-content-center mt-5 mb-3'}>
|
||||||
<HistoryToolbar />
|
<HistoryToolbar />
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { CustomBranding } from '../../../components/common/custom-branding/custo
|
||||||
import { HedgeDocLogoVertical } from '../../../components/common/hedge-doc-logo/hedge-doc-logo-vertical'
|
import { HedgeDocLogoVertical } from '../../../components/common/hedge-doc-logo/hedge-doc-logo-vertical'
|
||||||
import { LogoSize } from '../../../components/common/hedge-doc-logo/logo-size'
|
import { LogoSize } from '../../../components/common/hedge-doc-logo/logo-size'
|
||||||
import { EditorToRendererCommunicatorContextProvider } from '../../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider'
|
import { EditorToRendererCommunicatorContextProvider } from '../../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider'
|
||||||
import { CoverButtons } from '../../../components/intro-page/cover-buttons/cover-buttons'
|
|
||||||
import { IntroCustomContent } from '../../../components/intro-page/intro-custom-content'
|
import { IntroCustomContent } from '../../../components/intro-page/intro-custom-content'
|
||||||
import { LandingLayout } from '../../../components/landing-layout/landing-layout'
|
import { LandingLayout } from '../../../components/landing-layout/landing-layout'
|
||||||
import type { NextPage } from 'next'
|
import type { NextPage } from 'next'
|
||||||
|
@ -33,7 +32,6 @@ const IntroPage: NextPage = () => {
|
||||||
<div className={'mb-5'}>
|
<div className={'mb-5'}>
|
||||||
<CustomBranding />
|
<CustomBranding />
|
||||||
</div>
|
</div>
|
||||||
<CoverButtons />
|
|
||||||
<IntroCustomContent />
|
<IntroCustomContent />
|
||||||
</div>
|
</div>
|
||||||
</EditorToRendererCommunicatorContextProvider>
|
</EditorToRendererCommunicatorContextProvider>
|
||||||
|
|
|
@ -14,12 +14,17 @@ import { StoreProvider } from '../../redux/store-provider'
|
||||||
import { baseUrlFromEnvExtractor } from '../../utils/base-url-from-env-extractor'
|
import { baseUrlFromEnvExtractor } from '../../utils/base-url-from-env-extractor'
|
||||||
import { configureLuxon } from '../../utils/configure-luxon'
|
import { configureLuxon } from '../../utils/configure-luxon'
|
||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
|
import type { PropsWithChildren } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { getConfig } from '../../api/config'
|
import { getConfig } from '../../api/config'
|
||||||
|
|
||||||
configureLuxon()
|
configureLuxon()
|
||||||
|
|
||||||
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
interface RootLayoutProps extends PropsWithChildren {
|
||||||
|
appBar: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function RootLayout({ children, appBar }: RootLayoutProps) {
|
||||||
const baseUrls = baseUrlFromEnvExtractor.extractBaseUrls()
|
const baseUrls = baseUrlFromEnvExtractor.extractBaseUrls()
|
||||||
const frontendConfig = await getConfig(baseUrls.editor)
|
const frontendConfig = await getConfig(baseUrls.editor)
|
||||||
|
|
||||||
|
@ -35,7 +40,12 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||||
<ApplicationLoader>
|
<ApplicationLoader>
|
||||||
<DarkMode />
|
<DarkMode />
|
||||||
<MotdModal />
|
<MotdModal />
|
||||||
<UiNotificationBoundary>{children}</UiNotificationBoundary>
|
<UiNotificationBoundary>
|
||||||
|
<div className={'d-flex flex-column vh-100'}>
|
||||||
|
{appBar}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</UiNotificationBoundary>
|
||||||
</ApplicationLoader>
|
</ApplicationLoader>
|
||||||
</StoreProvider>
|
</StoreProvider>
|
||||||
</FrontendConfigContextProvider>
|
</FrontendConfigContextProvider>
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { NoteLoadingBoundary } from '../../../../components/common/note-loading-
|
||||||
import { DocumentReadOnlyPageContent } from '../../../../components/document-read-only-page/document-read-only-page-content'
|
import { DocumentReadOnlyPageContent } from '../../../../components/document-read-only-page/document-read-only-page-content'
|
||||||
import { useNoteAndAppTitle } from '../../../../components/editor-page/head-meta-properties/use-note-and-app-title'
|
import { useNoteAndAppTitle } from '../../../../components/editor-page/head-meta-properties/use-note-and-app-title'
|
||||||
import { EditorToRendererCommunicatorContextProvider } from '../../../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider'
|
import { EditorToRendererCommunicatorContextProvider } from '../../../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider'
|
||||||
import { BaseAppBar } from '../../../../components/layout/app-bar/base-app-bar'
|
|
||||||
import type { NextPage } from 'next'
|
import type { NextPage } from 'next'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
@ -26,10 +25,7 @@ const DocumentReadOnlyPage: NextPage<PageParams> = ({ params }) => {
|
||||||
return (
|
return (
|
||||||
<EditorToRendererCommunicatorContextProvider>
|
<EditorToRendererCommunicatorContextProvider>
|
||||||
<NoteLoadingBoundary noteId={params.noteId}>
|
<NoteLoadingBoundary noteId={params.noteId}>
|
||||||
<div className={'d-flex flex-column mvh-100'}>
|
|
||||||
<BaseAppBar />
|
|
||||||
<DocumentReadOnlyPageContent />
|
<DocumentReadOnlyPageContent />
|
||||||
</div>
|
|
||||||
</NoteLoadingBoundary>
|
</NoteLoadingBoundary>
|
||||||
</EditorToRendererCommunicatorContextProvider>
|
</EditorToRendererCommunicatorContextProvider>
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { EditorAppBar } from '../layout/app-bar/editor-app-bar'
|
|
||||||
import { CommunicatorImageLightbox } from '../markdown-renderer/extensions/image/communicator-image-lightbox'
|
import { CommunicatorImageLightbox } from '../markdown-renderer/extensions/image/communicator-image-lightbox'
|
||||||
import { ExtensionEventEmitterProvider } from '../markdown-renderer/hooks/use-extension-event-emitter'
|
import { ExtensionEventEmitterProvider } from '../markdown-renderer/hooks/use-extension-event-emitter'
|
||||||
import { ChangeEditorContentContextProvider } from './change-content-context/codemirror-reference-context'
|
import { ChangeEditorContentContextProvider } from './change-content-context/codemirror-reference-context'
|
||||||
|
@ -69,8 +68,6 @@ export const EditorPageContent: React.FC = () => {
|
||||||
<ExtensionEventEmitterProvider>
|
<ExtensionEventEmitterProvider>
|
||||||
{editorExtensionComponents}
|
{editorExtensionComponents}
|
||||||
<CommunicatorImageLightbox />
|
<CommunicatorImageLightbox />
|
||||||
<div className={'d-flex flex-column vh-100'}>
|
|
||||||
<EditorAppBar />
|
|
||||||
<div className={'flex-fill d-flex h-100 w-100 overflow-hidden flex-row'}>
|
<div className={'flex-fill d-flex h-100 w-100 overflow-hidden flex-row'}>
|
||||||
<Splitter
|
<Splitter
|
||||||
left={leftPane}
|
left={leftPane}
|
||||||
|
@ -79,7 +76,6 @@ export const EditorPageContent: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</ExtensionEventEmitterProvider>
|
</ExtensionEventEmitterProvider>
|
||||||
</ChangeEditorContentContextProvider>
|
</ChangeEditorContentContextProvider>
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,6 +20,5 @@
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
background: var(--bs-body-bg);
|
background: var(--bs-body-bg);
|
||||||
box-shadow: inset 0 7px 7px -6px #bbbbbb;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.cover-button {
|
|
||||||
min-width: 200px;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
|
||||||
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
|
|
||||||
import styles from './cover-buttons.module.scss'
|
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
export const CoverButtons: React.FC = () => {
|
|
||||||
const userExists = useApplicationState((state) => !!state.user)
|
|
||||||
|
|
||||||
if (userExists) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='mb-5'>
|
|
||||||
<SignInButton className={styles['cover-button']} variant='success' size='lg' />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -3,8 +3,6 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { BaseAppBar } from '../layout/app-bar/base-app-bar'
|
|
||||||
import { HeaderBar } from './navigation/header-bar/header-bar'
|
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Container } from 'react-bootstrap'
|
import { Container } from 'react-bootstrap'
|
||||||
|
@ -16,14 +14,10 @@ import { Container } from 'react-bootstrap'
|
||||||
*/
|
*/
|
||||||
export const LandingLayout: React.FC<PropsWithChildren> = ({ children }) => {
|
export const LandingLayout: React.FC<PropsWithChildren> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Container>
|
||||||
<BaseAppBar />
|
|
||||||
<Container className='d-flex flex-column'>
|
|
||||||
<HeaderBar />
|
|
||||||
<div className={'d-flex flex-column justify-content-between flex-fill text-center'}>
|
<div className={'d-flex flex-column justify-content-between flex-fill text-center'}>
|
||||||
<main>{children}</main>
|
<main>{children}</main>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
|
||||||
import { HeaderNavLink } from './header-nav-link'
|
|
||||||
import React from 'react'
|
|
||||||
import { Navbar } from 'react-bootstrap'
|
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a header bar for the intro and history page.
|
|
||||||
*/
|
|
||||||
const HeaderBar: React.FC = () => {
|
|
||||||
useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Navbar className='justify-content-between'>
|
|
||||||
<div className='nav'>
|
|
||||||
<HeaderNavLink to='/intro' {...cypressId('navLinkIntro')}>
|
|
||||||
<Trans i18nKey='landing.navigation.intro' />
|
|
||||||
</HeaderNavLink>
|
|
||||||
<HeaderNavLink to='/history' {...cypressId('navLinkHistory')}>
|
|
||||||
<Trans i18nKey='landing.navigation.history' />
|
|
||||||
</HeaderNavLink>
|
|
||||||
</div>
|
|
||||||
</Navbar>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { HeaderBar }
|
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.link {
|
|
||||||
border-bottom: 2px solid transparent
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
border-bottom-color: var(--bs-primary);
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
import { concatCssClasses } from '../../../../utils/concat-css-classes'
|
|
||||||
import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute'
|
|
||||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
|
||||||
import styles from './header-nav-link.module.scss'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { usePathname } from 'next/navigation'
|
|
||||||
import type { PropsWithChildren } from 'react'
|
|
||||||
import React, { useMemo } from 'react'
|
|
||||||
import { Nav } from 'react-bootstrap'
|
|
||||||
|
|
||||||
export interface HeaderNavLinkProps extends PropsWithDataCypressId {
|
|
||||||
to: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a link for the navigation top bar.
|
|
||||||
*
|
|
||||||
* @param to The target url
|
|
||||||
* @param children The react elements inside of link for more description
|
|
||||||
* @param props Other navigation item props
|
|
||||||
*/
|
|
||||||
export const HeaderNavLink: React.FC<PropsWithChildren<HeaderNavLinkProps>> = ({ to, children, ...props }) => {
|
|
||||||
const pathname = usePathname()
|
|
||||||
|
|
||||||
const className = useMemo(() => {
|
|
||||||
return concatCssClasses(
|
|
||||||
{
|
|
||||||
[styles.active]: pathname === to
|
|
||||||
},
|
|
||||||
'nav-link',
|
|
||||||
styles.link
|
|
||||||
)
|
|
||||||
}, [pathname, to])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Nav.Item>
|
|
||||||
<Link href={to} passHref={true} className={className} {...cypressId(props)}>
|
|
||||||
{children}
|
|
||||||
</Link>
|
|
||||||
</Nav.Item>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -4,12 +4,13 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useDarkModeState } from '../../../../hooks/dark-mode/use-dark-mode-state'
|
import { useDarkModeState } from '../../../../hooks/dark-mode/use-dark-mode-state'
|
||||||
import { BrandingSeparatorDash } from '../../../common/custom-branding/branding-separator-dash'
|
|
||||||
import { CustomBranding } from '../../../common/custom-branding/custom-branding'
|
import { CustomBranding } from '../../../common/custom-branding/custom-branding'
|
||||||
import { HedgeDocLogoHorizontalGrey } from '../../../common/hedge-doc-logo/hedge-doc-logo-horizontal-grey'
|
import { HedgeDocLogoHorizontalGrey } from '../../../common/hedge-doc-logo/hedge-doc-logo-horizontal-grey'
|
||||||
import { LogoSize } from '../../../common/hedge-doc-logo/logo-size'
|
import { LogoSize } from '../../../common/hedge-doc-logo/logo-size'
|
||||||
|
import { BrandingSeparatorDash } from './branding-separator-dash'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { Navbar } from 'react-bootstrap'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the HedgeDoc branding and branding customizations for the app bar.
|
* Renders the HedgeDoc branding and branding customizations for the app bar.
|
||||||
|
@ -18,14 +19,16 @@ export const BrandingElement: React.FC = () => {
|
||||||
const darkModeActivated = useDarkModeState()
|
const darkModeActivated = useDarkModeState()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Navbar.Brand>
|
||||||
href='/intro'
|
<Link href='/' className='text-secondary text-decoration-none d-flex align-items-center'>
|
||||||
className={'text-secondary text-decoration-none d-flex align-items-center justify-content-start gap-1'}>
|
<HedgeDocLogoHorizontalGrey
|
||||||
<div>
|
size={LogoSize.SMALL}
|
||||||
<HedgeDocLogoHorizontalGrey color={darkModeActivated ? 'dark' : 'light'} size={LogoSize.SMALL} />
|
className={'w-auto'}
|
||||||
</div>
|
color={darkModeActivated ? 'dark' : 'light'}
|
||||||
|
/>
|
||||||
<BrandingSeparatorDash />
|
<BrandingSeparatorDash />
|
||||||
<CustomBranding inline={true} />
|
<CustomBranding inline={true} />
|
||||||
</Link>
|
</Link>
|
||||||
|
</Navbar.Brand>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useBrandingDetails } from './use-branding-details'
|
import { useBrandingDetails } from '../../../common/custom-branding/use-branding-details'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -22,7 +22,7 @@ export const HelpDropdown: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<Dropdown.Toggle size={'sm'}>
|
<Dropdown.Toggle size={'sm'} className={'h-100'}>
|
||||||
<UiIcon icon={IconQuestion} />
|
<UiIcon icon={IconQuestion} />
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import { Button } from 'react-bootstrap'
|
||||||
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A button that links to the history page.
|
||||||
|
*/
|
||||||
|
export const HistoryButton: React.FC = () => {
|
||||||
|
useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={'/history'}>
|
||||||
|
<Button variant={'secondary'} size={'sm'}>
|
||||||
|
<Trans i18nKey='landing.navigation.history' />
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
*
|
*
|
||||||
|
@ -8,7 +10,7 @@ import { useNoteTitle } from '../../../../../hooks/common/use-note-title'
|
||||||
import { useTranslatedText } from '../../../../../hooks/common/use-translated-text'
|
import { useTranslatedText } from '../../../../../hooks/common/use-translated-text'
|
||||||
import { UiIcon } from '../../../../common/icons/ui-icon'
|
import { UiIcon } from '../../../../common/icons/ui-icon'
|
||||||
import { ShowIf } from '../../../../common/show-if/show-if'
|
import { ShowIf } from '../../../../common/show-if/show-if'
|
||||||
import React, { Fragment } from 'react'
|
import React from 'react'
|
||||||
import { Lock as IconLock } from 'react-bootstrap-icons'
|
import { Lock as IconLock } from 'react-bootstrap-icons'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,13 +22,13 @@ export const NoteTitleElement: React.FC = () => {
|
||||||
const readOnlyLabel = useTranslatedText('appbar.editor.readOnly')
|
const readOnlyLabel = useTranslatedText('appbar.editor.readOnly')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<span className={'m-0 text-truncate'}>
|
||||||
<ShowIf condition={!isWriteable}>
|
<ShowIf condition={!isWriteable}>
|
||||||
<span className={'text-secondary me-2'}>
|
<span className={'text-secondary me-2'}>
|
||||||
<UiIcon icon={IconLock} title={readOnlyLabel} />
|
<UiIcon icon={IconLock} className={'me-2'} title={readOnlyLabel} />
|
||||||
</span>
|
</span>
|
||||||
</ShowIf>
|
</ShowIf>
|
||||||
<span className={'text-truncate mw-100'}>{noteTitle}</span>
|
{noteTitle}
|
||||||
</Fragment>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,5 +13,5 @@ import React from 'react'
|
||||||
*/
|
*/
|
||||||
export const UserElement: React.FC = () => {
|
export const UserElement: React.FC = () => {
|
||||||
const userExists = useApplicationState((state) => !!state.user)
|
const userExists = useApplicationState((state) => !!state.user)
|
||||||
return userExists ? <UserDropdown /> : <SignInButton size={'sm'} />
|
return userExists ? <UserDropdown /> : <SignInButton size={'sm'} className={'h-100'} />
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
*
|
*
|
||||||
|
@ -8,30 +10,31 @@ import { SettingsButton } from '../../global-dialogs/settings-dialog/settings-bu
|
||||||
import { BrandingElement } from './app-bar-elements/branding-element'
|
import { BrandingElement } from './app-bar-elements/branding-element'
|
||||||
import { HelpDropdown } from './app-bar-elements/help-dropdown/help-dropdown'
|
import { HelpDropdown } from './app-bar-elements/help-dropdown/help-dropdown'
|
||||||
import { UserElement } from './app-bar-elements/user-element'
|
import { UserElement } from './app-bar-elements/user-element'
|
||||||
|
import styles from './navbar.module.scss'
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Col, Nav, Navbar } from 'react-bootstrap'
|
import { Nav, Navbar } from 'react-bootstrap'
|
||||||
|
import { HistoryButton } from './app-bar-elements/help-dropdown/history-button'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the base app bar with branding, help, settings user elements.
|
* Renders the base app bar with branding, help, settings user elements.
|
||||||
*/
|
*/
|
||||||
export const BaseAppBar: React.FC<PropsWithChildren> = ({ children }) => {
|
export const BaseAppBar: React.FC<PropsWithChildren> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<Navbar expand={true} className={'px-2 py-2 shadow-sm'}>
|
<Navbar expand={true} className={`px-2 py-1 align-items-center border-bottom ${styles.navbar}`}>
|
||||||
<Col>
|
<Nav className={`align-items-center justify-content-start gap-2 flex-grow-1 ${styles.side}`}>
|
||||||
<BrandingElement />
|
<BrandingElement />
|
||||||
</Col>
|
</Nav>
|
||||||
<Col md={6} className={'h-100'}>
|
<Nav className={`align-items-center flex-fill overflow-hidden px-4 ${styles.center}`}>{children}</Nav>
|
||||||
<Nav className={'d-flex align-items-center justify-content-center h-100'}>{children}</Nav>
|
<Nav className={`align-items-stretch justify-content-end flex-grow-1 ${styles.side} h-100 py-1`}>
|
||||||
</Col>
|
<div className={'d-flex gap-2'}>
|
||||||
<Col>
|
<HistoryButton />
|
||||||
<Nav className={'d-flex align-items-center justify-content-end gap-2'}>
|
|
||||||
<HelpDropdown />
|
<HelpDropdown />
|
||||||
<SettingsButton />
|
<SettingsButton />
|
||||||
<NewNoteButton />
|
<NewNoteButton />
|
||||||
<UserElement />
|
<UserElement />
|
||||||
|
</div>
|
||||||
</Nav>
|
</Nav>
|
||||||
</Col>
|
|
||||||
</Navbar>
|
</Navbar>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
23
frontend/src/components/layout/app-bar/navbar.module.scss
Normal file
23
frontend/src/components/layout/app-bar/navbar.module.scss
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
.side {
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 2 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history {
|
||||||
|
flex: 2 2 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
height: 48px;
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
import useResizeObserver from '@react-hook/resize-observer'
|
import useResizeObserver from '@react-hook/resize-observer'
|
||||||
import type { RefObject } from 'react'
|
import type { RefObject } from 'react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monitors the height of the referenced {@link HTMLElement} and executes the callback on change.
|
* Monitors the height of the referenced {@link HTMLElement} and executes the callback on change.
|
||||||
|
@ -18,6 +18,7 @@ export const useOnHeightChange = (
|
||||||
onHeightChange: undefined | ((value: number) => void)
|
onHeightChange: undefined | ((value: number) => void)
|
||||||
): void => {
|
): void => {
|
||||||
const [rendererSize, setRendererSize] = useState<number>(0)
|
const [rendererSize, setRendererSize] = useState<number>(0)
|
||||||
|
const lastPostedSize = useRef<number>(0)
|
||||||
useResizeObserver(elementRef, (entry) => {
|
useResizeObserver(elementRef, (entry) => {
|
||||||
setRendererSize(entry.contentRect.height)
|
setRendererSize(entry.contentRect.height)
|
||||||
})
|
})
|
||||||
|
@ -29,6 +30,10 @@ export const useOnHeightChange = (
|
||||||
setRendererSize(value)
|
setRendererSize(value)
|
||||||
}, [elementRef])
|
}, [elementRef])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (lastPostedSize.current === rendererSize) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastPostedSize.current = rendererSize
|
||||||
onHeightChange?.(rendererSize + 1)
|
onHeightChange?.(rendererSize + 1)
|
||||||
}, [rendererSize, onHeightChange])
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue