From c79fb7de78c6d561d841098d82b64e1865a547bf Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Thu, 27 Jun 2024 19:03:02 -0500 Subject: [PATCH] start --- .gitignore | 1 + biome.json | 17 +++++ bun.lockb | Bin 0 -> 64780 bytes index.html | 2 + package.json | 16 +++++ src/App.module.scss | 21 +++++++ src/App.tsx | 148 ++++++++++++++++++++++++++++++++++++++++++++ src/global.scss | 8 +++ src/index.tsx | 5 ++ tsconfig.json | 5 ++ vite.config.ts | 6 ++ 11 files changed, 229 insertions(+) create mode 100644 .gitignore create mode 100644 biome.json create mode 100755 bun.lockb create mode 100644 index.html create mode 100644 package.json create mode 100644 src/App.module.scss create mode 100644 src/App.tsx create mode 100644 src/global.scss create mode 100644 src/index.tsx create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..7f434f4 --- /dev/null +++ b/biome.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "organizeImports": { + "enabled": true + }, + "formatter": { + "enabled": true, + "indentWidth": 2, + "indentStyle": "space" + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..c178a16eac9b2de655923c8669c82c9f69240911 GIT binary patch literal 64780 zcmeFa2UHYG*ELKSvSbj+QIcc?Bu4>7GAM$eBmqg1K@gFwWF#mO1QkgNl8h)pB`5|2 zBuLJPpde9{zlxc;_PzHBdU@CS{`dQ=<@Qj0dha@Ys=B(mrw6z>MSZ+HMQxniMD5*t zIc$8~NCAXh+-;qYJGs~kA9we3we}YF6(hyN!NKX7*FP=)t#8BSYR$`5CG+ogd9H0W z)VN`O9}Vx_yH&#!v<`LxYX=;IANxTb)V22Xv<{FYK(`C(UXIo$?SVaM?d^yo0rsas^Sx}XUF<(& z;u8SdVf<{sLVY@5Nr7DheoR~r_U@Z;gOu7rz2o-YwvNEsdHLetc>1A_s|lnG^7aEU z$a@UxP`4KoHwAD)zX|w{8rU~rJFEj8TSsu1z}k5FctKrje-Ung&SsC?YZ-q+sE z+so74#l`0&4vscR4lCdZ0m}j`C8oXvlF10_UBI#dTL~-=uqnWD0PBm{ZVW6psEY&3 z1uPA)?7%L7prP#m7V@hx^$cKPJ7<7p2G$c;KnXeN?&WRk<%Ls2hptxu3y;Um+Rfd` z4hP5H%f`pa#SSN&0iAabU}=Hh5?HWnhz@4E45rS5@zY?of8UPYZxUGWFQgq)uK^b9 z5^@8xJq1{(8-nq>0So)t98=c-7W@kl2mitSSb(JmmKazV$2_P&e?PFW{sLIY_p$~D z2g*K>4A_sLlboIGz!0;ywzG5cJSh&61-E|yemGv8yo4QG+--1X!FHI3KFoH1kbKzR zZJ-XvmyM^jtvyiQ%Zc7E23W}N0~YF8y9I#r*49xHw7mn=3qU8q?VX_0n1FqVvH8Hl zxZ{9@{eBWyI4%xh>dL@EK0mN9UsRa-I?&rZZeU@Yh7V>vu zECa@pVC*`E2M&L8zedn8FwZ5x!t)^oSeWNvU}1mQ0}J~@4_KIYX^fu(0xVOE{4 z)zqLJk^`ib>%q+}sRfa1X$vb_T>f*avI*mL+asijb9wMjtTZ1GCAl>7!s{Z*=qCZc z9xa~Gb#9IQq1CU5lkiAKzL8GsrVWdhIXw4lGDMw{{Qce*-Qpt?Jqnhe6t1pwaMoHE z3-H<>RW@%fRl09yqQJ3A&Q(RPa^+Jeg{&J5z0SV)pp&$By}0hzUA=zVM>}q*mi%SG z=`xoQl9gAJ7PXAKnA`S@y(TW2UV6#SWyWgIalibbiPMJtEq;^}l@`gG|J}q;hKSkd zegl_YZOZGaNztB{`TC;wky8%85f@s2zRr3|A9pOMBSe|V@VIAmvzub$%)t$tP>Kad0tS$xb&TQF;N%)p|hnsqO&!<3r3aY3#6+2Ou zVv`ZHgx+m-E-x4G3^QhDRj$1GUbQhZPs-G?^8wv<8q0)Bp-NVG58LO62aZ_3Y?(Pg ze0EpIRnIrhYVN=6znE+ z9k0ik)#9DPQz2*2n99E8+bE`%ar6!$C0;hO*!{Otw&pw5*2Ilkr0tEAUhiUIrM}Tb zWRR{o>0004|2V5yqDIzeLhU?tlB2(*-sDX)ULhri+Vhso%A)tV*wYzVxr|~=kIihX zC0Z{95n5hWjJ@vW7k+$PMBhh>G;63QoM=Q+gR<;~ciijqcCpn5o!)29W=B3yFZf20 zUrX0Tc)wwSU2&oIb_Yit1%9$#Dz`>^4Hqel1ksn~XZ7Rb4 z@F}hAoBb|p$4swxPe-S7Cl|DS-lu0-v`smzP&W0X5#xMLzm#EsB~zAyX3<6=%WXBv z=m+_2JN(`VG12d&KFe+Vq9*o>Cf!`XGub8YZDau#l0wCDYFF}}&F((de|xTGkjISY z4E~dXR;^yT(2nt|@LuIa46wql%F)YQANA#3fJQ7p2?u`POkJ48mYDIZnhHJhOU=6yp6;gSBv*x zw+ z{|Xnjf#sh7e0cnD8@$$Ix5N5>D%kz51HKU86M>yc{uALZJ}loE{2&eZF!#Swu>4%` zgBpSl^Y}9b%O3$hs37=1!-e}``P?AzJqZ4IW8e8D!Alm=gwvq9_1AJuue^vu~ z{N!NZDQzKs9l%Gn|Eu{w3-~a9!2I;Ni#-0Pi1vT>*pd9NfRF8em=hSs&lGI_6u|u{ zJb$ru&>cTju>1Q0KC=CvB7>c<{9AyJ&Hq0+{!zd`0OE&n{}a9#Av%6+-mr1~x8uO> z7Y_I^fB&@oM+iP_^Jk9%+x`mRZ)N;TgNaiC6aPPH-xKgv5XX;=3u^tRg59qi@OLBl zzk2+0fR7!2|Eayd`mpWuf#)Jf{;#$l1NhkZVH>~7Zv%XI{enC=hJN+@`wsZ9{{R&S z4256j9|RXo6~Kr2fH7e60)G6D1-oAw;KT6`y5Xm}P#DX94ft^U!q{QKo`>k#zyBZ0 z-$9B#elQJemS4@kJ>c&L?PK$f)&2J^qdnODN-^zYu*G5v{93?2yahfZcu2LC_Pqh$cnj?}0>0%I_&nfYn9~;cae!~V1^z7HZzX;m z@UYKe3+B=#QWcf7(bTZ0{Ci}_OaW3MgqIA=U;do|Cxg2rvg4ae#iko;{0m<(!fo>#uoTJfUmg)KF1F58P*o~j)1?F z_)E6f{ujXC%JIvA4+pjqe=y*0C4bKVAKt(H)A2*Yw6*rl0bg$m?H2(4R^tBx_*?2f z=B*unDBy3U|DSEKeG2el?pE?=4)|Ni-xa`z>;Hc`{-yzcE8|}Re0aB&;|~V>t+f9P z@VCf0T2g}duVEHS6uYidk#{R4E@8&?q5A}YQ z3wHe@|K|W7o`2Z9i(8`1)Ije--dex4_riiN1gPr^jCm_*-co z-tfTZ5C61%W574u0{_|;+g}EJoh`Jl#`oj?2j&1g&itu@9lrs95AQ!A3G#pS{Hp|f zHNXeA@juOlZDHGA0(>}sVEMn=zA8U>cMR~sW2DXVe;XA4iGpoE67aFlAOFemKLmV9 z&^{4p69w?A@plWN$1j$Lwg2r9u<;A+LXRKVHY~r&_rvh9Jgg2{?ce{8ZNDDymBH~t z{y)hdt`K;q5W|P95&!!p|K!28zaQ|m5q$7C`=<()pA7g~i1=YY{!GF0y8$1LKWzR8 ze})Y9!SdOJx5l@{@UiXwYX21gzB(d)Y+V2CFtGc5_zNE!$ItQ=M1G7Ps0qi=&lGI? zfq<_H;wJ^`NLaw*@SiGJ{zD8O+kaU5-$bzdZ-3$cYW|f(e;hw-6RZ2P?Vm>QVeWo4 z{|^CQ5i$P%Tf104HqKQ{``F|D)$@0c*pK$1_OIsO2k=!8YX0p0 z!vEF$k4_o!glY|izvz`}yGaDT9--Yoyh!m$UQ8Eoo;bAQt+VJw)7 zHVd>+M`g2ywveX=KmeeH+5R&Y>SzPN;{>_d)IETygWPWxXyJYa=<1&=+z!Ubrr!wT zhZb%J_kNq!1XvgsIF~jHv}6Ew=<24W0YCd<>i>VRFdqS!c>a|IA1&b|V*LLbEj+#p znE0WE^TTBT*l(Etuplkmp0&9ZZDBndQ->DTb1?P_#^z#d94P27*FM5CuURk^@Y=PR z@89$0W;!^zHu^PTcLoS6S73(&*G=pv<2 z<8FK^MZq^yEZ(-Yvgc?@hIr_)`%WC4Pc4sjOMjr3bUP@gCi(kW5w*G_(Ie$)gKnO< z(uLZ+freR97uuTJxe>bX+JY^pI?-G_JfjF!(r}$uJfs767j@qaEp+znm=3lTxt-{y zxpMz5CG9urnz^dPTA#;j+e7?$hiq65O+H{aVSG@z`W!+Ro@3a8O5b%`;l#$_73b8c zYU0Q3o<1z^WCZ!~cu@+oN%38yN9WbJEtBIPou=u3bVP~`UqXjs;De!((r3LEB01pvHs7B<&4rNU?YD22&QzH<6h6P$iqJ*gFQWoY z_e@Laf2C-;KR|M)Ww;}TQtq=;Sp2n>Qrc_0UzBG?NVP|md;%?G8u%oMo*PZz&v1_? zzkNsia#{D7uwQsMLKk@-k8-c&JruyMT9k%2p6T*6&gD_*3C5Q@SM?%hbY3)&ixA%L zotubK^Lm)YB>&xf^yx7J!{nL3D&@Cd`r`(8RqCH3bm1BTTTpn(rNQzvTE)lSmN<*X zt&r_4_rCvi=L{~9Pw>VWL$N?6OUcN$vNzWU8BY1jN+#D`-p_rR#yS67qG^`7(kGn} zgf8-P6I8I>o5K|cGMdwhOZFYAU0l>3Y*_kK;@5hVsYzyN;5f-bq4?C2WIlhBi>~&& zJ(s#=JIxl!A3l%Gb}5b+Qdm}?+?N_xE zX^$34DY|^^LFjJ7iUNyzN%=maYTlpufVxpHdfb!LbEZ$Np~D$z{v>CI?cBnr&2L z^N&8CW34bNoFTvP^rOTxKZbohRieE-C*68e8?zF}zS}p6?;wdeYUr$Syq0p?@LNQ@ z@O~LvP^D55iu!G{59xx9C4^fw4#=EMP&d5mc^>!qY;x?U$R_7$ip;SA$*3slv&3E9 z)!sgSk-`pA3YLL`-)FKQBU;7Ap!Y zYScf__6U{Rl5F1DD~e@3q8Bd;;V+0w?TUSVy_r>dy$gTxfL|Q&|Atm{E zxGFTNr7?GBd*tdX*H#1Vy4nJ{`X=i7r30-p2t z{IjMq@)FoSaV{KO$r*S2suY9r8!Har;eBqXNcO_=NVar6tK*4QRFjO2Q(%i>KskyY zp}QTaiytVf=y+){B%Oh;>Z+TZU<`}+iJVW&_=}@9d>;KXqIv`+`@@L>4j5$7vVS!# zzu8DP^I&7~V*TC{oq#z9R5L;su7R-y#h7~cL$s83a8H=8e1!kNXC}J4xY|Yg^zd%J z(6pj(I)3?pk=d7pGqeYJ(&(Q@jU@IhC^ie*ozx9204roy39!3 z{-xU4#jd*Ls(sNKlyf2Pc&MXzvfv*~h=WkoDc_G&>jn){$ruqeBDwv8>4Xj2$ZqEGqdD6-R4j&M7hzn#iysqMKTrEUlmS$kM)) zUkjLfqSA5*9mv)1pAl7VC}$ZzqmoK$kSTti zBQ)y(?;6i?*6q&OSSu?PgG%eO1SUrPrO7OOCQCOA?{WjnuWyklEFm zOj&8uB8+O+pv;q4nB8xc@14lqCZTw{G0gOx<%iy}HtpFSPnl!*PaLRRJRa1%NMh4v zaVO1GyPHyt(B(nus#T>EjT`JU9_O^-tW>XIpA=)gN@&9wK*~JHH(X<9{p|Zw7w3|^ zQ?>Z*#~wGvmryJ2wmQP!IjgwW)b!;t%>{(+PNeRou8*8qhlKDI+}wv6eahlKf1Kjy zIHfnut|ud3A08{!IKfBnpK8jYy}S*$jfC91ckNW`q?K1O)rBl1{qN4oEN)1KhzIGOgEcU#PZF0|C%{8~O z&OULON|)X0@u3`qE+0~t(_fOnxlCz7 ztaQjCSGUt`uE?FaGTgA?`zia$tUT^DvX4gP-^s2oq`YjTj3&fUFciLBS2?*G5w8GJ z*F?`~O458ynYUbL=S&dylez{;m$+R%^~oQI(^6M25Z)qKoNX!-mZr2N%^7A1uByh{ zbNf-!y4p4~!Eny5x9~m_dtWGs)Wx|sVs2`d6iCnEq*p!`_3q=WuO=A!Q<5Rt81Sl0(ocER zh|mSEbNo?I(+2W;sOwUF#G^AU)GGTRppDd6tURd9~GW^$hw2=F2M?`*`=(N5sL!}m)81qrQg}NUdfb9V;E2eH^tce z!q0=S1*K-=^)BpP|HED$ZXazohQOs-+|wD8J!;C&F83_Yb~&b$)>3)a+#oqRN%e6) zYskEW?ZHcZnn)vw!!eN&o(Qgh8b1#x*Dx3hSYsF zxl`*zoykdAqk4s?r_4tq_Knm~Rvyb4Gk2+Ho9dZjDxi+g3uJ#qk9(?TJAq3+%L@w* zSG>z?T}xNGUp~6KfQdH*Xo8QS|0pPfX<7Q-m!kUFoF;=hG1@*=kMz3hgdL@hQDqw2 zq)FbJl|gwe-V-7}ZE;F%AL-@l(LQz4zR`5GOc0u^q?FBx$8W|PviTVy_&D~Df(q|Api){?LTQ}a zTl1B)_{h-b?HsE%4jM71%oB=Vy|uaBxudVEyi3a=L#Awors>$*ftm+WdEKr-Vu@qS zTH-wDJZ$Pp{hZcVlY1;w z*AC8yJjtP3*?FWgh`#Iis;TDrk19MbsYcQ^b%}98fTlE3SAWOQO025lJvyR+WXlTz zkHtM&C_8RtkpH-0iVs8U8_3Z{n^m?lio_%Du6|u`joFC0lw^ z7jH9O8KiD{KusY2U>qv_raDnr-`pK-Gh(L?ViR=N71rn;Sk#+Mb_KZU#J)XpqO=VY z+N;?&m2|hHZ7L+?{2Uk#^fm~1G_U3NJ^Z#eg+!`nwPsu6VVSV2dg%FK^JM~6n_tJ9;V`dY+;>ap1Z!QQ*?ox3d>BGC62;4B9E@N;f#L7n)N?X01q ztUIPxJ4DMjzs7X5^yU6DJnt)O@e6#cg>d~OjChJ$>y@9fcHN=MHMr>`_F#sz}}QZ3pKq+VC~r(Ok6Aj4Ue?5XwHZuCr^LL4q@_ zCxp!+-S3q^B44nFrzxx0CvU+o+zJ9!0+j>FL~1%F4@~GJ5V~qe-4kAm4lQMon&S^D z2$U~gxHCf~;4jwteLub>*55Fs=YqqVg@svAsLGe!3!R4i>U*w z<&%6GhE1XSYa7W4Eg~Y%e#YfTK0nhy>SmC?RCwwi5mZ*6f&c8|YoA*l#MijiK3zCo zO7kV>NxrsfS5S-9Et=g2H3mlZFMFCjeMK+MSGZ=JqAmKyB>!wNdTJmJ0h)V}x>ZDH zIwqPQNY+o6Mbzp%uWEYyM!_D>lMV!G*d#jm@pP^qPp9kt)p5Zbn}?mlxFhh;os z|2Os7F6Wxc$fS_N7wL?OGsxSTh}%l`9euOsT;~S+vCaIVHs^IMq^^TDg>L`q!Nuv8 zTNC9+{az8~dhFHLDie$vEMbf^L_O} zZ%s5;iabSCpD`2{NM7KX$jz~|a}OIPEEdG*asW;64)Pxb#ZsZ$FyK+X?$cwaYD`M| zX|LMtU2k?vc}QRE{#Y@w9N#;Sr{6l%+0nR zA#$BXtWxLfc9x!(2_Y%%-kpFcB?BX)PS{P(;VOxh{e4 z)9}-J@+LAKb0t2Riqsyo`lzL)yMNH= z*gUHpl}c@a45M1-2hEfI?P%T2>yR#16j)T3-=S@dr*|~bGF5bDX1?meYs*trem$!d zAth=S+0YkdE2d9U|0Oi@&DpHoL;Tjb%f^?Y)5$V`>Oe}?Sj&C1lK@EwmviC0TUa371Xg;IPXx%2otU5Wmv`!?JM z6|Js#YdYO0QRvSD(C=Eo&(*O7#aZz3;@o+jn|Iule7#NLh@VS#;4e~KmGi=Df9zAG zzU}q}uI&MhJ;9!`6X|o>C&G6Tzpyl#OnMs7g4;P()9i-Gg8^0)Skzs=MM7J``CQS~ zo&~%DY3;L*-@4yj7q6Qt-S5s@c|`uxHnq?zqKtcOWO**P`e=XjOVEvR$lrI&dn1#7L2HU@E5 z5Pg$mHdJQ6yKshtG+eWj^}qHwq1%S{Hi;uV5M3( zw5f|;pBZCCfknA2t>XB)x{heZa-}+S5Ei)bf7&ZDR6qTpPH~jNPw^v+nYL)qHvqonK=3*${jW3|mlq{dT)|EWXryVfChi-Q9xl zZej~QLs5i*EFsQHc*)yu*ZY*uBr8c@CC|^lQLMqD&Kq#eKrBO5xi6@r^XSrH5=jI;^Y(bvzK*g1zW@xt}PL8R{Ox#rlTYd75JDj)@oQ9zyC;lBO4C z-nx*;b=YCqaDG@rB7jQ9e|e9b{Ck$v>`{Z~ONXhQ2R*M^<*eSRKm5*wS#^=-!!k8- zZkY|UE^dNC5++`_jxzg`Zgs-mz2vM^C)rs(Ypw-N6pa{o@3k<=QbDyGIh|WT&V!q_ z7+#q7*0#`kduF=EXAYN*A0lPs3{~W}|#u)yYlSW2Yn2euVhRkyw!(;>~I=Txixe z<3(RQ&5^nfx#wE>rxZN6I-4%}TGXaBe73UcbQv>2QSsL~Ul}gfaxbN5tiN@_AhQTAAvuqStEn45OR0?QOj z5-rA>|4;4f- zwCbn6QC3j#iLlqecUNHf>VMehp?ul)C#>F*A7xY#x>iWtk0y(Kt5@^d3Yi4eEZQ_w z$V#X_E76GZrI!v48mL@6SGi*(Mq<2Xx7g&mRNWS zQ{_7mtgbZY+9#)I%6Q-fLiZR_cc<=0fA7|KYiSeCh?nCE)Is&+M^FW<8K1urKPF7B z^j9%UVl(Y??7&%ny;E*AUBl8l#&ws7D4uO1$H786IunGhHBxtK+-3BEjQQ)lGM~al z;Yedq)}sBIF(IZpiqB3Igip=Py6gFLX;f7Ch#9bjPKI7q)09^7-MFZ<-6pzTi%3ie zp=*QGeL7|2%-J}Qe~h;JV5HfD>#i%|ovrB&S05**W~iwTQ(005T7Q-qU0Px)t5CY+ z6MvlA?ri$!@X8ydDCeOmBlPtGJAy8Fs6<8+gM(CvtcescV~qVU*0iC_t4GUnDy z{*wI?G`nt=^-b)IvXBq12&O(ag^Pb}Tzk99$Lw4Cv=~d%{bzC!@!BDE?dqtlKgeBE z$Z8qN^KE<}SNFN9T>kK(6q?vUtrhv3ed0y}7o{z_HS}A$ni%z@%IDm)PfzIt970L< zk7u-N9!2QdBXvn8gJ)v{3TL154mak;v7cg{VjII}A)+>%iRnJb5s}HBude-kj^f~b0O<;aBaw3{!%Q-EvxZmSE9>`ljvdex(}`EfYfa! zd>1nJa$l-?R}7bk`Q+Ez15@NdR2ePWI;>Y-wD`}bd-e@JROT0MNc4_hnIm@P>aVyL>?TGx;Ax%fj&_}rHnQr!e)D<`GosjS(a{|5~E}s^NSo+VcqTV zYSDpG#r$Mt%=?eqF5W!<)u1L%Q6O(#@QzYAePs(m*9ocn;lkG#rSu8TT^%o2LcfT+ zmWzGdClfMr&pXQ1;e12Q@O0#zeageb>p`dPa8>Ts{N8n}JZmtPm$Rq@r(U~T;Wk3| z1X6dzATL;3F7U~Gs-R_+oRzS#U=4@Hq1_)V1D|Lpt-PCdaHu7Jth;;giOz^Ose^S@5CWHr{S;l%~BXpgSy8KV9==XmiiV3D1Ih(xOPvCrMZi}zwwF>!clap-u zXYpCz=f9_&O4Hxfaj0zLWut~mcgNHXMxzgQ(cTO-a*J9BT^FQo%RRn*H4UC3B@g+| z6j4uy8#m~XQ&r37d*0YD5vKdn_WHOngYKT9mr^&G!`Sw+3sd&sUt7c(`aUp|7=Nhg(6e_V60aHkX>yi> ziiu3SPxaRv*d0jK$4MuWH(2M*wRven-)FfYb;VOUC7bE$guh%j|LDFuk~+}pLVU)N z&W%gT>eCNsW_n-V|5mn^Zym?#-1?nNs(NB;v$U2`0z>+o84Ns$iu zB6TakVMn?K8&@*=xdsY6Il{p?iPY7w;}q{6?)i2Gr(LGMITw`V>;|u#`n7e$`Yi3jO$UvEZ|ysGSY^x`~e01F37#oPFXVgI+0) zTzly@HwgiDrtH2nGv!B3lY=ivtcX%o$fogD^B*2d(PQ4R&N}UOQ=jMl{R&Us>-Ax5 z#VMEUH~S5}PW43U5@{>@(1y}JlHbd4rDEk3+wi$Cg6B7H;76WHbZ+IL@7hk)H0JT> zr5L}^=-B!PF3r6nf*z5>V#mHHWw*E$E~B5rpzpiAkh+J{-G=dK@M`AeT#p{Y%Z=1~ zBvJ7-$@B|oNq@eTsmqYDL3o|X~_l?l^ zh2BVATvF9o#{N{CHrLT+{45;H_WLW`Yv64-jt1j;+P1S=Pi9zrLJ3XZ8M?%T%W69a zO_fqL91=NpHQ-}L{ zIpQ0R498)gkv(Roc<8B39k5l&3_qIxX%d?+gVpUgkLu~Q)8HXZKh!ccWt~3tz=;lDW{;Zl#^BJ#i}G2C zd(24$3g-tZD8H)b1kGsZb0iZ@G7IcFc9zKb7<%1@p4a`6x&?*x*$IylWJrdM_ejZf zom;JClVs& zhXAB*;4#Bt3zB3Fdx4y3Vy{n&X~|Dpx0z}MhIW2*Wb2Xm+`sc6+kC^>Phy7?zlBwX zM==VBEY+T$Qp(JV!>c;=5`Eu-j`tK&mxcu=e{@`oG5xM_UNRjS2R+~P(6I%RGNFbL zBT`l#OMmS_J>o`_x6$~y(UoSy`&bnHha{GJl_V&xr1f==sv+VHMCwL%J=WT9)OPbs zBm0H2_~j$#ul3$ITNZw)n^nw+{~eb<^{)0afmNTrJ?(gk_dqMz)?zeuL_9O+;-UBn zJDD{d9#prfLe*`a^@ZgiId!~|7HrHe`>bKEftkmm0mswW#x*E4m zGHilI?6%=)Gd3La!l2H}(JPok{I%CLOlS{8wrmvvl#0&%YR#-spig+WtlcEoLS zy|sMSV}1>Yc+dPv_xg*GkB+0~Eb<^}p zSnl%;ewaSNWzqZOi_Vw6G8&ov@)B!W=SmT}AxPbc9f~EQWoD0gy!R|loVw&$?@jL* zaYrpcgDu8d{cr;6#;L~Mw4$bOSo}* zqLy{OZt7!AqaA9#LaQVnRxKWHX`fI{TF#Bnyem5Z=P;53NG0vQB4+oQ;LdA`Wy4f^^AT-9h&~+S5j7Yhp#_nI>ji3 z&<#iGdJat*p&lRd*YTCm6W+bHd^EC~R1i-rRpi72_liYxd-Ouk9J=! znt5_f3*Vyg^&5QV9HN^_HeT#KFGUY9m~{H~x|&^*2p^L^?L zeR;*<_vMH@L?LyDzII1RvYi^-XPx@Ss#04mu-$q+z3yDh?XI|wKI10sq`SzENItuF zxDCIJ+}h-zdZF^+!lWJJi7UfdcTGix(br>i9-@)DcF&RpXK7hOQl_7t8oYaKx?rI5 z{Aa>0b26){eDhVok;bbh<1W{}W#js^ktEpt=t9}sFBaP8ZmLJzekP|L@6C>g_Z(99 z_*i`9vnW#a)6b&9C2HJUU2iZt3k-V-rjI13G7CS5Eey!1xZq1#i26d}W3|2{SWEO?}Ip^Xpy2uZN1Z_}g_jB!IG)XHZioA0Rp&NtLwY72R;WI3G*dBzJ+VwDr z%P#o&d8RIskGJsfms6tnm}8CZky{ddEyhcq-8@GV8gj6AeN) z7O88-*DxCBXa9l3s3Fdmd3+)2c&lxmyktnh)O#8IM-BT_8_X5t23vV7V?Dpt2W-p} z=5Mfun@47;+Pn>;r)1Vd=*A&+xsG26vuGX=S+jM>c>8j(KJ9W*cGbX~a_PQMA(f-P zJ)v{7G{MR?-c#cQv9Y;o1FR#>&uMWQ0y@Jpy}X8>)**D`k-9;6!`*puGb`>hDEA zmqU-U1f;G~>)TKItaj4*Q;QPP+&rh$?pW$D8E9Wy#yTbpO*J(>Al#N^G(sG+uc(>RFItmp__!%Rd%p9Uc1+^ zO5yc6C*5b~l6K~yd%-tL{f4jXJ;G^y^8$tF4Yv---TAJ@5iw7srzGTpt(P{+#d7xM zB<~+0mAr}2O-Aa5s#Teqd>H4pIhH+VCZW`HOx=3^Tqxm8 zH7?;fPrYm-avn;8CQ5~~tFOy^=UopvTO)KYAa#4P3sN=x8v>Qy-oK;gn&rmL_&h_6 zd`Yab>K*I8j#huF)SQ=1UrkWnH~3BJeA zvPb&GK39?%`!IxVDpL2f7`wEKk$Zc*+3V9>XYYB{4NkF#w5m2}S}>GJyGUo3Ji(<< z*0EFeCrxaR|6UlS6tY*J);U(r+2rJFiZxkBgl-yAH|H8I!@&0=sG^g3_v?zYLTUug ze7SeT>lJq`WmElg1HT~YA`z6$jBx`;2fzChLr{p7m3=%gOOaVg8`5)a?Z+rl?n&dbTRyTon8sYach zI@K!bTRo~f$X;rXT>oA~>ShL4a$eAVqO~h3Z_%Ec9N&Fcg4^t!ZRC!@Yvb^qoJBHl|#-Sb!RMwWKc@m!aGRm~-= zxx>kZm~fBfyU4i&vGFf-t{=u+!lpALpY) z=w={wrJnlmO5VGoFuBXEKvKF%NH5CbLF$pA=W3s4!s)jUsk7QS4BHm35{!yPb=Fi0 z&8_fdpH3m-?KjfItC|RRMSt#(o*ynFby;*rObWe>Uo(l9wBB3e*T_D6-6@7EO7S)M z*TcH!Bpix*Ur5oDh8}-G#r? zfV{r8W{#>}=NDy9ywOQr6`_j*iNgGRjm-6$_I+~4be?sfs&&6QjxZ6+e4ak`w5B5C z?9F{&2a)f4W+8PKvP(Qwy{~NaoeMM?mUKC{P1$Jp-f7&2=}R@HM*aI^a^5Yxa-o)r zF?~4U(otKogP4(kCowRS)7VWfMQm@KEF!J?!lsi*+K1f6V&&-tqG@_<6%PKFrK}T}=Nj<+s2rs3 zGJku}knS95*X_VF`YWizUpt-@Iz$DX&G*i17$9btrts%wo>0cqTh!>$D4=L<*ut%a}Mu(7p=pye(s}|Rnt=~Wt~nphXo%g`NBJ|OOz59D76cE z!mbe-ohh+YreF1mcA(x%7~Vi7eX#3l1Nyu`KZm)B)cqn&5!bx;T2bS7SG`w{jI<9J zel{wV;wcgF(Jt(63EZ(JLy{+9bZt2zqf=AE_j@ey&@;Ae<+)aYWR%-qJaTxC$uGR` zzJ}B_9J@RCxVGwEJVSZ9kl?njZ`(rFc5}!`P-`_^8SC=LH<$7mSh8uav()srj5k-v z^U55V6sYCvt+-(GwtwRjJ|@2*K=V3Mmv3lx(fI?xhk@sM*~Z4^ZRSU2M^9JtjxQaL zYVmBkLj3%A3f=pNM|*PH?{KZ(C|$m;c<#aFfKzr>{^8Zy+pMvlXF=V3r0)8Bz|tqF zd~Y^^0;@~c*azQVoe52OXh-AaYt=bS7&BH{dSvhH?E+W9S{zp?{S~@xu2qjzz;$Bx z``5fmJMD@SouJ$O#k}7z4+KM>2d3jsA zxQMv8+d3b2agGji<5(!`*BiC+ut|P1yJM-!S5=;?-jxC00jV? z0tf^M0)XG!hTpM<--iwXI13O85C#wq5CISg5Csqoa1P)+Kny@EKpX)4jv)L#pD%zP zfE~bb00#g^04IPG0L}m|0ImRT0PX-M0XzUa0pRQS-T*!TN&p4`2LN;d^Z+OTt^&aC zuE6ixWC9oh=mT5<$OY(vNdhGVCI^)NHiomoKJfTqK44y8e6Zhz00;r_0B`{a0Prz& zXi=Ct92cs}1>ZKe!#X4Y$L#;C8q_^kKKdpP?3ZEW>u85AF-M!MNbh z*!JKys0ZVMS{wj80B|gG0&oNH0`LLAeWBjJic14*R{;Knh?tfFytffH;5{fGB`EfEs`*fCYd#z+nJ0fI|QW0Zai*0E_{Q z01N@(n1ka<4*-t40{}Vz`vLX=Xai^gXaej7umZ3IfbBQ~z%dWUK0F6(0IUIy0>I-w z24D+d4*-3R08RiW0N{Sd09*lt0N{D*4*<_6cwYGcz;gqhBc1>r04D+50o(v!G9d?! z)2#^jC3Xlqr0+0-l2#^2}4-f}%9^f26G(Z$U zBtQf}I6xS{S%46LGXPKvZjS|kZD8Ak@xbj6aDVL2aKC(j>j2jPt^(u%9n`3VW z84zV!h^BNLN!l&Yg^d=i4$G@jEPTDfM+u{@y*c*k0)@4{F+mg$?+c^*niMt zG?MR}VpDn$c*H~`U_-Fi;gRCK^9V^-mDmM2BI5t59jZUt`S%*bTA*OE2({0Z)9;N!q(!T=(&CrO^(Yag66JZ|{8(?NPlSY9MMmPiqI9<~izC zt^uB*KYP5uDQx*k;p#dEXYHRn_SRnZI8)mME6c4pmjC2AZtrdDxVJ^O_{c<$!k<0v zp0@UL&n83EDaqge@O%cb!QS2;Ax)gigMZ?O2gK%J4<-uIiQTke(K3gBc);J!+8hVf zuZWZINJqZ?@DPI03eO+m$5pHjyFw&?c!1hTYi~y!$pO;J_2A}~A09fuff?1}30>#b z*dO}C!v;Jsqh1$DMn4Jo_5AP%01u3MrTKs;$)%YWKRlovFI#IDdoKSuRoR5`x*z=` z0XQ^(lUfkDmbS2>^@F1UJaAmc=8Z$=-un%Z zrh1SZamDn9#{j6o9BgMjw7vDqSn7`mfEwg@CeS_;A6^~{{=u;V9CE;6#O+h7`<^=Y z!vi>{+}&MqRI`GKvl2fh{_y;LREcv4bHMWfaQ-r?Bru-8_Yaoy_Z(n3e~%680qxlS z`7FVz{m0n;GJ3I`e;?c5^M>X8HEJr{t;F_MwI8|0M1bgRYy`mbml+8gfg}NXj1IZ% z?7F@`0E~FZ0n;_?pTC?7VD{gfM}g)dWZSmvMkSo`0V=!1*6Ix3O2vzn|M!5BA%ARNRW{enbm*(5nD(u&O!f?&WRk z<%M&^`en<^0phd3vj?t(_WbcpP6L--ZOZGaNx-vPL>#06=eU=>uf3bM*WJWWhKSkd zeiUx7#O4wSX8=s_aE?zBHIL)1Qk12`eF3VpV2g`AXatlDg|^8Sa{NzpxPq_>$~4#m z_D}F#AFrl89TIeK9U=iwAnbS6u&bvZ953$x7ayRfoNX;hp%hVz@c1}bdxEC!wc55n zCTP0#!{cr5>1FSUgY!AmeB%Z;t}19p3LGh1SApi?e63`K_pp7AcmRwY(AjV-{F@^M zQl^%jKRngI!wx)#8MCu0SKfU8 z;pqS#a90>Y=-p=L@^S(1hi3u9so06K6q}5o{o$bjAw#u2p91wMsG{Ed@Q4Es7x2U+ ze9OGUO+Ed?V+=fC9UHP@bt!mvX3NG8k1z1R2*y=+5-+N2Jo({C0Ui$Ek>R_!N8;;i z%^#jh;NbzD`?pRPq$PbSY@W6~x4!;o>T7SO&!=sJiM0;N5>x1a4P7~XT8@s?eCS>d4hlkb~os`8sGuph3xwOnz_>GsERBc5D^sx6*pAKh$5rD zgoGq4LWroiqoNT;Md);1l7`;w1rm&y0T%{$MVJ8v(NRIrBkqbQ?jDtKMIBVsQGuuj zW>8QwDD&N_dhb@h?tcBc{WEhsLsflszq(bg>Q>dQ+gW!{?Ad1i<*SEwf!)Cr#T3~91Y&Kk?{V(@(VIgrGO`WGK|sbhn|rgmN|+Hz|D_Rckogn{f2B=Gd`r4?O68yRU+GM~(97+p4R{R@oD1wtC!y==zh zE!~cOkCFR<;J>!zqXQFSBagX*k<}>1jjHY4B|VRLc|iL)jC=rubUSwc0fRbS_tg?c zHY>K^q`P;0c+1l#GV+^}sXJ)ro;~{Z+QG>FXoUK+X(|T(t3866-^WO z8`jLoU?8NyZ6|!+Z}JTbW;1f8Vte76Tb3Sh@TosBGC|3_zN4*g>)&f{W8^Z`PPhH4 zk9(@q+_xCHO0iX)_0|(hZhdSzBe-WVc=-Or4e!hWQZ7$5fw<8ajMNoPyQ*Ph z$vMeNAh4n(*lxd5GOb-FEF5wDyEF$_18afVc^q6 zTLdD;EnREuW#{IF)_8$BzVATwC9T?d4|MwjvB7p(32z$-v1rOanQNVj84 zPuuzP#l5x(8PQJq7>JVtdYZ9614}C4(}l4nwyQ6BebS%L+Ur}e!M2dCvi|CVQk#Yy(20v!U|5mI=M@#Jy178+C4eP;Y@tYE7@A1dUrh0lNV>Bf5&F!FC8G;VKR zIB)HHAIzM{=Y#Aciv9~3%O}*?KD*%LlxT^XMMHNay6@d`8=aBVIdR|sITKf3|KsRh zTU%abnXN!*ms!vy-ulZ^&%hU8orHEO?a^q!veKYs@Ym$+3 z6k^Qi`&e=5+7}s_0Hg={Q!@DG$}^W;P|rwEAy1rFR6DHyb5)F7t`M_+`>)%dY(9yR zIST1M>A>}Su4~82vUK~KLiXIX-;E_xW?an3Qia^o(YR{d?hkh`@}xrUeYo-cYdUWn z#mK7)+1$GE!z1Uf*q@Pg3VHN{p+6tFeajF=z6R10Jso#b^xA1v`;2AeM}<7TrsL&` zrDIkx(go_1MS9$f)x7rk+{KI>sE~Q%zPV-lr*E9WNPi%-o3(#=>E~P4>^+;2AqshI z#Vqrd$7D}P#b*MLzUGcpqh zS&CiDI}Th^Td&E?141X1PkPNqLm6WJGa%;ER1>g5x$!ytYVj(j=`#R08TXrP-xMi2gj$|JLav71e zT(SP3$`^Kw!L2~Kyo;)Ukd1%0>d{fp|6|*wjEn(7HonIX^XK)ge)}#)Od#~-RwU-0}yED&z2E-vHe zfFas*fImL9d~!Szez?c*E7sL6zI`IR31qo0>^{_(vt+~bgeTyVSa*4klh%FOw^tZA z?f>Sm<<3=Jf#SF(?i0wLHwbO5H6@9Zkn*6ZHkYKZR)D4DskbDe340tP;lY<;A_jL3YI&#sG1%I;6%A!c+tn(7G?z%xlwtxil`y-(cb|o_wz@|6V2;yYo zLKB+?ir6%4^9TmXF$^)WJvgPG!Ua%!@3=ohV64@*H#dm}pZ(vPe0R z6mlLu1;xflF-ox%EfG3kyDtc;0crXN!M%7cb z;7Ff_D}^r>s;Y26Smm)upgto8nL*_eF+%aFeuro<5KcBLxG|h`K|{%S&;e`h;@ngh zaKJXV0cOIR8nV_P_Ka1e{sA94kyZUzAnu=9RYA>UY#ssj6VwmR`aLkBiz zK-I7}rwzJP4CUGNA~0KfLJ*yHnQj^TV#)FJk;812YqO~kYN(ru8N~$=rw$S`rhhABToCsEjW>a=x2l4ueEFHpWa!h*K5Fut}bq! z(JXDOQoW|Vcx^hnL~B=;(YeS`4Nlu|V_L;fAR4c#FoTZDsUsmd)%M`1DRxCinXYx% zOi=gqqDa&XJImt9a3r2mOIzgC5{j~iI*be=uZz6V&eT%J7qsX@b;JXz#q!*>CRoi2z&Oii0b9^djC-IR%M zGg;@PLb$}jvYAA#puSn_)~2!w3Sv{X!6sArg1FSmkV99%%8-KA%FuEJX#*a+S4ii{ zp_yT@Xt@G;tX^46BA+K0-({c@X9{Dn6w-IAJejPslU%V{u0S43C5x$$G16o)NLnsm zovcFvu~=zu92CS2Df$N|p236R0>yEodW7(M77rIsoPkg%nef#G&FT^4B86L&PE`Y4 z_77VE)OH#tONuH4y^Ew3bO}K#iE|WrF(F5RjfmopA-WxYHJqw^u@({4AEsx|AbvzI zaxV0Eq!y8mptq7tPlrDdqv=9m*8zi_7GAVmZx5+DLOivA-GnZ!t1w9js}#mmy*r`6 zY);S!xy!3cl0s#KcRH8;sUt4swND^8#8*;<;Krjm9*5mrl4*6pn^q=6OTxwhd~imAZm%5sa@aTt%u9_Of7t}R{MBkB5yLCwc!`w>Rc?G z$aZx;=1U){4cMmv;!$qlbkNz1zPULYm%B@*<`qJ&acZK;3`X%w8W$VD)KY&Yu*TL| z&V%iV*6uy~b;Kugmgh;U509m(UX~}Fu8njLr720yA$y+X*LD8zma2Ap4CP7gm~Ixa zqc~4esTNIn<$}{j!7447GHqPd(#n%i_E2aeU2Q_g)Y3dD*(zAW$R*yXq!SEAnnLSv z-YUZ%0?1(?r5O;`O0+CeLNupsm94pi+2yk8dbA%JmrNqCPPDDyXe<&nL|blCUMj?HS&CzjmtfXzkDmM^Dz!?NfaE9JvIY+Sd$ztV%GP&-cJn^hZ zNJ(Pl@+DD`x{5^QI5Nl6SI!!EmdnU;XEKJ+&^=#_hpGp2NpV}AbjlbgQWr@Dcbk`MKVN#(s|SCN+}>H zlxKsmk5YAMD#?@JeYI0}x#RhddwMWp9ws7VP@;F!kQQ&~T~uu6vfm<^rzyx7QRe-F zX9%SC{>3sxhI^l>bX1bvrj+Ql_d&}L59htlFb}d}B+#ykdxnol{;b7qnWp^y1tcPm z(>?vcFgh?=xIY*$?At_!QgK60Dd)>VrsDMa_AM)=x_u?YbEmG0U0dAAGC=Ceo+JD! zos#7+8q{k|1JM=_j+b#|2J5DnLTeQkN-DHXVU+sYA24qnQ^-iYys31DgJRQ*3MPay gsdNQtCnvE!ZdytHy4h<&sb=SyO8ye<|L_0*2fBccZvX%Q literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 0000000..a49aafd --- /dev/null +++ b/index.html @@ -0,0 +1,2 @@ + +
\ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..977cb3a --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "d3": "^7.9.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@biomejs/biome": "^1.8.3", + "@types/d3": "^7.4.3", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react-swc": "^3.7.0", + "sass": "^1.77.6", + "vite": "^5.3.2" + } +} diff --git a/src/App.module.scss b/src/App.module.scss new file mode 100644 index 0000000..dd52477 --- /dev/null +++ b/src/App.module.scss @@ -0,0 +1,21 @@ +.container { + width: 100%; + height: 100%; + padding: 0; + margin: 0; +} + +circle { + fill: cadetblue; +} + +line { + stroke: #ccc; +} + +text { + text-anchor: middle; + font-family: "Helvetica Neue", Helvetica, sans-serif; + fill: #666; + font-size: 16px; +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..a8bc93f --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,148 @@ +import * as d3 from "d3"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import styles from "./App.module.scss"; +import { createPortal } from "react-dom"; + +interface NodeInfo extends d3.SimulationNodeDatum { + name: string; +} + +const nodes: NodeInfo[] = [ + { name: "A" }, + { name: "B" }, + { name: "C" }, + { name: "D" }, + { name: "E" }, + { name: "F" }, + { name: "G" }, + { name: "H" }, +]; + +const links = [ + { source: 0, target: 1 }, + { source: 0, target: 2 }, + { source: 0, target: 3 }, + { source: 1, target: 6 }, + { source: 3, target: 4 }, + { source: 3, target: 7 }, + { source: 4, target: 5 }, + { source: 4, target: 7 }, +]; + +export default function App() { + const svgRef = useRef(); + const simulationRef = useRef(); + + // Resize listener + const [rect, setRect] = useState(null); + useEffect(() => { + new ResizeObserver((entries) => { + for (const entry of entries) { + setRect(entry.contentRect); + } + }).observe(document.body); + }, []); + + const [simulationNodes, setSimulationNodes] = useState(() => nodes); + const ticked = useCallback((simulation: d3.Simulation) => { + const nodes = simulation.nodes(); + // setSimulationNodes(nodes); + // d3.select("svg .links") + // .selectAll("line") + // .data(links) + // .join("line") + // .attr("x1", (d) => d.source.x) + // .attr("x2", (d) => d.target.x) + // .attr("y1", (d) => d.source.y) + // .attr("y2", (d) => d.target.y); + + d3.select("svg .nodes") + .selectAll("rect") + .data(nodes) + .join("rect") + // .append("foreignObject") + .text((d) => d.name) + .attr("x", (d) => d.x) + .attr("y", (d) => d.y) + .attr("dy", (d) => 5); + }, []); + + useEffect(() => { + if (!rect) return; + + const simulation = d3 + .forceSimulation(nodes) + .force("charge", d3.forceManyBody()) + .force("link", d3.forceLink(links)) + .force("center", d3.forceCenter(rect.width / 2, rect.height / 2)); + + simulation.on("tick", () => ticked(simulation)); + + simulationRef.current = simulation; + }, [ticked, rect]); + + return ( +
+ {rect && ( + + Hello + + + {simulationNodes.map((node, idx) => { + console.log("node", node); + return ( + + {node.name} + + ); + })} + + + )} +
+ ); +} + +function ForeignObjectWrapper({ id }) { + const el = useMemo(() => { + return document.getElementById(id); + }, [id]); + return createPortal(, el); +} + +function Counter() { + const [counter, setCounter] = useState(0); + + //increase counter + const increase = () => { + setCounter((count) => count + 1); + }; + + //decrease counter + const decrease = () => { + setCounter((count) => count - 1); + }; + + //reset counter + const reset = () => { + setCounter(0); + }; + + return ( +
+ + + +
+ ); +} diff --git a/src/global.scss b/src/global.scss new file mode 100644 index 0000000..b0c76d2 --- /dev/null +++ b/src/global.scss @@ -0,0 +1,8 @@ +html, +body, +#root { + width: 100%; + height: 100%; + padding: 0; + margin: 0; +} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..413105c --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,5 @@ +import App from "./App"; +import { createRoot } from "react-dom/client"; +import "./global.scss"; + +createRoot(document.getElementById("root")).render(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6cacb64 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "jsx": "react-jsx" + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..cc93912 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react-swc"; + +defineConfig({ + plugins: [react()], +});