feat(frontend): replace forkawesome with bootstrap icons

These icon replace fork awesome. A linter informs the user about the deprecation.

See https://github.com/hedgedoc/hedgedoc/issues/2929

Co-authored-by: Philip Molares <philip.molares@udo.edu>
Co-authored-by: Tilman Vatteroth <git@tilmanvatteroth.de>
Signed-off-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Philip Molares 2023-02-05 22:05:02 +01:00 committed by Tilman Vatteroth
parent e7246f1484
commit 1c16e25e14
179 changed files with 4974 additions and 1943 deletions

View file

@ -1,84 +0,0 @@
SIL OPEN FONT LICENSE
Version 1.1 - 26 February 2007
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development
of collaborative font projects, to support the font creation efforts of academic
and linguistic communities, and to provide a free and open framework in which
fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed
freely as long as they are not sold by themselves. The fonts, including any
derivative works, can be bundled, embedded, redistributed and/or sold with
any software provided that any reserved names are not used by derivative works.
The fonts and derivatives, however, cannot be released under any other type
of license. The requirement for fonts to remain under this license does not
apply to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s)
under this license and clearly marked as such. This may include source files,
build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright
statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or
substituting — in part or in whole — any of the components of the Original
Version, by changing formats or by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer or
other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy
of the Font Software, to use, study, copy, merge, embed, modify, redistribute,
and sell modified and unmodified copies of the Font Software, subject to the
following conditions:
1) Neither the Font Software nor any of its individual components, in Original
or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, redistributed
and/or sold with any software, provided that each copy contains the above
copyright notice and this license. These can be included either as stand-alone
text files, human-readable headers or in the appropriate machine-readable
metadata fields within text or binary files as long as those fields can be
easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font Name(s)
unless explicit written permission is granted by the corresponding Copyright
Holder. This restriction only applies to the primary font name as presented
to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software
shall not be used to promote, endorse or advertise any Modified Version, except
to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s)
or with their explicit written permission.
5) The Font Software, modified or unmodified, in part or in whole, must be
distributed entirely under this license, and must not be distributed under
any other license. The requirement for fonts to remain under this license
does not apply to any document created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL,
INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

View file

@ -88,7 +88,7 @@ If some text is selected it will either put that as the link, if it thinks that
inserting `name=$YourName`, a time by inserting `time=$time` or a color by inserting `color=#FFFFFF` in the `[]`. inserting `name=$YourName`, a time by inserting `time=$time` or a color by inserting `color=#FFFFFF` in the `[]`.
Please note that you can only specify one of those per `[]`, but you can use multiple `[]`. Please note that you can only specify one of those per `[]`, but you can use multiple `[]`.
5. **Emoji Picker** 5. **Emoji Picker**
This button opens the emoji picker, where you can choose an emoji or a fork awesome icon to insert into your note. This button opens the emoji picker, where you can choose an emoji to insert into your note.
![Emoji Picker](../images/interface/toolbar/emoji.png) ![Emoji Picker](../images/interface/toolbar/emoji.png)
## Settings ## Settings

View file

@ -1,13 +1,12 @@
# HedgeDoc Flavored Markdown # HedgeDoc Flavored Markdown
HedgeDoc has its own markdown dialect which supports many features from [CommonMark][commonmark] and [Github Flavored Markdown][gfm]. It also adds some new extensions and is missing some. HedgeDoc has its own markdown dialect which supports many features from [CommonMark][commonmark]
and [Github Flavored Markdown][gfm]. It also adds some new extensions and is missing some.
These tables tell you what exactly we support in HedgeDoc 1.x (HFM 1) and HedgeDoc 2 (HFM 2). These tables tell you what exactly we support in HedgeDoc 1.x (HFM 1) and HedgeDoc 2 (HFM 2).
## Typography ## Typography
| Feature | HFM 1 | HFM 2 | CommonMark | GFM | | Feature | HFM 1 | HFM 2 | CommonMark | GFM |
| ------------- | :---: | :---: | :---------------: | :---------------: | | ------------- | :---: | :---: | :---------------: | :---------------: |
| bold | ☑️ | ☑️ | ☑️ | ☑️ | | bold | ☑️ | ☑️ | ☑️ | ☑️ |
@ -36,7 +35,8 @@ These tables tell you what exactly we support in HedgeDoc 1.x (HFM 1) and HedgeD
| task list | ☑️ | ☑️ | | ☑️ | | task list | ☑️ | ☑️ | | ☑️ |
| defition list | ☑️ | ☑️ | | | | defition list | ☑️ | ☑️ | | |
| emoji | [Unicode 6.1][unicode-6] | [Unicode 13][unicode-13] | | | | emoji | [Unicode 6.1][unicode-6] | [Unicode 13][unicode-13] | | |
| [ForkAwesome][fa] | ☑️ with `<i class='fa'>` | ☑️ with shortcodes | | | | [ForkAwesome][fa] | ☑️ with `<i class='fa'>` | removed | | |
| [Bootstrap Icons][bootstrap-icons] | | ☑️ with shortcodes | | |
| LaTeX | ☑️[^mj] | ☑️[^kt] | | | | LaTeX | ☑️[^mj] | ☑️[^kt] | | |
[^highlight]: Code blocks with a given language are rendered using syntax highlighting. [^highlight]: Code blocks with a given language are rendered using syntax highlighting.
@ -59,7 +59,6 @@ These tables tell you what exactly we support in HedgeDoc 1.x (HFM 1) and HedgeD
| image with given size | ☑️ | ☑️ | (☑️ with `<img>`) | (☑️ with `<img>`) | | image with given size | ☑️ | ☑️ | (☑️ with `<img>`) | (☑️ with `<img>`) |
| table of contents | ☑️ | ☑️ | | | | table of contents | ☑️ | ☑️ | | |
## Structural elements ## Structural elements
| Feature | HFM 1 | HFM 2 | CommonMark | GFM | | Feature | HFM 1 | HFM 2 | CommonMark | GFM |
@ -70,7 +69,9 @@ These tables tell you what exactly we support in HedgeDoc 1.x (HFM 1) and HedgeD
| Alerts | ☑️ | ☑️ | | | | Alerts | ☑️ | ☑️ | | |
## Embeddings ## Embeddings
HFM 1 includes support for certain embeddings of external content by using the `{%keyword parameter %}` syntax. To increase the readability of the markdown code we decided that HFM 2 should just use plain links if possible.
HFM 1 includes support for certain embeddings of external content by using the `{%keyword parameter %}` syntax. To
increase the readability of the markdown code we decided that HFM 2 should just use plain links if possible.
| Feature | HFM 1 | HFM 2 | CommonMark | GFM | | Feature | HFM 1 | HFM 2 | CommonMark | GFM |
| --------------------------------------------------- | :---: | :---------------------: | :--------: | :---: | | --------------------------------------------------- | :---: | :---------------------: | :--------: | :---: |
@ -81,10 +82,13 @@ HFM 1 includes support for certain embeddings of external content by using the `
| [Speakerdeck][speakerdeck] (`{%speakerdeck ... %}`) | ☑️ | removed | | | | [Speakerdeck][speakerdeck] (`{%speakerdeck ... %}`) | ☑️ | removed | | |
| [GitHub Gist][gist] (`{%gist ... %}`) | ☑️ | with plain link[^embed] | | | | [GitHub Gist][gist] (`{%gist ... %}`) | ☑️ | with plain link[^embed] | | |
[^embed]: The special syntax from HFM 1 is deprecated, but will still work in HFM 2. However, a plain link to the content should be used. [^embed]: The special syntax from HFM 1 is deprecated, but will still work in HFM 2. However, a plain link to the
content should be used.
## HTML ## HTML
Besides the basic HTML typography elements (`<p>`, `<a>`, `<b>`, `<ins>`, `<del>`) the following more special HTML elements are supported by some specification.
Besides the basic HTML typography elements (`<p>`, `<a>`, `<b>`, `<ins>`, `<del>`) the following more special HTML
elements are supported by some specification.
| Feature | HedgeDocMark 1 | HedgeDocMark 2 | CommonMark | GFM | | Feature | HedgeDocMark 1 | HedgeDocMark 2 | CommonMark | GFM |
| :-----------: | :------------: | :------------: | :--------: | :---: | | :-----------: | :------------: | :------------: | :--------: | :---: |
@ -99,14 +103,27 @@ Besides the basic HTML typography elements (`<p>`, `<a>`, `<b>`, `<ins>`, `<del>
| `<plaintext>` | | ☑️ | ☑️ | | | `<plaintext>` | | ☑️ | ☑️ | |
[fa]: https://forkaweso.me/ [fa]: https://forkaweso.me/
[bootstrap-icons]: https://icons.getbootstrap.com/
[youtube]: https://www.youtube.com/ [youtube]: https://www.youtube.com/
[vimeo]: https://vimeo.com/ [vimeo]: https://vimeo.com/
[slideshare]: https://www.slideshare.net/ [slideshare]: https://www.slideshare.net/
[speakerdeck]: https://speakerdeck.com/ [speakerdeck]: https://speakerdeck.com/
[gist]: https://gist.github.com/ [gist]: https://gist.github.com/
[mathjax]: https://www.mathjax.org/ [mathjax]: https://www.mathjax.org/
[katex]: https://katex.org/ [katex]: https://katex.org/
[gfm]: https://github.github.com/gfm/ [gfm]: https://github.github.com/gfm/
[commonmark]: https://spec.commonmark.org/ [commonmark]: https://spec.commonmark.org/
[unicode-6]: https://unicode.org/versions/Unicode6.1.0/ [unicode-6]: https://unicode.org/versions/Unicode6.1.0/
[unicode-13]: https://unicode.org/versions/Unicode13.0.0/ [unicode-13]: https://unicode.org/versions/Unicode13.0.0/

View file

@ -19,7 +19,6 @@ SPDX-License-Identifier: CC-BY-SA-4.0
- `{%pdf https://example.org/example-pdf.pdf %}` -> Embedding removed - `{%pdf https://example.org/example-pdf.pdf %}` -> Embedding removed
- The use of `sequence` as code block language ([Why?](https://github.com/hedgedoc/react-client/issues/488#issuecomment-683262875)) - The use of `sequence` as code block language ([Why?](https://github.com/hedgedoc/react-client/issues/488#issuecomment-683262875))
- Comma-separated definition of tags in the yaml-frontmatter - Comma-separated definition of tags in the yaml-frontmatter
- Fork Awesome Icons will be removed in a future release
### Removed ### Removed
@ -43,6 +42,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
- F9 shortcut to sort lines - F9 shortcut to sort lines
- Highlight.JS language support for `1c` was removed. - Highlight.JS language support for `1c` was removed.
- Support for tag definitions in headings - Support for tag definitions in headings
- Fork Awesome has been replaced with [Bootstrap Icons](https://icons.getbootstrap.com/)
### Added ### Added
@ -54,9 +54,9 @@ SPDX-License-Identifier: CC-BY-SA-4.0
- HedgeDoc instances can be branded either with a '@ \<custom string\>' or '@ \<custom logo\>' after the HedgeDoc logo and text - HedgeDoc instances can be branded either with a '@ \<custom string\>' or '@ \<custom logo\>' after the HedgeDoc logo and text
- Images will be loaded via proxy if an image proxy is configured in the backend - Images will be loaded via proxy if an image proxy is configured in the backend
- Asciinema videos may be embedded by pasting the URL of one video into a single line - Asciinema videos may be embedded by pasting the URL of one video into a single line
- The toolbar includes an emoji and fork-awesome icon picker. - The toolbar includes an emoji picker.
- Collapsible blocks can be added via a toolbar button or via autocompletion of "<details" - Collapsible blocks can be added via a toolbar button or via autocompletion of "<details"
- Added shortcodes for [fork-awesome icons](https://forkaweso.me/Fork-Awesome/icons/) (e.g. `:fa-picture-o:`) - Added shortcodes for icons (e.g. `:bi-picture:`)
- The code button now adds code fences even if the user selected nothing beforehand - The code button now adds code fences even if the user selected nothing beforehand
- Code blocks with 'csv' as language render as tables. - Code blocks with 'csv' as language render as tables.
- All images can be clicked to show them in full screen. - All images can be clicked to show them in full screen.

View file

@ -10,7 +10,6 @@
@import '~react-bootstrap-typeahead/css/Typeahead'; @import '~react-bootstrap-typeahead/css/Typeahead';
@import "~@fontsource/source-sans-pro/index.css"; @import "~@fontsource/source-sans-pro/index.css";
@import "~twemoji-colr-font/twemoji"; @import "~twemoji-colr-font/twemoji";
@import '~fork-awesome/css/fork-awesome.min.css';
@import '~firacode/distr/fira_code'; @import '~firacode/distr/fira_code';
@import "typeahead"; @import "typeahead";
@import "./button-inside"; @import "./button-inside";
@ -79,16 +78,6 @@ 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 { .dropup .dropdown-toggle, .dropdown-toggle {
&.no-arrow::after { &.no-arrow::after {
content: initial; content: initial;

View file

@ -23,6 +23,16 @@ const customJestConfig = {
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
testPathIgnorePatterns: ['/node_modules/', '/cypress/'] testPathIgnorePatterns: ['/node_modules/', '/cypress/']
} }
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig) module.exports = async () => {
const nextJestConfig = await createJestConfig(customJestConfig)()
return {
...nextJestConfig,
moduleNameMapper: {
...nextJestConfig.moduleNameMapper,
'^.+\\.(svg)$': '<rootDir>/src/test-utils/svg-mock.tsx',
'^react-bootstrap-icons$': '<rootDir>/src/test-utils/bootstrap-icon-mocks.tsx',
'^react-bootstrap-icons/dist/icons/.*$': '<rootDir>/src/test-utils/svg-mock.tsx'
}
}
}

View file

@ -223,7 +223,7 @@
"shortcode": "The {{shortcode}} short-code is deprecated and will be removed in a future release. Use a single line URL instead.", "shortcode": "The {{shortcode}} short-code is deprecated and will be removed in a future release. Use a single line URL instead.",
"frontmatter": "The yaml-metadata is invalid.", "frontmatter": "The yaml-metadata is invalid.",
"frontmatter-tags": "The comma-separated definition of tags in the yaml-metadata is deprecated. Use a yaml-array instead.", "frontmatter-tags": "The comma-separated definition of tags in the yaml-metadata is deprecated. Use a yaml-array instead.",
"fork-awesome": "Fork Awesome is deprecated and will be removed in a future release. Please use bootstrap icons instead. See {{link}} for more information." "fork-awesome": "The usage of Fork Awesome is deprecated and has been removed. Please use bootstrap icons instead. See {{link}} for more information."
}, },
"upload": { "upload": {
"uploadFile": { "uploadFile": {

View file

@ -31,29 +31,33 @@ if (isMockMode) {
`) `)
} }
/** @type {import('@svgr/webpack').LoaderOptions} */
const svgrConfig = {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
}
}
}
]
}
}
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const rawNextConfig = { const rawNextConfig = {
webpack: (config) => { webpack: (config) => {
config.module.rules.push({ config.module.rules.push({
test: /\.svg$/i, test: /\.svg$/i,
issuer: /\.[jt]sx?$/, issuer: /\.[jt]sx?$/,
use: [ use: [
{ {
loader: '@svgr/webpack', loader: '@svgr/webpack',
options: { options: svgrConfig
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
}
}
}
]
}
}
} }
] ]
}) })

View file

@ -74,7 +74,6 @@
"fast-deep-equal": "3.1.3", "fast-deep-equal": "3.1.3",
"firacode": "6.2.0", "firacode": "6.2.0",
"flowchart.js": "1.17.1", "flowchart.js": "1.17.1",
"fork-awesome": "1.2.0",
"highlight.js": "11.7.0", "highlight.js": "11.7.0",
"htmlparser2": "8.0.1", "htmlparser2": "8.0.1",
"i18next": "22.4.10", "i18next": "22.4.10",
@ -102,6 +101,7 @@
"next": "13.1.6", "next": "13.1.6",
"react": "18.2.0", "react": "18.2.0",
"react-bootstrap": "2.7.2", "react-bootstrap": "2.7.2",
"react-bootstrap-icons": "1.10.2",
"react-bootstrap-typeahead": "6.0.0", "react-bootstrap-typeahead": "6.0.0",
"react-diff-viewer": "3.1.1", "react-diff-viewer": "3.1.1",
"react-dom": "18.2.0", "react-dom": "18.2.0",

View file

