From 79db1f0f1eb992ebcfc86f68d612c0c750d4f177 Mon Sep 17 00:00:00 2001 From: lyy <2213732736@qq.com> Date: Mon, 1 Jul 2024 17:53:16 +0800 Subject: [PATCH] feat: add react demo for cherry-markdown #797 --- .../cherry-markdown-react-demo/.gitignore | 23 + examples/cherry-markdown-react-demo/README.md | 8 + .../cherry-markdown-react-demo/package.json | 35 ++ .../public/favicon.ico | Bin 0 -> 3520 bytes .../public/github.svg | 1 + .../public/index.html | 43 ++ .../public/logo--color.png | Bin 0 -> 23780 bytes .../public/manifest.json | 15 + .../cherry-markdown-react-demo/src/App.css | 178 +++++++ .../cherry-markdown-react-demo/src/App.jsx | 40 ++ .../src/components/Header.jsx | 16 + .../src/components/Menu.jsx | 496 ++++++++++++++++++ .../src/components/Title.jsx | 7 + .../cherry-markdown-react-demo/src/index.css | 13 + .../cherry-markdown-react-demo/src/index.js | 9 + 15 files changed, 884 insertions(+) create mode 100644 examples/cherry-markdown-react-demo/.gitignore create mode 100644 examples/cherry-markdown-react-demo/README.md create mode 100644 examples/cherry-markdown-react-demo/package.json create mode 100644 examples/cherry-markdown-react-demo/public/favicon.ico create mode 100644 examples/cherry-markdown-react-demo/public/github.svg create mode 100644 examples/cherry-markdown-react-demo/public/index.html create mode 100644 examples/cherry-markdown-react-demo/public/logo--color.png create mode 100644 examples/cherry-markdown-react-demo/public/manifest.json create mode 100644 examples/cherry-markdown-react-demo/src/App.css create mode 100644 examples/cherry-markdown-react-demo/src/App.jsx create mode 100644 examples/cherry-markdown-react-demo/src/components/Header.jsx create mode 100644 examples/cherry-markdown-react-demo/src/components/Menu.jsx create mode 100644 examples/cherry-markdown-react-demo/src/components/Title.jsx create mode 100644 examples/cherry-markdown-react-demo/src/index.css create mode 100644 examples/cherry-markdown-react-demo/src/index.js diff --git a/examples/cherry-markdown-react-demo/.gitignore b/examples/cherry-markdown-react-demo/.gitignore new file mode 100644 index 00000000..4d29575d --- /dev/null +++ b/examples/cherry-markdown-react-demo/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/examples/cherry-markdown-react-demo/README.md b/examples/cherry-markdown-react-demo/README.md new file mode 100644 index 00000000..a732af20 --- /dev/null +++ b/examples/cherry-markdown-react-demo/README.md @@ -0,0 +1,8 @@ +# 介绍 +这是cherry-markdown的react示例 + +# 开始 +``` +npm install +npm start +``` \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/package.json b/examples/cherry-markdown-react-demo/package.json new file mode 100644 index 00000000..6db4cf3c --- /dev/null +++ b/examples/cherry-markdown-react-demo/package.json @@ -0,0 +1,35 @@ +{ + "name": "cherry-markdown-react-demo", + "version": "0.1.0", + "private": true, + "dependencies": { + "cherry-markdown": "^0.8.44", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-scripts": "5.0.1" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/public/favicon.ico b/examples/cherry-markdown-react-demo/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..aa8c8987c7742d00a176b411c7a3244a8d772002 GIT binary patch literal 3520 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 \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/public/index.html b/examples/cherry-markdown-react-demo/public/index.html new file mode 100644 index 00000000..d35cfde0 --- /dev/null +++ b/examples/cherry-markdown-react-demo/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + Cherry React Demo + + + + +
+ + + + \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/public/logo--color.png b/examples/cherry-markdown-react-demo/public/logo--color.png new file mode 100644 index 0000000000000000000000000000000000000000..786b7a5bc7aaacb64c9442a517f74319ae0ee482 GIT binary patch literal 23780 zcmaHSWn5KF)GvtADbfuJNOv4MBqUY3>(I@iyO9=ba%rM>4v*OpZC5W z?(g~mH=CI?|J5^V*8ZrhD2;(ij0y(_haoE?`5q4LX*KXXfs6?JRBSF#0{$a{ywQMs zurr0Y8ajgE#7yjrz?8B!hGyXRU_%pkhd!_%Fzcy>iUveOK^|mmXTxInFowm=#vYgr z2PY`vW^ZV01%^->fz2#zg{Tjj+NdclOoXV_xfR$H>?OeF7BU`=;13>(D#jjG#{4GK zBEpn{ZXkew4H#lb>1JbX>jZKWqJE?c0-hgUvrT8m~EYC{-YoXb~1Lf zu!mUK*-}1GG&Hhvh6qsuNdK~6W3Qm_KZI?a{s{^Y8LOM2Ju5p48>@}Y17DA$ognYQ z{};ypbF`C+yFHloJ=n?4+0hu#hbhf}lL6WN{}=jipo#JSjI(!kw0`8%#F!Oq4YmQ> zLYx3l?2m{5;D1j9-msVebJ=;>r6eTzxOln5fthc_#5g!4#otJB@QR6v ziL>$Y{5MzD)(K*0YYcu^^kJ^W|DDVE|D6kxa0DAd>>O3>?5zJY6k?HRRuuwZ(%4 zfvgQ}%>Yp`n}AIXovk6%!kZuCH=@b~SsyIZ+5#B-`1E%k>;Izm`1*gD@_!=%|9E)( zAIAiK`5)H?+XDXV2so|`jlgp_I1LwBNih|-+5HTp`VVieprL3+@YZ7cErthQrpt?Q z(9UW~15)duZASavFkR!Q)aj;aXFTI>ma}k!?!_>T@_;|S`a|W^8;k3i{C%$v4%`>F z%J&A-`Ik~sIFAPhc*$txlMRBf#gVCku%YW?Go|$dScBI0Wy7eSN9i@CfY-$qza=1jQ z#;E2VUyybj7VUY#@Quzy6F^F>)0^|^5jC}fa7ISEn>LE8|CLzPrTh{1_O$pnNLw-E z10Y=f>c>%%>%DnZkv28>C#>69%0I;_4xgOt5=IFdrt5f*@ygu%(%aU{sV=a;02l^OG3!L%~27pbDwaFE^e=^e-M!G ze@ga$5GU87Wik5rtL!;8o=Jz1Z1HbgVrm3{5aRs*LF#x7aOa?d$-8*gF$BL5l82Z1 zR2Ntf+P4b~ldO!Dkb5vsalUp?WpuC$R(TdbDhQ*oSo!rWL(zk3`Xv9UW+eiVztSgY zC>KzzQe`86<&*V)SYRE{UZa~rc=$8M2Gk&QB1Iod6_=%+CDZpb|j; zWe>q&Qp)N`XAZInPdC>OL{~`ejB@ii~ z8}vm)WOT#vsCFUItSWCQdS@TP1TyhIP)3Tv8M+urR@oZ}GIvDg27uvfKDwG@?mux; ze_tQdEbRhef~fs`mUu^UMlb+C0maM@I+6Nk)RO3Zc~y+7j?C4LaDAK$5~!cwtNta! z`5zgnXuS1b2NPjK-5g;!H*s<83f?SaNx%!Ms~JOzfm8F z211O*V~D{v6#uKzurR>HfMA7PZ3@@d_S6P|(i-~+1&614L-}!?0AQWg`evg52h#;i z8%ncB;vL~?f5V+*m=~zuCb&nGF4cgRZa5H=S>-M6U+D+o{Sz#;xNrt6yx)1G7XGCY z(j-;z+X70t2dAUn|EEM8cukIyGIvV!fZLWP{sr;upFEKH0eLX%{Py>F8Y`Hg z=?Taq&E!!YlFyz6KSY8cWOiVPrw%!1JAc*PtG_6}g#0r~s#jSxSmKF-CM&KEe;8M1enEeI`7 zgs?DV@%j(m46*>>frceX^cvAE?IC+SV+ejg5XSJ=7r#)cI1!bWY7xw$&-S`07DvM` zozZOTZL4ixs#K8fM#ngt>Y~B$mu(aoRo?dGOQJqV@#kMDhW*IjM%m1Ir`T!F%s1VtFr`#$ECqK$eZuy0eto7bMQ5fh6 zrqwXRSI@Eer@7UjzxMS-cN$OgHfJq6nEH-yNqjCZd?Sz8eas8wDab;H=oTT80|DGr z<(1llsXF|#>Tw0`q#4pAr<_c74<=~hER`zL(o|U5#@NhVy-xR?hF=~3SLN3kdCwmb zd|3Uzcw_lJwVx@x|IG+KySF5$rdA%W9}CGn9*D2-ELdV02+e~)-l>j%TLL&L)oPg0 zqy2wTC~T0I-d4UjX2#Vw!_Nt{z|M63m~9Irt(_Omolo}$h!J5Waeo*3g7>&DG5>7I zQuUH$O3dojc7=YB2f&j93+`ZEm9D)zz83qB=6m!K&HRLrf|Ez>mj3hS?{eHpD-2oc z3YF=aCK&VtqK)dQ1{s3tW@sXj9K7rts)VXok1kSd zj?H*G%&;uq2-yTI4Lz7GG7iJvq0be$llX}M6=POHxinjZM1JN~$!RqqqSO)o2D0+q zxYfgUQ!;M*CGLY|^nFY1Pmk{ZR4G|>QhbgC_+aj#TC+0>pB)kaBjzOh5eC;^3Chq< z=xj>Tn5i)jj72Z4=hQqInvxj7^AG|k2FXzWbySqEsr@4>j?GN(aGjs^4FjvMI6D4t zzrvZ#Q+};5vWN31v%elALpY&hN`UKSoyYgi$+`jvm~G5roX^tpfZQ;e=fV9*_g?@& z`I6dS9NNz<1;4zvvIV+xQ8EwoJ3gj|eAA0~&{?gb22~TUCqrq zU=NFs#ECG3mSHt(`RL+Ze_i|&Tw%iiQ~f-Ka}xKisY#Tt>yvSsQ(q1{ZKO05OJUb-e zQZLjX=WG!H5`Bw79?!EA=&u?xuqRbvgiV7~fwh;OJw9P9_j} zIfz-^vu6#J#>i#)9hd6^TVQ9rnub23zJF38_ zp`;{88ZkYT1W;OM$<2An_v3rlz~AEhd~eI%J&4ia?-r8U6u@UI#wf=5lNc7_&~s)5 z!#dj8KdQIJHz02f#I)FRt~*-Nz*LlQuSZQ4D^q!#`!{q^`|M+u&~a9$NoPmB?>^KPf$8FsWRFQcELnLF zjQ<0i4fsQrXp%UIM8wzdjj`;5y^C9N_)XY=__q^9X5TPD&Y{T>(5T*w8)MD-_@j@0 z-TCXI6)R88O;ZdR>PJ6TeBZuU3I9%2V05`~MwiDAz^POuex5sTjXHPp#fav?Qu6;M0vxPpg=N=_p(5VwB)YLV0K=5u!5nWkqzsTk z%)~W~-~k;FAY^Zx0ztlwm|Vzh3&a*T1q?4AXwd(o;Q-iD0PQsHc}EA~w#7Eenhlv( z!>^(UAh2F5?jgmVsE_0b`}md=EakkZ1+rjkq3omGJ^yQWzqYW=Lz0E(*nrdz|B(6x zYx^z@ZwgA`zj}%SmN3+FfFwpBx-fe~j z<_!78gVDTEHmR1St=(d5bLts1ivTjx`Vwna`?_~)*zo5E?xYlqq+xkB#YEFXDu@uo z32=R!?~y=KhOgJz;G3BP+)9KjA-G@uT3ySb-%w=w+CS`+ju72f)_XLM1#=|OybL;F z-UGJR%=KTo^teD-Jzw#>*3n;WF!D}@U+j^pU(b$)<@Y3Y3S&*VWu;h)G!zxe!eyd&Q;D6wR2ftoW%j=nJ+Y=wriWn;PgMdfxW&z@U7X+6 z-=Vpak;Uy}g!LJi2tu$R?0c?-8kl>LbCuC!#?r37(iXawvj-!qa9dDdrvb9Q7n8gS z6IKsHn7gR)DSV z?X2ogYlA2$Ag#p~-N}MW?P{*0YUN{NRF->}Z*_JLWG3LrVH> znGR5fLRwgWfPCuah&aEQzLT?n+FdB@pdr6=KZe=5rqAU59;<%qgUbLm7)hQ%@^bp; z?h^sSVbz_f&RYEfj|mY`^!^_qpJVe7Vdyp%!~$~zLp$VZC-PYAdQ9PSMqFbwlJna< znk{7h9-ic-Q`tjzRAchb%q-7y4CpeEU0EO>suDlXon)HWmA%)@xwaa(R3CS|UR zhestoI9fZ@8hX{pJN}L~g@uvWPIXS8e9X2saJ-kNE%eZI_&jqciwSN4oHQJogD``X zgqC-l)sPBiyE8Swt*Td&>|deVCj@rXJ~&RM3cGame%c^RbV}bk2_NH--`a!VT6S^C zv{DxxvAn}AQae$(UMA|d*#ko;4mCn+eMgWb1bO2O&9MOO10}!3H;b9ow|zq%+O0Vl z?nb9YS`Ws<|p0&nmSIrgPSIvp2G=ymR}k(?|+#+xC#?{PJe4 zu9c_mK>xY@1zUZdgKKC;nJ|MS{e|xE8J&ofPfUR6)SQ#i6fC>WV7Q8kY<2{dSs_2Y zNFwlXc*{mwkO7k#@#~h}4sS#E*Iz}%LDI}L`KJGe&M7oPAgpczDAtU}n9 zCDsrVi_V(T9oXz;+Qg^zL^a#cBJRZ}>TiXt)w8)#GX;si9Sy>SufK(FEM(JXDQ_AH z?B4~yeccOtDhT_dlx0!AuU|;}zRP~U>NAW+@OvLC4v#V|L6F-IY;{&nze^^ZJ`kO# zVxX}2-8~ImBtt6ZvF zJLo4NnMN7_w%* zMu_NE*)`@?Ta@#g;jMTOe(9tK&glsS$D1_!?^obyOCf2Ph#8(;kfMdz+;Gg9f=r%S zAk3a)yey+_BRh=q{x)F1fIG?XCi0Z}eW_c(QV6goIZPhM6$yo7OB`;;_?cHUp-Pb5 zJZKmcMt8&GP_xHYJ=ep48EcM6@5&TNLm8d2R#|0WyVhN|K^;4BlIYzje26k4RK8Wr z(W77eREOXFJ*K+JuQ@?EbXZS}f$ufWykfU(*ht6B7bIn+>;4xu;z+kN^IEW~+bzpt zx=7wnD@d8eoo;j(M9OQz=#*Xw!zLH|f+dIdPX=~*XoBQ@ zEk21Ietd>#XJwi$Ca5uqehg+vB-bYShicMY84c6e>~2}%3D`Noa&=|mLKE*!Y&wZ7 zOCx6|wvb$%r7tx{Yb*R&s%kmI=216|Ua^ny@%-4AT6qdWR9a$JWoje+){zBNJwx#Q zwbJ0h_B$xfFFiS{fm;1l?z>_5rWoEuDk7E(SMDUucAy$vKXKzuGOwlmO)}OSvvl2u zlX}Uit^D34`H)n*e+PCQa)%d!lA`||`A1H7?wcrrI{!TOfn+vq%als&l@P@c>e+%8 zlkij!#ncR*9L%55(Z@A8OCkIC$-ai;_dW8p!O^DGZptIALGZTu4z523LTrbG&o3G9k=N$AlGv$w#VuP*v0C;7;Sy4~h0W6UU@{Xz+~@q41pRB7 zFeyN8LA#0AIiZRr&u5ETIo>D)PFookMeW{F%}Rg}E2EE{D32;#;WFe{{I}wKlQ?y0 zq6o&hk$GwnO4*#u3N>2k_n!#@b=JT;V?iW*y4A$=FEQ0T5Tv_J+HJddHK6Ke(40Lg zF|efc;%odK^R~+O(6%PR)RD-oPJwKv-@?*;HbAN1MTGLpj|#A5PjHGj->%(=fA`1C z=8sA`J02@tRL08sr0YAgDE^8iNY(?hU8Aez0pA^fstJ?7iwRSdglb+;7&>HpKETC_ zDCLU+UsxMiZDErQg)rogbA9?6pNNiOUFjN^+=v$D7zu(+@D>H$hycp9Y`Z?m`H*}b zQ0c@}_Pse4T6?CM(Gr!#hCgp}F}S3b2^0zFvN2!>`~^Mnv1Xn3bx$BnCtzgqsf~}} zd8$q+Uokr*IW(u;LPc#}e?ZPM-uMuhLF`t)bZ%;0C03c6*0Hj84>xwJV&171Y? zMySAm9M)Qf*L3&RdBoh>u)TGj@!`NZ;_36k2J{SexuE)V!4!r>JCp~oOzQD%w#_90 zFhh)OjIU12;}MK49eNIj!cZbs`WD7$H77!%)&!LF_RkCI%S{dj1DRF{dNxfL-=kX* z+jt|JrgcrzXc>v@@V-7tzYffkp*{M8udhhIH^7j(`5XQY^J<=lDIISk{Pj?*Q+8+W z7!keAogeY1IX3&(MR#4Pb5V8P^wD0Zx^vl{3#bc5mLhnOp>j;-&CG(4hE<||?)^E; z#=1V#FAp|oYnh#=8Ne_J?Gl7R0)s`&fD+r#lFXtl>5JqEbL@2 zS}ADhw7S;C-I2QgVh27c=2U#Kl$3n(;fUjH-!RZFV76p|RW$sGWeyW6*^t3lBYt(k zOU!B-fi_vV$+7nxC~X@5A@hPK#vij2yzY!jFE^a;+R8bM9^3e70G(f{T_ivA
o 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 literal 0 HcmV?d00001 diff --git a/examples/cherry-markdown-react-demo/public/manifest.json b/examples/cherry-markdown-react-demo/public/manifest.json new file mode 100644 index 00000000..c4e4f94b --- /dev/null +++ b/examples/cherry-markdown-react-demo/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/src/App.css b/examples/cherry-markdown-react-demo/src/App.css new file mode 100644 index 00000000..7e566de8 --- /dev/null +++ b/examples/cherry-markdown-react-demo/src/App.css @@ -0,0 +1,178 @@ +:root { + --width: 1400px; + --color: #e26862; +} + +header { + height: 60px; + border-bottom: 1px solid var(--color); + display: flex; + justify-content: center; +} + +h2 { + margin-top: 8px; + margin-bottom: 8px; +} + +h3 { + margin-top: 6px; + margin-bottom: 6px; +} + +.header-wrapper { + height: 100%; + width: var(--width); + display: flex; + justify-content: start; + align-items: center; +} + +.header-logo { + height: 60%; + width: auto; + object-fit: contain; +} + +.header-repo-a { + height: 100%; + display: flex; + align-items: center; + margin-left: auto; +} + +.header-text { + font-size: 20px; + text-decoration: none; + color: inherit; + padding: 10px; + + margin-left: 15px; + border-radius: 5px; +} + +.header-text:hover { + background-color: var(--color); + color: white; +} + +.header-text:nth-child(2) { + background-color: var(--color); + color: white; +} + +main { + display: flex; + align-items: center; + flex-direction: column; + width: 100%; +} + +.main-wrapper { + width: var(--width) +} + +.title-wrapper { + width: 100%; +} + +.title { + display: flex; + justify-content: start; +} + +.menu { + border: 1px solid gray; + padding: 8px; + border-radius: 5px; + margin-bottom: 10px; +} + + +#markdown-container { + width: 100%; + height: 800px !important; +} + +.custom-select-container { + display: inline-block; + min-width: 250px; + text-align: center; + position: relative; + z-index: 100; + margin-left: 5px; +} + +.custom-select { + padding: 5px; + border-bottom: 1px solid var(--color); +} + +.custom-option-wrapper { + position: absolute; + width: 100%; + margin: 0; + padding: 0; + text-align: center; + background: white; + border: 1px solid var(--color); + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} + +.custom-option { + list-style-type: none; + margin: 6px 10px; + border-bottom: 1px solid gainsboro; + cursor: pointer; + line-height: 30px; + border-radius: 5px; +} + +.custom-option:hover { + /* background-color: gray; */ + color: #ffffff; + background-color: var(--color); +} + + +.code-input { + width: 99%; + max-width: 99%; + padding: 5px; + font-size: 20px; +} + + +.demo-desc { + font-size: 17px; + white-space: pre-wrap; +} + + +.run-btn { + background-color: white; + font-size: 20px; + color: white; + border: 1px solid var(--color); + background-color: var(--color); + border-radius: 5px; + margin-left: 5px; + cursor: pointer; +} + +.run-btn:disabled { + background-color: gainsboro; + border: 1px solid gainsboro; + cursor: default; +} + +.run-btn:disabled:hover { + background-color: gainsboro; + border: 1px solid gainsboro; +} + +.run-btn:hover { + background-color: red; + border: 1px solid red; +} \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/src/App.jsx b/examples/cherry-markdown-react-demo/src/App.jsx new file mode 100644 index 00000000..04d749ca --- /dev/null +++ b/examples/cherry-markdown-react-demo/src/App.jsx @@ -0,0 +1,40 @@ +import { useEffect, useState, useRef } from 'react'; +import './App.css'; +import Header from './components/Header' +import 'cherry-markdown/dist/cherry-markdown.css'; +import Cherry from 'cherry-markdown'; +import Title from './components/Title'; +import Menu from "./components/Menu"; + +function App() { + const editorRef = useRef(null); + const [editor, setEditor] = useState(null); + useEffect(() => { + if (editor == null) { + // 初始化编辑器 + const config = { + el: editorRef.current, + value: '', + callback: { + afterChange: (md, html) => console.log('change'), + }, + }; + setEditor(new Cherry(config)); + } + }, []); + return ( + <> +
+
+
+ + <Menu cherryObj={editor} /> + {/* 该div作为编辑器的最外层容器 */} + <div ref={editorRef} id="markdown-container" /> + </div> + </main> + </> + ); +} + +export default App; \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/src/components/Header.jsx b/examples/cherry-markdown-react-demo/src/components/Header.jsx new file mode 100644 index 00000000..18f9efcf --- /dev/null +++ b/examples/cherry-markdown-react-demo/src/components/Header.jsx @@ -0,0 +1,16 @@ +export default () => { + return ( + <header> + <div className="header-wrapper"> + <img src="logo--color.png" className="header-logo" /> + <a href="#" className="header-text">Demo</a> + <a href="https://github.com/Tencent/cherry-markdown/wiki/%E5%88%9D%E8%AF%86cherry-markdown-%E7%BC%96%E8%BE%91%E5%99%A8" target="_blank" className="header-text">介绍</a> + <a href="https://tencent.github.io/cherry-markdown/examples/api.html" target="_blank" className="header-text">API文档</a> + <a href="https://tencent.github.io/cherry-markdown/examples/preview_only.html" target="_blank" className="header-text">Markdown语法</a> + <a href="https://github.com/Tencent/cherry-markdown" target="_black" className="header-repo-a"> + <img src="github.svg" className="header-logo" /> + </a> + </div> + </header> + ) +} \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/src/components/Menu.jsx b/examples/cherry-markdown-react-demo/src/components/Menu.jsx new file mode 100644 index 00000000..269512a5 --- /dev/null +++ b/examples/cherry-markdown-react-demo/src/components/Menu.jsx @@ -0,0 +1,496 @@ +import { useEffect, useRef, useState } from "react" + +const Select = ({ options, onChange, selected }) => { + const [showOptions, setShowOptions] = useState(false); + return ( + <div className="custom-select-container"> + <div + className={showOptions ? "custom-select active" : "custom-select"} + onClick={() => { setShowOptions(prev => !prev); }} + > + {options[selected]} + </div> + {showOptions && ( + <ul className="custom-option-wrapper"> + {options.map((option, idx) => { + return ( + <li key={idx} + className="custom-option" + onClick={() => { onChange(idx); setShowOptions(false); }} + > + {option} + </li> + ) + })} + </ul> + )} + </div> + ); +} + +const getFullCode = (code) => `\`\`\`javascript +import { useEffect, useState, useRef } from 'react'; +import 'cherry-markdown/dist/cherry-markdown.css'; +import Cherry from 'cherry-markdown'; +export default () => { + const editorRef = useRef(null); + const [cherry, setCherry] = useState(null); + useEffect(() => { + if (cherry == null) { + // 初始化编辑器 + const config = { + el: editorRef.current, + value: '', + callback: { + afterChange: (md, html) => console.log('change'), + }, + }; + const cherryObj = new Cherry(config); + setCherry(cherryObj); + // api操作如下 + ${code} + } + },[]) + return (<div ref={editorRef} />); +} +\`\`\`` + +const data = [ + { + name: "Cherry API", api: [ + { + name: "setMarkdown", title: `setMarkdown(content:string, keepCursor = false)`, + desc: `设置内容,直接编辑器中的全部文本。setValue(content:string, keepCursor = false)有同样的功能,keepCursor = true 时更新内容的时候保持光标位置`, + code: `cherryObj.setMarkdown("初始内容"); +setTimeout(()=>{cherryObj.setMarkdown("2秒后替换成的新内容")},2000);`, + markdown: `# Cherry API +## setMarkdown(content:string, keepCursor = false) +设置内容 +${getFullCode(`cherryObj.setMarkdown("初始内容"); + setTimeout(()=>{cherryObj.setMarkdown("2秒后替换成的新内容")},2000);`)} + + ` + }, + { + name: "insert", + title: `insert(content: string, isSelect = false, anchor = false, focus = true)`, + desc: `插入内容\nisSelect = true 选中刚插入的内容\nanchor = false 在光标处插入内容\nanchor = [1, 3] 在第2行第4个字符处插入内容`, + code: `cherryObj.insert("在光标处插入内容"); +cherryObj.insert("在第二行插入内容,并选中插入的内容", true, [1, 0]);`, + markdown: `# Cherry API +## insert(content: string, isSelect = false, anchor = false, focus = true) +在光标处或者指定行 + 偏移量插入内容 + > insert(\`content\`, \`isSelect\`, \`anchor\`, \`focus\`) +- \`content\` 被插入的文本 +- \`isSelect\` 是否选中刚插入的内容,默认false,不选中 +- \`anchor\` [x,y] 代表x+1行,y+1字符偏移量,默认false 会从光标处插入 +- \`focus\` 保持编辑器处于focus状态,默认true,选中编辑器(用户可以继续输入) +${getFullCode(`cherryObj.insert("在光标处插入内容"); + cherryObj.insert("在第二行插入内容,并选中插入的内容", true, [1, 0]);`)}`, + }, + { + name: "getMarkdown", + title: `getMarkdown()`, + desc: `获取markdown内容`, + code: `alert(cherryObj.getMarkdown()); +console.log(cherryObj.getMarkdown());`, + markdown: `# Cherry API +## getMarkdown() +获取markdown内容 +${getFullCode(`alert(cherryObj.getMarkdown()); + console.log(cherryObj.getMarkdown());`)}`, + }, + { + name: "getHtml", + title: `getHtml()`, + desc: `获取渲染后的html内容`, + code: `alert(cherryObj.getHtml()); +console.log(cherryObj.getHtml());`, + markdown: `# Cherry API +## getHtml() +获取渲染后的html内容 +${getFullCode(`alert(cherryObj.getHtml()); + console.log(cherryObj.getHtml());`)}`, + }, + { + name: "destroy", + title: `destroy()`, + desc: `销毁函数`, + code: `cherryObj.destroy();`, + markdown: `# Cherry API +## destroy() +销毁函数 +${getFullCode(`// cherryObj.destroy(); `)}`, + }, + { + name: "resetToolbar", + title: `resetToolbar(type:string, toolbar:array)`, + desc: `重置工具栏 +type 修改工具栏的类型 {'toolbar'|'toolbarRight'|'sidebar'|'bubble'|'float'} +toolbar 工具栏配置`, + code: `cherryObj.resetToolbar('toolbar', ['bold', 'table']);`, + markdown: `# Cherry API +## resetToolbar(type:string, toolbar:array) +重置工具栏 +type 修改工具栏的类型 {'toolbar'|'toolbarRight'|'sidebar'|'bubble'|'float'} +toolbar 工具栏配置 +${getFullCode(`cherryObj.resetToolbar('toolbar', ['bold', 'table']);`)}`, + }, + { + name: "export", + title: `export(type:string)`, + desc: `导出预览区域的内容,type:{'pdf'|'img'}`, + code: `if(confirm('导出pdf')) { + cherryObj.export(); +}else if(confirm('导出长图')) { + cherryObj.export('img'); +}`, + markdown: `# Cherry API +## export(type:string) +导出预览区域的内容,type:{'pdf'|'img'} +${getFullCode(`if(confirm('导出pdf')) { + cherryObj.export(); + }else if(confirm('导出长图')) { + cherryObj.export('img'); + }` + )}`, + }, + { + name: "switchModel", + title: `switchModel(model:string)`, + desc: `切换模式:{'edit&preview'|'editOnly'|'previewOnly'}`, + code: `if(confirm('只读模式')) { + cherryObj.switchModel('previewOnly'); +}else if(confirm('纯编辑模式')) { + cherryObj.switchModel('editOnly'); +}else if(confirm('双栏编辑模式')) { + cherryObj.switchModel('edit&preview'); +}`, + markdown: `# Cherry API +## switchModel(model:string) +切换模式:{'edit&preview'|'editOnly'|'previewOnly'} +${getFullCode(`if(confirm('只读模式')) { + cherryObj.switchModel('previewOnly'); + }else if(confirm('纯编辑模式')) { + cherryObj.switchModel('editOnly'); + }else if(confirm('双栏编辑模式')) { + cherryObj.switchModel('edit&preview'); + }` + )}`, + }, + { + name: "getToc", + title: `getToc()`, + desc: `获取由标题组成的目录`, + code: `alert(cherryObj.getToc()); +console.log(cherryObj.getToc());`, + markdown: `# Cherry API +## getToc() +获取由标题组成的目录 +${getFullCode(`alert(cherryObj.getToc()); + console.log(cherryObj.getToc());`)}`, + }, + { + name: "getCodeMirror", + title: `getCodeMirror()`, + desc: `获取左侧编辑器实例`, + code: `alert(cherryObj.getCodeMirror()); +console.log(cherryObj.getCodeMirror());`, + markdown: `# Cherry API +## getCodeMirror() +获取左侧编辑器实例 +${getFullCode(`alert(cherryObj.getCodeMirror()); + console.log(cherryObj.getCodeMirror());`)}`, + }, + { + name: "getPreviewer", + title: `getPreviewer()`, + desc: `获取右侧预览区对象实例`, + code: `alert(cherryObj.getPreviewer()); +console.log(cherryObj.getPreviewer());`, + markdown: `# Cherry API +## getPreviewer() +获取右侧预览区对象实例 +${getFullCode(`alert(cherryObj.getPreviewer()); + console.log(cherryObj.getPreviewer());`)}`, + }, + + ], + }, + { + name: "Cherry.Previewer API", + api: [{ + name: "Previewer.scrollToId", + title: `Previewer.scrollToId(id:string)`, + desc: `滚动到对应id的元素位置 +id 可以为带#号hash,也可以是id值`, + code: `// 查看可跳转的id +console.log(cherryObj.getToc()); +// 两种方式都可获得previewer对象 +console.log(cherryObj.previewer == cherryObj.getPreviewer()) +//两种写法都可以 +// cherryObj.previewer.scrollToId('#test-scroll'); +cherryObj.previewer.scrollToId('test-scroll');`, + markdown: `# Cherry API +## Previewer.scrollToId(id:string) +滚动到对应id的元素位置 +id 可以为带#号hash,也可以是id值 +// 查看可跳转的id +${getFullCode(`// 查看可跳转的id + console.log(cherryObj.getToc()); + // 两种方式都可获得previewer对象 + console.log(cherryObj.previewer == cherryObj.getPreviewer()) + // 两种写法都可以 + // cherryObj.previewer.scrollToId('#test-scroll'); + cherryObj.previewer.scrollToId('test-scroll');`)} +# Test Scroll`, + }] + }, + { + name: "Cherry.engine API", + api: [ + { + name: "makeHtml", + title: `engine.makeHtml(markdown:string)`, + desc: `将markdown字符串渲染成Html`, + code: `alert(cherryObj.engine.makeHtml('This is \`inline code\`')); +console.log(cherryObj.engine.makeHtml('This is \`inline code\`'));`, + markdown: `# Cherry.engine API +## engine.makeHtml(markdown:string) +将markdown字符串渲染成Html +${getFullCode(`alert(cherryObj.engine.makeHtml('This is \`inline code\`')); + console.log(cherryObj.engine.makeHtml('This is\`inline code\`'));`)}`, + }, + { + name: "makeMarkdown", + title: `engine.makeMarkdown(html:string)`, + desc: `将html字符串渲染成markdown`, + code: `var html = \` < p > This is<code>inline code</code></ >\`; +alert(cherryObj.engine.makeMarkdown(html)); +console.log(cherryObj.engine.makeMarkdown(html));`, + markdown: `# Cherry.engine API +## engine.makeMarkdown(html:string) +将html字符串渲染成markdown +${getFullCode(`var html = \` < p > This is < code > inline code</ ></ >\`; + alert(cherryObj.engine.makeMarkdown(html)); + console.log(cherryObj.engine.makeMarkdown(html));`)}`, + }, + ], + }, + { + name: "Cherry.toolbar.toolbarHandlers API", + api: [ + { + name: "bold", + title: `toolbar.toolbarHandlers.bold()`, + desc: `在cherry编辑区域的选定文本处插入加粗语法`, + code: `cherryObj.toolbar.toolbarHandlers.bold()`, + markdown: `# Cherry.toolbar.toolbarHandlers API +## toolbar.toolbarHandlers.bold() +在cherry编辑区域的选定文本处插入加粗语法 +${getFullCode(`cherryObj.toolbar.toolbarHandlers.bold()`)}`, + }, + { + name: "italic", + title: `toolbar.toolbarHandlers.italic()`, + desc: `在cherry编辑区域的选定文本处插入斜体语法`, + code: `cherryObj.toolbar.toolbarHandlers.italic()`, + markdown: `# Cherry.toolbar.toolbarHandlers API +## toolbar.toolbarHandlers.italic() +在cherry编辑区域的选定文本处插入斜体语法 +${getFullCode(`cherryObj.toolbar.toolbarHandlers.italic()`)}`, + }, + { + name: "header", + title: `toolbar.toolbarHandlers.header(level: int)`, + desc: `在cherry编辑区域的选定文本处插入标题语法`, + code: `cherryObj.toolbar.toolbarHandlers.header(1) +// cherryObj.toolbar.toolbarHandlers.header(2) +// cherryObj.toolbar.toolbarHandlers.header(4)`, + markdown: `# Cherry.toolbar.toolbarHandlers API +## toolbar.toolbarHandlers.header(level: int) + +在cherry编辑区域的选定文本处插入标题语法 + +${getFullCode(`cherryObj.toolbar.toolbarHandlers.header(1) + // cherryObj.toolbar.toolbarHandlers.header(2) + // cherryObj.toolbar.toolbarHandlers.header(4)`)}`, + }, + { + name: "strikethrough", + title: `toolbar.toolbarHandlers.strikethrough()`, + desc: `在cherry编辑区域的选定文本处插入删除线语法`, + code: `cherryObj.toolbar.toolbarHandlers.strikethrough()`, + markdown: `# Cherry.toolbar.toolbarHandlers API +## toolbar.toolbarHandlers.strikethrough() +在cherry编辑区域的选定文本处插入删除线语法 +${getFullCode(`cherryObj.toolbar.toolbarHandlers.strikethrough()`)}`, + }, + { + name: "list", + title: `toolbar.toolbarHandlers.list(type: string)`, + desc: `在cherry编辑区域的选定文本处插入有序、无序列表或者checklist语法`, + code: `if(confirm('有序列表')) { + cherryObj.toolbar.toolbarHandlers.list(1); + }else if (confirm('无序列表')) { + cherryObj.toolbar.toolbarHandlers.list('2'); + } else if (confirm('checklist')) { + cherryObj.toolbar.toolbarHandlers.list(3); + } `, + markdown: `# Cherry.toolbar.toolbarHandlers API +## toolbar.toolbarHandlers.list(type: string) +在cherry编辑区域的选定文本处插入有序、无序列表或者checklist语法 +| level | 效果 | +|:-:|:-:| +| 1 | ol 列表 | +| 2 | ul 列表 | +| 3 | checklist | +${getFullCode(`if(confirm('有序列表')) { + cherryObj.toolbar.toolbarHandlers.list(1); + }else if (confirm('无序列表')) { + cherryObj.toolbar.toolbarHandlers.list('2'); + } else if (confirm('checklist')) { + cherryObj.toolbar.toolbarHandlers.list(3); + } `)}`, + }, + { + name: "insert", + title: `toolbar.toolbarHandlers.insert(type: string)`, + desc: `在cherry编辑区域的光标处插入特定语法`, + code: `if (confirm('插入3*4的表格')) { + cherryObj.toolbar.toolbarHandlers.insert('normal-table-3*4'); +} else if (confirm('插入checklist')) { + cherryObj.toolbar.toolbarHandlers.insert('checklist'); +} `, + markdown: `# Cherry.toolbar.toolbarHandlers API +## toolbar.toolbarHandlers.insert(type: string) +在cherry编辑区域的光标处插入特定语法: + +| type | 效果 | +|:-:|:-:| +| 'hr' | 删除线 | +| 'br' | 强制换行 | +| 'code' | 代码块 | +| 'formula' | 行内公式 | +| 'checklist' | 检查项 | +| 'toc' | 目录 | +| 'link' | 超链接 | +| 'image' | 图片 | +| 'video' | 视频 | +| 'audio' | 音频 | +| 'normal-table' | 插入3行5列的表格 | +| 'normal-table-row*col' | 如 \`normal-table-2*4\` 插入2行(包含表头是3行)4列的表格 | + +${getFullCode(`if (confirm('插入3*4的表格')) { + cherryObj.toolbar.toolbarHandlers.insert('normal-table-3*4'); + } else if (confirm('插入checklist')) { + cherryObj.toolbar.toolbarHandlers.insert('checklist'); + } `)}`, + }, + { + name: "graph", + title: `toolbar.toolbarHandlers.graph(type:string)`, + desc: `在cherry编辑区域的光标处插入画图语法`, + code: `cherryObj.toolbar.toolbarHandlers.graph(1) +// cherryObj.toolbar.toolbarHandlers.graph('2') +// cherryObj.toolbar.toolbarHandlers.graph(4)`, + markdown: `# Cherry.toolbar.toolbarHandlers API +## toolbar.toolbarHandlers.graph(type:string) +在cherry编辑区域的光标处插入画图语法 + +|id |效果 | +|:-:|:-:| +|'1' |流程图 | +|'2' |时序图 | +|'3' |状态图 | +|'4' |类图 | +|'5' |饼图 | +|'6' |甘特图 | +\`\`\`mermaid +graph LR + A[公司] -->| 下 班 | B(菜市场) + B --> C{看见<br>卖西瓜的} + C -->|Yes| D[买一个包子] + C -->|No| E[买一斤包子] +\`\`\` +${getFullCode(`cherryObj.toolbar.toolbarHandlers.graph(1) + // cherryObj.toolbar.toolbarHandlers.graph('2') + // cherryObj.toolbar.toolbarHandlers.graph(4)`)}`, + }, + ] + } +] + +const CodeTextArea = ({ value, onChange }) => { + const ref = useRef(null); + useEffect(() => { + // 高度适应内容 + const resize = () => { + if (ref.current) { + ref.current.style.height = 'auto'; // 重置高度 + ref.current.style.height = `${ref.current.scrollHeight}px`; // 设置新高度 + } + }; + resize(); + }, [value]); + return ( + <textarea ref={ref} className="code-input" placeholder="请输入可执行的js代码,编辑器对象为'cherryObj'" onChange={e => onChange(e.target.value)} value={value} /> + ) +} + +export default ({ cherryObj }) => { + const [selectedGroup, setSelectedGroup] = useState(0); + const [selectedApi, setSelectedApi] = useState(0); + const [title, setTitle] = useState(""); + const [desc, setDesc] = useState(""); + const [code, setCode] = useState(""); + const [allowReset, setAllowReset] = useState(false); + const handleOptionChange = (groupIdx, apiIdx) => { + setSelectedGroup(groupIdx); + setSelectedApi(apiIdx); + const content = data[groupIdx].api[apiIdx]; + setCode(content.code); + setDesc(content.desc); + setTitle(content.title); + cherryObj.setMarkdown(content.markdown); + } + const reset = () => { + const content = data[selectedGroup].api[selectedApi]; + setCode(content.code); + cherryObj.setMarkdown(content.markdown); + } + const computeAllowReset = () => { + const content = data[selectedGroup].api[selectedApi]; + const allow = content.name != "resetToolbar" && content.name != "destroy"; + setAllowReset(allow); + return + } + useEffect(() => { + if (cherryObj) { + handleOptionChange(selectedGroup, selectedApi); + } + computeAllowReset() + }, [cherryObj, selectedGroup, selectedApi]) + return ( + <div className="menu"> + <div> + <span>选择例子</span> + {cherryObj && <> + <Select options={data.map(datum => datum.name)} onChange={(idx) => { setSelectedGroup(idx); setSelectedApi(0) }} selected={selectedGroup} /> + <Select options={data[selectedGroup].api.map(datum => datum.name)} onChange={setSelectedApi} selected={selectedApi} /> + </>} + </div> + <h2>{title}</h2> + <h3>描述</h3> + <div className="demo-desc">{desc}</div> + <h3> + <span>代码示例</span> + {cherryObj && <> + <button className="run-btn" onClick={() => { eval(code); }}>点击执行</button> + <button className="run-btn" onClick={reset} disabled={!allowReset}>{allowReset ? "重置" : "请通过刷新恢复"}</button> + </>} + </h3> + <CodeTextArea value={code} onChange={setCode} /> + </div> + ) +} \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/src/components/Title.jsx b/examples/cherry-markdown-react-demo/src/components/Title.jsx new file mode 100644 index 00000000..09240f00 --- /dev/null +++ b/examples/cherry-markdown-react-demo/src/components/Title.jsx @@ -0,0 +1,7 @@ +export default () => { + return ( + <div className="title-wrapper"> + <h2 className='title'>React Demo</h2> + </div> + ) +} \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/src/index.css b/examples/cherry-markdown-react-demo/src/index.css new file mode 100644 index 00000000..3e3b6a19 --- /dev/null +++ b/examples/cherry-markdown-react-demo/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} \ No newline at end of file diff --git a/examples/cherry-markdown-react-demo/src/index.js b/examples/cherry-markdown-react-demo/src/index.js new file mode 100644 index 00000000..f45e211e --- /dev/null +++ b/examples/cherry-markdown-react-demo/src/index.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + <App /> +); \ No newline at end of file