diff --git a/src/api/auth/index.ts b/src/api/auth/index.ts index 6b0c050c4..18a9872a7 100644 --- a/src/api/auth/index.ts +++ b/src/api/auth/index.ts @@ -1,5 +1,5 @@ import { RegisterError } from '../../components/register-page/register-page' -import { expectResponseCode, getApiUrl, defaultFetchConfig } from '../utils' +import { defaultFetchConfig, expectResponseCode, getApiUrl } from '../utils' export const doInternalLogin = async (username: string, password: string): Promise => { const response = await fetch(getApiUrl() + '/auth/internal', { diff --git a/src/api/config/index.ts b/src/api/config/index.ts index 1cab3fb38..ba17c574b 100644 --- a/src/api/config/index.ts +++ b/src/api/config/index.ts @@ -1,4 +1,4 @@ -import { expectResponseCode, getApiUrl, defaultFetchConfig } from '../utils' +import { defaultFetchConfig, expectResponseCode, getApiUrl } from '../utils' import { Config } from './types' export const getConfig = async (): Promise => { diff --git a/src/api/history/index.ts b/src/api/history/index.ts index a00e34050..8492755d6 100644 --- a/src/api/history/index.ts +++ b/src/api/history/index.ts @@ -1,6 +1,5 @@ - -import { expectResponseCode, getApiUrl, defaultFetchConfig } from '../utils' import { HistoryEntry } from '../../components/history-page/history-page' +import { defaultFetchConfig, expectResponseCode, getApiUrl } from '../utils' export const getHistory = async (): Promise => { const response = await fetch(getApiUrl() + '/history') diff --git a/src/api/me/index.ts b/src/api/me/index.ts index b310de105..50bf9bb6d 100644 --- a/src/api/me/index.ts +++ b/src/api/me/index.ts @@ -1,5 +1,5 @@ import { UserResponse } from '../users/types' -import { expectResponseCode, getApiUrl, defaultFetchConfig } from '../utils' +import { defaultFetchConfig, expectResponseCode, getApiUrl } from '../utils' export const getMe = async (): Promise => { const response = await fetch(getApiUrl() + '/me', { diff --git a/src/api/media/index.ts b/src/api/media/index.ts index 2f7fb2c4e..95e39f146 100644 --- a/src/api/media/index.ts +++ b/src/api/media/index.ts @@ -1,5 +1,5 @@ import { ImageProxyResponse } from '../../components/markdown-renderer/replace-components/image/types' -import { expectResponseCode, getApiUrl, defaultFetchConfig } from '../utils' +import { defaultFetchConfig, expectResponseCode, getApiUrl } from '../utils' export const getProxiedUrl = async (imageUrl: string): Promise => { const response = await fetch(getApiUrl() + '/media/proxy', { diff --git a/src/components/application-loader/application-loader.tsx b/src/components/application-loader/application-loader.tsx index 16f8bd07e..d81579925 100644 --- a/src/components/application-loader/application-loader.tsx +++ b/src/components/application-loader/application-loader.tsx @@ -1,8 +1,7 @@ -import React, { Fragment, useCallback, useEffect, useState } from 'react' +import React, { Suspense, useCallback, useEffect, useState } from 'react' import { useLocation } from 'react-router' import './application-loader.scss' import { createSetUpTaskList, InitTask } from './initializers' - import { LoadingScreen } from './loading-screen' export const ApplicationLoader: React.FC = ({ children }) => { @@ -33,9 +32,13 @@ export const ApplicationLoader: React.FC = ({ children }) => { } }, [initTasks, runTask]) - return ( - doneTasks < initTasks.length || initTasks.length === 0 - ? - : {children} - ) + const tasksAreRunning = doneTasks < initTasks.length || initTasks.length === 0 + + if (tasksAreRunning) { + return + } else { + return )}> + {children} + + } } diff --git a/src/components/application-loader/loading-screen.tsx b/src/components/application-loader/loading-screen.tsx index ca9d600d3..41b1a0812 100644 --- a/src/components/application-loader/loading-screen.tsx +++ b/src/components/application-loader/loading-screen.tsx @@ -4,7 +4,7 @@ import { ForkAwesomeIcon } from '../common/fork-awesome/fork-awesome-icon' import { ShowIf } from '../common/show-if/show-if' export interface LoadingScreenProps { - failedTitle: string + failedTitle?: string } export const LoadingScreen: React.FC = ({ failedTitle }) => { @@ -14,7 +14,7 @@ export const LoadingScreen: React.FC = ({ failedTitle }) => - + The task '{failedTitle}' failed.
For further information look into the browser console. diff --git a/src/components/common/branding/branding.tsx b/src/components/common/branding/branding.tsx index aabb7f126..f4e2c1337 100644 --- a/src/components/common/branding/branding.tsx +++ b/src/components/common/branding/branding.tsx @@ -1,4 +1,3 @@ - import equal from 'fast-deep-equal' import React from 'react' import { useSelector } from 'react-redux' diff --git a/src/components/common/fork-awesome/fork-awesome-icon.tsx b/src/components/common/fork-awesome/fork-awesome-icon.tsx index faca7252e..f98bf622d 100644 --- a/src/components/common/fork-awesome/fork-awesome-icon.tsx +++ b/src/components/common/fork-awesome/fork-awesome-icon.tsx @@ -1,4 +1,3 @@ -import 'fork-awesome/css/fork-awesome.min.css' import React from 'react' import { IconName, IconSize } from './types' diff --git a/src/components/common/icon-button/icon-button.tsx b/src/components/common/icon-button/icon-button.tsx index 75582e7c7..ad8a421e7 100644 --- a/src/components/common/icon-button/icon-button.tsx +++ b/src/components/common/icon-button/icon-button.tsx @@ -1,8 +1,8 @@ import React from 'react' import { Button, ButtonProps } from 'react-bootstrap' import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' -import './icon-button.scss' import { IconName } from '../fork-awesome/types' +import './icon-button.scss' export interface IconButtonProps extends ButtonProps { icon: IconName diff --git a/src/components/common/icon-button/translated-icon-button.tsx b/src/components/common/icon-button/translated-icon-button.tsx index 7ba365d33..029c3a614 100644 --- a/src/components/common/icon-button/translated-icon-button.tsx +++ b/src/components/common/icon-button/translated-icon-button.tsx @@ -1,6 +1,5 @@ import React from 'react' import { Trans } from 'react-i18next' -import './icon-button.scss' import { IconButton, IconButtonProps } from './icon-button' export interface TranslatedIconButton extends IconButtonProps { diff --git a/src/components/common/modals/deletion-modal.tsx b/src/components/common/modals/deletion-modal.tsx index 02c947e72..28a67c412 100644 --- a/src/components/common/modals/deletion-modal.tsx +++ b/src/components/common/modals/deletion-modal.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Modal, Button } from 'react-bootstrap' +import { Button, Modal } from 'react-bootstrap' import { Trans, useTranslation } from 'react-i18next' import { CommonModal, CommonModalProps } from './common-modal' diff --git a/src/components/common/motd-banner/motd-banner.tsx b/src/components/common/motd-banner/motd-banner.tsx index 21f4fddd0..4a63d651e 100644 --- a/src/components/common/motd-banner/motd-banner.tsx +++ b/src/components/common/motd-banner/motd-banner.tsx @@ -1,9 +1,9 @@ import equal from 'fast-deep-equal' import React from 'react' +import { Alert, Button } from 'react-bootstrap' import { useSelector } from 'react-redux' import { Link } from 'react-router-dom' import { ApplicationState } from '../../../redux' -import { Alert, Button } from 'react-bootstrap' import { setBanner } from '../../../redux/banner/methods' import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' import { ShowIf } from '../show-if/show-if' diff --git a/src/components/editor/app-bar/app-bar.tsx b/src/components/editor/app-bar/app-bar.tsx index 4c4294731..3c380ded9 100644 --- a/src/components/editor/app-bar/app-bar.tsx +++ b/src/components/editor/app-bar/app-bar.tsx @@ -9,12 +9,12 @@ import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon' import { ShowIf } from '../../common/show-if/show-if' import { SignInButton } from '../../landing-layout/navigation/sign-in-button' import { UserDropdown } from '../../landing-layout/navigation/user-dropdown' -import { SyncScrollButtons } from './sync-scroll-buttons/sync-scroll-buttons' import { EditorPathParams } from '../editor' import { DarkModeButton } from './dark-mode-button' import { EditorViewMode } from './editor-view-mode' import { HelpButton } from './help-button/help-button' import { NavbarBranding } from './navbar-branding' +import { SyncScrollButtons } from './sync-scroll-buttons/sync-scroll-buttons' export const AppBar: React.FC = () => { const { t } = useTranslation() diff --git a/src/components/editor/app-bar/help-button/links.tsx b/src/components/editor/app-bar/help-button/links.tsx index 378f8f354..1358ea113 100644 --- a/src/components/editor/app-bar/help-button/links.tsx +++ b/src/components/editor/app-bar/help-button/links.tsx @@ -2,10 +2,10 @@ import React from 'react' import { Col, Row } from 'react-bootstrap' import { Trans, useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' +import links from '../../../../links.json' import { ApplicationState } from '../../../../redux' import { TranslatedExternalLink } from '../../../common/links/translated-external-link' import { TranslatedInternalLink } from '../../../common/links/translated-internal-link' -import links from '../../../../links.json' export const Links: React.FC = () => { useTranslation() diff --git a/src/components/editor/app-bar/sync-scroll-buttons/buttonIcon.svg b/src/components/editor/app-bar/sync-scroll-buttons/buttonIcon.svg index e7c389701..a4ccb32c5 100644 --- a/src/components/editor/app-bar/sync-scroll-buttons/buttonIcon.svg +++ b/src/components/editor/app-bar/sync-scroll-buttons/buttonIcon.svg @@ -1,19 +1,18 @@ + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="512" + height="512" + viewBox="0 0 135.46666 135.46666" + version="1.1" + id="svg8" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + sodipodi:docname="buttonIcon.svg"> { useTranslation() diff --git a/src/components/editor/document-bar/revisions/revision-modal.tsx b/src/components/editor/document-bar/revisions/revision-modal.tsx index f4586b864..63f98a94b 100644 --- a/src/components/editor/document-bar/revisions/revision-modal.tsx +++ b/src/components/editor/document-bar/revisions/revision-modal.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from 'react' -import { Alert, Col, ListGroup, Modal, Row, Button } from 'react-bootstrap' +import { Alert, Button, Col, ListGroup, Modal, Row } from 'react-bootstrap' import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer' import { Trans, useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' diff --git a/src/components/editor/editor-pane/autocompletion/code-block.ts b/src/components/editor/editor-pane/autocompletion/code-block.ts index cc5b8f564..b93caa282 100644 --- a/src/components/editor/editor-pane/autocompletion/code-block.ts +++ b/src/components/editor/editor-pane/autocompletion/code-block.ts @@ -1,35 +1,38 @@ import { Editor, Hint, Hints, Pos } from 'codemirror' -import hljs from 'highlight.js' import { findWordAtCursor, Hinter, search } from './index' const allowedChars = /[`\w-_+]/ const wordRegExp = /^```((\w|-|_|\+)*)$/ -const allSupportedLanguages = hljs.listLanguages().concat('csv', 'flow', 'html') +let allSupportedLanguages: string[] = [] const codeBlockHint = (editor: Editor): Promise< Hints| null > => { - return new Promise((resolve) => { - const searchTerm = findWordAtCursor(editor, allowedChars) - const searchResult = wordRegExp.exec(searchTerm.text) - if (searchResult === null) { - resolve(null) - return - } - const term = searchResult[1] - const suggestions = search(term, allSupportedLanguages) - const cursor = editor.getCursor() - if (!suggestions) { - resolve(null) - } else { - resolve({ - list: suggestions.map((suggestion: string): Hint => ({ - text: '```' + suggestion + '\n\n```\n', - displayText: suggestion - })), - from: Pos(cursor.line, searchTerm.start), - to: Pos(cursor.line, searchTerm.end) - }) - } - }) + return import(/* webpackChunkName: "highlight.js" */ 'highlight.js').then(hljs => + new Promise((resolve) => { + const searchTerm = findWordAtCursor(editor, allowedChars) + const searchResult = wordRegExp.exec(searchTerm.text) + if (searchResult === null) { + resolve(null) + return + } + const term = searchResult[1] + if (allSupportedLanguages.length === 0) { + allSupportedLanguages = hljs.listLanguages().concat('csv', 'flow', 'html') + } + const suggestions = search(term, allSupportedLanguages) + const cursor = editor.getCursor() + if (!suggestions) { + resolve(null) + } else { + resolve({ + list: suggestions.map((suggestion: string): Hint => ({ + text: '```' + suggestion + '\n\n```\n', + displayText: suggestion + })), + from: Pos(cursor.line, searchTerm.start), + to: Pos(cursor.line, searchTerm.end) + }) + } + })) } export const CodeBlockHinter: Hinter = { diff --git a/src/components/editor/editor-pane/editor-pane.scss b/src/components/editor/editor-pane/editor-pane.scss index efafde729..b1d50a7c9 100644 --- a/src/components/editor/editor-pane/editor-pane.scss +++ b/src/components/editor/editor-pane/editor-pane.scss @@ -1,8 +1,8 @@ -@import '../../../../node_modules/codemirror/lib/codemirror.css'; -@import '../../../../node_modules/codemirror/addon/display/fullscreen.css'; -@import '../../../../node_modules/codemirror/addon/dialog/dialog.css'; -@import '../../../../node_modules/codemirror/theme/neat.css'; -@import './one-dark.css'; +@import '../../../../node_modules/codemirror/lib/codemirror'; +@import '../../../../node_modules/codemirror/addon/display/fullscreen'; +@import '../../../../node_modules/codemirror/addon/dialog/dialog'; +@import '../../../../node_modules/codemirror/theme/neat'; +@import './one-dark'; @import 'hints'; .CodeMirror { diff --git a/src/components/editor/editor-pane/one-dark.css b/src/components/editor/editor-pane/one-dark.scss similarity index 100% rename from src/components/editor/editor-pane/one-dark.css rename to src/components/editor/editor-pane/one-dark.scss diff --git a/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker-button.tsx b/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker-button.tsx index 0949b7d5e..214fe69b0 100644 --- a/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker-button.tsx +++ b/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker-button.tsx @@ -3,8 +3,8 @@ import React, { Fragment, useState } from 'react' import { Button } from 'react-bootstrap' import { useTranslation } from 'react-i18next' import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon' -import { EmojiPicker } from './emoji-picker' import { addEmoji } from '../utils/toolbarButtonUtils' +import { EmojiPicker } from './emoji-picker' export interface EmojiPickerButtonProps { editor: CodeMirror.Editor diff --git a/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker.scss b/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker.scss index d97a6af30..63b5a7b17 100644 --- a/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker.scss +++ b/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker.scss @@ -1,3 +1,5 @@ +@import '../../../../../../node_modules/emoji-mart/css/emoji-mart'; + .emoji-mart { position: absolute; z-index: 10000; diff --git a/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker.tsx b/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker.tsx index 6e39a8b6a..b81bbabac 100644 --- a/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker.tsx +++ b/src/components/editor/editor-pane/tool-bar/emoji-picker/emoji-picker.tsx @@ -1,12 +1,11 @@ import { CustomEmoji, Data, EmojiData, NimblePicker } from 'emoji-mart' -import 'emoji-mart/css/emoji-mart.css' import emojiData from 'emoji-mart/data/twitter.json' import React, { useRef } from 'react' import { useClickAway } from 'react-use' import { ShowIf } from '../../../../common/show-if/show-if' import './emoji-picker.scss' -import { ForkAwesomeIcons } from './icon-names' import forkawesomeIcon from './forkawesome.png' +import { ForkAwesomeIcons } from './icon-names' export interface EmojiPickerProps { show: boolean diff --git a/src/components/editor/editor.tsx b/src/components/editor/editor.tsx index e0aa107ed..4f8269b1f 100644 --- a/src/components/editor/editor.tsx +++ b/src/components/editor/editor.tsx @@ -145,3 +145,5 @@ export const Editor: React.FC = () => { ) } + +export default Editor diff --git a/src/components/history-page/entry-menu/entry-menu.scss b/src/components/history-page/entry-menu/entry-menu.scss index f94e809e5..ec62f421c 100644 --- a/src/components/history-page/entry-menu/entry-menu.scss +++ b/src/components/history-page/entry-menu/entry-menu.scss @@ -1,20 +1,5 @@ .history-menu { - - .fa, &::after { - opacity: 0.5; - } - - &:hover .fa, &:hover::after { - opacity: 1; - } - &.btn { padding: 0.6rem 0.65rem; } } - -.dropup .dropdown-toggle, .dropdown-toggle { - &.no-arrow::after { - content: initial; - } -} diff --git a/src/components/history-page/entry-menu/entry-menu.tsx b/src/components/history-page/entry-menu/entry-menu.tsx index 358d1c4d9..6e119e043 100644 --- a/src/components/history-page/entry-menu/entry-menu.tsx +++ b/src/components/history-page/entry-menu/entry-menu.tsx @@ -18,7 +18,7 @@ export interface EntryMenuProps { className?: string } -const EntryMenu: React.FC = ({ id, title, location, isDark, onRemove, onDelete, className }) => { +export const EntryMenu: React.FC = ({ id, title, location, isDark, onRemove, onDelete, className }) => { useTranslation() return ( @@ -54,5 +54,3 @@ const EntryMenu: React.FC = ({ id, title, location, isDark, onRe ) } - -export { EntryMenu } diff --git a/src/components/history-page/history-page.tsx b/src/components/history-page/history-page.tsx index 2b0f58d4e..07f90b243 100644 --- a/src/components/history-page/history-page.tsx +++ b/src/components/history-page/history-page.tsx @@ -189,33 +189,33 @@ export const HistoryPage: React.FC = () => { sortAndFilterEntries(allEntries, toolbarState), [allEntries, toolbarState]) - return ( - - -
- -
- -

- - - - + +
+ +
+
+

+ + - - ) + + + } + +export default HistoryPage diff --git a/src/components/intro-page/intro-page.tsx b/src/components/intro-page/intro-page.tsx index 323cd083f..b6c2c7568 100644 --- a/src/components/intro-page/intro-page.tsx +++ b/src/components/intro-page/intro-page.tsx @@ -6,26 +6,22 @@ import { CoverButtons } from './cover-buttons/cover-buttons' import { FeatureLinks } from './feature-links' import screenshot from './img/screenshot.png' -const IntroPage: React.FC = () => { +export const IntroPage: React.FC = () => { const { t } = useTranslation() - return ( - -

- - HedgeDoc - -

-

- -

+ return +

+ + HedgeDoc + +

+

+ +

- + - {t('landing.intro.screenShotAltText')} - -
- ) + {t('landing.intro.screenShotAltText')} + +
} - -export { IntroPage } diff --git a/src/components/login-page/auth/via-one-click.tsx b/src/components/login-page/auth/via-one-click.tsx index 377cabd56..c4788ff16 100644 --- a/src/components/login-page/auth/via-one-click.tsx +++ b/src/components/login-page/auth/via-one-click.tsx @@ -100,7 +100,7 @@ export interface ViaOneClickProps { optionalName?: string; } -const ViaOneClick: React.FC = ({ oneClickType, optionalName }) => { +export const ViaOneClick: React.FC = ({ oneClickType, optionalName }) => { const backendUrl = useSelector((state: ApplicationState) => state.apiUrl.apiUrl) const { name, icon, className, url } = getMetadata(backendUrl, oneClickType) const text = optionalName || name @@ -116,5 +116,3 @@ const ViaOneClick: React.FC = ({ oneClickType, optionalName }) ) } - -export { ViaOneClick } diff --git a/src/components/login-page/login-page.tsx b/src/components/login-page/login-page.tsx index e609e40d0..273da7329 100644 --- a/src/components/login-page/login-page.tsx +++ b/src/components/login-page/login-page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { Fragment } from 'react' import { Card, Col, Row } from 'react-bootstrap' import { Trans, useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' @@ -10,6 +10,7 @@ import { ViaLdap } from './auth/via-ldap' import { OneClickType, ViaOneClick } from './auth/via-one-click' import { ViaOpenId } from './auth/via-openid' import equal from 'fast-deep-equal' + export const LoginPage: React.FC = () => { useTranslation() const authProviders = useSelector((state: ApplicationState) => state.config.authProviders, equal) @@ -38,7 +39,7 @@ export const LoginPage: React.FC = () => { ) } - return ( + return
@@ -76,5 +77,5 @@ export const LoginPage: React.FC = () => {
- ) +
} diff --git a/src/components/markdown-renderer/replace-components/abc/abc-frame.tsx b/src/components/markdown-renderer/replace-components/abc/abc-frame.tsx index 079358c28..a8a6acb0f 100644 --- a/src/components/markdown-renderer/replace-components/abc/abc-frame.tsx +++ b/src/components/markdown-renderer/replace-components/abc/abc-frame.tsx @@ -1,19 +1,21 @@ import React, { useEffect, useRef } from 'react' -import { renderAbc } from 'abcjs' export interface AbcFrameProps { code: string } + export const AbcFrame: React.FC = ({ code }) => { const container = useRef(null) useEffect(() => { - if (container.current) { - renderAbc(container.current, code) + if (!container.current) { + return } + const actualContainer = container.current + import(/* webpackChunkName: "abc.js" */ 'abcjs').then((imp) => { + imp.renderAbc(actualContainer, code) + }).catch(() => { console.error('error while loading abcjs') }) }, [code]) - return ( -
- ) + return
} diff --git a/src/components/markdown-renderer/replace-components/flow/flowchart/flowchart.tsx b/src/components/markdown-renderer/replace-components/flow/flowchart/flowchart.tsx index f3b0b2e5c..39db90aa0 100644 --- a/src/components/markdown-renderer/replace-components/flow/flowchart/flowchart.tsx +++ b/src/components/markdown-renderer/replace-components/flow/flowchart/flowchart.tsx @@ -1,4 +1,3 @@ -import { parse } from 'flowchart.js' import React, { useEffect, useRef, useState } from 'react' import { Alert } from 'react-bootstrap' import { Trans, useTranslation } from 'react-i18next' @@ -17,20 +16,21 @@ export const FlowChart: React.FC = ({ code }) => { if (diagramRef.current === null) { return } - const parserOutput = parse(code) - try { - parserOutput.drawSVG(diagramRef.current, { - 'line-width': 2, - fill: 'none', - 'font-size': '16px', - 'font-family': 'Source Code Pro, twemoji, monospace' - }) - setError(false) - } catch (error) { - setError(true) - } - const currentDiagramRef = diagramRef.current + import(/* webpackChunkName: "flowchart.js" */ 'flowchart.js').then((imp) => { + const parserOutput = imp.parse(code) + try { + parserOutput.drawSVG(currentDiagramRef, { + 'line-width': 2, + fill: 'none', + 'font-size': '16px', + 'font-family': 'Source Code Pro, twemoji, monospace' + }) + setError(false) + } catch (error) { + setError(true) + } + }).catch(() => { console.error('error while loading flowchart.js') }) return () => { Array.from(currentDiagramRef.children).forEach(value => value.remove()) @@ -41,8 +41,8 @@ export const FlowChart: React.FC = ({ code }) => { return ( - - ) + ) + } else { + return
} - return
} diff --git a/src/components/markdown-renderer/replace-components/graphviz/graphviz-frame.tsx b/src/components/markdown-renderer/replace-components/graphviz/graphviz-frame.tsx index 53c0a6043..ecdd7c694 100644 --- a/src/components/markdown-renderer/replace-components/graphviz/graphviz-frame.tsx +++ b/src/components/markdown-renderer/replace-components/graphviz/graphviz-frame.tsx @@ -1,7 +1,5 @@ -import { graphviz } from 'd3-graphviz' import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react' import { Alert } from 'react-bootstrap' -import '@hpcc-js/wasm' import { ShowIf } from '../../../common/show-if/show-if' export interface GraphvizFrameProps { @@ -25,14 +23,18 @@ export const GraphvizFrame: React.FC = ({ code }) => { if (!container.current) { return } - try { - setError(undefined) - graphviz(container.current, { useWorker: false, zoom: false }) - .onerror(showError) - .renderDot(code) - } catch (error) { - showError(error) - } + const actualContainer = container.current + + Promise.all([import(/* webpackChunkName: "d3-graphviz" */ 'd3-graphviz'), import('@hpcc-js/wasm')]).then(([imp]) => { + try { + setError(undefined) + imp.graphviz(actualContainer, { useWorker: false, zoom: false }) + .onerror(showError) + .renderDot(code) + } catch (error) { + showError(error) + } + }).catch(() => { console.error('error while loading graphviz') }) }, [code, error, showError]) return @@ -42,3 +44,5 @@ export const GraphvizFrame: React.FC = ({ code }) => {
} + +export default GraphvizFrame diff --git a/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.tsx b/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.tsx index 42a5427b2..649b71257 100644 --- a/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.tsx +++ b/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.tsx @@ -1,5 +1,4 @@ -import hljs from 'highlight.js' -import React, { Fragment, useMemo } from 'react' +import React, { Fragment, ReactElement, useEffect, useState } from 'react' import ReactHtmlParser from 'react-html-parser' import { CopyToClipboardButton } from '../../../../common/copyable/copy-to-clipboard-button/copy-to-clipboard-button' import '../../../utils/button-inside.scss' @@ -21,10 +20,6 @@ export const escapeHtml = (unsafe: string): string => { .replace(/'/g, ''') } -const checkIfLanguageIsSupported = (language: string): boolean => { - return hljs.listLanguages().includes(language) -} - const correctLanguage = (language: string | undefined): string | undefined => { switch (language) { case 'html': @@ -34,29 +29,36 @@ const correctLanguage = (language: string | undefined): string | undefined => { } } +const replaceCode = (code: string): ReactElement[][] => { + return code.split('\n') + .filter(line => !!line) + .map(line => ReactHtmlParser(line)) +} + export const HighlightedCode: React.FC = ({ code, language, startLineNumber, wrapLines }) => { - const highlightedCode = useMemo(() => { - const replacedLanguage = correctLanguage(language) - return ((!!replacedLanguage && checkIfLanguageIsSupported(replacedLanguage)) ? hljs.highlight(replacedLanguage, code).value : escapeHtml(code)) - .split('\n') - .filter(line => !!line) - .map(line => ReactHtmlParser(line)) - }, [code, language]) + const [dom, setDom] = useState() + + useEffect(() => { + import(/* webpackChunkName: "highlight.js" */ 'highlight.js').then((hljs) => { + const correctedLanguage = correctLanguage(language) + const languageSupported = (lang: string) => hljs.listLanguages().includes(lang) + const unreplacedCode = !!correctedLanguage && languageSupported(correctedLanguage) ? hljs.highlight(correctedLanguage, code).value : escapeHtml(code) + const replacedDom = replaceCode(unreplacedCode).map((line, index) => ( + + +
+ {line} +
+
+ )) + setDom(replacedDom) + }).catch(() => { console.error('error while loading highlight.js') }) + }, [code, language, startLineNumber]) return ( - { - highlightedCode - .map((line, index) => ( - - -
- {line} -
-
- )) - } + { dom }
diff --git a/src/components/markdown-renderer/replace-components/katex/katex-replacer.tsx b/src/components/markdown-renderer/replace-components/katex/katex-replacer.tsx index a55a4f43d..6dcec4815 100644 --- a/src/components/markdown-renderer/replace-components/katex/katex-replacer.tsx +++ b/src/components/markdown-renderer/replace-components/katex/katex-replacer.tsx @@ -1,8 +1,7 @@ import { DomElement } from 'domhandler' import React from 'react' -import 'katex/dist/katex.min.css' -import TeX from '@matejmazur/react-katex' import { ComponentReplacer } from '../ComponentReplacer' +import './katex.scss' const getNodeIfKatexBlock = (node: DomElement): (DomElement|undefined) => { if (node.name !== 'p' || !node.children || node.children.length === 0) { @@ -17,13 +16,15 @@ const getNodeIfInlineKatex = (node: DomElement): (DomElement|undefined) => { return (node.name === 'app-katex' && node.attribs?.inline !== undefined) ? node : undefined } +const KaTeX = React.lazy(() => import(/* webpackChunkName: "katex" */ '@matejmazur/react-katex')) + export class KatexReplacer extends ComponentReplacer { public getReplacement (node: DomElement): React.ReactElement | undefined { const katex = getNodeIfKatexBlock(node) || getNodeIfInlineKatex(node) if (katex?.children && katex.children[0]) { const mathJaxContent = katex.children[0]?.data as string const isInline = (katex.attribs?.inline) !== undefined - return + return } } } diff --git a/src/components/markdown-renderer/replace-components/katex/katex.scss b/src/components/markdown-renderer/replace-components/katex/katex.scss new file mode 100644 index 000000000..656e33837 --- /dev/null +++ b/src/components/markdown-renderer/replace-components/katex/katex.scss @@ -0,0 +1 @@ +@import '../../../../../node_modules/katex/dist/katex.min'; diff --git a/src/components/markdown-renderer/replace-components/markmap/markmap-frame.tsx b/src/components/markdown-renderer/replace-components/markmap/markmap-frame.tsx index 219482d31..9b6749eb2 100644 --- a/src/components/markdown-renderer/replace-components/markmap/markmap-frame.tsx +++ b/src/components/markdown-renderer/replace-components/markmap/markmap-frame.tsx @@ -1,5 +1,3 @@ -import { transform } from 'markmap-lib/dist/transform' -import { Markmap } from 'markmap-lib/dist/view' import React, { useEffect, useRef } from 'react' export interface MarkmapFrameProps { @@ -13,12 +11,16 @@ export const MarkmapFrame: React.FC = ({ code }) => { if (!diagramContainer.current) { return } - const svg: SVGSVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg') - svg.setAttribute('width', '100%') - diagramContainer.current.querySelectorAll('svg').forEach(child => child.remove()) - diagramContainer.current.appendChild(svg) - const data = transform(code) - Markmap.create(svg, {}, data) + const actualContainer = diagramContainer.current + Promise.all([import(/* webpackChunkName: "markmap" */ 'markmap-lib/dist/transform'), import(/* webpackChunkName: "markmap" */ 'markmap-lib/dist/view')]) + .then(([transform, view]) => { + const svg: SVGSVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg') + svg.setAttribute('width', '100%') + actualContainer.querySelectorAll('svg').forEach(child => child.remove()) + actualContainer.appendChild(svg) + const data = transform.transform(code) + view.Markmap.create(svg, {}, data) + }).catch(() => { console.error('error while loading markmap') }) }, [code]) return
diff --git a/src/components/markdown-renderer/replace-components/vega-lite/vega-chart.tsx b/src/components/markdown-renderer/replace-components/vega-lite/vega-chart.tsx index b7cadbb68..d2003a49b 100644 --- a/src/components/markdown-renderer/replace-components/vega-lite/vega-chart.tsx +++ b/src/components/markdown-renderer/replace-components/vega-lite/vega-chart.tsx @@ -1,7 +1,7 @@ import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react' import { Alert } from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import embed, { VisualizationSpec } from 'vega-embed' +import { VisualizationSpec } from 'vega-embed' import { ShowIf } from '../../../common/show-if/show-if' export interface VegaChartProps { @@ -25,27 +25,31 @@ export const VegaChart: React.FC = ({ code }) => { if (!diagramContainer.current) { return } - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const spec: VisualizationSpec = JSON.parse(code) - showError('') - embed(diagramContainer.current, spec, { - actions: { - export: true, - source: false, - compiled: false, - editor: false - }, - i18n: { - PNG_ACTION: t('renderer.vega-lite.png'), - SVG_ACTION: t('renderer.vega-lite.svg') + import(/* webpackChunkName: "vega" */ 'vega-embed').then((embed) => { + try { + if (!diagramContainer.current) { + return } - }) - .then(result => console.log(result)) - .catch(err => showError(err)) - } catch (err) { - showError(t('renderer.vega-lite.errorJson')) - } + + const spec = JSON.parse(code) as VisualizationSpec + embed.default(diagramContainer.current, spec, { + actions: { + export: true, + source: false, + compiled: false, + editor: false + }, + i18n: { + PNG_ACTION: t('renderer.vega-lite.png'), + SVG_ACTION: t('renderer.vega-lite.svg') + } + }) + .then(() => showError('')) + .catch(err => showError(err)) + } catch (err) { + showError(t('renderer.vega-lite.errorJson')) + } + }).catch(() => { console.error('error while loading vega-light') }) }, [code, showError, t]) return diff --git a/src/components/markdown-renderer/utils/html-react-transformer.tsx b/src/components/markdown-renderer/utils/html-react-transformer.tsx index 50da9edd0..959a92313 100644 --- a/src/components/markdown-renderer/utils/html-react-transformer.tsx +++ b/src/components/markdown-renderer/utils/html-react-transformer.tsx @@ -1,5 +1,5 @@ import { DomElement } from 'domhandler' -import React, { Fragment, ReactElement } from 'react' +import React, { ReactElement, Suspense } from 'react' import { convertNodeToElement, Transform } from 'react-html-parser' import { ComponentReplacer, @@ -69,7 +69,9 @@ export const buildTransformer = (lineKeys: (LineKeys[] | undefined), allReplacer } else if (tryReplacement === undefined) { return nativeRenderer(node, key) } else { - return {tryReplacement} + return Loading...}> + { tryReplacement } + } } return transform diff --git a/src/components/profile-page/profile-page.tsx b/src/components/profile-page/profile-page.tsx index 432a99e24..50c57c259 100644 --- a/src/components/profile-page/profile-page.tsx +++ b/src/components/profile-page/profile-page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { Fragment } from 'react' import { Col, Row } from 'react-bootstrap' import { useSelector } from 'react-redux' import { Redirect } from 'react-router' @@ -18,7 +18,7 @@ export const ProfilePage: React.FC = () => { ) } - return ( + return
@@ -30,5 +30,5 @@ export const ProfilePage: React.FC = () => {
- ) +
} diff --git a/src/components/register-page/register-page.tsx b/src/components/register-page/register-page.tsx index 64845a760..a039daeeb 100644 --- a/src/components/register-page/register-page.tsx +++ b/src/components/register-page/register-page.tsx @@ -1,4 +1,4 @@ -import React, { FormEvent, useCallback, useEffect, useState } from 'react' +import React, { FormEvent, Fragment, useCallback, useEffect, useState } from 'react' import { useSelector } from 'react-redux' import { Redirect } from 'react-router' import { doInternalRegister } from '../../api/auth' @@ -53,7 +53,7 @@ export const RegisterPage: React.FC = () => { ) } - return ( + return

@@ -138,5 +138,5 @@ export const RegisterPage: React.FC = () => {
- ) +
} diff --git a/src/index.tsx b/src/index.tsx index 740dc795a..63415e72b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -5,7 +5,6 @@ import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-d import { ApplicationLoader } from './components/application-loader/application-loader' import { NotFoundErrorScreen } from './components/common/routing/not-found-error-screen' import { Redirector } from './components/common/routing/redirector' -import { Editor } from './components/editor/editor' import { ErrorBoundary } from './components/error-boundary/error-boundary' import { HistoryPage } from './components/history-page/history-page' import { IntroPage } from './components/intro-page/intro-page' @@ -15,8 +14,10 @@ import { ProfilePage } from './components/profile-page/profile-page' import { RegisterPage } from './components/register-page/register-page' import { store } from './redux' import * as serviceWorker from './service-worker' -import './style/index.scss' import './style/dark.scss' +import './style/index.scss' + +const Editor = React.lazy(() => import(/* webpackPrefetch: true */ './components/editor/editor')) ReactDOM.render( diff --git a/src/redux/banner/reducers.ts b/src/redux/banner/reducers.ts index fb2dce761..eaf46024e 100644 --- a/src/redux/banner/reducers.ts +++ b/src/redux/banner/reducers.ts @@ -1,10 +1,5 @@ import { Reducer } from 'redux' -import { - BannerActions, - BannerActionType, - BannerState, - SetBannerAction -} from './types' +import { BannerActions, BannerActionType, BannerState, SetBannerAction } from './types' export const initialState: BannerState = { show: true, diff --git a/src/redux/config/methods.ts b/src/redux/config/methods.ts index 2d527cc5d..0f3d1b82c 100644 --- a/src/redux/config/methods.ts +++ b/src/redux/config/methods.ts @@ -1,5 +1,5 @@ -import { Config } from '../../api/config/types' import { store } from '..' +import { Config } from '../../api/config/types' import { ConfigActionType, SetConfigAction } from './types' export const setConfig = (state: Config): void => { diff --git a/src/style/index.scss b/src/style/index.scss index 54ba650f3..f8a33c0ba 100644 --- a/src/style/index.scss +++ b/src/style/index.scss @@ -3,6 +3,7 @@ @import '../../node_modules/react-bootstrap-typeahead/css/Typeahead'; @import "fonts/source-code-pro/source-code-pro"; @import "fonts/twemoji/twemoji"; +@import '../../node_modules/fork-awesome/css/fork-awesome.min'; .text-black, body.dark .text-black { color: $black; @@ -56,3 +57,19 @@ body { .cursor-zoom-out { cursor: zoom-out; } + +.faded-fa { + .fa, &::after { + opacity: 0.5; + } + + &:hover .fa, &:hover::after { + opacity: 1; + } +} + +.dropup .dropdown-toggle, .dropdown-toggle { + &.no-arrow::after { + content: initial; + } +}