fix: replace dark mode hack with bootstrap's own dark mode

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2023-05-31 18:36:47 +02:00
parent 3f42798965
commit 0993372290
77 changed files with 244 additions and 365 deletions

View file

@ -1,30 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
body.dark {
@import "variables.dark";
/* redefine theme color variables */
@each $color, $value in $theme-colors {
--#{$prefix}#{$color}: #{$value};
}
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value");
@each $color, $value in $theme-colors-rgb {
--#{$prefix}#{$color}-rgb: #{$value};
}
--#{$prefix}body-color: #{$body-color};
--#{$prefix}body-bg: #{$body-bg};
@import "colors-only-bootstrap/bootstrap";
.btn-close {
filter: invert(1);
}
}

View file

@ -6,6 +6,6 @@
@import '~highlight.js/styles/github';
body.dark {
[data-bs-theme="dark"] {
@import '~highlight.js/styles/github-dark';
}

View file

@ -26,10 +26,6 @@
cursor: pointer;
}
body {
background-color: $dark;
}
*:focus {
outline: 0 !important;
}

View file

@ -1,134 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
$white: #fff;
$gray-100: #f8f9fa;
$gray-200: #ebebeb;
$gray-300: #dee2e6;
$gray-400: #ced4da;
$gray-500: #adb5bd;
$gray-600: #888;
$gray-700: #444;
$gray-800: #303030;
$gray-900: #222;
$black: #000;
$blue: #337ab7;
$indigo: #6610f2;
$purple: #6f42c1;
$pink: #e83e8c;
$red: #e74c3c;
$orange: #fd7e14;
$yellow: #f39c12;
$green: #00bc8c;
$teal: #20c997;
$cyan: #5EB7E0;
$primary: $blue;
$secondary: $white;
$success: $green;
$info: $cyan;
$warning: $yellow;
$danger: $red;
$light: $gray-900;
$dark: $white;
$yiq-contrasted-threshold: 175;
// Body
$body-bg: $light;
$body-color: $dark;
// Links
$link-color: $cyan;
// Fonts
$text-muted: $gray-400;
// Tables
$table-accent-bg: $gray-800;
$table-border-color: $gray-700;
// Forms
$input-border-color: $body-bg;
$input-group-addon-color: $gray-500;
$input-group-addon-bg: $gray-700;
$input-bg: $gray-700;
$input-placeholder-color: $gray-500;
$input-color: $white;
$input-disabled-bg: $gray-900;
$custom-file-color: $gray-500;
$custom-file-border-color: $body-bg;
// Dropdowns
$dropdown-bg: $gray-900;
$dropdown-border-color: $gray-700;
$dropdown-divider-bg: $gray-700;
$dropdown-link-color: $white;
$dropdown-link-hover-color: $white;
$dropdown-link-hover-bg: $primary;
// Navs
$nav-link-disabled-color: $gray-500;
$nav-tabs-border-color: $gray-700;
$nav-tabs-link-hover-border-color: $nav-tabs-border-color $nav-tabs-border-color transparent;
$nav-tabs-link-active-color: $white;
$nav-tabs-link-active-border-color: $nav-tabs-border-color $nav-tabs-border-color transparent;
// Navbar
$navbar-dark-color: rgba($white, .6);
$navbar-dark-hover-color: $white;
$navbar-light-color: rgba($gray-900, .7);
$navbar-light-hover-color: $gray-900;
$navbar-light-active-color: $gray-900;
$navbar-light-toggler-border-color: rgba($gray-900, .1);
// Jumbotron
$jumbotron-bg: $gray-800;
// Cards
$card-cap-bg: $gray-700;
$card-bg: $gray-800;
// Popovers
$popover-bg: $gray-800;
$popover-header-bg: $gray-700;
// Toasts
$toast-background-color: $gray-700;
$toast-header-background-color: $gray-800;
// Modals
$modal-content-color: $dark;
$modal-content-bg: $gray-800;
$modal-content-border-color: $gray-700;
$modal-header-border-color: $gray-700;
// Progress bars
$progress-bg: $gray-700;
// List group
$list-group-bg: $gray-800;
$list-group-border-color: $gray-700;
$list-group-hover-bg: $gray-700;
$list-group-action-hover-color: $white;
// Breadcrumbs
$breadcrumb-bg: $gray-700;
// Close
$close-color: $white;
$close-text-shadow: none;
// Code
$pre-color: $dark;
$list-group-color: $dark;
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/variables";

View file

@ -7,10 +7,3 @@
$blue: #337ab7 !default;
$cyan: #5EB7E0 !default;
$dark: #222222 !default;
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/variables";
$toast-background-color: $white;

View file

