mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-24 18:56:32 -05:00
fix: replace dark mode hack with bootstrap's own dark mode
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
3f42798965
commit
0993372290
77 changed files with 244 additions and 365 deletions
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,6 @@
|
||||||
|
|
||||||
@import '~highlight.js/styles/github';
|
@import '~highlight.js/styles/github';
|
||||||
|
|
||||||
body.dark {
|
[data-bs-theme="dark"] {
|
||||||
@import '~highlight.js/styles/github-dark';
|
@import '~highlight.js/styles/github-dark';
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,6 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: $dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
*:focus {
|
*:focus {
|
||||||
outline: 0 !important;
|
outline: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
|
|
@ -7,10 +7,3 @@
|
||||||
$blue: #337ab7 !default;
|
$blue: #337ab7 !default;
|
||||||
$cyan: #5EB7E0 !default;
|
$cyan: #5EB7E0 !default;
|
||||||
$dark: #222222 !default;
|
$dark: #222222 !default;
|
||||||
|
|
||||||
@import "~bootstrap/scss/functions";
|
|
||||||
@import "~bootstrap/scss/mixins";
|
|
||||||
@import "~bootstrap/scss/variables";
|
|
||||||
|
|
||||||
$toast-background-color: $white;
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ export interface LoadingScreenProps {
|
||||||
*/
|
*/
|
||||||
export const LoadingScreen: React.FC<LoadingScreenProps> = ({ errorMessage }) => {
|
export const LoadingScreen: React.FC<LoadingScreenProps> = ({ errorMessage }) => {
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.loader} text-light`}>
|
<div className={`${styles.loader} text-light bg-dark`}>
|
||||||
<div className='mb-3 text-light'>
|
<div className='mb-3 text-light'>
|
||||||
<span className={`d-block`}>
|
<span className={`d-block`}>
|
||||||
<LoadingAnimation error={!!errorMessage} />
|
<LoadingAnimation error={!!errorMessage} />
|
||||||
|
|
|
@ -38,7 +38,6 @@ export const DisplayNameField: React.FC<DisplayNameFieldProps> = ({ onChange, va
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
placeholder={t('profile.displayName') ?? undefined}
|
placeholder={t('profile.displayName') ?? undefined}
|
||||||
className='bg-dark text-light'
|
|
||||||
autoComplete='name'
|
autoComplete='name'
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -33,7 +33,6 @@ export const NewPasswordField: React.FC<CommonFieldProps> = ({ onChange, value,
|
||||||
isInvalid={hasError}
|
isInvalid={hasError}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
placeholder={t('login.auth.password') ?? undefined}
|
placeholder={t('login.auth.password') ?? undefined}
|
||||||
className='bg-dark text-light'
|
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -47,7 +47,6 @@ export const PasswordAgainField: React.FC<PasswordAgainFieldProps> = ({
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
placeholder={t('login.register.passwordAgain') ?? undefined}
|
placeholder={t('login.register.passwordAgain') ?? undefined}
|
||||||
className='bg-dark text-light'
|
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -33,7 +33,6 @@ export const UsernameField: React.FC<CommonFieldProps> = ({ onChange, value }) =
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
placeholder={t('login.auth.username') ?? undefined}
|
placeholder={t('login.auth.username') ?? undefined}
|
||||||
className='bg-dark text-light'
|
|
||||||
autoComplete='username'
|
autoComplete='username'
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
required
|
required
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* 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 { concatCssClasses } from '../../../utils/concat-css-classes'
|
||||||
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
|
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
|
||||||
import { testId } from '../../../utils/test-id'
|
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 styles from './highlighted-code.module.scss'
|
||||||
import { useAsyncHighlightJsImport } from './hooks/use-async-highlight-js-import'
|
import { useAsyncHighlightJsImport } from './hooks/use-async-highlight-js-import'
|
||||||
import { useAttachLineNumbers } from './hooks/use-attach-line-numbers'
|
import { useAttachLineNumbers } from './hooks/use-attach-line-numbers'
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
exports[`ExternalLink renders an external link correctly 1`] = `
|
exports[`ExternalLink renders an external link correctly 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
dir="auto"
|
dir="auto"
|
||||||
href="https://example.com"
|
href="https://example.com"
|
||||||
rel="noopener noreferrer"
|
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`] = `
|
exports[`ExternalLink renders an external link with a title 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
dir="auto"
|
dir="auto"
|
||||||
href="https://example.com"
|
href="https://example.com"
|
||||||
rel="noopener noreferrer"
|
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`] = `
|
exports[`ExternalLink renders an external link with an icon 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
dir="auto"
|
dir="auto"
|
||||||
href="https://example.com"
|
href="https://example.com"
|
||||||
rel="noopener noreferrer"
|
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`] = `
|
exports[`ExternalLink renders an external link with an id 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
dir="auto"
|
dir="auto"
|
||||||
href="https://example.com"
|
href="https://example.com"
|
||||||
id="testId"
|
id="testId"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
exports[`InternalLink renders an internal link correctly 1`] = `
|
exports[`InternalLink renders an internal link correctly 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
href="/test"
|
href="/test"
|
||||||
>
|
>
|
||||||
testText
|
testText
|
||||||
|
@ -14,7 +14,7 @@ exports[`InternalLink renders an internal link correctly 1`] = `
|
||||||
exports[`InternalLink renders an internal link with a title 1`] = `
|
exports[`InternalLink renders an internal link with a title 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
href="/test"
|
href="/test"
|
||||||
title="testTitle"
|
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`] = `
|
exports[`InternalLink renders an internal link with an icon 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
href="/test"
|
href="/test"
|
||||||
>
|
>
|
||||||
BootstrapIconMock_Heart
|
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`] = `
|
exports[`InternalLink renders an internal link with an id 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
href="/test"
|
href="/test"
|
||||||
id="testId"
|
id="testId"
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
exports[`TranslatedExternalLink renders with i18nKey 1`] = `
|
exports[`TranslatedExternalLink renders with i18nKey 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
dir="auto"
|
dir="auto"
|
||||||
href="https://example.com"
|
href="https://example.com"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
exports[`TranslatedInternalLink renders with i18nKey 1`] = `
|
exports[`TranslatedInternalLink renders with i18nKey 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
class="text-light"
|
class=""
|
||||||
href="/test"
|
href="/test"
|
||||||
>
|
>
|
||||||
testi18nKey
|
testi18nKey
|
||||||
|
|
|
@ -19,14 +19,7 @@ import React from 'react'
|
||||||
* @param className Additional class names added to the link object
|
* @param className Additional class names added to the link object
|
||||||
* @param title The title of the link
|
* @param title The title of the link
|
||||||
*/
|
*/
|
||||||
export const ExternalLink: React.FC<LinkWithTextProps> = ({
|
export const ExternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = '', title }) => {
|
||||||
href,
|
|
||||||
text,
|
|
||||||
icon,
|
|
||||||
id,
|
|
||||||
className = 'text-light',
|
|
||||||
title
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<a href={href} target='_blank' rel='noopener noreferrer' id={id} className={className} title={title} dir='auto'>
|
<a href={href} target='_blank' rel='noopener noreferrer' id={id} className={className} title={title} dir='auto'>
|
||||||
<UiIcon icon={icon} nbsp={true} />
|
<UiIcon icon={icon} nbsp={true} />
|
||||||
|
|
|
@ -19,14 +19,7 @@ import React from 'react'
|
||||||
* @param className Additional class names added to the link object
|
* @param className Additional class names added to the link object
|
||||||
* @param title The title of the link
|
* @param title The title of the link
|
||||||
*/
|
*/
|
||||||
export const InternalLink: React.FC<LinkWithTextProps> = ({
|
export const InternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = '', title }) => {
|
||||||
href,
|
|
||||||
text,
|
|
||||||
icon,
|
|
||||||
id,
|
|
||||||
className = 'text-light',
|
|
||||||
title
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<Link href={href} className={className} id={id} title={title}>
|
<Link href={href} className={className} id={id} title={title}>
|
||||||
<UiIcon icon={icon} nbsp={true} />
|
<UiIcon icon={icon} nbsp={true} />
|
||||||
|
|
|
@ -4,7 +4,7 @@ exports[`CommonModal does not render if show is false 1`] = `<div />`;
|
||||||
|
|
||||||
exports[`CommonModal render correctly in size lg 1`] = `
|
exports[`CommonModal render correctly in size lg 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark modal-lg"
|
class="modal-dialog modal-lg"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -26,7 +26,7 @@ exports[`CommonModal render correctly in size lg 1`] = `
|
||||||
|
|
||||||
exports[`CommonModal render correctly in size sm 1`] = `
|
exports[`CommonModal render correctly in size sm 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark modal-sm"
|
class="modal-dialog modal-sm"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -48,7 +48,7 @@ exports[`CommonModal render correctly in size sm 1`] = `
|
||||||
|
|
||||||
exports[`CommonModal render correctly in size xl 1`] = `
|
exports[`CommonModal render correctly in size xl 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark modal-xl"
|
class="modal-dialog modal-xl"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -70,7 +70,7 @@ exports[`CommonModal render correctly in size xl 1`] = `
|
||||||
|
|
||||||
exports[`CommonModal render correctly with additionalClasses 1`] = `
|
exports[`CommonModal render correctly with additionalClasses 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark testClass"
|
class="modal-dialog testClass"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -92,7 +92,7 @@ exports[`CommonModal render correctly with additionalClasses 1`] = `
|
||||||
|
|
||||||
exports[`CommonModal render correctly with i18nTitle 1`] = `
|
exports[`CommonModal render correctly with i18nTitle 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark"
|
class="modal-dialog"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -114,7 +114,7 @@ exports[`CommonModal render correctly with i18nTitle 1`] = `
|
||||||
|
|
||||||
exports[`CommonModal render correctly with title 1`] = `
|
exports[`CommonModal render correctly with title 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark"
|
class="modal-dialog"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -136,7 +136,7 @@ exports[`CommonModal render correctly with title 1`] = `
|
||||||
|
|
||||||
exports[`CommonModal render correctly with title icon 1`] = `
|
exports[`CommonModal render correctly with title icon 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark"
|
class="modal-dialog"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<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`] = `
|
exports[`CommonModal renders correctly and calls onHide, when close button is clicked 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark"
|
class="modal-dialog"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
exports[`DeletionModal disables deletion when user is not owner 1`] = `
|
exports[`DeletionModal disables deletion when user is not owner 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark"
|
class="modal-dialog"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -23,7 +23,7 @@ exports[`DeletionModal disables deletion when user is not owner 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-dark modal-body"
|
class="modal-body"
|
||||||
>
|
>
|
||||||
testText
|
testText
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,7 +44,7 @@ exports[`DeletionModal disables deletion when user is not owner 1`] = `
|
||||||
|
|
||||||
exports[`DeletionModal renders correctly with deletionButtonI18nKey 1`] = `
|
exports[`DeletionModal renders correctly with deletionButtonI18nKey 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-dialog text-dark"
|
class="modal-dialog"
|
||||||
data-testid="commonModal"
|
data-testid="commonModal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -65,7 +65,7 @@ exports[`DeletionModal renders correctly with deletionButtonI18nKey 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-dark modal-body"
|
class="modal-body"
|
||||||
>
|
>
|
||||||
testText
|
testText
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -74,7 +74,7 @@ export const CommonModal: React.FC<PropsWithChildren<CommonModalProps>> = ({
|
||||||
onHide={onHide}
|
onHide={onHide}
|
||||||
animation={true}
|
animation={true}
|
||||||
{...testId('commonModal')}
|
{...testId('commonModal')}
|
||||||
dialogClassName={concatCssClasses('text-dark', additionalClasses)}
|
dialogClassName={concatCssClasses(additionalClasses)}
|
||||||
size={modalSize}>
|
size={modalSize}>
|
||||||
<Modal.Header closeButton={!!showCloseButton}>
|
<Modal.Header closeButton={!!showCloseButton}>
|
||||||
<Modal.Title>
|
<Modal.Title>
|
||||||
|
|
|
@ -51,7 +51,7 @@ export const DeletionModal: React.FC<PropsWithChildren<DeletionModalProps>> = ({
|
||||||
titleIcon={titleIcon}
|
titleIcon={titleIcon}
|
||||||
showCloseButton={true}
|
showCloseButton={true}
|
||||||
{...props}>
|
{...props}>
|
||||||
<Modal.Body className='text-dark'>{children}</Modal.Body>
|
<Modal.Body>{children}</Modal.Body>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<Button {...cypressId('deletionModal.confirmButton')} variant='danger' onClick={onConfirm} disabled={!isOwner}>
|
<Button {...cypressId('deletionModal.confirmButton')} variant='danger' onClick={onConfirm} disabled={!isOwner}>
|
||||||
<Trans i18nKey={deletionButtonI18nKey} />
|
<Trans i18nKey={deletionButtonI18nKey} />
|
||||||
|
|
|
@ -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:
|
This is a mock implementation of a Modal:
|
||||||
<dialog>
|
<dialog>
|
||||||
<div
|
<div
|
||||||
class="bg-light modal-body"
|
class="modal-body"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
data-testid="motd-renderer"
|
data-testid="motd-renderer"
|
||||||
|
|
|
@ -53,7 +53,7 @@ export const MotdModal: React.FC = () => {
|
||||||
show={!!lines && !loading && !error && !dismissed}
|
show={!!lines && !loading && !error && !dismissed}
|
||||||
titleI18nKey={'motd.title'}
|
titleI18nKey={'motd.title'}
|
||||||
{...cypressId('motd-modal')}>
|
{...cypressId('motd-modal')}>
|
||||||
<Modal.Body className={'bg-light'}>
|
<Modal.Body>
|
||||||
<EditorToRendererCommunicatorContextProvider>
|
<EditorToRendererCommunicatorContextProvider>
|
||||||
<RendererIframe
|
<RendererIframe
|
||||||
frameClasses={'w-100'}
|
frameClasses={'w-100'}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
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 { 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 { CommunicationMessageType } from '../../../render-page/window-post-message-communicator/rendering-message'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
@ -13,27 +12,19 @@ import { useMemo } from 'react'
|
||||||
* Sends additional configuration options (dark mode, line break, etc.) to the renderer.
|
* Sends additional configuration options (dark mode, line break, etc.) to the renderer.
|
||||||
*
|
*
|
||||||
* @param rendererReady Defines if the target renderer is ready
|
* @param rendererReady Defines if the target renderer is ready
|
||||||
* @param forcedDarkMode Overwrites the value from the global application states if set.
|
|
||||||
*/
|
*/
|
||||||
export const useSendAdditionalConfigurationToRenderer = (
|
export const useSendAdditionalConfigurationToRenderer = (rendererReady: boolean): void => {
|
||||||
rendererReady: boolean,
|
|
||||||
forcedDarkMode: DarkModePreference = DarkModePreference.AUTO
|
|
||||||
): void => {
|
|
||||||
const darkModePreference = useApplicationState((state) => state.darkMode.darkModePreference)
|
const darkModePreference = useApplicationState((state) => state.darkMode.darkModePreference)
|
||||||
const newlinesAreBreaks = useApplicationState((state) => state.noteDetails.frontmatter.newlinesAreBreaks)
|
const newlinesAreBreaks = useApplicationState((state) => state.noteDetails.frontmatter.newlinesAreBreaks)
|
||||||
|
|
||||||
const darkMode = useMemo(() => {
|
|
||||||
return forcedDarkMode === DarkModePreference.AUTO ? darkModePreference : forcedDarkMode
|
|
||||||
}, [darkModePreference, forcedDarkMode])
|
|
||||||
|
|
||||||
useSendToRenderer(
|
useSendToRenderer(
|
||||||
useMemo(
|
useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
type: CommunicationMessageType.SET_ADDITIONAL_CONFIGURATION,
|
type: CommunicationMessageType.SET_ADDITIONAL_CONFIGURATION,
|
||||||
darkModePreference: darkMode,
|
darkModePreference: darkModePreference,
|
||||||
newLinesAreBreaks: newlinesAreBreaks
|
newLinesAreBreaks: newlinesAreBreaks
|
||||||
}),
|
}),
|
||||||
[darkMode, newlinesAreBreaks]
|
[darkModePreference, newlinesAreBreaks]
|
||||||
),
|
),
|
||||||
rendererReady
|
rendererReady
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import type { DarkModePreference } from '../../../redux/dark-mode/types'
|
|
||||||
import { concatCssClasses } from '../../../utils/concat-css-classes'
|
import { concatCssClasses } from '../../../utils/concat-css-classes'
|
||||||
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
|
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
|
||||||
import { Logger } from '../../../utils/logger'
|
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 { useSendAdditionalConfigurationToRenderer } from './hooks/use-send-additional-configuration-to-renderer'
|
||||||
import { useSendMarkdownToRenderer } from './hooks/use-send-markdown-to-renderer'
|
import { useSendMarkdownToRenderer } from './hooks/use-send-markdown-to-renderer'
|
||||||
import { useSendScrollState } from './hooks/use-send-scroll-state'
|
import { useSendScrollState } from './hooks/use-send-scroll-state'
|
||||||
|
import styles from './style.module.scss'
|
||||||
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
|
||||||
export interface RendererIframeProps extends Omit<CommonMarkdownRendererProps & ScrollProps, 'baseUrl'> {
|
export interface RendererIframeProps extends Omit<CommonMarkdownRendererProps & ScrollProps, 'baseUrl'> {
|
||||||
rendererType: RendererType
|
rendererType: RendererType
|
||||||
forcedDarkMode?: DarkModePreference
|
|
||||||
frameClasses?: string
|
frameClasses?: string
|
||||||
onRendererStatusChange?: undefined | ((rendererReady: boolean) => void)
|
onRendererStatusChange?: undefined | ((rendererReady: boolean) => void)
|
||||||
adaptFrameHeightToContent?: boolean
|
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 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 frameClasses CSS classes that should be applied to the iframe
|
||||||
* @param rendererType The {@link RendererType type} of the renderer to use.
|
* @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 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
|
* @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,
|
onMakeScrollSource,
|
||||||
frameClasses,
|
frameClasses,
|
||||||
rendererType,
|
rendererType,
|
||||||
forcedDarkMode,
|
|
||||||
adaptFrameHeightToContent,
|
adaptFrameHeightToContent,
|
||||||
onRendererStatusChange
|
onRendererStatusChange
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -142,7 +139,7 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffectOnRenderTypeChange(rendererType, onIframeLoad)
|
useEffectOnRenderTypeChange(rendererType, onIframeLoad)
|
||||||
useSendAdditionalConfigurationToRenderer(rendererReady, forcedDarkMode)
|
useSendAdditionalConfigurationToRenderer(rendererReady)
|
||||||
useSendMarkdownToRenderer(markdownContentLines, rendererReady)
|
useSendMarkdownToRenderer(markdownContentLines, rendererReady)
|
||||||
|
|
||||||
useSendScrollState(scrollState, rendererReady)
|
useSendScrollState(scrollState, rendererReady)
|
||||||
|
@ -169,7 +166,7 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
|
||||||
allowFullScreen={true}
|
allowFullScreen={true}
|
||||||
ref={frameReference}
|
ref={frameReference}
|
||||||
referrerPolicy={'no-referrer'}
|
referrerPolicy={'no-referrer'}
|
||||||
className={concatCssClasses('border-0', frameClasses)}
|
className={concatCssClasses('border-0', styles.frame, frameClasses)}
|
||||||
allow={'clipboard-write'}
|
allow={'clipboard-write'}
|
||||||
{...cypressAttribute('renderer-ready', rendererReady ? 'true' : 'false')}
|
{...cypressAttribute('renderer-ready', rendererReady ? 'true' : 'false')}
|
||||||
{...cypressAttribute('renderer-type', rendererType)}
|
{...cypressAttribute('renderer-type', rendererType)}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*!
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
.frame {
|
||||||
|
color-scheme: initial;
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
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 { NewNoteButton } from '../../common/new-note-button/new-note-button'
|
||||||
import { ShowIf } from '../../common/show-if/show-if'
|
import { ShowIf } from '../../common/show-if/show-if'
|
||||||
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
|
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
|
||||||
|
@ -35,9 +36,10 @@ export interface AppBarProps {
|
||||||
export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
|
export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
|
||||||
const userExists = useApplicationState((state) => !!state.user)
|
const userExists = useApplicationState((state) => !!state.user)
|
||||||
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)
|
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)
|
||||||
|
const buttonVariant = useOutlineButtonVariant()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Navbar expand={true} className={'bg-light px-3'}>
|
<Navbar expand={true} className={'px-3'}>
|
||||||
<Nav className='me-auto d-flex align-items-center'>
|
<Nav className='me-auto d-flex align-items-center'>
|
||||||
<NavbarBranding />
|
<NavbarBranding />
|
||||||
<ShowIf condition={mode === AppBarMode.EDITOR}>
|
<ShowIf condition={mode === AppBarMode.EDITOR}>
|
||||||
|
@ -52,7 +54,7 @@ export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
|
||||||
</ShowIf>
|
</ShowIf>
|
||||||
</Nav>
|
</Nav>
|
||||||
<Nav className='d-flex gap-2 align-items-center text-secondary justify-content-end'>
|
<Nav className='d-flex gap-2 align-items-center text-secondary justify-content-end'>
|
||||||
<SettingsButton variant={'outline-dark'} />
|
<SettingsButton variant={buttonVariant} />
|
||||||
<NewNoteButton />
|
<NewNoteButton />
|
||||||
<ShowIf condition={!userExists}>
|
<ShowIf condition={!userExists}>
|
||||||
<SignInButton size={'sm'} />
|
<SignInButton size={'sm'} />
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
|
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 { cypressId } from '../../../../utils/cypress-attribute'
|
||||||
import { CommonModal } from '../../../common/modals/common-modal'
|
import { CommonModal } from '../../../common/modals/common-modal'
|
||||||
import { CheatsheetContent } from './cheatsheet-content'
|
import { CheatsheetContent } from './cheatsheet-content'
|
||||||
|
@ -18,6 +19,7 @@ import { Trans, useTranslation } from 'react-i18next'
|
||||||
export const CheatsheetButton: React.FC = () => {
|
export const CheatsheetButton: React.FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [modalVisibility, showModal, closeModal] = useBooleanState()
|
const [modalVisibility, showModal, closeModal] = useBooleanState()
|
||||||
|
const buttonVariant = useOutlineButtonVariant()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
@ -25,7 +27,7 @@ export const CheatsheetButton: React.FC = () => {
|
||||||
{...cypressId('open.cheatsheet-button')}
|
{...cypressId('open.cheatsheet-button')}
|
||||||
title={t('cheatsheet.button') ?? undefined}
|
title={t('cheatsheet.button') ?? undefined}
|
||||||
className={'mx-2'}
|
className={'mx-2'}
|
||||||
variant='outline-dark'
|
variant={buttonVariant}
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
onClick={showModal}>
|
onClick={showModal}>
|
||||||
<Trans i18nKey={'cheatsheet.button'}></Trans>
|
<Trans i18nKey={'cheatsheet.button'}></Trans>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* 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 { IconButton } from '../../../common/icon-button/icon-button'
|
||||||
import type { MouseEvent } from 'react'
|
import type { MouseEvent } from 'react'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
|
@ -28,6 +29,8 @@ export const CheatsheetInNewTabButton: React.FC<CheatsheetInNewTabButtonProps> =
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const buttonVariant = useOutlineButtonVariant()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
|
@ -36,7 +39,7 @@ export const CheatsheetInNewTabButton: React.FC<CheatsheetInNewTabButtonProps> =
|
||||||
onClick={openPopUp}
|
onClick={openPopUp}
|
||||||
icon={BoxArrowUpRight}
|
icon={BoxArrowUpRight}
|
||||||
className={'p-2 border-0'}
|
className={'p-2 border-0'}
|
||||||
variant={'outline-dark'}
|
variant={buttonVariant}
|
||||||
target={'_blank'}
|
target={'_blank'}
|
||||||
title={t('cheatsheet.modal.popup') ?? ''}
|
title={t('cheatsheet.modal.popup') ?? ''}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const ReadMoreLinkItem: React.FC<ReadMoreLinkGroupProps> = ({ url }) => {
|
||||||
<h4>
|
<h4>
|
||||||
<Trans i18nKey={'cheatsheet.modal.headlines.readMoreLink'} />
|
<Trans i18nKey={'cheatsheet.modal.headlines.readMoreLink'} />
|
||||||
</h4>
|
</h4>
|
||||||
<ExternalLink className={'text-dark'} text={url.toString()} href={url.toString()}></ExternalLink>
|
<ExternalLink text={url.toString()} href={url.toString()}></ExternalLink>
|
||||||
</ListGroupItem>
|
</ListGroupItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
|
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 { cypressId } from '../../../../utils/cypress-attribute'
|
||||||
import { IconButton } from '../../../common/icon-button/icon-button'
|
import { IconButton } from '../../../common/icon-button/icon-button'
|
||||||
import { HelpModal } from './help-modal'
|
import { HelpModal } from './help-modal'
|
||||||
|
@ -17,6 +18,7 @@ import { Trans, useTranslation } from 'react-i18next'
|
||||||
export const HelpButton: React.FC = () => {
|
export const HelpButton: React.FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [modalVisibility, showModal, closeModal] = useBooleanState()
|
const [modalVisibility, showModal, closeModal] = useBooleanState()
|
||||||
|
const buttonVariant = useOutlineButtonVariant()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
@ -26,7 +28,7 @@ export const HelpButton: React.FC = () => {
|
||||||
title={t('editor.documentBar.help') ?? undefined}
|
title={t('editor.documentBar.help') ?? undefined}
|
||||||
className='ms-2'
|
className='ms-2'
|
||||||
size='sm'
|
size='sm'
|
||||||
variant='outline-dark'
|
variant={buttonVariant}
|
||||||
onClick={showModal}>
|
onClick={showModal}>
|
||||||
<Trans i18nKey={'editor.documentBar.help'} />
|
<Trans i18nKey={'editor.documentBar.help'} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
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 { UiIcon } from '../../common/icons/ui-icon'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
@ -17,14 +18,11 @@ import { useTranslation } from 'react-i18next'
|
||||||
export const ReadOnlyModeButton: React.FC = () => {
|
export const ReadOnlyModeButton: React.FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
||||||
|
const buttonVariant = useOutlineButtonVariant()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={`/s/${noteIdentifier}`} target='_blank'>
|
<Link href={`/s/${noteIdentifier}`} target='_blank'>
|
||||||
<Button
|
<Button title={t('editor.documentBar.readOnlyMode') ?? undefined} size='sm' variant={buttonVariant}>
|
||||||
title={t('editor.documentBar.readOnlyMode') ?? undefined}
|
|
||||||
className='ms-2 text-secondary'
|
|
||||||
size='sm'
|
|
||||||
variant='outline-light'>
|
|
||||||
<UiIcon icon={IconFileEarmarkTextFill} />
|
<UiIcon icon={IconFileEarmarkTextFill} />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
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 { UiIcon } from '../../common/icons/ui-icon'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
@ -17,6 +18,7 @@ import { useTranslation } from 'react-i18next'
|
||||||
export const SlideModeButton: React.FC = () => {
|
export const SlideModeButton: React.FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
||||||
|
const buttonVariant = useOutlineButtonVariant()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={`/p/${noteIdentifier}`} target='_blank'>
|
<Link href={`/p/${noteIdentifier}`} target='_blank'>
|
||||||
|
@ -24,7 +26,7 @@ export const SlideModeButton: React.FC = () => {
|
||||||
title={t('editor.documentBar.slideMode') ?? undefined}
|
title={t('editor.documentBar.slideMode') ?? undefined}
|
||||||
className='ms-2 text-secondary'
|
className='ms-2 text-secondary'
|
||||||
size='sm'
|
size='sm'
|
||||||
variant='outline-light'>
|
variant={buttonVariant}>
|
||||||
<UiIcon icon={IconTv} />
|
<UiIcon icon={IconTv} />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -16,7 +16,7 @@ import React from 'react'
|
||||||
*/
|
*/
|
||||||
export const StatusBar: React.FC = () => {
|
export const StatusBar: React.FC = () => {
|
||||||
return (
|
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>
|
<div>
|
||||||
<CursorPositionInfo />
|
<CursorPositionInfo />
|
||||||
<SelectedCharacters />
|
<SelectedCharacters />
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
&, :global(body.dark) & {
|
|
||||||
--bs-popover-max-width: 100%;
|
--bs-popover-max-width: 100%;
|
||||||
--bs-popover-body-padding-y: 0;
|
--bs-popover-body-padding-y: 0;
|
||||||
--bs-popover-body-padding-x: 0;
|
--bs-popover-body-padding-x: 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
:global(.btn-toolbar).toolbar {
|
:global(.btn-toolbar).toolbar {
|
||||||
border-bottom: 1px solid #ededed;
|
|
||||||
border-top: 1px solid #ededed;
|
|
||||||
|
|
||||||
:global(.btn) {
|
:global(.btn) {
|
||||||
padding: 0.1875rem 0.5rem;
|
padding: 0.1875rem 0.5rem;
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useMayEdit } from '../../../../hooks/common/use-may-edit'
|
import { useMayEdit } from '../../../../hooks/common/use-may-edit'
|
||||||
|
import { concatCssClasses } from '../../../../utils/concat-css-classes'
|
||||||
import { ShowIf } from '../../../common/show-if/show-if'
|
import { ShowIf } from '../../../common/show-if/show-if'
|
||||||
import { BoldButton } from './buttons/bold-button'
|
import { BoldButton } from './buttons/bold-button'
|
||||||
import { CheckListButton } from './buttons/check-list-button'
|
import { CheckListButton } from './buttons/check-list-button'
|
||||||
|
@ -39,7 +40,7 @@ export const ToolBar: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShowIf condition={mayEdit}>
|
<ShowIf condition={mayEdit}>
|
||||||
<ButtonToolbar className={`bg-light ${styles.toolbar}`}>
|
<ButtonToolbar className={concatCssClasses(styles.toolbar, 'mb-2')}>
|
||||||
<ButtonGroup className={'mx-1 flex-wrap'}>
|
<ButtonGroup className={'mx-1 flex-wrap'}>
|
||||||
<BoldButton />
|
<BoldButton />
|
||||||
<ItalicButton />
|
<ItalicButton />
|
||||||
|
|
|
@ -41,7 +41,8 @@ export const ToolbarButton: React.FC<PropsWithChildren<ToolbarButtonProps>> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant={'light'}
|
variant={'outline-secondary'}
|
||||||
|
className={'text-body-emphasis'}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
title={title}
|
title={title}
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
transition: height 0.2s, flex-basis 0.2s;
|
transition: height 0.2s, flex-basis 0.2s;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: var(--bs-dark);
|
color: var(--bs-emphasis-color);
|
||||||
|
|
||||||
&.hide {
|
&.hide {
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
.slide-sidebar {
|
.slide-sidebar {
|
||||||
--sidebar-entry-height: 44px;
|
--sidebar-entry-height: 44px;
|
||||||
--sidebar-menu-width: 280px;
|
--sidebar-menu-width: 280px;
|
||||||
--sidebar-separator-color: rgba(0, 0, 0, 0.15);
|
--sidebar-separator-color: var(--bs-secondary);
|
||||||
|
|
||||||
:global(body.dark) & {
|
:global(body.dark) & {
|
||||||
--sidebar-separator-color: rgba(255, 255, 255, 0.2);
|
--sidebar-separator-color: rgba(255, 255, 255, 0.2);
|
||||||
|
@ -35,6 +35,6 @@
|
||||||
left: calc(0px - var(--sidebar-menu-width) + var(--sidebar-entry-height));
|
left: calc(0px - var(--sidebar-menu-width) + var(--sidebar-entry-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
background: var(--bs-light);
|
background: var(--bs-body-bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@ exports[`Splitter resize can change size with mouse 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -75,11 +75,11 @@ exports[`Splitter resize can change size with mouse 2`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -134,11 +134,11 @@ exports[`Splitter resize can change size with mouse 3`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -193,11 +193,11 @@ exports[`Splitter resize can change size with mouse 4`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -252,11 +252,11 @@ exports[`Splitter resize can change size with touch 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -311,11 +311,11 @@ exports[`Splitter resize can change size with touch 2`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -370,11 +370,11 @@ exports[`Splitter resize can change size with touch 3`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -429,11 +429,11 @@ exports[`Splitter resize can change size with touch 4`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -488,11 +488,11 @@ exports[`Splitter resize can react to shortcuts 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle shift-right"
|
class="middle shift-right"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -547,11 +547,11 @@ exports[`Splitter resize can react to shortcuts 2`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle shift-left"
|
class="middle shift-left"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
@ -606,11 +606,11 @@ exports[`Splitter resize can react to shortcuts 3`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="divider"
|
||||||
data-testid="splitter-divider"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="bg-light middle "
|
class="middle"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="buttons"
|
class="buttons"
|
||||||
|
|
|
@ -4,19 +4,15 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.split-divider {
|
.divider {
|
||||||
width: 15px;
|
width: 15px;
|
||||||
background: white;
|
background: var(--bs-body-bg);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
box-shadow: 3px 0 6px #e7e7e7;
|
box-shadow: 0 0 6px var(--bs-gray-400);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
:global(body.dark) & {
|
|
||||||
box-shadow: 3px 0 6px #7b7b7b;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grabber {
|
.grabber {
|
||||||
|
@ -29,7 +25,8 @@
|
||||||
z-index: 100000;
|
z-index: 100000;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 90px;
|
border-radius: 90px;
|
||||||
border: solid 1px #d5d5d5;
|
border: solid 1px var(--bs-gray-500);
|
||||||
|
background: var(--bs-body-bg);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
import { concatCssClasses } from '../../../../utils/concat-css-classes'
|
||||||
import { testId } from '../../../../utils/test-id'
|
import { testId } from '../../../../utils/test-id'
|
||||||
import { UiIcon } from '../../../common/icons/ui-icon'
|
import { UiIcon } from '../../../common/icons/ui-icon'
|
||||||
import styles from './split-divider.module.scss'
|
import styles from './split-divider.module.scss'
|
||||||
import React from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import { ArrowLeftRight as IconArrowLeftRight } from 'react-bootstrap-icons'
|
import { ArrowLeftRight as IconArrowLeftRight } from 'react-bootstrap-icons'
|
||||||
import { ArrowLeft as IconArrowLeft } from 'react-bootstrap-icons'
|
import { ArrowLeft as IconArrowLeft } from 'react-bootstrap-icons'
|
||||||
|
@ -49,12 +50,17 @@ export const SplitDivider: React.FC<SplitDividerProps> = ({
|
||||||
focusRight,
|
focusRight,
|
||||||
forceOpen
|
forceOpen
|
||||||
}) => {
|
}) => {
|
||||||
const shiftClass = dividerButtonsShift == '' ? '' : styles[dividerButtonsShift]
|
const className = useMemo(() => {
|
||||||
|
return concatCssClasses(styles.middle, {
|
||||||
|
[styles.open]: forceOpen,
|
||||||
|
[styles[dividerButtonsShift]]: dividerButtonsShift !== ''
|
||||||
|
})
|
||||||
|
}, [dividerButtonsShift, forceOpen])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles['split-divider']}`} {...testId('splitter-divider')}>
|
<div className={styles.divider} {...testId('splitter-divider')}>
|
||||||
<div className={`bg-light ${styles['middle']} ${forceOpen ? styles['open'] : ''} ${shiftClass}`}>
|
<div className={className}>
|
||||||
<div className={styles['buttons']}>
|
<div className={styles.buttons}>
|
||||||
<Button variant={focusLeft ? 'secondary' : 'light'} onClick={onLeftButtonClick}>
|
<Button variant={focusLeft ? 'secondary' : 'light'} onClick={onLeftButtonClick}>
|
||||||
<UiIcon icon={IconArrowLeft} />
|
<UiIcon icon={IconArrowLeft} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -51,7 +51,7 @@ export const EntryMenu: React.FC<EntryMenuProps> = ({
|
||||||
return (
|
return (
|
||||||
<Dropdown className={`d-inline-flex ${className || ''}`} {...cypressId('history-entry-menu')}>
|
<Dropdown className={`d-inline-flex ${className || ''}`} {...cypressId('history-entry-menu')}>
|
||||||
<Dropdown.Toggle
|
<Dropdown.Toggle
|
||||||
variant={'light'}
|
variant={'secondary'}
|
||||||
id={`dropdown-card-${id}`}
|
id={`dropdown-card-${id}`}
|
||||||
className={`no-arrow ${styles['history-menu']} d-inline-flex align-items-center`}>
|
className={`no-arrow ${styles['history-menu']} d-inline-flex align-items-center`}>
|
||||||
<UiIcon icon={IconThreeDots} />
|
<UiIcon icon={IconThreeDots} />
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* 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 { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
|
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
|
||||||
import { UiIcon } from '../../common/icons/ui-icon'
|
import { UiIcon } from '../../common/icons/ui-icon'
|
||||||
import { EntryMenu } from '../entry-menu/entry-menu'
|
import { EntryMenu } from '../entry-menu/entry-menu'
|
||||||
|
@ -45,16 +46,19 @@ export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = (
|
||||||
|
|
||||||
const entryTitle = useHistoryEntryTitle(entry)
|
const entryTitle = useHistoryEntryTitle(entry)
|
||||||
|
|
||||||
|
const darkModeState = useDarkModeState()
|
||||||
|
|
||||||
const tags = useMemo(
|
const tags = useMemo(
|
||||||
() =>
|
() =>
|
||||||
entry.tags.map((tag) => (
|
entry.tags.map((tag) => {
|
||||||
<Badge className={'bg-dark me-1 mb-1'} key={tag}>
|
return (
|
||||||
|
<Badge className={'bg-secondary text-light me-1 mb-1'} key={tag}>
|
||||||
{tag}
|
{tag}
|
||||||
</Badge>
|
</Badge>
|
||||||
)),
|
)
|
||||||
|
}),
|
||||||
[entry.tags]
|
[entry.tags]
|
||||||
)
|
)
|
||||||
|
|
||||||
const lastVisited = useMemo(() => formatHistoryDate(entry.lastVisitedAt), [entry.lastVisitedAt])
|
const lastVisited = useMemo(() => formatHistoryDate(entry.lastVisitedAt), [entry.lastVisitedAt])
|
||||||
|
|
||||||
return (
|
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'
|
className='p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4'
|
||||||
{...cypressId('history-card')}
|
{...cypressId('history-card')}
|
||||||
{...cypressAttribute('card-title', entryTitle)}>
|
{...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'>
|
<Card.Body className='p-2 d-flex flex-row justify-content-between'>
|
||||||
<div className={'d-flex flex-column'}>
|
<div className={'d-flex flex-column'}>
|
||||||
<PinButton isDark={false} isPinned={entry.pinStatus} onPinClick={onPinEntry} />
|
<PinButton isDark={false} isPinned={entry.pinStatus} onPinClick={onPinEntry} />
|
||||||
</div>
|
</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'}>
|
<div className={'d-flex flex-column justify-content-between'}>
|
||||||
<Card.Title className='m-0 mt-1dot5' {...cypressId('history-entry-title')}>
|
<Card.Title className='m-0 mt-1dot5' {...cypressId('history-entry-title')}>
|
||||||
{entryTitle}
|
{entryTitle}
|
||||||
</Card.Title>
|
</Card.Title>
|
||||||
<div>
|
<div>
|
||||||
<div className='text-black-50 mt-2'>
|
<div className='mt-2'>
|
||||||
<UiIcon icon={IconClock} /> {DateTime.fromISO(entry.lastVisitedAt).toRelative()}
|
<UiIcon icon={IconClock} /> {DateTime.fromISO(entry.lastVisitedAt).toRelative()}
|
||||||
<br />
|
<br />
|
||||||
{lastVisited}
|
{lastVisited}
|
||||||
|
|
|
@ -44,14 +44,14 @@ export const HistoryTableRow: React.FC<HistoryEntryProps & HistoryEventHandlers>
|
||||||
return (
|
return (
|
||||||
<tr {...cypressAttribute('entry-title', entryTitle)}>
|
<tr {...cypressAttribute('entry-title', entryTitle)}>
|
||||||
<td>
|
<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}
|
{entryTitle}
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
<td>{formatHistoryDate(entry.lastVisitedAt)}</td>
|
<td>{formatHistoryDate(entry.lastVisitedAt)}</td>
|
||||||
<td>
|
<td>
|
||||||
{entry.tags.map((tag) => (
|
{entry.tags.map((tag) => (
|
||||||
<Badge className={'bg-light me-1 mb-1'} key={tag}>
|
<Badge className={'me-1 mb-1'} key={tag}>
|
||||||
{tag}
|
{tag}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* 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 { cypressId } from '../../../utils/cypress-attribute'
|
import { cypressId } from '../../../utils/cypress-attribute'
|
||||||
import { Pager } from '../../common/pagination/pager'
|
import { Pager } from '../../common/pagination/pager'
|
||||||
import type { HistoryEntriesProps, HistoryEventHandlers } from '../history-content/history-content'
|
import type { HistoryEntriesProps, HistoryEventHandlers } from '../history-content/history-content'
|
||||||
|
@ -44,13 +45,15 @@ export const HistoryTable: React.FC<HistoryEntriesProps & HistoryEventHandlers>
|
||||||
))
|
))
|
||||||
}, [entries, onPinClick, onRemoveEntryClick, onDeleteNoteClick])
|
}, [entries, onPinClick, onRemoveEntryClick, onDeleteNoteClick])
|
||||||
|
|
||||||
|
const darkModeState = useDarkModeState()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
striped
|
striped
|
||||||
bordered
|
bordered
|
||||||
hover
|
hover
|
||||||
size='sm'
|
size='sm'
|
||||||
variant='dark'
|
variant={darkModeState ? 'dark' : 'light'}
|
||||||
className={styles['history-table']}
|
className={styles['history-table']}
|
||||||
{...cypressId('history-table')}>
|
{...cypressId('history-table')}>
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -36,7 +36,7 @@ export const ClearHistoryButton: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Button
|
<Button
|
||||||
variant={'light'}
|
variant={'secondary'}
|
||||||
title={t('landing.history.toolbar.clear') ?? undefined}
|
title={t('landing.history.toolbar.clear') ?? undefined}
|
||||||
onClick={showModal}
|
onClick={showModal}
|
||||||
{...cypressId('history-clear-button')}>
|
{...cypressId('history-clear-button')}>
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const ExportHistoryButton: React.FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
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} />
|
<UiIcon icon={IconDownload} />
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const HistoryRefreshButton: React.FC = () => {
|
||||||
const refreshHistory = useSafeRefreshHistoryStateCallback()
|
const refreshHistory = useSafeRefreshHistoryStateCallback()
|
||||||
|
|
||||||
return (
|
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} />
|
<UiIcon icon={IconArrowRepeat} />
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
|
@ -90,7 +90,7 @@ export const HistoryToolbar: React.FC = () => {
|
||||||
<ShowIf condition={userExists}>
|
<ShowIf condition={userExists}>
|
||||||
<div className={'me-1 mb-1'}>
|
<div className={'me-1 mb-1'}>
|
||||||
<Button
|
<Button
|
||||||
variant={'light'}
|
variant={'secondary'}
|
||||||
title={t('landing.history.toolbar.uploadAll') ?? undefined}
|
title={t('landing.history.toolbar.uploadAll') ?? undefined}
|
||||||
onClick={onUploadAllToRemote}>
|
onClick={onUploadAllToRemote}>
|
||||||
<UiIcon icon={IconCloudUpload} />
|
<UiIcon icon={IconCloudUpload} />
|
||||||
|
|
|
@ -34,13 +34,13 @@ export const HistoryViewModeToggleButton: React.FC = () => {
|
||||||
<ToggleButtonGroup type='radio' name='options' dir='auto' className={'button-height'} onChange={onViewStateChange}>
|
<ToggleButtonGroup type='radio' name='options' dir='auto' className={'button-height'} onChange={onViewStateChange}>
|
||||||
<Button
|
<Button
|
||||||
title={t('landing.history.toolbar.cards') ?? undefined}
|
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)}>
|
onClick={() => onViewStateChange(ViewStateEnum.CARD)}>
|
||||||
<UiIcon icon={IconStickyFill} className={'fa-fix-line-height'} />
|
<UiIcon icon={IconStickyFill} className={'fa-fix-line-height'} />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
{...cypressId('history-mode-table')}
|
{...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}
|
title={t('landing.history.toolbar.table') ?? undefined}
|
||||||
onClick={() => onViewStateChange(ViewStateEnum.TABLE)}>
|
onClick={() => onViewStateChange(ViewStateEnum.TABLE)}>
|
||||||
<UiIcon icon={IconTable} className={'fa-fix-line-height'} />
|
<UiIcon icon={IconTable} className={'fa-fix-line-height'} />
|
||||||
|
|
|
@ -128,7 +128,7 @@ export const ImportHistoryButton: React.FC = () => {
|
||||||
{...cypressId('import-history-file-input')}
|
{...cypressId('import-history-file-input')}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
variant={'light'}
|
variant={'secondary'}
|
||||||
title={t('landing.history.toolbar.import') ?? undefined}
|
title={t('landing.history.toolbar.import') ?? undefined}
|
||||||
onClick={onUploadButtonClick}
|
onClick={onUploadButtonClick}
|
||||||
{...cypressId('import-history-file-button')}>
|
{...cypressId('import-history-file-button')}>
|
||||||
|
|
|
@ -28,7 +28,7 @@ export interface PinButtonProps {
|
||||||
export const PinButton: React.FC<PinButtonProps> = ({ isPinned, onPinClick, isDark, className }) => {
|
export const PinButton: React.FC<PinButtonProps> = ({ isPinned, onPinClick, isDark, className }) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant={isDark ? 'secondary' : 'light'}
|
variant={isDark ? 'secondary' : 'secondary'}
|
||||||
className={`${styles['history-pin']} ${className || ''} ${isPinned ? styles['pinned'] : ''}`}
|
className={`${styles['history-pin']} ${className || ''} ${isPinned ? styles['pinned'] : ''}`}
|
||||||
onClick={onPinClick}
|
onClick={onPinClick}
|
||||||
{...cypressId('history-entry-pin-button')}
|
{...cypressId('history-entry-pin-button')}
|
||||||
|
|
|
@ -64,7 +64,7 @@ export const SortButton: React.FC<SortButtonProps> = ({ children, onDirectionCha
|
||||||
}, [direction])
|
}, [direction])
|
||||||
|
|
||||||
return (
|
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}
|
{children}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { DarkModePreference } from '../../redux/dark-mode/types'
|
|
||||||
import { Logger } from '../../utils/logger'
|
import { Logger } from '../../utils/logger'
|
||||||
import { AsyncLoadingBoundary } from '../common/async-loading-boundary/async-loading-boundary'
|
import { AsyncLoadingBoundary } from '../common/async-loading-boundary/async-loading-boundary'
|
||||||
import { RendererIframe } from '../common/renderer-iframe/renderer-iframe'
|
import { RendererIframe } from '../common/renderer-iframe/renderer-iframe'
|
||||||
|
@ -32,7 +31,6 @@ export const IntroCustomContent: React.FC = () => {
|
||||||
frameClasses={'w-100 overflow-y-hidden'}
|
frameClasses={'w-100 overflow-y-hidden'}
|
||||||
markdownContentLines={value as string[]}
|
markdownContentLines={value as string[]}
|
||||||
rendererType={RendererType.SIMPLE}
|
rendererType={RendererType.SIMPLE}
|
||||||
forcedDarkMode={DarkModePreference.DARK}
|
|
||||||
adaptFrameHeightToContent={true}
|
adaptFrameHeightToContent={true}
|
||||||
/>
|
/>
|
||||||
</AsyncLoadingBoundary>
|
</AsyncLoadingBoundary>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import React from 'react'
|
||||||
*/
|
*/
|
||||||
export const Footer: React.FC = () => {
|
export const Footer: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<footer className='text-light small'>
|
<footer className='small'>
|
||||||
<PoweredByLinks />
|
<PoweredByLinks />
|
||||||
<SocialLink />
|
<SocialLink />
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
@ -18,12 +18,7 @@ export const VersionInfoLink: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Button
|
<Button size={'sm'} variant={'link'} {...cypressId('show-version-modal')} className={'p-0'} onClick={showModal}>
|
||||||
size={'sm'}
|
|
||||||
variant={'link'}
|
|
||||||
{...cypressId('show-version-modal')}
|
|
||||||
className={'text-light p-0'}
|
|
||||||
onClick={showModal}>
|
|
||||||
<Trans i18nKey={'landing.versionInfo.versionInfo'} />
|
<Trans i18nKey={'landing.versionInfo.versionInfo'} />
|
||||||
</Button>
|
</Button>
|
||||||
<VersionInfoModal onHide={closeModal} show={modalVisibility} />
|
<VersionInfoModal onHide={closeModal} show={modalVisibility} />
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* 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 { MotdModal } from '../common/motd-modal/motd-modal'
|
||||||
import { Footer } from './footer/footer'
|
import { Footer } from './footer/footer'
|
||||||
import { HeaderBar } from './navigation/header-bar/header-bar'
|
import { HeaderBar } from './navigation/header-bar/header-bar'
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import React, { Fragment } from 'react'
|
import React from 'react'
|
||||||
import { Container } from 'react-bootstrap'
|
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.
|
* @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 (
|
return (
|
||||||
<Fragment>
|
<div>
|
||||||
<MotdModal />
|
<MotdModal />
|
||||||
<Container className='text-light d-flex flex-column mvh-100'>
|
<Container className='d-flex flex-column mvh-100'>
|
||||||
<HeaderBar />
|
<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>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</Fragment>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ const HeaderBar: React.FC = () => {
|
||||||
</HeaderNavLink>
|
</HeaderNavLink>
|
||||||
</div>
|
</div>
|
||||||
<div className='d-inline-flex gap-2'>
|
<div className='d-inline-flex gap-2'>
|
||||||
<SettingsButton variant={'outline-light'} />
|
<SettingsButton variant={'outline-dark'} />
|
||||||
<NewNoteButton />
|
<NewNoteButton />
|
||||||
{!userExists ? <SignInButton size='sm' /> : <UserDropdown />}
|
{!userExists ? <SignInButton size='sm' /> : <UserDropdown />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.nav-link {
|
.link {
|
||||||
border-bottom: 2px solid transparent
|
border-bottom: 2px solid transparent
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link-active {
|
.active {
|
||||||
border-bottom-color: #fff;
|
border-bottom-color: var(--bs-primary);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
import { concatCssClasses } from '../../../../utils/concat-css-classes'
|
||||||
import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute'
|
import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute'
|
||||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||||
import styles from './header-nav-link.module.scss'
|
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 }) => {
|
export const HeaderNavLink: React.FC<PropsWithChildren<HeaderNavLinkProps>> = ({ to, children, ...props }) => {
|
||||||
const { route } = useRouter()
|
const { route } = useRouter()
|
||||||
|
|
||||||
const activeClass = useMemo(() => {
|
const className = useMemo(() => {
|
||||||
return route === to ? styles['nav-link-active'] : ''
|
return concatCssClasses(
|
||||||
|
{
|
||||||
|
[styles.active]: route === to
|
||||||
|
},
|
||||||
|
'nav-link',
|
||||||
|
styles.link
|
||||||
|
)
|
||||||
}, [route, to])
|
}, [route, to])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Nav.Item>
|
<Nav.Item>
|
||||||
<Link
|
<Link href={to} passHref={true} className={className} {...cypressId(props)}>
|
||||||
href={to}
|
|
||||||
passHref={true}
|
|
||||||
className={`nav-link text-light ${activeClass} ${styles['nav-link']}`}
|
|
||||||
{...cypressId(props)}>
|
|
||||||
{children}
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
</Nav.Item>
|
</Nav.Item>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { useBooleanState } from '../../../hooks/common/use-boolean-state'
|
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 { cypressId } from '../../../utils/cypress-attribute'
|
||||||
import { IconButton } from '../../common/icon-button/icon-button'
|
import { IconButton } from '../../common/icon-button/icon-button'
|
||||||
import { SettingsModal } from './settings-modal'
|
import { SettingsModal } from './settings-modal'
|
||||||
|
@ -17,9 +18,17 @@ export type SettingsButtonProps = Omit<ButtonProps, 'onClick'>
|
||||||
*/
|
*/
|
||||||
export const SettingsButton: React.FC<SettingsButtonProps> = (props) => {
|
export const SettingsButton: React.FC<SettingsButtonProps> = (props) => {
|
||||||
const [show, showModal, hideModal] = useBooleanState(false)
|
const [show, showModal, hideModal] = useBooleanState(false)
|
||||||
|
const buttonVariant = useOutlineButtonVariant()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<IconButton {...props} {...cypressId('settingsButton')} onClick={showModal} icon={IconGear} />
|
<IconButton
|
||||||
|
{...props}
|
||||||
|
{...cypressId('settingsButton')}
|
||||||
|
onClick={showModal}
|
||||||
|
icon={IconGear}
|
||||||
|
variant={buttonVariant}
|
||||||
|
/>
|
||||||
<SettingsModal show={show} onHide={hideModal} />
|
<SettingsModal show={show} onHide={hideModal} />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,7 +25,6 @@ export const PasswordField: React.FC<AuthFieldProps> = ({ onChange, invalid }) =
|
||||||
size='sm'
|
size='sm'
|
||||||
placeholder={t('login.auth.password') ?? undefined}
|
placeholder={t('login.auth.password') ?? undefined}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className='bg-dark text-light'
|
|
||||||
autoComplete='current-password'
|
autoComplete='current-password'
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|
|
@ -32,7 +32,6 @@ export const UsernameField: React.FC<UsernameFieldProps> = ({ onChange, invalid,
|
||||||
size='sm'
|
size='sm'
|
||||||
placeholder={t('login.auth.username') ?? undefined}
|
placeholder={t('login.auth.username') ?? undefined}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className='bg-dark text-light'
|
|
||||||
autoComplete='username'
|
autoComplete='username'
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|
|
@ -49,7 +49,7 @@ export const ViaLocal: React.FC = () => {
|
||||||
const onPasswordChange = useOnInputChange(setPassword)
|
const onPasswordChange = useOnInputChange(setPassword)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className='bg-dark mb-4'>
|
<Card className='mb-4'>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Card.Title>
|
<Card.Title>
|
||||||
<Trans i18nKey='login.signInVia' values={{ service: t('login.auth.username') }} />
|
<Trans i18nKey='login.signInVia' values={{ service: t('login.auth.username') }} />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.image-drop {
|
.image-drop {
|
||||||
border: 3px dashed var(--bs-dark);
|
border: 3px dashed var(--bs-primary);
|
||||||
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
transition: background-color 50ms, color 50ms;
|
transition: background-color 50ms, color 50ms;
|
||||||
|
|
|
@ -97,7 +97,7 @@ export const ImagePlaceholder: React.FC<PlaceholderImageFrameProps> = ({
|
||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
ref={fileInputReference}
|
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'}>
|
<div className={'d-flex flex-column'}>
|
||||||
<span className='my-2'>
|
<span className='my-2'>
|
||||||
<Trans i18nKey={'editor.embeddings.placeholderImage.placeholderText'} />
|
<Trans i18nKey={'editor.embeddings.placeholderImage.placeholderText'} />
|
||||||
|
|
|
@ -13,11 +13,16 @@ export const useApplyDarkModeStyle = (): void => {
|
||||||
const darkMode = useDarkModeState()
|
const darkMode = useDarkModeState()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (darkMode) {
|
if (darkMode) {
|
||||||
window.document.body.classList.add('dark')
|
window.document.body.dataset.bsTheme = 'dark'
|
||||||
} else {
|
} else {
|
||||||
window.document.body.classList.remove('dark')
|
window.document.body.dataset.bsTheme = 'light'
|
||||||
}
|
}
|
||||||
}, [darkMode])
|
}, [darkMode])
|
||||||
|
|
||||||
useEffect(() => () => window.document.body.classList.remove('dark'), [])
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
window.document.body.dataset.bsTheme = 'light'
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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')
|
||||||
|
})
|
||||||
|
})
|
10
frontend/src/hooks/dark-mode/use-outline-button-variant.ts
Normal file
10
frontend/src/hooks/dark-mode/use-outline-button-variant.ts
Normal 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'
|
||||||
|
}
|
|
@ -3,7 +3,6 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import '../../global-styles/dark.scss'
|
|
||||||
import '../../global-styles/index.scss'
|
import '../../global-styles/index.scss'
|
||||||
import type { FrontendConfig } from '../api/config/types'
|
import type { FrontendConfig } from '../api/config/types'
|
||||||
import { ApplicationLoader } from '../components/application-loader/application-loader'
|
import { ApplicationLoader } from '../components/application-loader/application-loader'
|
||||||
|
|
|
@ -72,7 +72,7 @@ export const LoginPage: React.FC = () => {
|
||||||
</ShowIf>
|
</ShowIf>
|
||||||
<ShowIf condition={oneClickProviders.length > 0}>
|
<ShowIf condition={oneClickProviders.length > 0}>
|
||||||
<Col xs={12} sm={10} lg={4}>
|
<Col xs={12} sm={10} lg={4}>
|
||||||
<Card className='bg-dark mb-4'>
|
<Card className='mb-4'>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Card.Title>
|
<Card.Title>
|
||||||
<Trans i18nKey='login.signInVia' values={{ service: '' }} />
|
<Trans i18nKey='login.signInVia' values={{ service: '' }} />
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { NoteLoadingBoundary } from '../../components/common/note-loading-bounda
|
||||||
import { EditorPageContent } from '../../components/editor-page/editor-page-content'
|
import { EditorPageContent } from '../../components/editor-page/editor-page-content'
|
||||||
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 { ResetRealtimeStateBoundary } from '../../components/editor-page/reset-realtime-state-boundary'
|
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 type { NextPage } from 'next'
|
||||||
import React from 'react'
|
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.
|
* Renders a page that is used by the user to edit markdown notes. It contains the editor and a renderer.
|
||||||
*/
|
*/
|
||||||
export const EditorPage: NextPage = () => {
|
export const EditorPage: NextPage = () => {
|
||||||
|
useApplyDarkModeStyle()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResetRealtimeStateBoundary>
|
<ResetRealtimeStateBoundary>
|
||||||
<NoteLoadingBoundary>
|
<NoteLoadingBoundary>
|
||||||
|
|
|
@ -84,7 +84,7 @@ export const RegisterPage: NextPage = () => {
|
||||||
</h1>
|
</h1>
|
||||||
<Row className='h-100 d-flex justify-content-center'>
|
<Row className='h-100 d-flex justify-content-center'>
|
||||||
<Col lg={6}>
|
<Col lg={6}>
|
||||||
<Card className='bg-dark mb-4 text-start'>
|
<Card className='mb-4 text-start'>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Form onSubmit={doRegisterSubmit} className={'d-flex flex-column gap-3'}>
|
<Form onSubmit={doRegisterSubmit} className={'d-flex flex-column gap-3'}>
|
||||||
<UsernameField onChange={onUsernameChange} value={username} />
|
<UsernameField onChange={onUsernameChange} value={username} />
|
||||||
|
|
|
@ -7,12 +7,17 @@ import { RendererToEditorCommunicatorContextProvider } from '../components/edito
|
||||||
import { RenderPageContent } from '../components/render-page/render-page-content'
|
import { RenderPageContent } from '../components/render-page/render-page-content'
|
||||||
import { useApplyDarkModeStyle } from '../hooks/dark-mode/use-apply-dark-mode-style'
|
import { useApplyDarkModeStyle } from '../hooks/dark-mode/use-apply-dark-mode-style'
|
||||||
import type { NextPage } from 'next'
|
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.
|
* Renders the actual markdown renderer that receives the content and metadata via iframe communication.
|
||||||
*/
|
*/
|
||||||
export const RenderPage: NextPage = () => {
|
export const RenderPage: NextPage = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.classList.add('bg-transparent')
|
||||||
|
return () => document.body.classList.remove('bg-transparent')
|
||||||
|
}, [])
|
||||||
|
|
||||||
useApplyDarkModeStyle()
|
useApplyDarkModeStyle()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const DocumentReadOnlyPage: React.FC = () => {
|
||||||
<NoteLoadingBoundary>
|
<NoteLoadingBoundary>
|
||||||
<HeadMetaProperties />
|
<HeadMetaProperties />
|
||||||
<MotdModal />
|
<MotdModal />
|
||||||
<div className={'d-flex flex-column mvh-100 bg-light'}>
|
<div className={'d-flex flex-column mvh-100'}>
|
||||||
<AppBar mode={AppBarMode.BASIC} />
|
<AppBar mode={AppBarMode.BASIC} />
|
||||||
<DocumentReadOnlyPageContent />
|
<DocumentReadOnlyPageContent />
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue