Skip to content

Commit 156c278

Browse files
shreeyash07frozenhelium
authored andcommitted
Add markdown editor
1 parent 6f4648c commit 156c278

File tree

9 files changed

+199
-9
lines changed

9 files changed

+199
-9
lines changed

manager-dashboard/app/Base/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
ApolloProvider,
1313
} from '@apollo/client';
1414
import { initializeApp } from 'firebase/app';
15+
import 'react-mde/lib/styles/css/react-mde-all.css';
1516

1617
import Init from '#base/components/Init';
1718
import PreloadMessage from '#base/components/PreloadMessage';
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import React, { useCallback } from 'react';
2+
import Markdown from 'react-mde';
3+
4+
import InputContainer, { Props as InputContainerProps } from '../InputContainer';
5+
import MarkdownPreview from '../MarkdownPreview';
6+
7+
import styles from './styles.css';
8+
9+
interface MarkdownEditorProps<K extends string> {
10+
name: K;
11+
className?: string;
12+
readOnly?: boolean;
13+
disabled?: boolean;
14+
value: string | null | undefined;
15+
onChange?:(newVal: string | undefined, name: K) => void;
16+
}
17+
18+
export type Props<K extends string> = Omit<InputContainerProps, 'input'> & MarkdownEditorProps<K>;
19+
20+
function MarkdownEditor<K extends string>(props: Props<K>) {
21+
const {
22+
name,
23+
value,
24+
onChange,
25+
actions,
26+
actionsContainerClassName,
27+
className,
28+
disabled,
29+
error,
30+
errorContainerClassName,
31+
hint,
32+
hintContainerClassName,
33+
icons,
34+
iconsContainerClassName,
35+
inputSectionClassName,
36+
label,
37+
labelContainerClassName,
38+
readOnly,
39+
} = props;
40+
41+
const [selectedTab, setSelectedTab] = React.useState<'write' | 'preview'>('write');
42+
const handleValueChange = useCallback(
43+
(newVal) => {
44+
if (!disabled && !readOnly && onChange) {
45+
onChange(newVal, name);
46+
}
47+
},
48+
[name, onChange, disabled, readOnly],
49+
);
50+
51+
const generateMarkdownPreview = useCallback((markdown: string | undefined) => (
52+
Promise.resolve(
53+
<MarkdownPreview
54+
markdown={markdown ?? ''}
55+
/>,
56+
)
57+
), []);
58+
59+
return (
60+
<InputContainer
61+
actionsContainerClassName={actionsContainerClassName}
62+
className={className}
63+
disabled={disabled}
64+
error={error}
65+
errorContainerClassName={errorContainerClassName}
66+
hint={hint}
67+
hintContainerClassName={hintContainerClassName}
68+
icons={icons}
69+
iconsContainerClassName={iconsContainerClassName}
70+
inputSectionClassName={inputSectionClassName}
71+
// inputContainerClassName={styles.input}
72+
label={label}
73+
labelContainerClassName={labelContainerClassName}
74+
readOnly={readOnly}
75+
actions={actions}
76+
input={!readOnly ? (
77+
<Markdown
78+
value={value ?? ''}
79+
selectedTab={selectedTab}
80+
onTabChange={setSelectedTab}
81+
onChange={handleValueChange}
82+
generateMarkdownPreview={generateMarkdownPreview}
83+
readOnly={disabled}
84+
disablePreview
85+
classes={{
86+
reactMde: styles.reactMde,
87+
textArea: styles.textArea,
88+
toolbar: styles.toolbar,
89+
}}
90+
/>
91+
) : (
92+
<MarkdownPreview
93+
markdown={value || '-'}
94+
/>
95+
)}
96+
/>
97+
);
98+
}
99+
100+
export default MarkdownEditor;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.react-mde {
2+
border: 0;
3+
border-radius: 0;
4+
}
5+
6+
.text-area {
7+
height: 70px !important;
8+
}
9+
10+
.toolbar {
11+
border-bottom: 0;
12+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
import MarkdownView, { MarkdownViewProps } from 'react-showdown';
3+
4+
export const markdownOptions: MarkdownViewProps['options'] = {
5+
simpleLineBreaks: true,
6+
headerLevelStart: 3,
7+
simplifiedAutoLink: true,
8+
openLinksInNewWindow: true,
9+
backslashEscapesHTMLTags: true,
10+
literalMidWordUnderscores: true,
11+
strikethrough: true,
12+
tables: true,
13+
tasklists: true,
14+
};
15+
16+
export default function MarkdownPreview(props: MarkdownViewProps) {
17+
const {
18+
options: markdownOptionsFromProps,
19+
...otherProps
20+
} = props;
21+
return (
22+
<MarkdownView
23+
{...otherProps}
24+
options={markdownOptionsFromProps ?? markdownOptions}
25+
/>
26+
);
27+
}

manager-dashboard/app/views/NewTutorial/InformationPageInput/Block/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88

99
import { PartialBlocksType } from '#views/NewTutorial/utils';
1010
import FileInput from '#components/FileInput';
11-
import TextInput from '#components/TextInput';
11+
import MarkdownEditor from '#components/MarkdownEditor';
1212

1313
// import styles from './styles.css';
1414

@@ -34,11 +34,11 @@ export default function Block(props: Props) {
3434
return (
3535
<div>
3636
{value.blockType === 'text' && (
37-
<TextInput
37+
<MarkdownEditor
3838
name={'textDescription' as const}
39-
value={value.textDescription}
40-
onChange={onBlockChange}
39+
value={value?.textDescription}
4140
label="Description"
41+
onChange={onBlockChange}
4242
error={error?.textDescription}
4343
/>
4444
)}

manager-dashboard/app/views/NewTutorial/InformationPageInput/InformationPagePreview/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { PartialTutorialFormType } from '#views/NewTutorial/utils';
44
import Preview from '#components/Preview';
55

66
import styles from './styles.css';
7+
import MarkdownPreview from '#components/MarkdownPreview';
78

89
interface Props {
910
value: NonNullable<PartialTutorialFormType['informationPages']>[number]
@@ -17,9 +18,9 @@ export default function InformationPagePreview(props: Props) {
1718
return (
1819
<div className={styles.informationPreview}>
1920
{value?.title && (
20-
<div>
21-
{value?.title}
22-
</div>
21+
<MarkdownPreview
22+
markdown={value.title}
23+
/>
2324
)}
2425
{value?.blocks?.map((preview) => {
2526
if (preview.blockType === 'text' && preview.textDescription) {

manager-dashboard/app/views/NewTutorial/InformationPageInput/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99

1010
import TextInput from '#components/TextInput';
1111
import Button from '#components/Button';
12+
import MarkdownEditor from '#components/MarkdownEditor';
1213
import InformationPagePreview from './InformationPagePreview';
1314

1415
import { InformationPagesType } from '../utils';

manager-dashboard/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
"leaflet": "^1.8.0",
5050
"react": "^17.0.2",
5151
"react-dom": "^17.0.2",
52-
"react-icons": "^4.3.1"
52+
"react-icons": "^4.3.1",
53+
"react-mde": "^11.5.0",
54+
"react-showdown": "^2.3.1"
5355
},
5456
"devDependencies": {
5557
"@babel/core": "^7.13.10",

manager-dashboard/yarn.lock

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7202,7 +7202,7 @@ htmlparser2@^3.10.0:
72027202
inherits "^2.0.1"
72037203
readable-stream "^3.1.1"
72047204

7205-
htmlparser2@^6.1.0:
7205+
htmlparser2@^6.0.1, htmlparser2@^6.1.0:
72067206
version "6.1.0"
72077207
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
72087208
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
@@ -10923,6 +10923,11 @@ react-is@^17.0.1:
1092310923
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
1092410924
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
1092510925

10926+
react-mde@^11.5.0:
10927+
version "11.5.0"
10928+
resolved "https://registry.yarnpkg.com/react-mde/-/react-mde-11.5.0.tgz#3e81a505071aa80287fb23a1c0ce5e8b34c82055"
10929+
integrity sha512-CH/VK6d+tpVjJ8rTXfh1dDt6GWedTgCU0668p8toqhAc3vy0Lu872O2RKYDSpkUrlbHI08fjUPTl++nExp6gag==
10930+
1092610931
react-refresh@^0.10.0:
1092710932
version "0.10.0"
1092810933
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3"
@@ -10957,6 +10962,15 @@ [email protected]:
1095710962
tiny-invariant "^1.0.2"
1095810963
tiny-warning "^1.0.0"
1095910964

10965+
react-showdown@^2.3.1:
10966+
version "2.3.1"
10967+
resolved "https://registry.yarnpkg.com/react-showdown/-/react-showdown-2.3.1.tgz#c1d4644e3c351ae131da49d8ba081db812c7ca80"
10968+
integrity sha512-3O9Gz6HF1MQbGK17gYOO6EJP27h9Sp+g/L7KeOhMJERuutYP3802MPaL8JybjeI97X9HTeeN4A89WjOpEHl3NQ==
10969+
dependencies:
10970+
domhandler "^4.0.0"
10971+
htmlparser2 "^6.0.1"
10972+
showdown "^1.9.1"
10973+
1096010974
react@^17.0.2:
1096110975
version "17.0.2"
1096210976
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
@@ -11679,6 +11693,13 @@ shebang-regex@^3.0.0:
1167911693
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
1168011694
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
1168111695

11696+
showdown@^1.9.1:
11697+
version "1.9.1"
11698+
resolved "https://registry.yarnpkg.com/showdown/-/showdown-1.9.1.tgz#134e148e75cd4623e09c21b0511977d79b5ad0ef"
11699+
integrity sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==
11700+
dependencies:
11701+
yargs "^14.2"
11702+
1168211703
side-channel@^1.0.4:
1168311704
version "1.0.4"
1168411705
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@@ -13650,6 +13671,14 @@ yargs-parser@^13.1.2:
1365013671
camelcase "^5.0.0"
1365113672
decamelize "^1.2.0"
1365213673

13674+
yargs-parser@^15.0.1:
13675+
version "15.0.3"
13676+
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.3.tgz#316e263d5febe8b38eef61ac092b33dfcc9b1115"
13677+
integrity sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==
13678+
dependencies:
13679+
camelcase "^5.0.0"
13680+
decamelize "^1.2.0"
13681+
1365313682
yargs-parser@^18.1.2:
1365413683
version "18.1.3"
1365513684
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
@@ -13684,6 +13713,23 @@ yargs@^13.3.2:
1368413713
y18n "^4.0.0"
1368513714
yargs-parser "^13.1.2"
1368613715

13716+
yargs@^14.2:
13717+
version "14.2.3"
13718+
resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414"
13719+
integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==
13720+
dependencies:
13721+
cliui "^5.0.0"
13722+
decamelize "^1.2.0"
13723+
find-up "^3.0.0"
13724+
get-caller-file "^2.0.1"
13725+
require-directory "^2.1.1"
13726+
require-main-filename "^2.0.0"
13727+
set-blocking "^2.0.0"
13728+
string-width "^3.0.0"
13729+
which-module "^2.0.0"
13730+
y18n "^4.0.0"
13731+
yargs-parser "^15.0.1"
13732+
1368713733
yargs@^15.3.1:
1368813734
version "15.4.1"
1368913735
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"

0 commit comments

Comments
 (0)