5htp-core 0.4.8 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -1
- package/src/client/assets/css/components/table.less +2 -0
- package/src/client/components/Form.ts +1 -1
- package/src/client/components/button.tsx +2 -1
- package/src/client/components/containers/Popover/index.tsx +2 -2
- package/src/client/components/dropdown/index.tsx +16 -6
- package/src/client/components/input/Slider/index.tsx +0 -2
- package/src/client/components/inputv3/Rte/Editor.tsx +271 -0
- package/src/client/components/inputv3/Rte/ToolbarPlugin/BlockFormat.tsx +220 -0
- package/src/client/components/inputv3/Rte/ToolbarPlugin/ElementFormat.tsx +107 -0
- package/src/client/components/inputv3/Rte/ToolbarPlugin/index.tsx +768 -0
- package/src/client/components/inputv3/Rte/appSettings.ts +36 -0
- package/src/client/components/inputv3/Rte/context/FlashMessageContext.tsx +68 -0
- package/src/client/components/inputv3/Rte/context/SettingsContext.tsx +71 -0
- package/src/client/components/inputv3/Rte/context/SharedAutocompleteContext.tsx +71 -0
- package/src/client/components/inputv3/Rte/context/SharedHistoryContext.tsx +35 -0
- package/src/client/components/inputv3/Rte/currentEditor.ts +42 -0
- package/src/client/components/inputv3/Rte/hooks/useFlashMessage.tsx +16 -0
- package/src/client/components/inputv3/Rte/hooks/useReport.ts +67 -0
- package/src/client/components/inputv3/Rte/images/emoji/1F600.png +0 -0
- package/src/client/components/inputv3/Rte/images/emoji/1F641.png +0 -0
- package/src/client/components/inputv3/Rte/images/emoji/1F642.png +0 -0
- package/src/client/components/inputv3/Rte/images/emoji/2764.png +0 -0
- package/src/client/components/inputv3/Rte/images/emoji/LICENSE.md +5 -0
- package/src/client/components/inputv3/Rte/images/icons/draggable-block-menu.svg +1 -0
- package/src/client/components/inputv3/Rte/images/icons/prettier-error.svg +1 -0
- package/src/client/components/inputv3/Rte/images/icons/prettier.svg +1 -0
- package/src/client/components/inputv3/Rte/images/image/LICENSE.md +5 -0
- package/src/client/components/inputv3/Rte/images/image-broken.svg +4 -0
- package/src/client/components/inputv3/Rte/images/logo.svg +1 -0
- package/src/client/components/inputv3/Rte/index.tsx +63 -79
- package/src/client/components/inputv3/Rte/nodes/AutocompleteNode.tsx +119 -0
- package/src/client/components/inputv3/Rte/nodes/EmojiNode.tsx +102 -0
- package/src/client/components/inputv3/Rte/nodes/EquationComponent.tsx +141 -0
- package/src/client/components/inputv3/Rte/nodes/EquationNode.tsx +174 -0
- package/src/client/components/inputv3/Rte/nodes/FigmaNode.tsx +135 -0
- package/src/client/components/inputv3/Rte/nodes/ImageComponent.tsx +468 -0
- package/src/client/components/inputv3/Rte/nodes/ImageNode.css +43 -0
- package/src/client/components/inputv3/Rte/nodes/ImageNode.tsx +266 -0
- package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageComponent.tsx +402 -0
- package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageNode.css +94 -0
- package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageNode.tsx +294 -0
- package/src/client/components/inputv3/Rte/nodes/KeywordNode.ts +67 -0
- package/src/client/components/inputv3/Rte/nodes/LayoutContainerNode.ts +137 -0
- package/src/client/components/inputv3/Rte/nodes/LayoutItemNode.ts +71 -0
- package/src/client/components/inputv3/Rte/nodes/MentionNode.ts +130 -0
- package/src/client/components/inputv3/Rte/nodes/PageBreakNode/index.css +62 -0
- package/src/client/components/inputv3/Rte/nodes/PageBreakNode/index.tsx +170 -0
- package/src/client/components/inputv3/Rte/nodes/PlaygroundNodes.ts +76 -0
- package/src/client/components/inputv3/Rte/nodes/PollComponent.tsx +249 -0
- package/src/client/components/inputv3/Rte/nodes/PollNode.css +187 -0
- package/src/client/components/inputv3/Rte/nodes/PollNode.tsx +209 -0
- package/src/client/components/inputv3/Rte/nodes/StickyComponent.tsx +261 -0
- package/src/client/components/inputv3/Rte/nodes/StickyNode.css +37 -0
- package/src/client/components/inputv3/Rte/nodes/StickyNode.tsx +150 -0
- package/src/client/components/inputv3/Rte/nodes/TweetNode.tsx +223 -0
- package/src/client/components/inputv3/Rte/nodes/YouTubeNode.tsx +184 -0
- package/src/client/components/inputv3/Rte/plugins/ActionsPlugin/index.tsx +334 -0
- package/src/client/components/inputv3/Rte/plugins/AutoEmbedPlugin/index.tsx +352 -0
- package/src/client/components/inputv3/Rte/plugins/AutoLinkPlugin/index.tsx +32 -0
- package/src/client/components/inputv3/Rte/plugins/AutocompletePlugin/index.tsx +2529 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/CopyButton/index.tsx +70 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/PrettierButton/index.css +14 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/PrettierButton/index.tsx +156 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/index.css +54 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/index.tsx +190 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/utils.ts +33 -0
- package/src/client/components/inputv3/Rte/plugins/CodeHighlightPlugin/index.ts +21 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/Collapsible.css +57 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleContainerNode.ts +168 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleContentNode.ts +127 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleTitleNode.ts +152 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleUtils.ts +17 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/index.ts +284 -0
- package/src/client/components/inputv3/Rte/plugins/ComponentPickerPlugin/index.tsx +370 -0
- package/src/client/components/inputv3/Rte/plugins/ContextMenuPlugin/index.tsx +270 -0
- package/src/client/components/inputv3/Rte/plugins/DocsPlugin/index.tsx +20 -0
- package/src/client/components/inputv3/Rte/plugins/DragDropPastePlugin/index.ts +51 -0
- package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.css +36 -0
- package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.tsx +43 -0
- package/src/client/components/inputv3/Rte/plugins/EmojiPickerPlugin/index.tsx +198 -0
- package/src/client/components/inputv3/Rte/plugins/EmojisPlugin/index.ts +75 -0
- package/src/client/components/inputv3/Rte/plugins/EquationsPlugin/index.tsx +82 -0
- package/src/client/components/inputv3/Rte/plugins/FigmaPlugin/index.tsx +40 -0
- package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.css +41 -0
- package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.tsx +393 -0
- package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.css +141 -0
- package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.tsx +388 -0
- package/src/client/components/inputv3/Rte/plugins/ImagesPlugin/index.tsx +350 -0
- package/src/client/components/inputv3/Rte/plugins/InlineImagePlugin/index.tsx +336 -0
- package/src/client/components/inputv3/Rte/plugins/KeywordsPlugin/index.ts +56 -0
- package/src/client/components/inputv3/Rte/plugins/LayoutPlugin/InsertLayoutDialog.tsx +58 -0
- package/src/client/components/inputv3/Rte/plugins/LayoutPlugin/LayoutPlugin.tsx +219 -0
- package/src/client/components/inputv3/Rte/plugins/LinkPlugin/index.tsx +34 -0
- package/src/client/components/inputv3/Rte/plugins/ListMaxIndentLevelPlugin/index.ts +85 -0
- package/src/client/components/inputv3/Rte/plugins/MarkdownShortcutPlugin/index.tsx +16 -0
- package/src/client/components/inputv3/Rte/plugins/MarkdownTransformers/index.ts +324 -0
- package/src/client/components/inputv3/Rte/plugins/MaxLengthPlugin/index.tsx +53 -0
- package/src/client/components/inputv3/Rte/plugins/MentionsPlugin/index.tsx +696 -0
- package/src/client/components/inputv3/Rte/plugins/PageBreakPlugin/index.tsx +57 -0
- package/src/client/components/inputv3/Rte/plugins/PasteLogPlugin/index.tsx +54 -0
- package/src/client/components/inputv3/Rte/plugins/PollPlugin/index.tsx +86 -0
- package/src/client/components/inputv3/Rte/plugins/SpeechToTextPlugin/index.ts +125 -0
- package/src/client/components/inputv3/Rte/plugins/StickyPlugin/index.ts +22 -0
- package/src/client/components/inputv3/Rte/plugins/TabFocusPlugin/index.tsx +65 -0
- package/src/client/components/inputv3/Rte/plugins/TableActionMenuPlugin/index.tsx +773 -0
- package/src/client/components/inputv3/Rte/plugins/TableCellResizer/index.css +12 -0
- package/src/client/components/inputv3/Rte/plugins/TableCellResizer/index.tsx +436 -0
- package/src/client/components/inputv3/Rte/plugins/TableHoverActionsPlugin/index.tsx +287 -0
- package/src/client/components/inputv3/Rte/plugins/TableOfContentsPlugin/index.css +95 -0
- package/src/client/components/inputv3/Rte/plugins/TableOfContentsPlugin/index.tsx +197 -0
- package/src/client/components/inputv3/Rte/plugins/TablePlugin.tsx +178 -0
- package/src/client/components/inputv3/Rte/plugins/TestRecorderPlugin/index.tsx +468 -0
- package/src/client/components/inputv3/Rte/plugins/TreeViewPlugin/index.tsx +26 -0
- package/src/client/components/inputv3/Rte/plugins/TwitterPlugin/index.ts +41 -0
- package/src/client/components/inputv3/Rte/plugins/TypingPerfPlugin/index.ts +117 -0
- package/src/client/components/inputv3/Rte/plugins/YouTubePlugin/index.ts +41 -0
- package/src/client/components/inputv3/Rte/shared/canUseDOM.ts +4 -0
- package/src/client/components/inputv3/Rte/shared/caretFromPoint.ts +40 -0
- package/src/client/components/inputv3/Rte/shared/environment.ts +56 -0
- package/src/client/components/inputv3/Rte/shared/invariant.ts +26 -0
- package/src/client/components/inputv3/Rte/shared/normalizeClassNames.ts +21 -0
- package/src/client/components/inputv3/Rte/shared/react-test-utils.ts +18 -0
- package/src/client/components/inputv3/Rte/shared/reactPatches.ts +22 -0
- package/src/client/components/inputv3/Rte/shared/simpleDiffWithCursor.ts +49 -0
- package/src/client/components/inputv3/Rte/shared/useLayoutEffect.ts +19 -0
- package/src/client/components/inputv3/Rte/shared/warnOnlyOnce.ts +20 -0
- package/src/client/components/inputv3/Rte/style.less +30 -60
- package/src/client/components/inputv3/Rte/themes/CommentEditorTheme.css +13 -0
- package/src/client/components/inputv3/Rte/themes/CommentEditorTheme.ts +20 -0
- package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.css +447 -0
- package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.ts +120 -0
- package/src/client/components/inputv3/Rte/themes/StickyEditorTheme.css +13 -0
- package/src/client/components/inputv3/Rte/themes/StickyEditorTheme.ts +20 -0
- package/src/client/components/inputv3/Rte/ui/ColorPicker.css +88 -0
- package/src/client/components/inputv3/Rte/ui/ColorPicker.tsx +365 -0
- package/src/client/components/inputv3/Rte/ui/ContentEditable.css +44 -0
- package/src/client/components/inputv3/Rte/ui/ContentEditable.tsx +36 -0
- package/src/client/components/inputv3/Rte/ui/DropDown.tsx +259 -0
- package/src/client/components/inputv3/Rte/ui/DropdownColorPicker.tsx +41 -0
- package/src/client/components/inputv3/Rte/ui/EquationEditor.css +38 -0
- package/src/client/components/inputv3/Rte/ui/EquationEditor.tsx +56 -0
- package/src/client/components/inputv3/Rte/ui/FileInput.tsx +38 -0
- package/src/client/components/inputv3/Rte/ui/FlashMessage.css +28 -0
- package/src/client/components/inputv3/Rte/ui/FlashMessage.tsx +29 -0
- package/src/client/components/inputv3/Rte/ui/ImageResizer.tsx +316 -0
- package/src/client/components/inputv3/Rte/ui/Input.css +32 -0
- package/src/client/components/inputv3/Rte/ui/KatexRenderer.tsx +54 -0
- package/src/client/components/inputv3/Rte/ui/Switch.tsx +36 -0
- package/src/client/components/inputv3/Rte/utils/docSerialization.ts +77 -0
- package/src/client/components/inputv3/Rte/utils/emoji-list.ts +16615 -0
- package/src/client/components/inputv3/Rte/utils/getDOMRangeRect.ts +27 -0
- package/src/client/components/inputv3/Rte/utils/getSelectedNode.ts +27 -0
- package/src/client/components/inputv3/Rte/utils/guard.ts +10 -0
- package/src/client/components/inputv3/Rte/utils/isMobileWidth.ts +7 -0
- package/src/client/components/inputv3/Rte/utils/joinClasses.ts +13 -0
- package/src/client/components/inputv3/Rte/utils/setFloatingElemPosition.ts +51 -0
- package/src/client/components/inputv3/Rte/utils/setFloatingElemPositionForLinkEditor.ts +46 -0
- package/src/client/components/inputv3/Rte/utils/swipe.ts +127 -0
- package/src/client/components/inputv3/Rte/utils/url.ts +38 -0
- package/src/client/components/inputv3/base.tsx +8 -5
- package/src/client/components/inputv3/file/index.tsx +11 -5
- package/src/common/data/rte/nodes.ts +60 -9
- package/src/common/validation/index.ts +21 -2
- package/src/common/validation/schema.ts +42 -10
- package/src/common/validation/validator.ts +12 -4
- package/src/common/validation/validators.ts +82 -53
- package/src/server/services/router/http/multipart.ts +0 -1
- package/src/server/services/schema/index.ts +24 -2
- package/src/server/services/schema/request.ts +3 -2
- package/src/server/services/schema/rte.ts +110 -0
- package/src/{common/data/rte/index.ts → server/utils/rte.ts} +27 -16
- package/src/client/components/inputv3/Rte/ExampleTheme.tsx +0 -42
- package/src/client/components/inputv3/Rte/ToolbarPlugin.tsx +0 -167
- package/src/client/components/inputv3/Rte/icons/LICENSE.md +0 -5
- package/src/client/components/inputv3/Rte/icons/arrow-clockwise.svg +0 -4
- package/src/client/components/inputv3/Rte/icons/arrow-counterclockwise.svg +0 -4
- package/src/client/components/inputv3/Rte/icons/journal-text.svg +0 -5
- package/src/client/components/inputv3/Rte/icons/justify.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/text-center.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/text-left.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/text-paragraph.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/text-right.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/type-bold.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/type-italic.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/type-strikethrough.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/type-underline.svg +0 -3
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as React from 'react';
|
|
10
|
+
|
|
11
|
+
import ColorPicker from './ColorPicker';
|
|
12
|
+
import DropDown from './DropDown';
|
|
13
|
+
|
|
14
|
+
type Props = {
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
buttonAriaLabel?: string;
|
|
17
|
+
buttonClassName: string;
|
|
18
|
+
buttonIconClassName?: string;
|
|
19
|
+
buttonLabel?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
stopCloseOnClickSelf?: boolean;
|
|
22
|
+
color: string;
|
|
23
|
+
onChange?: (color: string, skipHistoryStack: boolean) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default function DropdownColorPicker({
|
|
27
|
+
disabled = false,
|
|
28
|
+
stopCloseOnClickSelf = true,
|
|
29
|
+
color,
|
|
30
|
+
onChange,
|
|
31
|
+
...rest
|
|
32
|
+
}: Props) {
|
|
33
|
+
return (
|
|
34
|
+
<DropDown
|
|
35
|
+
{...rest}
|
|
36
|
+
disabled={disabled}
|
|
37
|
+
stopCloseOnClickSelf={stopCloseOnClickSelf}>
|
|
38
|
+
<ColorPicker color={color} onChange={onChange} />
|
|
39
|
+
</DropDown>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
.EquationEditor_inlineEditor {
|
|
11
|
+
padding: 0;
|
|
12
|
+
margin: 0;
|
|
13
|
+
border: 0;
|
|
14
|
+
outline: 0;
|
|
15
|
+
color: #8421a2;
|
|
16
|
+
background-color: inherit;
|
|
17
|
+
resize: none;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.EquationEditor_blockEditor {
|
|
21
|
+
padding: 0;
|
|
22
|
+
margin: 0;
|
|
23
|
+
border: 0;
|
|
24
|
+
outline: 0;
|
|
25
|
+
color: #8421a2;
|
|
26
|
+
background-color: inherit;
|
|
27
|
+
resize: none;
|
|
28
|
+
width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.EquationEditor_inputBackground {
|
|
32
|
+
background-color: #eee;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.EquationEditor_dollarSign {
|
|
36
|
+
text-align: left;
|
|
37
|
+
color: #b0b0b0;
|
|
38
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {Ref, RefObject} from 'react';
|
|
10
|
+
|
|
11
|
+
import './EquationEditor.css';
|
|
12
|
+
|
|
13
|
+
import * as React from 'react';
|
|
14
|
+
import {ChangeEvent, forwardRef} from 'react';
|
|
15
|
+
|
|
16
|
+
type BaseEquationEditorProps = {
|
|
17
|
+
equation: string;
|
|
18
|
+
inline: boolean;
|
|
19
|
+
setEquation: (equation: string) => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function EquationEditor(
|
|
23
|
+
{equation, setEquation, inline}: BaseEquationEditorProps,
|
|
24
|
+
forwardedRef: Ref<HTMLInputElement | HTMLTextAreaElement>,
|
|
25
|
+
): JSX.Element {
|
|
26
|
+
const onChange = (event: ChangeEvent) => {
|
|
27
|
+
setEquation((event.target as HTMLInputElement).value);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return inline && forwardedRef instanceof HTMLInputElement ? (
|
|
31
|
+
<span className="EquationEditor_inputBackground">
|
|
32
|
+
<span className="EquationEditor_dollarSign">$</span>
|
|
33
|
+
<input
|
|
34
|
+
className="EquationEditor_inlineEditor"
|
|
35
|
+
value={equation}
|
|
36
|
+
onChange={onChange}
|
|
37
|
+
autoFocus={true}
|
|
38
|
+
ref={forwardedRef as RefObject<HTMLInputElement>}
|
|
39
|
+
/>
|
|
40
|
+
<span className="EquationEditor_dollarSign">$</span>
|
|
41
|
+
</span>
|
|
42
|
+
) : (
|
|
43
|
+
<div className="EquationEditor_inputBackground">
|
|
44
|
+
<span className="EquationEditor_dollarSign">{'$$\n'}</span>
|
|
45
|
+
<textarea
|
|
46
|
+
className="EquationEditor_blockEditor"
|
|
47
|
+
value={equation}
|
|
48
|
+
onChange={onChange}
|
|
49
|
+
ref={forwardedRef as RefObject<HTMLTextAreaElement>}
|
|
50
|
+
/>
|
|
51
|
+
<span className="EquationEditor_dollarSign">{'\n$$'}</span>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default forwardRef(EquationEditor);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import './Input.css';
|
|
10
|
+
|
|
11
|
+
import * as React from 'react';
|
|
12
|
+
|
|
13
|
+
type Props = Readonly<{
|
|
14
|
+
'data-test-id'?: string;
|
|
15
|
+
accept?: string;
|
|
16
|
+
label: string;
|
|
17
|
+
onChange: (files: FileList | null) => void;
|
|
18
|
+
}>;
|
|
19
|
+
|
|
20
|
+
export default function FileInput({
|
|
21
|
+
accept,
|
|
22
|
+
label,
|
|
23
|
+
onChange,
|
|
24
|
+
'data-test-id': dataTestId,
|
|
25
|
+
}: Props): JSX.Element {
|
|
26
|
+
return (
|
|
27
|
+
<div className="Input__wrapper">
|
|
28
|
+
<label className="Input__label">{label}</label>
|
|
29
|
+
<input
|
|
30
|
+
type="file"
|
|
31
|
+
accept={accept}
|
|
32
|
+
className="Input__input"
|
|
33
|
+
onChange={(e) => onChange(e.target.files)}
|
|
34
|
+
data-test-id={dataTestId}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
.FlashMessage__overlay {
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
position: fixed;
|
|
15
|
+
pointer-events: none;
|
|
16
|
+
top: 0px;
|
|
17
|
+
bottom: 0px;
|
|
18
|
+
left: 0px;
|
|
19
|
+
right: 0px;
|
|
20
|
+
}
|
|
21
|
+
.FlashMessage__alert {
|
|
22
|
+
background-color: rgba(0, 0, 0, 0.8);
|
|
23
|
+
color: white;
|
|
24
|
+
padding: 20px;
|
|
25
|
+
font-size: 1.5rem;
|
|
26
|
+
border-radius: 1em;
|
|
27
|
+
padding: 0.5em 1.5em;
|
|
28
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import './FlashMessage.css';
|
|
10
|
+
|
|
11
|
+
import {ReactNode} from 'react';
|
|
12
|
+
import {createPortal} from 'react-dom';
|
|
13
|
+
|
|
14
|
+
export interface FlashMessageProps {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function FlashMessage({
|
|
19
|
+
children,
|
|
20
|
+
}: FlashMessageProps): JSX.Element {
|
|
21
|
+
return createPortal(
|
|
22
|
+
<div className="FlashMessage__overlay" role="dialog">
|
|
23
|
+
<p className="FlashMessage__alert" role="alert">
|
|
24
|
+
{children}
|
|
25
|
+
</p>
|
|
26
|
+
</div>,
|
|
27
|
+
document.body,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {LexicalEditor} from 'lexical';
|
|
10
|
+
|
|
11
|
+
import {calculateZoomLevel} from '@lexical/utils';
|
|
12
|
+
import * as React from 'react';
|
|
13
|
+
import {useRef} from 'react';
|
|
14
|
+
|
|
15
|
+
function clamp(value: number, min: number, max: number) {
|
|
16
|
+
return Math.min(Math.max(value, min), max);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Direction = {
|
|
20
|
+
east: 1 << 0,
|
|
21
|
+
north: 1 << 3,
|
|
22
|
+
south: 1 << 1,
|
|
23
|
+
west: 1 << 2,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default function ImageResizer({
|
|
27
|
+
onResizeStart,
|
|
28
|
+
onResizeEnd,
|
|
29
|
+
buttonRef,
|
|
30
|
+
imageRef,
|
|
31
|
+
maxWidth,
|
|
32
|
+
editor,
|
|
33
|
+
showCaption,
|
|
34
|
+
setShowCaption,
|
|
35
|
+
captionsEnabled,
|
|
36
|
+
}: {
|
|
37
|
+
editor: LexicalEditor;
|
|
38
|
+
buttonRef: {current: null | HTMLButtonElement};
|
|
39
|
+
imageRef: {current: null | HTMLElement};
|
|
40
|
+
maxWidth?: number;
|
|
41
|
+
onResizeEnd: (width: 'inherit' | number, height: 'inherit' | number) => void;
|
|
42
|
+
onResizeStart: () => void;
|
|
43
|
+
setShowCaption: (show: boolean) => void;
|
|
44
|
+
showCaption: boolean;
|
|
45
|
+
captionsEnabled: boolean;
|
|
46
|
+
}): JSX.Element {
|
|
47
|
+
const controlWrapperRef = useRef<HTMLDivElement>(null);
|
|
48
|
+
const userSelect = useRef({
|
|
49
|
+
priority: '',
|
|
50
|
+
value: 'default',
|
|
51
|
+
});
|
|
52
|
+
const positioningRef = useRef<{
|
|
53
|
+
currentHeight: 'inherit' | number;
|
|
54
|
+
currentWidth: 'inherit' | number;
|
|
55
|
+
direction: number;
|
|
56
|
+
isResizing: boolean;
|
|
57
|
+
ratio: number;
|
|
58
|
+
startHeight: number;
|
|
59
|
+
startWidth: number;
|
|
60
|
+
startX: number;
|
|
61
|
+
startY: number;
|
|
62
|
+
}>({
|
|
63
|
+
currentHeight: 0,
|
|
64
|
+
currentWidth: 0,
|
|
65
|
+
direction: 0,
|
|
66
|
+
isResizing: false,
|
|
67
|
+
ratio: 0,
|
|
68
|
+
startHeight: 0,
|
|
69
|
+
startWidth: 0,
|
|
70
|
+
startX: 0,
|
|
71
|
+
startY: 0,
|
|
72
|
+
});
|
|
73
|
+
const editorRootElement = editor.getRootElement();
|
|
74
|
+
// Find max width, accounting for editor padding.
|
|
75
|
+
const maxWidthContainer = maxWidth
|
|
76
|
+
? maxWidth
|
|
77
|
+
: editorRootElement !== null
|
|
78
|
+
? editorRootElement.getBoundingClientRect().width - 20
|
|
79
|
+
: 100;
|
|
80
|
+
const maxHeightContainer =
|
|
81
|
+
editorRootElement !== null
|
|
82
|
+
? editorRootElement.getBoundingClientRect().height - 20
|
|
83
|
+
: 100;
|
|
84
|
+
|
|
85
|
+
const minWidth = 100;
|
|
86
|
+
const minHeight = 100;
|
|
87
|
+
|
|
88
|
+
const setStartCursor = (direction: number) => {
|
|
89
|
+
const ew = direction === Direction.east || direction === Direction.west;
|
|
90
|
+
const ns = direction === Direction.north || direction === Direction.south;
|
|
91
|
+
const nwse =
|
|
92
|
+
(direction & Direction.north && direction & Direction.west) ||
|
|
93
|
+
(direction & Direction.south && direction & Direction.east);
|
|
94
|
+
|
|
95
|
+
const cursorDir = ew ? 'ew' : ns ? 'ns' : nwse ? 'nwse' : 'nesw';
|
|
96
|
+
|
|
97
|
+
if (editorRootElement !== null) {
|
|
98
|
+
editorRootElement.style.setProperty(
|
|
99
|
+
'cursor',
|
|
100
|
+
`${cursorDir}-resize`,
|
|
101
|
+
'important',
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (document.body !== null) {
|
|
105
|
+
document.body.style.setProperty(
|
|
106
|
+
'cursor',
|
|
107
|
+
`${cursorDir}-resize`,
|
|
108
|
+
'important',
|
|
109
|
+
);
|
|
110
|
+
userSelect.current.value = document.body.style.getPropertyValue(
|
|
111
|
+
'-webkit-user-select',
|
|
112
|
+
);
|
|
113
|
+
userSelect.current.priority = document.body.style.getPropertyPriority(
|
|
114
|
+
'-webkit-user-select',
|
|
115
|
+
);
|
|
116
|
+
document.body.style.setProperty(
|
|
117
|
+
'-webkit-user-select',
|
|
118
|
+
`none`,
|
|
119
|
+
'important',
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const setEndCursor = () => {
|
|
125
|
+
if (editorRootElement !== null) {
|
|
126
|
+
editorRootElement.style.setProperty('cursor', 'text');
|
|
127
|
+
}
|
|
128
|
+
if (document.body !== null) {
|
|
129
|
+
document.body.style.setProperty('cursor', 'default');
|
|
130
|
+
document.body.style.setProperty(
|
|
131
|
+
'-webkit-user-select',
|
|
132
|
+
userSelect.current.value,
|
|
133
|
+
userSelect.current.priority,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const handlePointerDown = (
|
|
139
|
+
event: React.PointerEvent<HTMLDivElement>,
|
|
140
|
+
direction: number,
|
|
141
|
+
) => {
|
|
142
|
+
if (!editor.isEditable()) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const image = imageRef.current;
|
|
147
|
+
const controlWrapper = controlWrapperRef.current;
|
|
148
|
+
|
|
149
|
+
if (image !== null && controlWrapper !== null) {
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
const {width, height} = image.getBoundingClientRect();
|
|
152
|
+
const zoom = calculateZoomLevel(image);
|
|
153
|
+
const positioning = positioningRef.current;
|
|
154
|
+
positioning.startWidth = width;
|
|
155
|
+
positioning.startHeight = height;
|
|
156
|
+
positioning.ratio = width / height;
|
|
157
|
+
positioning.currentWidth = width;
|
|
158
|
+
positioning.currentHeight = height;
|
|
159
|
+
positioning.startX = event.clientX / zoom;
|
|
160
|
+
positioning.startY = event.clientY / zoom;
|
|
161
|
+
positioning.isResizing = true;
|
|
162
|
+
positioning.direction = direction;
|
|
163
|
+
|
|
164
|
+
setStartCursor(direction);
|
|
165
|
+
onResizeStart();
|
|
166
|
+
|
|
167
|
+
controlWrapper.classList.add('image-control-wrapper--resizing');
|
|
168
|
+
image.style.height = `${height}px`;
|
|
169
|
+
image.style.width = `${width}px`;
|
|
170
|
+
|
|
171
|
+
document.addEventListener('pointermove', handlePointerMove);
|
|
172
|
+
document.addEventListener('pointerup', handlePointerUp);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
const handlePointerMove = (event: PointerEvent) => {
|
|
176
|
+
const image = imageRef.current;
|
|
177
|
+
const positioning = positioningRef.current;
|
|
178
|
+
|
|
179
|
+
const isHorizontal =
|
|
180
|
+
positioning.direction & (Direction.east | Direction.west);
|
|
181
|
+
const isVertical =
|
|
182
|
+
positioning.direction & (Direction.south | Direction.north);
|
|
183
|
+
|
|
184
|
+
if (image !== null && positioning.isResizing) {
|
|
185
|
+
const zoom = calculateZoomLevel(image);
|
|
186
|
+
// Corner cursor
|
|
187
|
+
if (isHorizontal && isVertical) {
|
|
188
|
+
let diff = Math.floor(positioning.startX - event.clientX / zoom);
|
|
189
|
+
diff = positioning.direction & Direction.east ? -diff : diff;
|
|
190
|
+
|
|
191
|
+
const width = clamp(
|
|
192
|
+
positioning.startWidth + diff,
|
|
193
|
+
minWidth,
|
|
194
|
+
maxWidthContainer,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const height = width / positioning.ratio;
|
|
198
|
+
image.style.width = `${width}px`;
|
|
199
|
+
image.style.height = `${height}px`;
|
|
200
|
+
positioning.currentHeight = height;
|
|
201
|
+
positioning.currentWidth = width;
|
|
202
|
+
} else if (isVertical) {
|
|
203
|
+
let diff = Math.floor(positioning.startY - event.clientY / zoom);
|
|
204
|
+
diff = positioning.direction & Direction.south ? -diff : diff;
|
|
205
|
+
|
|
206
|
+
const height = clamp(
|
|
207
|
+
positioning.startHeight + diff,
|
|
208
|
+
minHeight,
|
|
209
|
+
maxHeightContainer,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
image.style.height = `${height}px`;
|
|
213
|
+
positioning.currentHeight = height;
|
|
214
|
+
} else {
|
|
215
|
+
let diff = Math.floor(positioning.startX - event.clientX / zoom);
|
|
216
|
+
diff = positioning.direction & Direction.east ? -diff : diff;
|
|
217
|
+
|
|
218
|
+
const width = clamp(
|
|
219
|
+
positioning.startWidth + diff,
|
|
220
|
+
minWidth,
|
|
221
|
+
maxWidthContainer,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
image.style.width = `${width}px`;
|
|
225
|
+
positioning.currentWidth = width;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const handlePointerUp = () => {
|
|
230
|
+
const image = imageRef.current;
|
|
231
|
+
const positioning = positioningRef.current;
|
|
232
|
+
const controlWrapper = controlWrapperRef.current;
|
|
233
|
+
if (image !== null && controlWrapper !== null && positioning.isResizing) {
|
|
234
|
+
const width = positioning.currentWidth;
|
|
235
|
+
const height = positioning.currentHeight;
|
|
236
|
+
positioning.startWidth = 0;
|
|
237
|
+
positioning.startHeight = 0;
|
|
238
|
+
positioning.ratio = 0;
|
|
239
|
+
positioning.startX = 0;
|
|
240
|
+
positioning.startY = 0;
|
|
241
|
+
positioning.currentWidth = 0;
|
|
242
|
+
positioning.currentHeight = 0;
|
|
243
|
+
positioning.isResizing = false;
|
|
244
|
+
|
|
245
|
+
controlWrapper.classList.remove('image-control-wrapper--resizing');
|
|
246
|
+
|
|
247
|
+
setEndCursor();
|
|
248
|
+
onResizeEnd(width, height);
|
|
249
|
+
|
|
250
|
+
document.removeEventListener('pointermove', handlePointerMove);
|
|
251
|
+
document.removeEventListener('pointerup', handlePointerUp);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
return (
|
|
255
|
+
<div ref={controlWrapperRef}>
|
|
256
|
+
{!showCaption && captionsEnabled && (
|
|
257
|
+
<button
|
|
258
|
+
className="image-caption-button"
|
|
259
|
+
ref={buttonRef}
|
|
260
|
+
onClick={() => {
|
|
261
|
+
setShowCaption(!showCaption);
|
|
262
|
+
}}>
|
|
263
|
+
Add Caption
|
|
264
|
+
</button>
|
|
265
|
+
)}
|
|
266
|
+
<div
|
|
267
|
+
className="image-resizer image-resizer-n"
|
|
268
|
+
onPointerDown={(event) => {
|
|
269
|
+
handlePointerDown(event, Direction.north);
|
|
270
|
+
}}
|
|
271
|
+
/>
|
|
272
|
+
<div
|
|
273
|
+
className="image-resizer image-resizer-ne"
|
|
274
|
+
onPointerDown={(event) => {
|
|
275
|
+
handlePointerDown(event, Direction.north | Direction.east);
|
|
276
|
+
}}
|
|
277
|
+
/>
|
|
278
|
+
<div
|
|
279
|
+
className="image-resizer image-resizer-e"
|
|
280
|
+
onPointerDown={(event) => {
|
|
281
|
+
handlePointerDown(event, Direction.east);
|
|
282
|
+
}}
|
|
283
|
+
/>
|
|
284
|
+
<div
|
|
285
|
+
className="image-resizer image-resizer-se"
|
|
286
|
+
onPointerDown={(event) => {
|
|
287
|
+
handlePointerDown(event, Direction.south | Direction.east);
|
|
288
|
+
}}
|
|
289
|
+
/>
|
|
290
|
+
<div
|
|
291
|
+
className="image-resizer image-resizer-s"
|
|
292
|
+
onPointerDown={(event) => {
|
|
293
|
+
handlePointerDown(event, Direction.south);
|
|
294
|
+
}}
|
|
295
|
+
/>
|
|
296
|
+
<div
|
|
297
|
+
className="image-resizer image-resizer-sw"
|
|
298
|
+
onPointerDown={(event) => {
|
|
299
|
+
handlePointerDown(event, Direction.south | Direction.west);
|
|
300
|
+
}}
|
|
301
|
+
/>
|
|
302
|
+
<div
|
|
303
|
+
className="image-resizer image-resizer-w"
|
|
304
|
+
onPointerDown={(event) => {
|
|
305
|
+
handlePointerDown(event, Direction.west);
|
|
306
|
+
}}
|
|
307
|
+
/>
|
|
308
|
+
<div
|
|
309
|
+
className="image-resizer image-resizer-nw"
|
|
310
|
+
onPointerDown={(event) => {
|
|
311
|
+
handlePointerDown(event, Direction.north | Direction.west);
|
|
312
|
+
}}
|
|
313
|
+
/>
|
|
314
|
+
</div>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
.Input__wrapper {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: row;
|
|
13
|
+
align-items: center;
|
|
14
|
+
margin-bottom: 10px;
|
|
15
|
+
}
|
|
16
|
+
.Input__label {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex: 1;
|
|
19
|
+
color: #666;
|
|
20
|
+
}
|
|
21
|
+
.Input__input {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex: 2;
|
|
24
|
+
border: 1px solid #999;
|
|
25
|
+
padding-top: 7px;
|
|
26
|
+
padding-bottom: 7px;
|
|
27
|
+
padding-left: 10px;
|
|
28
|
+
padding-right: 10px;
|
|
29
|
+
font-size: 16px;
|
|
30
|
+
border-radius: 5px;
|
|
31
|
+
min-width: 0;
|
|
32
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import katex from 'katex';
|
|
10
|
+
import * as React from 'react';
|
|
11
|
+
import {useEffect, useRef} from 'react';
|
|
12
|
+
|
|
13
|
+
export default function KatexRenderer({
|
|
14
|
+
equation,
|
|
15
|
+
inline,
|
|
16
|
+
onDoubleClick,
|
|
17
|
+
}: Readonly<{
|
|
18
|
+
equation: string;
|
|
19
|
+
inline: boolean;
|
|
20
|
+
onDoubleClick: () => void;
|
|
21
|
+
}>): JSX.Element {
|
|
22
|
+
const katexElementRef = useRef(null);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const katexElement = katexElementRef.current;
|
|
26
|
+
|
|
27
|
+
if (katexElement !== null) {
|
|
28
|
+
katex.render(equation, katexElement, {
|
|
29
|
+
displayMode: !inline, // true === block display //
|
|
30
|
+
errorColor: '#cc0000',
|
|
31
|
+
output: 'html',
|
|
32
|
+
strict: 'warn',
|
|
33
|
+
throwOnError: false,
|
|
34
|
+
trust: false,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}, [equation, inline]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
// We use an empty image tag either side to ensure Android doesn't try and compose from the
|
|
41
|
+
// inner text from Katex. There didn't seem to be any other way of making this work,
|
|
42
|
+
// without having a physical space.
|
|
43
|
+
<>
|
|
44
|
+
<img src="#" alt="" />
|
|
45
|
+
<span
|
|
46
|
+
role="button"
|
|
47
|
+
tabIndex={-1}
|
|
48
|
+
onDoubleClick={onDoubleClick}
|
|
49
|
+
ref={katexElementRef}
|
|
50
|
+
/>
|
|
51
|
+
<img src="#" alt="" />
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as React from 'react';
|
|
10
|
+
import {useMemo} from 'react';
|
|
11
|
+
|
|
12
|
+
export default function Switch({
|
|
13
|
+
checked,
|
|
14
|
+
onClick,
|
|
15
|
+
text,
|
|
16
|
+
id,
|
|
17
|
+
}: Readonly<{
|
|
18
|
+
checked: boolean;
|
|
19
|
+
id?: string;
|
|
20
|
+
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
21
|
+
text: string;
|
|
22
|
+
}>): JSX.Element {
|
|
23
|
+
const buttonId = useMemo(() => 'id_' + Math.floor(Math.random() * 10000), []);
|
|
24
|
+
return (
|
|
25
|
+
<div className="switch" id={id}>
|
|
26
|
+
<label htmlFor={buttonId}>{text}</label>
|
|
27
|
+
<button
|
|
28
|
+
role="switch"
|
|
29
|
+
aria-checked={checked}
|
|
30
|
+
id={buttonId}
|
|
31
|
+
onClick={onClick}>
|
|
32
|
+
<span />
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|