@ -43,8 +43,8 @@
z-index: 1000; z-index: 1000;
position: relative; position: relative;
font-size: 3em; font-size: 3em;
height: 240px; height: 5em;
width: 203px; width: 5em;
color: #ffffff; color: #ffffff;
text-shadow: 4px 4px 0 #3b4045; text-shadow: 4px 4px 0 #3b4045;
@ -55,10 +55,11 @@
animation: fill 6s infinite; animation: fill 6s infinite;
width: 100%; width: 100%;
&, :global(.fa) { & > * {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
z-index: 1001;
} }
} }
} }

View file

@ -3,11 +3,13 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../common/icons/ui-icon'
import { createNumberRangeArray } from '../../common/number-range/number-range' import { createNumberRangeArray } from '../../common/number-range/number-range'
import styles from './animations.module.scss' import styles from './animations.module.scss'
import { IconRow } from './icon-row' import { IconRow } from './icon-row'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Pencil as IconPencil } from 'react-bootstrap-icons'
import { PencilFill as IconPencilFill } from 'react-bootstrap-icons'
export interface HedgeDocLogoProps { export interface HedgeDocLogoProps {
error: boolean error: boolean
@ -25,10 +27,10 @@ export const LoadingAnimation: React.FC<HedgeDocLogoProps> = ({ error }) => {
<div className={`position-relative ${error ? styles.error : ''}`}> <div className={`position-relative ${error ? styles.error : ''}`}>
<div className={styles.logo}> <div className={styles.logo}>
<div> <div>
<ForkAwesomeIcon icon={'pencil'} className={styles.background} size={'5x'}></ForkAwesomeIcon> <UiIcon icon={IconPencilFill} className={styles.background} size={5} />
</div> </div>
<div className={`${styles.overlay}`}> <div className={`${styles.overlay}`}>
<ForkAwesomeIcon icon={'pencil'} size={'5x'}></ForkAwesomeIcon> <UiIcon icon={IconPencil} size={5} />
</div> </div>
</div> </div>
<div className={styles.pulse}></div> <div className={styles.pulse}></div>

View file

@ -3,28 +3,39 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
import type { IconName } from '../../common/fork-awesome/types'
import styles from './animations.module.scss' import styles from './animations.module.scss'
import React, { useEffect, useState } from 'react' import React, { Fragment, useEffect, useState } from 'react'
import type { Icon } from 'react-bootstrap-icons'
import { FileText as IconFileText } from 'react-bootstrap-icons'
import { File as IconFile } from 'react-bootstrap-icons'
import { Fonts as IconFonts } from 'react-bootstrap-icons'
import { Gear as IconGear } from 'react-bootstrap-icons'
import { KeyboardFill as IconKeyboardFill } from 'react-bootstrap-icons'
import { ListCheck as IconListCheck } from 'react-bootstrap-icons'
import { Markdown as IconMarkdown } from 'react-bootstrap-icons'
import { Pencil as IconPencil } from 'react-bootstrap-icons'
import { Person as IconPerson } from 'react-bootstrap-icons'
import { Tag as IconTag } from 'react-bootstrap-icons'
import { TypeBold as IconTypeBold } from 'react-bootstrap-icons'
import { TypeItalic as IconTypeItalic } from 'react-bootstrap-icons'
const elements: IconName[] = [ const elements: Icon[] = [
'file-text', IconFileText,
'markdown', IconMarkdown,
'pencil', IconPencil,
'bold', IconTypeBold,
'italic', IconTypeItalic,
'align-justify', IconListCheck,
'tag', IconTag,
'user', IconPerson,
'file', IconFile,
'keyboard-o', IconKeyboardFill,
'cog', IconGear,
'font' IconFonts
] ]
/** /**
* Chooses a random fork awesome icon from a predefined set and renders it. * Chooses a random icon from a predefined set and renders it.
* *
* The component uses a static icon in the first rendering and will choose the random icon after that. * The component uses a static icon in the first rendering and will choose the random icon after that.
* This is done because if the loading screen is prepared using SSR and then hydrated in the client, the rendered css class isn't the expected one from the SSR. (It's random. d'uh). * This is done because if the loading screen is prepared using SSR and then hydrated in the client, the rendered css class isn't the expected one from the SSR. (It's random. d'uh).
@ -33,13 +44,12 @@ const elements: IconName[] = [
* See https://nextjs.org/docs/messages/react-hydration-error * See https://nextjs.org/docs/messages/react-hydration-error
*/ */
export const RandomIcon: React.FC = () => { export const RandomIcon: React.FC = () => {
const [icon, setIcon] = useState<number | undefined>() const [icon, setIcon] = useState<JSX.Element | undefined>()
useEffect(() => { useEffect(() => {
setIcon(Math.floor(Math.random() * elements.length)) const index = Math.floor(Math.random() * elements.length)
setIcon(React.createElement(elements[index], { className: styles.particle }))
}, []) }, [])
return icon === undefined ? null : ( return <Fragment>{icon}</Fragment>
<ForkAwesomeIcon icon={elements[icon]} className={styles.particle}></ForkAwesomeIcon>
)
} }

View file

@ -5,9 +5,7 @@ exports[`Async loading boundary shows a waiting spinner if loading 1`] = `
<div <div
class="m-3 d-flex align-items-center justify-content-center" class="m-3 d-flex align-items-center justify-content-center"
> >
<i BootstrapIconMock_ArrowRepeat
class="fa fa-spinner fa-spin "
/>
</div> </div>
</div> </div>
`; `;

View file

@ -7,9 +7,7 @@ exports[`Copy to clipboard button show an error text if clipboard api isn't avai
title="renderer.highlightCode.copyCode" title="renderer.highlightCode.copyCode"
type="button" type="button"
> >
<i BootstrapIconMock_Files
class="fa fa-files-o "
/>
</button> </button>
</div> </div>
`; `;
@ -22,9 +20,7 @@ exports[`Copy to clipboard button show an error text if clipboard api isn't avai
title="renderer.highlightCode.copyCode" title="renderer.highlightCode.copyCode"
type="button" type="button"
> >
<i BootstrapIconMock_Files
class="fa fa-files-o "
/>
</button> </button>
</div> </div>
`; `;
@ -36,9 +32,7 @@ exports[`Copy to clipboard button shows an error text if writing failed 1`] = `
title="renderer.highlightCode.copyCode" title="renderer.highlightCode.copyCode"
type="button" type="button"
> >
<i BootstrapIconMock_Files
class="fa fa-files-o "
/>
</button> </button>
</div> </div>
`; `;
@ -51,9 +45,7 @@ exports[`Copy to clipboard button shows an error text if writing failed 2`] = `
title="renderer.highlightCode.copyCode" title="renderer.highlightCode.copyCode"
type="button" type="button"
> >
<i BootstrapIconMock_Files
class="fa fa-files-o "
/>
</button> </button>
</div> </div>
`; `;
@ -65,9 +57,7 @@ exports[`Copy to clipboard button shows an success text if writing succeeded 1`]
title="renderer.highlightCode.copyCode" title="renderer.highlightCode.copyCode"
type="button" type="button"
> >
<i BootstrapIconMock_Files
class="fa fa-files-o "
/>
</button> </button>
</div> </div>
`; `;
@ -80,9 +70,7 @@ exports[`Copy to clipboard button shows an success text if writing succeeded 2`]
title="renderer.highlightCode.copyCode" title="renderer.highlightCode.copyCode"
type="button" type="button"
> >
<i BootstrapIconMock_Files
class="fa fa-files-o "
/>
</button> </button>
</div> </div>
`; `;

View file

@ -5,10 +5,11 @@
*/ */
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 { ForkAwesomeIcon } from '../../fork-awesome/fork-awesome-icon' import { UiIcon } from '../../icons/ui-icon'
import { useCopyOverlay } from '../hooks/use-copy-overlay' import { useCopyOverlay } from '../hooks/use-copy-overlay'
import React, { Fragment, useRef } from 'react' import React, { Fragment, useRef } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { Files as IconFiles } from 'react-bootstrap-icons'
import type { Variant } from 'react-bootstrap/types' import type { Variant } from 'react-bootstrap/types'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -46,7 +47,7 @@ export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({
title={t('renderer.highlightCode.copyCode') ?? undefined} title={t('renderer.highlightCode.copyCode') ?? undefined}
onClick={copyToClipboard} onClick={copyToClipboard}
{...cypressId(props)}> {...cypressId(props)}>
<ForkAwesomeIcon icon='files-o' /> <UiIcon icon={IconFiles} />
</Button> </Button>
{overlayElement} {overlayElement}
</Fragment> </Fragment>

View file

@ -5,11 +5,12 @@
*/ */
import { isClientSideRendering } from '../../../../utils/is-client-side-rendering' import { isClientSideRendering } from '../../../../utils/is-client-side-rendering'
import { Logger } from '../../../../utils/logger' import { Logger } from '../../../../utils/logger'
import { ForkAwesomeIcon } from '../../fork-awesome/fork-awesome-icon' import { UiIcon } from '../../icons/ui-icon'
import { ShowIf } from '../../show-if/show-if' import { ShowIf } from '../../show-if/show-if'
import { CopyToClipboardButton } from '../copy-to-clipboard-button/copy-to-clipboard-button' import { CopyToClipboardButton } from '../copy-to-clipboard-button/copy-to-clipboard-button'
import React, { useCallback, useMemo } from 'react' import React, { useCallback, useMemo } from 'react'
import { Button, FormControl, InputGroup } from 'react-bootstrap' import { Button, FormControl, InputGroup } from 'react-bootstrap'
import { Share as IconShare } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export interface CopyableFieldProps { export interface CopyableFieldProps {
@ -57,7 +58,7 @@ export const CopyableField: React.FC<CopyableFieldProps> = ({ content, shareOrig
<ShowIf condition={sharingSupported}> <ShowIf condition={sharingSupported}>
<InputGroup.Text> <InputGroup.Text>
<Button variant='secondary' title={'Share'} onClick={doShareAction}> <Button variant='secondary' title={'Share'} onClick={doShareAction}>
<ForkAwesomeIcon icon='share-alt' /> <UiIcon icon={IconShare} />
</Button> </Button>
</InputGroup.Text> </InputGroup.Text>
</ShowIf> </ShowIf>

View file

@ -1,65 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[` 1`] = `
<div>
<i
class="fa fa-heart fa-stack-1x"
/>
</div>
`;
exports[`ForkAwesomeIcon renders a heart correctly 1`] = `
<div>
<i
class="fa fa-heart "
/>
</div>
`;
exports[`ForkAwesomeIcon renders in size 2x 1`] = `
<div>
<i
class="fa fa-heart fa-2x"
/>
</div>
`;
exports[`ForkAwesomeIcon renders in size 3x 1`] = `
<div>
<i
class="fa fa-heart fa-3x"
/>
</div>
`;
exports[`ForkAwesomeIcon renders in size 4x 1`] = `
<div>
<i
class="fa fa-heart fa-4x"
/>
</div>
`;
exports[`ForkAwesomeIcon renders in size 5x 1`] = `
<div>
<i
class="fa fa-heart fa-5x"
/>
</div>
`;
exports[`ForkAwesomeIcon renders with additional className 1`] = `
<div>
<i
class="fa fa-heart testClass "
/>
</div>
`;
exports[`ForkAwesomeIcon renders with fixed width icon 1`] = `
<div>
<i
class="fa fa-fw fa-heart "
/>
</div>
`;

View file

@ -1,76 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ForkAwesomeStack renders a heart and a download icon stack 1`] = `
<div>
<span
class="fa-stack "
>
<i
class="fa fa-heart fa-stack-1x"
/>
<i
class="fa fa-download fa-stack-1x"
/>
</span>
</div>
`;
exports[`ForkAwesomeStack renders in size 2x 1`] = `
<div>
<span
class="fa-stack fa-2x"
>
<i
class="fa fa-heart fa-stack-1x"
/>
<i
class="fa fa-download fa-stack-1x"
/>
</span>
</div>
`;
exports[`ForkAwesomeStack renders in size 3x 1`] = `
<div>
<span
class="fa-stack fa-3x"
>
<i
class="fa fa-heart fa-stack-1x"
/>
<i
class="fa fa-download fa-stack-1x"
/>
</span>
</div>
`;
exports[`ForkAwesomeStack renders in size 4x 1`] = `
<div>
<span
class="fa-stack fa-4x"
>
<i
class="fa fa-heart fa-stack-1x"
/>
<i
class="fa fa-download fa-stack-1x"
/>
</span>
</div>
`;
exports[`ForkAwesomeStack renders in size 5x 1`] = `
<div>
<span
class="fa-stack fa-5x"
>
<i
class="fa fa-heart fa-stack-1x"
/>
<i
class="fa fa-download fa-stack-1x"
/>
</span>
</div>
`;

View file

@ -1,46 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ForkAwesomeIcon } from './fork-awesome-icon'
import type { IconName } from './types'
import { render } from '@testing-library/react'
describe('ForkAwesomeIcon', () => {
const icon: IconName = 'heart'
it('renders a heart correctly', () => {
const view = render(<ForkAwesomeIcon icon={icon} />)
expect(view.container).toMatchSnapshot()
})
it('renders with fixed width icon', () => {
const view = render(<ForkAwesomeIcon icon={icon} fixedWidth={true} />)
expect(view.container).toMatchSnapshot()
})
it('renders with additional className', () => {
const view = render(<ForkAwesomeIcon icon={icon} className={'testClass'} />)
expect(view.container).toMatchSnapshot()
})
describe('renders in size', () => {
it('2x', () => {
const view = render(<ForkAwesomeIcon icon={icon} size={'2x'} />)
expect(view.container).toMatchSnapshot()
})
it('3x', () => {
const view = render(<ForkAwesomeIcon icon={icon} size={'3x'} />)
expect(view.container).toMatchSnapshot()
})
it('4x', () => {
const view = render(<ForkAwesomeIcon icon={icon} size={'4x'} />)
expect(view.container).toMatchSnapshot()
})
it('5x', () => {
const view = render(<ForkAwesomeIcon icon={icon} size={'5x'} />)
expect(view.container).toMatchSnapshot()
})
})
describe('renders in stack', () => {
const view = render(<ForkAwesomeIcon icon={icon} stacked={true} />)
expect(view.container).toMatchSnapshot()
})
})

View file

@ -1,39 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { IconName, IconSize } from './types'
import React from 'react'
export interface ForkAwesomeIconProps {
icon: IconName
className?: string
fixedWidth?: boolean
size?: IconSize
stacked?: boolean
}
/**
* Renders a fork awesome icon.
*
* @param icon The icon that should be rendered.
* @param fixedWidth If the icon should be rendered with a fixed width.
* @param size The size class the icon should be rendered in.
* @param className Additional classes the icon should get.
* @param stacked If the icon is part of a {@link ForkAwesomeStack stack}.
* @see https://forkaweso.me
*/
export const ForkAwesomeIcon: React.FC<ForkAwesomeIconProps> = ({
icon,
fixedWidth = false,
size,
className,
stacked = false
}) => {
const fixedWithClass = fixedWidth ? 'fa-fw' : ''
const sizeClass = size ? `-${size}` : stacked ? '-1x' : ''
const stackClass = stacked ? '-stack' : ''
const extraClasses = `${className ?? ''} ${sizeClass || stackClass ? `fa${stackClass}${sizeClass}` : ''}`
return <i className={`fa ${fixedWithClass} fa-${icon} ${extraClasses}`} />
}

View file

@ -1,804 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
export const ForkAwesomeIcons = [
'500px',
'activitypub',
'address-book-o',
'address-book',
'address-card-o',
'address-card',
'adjust',
'adn',
'align-center',
'align-justify',
'align-left',
'align-right',
'amazon',
'ambulance',
'american-sign-language-interpreting',
'anchor',
'android',
'angellist',
'angle-double-down',
'angle-double-left',
'angle-double-right',
'angle-double-up',
'angle-down',
'angle-left',
'angle-right',
'angle-up',
'apple',
'archive-org',
'archive',
'archlinux',
'area-chart',
'arrow-circle-down',
'arrow-circle-left',
'arrow-circle-o-down',
'arrow-circle-o-left',
'arrow-circle-o-right',
'arrow-circle-o-up',
'arrow-circle-right',
'arrow-circle-up',
'arrow-down',
'arrow-left',
'arrow-right',
'arrows-alt',
'arrows-h',
'arrows',
'arrows-v',
'arrow-up',
'artstation',
'askfm',
'assistive-listening-systems',
'asterisk',
'at',
'att',
'audio-description',
'backward',
'balance-scale',
'bandcamp',
'ban',
'bar-chart',
'barcode',
'bars',
'bath',
'battery-empty',
'battery-full',
'battery-half',
'battery-quarter',
'battery-three-quarters',
'bed',
'beer',
'behance-square',
'behance',
'bell-o',
'bell-rigning-o',
'bell-ringing',
'bell-slash-o',
'bell-slash',
'bell',
'bicycle',
'binoculars',
'biometric',
'birthday-cake',
'bitbucket-square',
'bitbucket',
'black-tie',
'blind',
'blockstack',
'bluetooth-b',
'bluetooth',
'boardgamegeek',
'bold',
'bolt',
'bomb',
'bookmark-o',
'bookmark',
'book',
'bootstrap',
'braille',
'briefcase',
'btc',
'bug',
'building-o',
'building',
'bullhorn',
'bullseye',
'bunny',
'bus',
'buymeacoffee',
'buysellads',
'calculator',
'calendar-check-o',
'calendar-minus-o',
'calendar-o',
'calendar-plus-o',
'calendar',
'calendar-times-o',
'camera-retro',
'camera',
'caret-down',
'caret-left',
'caret-right',
'caret-square-o-down',
'caret-square-o-left',
'caret-square-o-right',
'caret-square-o-up',
'caret-up',
'car',
'cart-arrow-down',
'cart-plus',
'cc-amex',
'cc-by',
'cc-cc',
'cc-diners-club',
'cc-discover',
'cc-jcb',
'cc-mastercard',
'cc-nc-eu',
'cc-nc-jp',
'cc-nc',
'cc-nd',
'cc-paypal',
'cc-pd',
'cc-remix',
'cc-sa',
'cc-share',
'cc-stripe',
'cc',
'cc-visa',
'cc-zero',
'certificate',
'chain-broken',
'check-circle-o',
'check-circle',
'check-square-o',
'check-square',
'check',
'chevron-circle-down',
'chevron-circle-left',
'chevron-circle-right',
'chevron-circle-up',
'chevron-down',
'chevron-left',
'chevron-right',
'chevron-up',
'child',
'chrome',
'circle-o-notch',
'circle-o',
'circle',
'circle-thin',
'classicpress-circle',
'classicpress',
'clipboard',
'clock-o',
'clone',
'cloud-download',
'cloud',
'cloud-upload',
'code-fork',
'codepen',
'code',
'codiepie',
'coffee',
'cogs',
'cog',
'columns',
'commenting-o',
'commenting',
'comment-o',
'comments-o',
'comments',
'comment',
'compass',
'compress',
'connectdevelop',
'contao',
'conway-glider',
'copyright',
'creative-commons',
'credit-card-alt',
'credit-card',
'crop',
'crosshairs',
'csharp',
'css3',
'c',
'cubes',
'cube',
'cutlery',
'dashcube',
'database',
'deaf',
'debian',
'delicious',
'desktop',
'deviantart',
'dev-to',
'diamond',
'diaspora',
'digg',
'digitalocean',
'discord-alt',
'discord',
'dogmazic',
'dot-circle-o',
'download',
'dribbble',
'dropbox',
'drupal',
'edge',
'eercast',
'eject',
'ellipsis-h',
'ellipsis-v',
'email-bulk-o',
'email-bulk',
'emby',
'empire',
'envelope-open-o',
'envelope-open',
'envelope-o',
'envelope-square',
'envelope',
'envira',
'eraser',
'ethereum',
'etsy',
'eur',
'exchange',
'exclamation-circle',
'exclamation',
'exclamation-triangle',
'expand',
'expeditedssl',
'external-link-square',
'external-link',
'eyedropper',
'eye-slash',
'eye',
'facebook-messenger',
'facebook-official',
'facebook-square',
'facebook',
'fast-backward',
'fast-forward',
'fax',
'f-droid',
'female',
'ffmpeg',
'fighter-jet',
'file-archive-o',
'file-audio-o',
'file-code-o',
'file-epub',
'file-excel-o',
'file-image-o',
'file-o',
'file-pdf-o',
'file-powerpoint-o',
'files-o',
'file',
'file-text-o',
'file-text',
'file-video-o',
'file-word-o',
'film',
'filter',
'fire-extinguisher',
'firefox',
'fire',
'first-order',
'flag-checkered',
'flag-o',
'flag',
'flask',
'flickr',
'floppy-o',
'folder-open-o',
'folder-open',
'folder-o',
'folder',
'font-awesome',
'fonticons',
'font',
'fork-awesome',
'fort-awesome',
'forumbee',
'forward',
'foursquare',
'free-code-camp',
'freedombox',
'friendica',
'frown-o',
'funkwhale',
'futbol-o',
'gamepad',
'gavel',
'gbp',
'genderless',
'get-pocket',
'gg-circle',
'gg',
'gift',
'gimp',
'gitea',
'github-alt',
'github-square',
'github',
'gitlab',
'git-square',
'git',
'glass',
'glide-g',
'glide',
'globe-e',
'globe',
'globe-w',
'gnupg',
'gnu-social',
'gnu',
'google-play',
'google-plus-official',
'google-plus-square',
'google-plus',
'google',
'google-wallet',
'graduation-cap',
'gratipay',
'grav',
'hackaday',
'hacker-news',
'hackster',
'hal',
'hand-lizard-o',
'hand-o-down',
'hand-o-left',
'hand-o-right',
'hand-o-up',
'hand-paper-o',
'hand-peace-o',
'hand-pointer-o',
'hand-rock-o',
'hand-scissors-o',
'handshake-o',
'hand-spock-o',
'hashnode',
'hashtag',
'hdd-o',
'header',
'headphones',
'heartbeat',
'heart-o',
'heart',
'heroku',
'history',
'home-assistant',
'home',
'hospital-o',
'hourglass-end',
'hourglass-half',
'hourglass-o',
'hourglass-start',
'hourglass',
'houzz',
'h-square',
'html5',
'hubzilla',
'i-cursor',
'id-badge',
'id-card-o',
'id-card',
'ils',
'imdb',
'inbox',
'indent',
'industry',
'info-circle',
'info',
'inkscape',
'inr',
'instagram',
'internet-explorer',
'ioxhost',
'italic',
'java',
'jirafeau',
'joomla',
'joplin',
'jpy',
'jsfiddle',
'julia',
'jupyter',
'keybase',
'keyboard-o',
'key-modern',
'key',
'krw',
'language',
'laptop',
'laravel',
'lastfm-square',
'lastfm',
'leaf',
'leanpub',
'lemon-o',
'level-down',
'level-up',
'liberapay-square',
'liberapay',
'life-ring',
'lightbulb-o',
'line-chart',
'linkedin-square',
'linkedin',
'link',
'linode',
'linux',
'list-alt',
'list-ol',
'list',
'list-ul',
'location-arrow',
'lock',
'long-arrow-down',
'long-arrow-left',
'long-arrow-right',
'long-arrow-up',
'low-vision',
'magic',
'magnet',
'male',
'map-marker',
'map-o',
'map-pin',
'map-signs',
'map',
'mariadb',
'markdown',
'mars-double',
'mars-stroke-h',
'mars-stroke',
'mars-stroke-v',
'mars',
'mastodon-alt',
'mastodon-square',
'mastodon',
'matrix-org',
'maxcdn',
'meanpath',
'medium-square',
'medium',
'medkit',
'meetup',
'meh-o',
'mercury',
'microchip',
'microphone-slash',
'microphone',
'minus-circle',
'minus-square-o',
'minus-square',
'minus',
'mixcloud',
'mobile',
'modx',
'money',
'moon-o',
'moon',
'motorcycle',
'mouse-pointer',
'music',
'mysql',
'neuter',
'newspaper-o',
'nextcloud-square',
'nextcloud',
'nodejs',
'nordcast',
'object-group',
'object-ungroup',
'odnoklassniki-square',
'odnoklassniki',
'opencart',
'open-collective',
'openid',
'opera',
'optin-monster',
'orcid',
'outdent',
'pagelines',
'paint-brush',
'paperclip',
'paper-plane-o',
'paper-plane',
'paragraph',
'patreon',
'pause-circle-o',
'pause-circle',
'pause',
'paw',
'paypal',
'peertube',
'pencil-square-o',
'pencil-square',
'pencil',
'percent',
'phone-square',
'phone',
'php',
'picture-o',
'pie-chart',
'pinterest-p',
'pinterest-square',
'pinterest',
'pixelfed',
'plane',
'play-circle-o',
'play-circle',
'play',
'pleroma',
'plug',
'plume',
'plus-circle',
'plus-square-o',
'plus-square',
'plus',
'podcast',
'postgresql',
'power-off',
'print',
'product-hunt',
'puzzle-piece',
'python',
'qq',
'qrcode',
'question-circle-o',
'question-circle',
'question',
'quora',
'quote-left',
'quote-right',
'random',
'ravelry',
'react',
'rebel',
'recycle',
'reddit-alien',
'reddit-square',
'reddit',
'refresh',
'registered',
'renren',
'repeat',
'reply-all',
'reply',
'researchgate',
'retweet',
'road',
'rocket',
'rss-square',
'rss',
'rub',
'safari',
'sass-alt',
'sass',
'scissors',
'scribd',
'scuttlebutt',
'search-minus',
'search-plus',
'search',
'sellsy',
'server',
'shaarli-o',
'shaarli',
'share-alt-square',
'share-alt',
'share-square-o',
'share-square',
'share',
'shield',
'ship',
'shirtsinbulk',
'shopping-bag',
'shopping-basket',
'shopping-cart',
'shower',
'signalapp',
'signal',
'sign-in',
'sign-language',
'sign-out',
'simplybuilt',
'sitemap',
'skate',
'sketchfab',
'skyatlas',
'skype',
'slack',
'sliders',
'slideshare',
'smile-o',
'snapchat-ghost',
'snapchat-square',
'snapchat',
'snowdrift',
'snowflake-o',
'social-home',
'sort-alpha-asc',
'sort-alpha-desc',
'sort-amount-asc',
'sort-amount-desc',
'sort-asc',
'sort-desc',
'sort-numeric-asc',
'sort-numeric-desc',
'sort',
'soundcloud',
'space-shuttle',
'spell-check',
'spinner',
'spoon',
'spotify',
'square-o',
'square',
'stack-exchange',
'stack-overflow',
'star-half-o',
'star-half',
'star-o',
'star',
'steam-square',
'steam',
'step-backward',
'step-forward',
'stethoscope',
'sticky-note-o',
'sticky-note',
'stop-circle-o',
'stop-circle',
'stop',
'street-view',
'strikethrough',
'stumbleupon-circle',
'stumbleupon',
'subscript',
'subway',
'suitcase',
'sun-o',
'sun',
'superpowers',
'superscript',
'syncthing',
'table',
'tablet',
'tachometer',
'tags',
'tag',
'tasks',
'taxi',
'telegram',
'television',
'tencent-weibo',
'terminal',
'tex',
'text-height',
'textpattern',
'text-width',
'themeisle',
'thermometer-empty',
'thermometer-full',
'thermometer-half',
'thermometer-quarter',
'thermometer-three-quarters',
'th-large',
'th-list',
'th',
'thumbs-down',
'thumbs-o-down',
'thumbs-o-up',
'thumbs-up',
'thumb-tack',
'ticket',
'times-circle-o',
'times-circle',
'times',
'tint',
'tipeee',
'toggle-off',
'toggle-on',
'tor-onion',
'trademark',
'train',
'transgender-alt',
'transgender',
'trash-o',
'trash',
'tree',
'trello',
'tripadvisor',
'trophy',
'truck',
'try',
'tty',
'tumblr-square',
'tumblr',
'twitch',
'twitter-square',
'twitter',
'umbrella',
'underline',
'undo',
'unity',
'universal-access',
'university',
'unlock-alt',
'unlock',
'unslpash',
'upload',
'usb',
'usd',
'user-circle-o',
'user-circle',
'user-md',
'user-o',
'user-plus',
'user-secret',
'users',
'user',
'user-times',
'venus-double',
'venus-mars',
'venus',
'viacoin',
'viadeo-square',
'viadeo',
'video-camera',
'vimeo-square',
'vimeo',
'vine',
'vk',
'volume-control-phone',
'volume-down',
'volume-mute',
'volume-off',
'volume-up',
'weibo',
'weixin',
'whatsapp',
'wheelchair-alt',
'wheelchair',
'wifi',
'wikidata',
'wikipedia-w',
'window-close-o',
'window-close',
'window-maximize',
'window-minimize',
'window-restore',
'windows',
'wire',
'wordpress',
'wpbeginner',
'wpexplorer',
'wpforms',
'wrench',
'xing-square',
'xing',
'xmpp',
'yahoo',
'y-combinator',
'yelp',
'yoast',
'youtube-play',
'youtube-square',
'youtube',
'zotero'
] as const

View file

@ -1,39 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { ForkAwesomeIconProps } from './fork-awesome-icon'
import { ForkAwesomeIcon } from './fork-awesome-icon'
import { ForkAwesomeStack } from './fork-awesome-stack'
import { render } from '@testing-library/react'
import type { ReactElement } from 'react'
describe('ForkAwesomeStack', () => {
const stack: Array<ReactElement<ForkAwesomeIconProps>> = [
<ForkAwesomeIcon icon={'heart'} key={'heart'} />,
<ForkAwesomeIcon icon={'download'} key={'download'} />
]
it('renders a heart and a download icon stack', () => {
const view = render(<ForkAwesomeStack>{stack}</ForkAwesomeStack>)
expect(view.container).toMatchSnapshot()
})
describe('renders in size', () => {
it('2x', () => {
const view = render(<ForkAwesomeStack size={'2x'}>{stack}</ForkAwesomeStack>)
expect(view.container).toMatchSnapshot()
})
it('3x', () => {
const view = render(<ForkAwesomeStack size={'3x'}>{stack}</ForkAwesomeStack>)
expect(view.container).toMatchSnapshot()
})
it('4x', () => {
const view = render(<ForkAwesomeStack size={'4x'}>{stack}</ForkAwesomeStack>)
expect(view.container).toMatchSnapshot()
})
it('5x', () => {
const view = render(<ForkAwesomeStack size={'5x'}>{stack}</ForkAwesomeStack>)
expect(view.container).toMatchSnapshot()
})
})
})

View file

@ -1,34 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { ForkAwesomeIconProps } from './fork-awesome-icon'
import { ForkAwesomeIcon } from './fork-awesome-icon'
import type { IconSize } from './types'
import type { ReactElement } from 'react'
import React from 'react'
export interface ForkAwesomeStackProps {
size?: IconSize
children: ReactElement<ForkAwesomeIconProps> | Array<ReactElement<ForkAwesomeIconProps>>
}
/**
* A stack of {@link ForkAwesomeIcon ForkAwesomeIcons}.
*
* @param size Which size the stack should have.
* @param children One or more {@link ForkAwesomeIcon ForkAwesomeIcons} to be stacked.
*/
export const ForkAwesomeStack: React.FC<ForkAwesomeStackProps> = ({ size, children }) => {
return (
<span className={`fa-stack ${size ? 'fa-' : ''}${size ?? ''}`}>
{React.Children.map(children, (child) => {
if (!React.isValidElement<ForkAwesomeIconProps>(child)) {
return null
}
return <ForkAwesomeIcon {...child.props} stacked={true} />
})}
</span>
)
}

View file

@ -1,9 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { ForkAwesomeIcons } from './fork-awesome-icons'
export type IconName = (typeof ForkAwesomeIcons)[number]
export type IconSize = '2x' | '3x' | '4x' | '5x'

View file

@ -6,8 +6,7 @@
import LogoBwHorizontal from './logo_text_bw_horizontal.svg' import LogoBwHorizontal from './logo_text_bw_horizontal.svg'
import LogoColorVertical from './logo_text_color_vertical.svg' import LogoColorVertical from './logo_text_color_vertical.svg'
import LogoWbHorizontal from './logo_text_wb_horizontal.svg' import LogoWbHorizontal from './logo_text_wb_horizontal.svg'
import React, { useMemo } from 'react' import React from 'react'
import { useTranslation } from 'react-i18next'
export enum HedgeDocLogoSize { export enum HedgeDocLogoSize {
SMALL = 32, SMALL = 32,
@ -33,17 +32,13 @@ export enum HedgeDocLogoType {
* @param logoType The logo type to be used. * @param logoType The logo type to be used.
*/ */
export const HedgeDocLogoWithText: React.FC<HedgeDocLogoProps> = ({ size = HedgeDocLogoSize.MEDIUM, logoType }) => { export const HedgeDocLogoWithText: React.FC<HedgeDocLogoProps> = ({ size = HedgeDocLogoSize.MEDIUM, logoType }) => {
const { t } = useTranslation()
const altText = useMemo(() => t('app.icon'), [t])
const style = useMemo(() => ({ height: size }), [size])
switch (logoType) { switch (logoType) {
case HedgeDocLogoType.COLOR_VERTICAL: case HedgeDocLogoType.COLOR_VERTICAL:
return <LogoColorVertical className={'w-auto'} title={altText} style={style} /> return <LogoColorVertical className={'w-auto'} height={`${size}px`} width={'auto'} />
case HedgeDocLogoType.BW_HORIZONTAL: case HedgeDocLogoType.BW_HORIZONTAL:
return <LogoBwHorizontal className={'w-auto'} title={altText} style={style} /> return <LogoBwHorizontal className={'w-auto'} height={`${size}px`} width={'auto'} />
case HedgeDocLogoType.WB_HORIZONTAL: case HedgeDocLogoType.WB_HORIZONTAL:
return <LogoWbHorizontal className={'w-auto'} title={altText} style={style} /> return <LogoWbHorizontal className={'w-auto'} height={`${size}px`} width={'auto'} />
default: default:
return null return null
} }

View file

@ -10,9 +10,7 @@ exports[`IconButton correctly uses the onClick callback 1`] = `
<span <span
class="icon-part d-flex align-items-center" class="icon-part d-flex align-items-center"
> >
<i BootstrapIconMock_Heart
class="fa fa-heart icon "
/>
</span> </span>
<span <span
class="text-part d-flex align-items-center" class="text-part d-flex align-items-center"
@ -33,9 +31,7 @@ exports[`IconButton renders heart icon 1`] = `
<span <span
class="icon-part d-flex align-items-center" class="icon-part d-flex align-items-center"
> >
<i BootstrapIconMock_Heart
class="fa fa-heart icon "
/>
</span> </span>
<span <span
class="text-part d-flex align-items-center" class="text-part d-flex align-items-center"
@ -56,9 +52,7 @@ exports[`IconButton renders with additional className 1`] = `
<span <span
class="icon-part d-flex align-items-center" class="icon-part d-flex align-items-center"
> >
<i BootstrapIconMock_Heart
class="fa fa-heart icon "
/>
</span> </span>
<span <span
class="text-part d-flex align-items-center" class="text-part d-flex align-items-center"
@ -79,9 +73,7 @@ exports[`IconButton renders with border 1`] = `
<span <span
class="icon-part d-flex align-items-center" class="icon-part d-flex align-items-center"
> >
<i BootstrapIconMock_Heart
class="fa fa-heart icon "
/>
</span> </span>
<span <span
class="text-part d-flex align-items-center" class="text-part d-flex align-items-center"
@ -91,26 +83,3 @@ exports[`IconButton renders with border 1`] = `
</button> </button>
</div> </div>
`; `;
exports[`IconButton renders with fixed width icon 1`] = `
<div>
<button
class="btn-icon p-0 d-inline-flex align-items-stretch btn btn-primary"
data-testid="icon-button"
type="button"
>
<span
class="icon-part d-flex align-items-center"
>
<i
class="fa fa-fw fa-heart icon "
/>
</span>
<span
class="text-part d-flex align-items-center"
>
test with fixed with icon
</span>
</button>
</div>
`;

View file

@ -3,35 +3,26 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { IconName } from '../fork-awesome/types'
import { IconButton } from './icon-button' import { IconButton } from './icon-button'
import { fireEvent, render, screen } from '@testing-library/react' import { fireEvent, render, screen } from '@testing-library/react'
import { Heart as IconHeart } from 'react-bootstrap-icons'
describe('IconButton', () => { describe('IconButton', () => {
const icon: IconName = 'heart'
it('renders heart icon', () => { it('renders heart icon', () => {
const view = render(<IconButton icon={icon}>test</IconButton>) const view = render(<IconButton icon={IconHeart}>test</IconButton>)
expect(view.container).toMatchSnapshot() expect(view.container).toMatchSnapshot()
}) })
it('renders with border', () => { it('renders with border', () => {
const view = render( const view = render(
<IconButton icon={icon} border={true}> <IconButton icon={IconHeart} border={true}>
test with border test with border
</IconButton> </IconButton>
) )
expect(view.container).toMatchSnapshot() expect(view.container).toMatchSnapshot()
}) })
it('renders with fixed width icon', () => {
const view = render(
<IconButton icon={icon} iconFixedWidth={true}>
test with fixed with icon
</IconButton>
)
expect(view.container).toMatchSnapshot()
})
it('renders with additional className', () => { it('renders with additional className', () => {
const view = render( const view = render(
<IconButton icon={icon} className={'testClass'}> <IconButton icon={IconHeart} className={'testClass'}>
test with additional className test with additional className
</IconButton> </IconButton>
) )
@ -40,7 +31,7 @@ describe('IconButton', () => {
it('correctly uses the onClick callback', async () => { it('correctly uses the onClick callback', async () => {
const onClick = jest.fn() const onClick = jest.fn()
const view = render( const view = render(
<IconButton icon={icon} onClick={onClick}> <IconButton icon={IconHeart} onClick={onClick}>
test with onClick test with onClick
</IconButton> </IconButton>
) )

View file

@ -5,37 +5,38 @@
*/ */
import type { PropsWithDataTestId } from '../../../utils/test-id' import type { PropsWithDataTestId } from '../../../utils/test-id'
import { testId } from '../../../utils/test-id' import { testId } from '../../../utils/test-id'
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' import { UiIcon } from '../icons/ui-icon'
import type { IconName } from '../fork-awesome/types'
import { ShowIf } from '../show-if/show-if' import { ShowIf } from '../show-if/show-if'
import styles from './icon-button.module.scss' import styles from './icon-button.module.scss'
import React from 'react' import React from 'react'
import type { ButtonProps } from 'react-bootstrap' import type { ButtonProps } from 'react-bootstrap'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import type { Icon } from 'react-bootstrap-icons'
export interface IconButtonProps extends ButtonProps, PropsWithDataTestId { export interface IconButtonProps extends ButtonProps, PropsWithDataTestId {
icon: IconName icon: Icon
onClick?: () => void onClick?: () => void
border?: boolean border?: boolean
iconFixedWidth?: boolean iconSize?: number | string
} }
/** /**
* A generic {@link Button button} with an {@link ForkAwesomeIcon icon} in it. * A generic {@link Button button} with an icon in it.
* *
* @param icon Which icon should be used * @param icon Which icon should be used
* @param children The children that will be added as the content of the button. * @param children The children that will be added as the content of the button.
* @param iconFixedWidth If the icon should be of fixed width. * @param iconFixedWidth If the icon should be of fixed width.
* @param border Should the button have a border. * @param border Should the button have a border.
* @param className Additional class names added to the button. * @param className Additional class names added to the button.
* @param iconSize Size of the icon
* @param props Additional props for the button. * @param props Additional props for the button.
*/ */
export const IconButton: React.FC<IconButtonProps> = ({ export const IconButton: React.FC<IconButtonProps> = ({
icon, icon,
children, children,
iconFixedWidth = false,
border = false, border = false,
className, className,
iconSize,
...props ...props
}) => { }) => {
return ( return (
@ -46,7 +47,7 @@ export const IconButton: React.FC<IconButtonProps> = ({
}`} }`}
{...testId('icon-button')}> {...testId('icon-button')}>
<span className={`${styles['icon-part']} d-flex align-items-center`}> <span className={`${styles['icon-part']} d-flex align-items-center`}>
<ForkAwesomeIcon icon={icon} fixedWidth={iconFixedWidth} className={'icon'} /> <UiIcon size={iconSize} icon={icon} className={'icon'} />
</span> </span>
<ShowIf condition={!!children}> <ShowIf condition={!!children}>
<span className={`${styles['text-part']} d-flex align-items-center`}>{children}</span> <span className={`${styles['text-part']} d-flex align-items-center`}>{children}</span>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { testId } from '../../../utils/test-id'
import { BootstrapLazyIcons } from './bootstrap-icons'
import type { BootstrapIconName } from './bootstrap-icons'
import React, { Suspense, useMemo } from 'react'
export interface LazyBootstrapIconProps {
icon: BootstrapIconName
size?: number
className?: string
}
/**
* Renders a bootstrap icon.
*
* @param iconName the internal name of the icon
* @param size the size of the icon - the default is 1
* @see https://icons.getbootstrap.com/
*/
export const LazyBootstrapIcon: React.FC<LazyBootstrapIconProps> = ({ icon, size, className }) => {
const fullSize = useMemo(() => `${size ?? 1}em`, [size])
const Icon = BootstrapLazyIcons[icon]
return (
<Suspense fallback={<></>}>
<Icon
width={fullSize}
height={fullSize}
fill={'currentColor'}
className={className}
{...testId(`lazy-bootstrap-icon-${icon}`)}></Icon>
</Suspense>
)
}

View file

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import styles from './ui-icons.module.scss'
import React, { Fragment, useMemo } from 'react'
import type { Icon } from 'react-bootstrap-icons'
export interface UiIconProps {
icon: Icon | undefined
nbsp?: boolean
size?: number | string
className?: string
spin?: boolean
}
export const UiIcon: React.FC<UiIconProps> = ({ icon, nbsp, className, size, spin }) => {
const finalSize = useMemo(() => {
if (size === undefined) {
return '1em'
} else if (typeof size === 'number') {
return `${size}em`
} else {
return size
}
}, [size])
const finalClassName = useMemo(() => {
return `${spin ? styles.spin : ''} ${className ?? ''}`
}, [className, spin])
if (icon) {
return (
<Fragment>
{React.createElement(icon, {
className: finalClassName,
width: finalSize,
height: finalSize
})}
{nbsp ? <Fragment>&nbsp;</Fragment> : null}
</Fragment>
)
} else {
return null
}
}

View file

@ -0,0 +1,20 @@
/*!
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.spin {
transform-origin: center center;
@keyframes rotation {
0% {
transform: rotateZ(0deg);
}
100% {
transform: rotateZ(360deg);
}
}
animation: rotation linear 2s infinite;
}

View file

@ -52,9 +52,7 @@ exports[`ExternalLink renders an external link with an icon 1`] = `
rel="noopener noreferrer" rel="noopener noreferrer"
target="_blank" target="_blank"
> >
<i BootstrapIconMock_Heart
class="fa fa-fw fa-heart "
/>
   
testText testText
</a> </a>

View file

@ -40,9 +40,7 @@ exports[`InternalLink renders an internal link with an icon 1`] = `
class="text-light" class="text-light"
href="/test" href="/test"
> >
<i BootstrapIconMock_Heart
class="fa fa-fw fa-heart "
/>
   
testText testText
</a> </a>

View file

@ -5,6 +5,7 @@
*/ */
import { ExternalLink } from './external-link' import { ExternalLink } from './external-link'
import { render } from '@testing-library/react' import { render } from '@testing-library/react'
import { Heart as IconHeart } from 'react-bootstrap-icons'
describe('ExternalLink', () => { describe('ExternalLink', () => {
const href = 'https://example.com' const href = 'https://example.com'
@ -14,7 +15,7 @@ describe('ExternalLink', () => {
expect(view.container).toMatchSnapshot() expect(view.container).toMatchSnapshot()
}) })
it('renders an external link with an icon', () => { it('renders an external link with an icon', () => {
const view = render(<ExternalLink text={text} href={href} icon={'heart'} />) const view = render(<ExternalLink text={text} href={href} icon={IconHeart} />)
expect(view.container).toMatchSnapshot() expect(view.container).toMatchSnapshot()
}) })
it('renders an external link with an id', () => { it('renders an external link with an id', () => {

View file

@ -3,9 +3,7 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' import { UiIcon } from '../icons/ui-icon'
import type { IconName } from '../fork-awesome/types'
import { ShowIf } from '../show-if/show-if'
import type { LinkWithTextProps } from './types' import type { LinkWithTextProps } from './types'
import React from 'react' import React from 'react'
@ -31,10 +29,7 @@ export const ExternalLink: React.FC<LinkWithTextProps> = ({
}) => { }) => {
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'>
<ShowIf condition={!!icon}> <UiIcon icon={icon} nbsp={true} />
<ForkAwesomeIcon icon={icon as IconName} fixedWidth={true} />
&nbsp;
</ShowIf>
{text} {text}
</a> </a>
) )

View file

@ -5,6 +5,7 @@
*/ */
import { InternalLink } from './internal-link' import { InternalLink } from './internal-link'
import { render } from '@testing-library/react' import { render } from '@testing-library/react'
import { Heart as IconHeart } from 'react-bootstrap-icons'
describe('InternalLink', () => { describe('InternalLink', () => {
const href = '/test' const href = '/test'
@ -14,7 +15,7 @@ describe('InternalLink', () => {
expect(view.container).toMatchSnapshot() expect(view.container).toMatchSnapshot()
}) })
it('renders an internal link with an icon', () => { it('renders an internal link with an icon', () => {
const view = render(<InternalLink text={text} href={href} icon={'heart'} />) const view = render(<InternalLink text={text} href={href} icon={IconHeart} />)
expect(view.container).toMatchSnapshot() expect(view.container).toMatchSnapshot()
}) })
it('renders an internal link with an id', () => { it('renders an internal link with an id', () => {

View file

@ -3,9 +3,7 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' import { UiIcon } from '../icons/ui-icon'
import type { IconName } from '../fork-awesome/types'
import { ShowIf } from '../show-if/show-if'
import type { LinkWithTextProps } from './types' import type { LinkWithTextProps } from './types'
import Link from 'next/link' import Link from 'next/link'
import React from 'react' import React from 'react'
@ -31,10 +29,7 @@ export const InternalLink: React.FC<LinkWithTextProps> = ({
}) => { }) => {
return ( return (
<Link href={href} className={className} id={id} title={title}> <Link href={href} className={className} id={id} title={title}>
<ShowIf condition={!!icon}> <UiIcon icon={icon} nbsp={true} />
<ForkAwesomeIcon icon={icon as IconName} fixedWidth={true} />
&nbsp;
</ShowIf>
{text} {text}
</Link> </Link>
) )

View file

@ -3,12 +3,12 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { IconName } from '../fork-awesome/fork-awesome-icon'
import type { TOptionsBase } from 'i18next' import type { TOptionsBase } from 'i18next'
import type { Icon } from 'react-bootstrap-icons'
interface GeneralLinkProp { interface GeneralLinkProp {
href: string href: string
icon?: IconName icon?: Icon
id?: string id?: string
className?: string className?: string
title?: string title?: string

View file

@ -1,29 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon'
import React from 'react'
import { Button } from 'react-bootstrap'
export interface LockButtonProps {
locked: boolean
onLockedChanged: (newState: boolean) => void
title: string
}
/**
* A button with a lock icon.
*
* @param locked If the button should be shown as locked or not
* @param onLockedChanged The callback to change the state from locked to unlocked and vise-versa.
* @param title The title for the button.
*/
export const LockButton: React.FC<LockButtonProps> = ({ locked, onLockedChanged, title }) => {
return (
<Button variant='dark' size='sm' onClick={() => onLockedChanged(!locked)} title={title}>
{locked ? <ForkAwesomeIcon icon='lock' /> : <ForkAwesomeIcon icon='unlock' />}
</Button>
)
}

View file

@ -148,9 +148,7 @@ exports[`CommonModal render correctly with title icon 1`] = `
<div <div
class="modal-title h4" class="modal-title h4"
> >
<i BootstrapIconMock_Heart
class="fa fa-heart "
/>
   
<span /> <span />
</div> </div>

View file

@ -7,6 +7,7 @@ import { mockI18n } from '../../markdown-renderer/test-utils/mock-i18n'
import { CommonModal } from './common-modal' import { CommonModal } from './common-modal'
import { fireEvent, render, screen } from '@testing-library/react' import { fireEvent, render, screen } from '@testing-library/react'
import React from 'react' import React from 'react'
import { Heart as IconHeart } from 'react-bootstrap-icons'
describe('CommonModal', () => { describe('CommonModal', () => {
afterAll(() => { afterAll(() => {
@ -65,7 +66,7 @@ describe('CommonModal', () => {
it('render correctly with title icon', async () => { it('render correctly with title icon', async () => {
render( render(
<CommonModal show={true} titleIcon={'heart'}> <CommonModal show={true} titleIcon={IconHeart}>
testText testText
</CommonModal> </CommonModal>
) )

View file

@ -6,12 +6,12 @@
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 { testId } from '../../../utils/test-id' import { testId } from '../../../utils/test-id'
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' import { UiIcon } from '../icons/ui-icon'
import type { IconName } from '../fork-awesome/types'
import { ShowIf } from '../show-if/show-if' import { ShowIf } from '../show-if/show-if'
import type { PropsWithChildren } from 'react' import type { PropsWithChildren } from 'react'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Modal } from 'react-bootstrap' import { Modal } from 'react-bootstrap'
import type { Icon } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
export interface ModalVisibilityProps { export interface ModalVisibilityProps {
@ -23,7 +23,7 @@ export interface ModalContentProps {
titleI18nKey?: string titleI18nKey?: string
title?: string title?: string
showCloseButton?: boolean showCloseButton?: boolean
titleIcon?: IconName titleIcon?: Icon
modalSize?: 'lg' | 'sm' | 'xl' modalSize?: 'lg' | 'sm' | 'xl'
additionalClasses?: string additionalClasses?: string
} }
@ -74,10 +74,7 @@ export const CommonModal: React.FC<PropsWithChildren<CommonModalProps>> = ({
size={modalSize}> size={modalSize}>
<Modal.Header closeButton={!!showCloseButton}> <Modal.Header closeButton={!!showCloseButton}>
<Modal.Title> <Modal.Title>
<ShowIf condition={!!titleIcon}> <UiIcon icon={titleIcon} nbsp={true} />
<ForkAwesomeIcon icon={titleIcon as IconName} />
&nbsp;
</ShowIf>
{titleElement} {titleElement}
</Modal.Title> </Modal.Title>
</Modal.Header> </Modal.Header>

View file

@ -1,20 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`create non existing note hint shows success message when the note has been created 1`] = `
<div>
<div
class="fade mt-5 alert alert-info show"
data-testid="noteCreated"
role="alert"
>
<i
class="fa fa-check-circle me-2 "
/>
noteLoadingBoundary.createNote.success
</div>
</div>
`;
exports[`create non existing note hint renders a waiting message when button is clicked 1`] = ` exports[`create non existing note hint renders a waiting message when button is clicked 1`] = `
<div> <div>
<div <div
@ -22,9 +7,7 @@ exports[`create non existing note hint renders a waiting message when button is
data-testid="loadingMessage" data-testid="loadingMessage"
role="alert" role="alert"
> >
<i BootstrapIconMock_ArrowRepeat
class="fa fa-spinner fa-spin me-2 "
/>
noteLoadingBoundary.createNote.creating noteLoadingBoundary.createNote.creating
</div> </div>
</div> </div>
@ -62,10 +45,21 @@ exports[`create non existing note hint shows an error message if note couldn't b
data-testid="failedMessage" data-testid="failedMessage"
role="alert" role="alert"
> >
<i BootstrapIconMock_ExclamationTriangle
class="fa fa-exclamation-triangle me-1 "
/>
noteLoadingBoundary.createNote.error noteLoadingBoundary.createNote.error
</div> </div>
</div> </div>
`; `;
exports[`create non existing note hint shows success message when the note has been created 1`] = `
<div>
<div
class="fade mt-5 alert alert-info show"
data-testid="noteCreated"
role="alert"
>
BootstrapIconMock_CheckCircle
noteLoadingBoundary.createNote.success
</div>
</div>
`;

View file

@ -6,10 +6,13 @@
import { createNoteWithPrimaryAlias } from '../../../api/notes' import { createNoteWithPrimaryAlias } from '../../../api/notes'
import { useSingleStringUrlParameter } from '../../../hooks/common/use-single-string-url-parameter' import { useSingleStringUrlParameter } from '../../../hooks/common/use-single-string-url-parameter'
import { testId } from '../../../utils/test-id' import { testId } from '../../../utils/test-id'
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' import { UiIcon } from '../icons/ui-icon'
import { ShowIf } from '../show-if/show-if' import { ShowIf } from '../show-if/show-if'
import React, { useCallback, useEffect } from 'react' import React, { useCallback, useEffect } from 'react'
import { Alert, Button } from 'react-bootstrap' import { Alert, Button } from 'react-bootstrap'
import { ArrowRepeat as IconArrowRepeat } from 'react-bootstrap-icons'
import { CheckCircle as IconCheckCircle } from 'react-bootstrap-icons'
import { ExclamationTriangle as IconExclamationTriangle } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
import { useAsyncFn } from 'react-use' import { useAsyncFn } from 'react-use'
@ -49,21 +52,21 @@ export const CreateNonExistingNoteHint: React.FC<CreateNonExistingNoteHintProps>
} else if (returnState.value) { } else if (returnState.value) {
return ( return (
<Alert variant={'info'} {...testId('noteCreated')} className={'mt-5'}> <Alert variant={'info'} {...testId('noteCreated')} className={'mt-5'}>
<ForkAwesomeIcon icon={'check-circle'} className={'me-2'} /> <UiIcon icon={IconCheckCircle} className={'me-2'} />
<Trans i18nKey={'noteLoadingBoundary.createNote.success'} /> <Trans i18nKey={'noteLoadingBoundary.createNote.success'} />
</Alert> </Alert>
) )
} else if (returnState.loading) { } else if (returnState.loading) {
return ( return (
<Alert variant={'info'} {...testId('loadingMessage')} className={'mt-5'}> <Alert variant={'info'} {...testId('loadingMessage')} className={'mt-5'}>
<ForkAwesomeIcon icon={'spinner'} className={'fa-spin me-2'} /> <UiIcon icon={IconArrowRepeat} className={'me-2'} spin={true} />
<Trans i18nKey={'noteLoadingBoundary.createNote.creating'} /> <Trans i18nKey={'noteLoadingBoundary.createNote.creating'} />
</Alert> </Alert>
) )
} else if (returnState.error !== undefined) { } else if (returnState.error !== undefined) {
return ( return (
<Alert variant={'danger'} {...testId('failedMessage')} className={'mt-5'}> <Alert variant={'danger'} {...testId('failedMessage')} className={'mt-5'}>
<ForkAwesomeIcon icon={'exclamation-triangle'} className={'me-1'} /> <UiIcon icon={IconExclamationTriangle} className={'me-1'} />
<Trans i18nKey={'noteLoadingBoundary.createNote.error'} /> <Trans i18nKey={'noteLoadingBoundary.createNote.error'} />
</Alert> </Alert>
) )
@ -82,7 +85,7 @@ export const CreateNonExistingNoteHint: React.FC<CreateNonExistingNoteHintProps>
onClick={onClickHandler} onClick={onClickHandler}
{...testId('createNoteButton')}> {...testId('createNoteButton')}>
<ShowIf condition={returnState.loading}> <ShowIf condition={returnState.loading}>
<ForkAwesomeIcon icon={'spinner'} className={'fa-spin me-2'} /> <UiIcon icon={IconArrowRepeat} className={'me-2'} spin={true} />
</ShowIf> </ShowIf>
<Trans i18nKey={'noteLoadingBoundary.createNote.create'} /> <Trans i18nKey={'noteLoadingBoundary.createNote.create'} />
</Button> </Button>

View file

@ -3,8 +3,9 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon' import { UiIcon } from '../icons/ui-icon'
import React from 'react' import React from 'react'
import { ArrowRepeat as IconArrowRepeat } from 'react-bootstrap-icons'
/** /**
* Renders a indefinitely spinning spinner. * Renders a indefinitely spinning spinner.
@ -12,7 +13,7 @@ import React from 'react'
export const WaitSpinner: React.FC = () => { export const WaitSpinner: React.FC = () => {
return ( return (
<div className={'m-3 d-flex align-items-center justify-content-center'}> <div className={'m-3 d-flex align-items-center justify-content-center'}>
<ForkAwesomeIcon icon={'spinner'} className={'fa-spin'} /> <UiIcon icon={IconArrowRepeat} spin={true} />
</div> </div>
) )
} }

View file

@ -10,6 +10,7 @@ import { NoteInfoLineCreated } from '../editor-page/document-bar/note-info/note-
import { NoteInfoLineUpdated } from '../editor-page/document-bar/note-info/note-info-line-updated' import { NoteInfoLineUpdated } from '../editor-page/document-bar/note-info/note-info-line-updated'
import styles from './document-infobar.module.scss' import styles from './document-infobar.module.scss'
import React from 'react' import React from 'react'
import { Pencil as IconPencil } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
/** /**
@ -35,7 +36,7 @@ export const DocumentInfobar: React.FC = () => {
<InternalLink <InternalLink
text={''} text={''}
href={`/n/${noteDetails.primaryAddress}`} href={`/n/${noteDetails.primaryAddress}`}
icon={'pencil'} icon={IconPencil}
className={'text-primary text-decoration-none mx-1'} className={'text-primary text-decoration-none mx-1'}
title={t('views.readOnly.editNote') ?? undefined} title={t('views.readOnly.editNote') ?? undefined}
/> />

View file

@ -34,6 +34,7 @@ export const CheatsheetTabContent: React.FC = () => {
`[${t('editor.editorToolbar.link')}](https://example.com)`, `[${t('editor.editorToolbar.link')}](https://example.com)`,
`![${t('editor.editorToolbar.image')}](/icons/apple-touch-icon.png)`, `![${t('editor.editorToolbar.image')}](/icons/apple-touch-icon.png)`,
':smile:', ':smile:',
':bi-bootstrap:',
`:::info\n${t('editor.help.cheatsheet.exampleAlert')}\n:::` `:::info\n${t('editor.help.cheatsheet.exampleAlert')}\n:::`
], ],
[checked, t] [checked, t]

View file

@ -5,10 +5,11 @@
*/ */
import { useBooleanState } from '../../../../hooks/common/use-boolean-state' import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
import { cypressId } from '../../../../utils/cypress-attribute' import { cypressId } from '../../../../utils/cypress-attribute'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import { HelpModal } from './help-modal' import { HelpModal } from './help-modal'
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { QuestionCircle as IconQuestionCircle } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
/** /**
@ -27,7 +28,7 @@ export const HelpButton: React.FC = () => {
size='sm' size='sm'
variant='outline-light' variant='outline-light'
onClick={showModal}> onClick={showModal}>
<ForkAwesomeIcon icon='question-circle' /> <UiIcon icon={IconQuestionCircle} />
</Button> </Button>
<HelpModal show={modalVisibility} onHide={closeModal} /> <HelpModal show={modalVisibility} onHide={closeModal} />
</Fragment> </Fragment>

View file

@ -10,6 +10,7 @@ import { LinksTabContent } from './links-tab-content'
import { ShortcutTabContent } from './shortcuts-tab-content' import { ShortcutTabContent } from './shortcuts-tab-content'
import React, { useMemo, useState } from 'react' import React, { useMemo, useState } from 'react'
import { Button, Modal } from 'react-bootstrap' import { Button, Modal } from 'react-bootstrap'
import { QuestionCircle as IconQuestionCircle } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
export enum HelpTabStatus { export enum HelpTabStatus {
@ -47,7 +48,7 @@ export const HelpModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) => {
const modalTitle = useMemo(() => t('editor.documentBar.help') + ' - ' + t(`editor.help.${tab}`), [t, tab]) const modalTitle = useMemo(() => t('editor.documentBar.help') + ' - ' + t(`editor.help.${tab}`), [t, tab])
return ( return (
<CommonModal modalSize={'lg'} titleIcon={'question-circle'} show={show} onHide={onHide} title={modalTitle}> <CommonModal modalSize={'lg'} titleIcon={IconQuestionCircle} show={show} onHide={onHide} title={modalTitle}>
<Modal.Body> <Modal.Body>
<nav className='nav nav-tabs'> <nav className='nav nav-tabs'>
<Button <Button

View file

@ -8,6 +8,11 @@ import { TranslatedExternalLink } from '../../../common/links/translated-externa
import { TranslatedInternalLink } from '../../../common/links/translated-internal-link' import { TranslatedInternalLink } from '../../../common/links/translated-internal-link'
import React from 'react' import React from 'react'
import { Col, Row } from 'react-bootstrap' import { Col, Row } from 'react-bootstrap'
import { Dot as IconDot } from 'react-bootstrap-icons'
import { Flag as IconFlag } from 'react-bootstrap-icons'
import { Hash as IconHash } from 'react-bootstrap-icons'
import { PeopleFill as IconPeopleFill } from 'react-bootstrap-icons'
import { Tag as IconTag } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
/** /**
@ -28,7 +33,7 @@ export const LinksTabContent: React.FC = () => {
<TranslatedExternalLink <TranslatedExternalLink
i18nKey='editor.help.contacts.community' i18nKey='editor.help.contacts.community'
href={links.community} href={links.community}
icon='users' icon={IconPeopleFill}
className='text-primary' className='text-primary'
/> />
</li> </li>
@ -37,7 +42,7 @@ export const LinksTabContent: React.FC = () => {
i18nKey='editor.help.contacts.meetUsOn' i18nKey='editor.help.contacts.meetUsOn'
i18nOption={{ service: 'Matrix' }} i18nOption={{ service: 'Matrix' }}
href={links.chat} href={links.chat}
icon='hashtag' icon={IconHash}
className='text-primary' className='text-primary'
/> />
</li> </li>
@ -45,7 +50,7 @@ export const LinksTabContent: React.FC = () => {
<TranslatedExternalLink <TranslatedExternalLink
i18nKey='editor.help.contacts.reportIssue' i18nKey='editor.help.contacts.reportIssue'
href={links.backendIssues} href={links.backendIssues}
icon='tag' icon={IconTag}
className='text-primary' className='text-primary'
/> />
</li> </li>
@ -53,7 +58,7 @@ export const LinksTabContent: React.FC = () => {
<TranslatedExternalLink <TranslatedExternalLink
i18nKey='editor.help.contacts.helpTranslating' i18nKey='editor.help.contacts.helpTranslating'
href={links.translate} href={links.translate}
icon='language' icon={IconFlag}
className='text-primary' className='text-primary'
/> />
</li> </li>
@ -70,7 +75,7 @@ export const LinksTabContent: React.FC = () => {
<TranslatedInternalLink <TranslatedInternalLink
i18nKey='editor.help.documents.features' i18nKey='editor.help.documents.features'
href='/n/features' href='/n/features'
icon='dot-circle-o' icon={IconDot}
className='text-primary' className='text-primary'
/> />
</li> </li>
@ -78,7 +83,7 @@ export const LinksTabContent: React.FC = () => {
<TranslatedInternalLink <TranslatedInternalLink
i18nKey='editor.help.documents.yamlMetadata' i18nKey='editor.help.documents.yamlMetadata'
href='/n/yaml-metadata' href='/n/yaml-metadata'
icon='dot-circle-o' icon={IconDot}
className='text-primary' className='text-primary'
/> />
</li> </li>
@ -86,7 +91,7 @@ export const LinksTabContent: React.FC = () => {
<TranslatedInternalLink <TranslatedInternalLink
i18nKey='editor.help.documents.slideExample' i18nKey='editor.help.documents.slideExample'
href='/n/slide-example' href='/n/slide-example'
icon='dot-circle-o' icon={IconDot}
className='text-primary' className='text-primary'
/> />
</li> </li>

View file

@ -3,10 +3,10 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon' import { IconButton } from '../../common/icon-button/icon-button'
import Link from 'next/link' import Link from 'next/link'
import React from 'react' import React from 'react'
import { Button } from 'react-bootstrap' import { Plus as IconPlus } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
/** /**
@ -17,9 +17,9 @@ export const NewNoteButton: React.FC = () => {
return ( return (
<Link href={'/new'} passHref={true}> <Link href={'/new'} passHref={true}>
<Button className='mx-2' size='sm' variant='primary'> <IconButton className='mx-2' iconSize={1.5} icon={IconPlus}>
<ForkAwesomeIcon icon='plus' /> <Trans i18nKey='editor.appBar.new' /> <Trans i18nKey='editor.appBar.new' />
</Button> </IconButton>
</Link> </Link>
) )
} }

View file

@ -4,10 +4,11 @@
* 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 { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-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'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { FileEarmarkTextFill as IconFileEarmarkTextFill } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
/** /**
@ -24,7 +25,7 @@ export const ReadOnlyModeButton: React.FC = () => {
className='ms-2 text-secondary' className='ms-2 text-secondary'
size='sm' size='sm'
variant='outline-light'> variant='outline-light'>
<ForkAwesomeIcon icon='file-text-o' /> <UiIcon icon={IconFileEarmarkTextFill} />
</Button> </Button>
</Link> </Link>
) )

View file

@ -4,10 +4,11 @@
* 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 { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-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'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { Tv as IconTv } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
/** /**
@ -24,7 +25,7 @@ export const SlideModeButton: React.FC = () => {
className='ms-2 text-secondary' className='ms-2 text-secondary'
size='sm' size='sm'
variant='outline-light'> variant='outline-light'>
<ForkAwesomeIcon icon='television' /> <UiIcon icon={IconTv} />
</Button> </Button>
</Link> </Link>
) )

View file

@ -20,9 +20,7 @@ exports[`AliasesAddForm renders the input form 1`] = `
title="editor.modal.aliases.addAlias" title="editor.modal.aliases.addAlias"
type="submit" type="submit"
> >
<i BootstrapIconMock_Plus
class="fa fa-plus "
/>
</button> </button>
</div> </div>
</form> </form>

View file

@ -13,9 +13,7 @@ exports[`AliasesListEntry renders an AliasesListEntry that is not primary 1`] =
title="editor.modal.aliases.makePrimary" title="editor.modal.aliases.makePrimary"
type="button" type="button"
> >
<i BootstrapIconMock_StarFill
class="fa fa-star-o "
/>
</button> </button>
<button <button
class="text-danger btn btn-light" class="text-danger btn btn-light"
@ -23,9 +21,7 @@ exports[`AliasesListEntry renders an AliasesListEntry that is not primary 1`] =
title="editor.modal.aliases.removeAlias" title="editor.modal.aliases.removeAlias"
type="button" type="button"
> >
<i BootstrapIconMock_X
class="fa fa-times "
/>
</button> </button>
</div> </div>
</li> </li>
@ -46,9 +42,7 @@ exports[`AliasesListEntry renders an AliasesListEntry that is primary 1`] = `
title="editor.modal.aliases.isPrimary" title="editor.modal.aliases.isPrimary"
type="button" type="button"
> >
<i BootstrapIconMock_Star
class="fa fa-star "
/>
</button> </button>
<button <button
class="text-danger btn btn-light" class="text-danger btn btn-light"
@ -56,9 +50,7 @@ exports[`AliasesListEntry renders an AliasesListEntry that is primary 1`] = `
title="editor.modal.aliases.removeAlias" title="editor.modal.aliases.removeAlias"
type="button" type="button"
> >
<i BootstrapIconMock_X
class="fa fa-times "
/>
</button> </button>
</div> </div>
</li> </li>

View file

@ -8,11 +8,12 @@ import { useApplicationState } from '../../../../hooks/common/use-application-st
import { useOnInputChange } from '../../../../hooks/common/use-on-input-change' import { useOnInputChange } from '../../../../hooks/common/use-on-input-change'
import { updateMetadata } from '../../../../redux/note-details/methods' import { updateMetadata } from '../../../../redux/note-details/methods'
import { testId } from '../../../../utils/test-id' import { testId } from '../../../../utils/test-id'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import { useUiNotifications } from '../../../notifications/ui-notification-boundary' import { useUiNotifications } from '../../../notifications/ui-notification-boundary'
import type { FormEvent } from 'react' import type { FormEvent } from 'react'
import React, { useCallback, useMemo, useState } from 'react' import React, { useCallback, useMemo, useState } from 'react'
import { Button, Form, InputGroup } from 'react-bootstrap' import { Button, Form, InputGroup } from 'react-bootstrap'
import { Plus as IconPlus } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
const validAliasRegex = /^[a-z0-9_-]*$/ const validAliasRegex = /^[a-z0-9_-]*$/
@ -63,7 +64,7 @@ export const AliasesAddForm: React.FC = () => {
disabled={!newAliasValid || newAlias === ''} disabled={!newAliasValid || newAlias === ''}
title={t('editor.modal.aliases.addAlias') ?? undefined} title={t('editor.modal.aliases.addAlias') ?? undefined}
{...testId('addAliasButton')}> {...testId('addAliasButton')}>
<ForkAwesomeIcon icon={'plus'} /> <UiIcon icon={IconPlus} />
</Button> </Button>
</InputGroup> </InputGroup>
</form> </form>

View file

@ -7,11 +7,14 @@ import { deleteAlias, markAliasAsPrimary } from '../../../../api/alias'
import type { Alias } from '../../../../api/alias/types' import type { Alias } from '../../../../api/alias/types'
import { updateMetadata } from '../../../../redux/note-details/methods' import { updateMetadata } from '../../../../redux/note-details/methods'
import { testId } from '../../../../utils/test-id' import { testId } from '../../../../utils/test-id'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import { ShowIf } from '../../../common/show-if/show-if' import { ShowIf } from '../../../common/show-if/show-if'
import { useUiNotifications } from '../../../notifications/ui-notification-boundary' import { useUiNotifications } from '../../../notifications/ui-notification-boundary'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { StarFill as IconStarFill } from 'react-bootstrap-icons'
import { Star as IconStar } from 'react-bootstrap-icons'
import { X as IconX } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export interface AliasesListEntryProps { export interface AliasesListEntryProps {
@ -50,7 +53,7 @@ export const AliasesListEntry: React.FC<AliasesListEntryProps> = ({ alias }) =>
disabled={true} disabled={true}
title={t('editor.modal.aliases.isPrimary') ?? undefined} title={t('editor.modal.aliases.isPrimary') ?? undefined}
{...testId('aliasIsPrimary')}> {...testId('aliasIsPrimary')}>
<ForkAwesomeIcon icon={'star'} /> <UiIcon icon={IconStar} />
</Button> </Button>
</ShowIf> </ShowIf>
<ShowIf condition={!alias.primaryAlias}> <ShowIf condition={!alias.primaryAlias}>
@ -60,7 +63,7 @@ export const AliasesListEntry: React.FC<AliasesListEntryProps> = ({ alias }) =>
title={t('editor.modal.aliases.makePrimary') ?? undefined} title={t('editor.modal.aliases.makePrimary') ?? undefined}
onClick={onMakePrimaryClick} onClick={onMakePrimaryClick}
{...testId('aliasButtonMakePrimary')}> {...testId('aliasButtonMakePrimary')}>
<ForkAwesomeIcon icon={'star-o'} /> <UiIcon icon={IconStarFill} />
</Button> </Button>
</ShowIf> </ShowIf>
<Button <Button
@ -69,7 +72,7 @@ export const AliasesListEntry: React.FC<AliasesListEntryProps> = ({ alias }) =>
title={t('editor.modal.aliases.removeAlias') ?? undefined} title={t('editor.modal.aliases.removeAlias') ?? undefined}
onClick={onRemoveClick} onClick={onRemoveClick}
{...testId('aliasButtonRemove')}> {...testId('aliasButtonRemove')}>
<ForkAwesomeIcon icon={'times'} /> <UiIcon icon={IconX} />
</Button> </Button>
</div> </div>
</li> </li>

View file

@ -7,6 +7,7 @@ import { useApplicationState } from '../../../../hooks/common/use-application-st
import { NoteInfoLine } from './note-info-line' import { NoteInfoLine } from './note-info-line'
import { UnitalicBoldContent } from './unitalic-bold-content' import { UnitalicBoldContent } from './unitalic-bold-content'
import React from 'react' import React from 'react'
import { People as IconPeople } from 'react-bootstrap-icons'
import { Trans } from 'react-i18next' import { Trans } from 'react-i18next'
/** /**
@ -16,7 +17,7 @@ export const NoteInfoLineContributors: React.FC = () => {
const contributors = useApplicationState((state) => state.noteDetails.editedBy.length) const contributors = useApplicationState((state) => state.noteDetails.editedBy.length)
return ( return (
<NoteInfoLine icon={'users'} size={'2x'}> <NoteInfoLine icon={IconPeople} size={2}>
<Trans i18nKey={'editor.modal.documentInfo.usersContributed'}> <Trans i18nKey={'editor.modal.documentInfo.usersContributed'}>
<UnitalicBoldContent text={contributors} /> <UnitalicBoldContent text={contributors} />
</Trans> </Trans>

View file

@ -9,6 +9,7 @@ import type { NoteInfoTimeLineProps } from './note-info-time-line'
import { UnitalicBoldTimeFromNow } from './utils/unitalic-bold-time-from-now' import { UnitalicBoldTimeFromNow } from './utils/unitalic-bold-time-from-now'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Plus as IconPlus } from 'react-bootstrap-icons'
import { Trans } from 'react-i18next' import { Trans } from 'react-i18next'
/** /**
@ -21,7 +22,7 @@ export const NoteInfoLineCreated: React.FC<NoteInfoTimeLineProps> = ({ size }) =
const noteCreateDateTime = useMemo(() => DateTime.fromSeconds(noteCreateTime), [noteCreateTime]) const noteCreateDateTime = useMemo(() => DateTime.fromSeconds(noteCreateTime), [noteCreateTime])
return ( return (
<NoteInfoLine icon={'plus'} size={size}> <NoteInfoLine icon={IconPlus} size={size}>
<Trans i18nKey={'editor.modal.documentInfo.created'}> <Trans i18nKey={'editor.modal.documentInfo.created'}>
<UnitalicBoldTimeFromNow time={noteCreateDateTime} /> <UnitalicBoldTimeFromNow time={noteCreateDateTime} />
</Trans> </Trans>

View file

@ -11,6 +11,7 @@ import { UnitalicBoldTimeFromNow } from './utils/unitalic-bold-time-from-now'
import { UnitalicBoldTrans } from './utils/unitalic-bold-trans' import { UnitalicBoldTrans } from './utils/unitalic-bold-trans'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Pencil as IconPencil } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
/** /**
@ -38,7 +39,7 @@ export const NoteInfoLineUpdated: React.FC<NoteInfoTimeLineProps> = ({ size }) =
}, [noteUpdateUser, size]) }, [noteUpdateUser, size])
return ( return (
<NoteInfoLine icon={'pencil'} size={size}> <NoteInfoLine icon={IconPencil} size={size}>
<Trans i18nKey={'editor.modal.documentInfo.edited'}> <Trans i18nKey={'editor.modal.documentInfo.edited'}>
{userBlock} {userBlock}
<UnitalicBoldTimeFromNow time={noteUpdateDateTime} /> <UnitalicBoldTimeFromNow time={noteUpdateDateTime} />

View file

@ -14,6 +14,7 @@ import { NoteInfoLine } from './note-info-line'
import { UnitalicBoldContent } from './unitalic-bold-content' import { UnitalicBoldContent } from './unitalic-bold-content'
import type { PropsWithChildren } from 'react' import type { PropsWithChildren } from 'react'
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import { AlignStart as IconAlignStart } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
/** /**
@ -38,7 +39,7 @@ export const NoteInfoLineWordCount: React.FC<PropsWithChildren<unknown>> = () =>
}, [editorToRendererCommunicator, rendererReady]) }, [editorToRendererCommunicator, rendererReady])
return ( return (
<NoteInfoLine icon={'align-left'} size={'2x'}> <NoteInfoLine icon={IconAlignStart} size={2}>
<ShowIf condition={wordCount === null}> <ShowIf condition={wordCount === null}>
<Trans i18nKey={'common.loading'} /> <Trans i18nKey={'common.loading'} />
</ShowIf> </ShowIf>

View file

@ -3,14 +3,14 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import type { IconName } from '../../../common/fork-awesome/types'
import type { PropsWithChildren } from 'react' import type { PropsWithChildren } from 'react'
import React from 'react' import React from 'react'
import type { Icon } from 'react-bootstrap-icons'
export interface NoteInfoLineProps { export interface NoteInfoLineProps {
icon: IconName icon: Icon
size?: '2x' | '3x' | '4x' | '5x' | undefined size?: 2 | 3 | 4 | 5 | undefined
} }
/** /**
@ -24,7 +24,7 @@ export interface NoteInfoLineProps {
export const NoteInfoLine: React.FC<PropsWithChildren<NoteInfoLineProps>> = ({ icon, size, children }) => { export const NoteInfoLine: React.FC<PropsWithChildren<NoteInfoLineProps>> = ({ icon, size, children }) => {
return ( return (
<span className={'d-flex align-items-center'}> <span className={'d-flex align-items-center'}>
<ForkAwesomeIcon icon={icon} size={size} fixedWidth={true} className={'mx-2'} /> <UiIcon icon={icon} size={size} className={'mx-2'} />
<i className={'d-flex align-items-center'}>{children}</i> <i className={'d-flex align-items-center'}>{children}</i>
</span> </span>
) )

View file

@ -33,10 +33,10 @@ export const NoteInfoModal: React.FC<ModalVisibilityProps> = ({ show, onHide })
<Modal.Body> <Modal.Body>
<ListGroup> <ListGroup>
<ListGroup.Item> <ListGroup.Item>
<NoteInfoLineCreated size={'2x'} /> <NoteInfoLineCreated size={2} />
</ListGroup.Item> </ListGroup.Item>
<ListGroup.Item> <ListGroup.Item>
<NoteInfoLineUpdated size={'2x'} /> <NoteInfoLineUpdated size={2} />
</ListGroup.Item> </ListGroup.Item>
<ListGroup.Item> <ListGroup.Item>
<NoteInfoLineContributors /> <NoteInfoLineContributors />

View file

@ -4,5 +4,5 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
export interface NoteInfoTimeLineProps { export interface NoteInfoTimeLineProps {
size?: '2x' | '3x' | '4x' | '5x' size?: 2 | 3 | 4 | 5
} }

View file

@ -4,9 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useOnInputChange } from '../../../../hooks/common/use-on-input-change' import { useOnInputChange } from '../../../../hooks/common/use-on-input-change'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import React, { useCallback, useState } from 'react' import React, { useCallback, useState } from 'react'
import { Button, FormControl, InputGroup } from 'react-bootstrap' import { Button, FormControl, InputGroup } from 'react-bootstrap'
import { Plus as IconPlus } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export interface PermissionAddEntryFieldProps { export interface PermissionAddEntryFieldProps {
@ -35,7 +36,7 @@ export const PermissionAddEntryField: React.FC<PermissionAddEntryFieldProps> = (
<InputGroup className={'me-1 mb-1'}> <InputGroup className={'me-1 mb-1'}>
<FormControl value={newEntryIdentifier} placeholder={t(i18nKey) ?? undefined} onChange={onChange} /> <FormControl value={newEntryIdentifier} placeholder={t(i18nKey) ?? undefined} onChange={onChange} />
<Button variant='light' className={'text-secondary ms-2'} title={t(i18nKey) ?? undefined} onClick={onSubmit}> <Button variant='light' className={'text-secondary ms-2'} title={t(i18nKey) ?? undefined} onClick={onSubmit}>
<ForkAwesomeIcon icon={'plus'} /> <UiIcon icon={IconPlus} />
</Button> </Button>
</InputGroup> </InputGroup>
</li> </li>

View file

@ -3,10 +3,13 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import { AccessLevel } from './types' import { AccessLevel } from './types'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Button, ToggleButtonGroup } from 'react-bootstrap' import { Button, ToggleButtonGroup } from 'react-bootstrap'
import { Eye as IconEye } from 'react-bootstrap-icons'
import { Pencil as IconPencil } from 'react-bootstrap-icons'
import { X as IconX } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
interface PermissionEntryButtonI18nKeys { interface PermissionEntryButtonI18nKeys {
@ -73,20 +76,20 @@ export const PermissionEntryButtons: React.FC<PermissionEntryButtonsProps> = ({
className={'text-danger me-2'} className={'text-danger me-2'}
title={t(i18nKeys.remove, { name }) ?? undefined} title={t(i18nKeys.remove, { name }) ?? undefined}
onClick={onRemove}> onClick={onRemove}>
<ForkAwesomeIcon icon={'times'} /> <UiIcon icon={IconX} />
</Button> </Button>
<ToggleButtonGroup type='radio' name='edit-mode' value={currentSetting}> <ToggleButtonGroup type='radio' name='edit-mode' value={currentSetting}>
<Button <Button
title={t(i18nKeys.setReadOnly, { name }) ?? undefined} title={t(i18nKeys.setReadOnly, { name }) ?? undefined}
variant={currentSetting === AccessLevel.READ_ONLY ? 'secondary' : 'outline-secondary'} variant={currentSetting === AccessLevel.READ_ONLY ? 'secondary' : 'outline-secondary'}
onClick={onSetReadOnly}> onClick={onSetReadOnly}>
<ForkAwesomeIcon icon='eye' /> <UiIcon icon={IconEye} />
</Button> </Button>
<Button <Button
title={t(i18nKeys.setWriteable, { name }) ?? undefined} title={t(i18nKeys.setWriteable, { name }) ?? undefined}
variant={currentSetting === AccessLevel.WRITEABLE ? 'secondary' : 'outline-secondary'} variant={currentSetting === AccessLevel.WRITEABLE ? 'secondary' : 'outline-secondary'}
onClick={onSetWriteable}> onClick={onSetWriteable}>
<ForkAwesomeIcon icon='pencil' /> <UiIcon icon={IconPencil} />
</Button> </Button>
</ToggleButtonGroup> </ToggleButtonGroup>
</div> </div>

View file

@ -6,11 +6,14 @@
import { removeGroupPermission, setGroupPermission } from '../../../../api/permissions' import { removeGroupPermission, setGroupPermission } from '../../../../api/permissions'
import { useApplicationState } from '../../../../hooks/common/use-application-state' import { useApplicationState } from '../../../../hooks/common/use-application-state'
import { setNotePermissionsFromServer } from '../../../../redux/note-details/methods' import { setNotePermissionsFromServer } from '../../../../redux/note-details/methods'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { IconButton } from '../../../common/icon-button/icon-button'
import { useUiNotifications } from '../../../notifications/ui-notification-boundary' import { useUiNotifications } from '../../../notifications/ui-notification-boundary'
import { AccessLevel, SpecialGroup } from './types' import { AccessLevel, SpecialGroup } from './types'
import React, { useCallback, useMemo } from 'react' import React, { useCallback, useMemo } from 'react'
import { Button, ToggleButtonGroup } from 'react-bootstrap' import { ToggleButtonGroup } from 'react-bootstrap'
import { Eye as IconEye } from 'react-bootstrap-icons'
import { Pencil as IconPencil } from 'react-bootstrap-icons'
import { SlashCircle as IconSlashCircle } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export interface PermissionEntrySpecialGroupProps { export interface PermissionEntrySpecialGroupProps {
@ -67,24 +70,27 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
<span>{name}</span> <span>{name}</span>
<div> <div>
<ToggleButtonGroup type='radio' name='edit-mode'> <ToggleButtonGroup type='radio' name='edit-mode'>
<Button <IconButton
icon={IconSlashCircle}
title={t('editor.modal.permissions.denyGroup', { name }) ?? undefined} title={t('editor.modal.permissions.denyGroup', { name }) ?? undefined}
variant={level === AccessLevel.NONE ? 'secondary' : 'outline-secondary'} variant={level === AccessLevel.NONE ? 'secondary' : 'outline-secondary'}
onClick={onSetEntryDenied}> onClick={onSetEntryDenied}
<ForkAwesomeIcon icon={'ban'} /> className={'p-1'}
</Button> />
<Button <IconButton
icon={IconEye}
title={t('editor.modal.permissions.viewOnlyGroup', { name }) ?? undefined} title={t('editor.modal.permissions.viewOnlyGroup', { name }) ?? undefined}
variant={level === AccessLevel.READ_ONLY ? 'secondary' : 'outline-secondary'} variant={level === AccessLevel.READ_ONLY ? 'secondary' : 'outline-secondary'}
onClick={onSetEntryReadOnly}> onClick={onSetEntryReadOnly}
<ForkAwesomeIcon icon={'eye'} /> className={'p-1'}
</Button> />
<Button <IconButton
icon={IconPencil}
title={t('editor.modal.permissions.editGroup', { name }) ?? undefined} title={t('editor.modal.permissions.editGroup', { name }) ?? undefined}
variant={level === AccessLevel.WRITEABLE ? 'secondary' : 'outline-secondary'} variant={level === AccessLevel.WRITEABLE ? 'secondary' : 'outline-secondary'}
onClick={() => onSetEntryWriteable}> onClick={onSetEntryWriteable}
<ForkAwesomeIcon icon={'pencil'} /> className={'p-1'}
</Button> />
</ToggleButtonGroup> </ToggleButtonGroup>
</div> </div>
</li> </li>

View file

@ -4,9 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useOnInputChange } from '../../../../hooks/common/use-on-input-change' import { useOnInputChange } from '../../../../hooks/common/use-on-input-change'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import React, { useCallback, useMemo, useState } from 'react' import React, { useCallback, useMemo, useState } from 'react'
import { Button, FormControl, InputGroup } from 'react-bootstrap' import { Button, FormControl, InputGroup } from 'react-bootstrap'
import { Check as IconCheck } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export interface PermissionOwnerChangeProps { export interface PermissionOwnerChangeProps {
@ -44,7 +45,7 @@ export const PermissionOwnerChange: React.FC<PermissionOwnerChangeProps> = ({ on
onClick={onClickConfirm} onClick={onClickConfirm}
className={'ms-2'} className={'ms-2'}
disabled={confirmButtonDisabled}> disabled={confirmButtonDisabled}>
<ForkAwesomeIcon icon={'check'} /> <UiIcon icon={IconCheck} />
</Button> </Button>
</InputGroup> </InputGroup>
) )

View file

@ -4,10 +4,11 @@
* 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 { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import { UserAvatarForUsername } from '../../../common/user-avatar/user-avatar-for-username' import { UserAvatarForUsername } from '../../../common/user-avatar/user-avatar-for-username'
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { Pencil as IconPencil } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export interface PermissionOwnerInfoProps { export interface PermissionOwnerInfoProps {
@ -30,7 +31,7 @@ export const PermissionOwnerInfo: React.FC<PermissionOwnerInfoProps> = ({ onEdit
variant='light' variant='light'
title={t('editor.modal.permissions.ownerChange.button') ?? undefined} title={t('editor.modal.permissions.ownerChange.button') ?? undefined}
onClick={onEditOwner}> onClick={onEditOwner}>
<ForkAwesomeIcon icon={'pencil'} /> <UiIcon icon={IconPencil} />
</Button> </Button>
</Fragment> </Fragment>
) )

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { RevisionMetadata } from '../../../../api/revisions/types' import type { RevisionMetadata } from '../../../../api/revisions/types'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import { ShowIf } from '../../../common/show-if/show-if' import { ShowIf } from '../../../common/show-if/show-if'
import { UserAvatar } from '../../../common/user-avatar/user-avatar' import { UserAvatar } from '../../../common/user-avatar/user-avatar'
import { WaitSpinner } from '../../../common/wait-spinner/wait-spinner' import { WaitSpinner } from '../../../common/wait-spinner/wait-spinner'
@ -14,6 +14,9 @@ import { getUserDataForRevision } from './utils'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { ListGroup } from 'react-bootstrap' import { ListGroup } from 'react-bootstrap'
import { Clock as IconClock } from 'react-bootstrap-icons'
import { FileText as IconFileText } from 'react-bootstrap-icons'
import { Person as IconPerson } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
import { useAsync } from 'react-use' import { useAsync } from 'react-use'
@ -57,15 +60,15 @@ export const RevisionListEntry: React.FC<RevisionListEntryProps> = ({ active, on
action action
className={`${styles['revision-item']} d-flex flex-column`}> className={`${styles['revision-item']} d-flex flex-column`}>
<span> <span>
<ForkAwesomeIcon icon={'clock-o'} className='mx-2' /> <UiIcon icon={IconClock} className='mx-2' />
{revisionCreationTime} {revisionCreationTime}
</span> </span>
<span> <span>
<ForkAwesomeIcon icon={'file-text-o'} className='mx-2' /> <UiIcon icon={IconFileText} className='mx-2' />
<Trans i18nKey={'editor.modal.revision.length'} />: {revision.length} <Trans i18nKey={'editor.modal.revision.length'} />: {revision.length}
</span> </span>
<span className={'d-flex flex-row my-1 align-items-center'}> <span className={'d-flex flex-row my-1 align-items-center'}>
<ForkAwesomeIcon icon={'user-o'} className={'mx-2'} /> <UiIcon icon={IconPerson} className={'mx-2'} />
<ShowIf condition={revisionAuthors.loading}> <ShowIf condition={revisionAuthors.loading}>
<WaitSpinner /> <WaitSpinner />
</ShowIf> </ShowIf>

View file

@ -11,6 +11,7 @@ import styles from './revision-modal.module.scss'
import { RevisionViewer } from './revision-viewer' import { RevisionViewer } from './revision-viewer'
import React, { useState } from 'react' import React, { useState } from 'react'
import { Col, Modal, Row } from 'react-bootstrap' import { Col, Modal, Row } from 'react-bootstrap'
import { ClockHistory as IconClockHistory } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
/** /**
@ -28,7 +29,7 @@ export const RevisionModal: React.FC<ModalVisibilityProps> = ({ show, onHide })
show={show} show={show}
onHide={onHide} onHide={onHide}
titleI18nKey={'editor.modal.revision.title'} titleI18nKey={'editor.modal.revision.title'}
titleIcon={'history'} titleIcon={IconClockHistory}
showCloseButton={true} showCloseButton={true}
modalSize={'xl'} modalSize={'xl'}
additionalClasses={styles['revision-modal']}> additionalClasses={styles['revision-modal']}>

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { TypeBold as IconTypeBold } from 'react-bootstrap-icons'
/** /**
* Renders a button to make the selection in the {@link Editor editor} bold. * Renders a button to make the selection in the {@link Editor editor} bold.
@ -15,5 +16,5 @@ export const BoldButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '**', '**') return wrapSelection(currentSelection, '**', '**')
}, []) }, [])
return <ToolbarButton i18nKey={'bold'} iconName={'bold'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'bold'} icon={IconTypeBold} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { CheckSquare as IconCheckSquare } from 'react-bootstrap-icons'
/** /**
* Renders a button to create a checklist in the {@link Editor editor}. * Renders a button to create a checklist in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const CheckListButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(markdownContent, currentSelection, () => `- [ ] `) return prependLinesOfSelection(markdownContent, currentSelection, () => `- [ ] `)
}, []) }, [])
return <ToolbarButton i18nKey={'checkList'} iconName={'check-square'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'checkList'} icon={IconCheckSquare} formatter={formatter}></ToolbarButton>
} }

View file

@ -8,6 +8,7 @@ import { changeCursorsToWholeLineIfNoToCursor } from '../formatters/utils/change
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Code as IconCode } from 'react-bootstrap-icons'
/** /**
* Renders a button to create a code fence in the {@link Editor editor}. * Renders a button to create a code fence in the {@link Editor editor}.
@ -16,5 +17,5 @@ export const CodeFenceButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return wrapSelection(changeCursorsToWholeLineIfNoToCursor(markdownContent, currentSelection), '```\n', '\n```') return wrapSelection(changeCursorsToWholeLineIfNoToCursor(markdownContent, currentSelection), '```\n', '\n```')
}, []) }, [])
return <ToolbarButton i18nKey={'code'} iconName={'code'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'code'} icon={IconCode} formatter={formatter}></ToolbarButton>
} }

View file

@ -8,6 +8,7 @@ import { changeCursorsToWholeLineIfNoToCursor } from '../formatters/utils/change
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { ArrowsCollapse as IconArrowsCollapse } from 'react-bootstrap-icons'
/** /**
* Renders a button to create a spoiler section in the {@link Editor editor}. * Renders a button to create a spoiler section in the {@link Editor editor}.
@ -20,7 +21,5 @@ export const CollapsibleBlockButton: React.FC = () => {
'\n:::\n' '\n:::\n'
) )
}, []) }, [])
return ( return <ToolbarButton i18nKey={'collapsibleBlock'} icon={IconArrowsCollapse} formatter={formatter}></ToolbarButton>
<ToolbarButton i18nKey={'collapsibleBlock'} iconName={'caret-square-o-down'} formatter={formatter}></ToolbarButton>
)
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { replaceSelection } from '../formatters/replace-selection' import { replaceSelection } from '../formatters/replace-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { ChatDots as IconChatDots } from 'react-bootstrap-icons'
/** /**
* Renders a button to create a comment in the {@link Editor editor}. * Renders a button to create a comment in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const CommentButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return replaceSelection({ from: currentSelection.to ?? currentSelection.from }, '> []', true) return replaceSelection({ from: currentSelection.to ?? currentSelection.from }, '> []', true)
}, []) }, [])
return <ToolbarButton i18nKey={'comment'} iconName={'comment'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'comment'} icon={IconChatDots} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { TypeH1 as IconTypeH1 } from 'react-bootstrap-icons'
/** /**
* Renders a button to add a header in the {@link Editor editor}. * Renders a button to add a header in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const HeaderLevelButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(markdownContent, currentSelection, (line) => (line.startsWith('#') ? `#` : `# `)) return prependLinesOfSelection(markdownContent, currentSelection, (line) => (line.startsWith('#') ? `#` : `# `))
}, []) }, [])
return <ToolbarButton i18nKey={'header'} iconName={'header'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'header'} icon={IconTypeH1} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Eraser as IconEraser } from 'react-bootstrap-icons'
/** /**
* Renders a button that highlights the selection in the {@link Editor editor}. * Renders a button that highlights the selection in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const HighlightButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '==', '==') return wrapSelection(currentSelection, '==', '==')
}, []) }, [])
return <ToolbarButton i18nKey={'highlight'} iconName={'eraser'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'highlight'} icon={IconEraser} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { replaceSelection } from '../formatters/replace-selection' import { replaceSelection } from '../formatters/replace-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { DashLg as IconDashLg } from 'react-bootstrap-icons'
/** /**
* Renders a button to insert a horizontal line in the {@link Editor editor}. * Renders a button to insert a horizontal line in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const HorizontalLineButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return replaceSelection({ from: currentSelection.to ?? currentSelection.from }, '----\n', true) return replaceSelection({ from: currentSelection.to ?? currentSelection.from }, '----\n', true)
}, []) }, [])
return <ToolbarButton i18nKey={'horizontalLine'} iconName={'minus'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'horizontalLine'} icon={IconDashLg} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { addLink } from '../formatters/add-link' import { addLink } from '../formatters/add-link'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Image as IconImage } from 'react-bootstrap-icons'
/** /**
* Renders a button to insert an image in the {@link Editor editor}. * Renders a button to insert an image in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const ImageLinkButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return addLink(markdownContent, currentSelection, '!') return addLink(markdownContent, currentSelection, '!')
}, []) }, [])
return <ToolbarButton i18nKey={'imageLink'} iconName={'picture-o'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'imageLink'} icon={IconImage} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { TypeItalic as IconTypeItalic } from 'react-bootstrap-icons'
/** /**
* Renders a button to make the selection in the {@link Editor editor} italic. * Renders a button to make the selection in the {@link Editor editor} italic.
@ -15,5 +16,5 @@ export const ItalicButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '*', '*') return wrapSelection(currentSelection, '*', '*')
}, []) }, [])
return <ToolbarButton i18nKey={'italic'} iconName={'italic'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'italic'} icon={IconTypeItalic} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { addLink } from '../formatters/add-link' import { addLink } from '../formatters/add-link'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Link as IconLink } from 'react-bootstrap-icons'
/** /**
* Renders a button to insert a link in the {@link Editor editor}. * Renders a button to insert a link in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const LinkButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return addLink(markdownContent, currentSelection) return addLink(markdownContent, currentSelection)
}, []) }, [])
return <ToolbarButton i18nKey={'link'} iconName={'link'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'link'} icon={IconLink} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { ListOl as IconListOl } from 'react-bootstrap-icons'
/** /**
* Renders a button to insert an ordered list in the {@link Editor editor}. * Renders a button to insert an ordered list in the {@link Editor editor}.
@ -19,5 +20,5 @@ export const OrderedListButton: React.FC = () => {
(line, lineIndexInBlock) => `${lineIndexInBlock + 1}. ` (line, lineIndexInBlock) => `${lineIndexInBlock + 1}. `
) )
}, []) }, [])
return <ToolbarButton i18nKey={'orderedList'} iconName={'list-ol'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'orderedList'} icon={IconListOl} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Quote as IconQuote } from 'react-bootstrap-icons'
/** /**
* Renders a button to insert a quotation in the {@link Editor editor}. * Renders a button to insert a quotation in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const QuotesButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(markdownContent, currentSelection, () => `> `) return prependLinesOfSelection(markdownContent, currentSelection, () => `> `)
}, []) }, [])
return <ToolbarButton i18nKey={'blockquote'} iconName={'quote-right'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'blockquote'} icon={IconQuote} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { TypeStrikethrough as IconTypeStrikethrough } from 'react-bootstrap-icons'
/** /**
* Renders a button to strike through the selection in the {@link Editor editor}. * Renders a button to strike through the selection in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const StrikethroughButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '~~', '~~') return wrapSelection(currentSelection, '~~', '~~')
}, []) }, [])
return <ToolbarButton i18nKey={'strikethrough'} iconName={'strikethrough'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'strikethrough'} icon={IconTypeStrikethrough} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Subscript as IconSubscript } from 'react-bootstrap-icons'
/** /**
* Renders a button to format the selection in the {@link Editor editor} as subscript. * Renders a button to format the selection in the {@link Editor editor} as subscript.
@ -15,5 +16,5 @@ export const SubscriptButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '~', '~') return wrapSelection(currentSelection, '~', '~')
}, []) }, [])
return <ToolbarButton i18nKey={'subscript'} iconName={'subscript'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'subscript'} icon={IconSubscript} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Superscript as IconSuperscript } from 'react-bootstrap-icons'
/** /**
* Renders a button to format the selection in the {@link Editor editor} as superscript. * Renders a button to format the selection in the {@link Editor editor} as superscript.
@ -15,5 +16,5 @@ export const SuperscriptButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '^', '^') return wrapSelection(currentSelection, '^', '^')
}, []) }, [])
return <ToolbarButton i18nKey={'superscript'} iconName={'superscript'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'superscript'} icon={IconSuperscript} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { wrapSelection } from '../formatters/wrap-selection' import { wrapSelection } from '../formatters/wrap-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { TypeUnderline as IconTypeUnderline } from 'react-bootstrap-icons'
/** /**
* Renders a button to underline the selection in the {@link Editor editor}. * Renders a button to underline the selection in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const UnderlineButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection }) => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => {
return wrapSelection(currentSelection, '++', '++') return wrapSelection(currentSelection, '++', '++')
}, []) }, [])
return <ToolbarButton i18nKey={'underline'} iconName={'underline'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'underline'} icon={IconTypeUnderline} formatter={formatter}></ToolbarButton>
} }

View file

@ -7,6 +7,7 @@ import type { ContentFormatter } from '../../../change-content-context/change-co
import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection'
import { ToolbarButton } from '../toolbar-button' import { ToolbarButton } from '../toolbar-button'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { List as IconList } from 'react-bootstrap-icons'
/** /**
* Renders a button to insert an unordered list in the {@link Editor editor}. * Renders a button to insert an unordered list in the {@link Editor editor}.
@ -15,5 +16,5 @@ export const UnorderedListButton: React.FC = () => {
const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => {
return prependLinesOfSelection(markdownContent, currentSelection, () => `- `) return prependLinesOfSelection(markdownContent, currentSelection, () => `- `)
}, []) }, [])
return <ToolbarButton i18nKey={'unorderedList'} iconName={'list'} formatter={formatter}></ToolbarButton> return <ToolbarButton i18nKey={'unorderedList'} icon={IconList} formatter={formatter}></ToolbarButton>
} }

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { cypressId } from '../../../../../utils/cypress-attribute' import { cypressId } from '../../../../../utils/cypress-attribute'
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../../common/icons/ui-icon'
import { useChangeEditorContentCallback } from '../../../change-content-context/use-change-editor-content-callback' import { useChangeEditorContentCallback } from '../../../change-content-context/use-change-editor-content-callback'
import { replaceSelection } from '../formatters/replace-selection' import { replaceSelection } from '../formatters/replace-selection'
import { EmojiPickerPopover } from './emoji-picker-popover' import { EmojiPickerPopover } from './emoji-picker-popover'
@ -13,6 +13,7 @@ import { extractEmojiShortCode } from './extract-emoji-short-code'
import type { EmojiClickEventDetail } from 'emoji-picker-element/shared' import type { EmojiClickEventDetail } from 'emoji-picker-element/shared'
import React, { Fragment, useCallback, useRef, useState } from 'react' import React, { Fragment, useCallback, useRef, useState } from 'react'
import { Button, Overlay } from 'react-bootstrap' import { Button, Overlay } from 'react-bootstrap'
import { EmojiSmile as IconEmojiSmile } from 'react-bootstrap-icons'
import type { OverlayInjectedProps } from 'react-bootstrap/Overlay' import type { OverlayInjectedProps } from 'react-bootstrap/Overlay'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -63,7 +64,7 @@ export const EmojiPickerButton: React.FC = () => {
title={t('editor.editorToolbar.emoji') ?? undefined} title={t('editor.editorToolbar.emoji') ?? undefined}
disabled={!changeEditorContent} disabled={!changeEditorContent}
ref={buttonRef}> ref={buttonRef}>
<ForkAwesomeIcon icon='smile-o' /> <UiIcon icon={IconEmojiSmile} />
</Button> </Button>
</Fragment> </Fragment>
) )

View file

@ -1,30 +1,21 @@
/* /*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import fontStyles from '../../../../../../global-styles/variables.module.scss' import fontStyles from '../../../../../../global-styles/variables.module.scss'
import { useDarkModeState } from '../../../../../hooks/common/use-dark-mode-state' import { useDarkModeState } from '../../../../../hooks/common/use-dark-mode-state'
import { ForkAwesomeIcons } from '../../../../common/fork-awesome/fork-awesome-icons'
import styles from './emoji-picker.module.scss' import styles from './emoji-picker.module.scss'
import forkawesomeIcon from './forkawesome.png'
import { Picker } from 'emoji-picker-element' import { Picker } from 'emoji-picker-element'
import type { CustomEmoji, EmojiClickEvent, EmojiClickEventDetail } from 'emoji-picker-element/shared' import type { EmojiClickEvent, EmojiClickEventDetail } from 'emoji-picker-element/shared'
import type { PickerConstructorOptions } from 'emoji-picker-element/shared'
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import { Popover } from 'react-bootstrap' import { Popover } from 'react-bootstrap'
import type { PopoverProps } from 'react-bootstrap/Popover' import type { PopoverProps } from 'react-bootstrap/Popover'
const customEmojis: CustomEmoji[] = ForkAwesomeIcons.map((name) => ({
name: `fa-${name}`,
shortcodes: [`fa-${name.toLowerCase()}`],
url: forkawesomeIcon.src,
category: 'ForkAwesome'
}))
const EMOJI_DATA_PATH = '_next/static/js/emoji-data.json' const EMOJI_DATA_PATH = '_next/static/js/emoji-data.json'
const emojiPickerConfig = { const emojiPickerConfig: PickerConstructorOptions = {
customEmoji: customEmojis,
dataSource: EMOJI_DATA_PATH dataSource: EMOJI_DATA_PATH
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View file

@ -1,3 +0,0 @@
SPDX-FileCopyrightText: 2018 Dave Gandy & Fork Awesome
SPDX-License-Identifier: OFL-1.1

View file

@ -4,12 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { cypressId } from '../../../../../utils/cypress-attribute' import { cypressId } from '../../../../../utils/cypress-attribute'
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../../common/icons/ui-icon'
import { CommonModal } from '../../../../common/modals/common-modal' import { CommonModal } from '../../../../common/modals/common-modal'
import type { TableSize } from './table-size-picker-popover' import type { TableSize } from './table-size-picker-popover'
import type { ChangeEvent } from 'react' import type { ChangeEvent } from 'react'
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import { Button, Form, ModalFooter } from 'react-bootstrap' import { Button, Form, ModalFooter } from 'react-bootstrap'
import { Table as IconTable } from 'react-bootstrap-icons'
import { X as IconX } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
export interface CustomTableSizeModalProps { export interface CustomTableSizeModalProps {
@ -67,7 +69,7 @@ export const CustomTableSizeModal: React.FC<CustomTableSizeModalProps> = ({ show
onHide={onDismiss} onHide={onDismiss}
titleI18nKey={'editor.editorToolbar.table.customSize'} titleI18nKey={'editor.editorToolbar.table.customSize'}
showCloseButton={true} showCloseButton={true}
titleIcon={'table'} titleIcon={IconTable}
{...cypressId('custom-table-size-modal')}> {...cypressId('custom-table-size-modal')}>
<div className={'col-lg-10 d-flex flex-row p-3 align-items-center'}> <div className={'col-lg-10 d-flex flex-row p-3 align-items-center'}>
<Form.Control <Form.Control
@ -77,7 +79,7 @@ export const CustomTableSizeModal: React.FC<CustomTableSizeModalProps> = ({ show
isInvalid={tableSize.columns <= 0} isInvalid={tableSize.columns <= 0}
onChange={onColChange} onChange={onColChange}
/> />
<ForkAwesomeIcon icon='times' className='mx-2' fixedWidth={true} /> <UiIcon icon={IconX} className='mx-2' />
<Form.Control <Form.Control
type={'number'} type={'number'}
min={1} min={1}

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { cypressId } from '../../../../../utils/cypress-attribute' import { cypressId } from '../../../../../utils/cypress-attribute'
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../../common/icons/ui-icon'
import { useChangeEditorContentCallback } from '../../../change-content-context/use-change-editor-content-callback' import { useChangeEditorContentCallback } from '../../../change-content-context/use-change-editor-content-callback'
import { replaceSelection } from '../formatters/replace-selection' import { replaceSelection } from '../formatters/replace-selection'
import { createMarkdownTable } from './create-markdown-table' import { createMarkdownTable } from './create-markdown-table'
@ -13,6 +13,7 @@ import './table-picker.module.scss'
import { TableSizePickerPopover } from './table-size-picker-popover' import { TableSizePickerPopover } from './table-size-picker-popover'
import React, { Fragment, useCallback, useMemo, useRef, useState } from 'react' import React, { Fragment, useCallback, useMemo, useRef, useState } from 'react'
import { Button, Overlay } from 'react-bootstrap' import { Button, Overlay } from 'react-bootstrap'
import { Table as IconTable } from 'react-bootstrap-icons'
import type { OverlayInjectedProps } from 'react-bootstrap/Overlay' import type { OverlayInjectedProps } from 'react-bootstrap/Overlay'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -77,7 +78,7 @@ export const TablePickerButton: React.FC = () => {
title={tableTitle} title={tableTitle}
ref={button} ref={button}
disabled={!changeEditorContent}> disabled={!changeEditorContent}>
<ForkAwesomeIcon icon='table' /> <UiIcon icon={IconTable} />
</Button> </Button>
<Overlay <Overlay
target={button.current} target={button.current}

View file

@ -4,12 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { cypressAttribute, cypressId } from '../../../../../utils/cypress-attribute' import { cypressAttribute, cypressId } from '../../../../../utils/cypress-attribute'
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../../common/icons/ui-icon'
import { createNumberRangeArray } from '../../../../common/number-range/number-range' import { createNumberRangeArray } from '../../../../common/number-range/number-range'
import styles from './table-picker.module.scss' import styles from './table-picker.module.scss'
import { TableSizeText } from './table-size-text' import { TableSizeText } from './table-size-text'
import React, { useCallback, useMemo, useState } from 'react' import React, { useCallback, useMemo, useState } from 'react'
import { Button, Popover } from 'react-bootstrap' import { Button, Popover } from 'react-bootstrap'
import { Table as IconTable } from 'react-bootstrap-icons'
import type { PopoverProps } from 'react-bootstrap/Popover' import type { PopoverProps } from 'react-bootstrap/Popover'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
@ -79,7 +80,7 @@ export const TableSizePickerPopover = React.forwardRef<HTMLDivElement, TableSize
</div> </div>
<div className='d-flex justify-content-center mt-2'> <div className='d-flex justify-content-center mt-2'>
<Button {...cypressId('show-custom-table-modal')} className={'text-center'} onClick={onShowCustomSizeModal}> <Button {...cypressId('show-custom-table-modal')} className={'text-center'} onClick={onShowCustomSizeModal}>
<ForkAwesomeIcon icon='table' /> <UiIcon icon={IconTable} />
&nbsp; &nbsp;
<Trans i18nKey={'editor.editorToolbar.table.customSize'} /> <Trans i18nKey={'editor.editorToolbar.table.customSize'} />
</Button> </Button>

View file

@ -4,17 +4,17 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { cypressId } from '../../../../utils/cypress-attribute' import { cypressId } from '../../../../utils/cypress-attribute'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import type { IconName } from '../../../common/fork-awesome/types'
import type { ContentFormatter } from '../../change-content-context/change-content-context' import type { ContentFormatter } from '../../change-content-context/change-content-context'
import { useChangeEditorContentCallback } from '../../change-content-context/use-change-editor-content-callback' import { useChangeEditorContentCallback } from '../../change-content-context/use-change-editor-content-callback'
import React, { useCallback, useMemo } from 'react' import React, { useCallback, useMemo } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import type { Icon } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export interface ToolbarButtonProps { export interface ToolbarButtonProps {
i18nKey: string i18nKey: string
iconName: IconName icon: Icon
formatter: ContentFormatter formatter: ContentFormatter
} }
@ -25,7 +25,7 @@ export interface ToolbarButtonProps {
* @param iconName A fork awesome icon name that is shown in the button * @param iconName A fork awesome icon name that is shown in the button
* @param formatter The formatter function changes the editor content on click * @param formatter The formatter function changes the editor content on click
*/ */
export const ToolbarButton: React.FC<ToolbarButtonProps> = ({ i18nKey, iconName, formatter }) => { export const ToolbarButton: React.FC<ToolbarButtonProps> = ({ i18nKey, icon, formatter }) => {
const { t } = useTranslation('', { keyPrefix: 'editor.editorToolbar' }) const { t } = useTranslation('', { keyPrefix: 'editor.editorToolbar' })
const changeEditorContent = useChangeEditorContentCallback() const changeEditorContent = useChangeEditorContentCallback()
@ -41,7 +41,7 @@ export const ToolbarButton: React.FC<ToolbarButtonProps> = ({ i18nKey, iconName,
title={title} title={title}
disabled={!changeEditorContent} disabled={!changeEditorContent}
{...cypressId('toolbar.' + i18nKey)}> {...cypressId('toolbar.' + i18nKey)}>
<ForkAwesomeIcon icon={iconName} /> <UiIcon icon={icon} />
</Button> </Button>
) )
} }

View file

@ -5,7 +5,7 @@
*/ */
import { cypressId } from '../../../../../utils/cypress-attribute' import { cypressId } from '../../../../../utils/cypress-attribute'
import { Logger } from '../../../../../utils/logger' import { Logger } from '../../../../../utils/logger'
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon' import { UiIcon } from '../../../../common/icons/ui-icon'
import { ShowIf } from '../../../../common/show-if/show-if' import { ShowIf } from '../../../../common/show-if/show-if'
import { acceptedMimeTypes } from '../../../../common/upload-image-mimetypes' import { acceptedMimeTypes } from '../../../../common/upload-image-mimetypes'
import { useCodeMirrorReference } from '../../../change-content-context/change-content-context' import { useCodeMirrorReference } from '../../../change-content-context/change-content-context'
@ -15,6 +15,7 @@ import { extractSelectedText } from './extract-selected-text'
import { Optional } from '@mrdrogdrog/optional' import { Optional } from '@mrdrogdrog/optional'
import React, { Fragment, useCallback, useRef } from 'react' import React, { Fragment, useCallback, useRef } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { Upload as IconUpload } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
const logger = new Logger('Upload image button') const logger = new Logger('Upload image button')
@ -54,7 +55,7 @@ export const UploadImageButton: React.FC = () => {
disabled={!codeMirror} disabled={!codeMirror}
title={t('editor.editorToolbar.uploadImage') ?? undefined} title={t('editor.editorToolbar.uploadImage') ?? undefined}
{...cypressId('editor-toolbar-upload-image-button')}> {...cypressId('editor-toolbar-upload-image-button')}>
<ForkAwesomeIcon icon={'upload'} /> <UiIcon icon={IconUpload} />
</Button> </Button>
<ShowIf condition={!!codeMirror}> <ShowIf condition={!!codeMirror}>
<UploadInput <UploadInput

View file

@ -15,6 +15,7 @@ import { DeleteNoteModal } from './delete-note-modal'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import type { PropsWithChildren } from 'react' import type { PropsWithChildren } from 'react'
import React, { Fragment, useCallback } from 'react' import React, { Fragment, useCallback } from 'react'
import { Trash as IconTrash } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
const logger = new Logger('note-deletion') const logger = new Logger('note-deletion')
@ -45,7 +46,7 @@ export const DeleteNoteSidebarEntry: React.FC<PropsWithChildren<SpecificSidebarE
<Fragment> <Fragment>
<SidebarButton <SidebarButton
{...cypressId('sidebar.deleteNote.button')} {...cypressId('sidebar.deleteNote.button')}
icon={'trash'} icon={IconTrash}
className={className} className={className}
hide={hide} hide={hide}
onClick={showModal}> onClick={showModal}>

Some files were not shown because too many files have changed in this diff Show more