@ -21,7 +21,7 @@ export interface LoadingScreenProps {
*/
export const LoadingScreen: React.FC<LoadingScreenProps> = ({ errorMessage }) => {
return (
<div className={`${styles.loader} text-light`}>
<div className={`${styles.loader} text-light bg-dark`}>
<div className='mb-3 text-light'>
<span className={`d-block`}>
<LoadingAnimation error={!!errorMessage} />

View file

@ -38,7 +38,6 @@ export const DisplayNameField: React.FC<DisplayNameFieldProps> = ({ onChange, va
isValid={isValid}
onChange={onChange}
placeholder={t('profile.displayName') ?? undefined}
className='bg-dark text-light'
autoComplete='name'
required
/>

View file

@ -33,7 +33,6 @@ export const NewPasswordField: React.FC<CommonFieldProps> = ({ onChange, value,
isInvalid={hasError}
onChange={onChange}
placeholder={t('login.auth.password') ?? undefined}
className='bg-dark text-light'
autoComplete='new-password'
required
/>

View file

@ -47,7 +47,6 @@ export const PasswordAgainField: React.FC<PasswordAgainFieldProps> = ({
isValid={isValid}
onChange={onChange}
placeholder={t('login.register.passwordAgain') ?? undefined}
className='bg-dark text-light'
autoComplete='new-password'
required
/>

View file

@ -33,7 +33,6 @@ export const UsernameField: React.FC<CommonFieldProps> = ({ onChange, value }) =
isValid={isValid}
onChange={onChange}
placeholder={t('login.auth.username') ?? undefined}
className='bg-dark text-light'
autoComplete='username'
autoFocus={true}
required

View file

@ -3,11 +3,11 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { AsyncLoadingBoundary } from '../../../components/common/async-loading-boundary/async-loading-boundary'
import { CopyToClipboardButton } from '../../../components/common/copyable/copy-to-clipboard-button/copy-to-clipboard-button'
import { concatCssClasses } from '../../../utils/concat-css-classes'
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
import { testId } from '../../../utils/test-id'
import { AsyncLoadingBoundary } from '../async-loading-boundary/async-loading-boundary'
import { CopyToClipboardButton } from '../copyable/copy-to-clipboard-button/copy-to-clipboard-button'
import styles from './highlighted-code.module.scss'
import { useAsyncHighlightJsImport } from './hooks/use-async-highlight-js-import'
import { useAttachLineNumbers } from './hooks/use-attach-line-numbers'

View file

@ -3,7 +3,7 @@
exports[`ExternalLink renders an external link correctly 1`] = `
<div>
<a
class="text-light"
class=""
dir="auto"
href="https://example.com"
rel="noopener noreferrer"
@ -17,7 +17,7 @@ exports[`ExternalLink renders an external link correctly 1`] = `
exports[`ExternalLink renders an external link with a title 1`] = `
<div>
<a
class="text-light"
class=""
dir="auto"
href="https://example.com"
rel="noopener noreferrer"
@ -46,7 +46,7 @@ exports[`ExternalLink renders an external link with additional className 1`] = `
exports[`ExternalLink renders an external link with an icon 1`] = `
<div>
<a
class="text-light"
class=""
dir="auto"
href="https://example.com"
rel="noopener noreferrer"
@ -62,7 +62,7 @@ exports[`ExternalLink renders an external link with an icon 1`] = `
exports[`ExternalLink renders an external link with an id 1`] = `
<div>
<a
class="text-light"
class=""
dir="auto"
href="https://example.com"
id="testId"

View file

@ -3,7 +3,7 @@
exports[`InternalLink renders an internal link correctly 1`] = `
<div>
<a
class="text-light"
class=""
href="/test"
>
testText
@ -14,7 +14,7 @@ exports[`InternalLink renders an internal link correctly 1`] = `
exports[`InternalLink renders an internal link with a title 1`] = `
<div>
<a
class="text-light"
class=""
href="/test"
title="testTitle"
>
@ -37,7 +37,7 @@ exports[`InternalLink renders an internal link with additional className 1`] = `
exports[`InternalLink renders an internal link with an icon 1`] = `
<div>
<a
class="text-light"
class=""
href="/test"
>
BootstrapIconMock_Heart
@ -50,7 +50,7 @@ exports[`InternalLink renders an internal link with an icon 1`] = `
exports[`InternalLink renders an internal link with an id 1`] = `
<div>
<a
class="text-light"
class=""
href="/test"
id="testId"
>

View file

@ -3,7 +3,7 @@
exports[`TranslatedExternalLink renders with i18nKey 1`] = `
<div>
<a
class="text-light"
class=""
dir="auto"
href="https://example.com"
rel="noopener noreferrer"

View file

@ -3,7 +3,7 @@
exports[`TranslatedInternalLink renders with i18nKey 1`] = `
<div>
<a
class="text-light"
class=""
href="/test"
>
testi18nKey

View file

@ -19,14 +19,7 @@ import React from 'react'
* @param className Additional class names added to the link object
* @param title The title of the link
*/
export const ExternalLink: React.FC<LinkWithTextProps> = ({
href,
text,
icon,
id,
className = 'text-light',
title
}) => {
export const ExternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = '', title }) => {
return (
<a href={href} target='_blank' rel='noopener noreferrer' id={id} className={className} title={title} dir='auto'>
<UiIcon icon={icon} nbsp={true} />

View file

@ -19,14 +19,7 @@ import React from 'react'
* @param className Additional class names added to the link object
* @param title The title of the link
*/
export const InternalLink: React.FC<LinkWithTextProps> = ({
href,
text,
icon,
id,
className = 'text-light',
title
}) => {
export const InternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = '', title }) => {
return (
<Link href={href} className={className} id={id} title={title}>
<UiIcon icon={icon} nbsp={true} />

View file

@ -4,7 +4,7 @@ exports[`CommonModal does not render if show is false 1`] = `<div />`;
exports[`CommonModal render correctly in size lg 1`] = `
<div
class="modal-dialog text-dark modal-lg"
class="modal-dialog modal-lg"
data-testid="commonModal"
>
<div
@ -26,7 +26,7 @@ exports[`CommonModal render correctly in size lg 1`] = `
exports[`CommonModal render correctly in size sm 1`] = `
<div
class="modal-dialog text-dark modal-sm"
class="modal-dialog modal-sm"
data-testid="commonModal"
>
<div
@ -48,7 +48,7 @@ exports[`CommonModal render correctly in size sm 1`] = `
exports[`CommonModal render correctly in size xl 1`] = `
<div
class="modal-dialog text-dark modal-xl"
class="modal-dialog modal-xl"
data-testid="commonModal"
>
<div
@ -70,7 +70,7 @@ exports[`CommonModal render correctly in size xl 1`] = `
exports[`CommonModal render correctly with additionalClasses 1`] = `
<div
class="modal-dialog text-dark testClass"
class="modal-dialog testClass"
data-testid="commonModal"
>
<div
@ -92,7 +92,7 @@ exports[`CommonModal render correctly with additionalClasses 1`] = `
exports[`CommonModal render correctly with i18nTitle 1`] = `
<div
class="modal-dialog text-dark"
class="modal-dialog"
data-testid="commonModal"
>
<div
@ -114,7 +114,7 @@ exports[`CommonModal render correctly with i18nTitle 1`] = `
exports[`CommonModal render correctly with title 1`] = `
<div
class="modal-dialog text-dark"
class="modal-dialog"
data-testid="commonModal"
>
<div
@ -136,7 +136,7 @@ exports[`CommonModal render correctly with title 1`] = `
exports[`CommonModal render correctly with title icon 1`] = `
<div
class="modal-dialog text-dark"
class="modal-dialog"
data-testid="commonModal"
>
<div
@ -160,7 +160,7 @@ exports[`CommonModal render correctly with title icon 1`] = `
exports[`CommonModal renders correctly and calls onHide, when close button is clicked 1`] = `
<div
class="modal-dialog text-dark"
class="modal-dialog"
data-testid="commonModal"
>
<div

View file

@ -2,7 +2,7 @@
exports[`DeletionModal disables deletion when user is not owner 1`] = `
<div
class="modal-dialog text-dark"
class="modal-dialog"
data-testid="commonModal"
>
<div
@ -23,7 +23,7 @@ exports[`DeletionModal disables deletion when user is not owner 1`] = `
/>
</div>
<div
class="text-dark modal-body"
class="modal-body"
>
testText
</div>
@ -44,7 +44,7 @@ exports[`DeletionModal disables deletion when user is not owner 1`] = `
exports[`DeletionModal renders correctly with deletionButtonI18nKey 1`] = `
<div
class="modal-dialog text-dark"
class="modal-dialog"
data-testid="commonModal"
>
<div
@ -65,7 +65,7 @@ exports[`DeletionModal renders correctly with deletionButtonI18nKey 1`] = `
/>
</div>
<div
class="text-dark modal-body"
class="modal-body"
>
testText
</div>

View file

@ -74,7 +74,7 @@ export const CommonModal: React.FC<PropsWithChildren<CommonModalProps>> = ({
onHide={onHide}
animation={true}
{...testId('commonModal')}
dialogClassName={concatCssClasses('text-dark', additionalClasses)}
dialogClassName={concatCssClasses(additionalClasses)}
size={modalSize}>
<Modal.Header closeButton={!!showCloseButton}>
<Modal.Title>

View file

@ -51,7 +51,7 @@ export const DeletionModal: React.FC<PropsWithChildren<DeletionModalProps>> = ({
titleIcon={titleIcon}
showCloseButton={true}
{...props}>
<Modal.Body className='text-dark'>{children}</Modal.Body>
<Modal.Body>{children}</Modal.Body>
<Modal.Footer>
<Button {...cypressId('deletionModal.confirmButton')} variant='danger' onClick={onConfirm} disabled={!isOwner}>
<Trans i18nKey={deletionButtonI18nKey} />

View file

@ -14,7 +14,7 @@ exports[`motd modal renders a modal if a motd was fetched and can dismiss it 1`]
This is a mock implementation of a Modal:
<dialog>
<div
class="bg-light modal-body"
class="modal-body"
>
<span
data-testid="motd-renderer"

View file

@ -53,7 +53,7 @@ export const MotdModal: React.FC = () => {
show={!!lines && !loading && !error && !dismissed}
titleI18nKey={'motd.title'}
{...cypressId('motd-modal')}>
<Modal.Body className={'bg-light'}>
<Modal.Body>
<EditorToRendererCommunicatorContextProvider>
<RendererIframe
frameClasses={'w-100'}

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplicationState } from '../../../../hooks/common/use-application-state'
import { DarkModePreference } from '../../../../redux/dark-mode/types'
import { useSendToRenderer } from '../../../render-page/window-post-message-communicator/hooks/use-send-to-renderer'
import { CommunicationMessageType } from '../../../render-page/window-post-message-communicator/rendering-message'
import { useMemo } from 'react'
@ -13,27 +12,19 @@ import { useMemo } from 'react'
* Sends additional configuration options (dark mode, line break, etc.) to the renderer.
*
* @param rendererReady Defines if the target renderer is ready
* @param forcedDarkMode Overwrites the value from the global application states if set.
*/
export const useSendAdditionalConfigurationToRenderer = (
rendererReady: boolean,
forcedDarkMode: DarkModePreference = DarkModePreference.AUTO
): void => {
export const useSendAdditionalConfigurationToRenderer = (rendererReady: boolean): void => {
const darkModePreference = useApplicationState((state) => state.darkMode.darkModePreference)
const newlinesAreBreaks = useApplicationState((state) => state.noteDetails.frontmatter.newlinesAreBreaks)
const darkMode = useMemo(() => {
return forcedDarkMode === DarkModePreference.AUTO ? darkModePreference : forcedDarkMode
}, [darkModePreference, forcedDarkMode])
useSendToRenderer(
useMemo(
() => ({
type: CommunicationMessageType.SET_ADDITIONAL_CONFIGURATION,
darkModePreference: darkMode,
darkModePreference: darkModePreference,
newLinesAreBreaks: newlinesAreBreaks
}),
[darkMode, newlinesAreBreaks]
[darkModePreference, newlinesAreBreaks]
),
rendererReady
)

View file

@ -3,7 +3,6 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { DarkModePreference } from '../../../redux/dark-mode/types'
import { concatCssClasses } from '../../../utils/concat-css-classes'
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
import { Logger } from '../../../utils/logger'
@ -27,11 +26,11 @@ import { useForceRenderPageUrlOnIframeLoadCallback } from './hooks/use-force-ren
import { useSendAdditionalConfigurationToRenderer } from './hooks/use-send-additional-configuration-to-renderer'
import { useSendMarkdownToRenderer } from './hooks/use-send-markdown-to-renderer'
import { useSendScrollState } from './hooks/use-send-scroll-state'
import styles from './style.module.scss'
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
export interface RendererIframeProps extends Omit<CommonMarkdownRendererProps & ScrollProps, 'baseUrl'> {
rendererType: RendererType
forcedDarkMode?: DarkModePreference
frameClasses?: string
onRendererStatusChange?: undefined | ((rendererReady: boolean) => void)
adaptFrameHeightToContent?: boolean
@ -51,7 +50,6 @@ const log = new Logger('RendererIframe')
* @param onMakeScrollSource Callback that is fired when the renderer requests to be set as the current scroll source
* @param frameClasses CSS classes that should be applied to the iframe
* @param rendererType The {@link RendererType type} of the renderer to use.
* @param forcedDarkMode If set, the dark mode will be set to the given value. Otherwise, the dark mode won't be changed.
* @param adaptFrameHeightToContent If set, the iframe height will be adjusted to the content height
* @param onRendererStatusChange Callback that is fired when the renderer in the iframe is ready
*/
@ -62,7 +60,6 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
onMakeScrollSource,
frameClasses,
rendererType,
forcedDarkMode,
adaptFrameHeightToContent,
onRendererStatusChange
}) => {
@ -142,7 +139,7 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
)
useEffectOnRenderTypeChange(rendererType, onIframeLoad)
useSendAdditionalConfigurationToRenderer(rendererReady, forcedDarkMode)
useSendAdditionalConfigurationToRenderer(rendererReady)
useSendMarkdownToRenderer(markdownContentLines, rendererReady)
useSendScrollState(scrollState, rendererReady)
@ -169,7 +166,7 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
allowFullScreen={true}
ref={frameReference}
referrerPolicy={'no-referrer'}
className={concatCssClasses('border-0', frameClasses)}
className={concatCssClasses('border-0', styles.frame, frameClasses)}
allow={'clipboard-write'}
{...cypressAttribute('renderer-ready', rendererReady ? 'true' : 'false')}
{...cypressAttribute('renderer-type', rendererType)}

View file

@ -0,0 +1,9 @@
/*!
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.frame {
color-scheme: initial;
}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
import { NewNoteButton } from '../../common/new-note-button/new-note-button'
import { ShowIf } from '../../common/show-if/show-if'
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
@ -35,9 +36,10 @@ export interface AppBarProps {
export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
const userExists = useApplicationState((state) => !!state.user)
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)
const buttonVariant = useOutlineButtonVariant()
return (
<Navbar expand={true} className={'bg-light px-3'}>
<Navbar expand={true} className={'px-3'}>
<Nav className='me-auto d-flex align-items-center'>
<NavbarBranding />
<ShowIf condition={mode === AppBarMode.EDITOR}>
@ -52,7 +54,7 @@ export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
</ShowIf>
</Nav>
<Nav className='d-flex gap-2 align-items-center text-secondary justify-content-end'>
<SettingsButton variant={'outline-dark'} />
<SettingsButton variant={buttonVariant} />
<NewNoteButton />
<ShowIf condition={!userExists}>
<SignInButton size={'sm'} />

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
import { useOutlineButtonVariant } from '../../../../hooks/dark-mode/use-outline-button-variant'
import { cypressId } from '../../../../utils/cypress-attribute'
import { CommonModal } from '../../../common/modals/common-modal'
import { CheatsheetContent } from './cheatsheet-content'
@ -18,6 +19,7 @@ import { Trans, useTranslation } from 'react-i18next'
export const CheatsheetButton: React.FC = () => {
const { t } = useTranslation()
const [modalVisibility, showModal, closeModal] = useBooleanState()
const buttonVariant = useOutlineButtonVariant()
return (
<Fragment>
@ -25,7 +27,7 @@ export const CheatsheetButton: React.FC = () => {
{...cypressId('open.cheatsheet-button')}
title={t('cheatsheet.button') ?? undefined}
className={'mx-2'}
variant='outline-dark'
variant={buttonVariant}
size={'sm'}
onClick={showModal}>
<Trans i18nKey={'cheatsheet.button'}></Trans>

View file

@ -3,6 +3,7 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useOutlineButtonVariant } from '../../../../hooks/dark-mode/use-outline-button-variant'
import { IconButton } from '../../../common/icon-button/icon-button'
import type { MouseEvent } from 'react'
import React, { useCallback } from 'react'
@ -28,6 +29,8 @@ export const CheatsheetInNewTabButton: React.FC<CheatsheetInNewTabButtonProps> =
const { t } = useTranslation()
const buttonVariant = useOutlineButtonVariant()
return (
<IconButton
size={'sm'}
@ -36,7 +39,7 @@ export const CheatsheetInNewTabButton: React.FC<CheatsheetInNewTabButtonProps> =
onClick={openPopUp}
icon={BoxArrowUpRight}
className={'p-2 border-0'}
variant={'outline-dark'}
variant={buttonVariant}
target={'_blank'}
title={t('cheatsheet.modal.popup') ?? ''}
/>

View file

@ -23,7 +23,7 @@ export const ReadMoreLinkItem: React.FC<ReadMoreLinkGroupProps> = ({ url }) => {
<h4>
<Trans i18nKey={'cheatsheet.modal.headlines.readMoreLink'} />
</h4>
<ExternalLink className={'text-dark'} text={url.toString()} href={url.toString()}></ExternalLink>
<ExternalLink text={url.toString()} href={url.toString()}></ExternalLink>
</ListGroupItem>
)
}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
import { useOutlineButtonVariant } from '../../../../hooks/dark-mode/use-outline-button-variant'
import { cypressId } from '../../../../utils/cypress-attribute'
import { IconButton } from '../../../common/icon-button/icon-button'
import { HelpModal } from './help-modal'
@ -17,6 +18,7 @@ import { Trans, useTranslation } from 'react-i18next'
export const HelpButton: React.FC = () => {
const { t } = useTranslation()
const [modalVisibility, showModal, closeModal] = useBooleanState()
const buttonVariant = useOutlineButtonVariant()
return (
<Fragment>
@ -26,7 +28,7 @@ export const HelpButton: React.FC = () => {
title={t('editor.documentBar.help') ?? undefined}
className='ms-2'
size='sm'
variant='outline-dark'
variant={buttonVariant}
onClick={showModal}>
<Trans i18nKey={'editor.documentBar.help'} />
</IconButton>

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
import { UiIcon } from '../../common/icons/ui-icon'
import Link from 'next/link'
import React from 'react'
@ -17,14 +18,11 @@ import { useTranslation } from 'react-i18next'
export const ReadOnlyModeButton: React.FC = () => {
const { t } = useTranslation()
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
const buttonVariant = useOutlineButtonVariant()
return (
<Link href={`/s/${noteIdentifier}`} target='_blank'>
<Button
title={t('editor.documentBar.readOnlyMode') ?? undefined}
className='ms-2 text-secondary'
size='sm'
variant='outline-light'>
<Button title={t('editor.documentBar.readOnlyMode') ?? undefined} size='sm' variant={buttonVariant}>
<UiIcon icon={IconFileEarmarkTextFill} />
</Button>
</Link>

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
import { UiIcon } from '../../common/icons/ui-icon'
import Link from 'next/link'
import React from 'react'
@ -17,6 +18,7 @@ import { useTranslation } from 'react-i18next'
export const SlideModeButton: React.FC = () => {
const { t } = useTranslation()
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
const buttonVariant = useOutlineButtonVariant()
return (
<Link href={`/p/${noteIdentifier}`} target='_blank'>
@ -24,7 +26,7 @@ export const SlideModeButton: React.FC = () => {
title={t('editor.documentBar.slideMode') ?? undefined}
className='ms-2 text-secondary'
size='sm'
variant='outline-light'>
variant={buttonVariant}>
<UiIcon icon={IconTv} />
</Button>
</Link>

View file

@ -16,7 +16,7 @@ import React from 'react'
*/
export const StatusBar: React.FC = () => {
return (
<div className={`d-flex flex-row border-secondary border-top small bg-light px-2`}>
<div className={`d-flex flex-row border-secondary border-top small px-2`}>
<div>
<CursorPositionInfo />
<SelectedCharacters />

View file

@ -5,9 +5,7 @@
*/
.tooltip {
&, :global(body.dark) & {
--bs-popover-max-width: 100%;
--bs-popover-body-padding-y: 0;
--bs-popover-body-padding-x: 0;
}
}

View file

@ -5,9 +5,6 @@
*/
:global(.btn-toolbar).toolbar {
border-bottom: 1px solid #ededed;
border-top: 1px solid #ededed;
:global(.btn) {
padding: 0.1875rem 0.5rem;
min-width: 30px;

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useMayEdit } from '../../../../hooks/common/use-may-edit'
import { concatCssClasses } from '../../../../utils/concat-css-classes'
import { ShowIf } from '../../../common/show-if/show-if'
import { BoldButton } from './buttons/bold-button'
import { CheckListButton } from './buttons/check-list-button'
@ -39,7 +40,7 @@ export const ToolBar: React.FC = () => {
return (
<ShowIf condition={mayEdit}>
<ButtonToolbar className={`bg-light ${styles.toolbar}`}>
<ButtonToolbar className={concatCssClasses(styles.toolbar, 'mb-2')}>
<ButtonGroup className={'mx-1 flex-wrap'}>
<BoldButton />
<ItalicButton />

View file

@ -41,7 +41,8 @@ export const ToolbarButton: React.FC<PropsWithChildren<ToolbarButtonProps>> = ({
return (
<Button
variant={'light'}
variant={'outline-secondary'}
className={'text-body-emphasis'}
onClick={onClick}
title={title}
ref={buttonRef}

View file

@ -18,7 +18,7 @@
padding: 0;
transition: height 0.2s, flex-basis 0.2s;
overflow: hidden;
color: var(--bs-dark);
color: var(--bs-emphasis-color);
&.hide {
flex-basis: 0;

View file

@ -7,7 +7,7 @@
.slide-sidebar {
--sidebar-entry-height: 44px;
--sidebar-menu-width: 280px;
--sidebar-separator-color: rgba(0, 0, 0, 0.15);
--sidebar-separator-color: var(--bs-secondary);
:global(body.dark) & {
--sidebar-separator-color: rgba(255, 255, 255, 0.2);
@ -35,6 +35,6 @@
left: calc(0px - var(--sidebar-menu-width) + var(--sidebar-entry-height));
}
background: var(--bs-light);
background: var(--bs-body-bg);
}
}

View file

@ -16,11 +16,11 @@ exports[`Splitter resize can change size with mouse 1`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"
@ -75,11 +75,11 @@ exports[`Splitter resize can change size with mouse 2`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"
@ -134,11 +134,11 @@ exports[`Splitter resize can change size with mouse 3`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"
@ -193,11 +193,11 @@ exports[`Splitter resize can change size with mouse 4`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"
@ -252,11 +252,11 @@ exports[`Splitter resize can change size with touch 1`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"
@ -311,11 +311,11 @@ exports[`Splitter resize can change size with touch 2`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"
@ -370,11 +370,11 @@ exports[`Splitter resize can change size with touch 3`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"
@ -429,11 +429,11 @@ exports[`Splitter resize can change size with touch 4`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"
@ -488,11 +488,11 @@ exports[`Splitter resize can react to shortcuts 1`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle shift-right"
class="middle shift-right"
>
<div
class="buttons"
@ -547,11 +547,11 @@ exports[`Splitter resize can react to shortcuts 2`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle shift-left"
class="middle shift-left"
>
<div
class="buttons"
@ -606,11 +606,11 @@ exports[`Splitter resize can react to shortcuts 3`] = `
</div>
</div>
<div
class="split-divider"
class="divider"
data-testid="splitter-divider"
>
<div
class="bg-light middle "
class="middle"
>
<div
class="buttons"

View file

@ -4,19 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
.split-divider {
.divider {
width: 15px;
background: white;
background: var(--bs-body-bg);
z-index: 1;
box-shadow: 3px 0 6px #e7e7e7;
box-shadow: 0 0 6px var(--bs-gray-400);
display: flex;
align-items: center;
justify-content: center;
:global(body.dark) & {
box-shadow: 3px 0 6px #7b7b7b;
}
}
.grabber {
@ -29,7 +25,8 @@
z-index: 100000;
position: absolute;
border-radius: 90px;
border: solid 1px #d5d5d5;
border: solid 1px var(--bs-gray-500);
background: var(--bs-body-bg);
overflow: hidden;
display: flex;
flex-direction: row;

View file

@ -3,10 +3,11 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { concatCssClasses } from '../../../../utils/concat-css-classes'
import { testId } from '../../../../utils/test-id'
import { UiIcon } from '../../../common/icons/ui-icon'
import styles from './split-divider.module.scss'
import React from 'react'
import React, { useMemo } from 'react'
import { Button } from 'react-bootstrap'
import { ArrowLeftRight as IconArrowLeftRight } from 'react-bootstrap-icons'
import { ArrowLeft as IconArrowLeft } from 'react-bootstrap-icons'
@ -49,12 +50,17 @@ export const SplitDivider: React.FC<SplitDividerProps> = ({
focusRight,
forceOpen
}) => {
const shiftClass = dividerButtonsShift == '' ? '' : styles[dividerButtonsShift]
const className = useMemo(() => {
return concatCssClasses(styles.middle, {
[styles.open]: forceOpen,
[styles[dividerButtonsShift]]: dividerButtonsShift !== ''
})
}, [dividerButtonsShift, forceOpen])
return (
<div className={`${styles['split-divider']}`} {...testId('splitter-divider')}>
<div className={`bg-light ${styles['middle']} ${forceOpen ? styles['open'] : ''} ${shiftClass}`}>
<div className={styles['buttons']}>
<div className={styles.divider} {...testId('splitter-divider')}>
<div className={className}>
<div className={styles.buttons}>
<Button variant={focusLeft ? 'secondary' : 'light'} onClick={onLeftButtonClick}>
<UiIcon icon={IconArrowLeft} />
</Button>

View file

@ -51,7 +51,7 @@ export const EntryMenu: React.FC<EntryMenuProps> = ({
return (
<Dropdown className={`d-inline-flex ${className || ''}`} {...cypressId('history-entry-menu')}>
<Dropdown.Toggle
variant={'light'}
variant={'secondary'}
id={`dropdown-card-${id}`}
className={`no-arrow ${styles['history-menu']} d-inline-flex align-items-center`}>
<UiIcon icon={IconThreeDots} />

View file

@ -3,6 +3,7 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useDarkModeState } from '../../../hooks/dark-mode/use-dark-mode-state'
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
import { UiIcon } from '../../common/icons/ui-icon'
import { EntryMenu } from '../entry-menu/entry-menu'
@ -45,16 +46,19 @@ export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = (
const entryTitle = useHistoryEntryTitle(entry)
const darkModeState = useDarkModeState()
const tags = useMemo(
() =>
entry.tags.map((tag) => (
<Badge className={'bg-dark me-1 mb-1'} key={tag}>
entry.tags.map((tag) => {
return (
<Badge className={'bg-secondary text-light me-1 mb-1'} key={tag}>
{tag}
</Badge>
)),
)
}),
[entry.tags]
)
const lastVisited = useMemo(() => formatHistoryDate(entry.lastVisitedAt), [entry.lastVisitedAt])
return (
@ -62,18 +66,18 @@ export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = (
className='p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4'
{...cypressId('history-card')}
{...cypressAttribute('card-title', entryTitle)}>
<Card className={styles['card-min-height']} text={'dark'} bg={'light'}>
<Card className={`${styles['card-min-height']}`} bg={darkModeState ? 'dark' : 'light'}>
<Card.Body className='p-2 d-flex flex-row justify-content-between'>
<div className={'d-flex flex-column'}>
<PinButton isDark={false} isPinned={entry.pinStatus} onPinClick={onPinEntry} />
</div>
<Link href={`/n/${entry.identifier}`} className='text-decoration-none flex-fill text-dark'>
<Link href={`/n/${entry.identifier}`} className='text-decoration-none text-body-emphasis flex-fill'>
<div className={'d-flex flex-column justify-content-between'}>
<Card.Title className='m-0 mt-1dot5' {...cypressId('history-entry-title')}>
{entryTitle}
</Card.Title>
<div>
<div className='text-black-50 mt-2'>
<div className='mt-2'>
<UiIcon icon={IconClock} /> {DateTime.fromISO(entry.lastVisitedAt).toRelative()}
<br />
{lastVisited}

View file

@ -44,14 +44,14 @@ export const HistoryTableRow: React.FC<HistoryEntryProps & HistoryEventHandlers>
return (
<tr {...cypressAttribute('entry-title', entryTitle)}>
<td>
<Link href={`/n/${entry.identifier}`} className='text-light' {...cypressId('history-entry-title')}>
<Link href={`/n/${entry.identifier}`} className='text-secondary' {...cypressId('history-entry-title')}>
{entryTitle}
</Link>
</td>
<td>{formatHistoryDate(entry.lastVisitedAt)}</td>
<td>
{entry.tags.map((tag) => (
<Badge className={'bg-light me-1 mb-1'} key={tag}>
<Badge className={'me-1 mb-1'} key={tag}>
{tag}
</Badge>
))}

View file

@ -3,6 +3,7 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useDarkModeState } from '../../../hooks/dark-mode/use-dark-mode-state'
import { cypressId } from '../../../utils/cypress-attribute'
import { Pager } from '../../common/pagination/pager'
import type { HistoryEntriesProps, HistoryEventHandlers } from '../history-content/history-content'
@ -44,13 +45,15 @@ export const HistoryTable: React.FC<HistoryEntriesProps & HistoryEventHandlers>
))
}, [entries, onPinClick, onRemoveEntryClick, onDeleteNoteClick])
const darkModeState = useDarkModeState()
return (
<Table
striped
bordered
hover
size='sm'
variant='dark'
variant={darkModeState ? 'dark' : 'light'}
className={styles['history-table']}
{...cypressId('history-table')}>
<thead>

View file

@ -36,7 +36,7 @@ export const ClearHistoryButton: React.FC = () => {
return (
<Fragment>
<Button
variant={'light'}
variant={'secondary'}
title={t('landing.history.toolbar.clear') ?? undefined}
onClick={showModal}
{...cypressId('history-clear-button')}>

View file

@ -17,7 +17,7 @@ export const ExportHistoryButton: React.FC = () => {
const { t } = useTranslation()
return (
<Button variant={'light'} title={t('landing.history.toolbar.export') ?? undefined} onClick={downloadHistory}>
<Button variant={'secondary'} title={t('landing.history.toolbar.export') ?? undefined} onClick={downloadHistory}>
<UiIcon icon={IconDownload} />
</Button>
)

View file

@ -19,7 +19,7 @@ export const HistoryRefreshButton: React.FC = () => {
const refreshHistory = useSafeRefreshHistoryStateCallback()
return (
<Button variant={'light'} title={t('landing.history.toolbar.refresh') ?? undefined} onClick={refreshHistory}>
<Button variant={'secondary'} title={t('landing.history.toolbar.refresh') ?? undefined} onClick={refreshHistory}>
<UiIcon icon={IconArrowRepeat} />
</Button>
)

View file

@ -90,7 +90,7 @@ export const HistoryToolbar: React.FC = () => {
<ShowIf condition={userExists}>
<div className={'me-1 mb-1'}>
<Button
variant={'light'}
variant={'secondary'}
title={t('landing.history.toolbar.uploadAll') ?? undefined}
onClick={onUploadAllToRemote}>
<UiIcon icon={IconCloudUpload} />

View file

@ -34,13 +34,13 @@ export const HistoryViewModeToggleButton: React.FC = () => {
<ToggleButtonGroup type='radio' name='options' dir='auto' className={'button-height'} onChange={onViewStateChange}>
<Button
title={t('landing.history.toolbar.cards') ?? undefined}
variant={historyToolbarState.viewState === ViewStateEnum.CARD ? 'light' : 'outline-light'}
variant={historyToolbarState.viewState === ViewStateEnum.CARD ? 'secondary' : 'outline-secondary'}
onClick={() => onViewStateChange(ViewStateEnum.CARD)}>
<UiIcon icon={IconStickyFill} className={'fa-fix-line-height'} />
</Button>
<Button
{...cypressId('history-mode-table')}
variant={historyToolbarState.viewState === ViewStateEnum.TABLE ? 'light' : 'outline-light'}
variant={historyToolbarState.viewState === ViewStateEnum.TABLE ? 'secondary' : 'outline-secondary'}
title={t('landing.history.toolbar.table') ?? undefined}
onClick={() => onViewStateChange(ViewStateEnum.TABLE)}>
<UiIcon icon={IconTable} className={'fa-fix-line-height'} />

View file

@ -128,7 +128,7 @@ export const ImportHistoryButton: React.FC = () => {
{...cypressId('import-history-file-input')}
/>
<Button
variant={'light'}
variant={'secondary'}
title={t('landing.history.toolbar.import') ?? undefined}
onClick={onUploadButtonClick}
{...cypressId('import-history-file-button')}>

View file

@ -28,7 +28,7 @@ export interface PinButtonProps {
export const PinButton: React.FC<PinButtonProps> = ({ isPinned, onPinClick, isDark, className }) => {
return (
<Button
variant={isDark ? 'secondary' : 'light'}
variant={isDark ? 'secondary' : 'secondary'}
className={`${styles['history-pin']} ${className || ''} ${isPinned ? styles['pinned'] : ''}`}
onClick={onPinClick}
{...cypressId('history-entry-pin-button')}

View file

@ -64,7 +64,7 @@ export const SortButton: React.FC<SortButtonProps> = ({ children, onDirectionCha
}, [direction])
return (
<IconButton onClick={toggleSort} variant={'light'} icon={icon} iconSize={1.5} border={true}>
<IconButton onClick={toggleSort} variant={'secondary'} icon={icon} iconSize={1.5} border={true}>
{children}
</IconButton>
)

View file

@ -3,7 +3,6 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { DarkModePreference } from '../../redux/dark-mode/types'
import { Logger } from '../../utils/logger'
import { AsyncLoadingBoundary } from '../common/async-loading-boundary/async-loading-boundary'
import { RendererIframe } from '../common/renderer-iframe/renderer-iframe'
@ -32,7 +31,6 @@ export const IntroCustomContent: React.FC = () => {
frameClasses={'w-100 overflow-y-hidden'}
markdownContentLines={value as string[]}
rendererType={RendererType.SIMPLE}
forcedDarkMode={DarkModePreference.DARK}
adaptFrameHeightToContent={true}
/>
</AsyncLoadingBoundary>

View file

@ -12,7 +12,7 @@ import React from 'react'
*/
export const Footer: React.FC = () => {
return (
<footer className='text-light small'>
<footer className='small'>
<PoweredByLinks />
<SocialLink />
</footer>

View file

@ -18,12 +18,7 @@ export const VersionInfoLink: React.FC = () => {
return (
<Fragment>
<Button
size={'sm'}
variant={'link'}
{...cypressId('show-version-modal')}
className={'text-light p-0'}
onClick={showModal}>
<Button size={'sm'} variant={'link'} {...cypressId('show-version-modal')} className={'p-0'} onClick={showModal}>
<Trans i18nKey={'landing.versionInfo.versionInfo'} />
</Button>
<VersionInfoModal onHide={closeModal} show={modalVisibility} />

View file

@ -3,11 +3,13 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplyDarkModeStyle } from '../../hooks/dark-mode/use-apply-dark-mode-style'
import { useSaveDarkModePreferenceToLocalStorage } from '../../hooks/dark-mode/use-save-dark-mode-preference-to-local-storage'
import { MotdModal } from '../common/motd-modal/motd-modal'
import { Footer } from './footer/footer'
import { HeaderBar } from './navigation/header-bar/header-bar'
import type { PropsWithChildren } from 'react'
import React, { Fragment } from 'react'
import React from 'react'
import { Container } from 'react-bootstrap'
/**
@ -15,17 +17,20 @@ import { Container } from 'react-bootstrap'
*
* @param children The children that should be rendered on the page.
*/
export const LandingLayout: React.FC<PropsWithChildren<unknown>> = ({ children }) => {
export const LandingLayout: React.FC<PropsWithChildren> = ({ children }) => {
useApplyDarkModeStyle()
useSaveDarkModePreferenceToLocalStorage()
return (
<Fragment>
<div>
<MotdModal />
<Container className='text-light d-flex flex-column mvh-100'>
<Container className='d-flex flex-column mvh-100'>
<HeaderBar />
<div className={'d-flex flex-column justify-content-between flex-fill text-center'}>
<main>{children}</main>
<Footer />
</div>
</Container>
</Fragment>
</div>
)
}

View file

@ -32,7 +32,7 @@ const HeaderBar: React.FC = () => {
</HeaderNavLink>
</div>
<div className='d-inline-flex gap-2'>
<SettingsButton variant={'outline-light'} />
<SettingsButton variant={'outline-dark'} />
<NewNoteButton />
{!userExists ? <SignInButton size='sm' /> : <UserDropdown />}
</div>

View file

@ -4,10 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
.nav-link {
.link {
border-bottom: 2px solid transparent
}
.nav-link-active {
border-bottom-color: #fff;
.active {
border-bottom-color: var(--bs-primary);
}

View file

@ -3,6 +3,7 @@
*
* 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'
@ -26,17 +27,19 @@ export interface HeaderNavLinkProps extends PropsWithDataCypressId {
export const HeaderNavLink: React.FC<PropsWithChildren<HeaderNavLinkProps>> = ({ to, children, ...props }) => {
const { route } = useRouter()
const activeClass = useMemo(() => {
return route === to ? styles['nav-link-active'] : ''
const className = useMemo(() => {
return concatCssClasses(
{
[styles.active]: route === to
},
'nav-link',
styles.link
)
}, [route, to])
return (
<Nav.Item>
<Link
href={to}
passHref={true}
className={`nav-link text-light ${activeClass} ${styles['nav-link']}`}
{...cypressId(props)}>
<Link href={to} passHref={true} className={className} {...cypressId(props)}>
{children}
</Link>
</Nav.Item>

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useBooleanState } from '../../../hooks/common/use-boolean-state'
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
import { cypressId } from '../../../utils/cypress-attribute'
import { IconButton } from '../../common/icon-button/icon-button'
import { SettingsModal } from './settings-modal'
@ -17,9 +18,17 @@ export type SettingsButtonProps = Omit<ButtonProps, 'onClick'>
*/
export const SettingsButton: React.FC<SettingsButtonProps> = (props) => {
const [show, showModal, hideModal] = useBooleanState(false)
const buttonVariant = useOutlineButtonVariant()
return (
<Fragment>
<IconButton {...props} {...cypressId('settingsButton')} onClick={showModal} icon={IconGear} />
<IconButton
{...props}
{...cypressId('settingsButton')}
onClick={showModal}
icon={IconGear}
variant={buttonVariant}
/>
<SettingsModal show={show} onHide={hideModal} />
</Fragment>
)

View file

@ -25,7 +25,6 @@ export const PasswordField: React.FC<AuthFieldProps> = ({ onChange, invalid }) =
size='sm'
placeholder={t('login.auth.password') ?? undefined}
onChange={onChange}
className='bg-dark text-light'
autoComplete='current-password'
/>
</Form.Group>

View file

@ -32,7 +32,6 @@ export const UsernameField: React.FC<UsernameFieldProps> = ({ onChange, invalid,
size='sm'
placeholder={t('login.auth.username') ?? undefined}
onChange={onChange}
className='bg-dark text-light'
autoComplete='username'
/>
</Form.Group>

View file

@ -49,7 +49,7 @@ export const ViaLocal: React.FC = () => {
const onPasswordChange = useOnInputChange(setPassword)
return (
<Card className='bg-dark mb-4'>
<Card className='mb-4'>
<Card.Body>
<Card.Title>
<Trans i18nKey='login.signInVia' values={{ service: t('login.auth.username') }} />

View file

@ -5,7 +5,7 @@
*/
.image-drop {
border: 3px dashed var(--bs-dark);
border: 3px dashed var(--bs-primary);
border-radius: 3px;
transition: background-color 50ms, color 50ms;

View file

@ -97,7 +97,7 @@ export const ImagePlaceholder: React.FC<PlaceholderImageFrameProps> = ({
onChange={onChangeHandler}
ref={fileInputReference}
/>
<div className={'align-items-center flex-column justify-content-center flex-fill d-flex'}>
<div className={'align-items-center flex-column justify-content-center flex-fill d-flex text-body-emphasis'}>
<div className={'d-flex flex-column'}>
<span className='my-2'>
<Trans i18nKey={'editor.embeddings.placeholderImage.placeholderText'} />

View file

@ -13,11 +13,16 @@ export const useApplyDarkModeStyle = (): void => {
const darkMode = useDarkModeState()
useEffect(() => {
if (darkMode) {
window.document.body.classList.add('dark')
window.document.body.dataset.bsTheme = 'dark'
} else {
window.document.body.classList.remove('dark')
window.document.body.dataset.bsTheme = 'light'
}
}, [darkMode])
useEffect(() => () => window.document.body.classList.remove('dark'), [])
useEffect(
() => () => {
window.document.body.dataset.bsTheme = 'light'
},
[]
)
}

View file

@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as UseDarkModeStateModule from './use-dark-mode-state'
import { useOutlineButtonVariant } from './use-outline-button-variant'
import { render, screen } from '@testing-library/react'
import React from 'react'
jest.mock('./use-dark-mode-state')
describe('useOutlineButtonVariant', () => {
const TestComponent: React.FC = () => {
return useOutlineButtonVariant()
}
it('returns the correct variant for dark mode', async () => {
jest.spyOn(UseDarkModeStateModule, 'useDarkModeState').mockReturnValue(true)
render(<TestComponent />)
await screen.findByText('outline-light')
})
it('returns the correct variant for light mode', async () => {
jest.spyOn(UseDarkModeStateModule, 'useDarkModeState').mockReturnValue(false)
render(<TestComponent />)
await screen.findByText('outline-dark')
})
})

View file

@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useDarkModeState } from './use-dark-mode-state'
export const useOutlineButtonVariant = (): 'outline-light' | 'outline-dark' => {
return useDarkModeState() ? 'outline-light' : 'outline-dark'
}

View file

@ -3,7 +3,6 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import '../../global-styles/dark.scss'
import '../../global-styles/index.scss'
import type { FrontendConfig } from '../api/config/types'
import { ApplicationLoader } from '../components/application-loader/application-loader'

View file

@ -72,7 +72,7 @@ export const LoginPage: React.FC = () => {
</ShowIf>
<ShowIf condition={oneClickProviders.length > 0}>
<Col xs={12} sm={10} lg={4}>
<Card className='bg-dark mb-4'>
<Card className='mb-4'>
<Card.Body>
<Card.Title>
<Trans i18nKey='login.signInVia' values={{ service: '' }} />

View file

@ -7,6 +7,7 @@ import { NoteLoadingBoundary } from '../../components/common/note-loading-bounda
import { EditorPageContent } from '../../components/editor-page/editor-page-content'
import { EditorToRendererCommunicatorContextProvider } from '../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider'
import { ResetRealtimeStateBoundary } from '../../components/editor-page/reset-realtime-state-boundary'
import { useApplyDarkModeStyle } from '../../hooks/dark-mode/use-apply-dark-mode-style'
import type { NextPage } from 'next'
import React from 'react'
@ -14,6 +15,8 @@ import React from 'react'
* Renders a page that is used by the user to edit markdown notes. It contains the editor and a renderer.
*/
export const EditorPage: NextPage = () => {
useApplyDarkModeStyle()
return (
<ResetRealtimeStateBoundary>
<NoteLoadingBoundary>

View file

@ -84,7 +84,7 @@ export const RegisterPage: NextPage = () => {
</h1>
<Row className='h-100 d-flex justify-content-center'>
<Col lg={6}>
<Card className='bg-dark mb-4 text-start'>
<Card className='mb-4 text-start'>
<Card.Body>
<Form onSubmit={doRegisterSubmit} className={'d-flex flex-column gap-3'}>
<UsernameField onChange={onUsernameChange} value={username} />

View file

@ -7,12 +7,17 @@ import { RendererToEditorCommunicatorContextProvider } from '../components/edito
import { RenderPageContent } from '../components/render-page/render-page-content'
import { useApplyDarkModeStyle } from '../hooks/dark-mode/use-apply-dark-mode-style'
import type { NextPage } from 'next'
import React from 'react'
import React, { useEffect } from 'react'
/**
* Renders the actual markdown renderer that receives the content and metadata via iframe communication.
*/
export const RenderPage: NextPage = () => {
useEffect(() => {
document.body.classList.add('bg-transparent')
return () => document.body.classList.remove('bg-transparent')
}, [])
useApplyDarkModeStyle()
return (

View file

@ -25,7 +25,7 @@ export const DocumentReadOnlyPage: React.FC = () => {
<NoteLoadingBoundary>
<HeadMetaProperties />
<MotdModal />
<div className={'d-flex flex-column mvh-100 bg-light'}>
<div className={'d-flex flex-column mvh-100'}>
<AppBar mode={AppBarMode.BASIC} />
<DocumentReadOnlyPageContent />
</div>