From 8c3afdfc7788c16c5c6114e3d7cf57506221684d Mon Sep 17 00:00:00 2001 From: Junsik Shim Date: Tue, 20 Apr 2021 21:22:55 +0900 Subject: [PATCH] - Add images for the examples. - Convert more examples to storybook. --- packages/core/src/serialization/mxCodec.js | 4 +- packages/core/src/util/mxGestureUtils.js | 1 + packages/html/.storybook/preview.js | 13 +- packages/html/public/images/add.png | Bin 0 -> 1564 bytes packages/html/public/images/camera.png | Bin 0 -> 887 bytes packages/html/public/images/check.png | Bin 0 -> 253 bytes packages/html/public/images/close.png | Bin 0 -> 1910 bytes packages/html/public/images/connector.gif | Bin 0 -> 954 bytes packages/html/public/images/copy.png | Bin 0 -> 728 bytes packages/html/public/images/cut.png | Bin 0 -> 781 bytes packages/html/public/images/delete2.png | Bin 0 -> 914 bytes packages/html/public/images/dot.gif | Bin 0 -> 517 bytes packages/html/public/images/export1.png | Bin 0 -> 857 bytes packages/html/public/images/fit_to_size.png | Bin 0 -> 529 bytes .../public/images/gradient_background.jpg | Bin 0 -> 6164 bytes packages/html/public/images/green-dot.gif | Bin 0 -> 326 bytes packages/html/public/images/group.png | Bin 0 -> 899 bytes .../html/public/images/handle-connect.png | Bin 0 -> 1300 bytes packages/html/public/images/handle-main.png | Bin 0 -> 379 bytes .../html/public/images/icons48/column.png | Bin 0 -> 1787 bytes packages/html/public/images/icons48/earth.png | Bin 0 -> 4520 bytes packages/html/public/images/icons48/gear.png | Bin 0 -> 4418 bytes packages/html/public/images/icons48/keys.png | Bin 0 -> 4295 bytes .../html/public/images/icons48/mail_new.png | Bin 0 -> 3944 bytes .../html/public/images/icons48/server.png | Bin 0 -> 3556 bytes packages/html/public/images/icons48/table.png | Bin 0 -> 1574 bytes packages/html/public/images/key.png | Bin 0 -> 300 bytes packages/html/public/images/loading.gif | Bin 0 -> 7517 bytes .../html/public/images/navigate_minus.png | Bin 0 -> 485 bytes packages/html/public/images/navigate_plus.png | Bin 0 -> 709 bytes packages/html/public/images/paste.png | Bin 0 -> 783 bytes packages/html/public/images/plus.png | Bin 0 -> 236 bytes packages/html/public/images/press32.png | Bin 0 -> 2261 bytes packages/html/public/images/print32.png | Bin 0 -> 2111 bytes packages/html/public/images/printer.png | Bin 0 -> 896 bytes packages/html/public/images/redo.png | Bin 0 -> 895 bytes packages/html/public/images/sidebar_bg.gif | Bin 0 -> 80 bytes packages/html/public/images/spacer.gif | Bin 0 -> 43 bytes packages/html/public/images/toolbar_bg.gif | Bin 0 -> 155 bytes packages/html/public/images/undo.png | Bin 0 -> 879 bytes packages/html/public/images/view_1_1.png | Bin 0 -> 849 bytes packages/html/public/images/view_1_132.png | Bin 0 -> 2199 bytes packages/html/public/images/view_next.png | Bin 0 -> 918 bytes packages/html/public/images/view_previous.png | Bin 0 -> 912 bytes packages/html/public/images/wires-grid.gif | Bin 0 -> 50 bytes packages/html/public/images/zoom_in.png | Bin 0 -> 858 bytes packages/html/public/images/zoom_in32.png | Bin 0 -> 2184 bytes packages/html/public/images/zoom_out.png | Bin 0 -> 847 bytes packages/html/public/images/zoom_out32.png | Bin 0 -> 2150 bytes packages/html/stories/Anchors.stories.js | 9 +- packages/html/stories/Clipboard.stories.js | 359 ++++++++++++++++++ packages/html/stories/DragSource.stories.js | 189 +++++++++ packages/html/stories/Drop.stories.js | 193 ++++++++++ .../html/stories/EdgeTolerance.stories.js | 11 +- packages/html/stories/Editing.stories.js | 164 ++++++++ packages/html/stories/ExtendCanvas.stories.js | 14 +- packages/html/stories/FixedPoints.stories.js | 266 +++++++++++++ packages/html/stories/Grid.stories.js | 12 +- packages/html/stories/HelloPort.stories.js | 115 ++++++ packages/html/stories/HelloWorld.stories.js | 12 +- packages/html/stories/Orthogonal.stories.js | 177 +++++++++ packages/html/stories/PortRefs.stories.js | 279 ++++++++++++++ src/pages/backgrounds/ExtendCanvas.js | 270 ------------- src/pages/backgrounds/Grid.js | 218 ----------- src/pages/backgrounds/index.js | 14 - src/pages/basic/HelloWorld.js | 75 ---- src/pages/basic/Template.js | 57 --- src/pages/basic/index.js | 15 - src/pages/connections/Anchors.js | 144 ------- src/pages/connections/Orthogonal.js | 189 --------- src/pages/connections/index.js | 22 -- src/pages/dnd_copypaste/DragSource.js | 225 ----------- src/pages/dnd_copypaste/Drop.js | 184 --------- src/pages/dnd_copypaste/index.js | 16 - src/pages/editing/Editing.js | 156 -------- src/pages/editing/index.js | 12 - 76 files changed, 1791 insertions(+), 1624 deletions(-) create mode 100644 packages/html/public/images/add.png create mode 100644 packages/html/public/images/camera.png create mode 100644 packages/html/public/images/check.png create mode 100644 packages/html/public/images/close.png create mode 100644 packages/html/public/images/connector.gif create mode 100644 packages/html/public/images/copy.png create mode 100644 packages/html/public/images/cut.png create mode 100644 packages/html/public/images/delete2.png create mode 100644 packages/html/public/images/dot.gif create mode 100644 packages/html/public/images/export1.png create mode 100644 packages/html/public/images/fit_to_size.png create mode 100644 packages/html/public/images/gradient_background.jpg create mode 100644 packages/html/public/images/green-dot.gif create mode 100644 packages/html/public/images/group.png create mode 100644 packages/html/public/images/handle-connect.png create mode 100644 packages/html/public/images/handle-main.png create mode 100644 packages/html/public/images/icons48/column.png create mode 100644 packages/html/public/images/icons48/earth.png create mode 100644 packages/html/public/images/icons48/gear.png create mode 100644 packages/html/public/images/icons48/keys.png create mode 100644 packages/html/public/images/icons48/mail_new.png create mode 100644 packages/html/public/images/icons48/server.png create mode 100644 packages/html/public/images/icons48/table.png create mode 100644 packages/html/public/images/key.png create mode 100644 packages/html/public/images/loading.gif create mode 100644 packages/html/public/images/navigate_minus.png create mode 100644 packages/html/public/images/navigate_plus.png create mode 100644 packages/html/public/images/paste.png create mode 100644 packages/html/public/images/plus.png create mode 100644 packages/html/public/images/press32.png create mode 100644 packages/html/public/images/print32.png create mode 100644 packages/html/public/images/printer.png create mode 100644 packages/html/public/images/redo.png create mode 100644 packages/html/public/images/sidebar_bg.gif create mode 100644 packages/html/public/images/spacer.gif create mode 100644 packages/html/public/images/toolbar_bg.gif create mode 100644 packages/html/public/images/undo.png create mode 100644 packages/html/public/images/view_1_1.png create mode 100644 packages/html/public/images/view_1_132.png create mode 100644 packages/html/public/images/view_next.png create mode 100644 packages/html/public/images/view_previous.png create mode 100644 packages/html/public/images/wires-grid.gif create mode 100644 packages/html/public/images/zoom_in.png create mode 100644 packages/html/public/images/zoom_in32.png create mode 100644 packages/html/public/images/zoom_out.png create mode 100644 packages/html/public/images/zoom_out32.png create mode 100644 packages/html/stories/Clipboard.stories.js create mode 100644 packages/html/stories/DragSource.stories.js create mode 100644 packages/html/stories/Drop.stories.js create mode 100644 packages/html/stories/Editing.stories.js create mode 100644 packages/html/stories/FixedPoints.stories.js create mode 100644 packages/html/stories/HelloPort.stories.js create mode 100644 packages/html/stories/Orthogonal.stories.js create mode 100644 packages/html/stories/PortRefs.stories.js delete mode 100644 src/pages/backgrounds/ExtendCanvas.js delete mode 100644 src/pages/backgrounds/Grid.js delete mode 100644 src/pages/backgrounds/index.js delete mode 100644 src/pages/basic/HelloWorld.js delete mode 100644 src/pages/basic/Template.js delete mode 100644 src/pages/basic/index.js delete mode 100644 src/pages/connections/Anchors.js delete mode 100644 src/pages/connections/Orthogonal.js delete mode 100644 src/pages/connections/index.js delete mode 100644 src/pages/dnd_copypaste/DragSource.js delete mode 100644 src/pages/dnd_copypaste/Drop.js delete mode 100644 src/pages/dnd_copypaste/index.js delete mode 100644 src/pages/editing/Editing.js delete mode 100644 src/pages/editing/index.js diff --git a/packages/core/src/serialization/mxCodec.js b/packages/core/src/serialization/mxCodec.js index ffa961225..317b280a2 100644 --- a/packages/core/src/serialization/mxCodec.js +++ b/packages/core/src/serialization/mxCodec.js @@ -5,7 +5,6 @@ * Type definitions from the typed-mxgraph project */ -import mxUtils from '../util/mxUtils'; import mxCellPath from '../view/cell/mxCellPath'; import mxCodecRegistry from './mxCodecRegistry'; import mxConstants from '../util/mxConstants'; @@ -13,6 +12,7 @@ import mxCell from '../view/cell/mxCell'; import mxLog from '../util/gui/mxLog'; import { getFunctionName } from '../util/mxStringUtils'; import { importNode, isNode } from '../util/mxDomUtils'; +import { createXmlDocument } from '../util/mxXmlUtils'; /** * XML codec for JavaScript object graphs. See {@link mxObjectCodec} for a @@ -120,7 +120,7 @@ import { importNode, isNode } from '../util/mxDomUtils'; */ class mxCodec { constructor(document) { - this.document = document || mxUtils.createXmlDocument(); + this.document = document || createXmlDocument(); this.objects = []; } diff --git a/packages/core/src/util/mxGestureUtils.js b/packages/core/src/util/mxGestureUtils.js index 15e48d334..889d84acb 100644 --- a/packages/core/src/util/mxGestureUtils.js +++ b/packages/core/src/util/mxGestureUtils.js @@ -1,5 +1,6 @@ import mxDragSource from './drag_pan/mxDragSource'; import mxPoint from './datatypes/mxPoint'; +import mxConstants from './mxConstants'; /** * Function: makeDraggable diff --git a/packages/html/.storybook/preview.js b/packages/html/.storybook/preview.js index 645f52de9..51c36b80f 100644 --- a/packages/html/.storybook/preview.js +++ b/packages/html/.storybook/preview.js @@ -7,4 +7,15 @@ export const parameters = { date: /Date$/, }, }, -} \ No newline at end of file +} + +export const defaultArgTypes = { + width: { + type: 'number', + defaultValue: 800 + }, + height: { + type: 'number', + defaultValue: 600 + } +}; diff --git a/packages/html/public/images/add.png b/packages/html/public/images/add.png new file mode 100644 index 0000000000000000000000000000000000000000..bf5f8edb1973c0db89872e1aff48a991ebc0d9d6 GIT binary patch literal 1564 zcmV+%2IKjOP)WdKHUATc!{PH%P~GB7YQATcmHFgQ9gIUp-AF)%RoqGHAX000McNliru z)CUI$7#)RzwFm$J010qNS#tmY3lRVS3lRZ-WM7d0000DMK}|sb0I`n?{9y$E00neO zL_t(|+I?1CY!p=#{^oCg+FA+}Tp*(Sw^XfGc_~s8B}zyn;SDfgO!P(L6XDS}!wZS{ zVB(8U24iBP@j-=%)PzJ62!c=qyIJfO+HSjace_6`vokw$uiu@{V%5rQPj}|d%y-WD z&UepQ4gY^=_z)wgVn0j_CDLX_gwTRUP=bq_XkZlQaRGOD281-ZJjim#XLwByaPrml z&kyX%^lQCcE8&MB+`xm=Y9e2m#f|AZ_+uiU4{P!?PT>sSs?manJNN`C_5YP8cD?cX zlY2EC3D~}aeB)0P>=~52G8(-01Hm^Y`j_VlbqPH{~;~(G-zB@5| z=-|H1!^XGs*++LqQC_N?MLnqC;?*e>#x2~PbTD@>!2GnphV4l>%@!Sr$XTcf=so6I zFBR;-7_P7)m9`VJ&H3I{L&t_P{jj|zE|<^2&~`dY~sE83%Fml zA*F<%6Uh=ANdaq9_wLq9wWBzPkBORBPV}e_O`JUZ)LtzJ4E$QTKo~7r4%N$yt{PNP z_RFk63ALaO!%9TpZ4*jHfrQpgLi(*ENaHy!tx&P+IE|5!%;10s6UbGmzE%w_mPo0l zNn-*qbrZVI_zeT5WulV^A%WW_lrbiuv%X!c)?f#A6FZ|AGEv6YK~37Yv9V0w*N;x39x^wm>I7-&+YhKG%oeu$c~~ z7#fB$UA>s03>#F6US5IxJCpEyF9H>$$PR31QUi61q}GMmW2~PQi-V`9?4x);`vvR4 zYrz|<^({Pkj8`2dAIvD}%{o)mw}c~HIG%%|TZ|l7mKD*X&Xt8kthTDqLmiTtrbRc^ zR%h0%bdUsEtV7ydLy~DoDHUnW&_~H#fJe?{R9*tcylV`*?Z7nSwMYgU1t4K6Jy30` zR&YC5ZKg@ps_4}=Fkz!q_ifVS!VB1Jgo%WfL`v&Kf`Dd9kBb;a&XPuwc!DxsR1%lz zgloz{qX*H68_u`8gvpIPHc=^z_+bDi2(V>$8gDs+&=_%-*MW0CT|@SOgNA6pVmzMi z;_b;n_%hA9Fe}M5S8-3Pu4T-;D29`~a@4BIVD$FXop;x*??ICeS{0^r#g3>_Hl1S? z6Fhert;V*%X#Fsav%h5FtDUU+l6tVTc99-g26DKkR%lHfKVmeOpU#JBztMp*H~v;8 zRxC_So`kqDD)Ax6}L9!!CWL4qreNc}bc2efcst6c z&3v8eek4Ih=ODw@{T>+!gUFa~zKMaaCAy zyGZC8MbhLV_l1Z7l>?6(sj`~D}WG10VWb0pA* zJvfR!^9UYUy#^N3mrTj0l!US9Op+gVSa@?aa(t8MfT<%cC@20JzZK7?Y z^~T#@z2n9@zZa`zhPI$MfB4Y$wc&KNkgWW*|>nsKyF47}{mmcIbUf6#rG4DLw) O0000IP)WdKHUATc>0L}hv)GB7YRATTjHG&4FhIUp-AF)%O>gyEO~000McNliru z)CUI$919gX#a#dZ010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00PfR zL_t(|+J#e1PZLoTJ)O?bNlOcDfhr#cS}2f6LriL1G@4-8bOZhY_yd@jnBYnluG|{` zf-8tiNu;=PK`fz$2x3HNLg@##oziwXo$1VUyi>*n#^_7VyiDFb=iPhmyEa^3HvE4X z*9c2Xue=_Q|H<&MXR=rnoeqb_y4@VpYBiy0s;;U{g3_wj>&ofr$(bM&-=|XBpNOjv z+N|I2d9%72k2jkVHaC;VPY8Oh|{GoP<#eS7=uQ#$xb z0qTSXpFc5&E)G$6iiB#&FgUDgR4%5@)F@nLHFbokc zmrA=pffB0KA6QtJ$JEpqWceH;Bcq5$A7X55#`KY)4^ObOvMi~+yLVQuXti5ZxgxrN!;JMjDa$P*iMT_;Ciih$9ymg#0EO~cS(voY{`c_b2B2!-ab zxcC^^?1fo7kwGOgCj)&9{KT!BI?4-=+*0IGqkMyjml2%QS<{ z=L`9(e%r0|y>vs1jH{7p_vl6r!QccO4kyKH*xgMU`}?22(@&kCvS(+g0Jhe7-&sk&w@4>4AM{9UUDU?d>H`N~JL?zfSJkB%g1J_58xF)!T1mI^H_ARiRov=b`RvKCOcr#Osung*LqXxMhR=?6W0r rJ}jGa_`qtL>5qzikNz^Q>-8KIssI20AY({UO#lFGm;eBCjsO7iaR2~=Yybd* zy8r;x$N&H_>;M26yq<`~KL7v*>`6pHRCwBAWWWy?p_swJ!GY=X=g*8ke*9qk`ST}Z zdU`qw7Z(@fv}x0Rf#f+jIR5kU^8P2LK7aoF=BZPs zUOIK^)MsF*`~o`QH`D=t(JcoEAauk84FygG1qFf7&``O#bLZLs4PE;m4ZL{q;_rh8 z5B{QyfBg7y1u$e3few}u6cpqKnFTeI3E3im07CaENCB6OjEqoBOpMCLjT`;`{{8y^ zWZ_n$O7C)YSBAZ*T9f@87@wgQ~r-ckkXvV`F1EUVpg2H5Y-eYuxnRM9M39EBU%&nZ#x+Xt8W|aVwYIj#h+{D^u`kKV z$v=Qu3ogdS#`gK?)2F{dW*t0uupVfZDo|VqWEaTS009KEm>Z}`3Ydy*?%%(^5TtnW z(;G*fzk0D zSSo?}K!do)4&2r=HI`6@Kg;DK;Y5@D8S6f$jA!R2#R=y z-@kw3EZXkgz5D;mmoF#|yK?2qKWvtQ08AWYDKNn@LoH?k2q3V5?BoZ76wef+J_SucZ|q}*4Eat0ppwvn;u{u2G>@=;`K8pCnvlB0SF)lXugsH z7X0Rlii*j=;_ekFPSCRwF!6l>MgdBk`uh5!#Cc9m&JXm$1Xy;1vlOsk$^_bB1{9YD zI&JkwlS1w(;^Z^)!@LCEWfM7lr1G>gQO-(Jludi=U<2%PtU4%`gn2LZ7@5Q~G#ZGZrRXD5)S*xA{=fSSvIdGkEBd`SvUNJ#h& zEi#`0(?mH?%mZWxEIR=N5WD~ZWo}@L!d6H~C^;o1WhN}%Kn9R%DX3Tl1uIBDuwk$s zD3%Dsw#WqtKmai#w>W@Fo)wt5IG#Lt!pzOh{RtR^ngIa;EI|DX!1DGl0}&uLHkM`0 znlZR5`~$4;!Ko9NO&EZQ5}bNLd|;LO3|NQX zDk&*B3FO`b6<$F66o}sdF(~nY(#>Ch0AfUH2(Un1!w0oo5@@*$Fu}2#z zG+4x;SOQq;hynAf5U?N-FflO^1=e1m<_rU{W%&_U5xfEBgts6ML*w!V5Wj@tchKm7 z#Vt550|XEwmS!y*Qm}(kyC5{X2|-gN4>0wE+fTqMj{(U315A|Pffy8wAD|Y29Pu8S zm|(&C3%QvK5I~Grnzu|)pR++TIyW=~c%hErLTWoPKn(*K{0(XeEWv#R;_pxkeqBrOP0*Db$8yndX$XyynEc#&m wB;+m&vSk?cJ3s(2G7t}lRr{Y9MF0T?00rpcocj25u>b%707*qoM6N<$g7B~_>jRZ~q>szcq>Q=J;n zFpbop7PL$&wWtkk(@t%Ujg_WMC8w*y9OPwkW)mkLKawp5-Z6PmZH>3vy|m1xAH7!1uCo}D_DgptI8@?quQ#onuB)H zidp0oJFJlnwxGpYvW2Z^wbpE9JKC*1+u4B*>&Omvq073mi{0q9?(F7hP}7>(AKFP6 zk)u_kOcbN!XutqZHEIr80}|cnIg%ZLGK`TUg|R%#SUDUIqikd6P#={ijm$BvyqlR| zytC79dJ|i7KVdSrxORDWGQ0lz_~OpiZ{KZveB#Qb7jO3`m_PHr|GT<7@26L9&0Kr- z<mPT1*RLio?%%X~ PuMXwY3s2V1FJtq6qc*w6 literal 0 HcmV?d00001 diff --git a/packages/html/public/images/copy.png b/packages/html/public/images/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..a987d4397177414f386a8a819d7ccb7c4091a5d9 GIT binary patch literal 728 zcmV;}0w?{6P)WdKcYATc!{L3L*!GB7YTATcpIGch_bG$1Q5F)%RM{uz1z000McNliru z)CUI$94NP4v@ZYv010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00J;c zL_t(|+I><*)FHsQHeVNL`^kK}IO{y4L5-@8i`2R>%mo%v?wn+>qWM$S)K z_F<@@2y9Yngms0b@8yAs$HVvO>KDO13ZECm1a0|N96C{0%Zt$qPf^J4xL)c!v*G#d z?Qy#L#w6m(*dt$2K@vX6A9#P~73wm?(SzI2p3g$7h~&u0E_Ro#HNo_oySJFcb#QT@ z@2`r3m+s-k?1DQNa#`mmZp=7eY(G3g1oL!!nKD`@N!D5zLa1F+INjgtNS-|@qwiP; zy7uqHsk3`&_!>WV64s06szDGi3j(jmweczE-&L2Y=#Qp%j{VQPR5V(f`0000< KMNUMnLSTZ1qdVFF literal 0 HcmV?d00001 diff --git a/packages/html/public/images/cut.png b/packages/html/public/images/cut.png new file mode 100644 index 0000000000000000000000000000000000000000..52bf94431fc3ad71a2ea021857e292b9755f8a06 GIT binary patch literal 781 zcmV+o1M>WdP)WdKHUATc%|O<`#uGB7YRATcpIF*Z6eH6SZ6F)%Qnf5sXB000McNliru z)CUI$94{$3A&dY3010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00L!6 zL_t(|+GAkA4Hy}S0L<6~7cXA)Kut|e=IGI*=YZT_K>Uvw!{A`~vZep;+`02FEG%>a zkShWo1SV4$dGT*A$5PNHKu%)2C1GK@4_wc1|Fr4#d2K3}8gU z3_pMV{PpqUM+P=lR!*p9R>_<*Vj|JoH5jBLxR5PonK6CZ4-^UFVf z{=Srwl2&{6>=^?a8!M-0aEz>8($qsY@4k>YbMY;skahH{&v%q^f1hjH3Dp0Y<@4vy z7ed3r&4q-7gn`=>jf z92~Sh@7%$t|NBUZ|;X{`WN?Mo)1*JTY`SzPt>eu@> zH-W<6fcQ8NKZ7RkPaqm=UikN$<@)>AKlSh3|Eh9+&+*59F97}f_uFHj)ETG&AjAH` z5)d}P0S#CyAeMqU`4|vifoeu1TO0;3LJbiFVoqony@zW4kE{>?eQNj$0mvl#00000 LNkvXXu0mjf%B^d! literal 0 HcmV?d00001 diff --git a/packages/html/public/images/delete2.png b/packages/html/public/images/delete2.png new file mode 100644 index 0000000000000000000000000000000000000000..be78c614b59c6f469e163c059229b3fbcf297189 GIT binary patch literal 914 zcmV;D18w|?P)WdKBJATux^Q)O@fYOAIhl9J+|sKf}hUd$*e5Wx%|N}}F+D(JNydgw){ z-g=B81PfD(kp5ViMOGSUWun%mX1eL!AGdq&?)`P{-INVJ*v|QV-_H5YIY8Ry1JE!4 z+`kLd(N70~t><0?=ld95+y+{50Fgr0bS{1YxBSfqb@h5HA7E^lLi-4rM(!hUbfPp* z;b%&@uS(?IRyIcAVnT*xL!lt|r1#h!X4?c*G?pLe09q-!?HN$Zykb|`p#lLh8A_&r zPa6M2xduYchQPEx(4)@&1!NgpYj>jE?L5~49CzhsKdCuY=7JPKSXS}=b8x;BSR#sU zV55(EAmrAy4tH@rLoAMhBGKvfg_`Qxs+B)GOO2hii!?ozs!$Y@Y<`cakT@drZ*v+jilhe` zQnO_5r~_-E`_bX{{bkw4;MQU=TTzk%nRbRskKirz$HV}~kRO<*SE?!S9B3}8ExK3P zSZue1q-5!d1T;m4g?iT$8fj_Bpjykf3!c1X^@vO53slC8;emzJqK3m*4iY(TjxiL8 zqIX~}G5tGCtEhqNBtoJ##?iFb?%4%gAaXUqY00&+n>&iKsLz;5UX-Hff3+CA6jmR0 zj!L~Vev0clI8~x(8b+oS^Z`Foo9MJHX?#!%N2+88!z4X^IVOF-rt^^%z;_22&-)gR zwC;Aafi`q%PC<7->!lH_5qXgyGupP26N05nx3Rp(F>SaX2$Q`(lY_w&LA(rHq2&?j z(4>Eqr`Q+58^XMCZ5f1;5R6M9R1)l|_-tyFDodTLk=UGi96@s1 o)U+it_QjQYOQwL_1~(G_0qrJ3Y23kQ+W-In07*qoM6N<$f=RE9(*OVf literal 0 HcmV?d00001 diff --git a/packages/html/public/images/dot.gif b/packages/html/public/images/dot.gif new file mode 100644 index 0000000000000000000000000000000000000000..08b9947649822e6166a33d18028b389973493a75 GIT binary patch literal 517 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 L$MO{}jttfSRj*FG literal 0 HcmV?d00001 diff --git a/packages/html/public/images/group.png b/packages/html/public/images/group.png new file mode 100644 index 0000000000000000000000000000000000000000..585ad790df421dcd257bd1dda836f619956fd3ab GIT binary patch literal 899 zcmV-}1AP36P)WdKHUATc!{PH%P~GB7YQATcmHFgQ9gIUp-AF)%RoqGHAX000McNliru z)CUI$8z*R^KY0KE010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00P@d zL_t(|+D%hUPZLoTJ@ZAUv{cGhLs+2EB8rBv&_oPzLE=gg5~D8MxgeTIOo)GgjZ2Lt zE+`mxBr4H02?mX*Ary@oq!3yHp#@5@)0wuk^W}YQ2Y8c{d2{p5opbL!kHgXR6wOdE z)L7rpSLg8tO~c}L(@HEISEi6a2os3VsRF@vDBJ=06>fFN!O;B+{XTaUhZSWR2`vew zn8kKR!>{FyTsB%5!Ap$O=`_J$G{GTXZ|<2G?!F^0Yhi?@XTZl>P)K^w)SAJXl7!nY zNc)o2tJj%2e&a8(@&rr3Gc=#|otk>weOLaN_<(d_7v7x?Ob)z7|EZfuw*A1|(h~M{ z3%u9n@|JMX-ZYl5hYgm5hk~JdUDtf`yR%Sr4Te>&NHa9#Os%vxEen=uqF6M+`>SQN z;|7IK5@fOVn6J+(yYXe3r|)2i(9?DqwqQdPd4$7ZwC!Cf#T&cUF#dfNXz^SEvKTP81JsnHHDG z?vxT3rj4R*m4+jU5+tb#qU0*q zf`oC`V3}CS10?|DA}MG3qPC??Z7E9d&rTfE(I{-&MrWoMRp+Y?T=H;i3`Lui{i~(E zpP|atFw??B^VmdaF|wA^Yy)bc0JT^=lx&u_MVbQglx>6hW=0LnBTnIULQcdaB5Hkp zq#y`SgaF4ef3a?lI?~xndx+dgwn^Gp*nW=;xd2#X1#uNS*W)%GA;=G-!6ieaKk8)C zJoQPThO6{8Vg`N^c@C`pPlOr5wsRLmLI5Xll}yq{bMA&qZiGe2b9xHvnJ^Yewo>}@ z9N`;*m0=uSrg9R0#bL}Ot%Mq9c$J!&>=i=FxlBhBINvhC$4o+yDsi1PjF2JhvrFU< Z{sTHZE)<@5Hy{82002ovPDHLkV1nnJh=~9I literal 0 HcmV?d00001 diff --git a/packages/html/public/images/handle-connect.png b/packages/html/public/images/handle-connect.png new file mode 100644 index 0000000000000000000000000000000000000000..513ab2b41b53049dbaa4216d48a6d3e1faf79e1c GIT binary patch literal 1300 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 literal 0 HcmV?d00001 diff --git a/packages/html/public/images/icons48/mail_new.png b/packages/html/public/images/icons48/mail_new.png new file mode 100644 index 0000000000000000000000000000000000000000..16c66628067daaa015199a3b8966cb1134225eb0 GIT binary patch literal 3944 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 literal 0 HcmV?d00001 diff --git a/packages/html/public/images/loading.gif b/packages/html/public/images/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..118f4b0dff870db00602981cf0be59223eecba37 GIT binary patch literal 7517 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 literal 0 HcmV?d00001 diff --git a/packages/html/public/images/navigate_minus.png b/packages/html/public/images/navigate_minus.png new file mode 100644 index 0000000000000000000000000000000000000000..71edaf9993e8d2504eed39d17aba1c4be7df5c60 GIT binary patch literal 485 zcmVWdKBJAT}UMb!;FqFfcSAF*Q0cFgi0fAS*C2Ffi8GJazy800(qQO+^RP z2L}iq23ft^VgLXD32;bRa{vGe@Bjb`@Bu=sG?)MY00d`2O+f$vv5yPs7*S3pCt7uTDZ;*bPa zHj2TLViE+q=eWl7LfpTcC_bWmEp&jneZ~GmIfu05ANcNZ^ zH0@(e&10sM%&ZAz&R?FnH>egBv`rildl=97PN)-)#HA9@T5PEyYy1awwbN((^+)>Q bzP}Xjq;{CKZX$qo00000NkvXXu0mjfvk|`C literal 0 HcmV?d00001 diff --git a/packages/html/public/images/navigate_plus.png b/packages/html/public/images/navigate_plus.png new file mode 100644 index 0000000000000000000000000000000000000000..b5b7e8706fab2e693b717d42d92fb686924b7221 GIT binary patch literal 709 zcmV;$0y_PPP)WdKBJAT}UMb!;FqFfcSAF*Q0dGdeXeAS*C2Ffdu(WC#EN00(qQO+^RP z2L}iq2QIqn^Z)<=32;bRa{vGe@Bjb`@Bu=sG?)MY00d`2O+f$vv5yPxy!6??L@Z4O7HgeV`kpGnPr$Bx1Kpmi21WaghBL7wVgp>o=|-<_l7swi@*@FULY z-)!o2`qVII@R^-q2U(#p%z==s^I~$3Sxlwy;H`sxizDhXC<>S7wSuNNBNl|rFknoh zvh1RMrwVsfp@@KwkJ5T;Nz;PF4?@aM&sxa;aFO)N0SuD=eM%w6k!m@2cHgkPSwZWdKcSATcu_MrC3kGB7YQATcvKF)}(bF(4~2F)%O}y-DBz000McNliru z)CUI$9u3ZK0_p$&010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00L)8 zL_t(|+J#e1YZE~jerB@UHrchBG*XCdsY*}~K@h(vD1IP#tS3<@_$w-uf_Sik9_q=H zrDu=Yp7hX*2a!roqHUnyN43Tpo2J?9?tJu}*(4e%IPmQ3`|iy1KJPQL44s3S$JEr+ z(R@DtObDUo=jU(F&CPwFbc3Lq>vYG4;PJ65$PPYWj1|kOFZH`Pmd^Aa=C12vW95z9 z`~FEIC%?(Cp;uln|GIyD=EqCgwoOourO6LH?Ao6{!Sg^d-b`Y(EtWqp5|+ z^$yAzhYd)rLsL#FQ%qs~XXW$6#P|)``z}!sMNI`Nv6dF6Js&r3jddJROU&ftodXn} zBYd(1T1sv=OW7vSTA@}GD3$6+r5seN>)6~}!@$6R6-mR8WD)`uvZPOyv|waQ4c~V# zG}KLva)e<&f*^~Yo;@u=+_Y~AWSkh-RzL)%_ew=#X(>P#xnHZTlZr1G9ve_h0c0AhCqK@d~b?{Hm*F4i`pXp0ba7;6Z05XLLvX7zZt z?urK{o4AWljPeq|**OudijIZYx+UZ9F=glCx51X5?A||2(Fr$6Jfr!^E6- zuNmu1$eQ_H!WqH{VPDfAhTVs5ZY~j?w-%8-Fz1saxcL7tBz5ys?Eo~AI2dn*Kg<9C N002ovPDHLkV1lu0RB8YK literal 0 HcmV?d00001 diff --git a/packages/html/public/images/plus.png b/packages/html/public/images/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..24a84bb492bfe79e313da4079162ff42d2d7c372 GIT binary patch literal 236 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 VDsF_*qTT=i002ovPDHLkV1lt@lLr6* literal 0 HcmV?d00001 diff --git a/packages/html/public/images/sidebar_bg.gif b/packages/html/public/images/sidebar_bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..67e824486baac86e93cdf52bed49561254853520 GIT binary patch literal 80 zcmZ?wbhEHbG-qIDn8?5&sOQ)w@` excS(@HT+JOJtA*gmOSXPY4G-&-dV)TU=08sqZhdV literal 0 HcmV?d00001 diff --git a/packages/html/public/images/spacer.gif b/packages/html/public/images/spacer.gif new file mode 100644 index 0000000000000000000000000000000000000000..35d42e808f0a8017b8d52a06be2f8fec0b466a66 GIT binary patch literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7L;iE{qJ;0LZEa`2YX_ literal 0 HcmV?d00001 diff --git a/packages/html/public/images/toolbar_bg.gif b/packages/html/public/images/toolbar_bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..87b93740246a2fd1ad0ad0ee93c662c37862decf GIT binary patch literal 155 zcmZ?wbhEHbWMoibIKsdnm%3iB^0;R40r~We2DPUx+OI2RZBZ}SV^numC3mNJ>oxuA zlgc^UO&TvMW^6WYI4_^JL8EA&TK;aW(nH#1N6cET=vEvPid>?Uz0I`gl0y0>22`N< rlLaiG10q3oGO$V{EDVYKtZ+mq;);q(`xJv24>Ooq-qh5$GFSru1MD;X literal 0 HcmV?d00001 diff --git a/packages/html/public/images/undo.png b/packages/html/public/images/undo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ba0ffb168106c1a41ca49691efd2c1811a9e6ee GIT binary patch literal 879 zcmV-#1CacQP)WdKBPATcx`PH%P~GB7YQATl#LGch_eG$1Q5F)%QMP#j1A000McNliru z)CUI$9wdz2nuY)X010qNS#tmY3lRVS3lRZ-WM7d0000DMK}|sb0I`n?{9y$E00PHJ zL_t(|+Le=ENKJ1d?+N)W@aV+Nt`-$TWXoEjZJIL{k!{f*S&Y08&?X8{`}wrzx(^0bAI=H z&p8zQeNh{}6Mwq=u?}=+L1OhvYIfFf1(EAkT`RMMW{q55b35J!5l%Fd{nq)^Xw9Yq z#hY7~j~%S9E7BI06e_6V(%c}GyFV7s-wdvvNwf+h!mk0&*h=;Jbbm){WyZ7j05e|z zoFM>)1)$T_AgHs;xG)<}d^XLAK?t;3+ftNd7xSR;Jml3zQZ=E@J1y0kP745`0c@rz z)@kZ9Q8IOsw*1iUjGYEqpg*Guod*#=sne5dO?!(;>v)p`)OoS?vz6NimH<>q09~EE zF2C{WD{xPQQ8`!8+hUfdT|cKZ)RjfeuoVNC$x0yP*rEe{#gWElmJhmEoQii$GH>V_gn%pp@e+C4-za0OR-v6d)yqZg^M(NADTX z@T*jRj*oLQVYl6l`un%c6ssGTOL-n;zhz9yD}Hjgx>#aFCn4t(H=X zh^Pl>&GrEfmt<@M0S4vyhI|BIO8H2tFdH zB&;FvM7QPKzqBI^MNqCrxnAz>u=jnnrpqf7md!#AN4BlZb8jDY|E002ovPDHLk FV1m*$dWir4 literal 0 HcmV?d00001 diff --git a/packages/html/public/images/view_1_1.png b/packages/html/public/images/view_1_1.png new file mode 100644 index 0000000000000000000000000000000000000000..88657a1edadc3f23ce4c3b5b43bfdd90837d720c GIT binary patch literal 849 zcmV-X1FrmuP)WdKBJATc%|L3L*!GB7YTATcvKH8(mnFd!>1F)%REhL}eH000McNliru z)CUI$9w>!t)~x^l010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00OB= zL_t(|+Le-BNK;`L$DgxL(OgS&et%t9w3Nl@m-(6aBU)3ZgWpaLoby+7q%-bCemcG7Oln4lcSTQp_t$NnC@=;8K*+oZf>aGIlXl(^SjKJAq6R0TyU%NW+R0-(^j+N+ix-!)6jMarg zq0{Fv96&e%u)vCNy{Q>^G{V~eh+#-I9;CDyO*U$i5!EJx&^qH=Z{c)p0+-qzA^I4O*&ty*F(lOh-T0pP~Fjyd2M4 z0|A!Cq0zV>TU@dYU$OOV%gHwEfsz6+Wa{%P{7$3AE_`2;z$bUIajNo=){#|Y39$#c zpuJ-SMvwSb3^_Wj5f0@;d{$oWvhnhXAxqCk)c9eGkguoJ0_^@&rfuUNmlK=IOU_@s z)PwmtB%RvIAsQMq$*rlcf9a+dz5SM6+UazTVE)~|8vHgv@{z;Q{Wvo5KL&DOBcPA2 bStR%iR@yLCHUExD00000NkvXXu0mjfO$=_a literal 0 HcmV?d00001 diff --git a/packages/html/public/images/view_1_132.png b/packages/html/public/images/view_1_132.png new file mode 100644 index 0000000000000000000000000000000000000000..e9a1b72993cdd6cf8b40843d59ebf6b275f68b0a GIT binary patch literal 2199 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 Z{sY;b4iG3(D2)IB002ovPDHLkV1icv71aO$ literal 0 HcmV?d00001 diff --git a/packages/html/public/images/view_next.png b/packages/html/public/images/view_next.png new file mode 100644 index 0000000000000000000000000000000000000000..b4094f04e642e083b07efe53c7657df534534ea5 GIT binary patch literal 918 zcmV;H18Mw;P)WdKBJATc%|L3L*!GB7YTATcvKH8(mnFd!>1F)%REhL}eH000McNliru z)CUI$9w>!t)~x^l010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00Qnw zL_t(|+Le<_XcJ)&$7eU22Ae*Dh*nw?i;5T@CSqtVm#k|ty@u97g%YhOo3+Ayt@y~<>n)6kCoa>O=U&)m6n^F;BQ4Ocf; zxMu>Rau$m4TY0w}SZfA|VG>1Gl&zJk?0uiYW;7V@BNw0!Q^NFQoTOw9tyt*>SvP?p z08<$Q95xHc#w0ZC-Zs+&u~VnQ>11z^SDx<;;%#yc4q_<8K|%2nNe0jv9WsPKN;9E^ zV*zginx?^@W|$_0KpF@=BqDOt$wWLR6T7HqfW=-~mVlnm0Wlf4($)>A1S~*y!#Woi z82D!V@}*vr7Wjt6CKcs`$WVt>l#GTzcdy?avltLACsOZ_oml+HqP-Ue5PNEgD>AzC6lKb4-0f1Z~Jx@{J=#L58CVmz7XXq*tf^iQ9^*5uHKpE^#iKB``6 zyxEKTFR(C3i)SZHDMZ{0OBPgHIj&Sz6jqWF=D{P!ox4`qcI>Q90u0CDP1Eb{nwN>Q zKMe?AzDflwxdGFcCCPF+l1xp7RaK)q{umwO{f8qj3!RJRuYmQ-plp$2@%B_~sW+0l& z*>FG0yEobHE7!LWenKL(`}TKW{1~#3`I|z{=#pLaXU?<=qu-?-Z|7(%7Ngr}|5t$- s5u`l#0;vrmL;oWHxxdhn^JTh;-{QAwnjz0-1^@s607*qoM6N<$g7O26Pyhe` literal 0 HcmV?d00001 diff --git a/packages/html/public/images/view_previous.png b/packages/html/public/images/view_previous.png new file mode 100644 index 0000000000000000000000000000000000000000..b385b44c34c5abf1e094ba31abb46135674a3fce GIT binary patch literal 912 zcmV;B18@9^P)WdKBJATc%|L3L*!GB7YTATcvKH8(mnFd!>1F)%REhL}eH000McNliru z)CUI$9w>!t)~x^l010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00QVq zL_t(|+HI3fOcOyA$KP(d?ebL$B?f^Ym4Kl|L?VKLh$2QwOq6Jpgs3s#iG-*ZO$-M; z5f2{V>dBLe9|t2uLNSUT2WZ8F7FrM?5rLL&X}fg0+jhpcq$CP`$!~Wv^WQh~-hYMx z4F9^1d-L+uy$B_pTxM>uxFLjm^D}Q9)6Peuwy`;s$OwfLq4BugTAR)GF8@eFSN*wG zV@_Emqd!o~^W{em8lr&<0pHq`7X!7ilnH8U4)++V>bje1ERcZ%q8x{CSO$se7+qO? zR{DywWWsKD;FN&GcP3zkh;pOAY5_4uU{fIwRuT{=1QIbB7*-1f^=BKi4TeV4nh<7Z z0xe`_mkJ{8kBNaoDTJbG5&|+Nq>KV-`DRA1(-osO4_Vt;3M&T#{#B8}FF^sG0HmvB zL=q5^32-b6N;m{kG|J(yTA^lF3BCo_h<{xGOAZf_zFx@HvEY{yzz_x`(8J2a&|+8= zX>#S=PPCkAuJFwNl*YSz=wJp%=K?W0BgCjnAau&JNF5G)?C!(9FMBfdCs3Z2$_f2s zd)xB&iY)4`Gt%d|+4MzT9vw90kY@!2M2=Hx8|bEvM%_rE7OGLt|7YU|13WSDzJ|(c zm5d3SU^Pa-stG_I7iDHb94)zek+J5F!QRrs(lbF{`S7A--yjg&wv;6-@EosUAYG3g z)WO!9*Fc8awnQQ>O*@ugav7l74A!pW%|oY+{S|t!Z0|(#GzuRNt!|oiOa2N;BFy^{ z@22ZFk=<*ch>{%iwzj!`^bgEdnfP+3Gede&$-ZUF^_H(=BR2d$sKrO9Q-b@CouS13 z+0fTn;PFt-e^YEXPu3OtZubbE#zq&DJs|%cO>6)ZA%(~+vcAJDwJYZ2%4#?f7n4Gy mHb+Wzaa79@WAa;6b$O{gd!R^Wb0000WdKBJATc%|L3L*!GB7YTATcvKH8(mnFd!>1F)%REhL}eH000McNliru z)CUI$9xQ?e60`sS010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00Oc} zL_t(|+Le(@OcPNQhR<}S4_hb|Nhu)3YEY??xR8fX#GnyPAR%?3#>59=f{6>`$`H41 zTp4$|a77GJ2{EXPrmX>yCP}(x>w9`&IQwkkVwF^WN^iRIz-kbmY_nvbJ zK!roJbl_$G-K3bgF3l^{b5SNnyJrU{zs)?b+U(!al0rn-LZr*-q--|Z8{N^yuKKpK z8ihVasElTXVt;M5T8MN+=-`(X6nfKJ1xP-A7`e5xy7si`NEMLag67^v(4^;pl&J`9 z$L-RlhK76SJBTQ@3}BM0*Gdl=A((Li&C;MGWuT-|V9>gOkWf%sf5udxR-4ef3n4ZK zP>@%!kL5<8=2;``pk!zfq3J~p2%$6#bPhpEXCUMoJG7uIECK%WD^c*dq|I$+(mnQm zIFVjo#+tQ10z6)CJP{3lEs8jea#Es;C3GTiVKHcV9Hi$aQuiM|(I{v8%Dci7Wv`dl zr{WoiZVVxEr~ zAUBk(RuP8d5*{`dpXYgD9{1CWKJR#^=Uu+OTw4XTm7p!t7+TqY(Y`25hSnjHeF?dt zo$Qce2#xp=AD`ftp8H|yc*L*OtK~+hGk|jUo(iAw%BfMi#fBahwo$e{xg6lYkV)Q` zd~{Ou1{v|zyzKT{%gRm5X68S%k5_n k$NvOmg%1HuZp|V5PkvP&tL&dn*8l(j07*qoM6N<$g1W4BhX4Qo literal 0 HcmV?d00001 diff --git a/packages/html/public/images/zoom_in32.png b/packages/html/public/images/zoom_in32.png new file mode 100644 index 0000000000000000000000000000000000000000..438ff0ff02b2011d8cb3c34b86a28570ea850fb8 GIT binary patch literal 2184 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 literal 0 HcmV?d00001 diff --git a/packages/html/stories/Anchors.stories.js b/packages/html/stories/Anchors.stories.js index 7728b9be0..8c69750ed 100644 --- a/packages/html/stories/Anchors.stories.js +++ b/packages/html/stories/Anchors.stories.js @@ -1,10 +1,15 @@ import mxgraph from '@mxgraph/core'; -import HelloWorld from './HelloWorld.stories'; + +import { defaultArgTypes } from '../.storybook/preview'; export default { title: 'Connections/Anchors', argTypes: { - ...HelloWorld.argTypes + ...defaultArgTypes.argTypes, + rubberBand: { + type: 'boolean', + defaultValue: true + } } }; diff --git a/packages/html/stories/Clipboard.stories.js b/packages/html/stories/Clipboard.stories.js new file mode 100644 index 000000000..360e57d26 --- /dev/null +++ b/packages/html/stories/Clipboard.stories.js @@ -0,0 +1,359 @@ +import mxgraph from '@mxgraph/core'; + +import { defaultArgTypes } from '../.storybook/preview'; + +export default { + title: 'DnD_CopyPaste/Clipboard', + argTypes: { + ...defaultArgTypes, + contextMenu: { + type: 'boolean', + defaultValue: false + }, + rubberBand: { + type: 'boolean', + defaultValue: true + } + } +}; + +const Template = ({ label, ...args }) => { + const { + mxGraph, + mxEvent, + mxRubberband, + mxClipboard, + mxUtils, + mxEventUtils, + mxClient, + mxCodec, + mxGraphModel, + mxStringUtils + } = mxgraph; + + const container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflow = 'hidden'; + container.style.width = `${args.width}px`; + container.style.height = `${args.height}px`; + container.style.background = 'url(/images/grid.gif)'; + container.style.cursor = 'default'; + + // Disables the built-in context menu + if (!args.contextMenu) + mxEvent.disableContextMenu(container); + + // Creates the graph inside the given this.el + const graph = new mxGraph(container); + + // Public helper method for shared clipboard. + mxClipboard.cellsToString = function(cells) { + const codec = new mxCodec(); + const model = new mxGraphModel(); + const parent = model.getChildAt(model.getRoot(), 0); + + for (let i = 0; i < cells.length; i++) { + model.add(parent, cells[i]); + } + + return mxUtils.getXml(codec.encode(model)); + }; + + // Focused but invisible textarea during control or meta key events + const textInput = document.createElement('textarea'); + mxUtils.setOpacity(textInput, 0); + textInput.style.width = '1px'; + textInput.style.height = '1px'; + let restoreFocus = false; + const gs = graph.gridSize; + let lastPaste = null; + let dx = 0; + let dy = 0; + + // Workaround for no copy event in IE/FF if empty + textInput.value = ' '; + + // Shows a textare when control/cmd is pressed to handle native clipboard actions + mxEvent.addListener(document, 'keydown', function(evt) { + // No dialog visible + const source = mxEventUtils.getSource(evt); + + if ( + graph.isEnabled() && + !graph.isMouseDown && + !graph.isEditing() && + source.nodeName !== 'INPUT' + ) { + if ( + evt.keyCode === 224 /* FF */ || + (!mxClient.IS_MAC && evt.keyCode === 17) /* Control */ || + (mxClient.IS_MAC && + (evt.keyCode === 91 || evt.keyCode === 93)) /* Left/Right Meta */ + ) { + // Cannot use parentNode for check in IE + if (!restoreFocus) { + // Avoid autoscroll but allow handling of events + textInput.style.position = 'absolute'; + textInput.style.left = `${graph.container.scrollLeft + 10}px`; + textInput.style.top = `${graph.container.scrollTop + 10}px`; + graph.container.appendChild(textInput); + + restoreFocus = true; + textInput.focus(); + textInput.select(); + } + } + } + }); + + // Restores focus on graph this.el and removes text input from DOM + mxEvent.addListener(document, 'keyup', function(evt) { + if ( + restoreFocus && + (evt.keyCode === 224 /* FF */ || + evt.keyCode === 17 /* Control */ || + evt.keyCode === 91 || + evt.keyCode === 93) /* Meta */ + ) { + restoreFocus = false; + + if (!graph.isEditing()) { + graph.container.focus(); + } + + textInput.parentNode.removeChild(textInput); + } + }); + + // Inserts the XML for the given cells into the text input for copy + const copyCells = function(graph, cells) { + if (cells.length > 0) { + const clones = graph.cloneCells(cells); + + // Checks for orphaned relative children and makes absolute + for (let i = 0; i < clones.length; i++) { + const state = graph.view.getState(cells[i]); + + if (state != null) { + const geo = graph.getCellGeometry(clones[i]); + + if (geo != null && geo.relative) { + geo.relative = false; + geo.x = state.x / state.view.scale - state.view.translate.x; + geo.y = state.y / state.view.scale - state.view.translate.y; + } + } + } + + textInput.value = mxClipboard.cellsToString(clones); + } + + textInput.select(); + lastPaste = textInput.value; + }; + + // Handles copy event by putting XML for current selection into text input + mxEvent.addListener( + textInput, + 'copy', + mxUtils.bind(this, function(evt) { + if (graph.isEnabled() && !graph.isSelectionEmpty()) { + copyCells( + graph, + mxUtils.sortCells( + graph.model.getTopmostCells(graph.getSelectionCells()) + ) + ); + dx = 0; + dy = 0; + } + }) + ); + + // Handles cut event by removing cells putting XML into text input + mxEvent.addListener( + textInput, + 'cut', + mxUtils.bind(this, function(evt) { + if (graph.isEnabled() && !graph.isSelectionEmpty()) { + copyCells(graph, graph.removeCells()); + dx = -gs; + dy = -gs; + } + }) + ); + + // Merges XML into existing graph and layers + const importXml = function(xml, dx, dy) { + dx = dx != null ? dx : 0; + dy = dy != null ? dy : 0; + let cells = []; + + try { + const doc = mxUtils.parseXml(xml); + const node = doc.documentElement; + + if (node != null) { + const model = new mxGraphModel(); + const codec = new mxCodec(node.ownerDocument); + codec.decode(node, model); + + const childCount = model.getChildCount(model.getRoot()); + const targetChildCount = graph.model.getChildCount( + graph.model.getRoot() + ); + + // Merges existing layers and adds new layers + graph.model.beginUpdate(); + try { + for (let i = 0; i < childCount; i++) { + let parent = model.getChildAt(model.getRoot(), i); + + // Adds cells to existing layers if not locked + if (targetChildCount > i) { + // Inserts into active layer if only one layer is being pasted + const target = + childCount === 1 + ? graph.getDefaultParent() + : graph.model.getChildAt(graph.model.getRoot(), i); + + if (!graph.isCellLocked(target)) { + const children = model.getChildren(parent); + cells = cells.concat( + graph.importCells(children, dx, dy, target) + ); + } + } else { + // Delta is non cascading, needs separate move for layers + parent = graph.importCells( + [parent], + 0, + 0, + graph.model.getRoot() + )[0]; + const children = graph.model.getChildren(parent); + graph.moveCells(children, dx, dy); + cells = cells.concat(children); + } + } + } finally { + graph.model.endUpdate(); + } + } + } catch (e) { + alert(e); + throw e; + } + + return cells; + }; + + // Parses and inserts XML into graph + const pasteText = function(text) { + const xml = mxStringUtils.trim(text); + const x = + graph.container.scrollLeft / graph.view.scale - graph.view.translate.x; + const y = + graph.container.scrollTop / graph.view.scale - graph.view.translate.y; + + if (xml.length > 0) { + if (lastPaste !== xml) { + lastPaste = xml; + dx = 0; + dy = 0; + } else { + dx += gs; + dy += gs; + } + + // Standard paste via control-v + if (xml.substring(0, 14) === '') { + graph.setSelectionCells(importXml(xml, dx, dy)); + graph.scrollCellToVisible(graph.getSelectionCell()); + } + } + }; + + // Function to fetch text from paste events + const extractGraphModelFromEvent = function(evt) { + let data = null; + + if (evt != null) { + const provider = + evt.dataTransfer != null ? evt.dataTransfer : evt.clipboardData; + + if (provider != null) { + data = + mxUtils.indexOf(provider.types, 'text/html') >= 0 + ? provider.getData('text/html') + : null; + + if ( + mxUtils.indexOf( + provider.types, + 'text/plain' && (data == null || data.length === 0) + ) + ) { + data = provider.getData('text/plain'); + } + } + } + + return data; + }; + + // Handles paste event by parsing and inserting XML + mxEvent.addListener(textInput, 'paste', function(evt) { + // Clears existing contents before paste - should not be needed + // because all text is selected, but doesn't hurt since the + // actual pasting of the new text is delayed in all cases. + textInput.value = ''; + + if (graph.isEnabled()) { + const xml = extractGraphModelFromEvent(evt); + + if (xml != null && xml.length > 0) { + pasteText(xml); + } else { + // Timeout for new value to appear + window.setTimeout( + mxUtils.bind(this, function() { + pasteText(textInput.value); + }), + 0 + ); + } + } + + textInput.select(); + }); + + // Enables rubberband selection + if (args.rubberBand) + new mxRubberband(graph); + + // Gets the default parent for inserting new cells. This + // is normally the first child of the root (ie. layer 0). + const parent = graph.getDefaultParent(); + + // Adds cells to the model in a single step + graph.batchUpdate(() => { + const v1 = graph.insertVertex({ + parent, + value: 'Hello,', + position: [20, 20], + size: [80, 30], + }); + const v2 = graph.insertVertex({ + parent, + value: 'World!', + position: [200, 150], + size: [80, 30], + }); + const e1 = graph.insertEdge({ parent, source: v1, target: v2 }); + }); + + return container; +} + +export const Default = Template.bind({}); \ No newline at end of file diff --git a/packages/html/stories/DragSource.stories.js b/packages/html/stories/DragSource.stories.js new file mode 100644 index 000000000..46d4c995b --- /dev/null +++ b/packages/html/stories/DragSource.stories.js @@ -0,0 +1,189 @@ +import mxgraph from '@mxgraph/core'; + +import { defaultArgTypes } from '../.storybook/preview'; + +export default { + title: 'DnD_CopyPaste/DragSource', + argTypes: { + ...defaultArgTypes, + rubberBand: { + type: 'boolean', + defaultValue: true + } + } +}; + +const Template = ({ label, ...args }) => { + const { + mxGraph, + mxDomUtils, + mxRubberband, + mxDragSource, + mxUtils, + mxGestureUtils, + mxEdgeHandler, + mxGraphHandler, + mxGuide, + mxEventUtils, + mxCell, + mxGeometry + } = mxgraph; + + const container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflow = 'hidden'; + container.style.width = `${args.width}px`; + container.style.height = `${args.height}px`; + container.style.cursor = 'default'; + + class MyCustomGuide extends mxGuide { + isEnabledForEvent(evt) { + // Alt disables guides + return !mxEventUtils.isAltDown(evt); + } + } + + class MyCustomGraphHandler extends mxGraphHandler { + // Enables guides + guidesEnabled = true; + + createGuide() { + return new MyCustomGuide(this.graph, this.getGuideStates()); + } + } + + class MyCustomEdgeHandler extends mxEdgeHandler { + // Enables snapping waypoints to terminals + snapToTerminals = true; + } + + class MyCustomGraph extends mxGraph { + createGraphHandler() { + return new MyCustomGraphHandler(this); + } + + createEdgeHandler(state, edgeStyle) { + return new MyCustomEdgeHandler(state, edgeStyle); + } + } + + const graphs = []; + + // Creates the graph inside the given container + for (let i = 0; i < 2; i++) { + const subContainer = document.createElement('div'); + subContainer.style.overflow = 'hidden'; + subContainer.style.position = 'relative'; + subContainer.style.width = '321px'; + subContainer.style.height = '241px'; + subContainer.style.background = "url('/images/grid.gif')"; + subContainer.style.cursor = 'default'; + + container.appendChild(subContainer); + + const graph = new MyCustomGraph(subContainer); + graph.gridSize = 30; + + // Uncomment the following if you want the container + // to fit the size of the graph + // graph.setResizeContainer(true); + + // Enables rubberband selection + if (args.rubberBand) + new mxRubberband(graph); + + // Gets the default parent for inserting new cells. This + // is normally the first child of the root (ie. layer 0). + const parent = graph.getDefaultParent(); + + // Adds cells to the model in a single step + graph.batchUpdate(() => { + const v1 = graph.insertVertex({ + parent, + value: 'Hello,', + position: [20, 20], + size: [80, 30], + }); + const v2 = graph.insertVertex({ + parent, + value: 'World!', + position: [200, 150], + size: [80, 30], + }); + const e1 = graph.insertEdge({ + parent, + source: v1, + target: v2, + }); + }); + + graphs.push(graph); + } + + // Returns the graph under the mouse + const graphF = evt => { + const x = mxEventUtils.getClientX(evt); + const y = mxEventUtils.getClientY(evt); + const elt = document.elementFromPoint(x, y); + + for (const graph of graphs) { + if (mxDomUtils.isAncestorNode(graph.container, elt)) { + return graph; + } + } + + return null; + }; + + // Inserts a cell at the given location + const funct = (graph, evt, target, x, y) => { + const cell = new mxCell('Test', new mxGeometry(0, 0, 120, 40)); + cell.vertex = true; + const cells = graph.importCells([cell], x, y, target); + + if (cells != null && cells.length > 0) { + graph.scrollCellToVisible(cells[0]); + graph.setSelectionCells(cells); + } + }; + + // Creates a DOM node that acts as the drag source + const img = mxUtils.createImage('images/icons48/gear.png'); + img.style.width = '48px'; + img.style.height = '48px'; + container.appendChild(img); + + // Creates the element that is being for the actual preview. + const dragElt = document.createElement('div'); + dragElt.style.border = 'dashed black 1px'; + dragElt.style.width = '120px'; + dragElt.style.height = '40px'; + + // Drag source is configured to use dragElt for preview and as drag icon + // if scalePreview (last) argument is true. Dx and dy are null to force + // the use of the defaults. Note that dx and dy are only used for the + // drag icon but not for the preview. + const ds = mxGestureUtils.makeDraggable( + img, + graphF, + funct, + dragElt, + null, + null, + graphs[0].autoscroll, + true + ); + + // Redirects feature to global switch. Note that this feature should only be used + // if the the x and y arguments are used in funct to insert the cell. + ds.isGuidesEnabled = () => { + return graphs[0].graphHandler.guidesEnabled; + }; + + // Restores original drag icon while outside of graph + ds.createDragElement = mxDragSource.prototype.createDragElement; + + return container; +} + +export const Default = Template.bind({}); \ No newline at end of file diff --git a/packages/html/stories/Drop.stories.js b/packages/html/stories/Drop.stories.js new file mode 100644 index 000000000..ca3d6c728 --- /dev/null +++ b/packages/html/stories/Drop.stories.js @@ -0,0 +1,193 @@ +import mxgraph from '@mxgraph/core'; + +import { defaultArgTypes } from '../.storybook/preview'; + +export default { + title: 'DnD_CopyPaste/Drop', + argTypes: { + ...defaultArgTypes, + contextMenu: { + type: 'boolean', + defaultValue: false + }, + rubberBand: { + type: 'boolean', + defaultValue: true + } + } +}; + +const Template = ({ label, ...args }) => { + const { + mxGraph, + mxDomUtils, + mxRubberband, + mxDragSource, + mxUtils, + mxGestureUtils, + mxEdgeHandler, + mxGraphHandler, + mxGuide, + mxEventUtils, + mxEvent, + mxClient + } = mxgraph; + + const div = document.createElement('div'); + div.innerHTML = 'Drag & drop your images below:
'; + + const container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflow = 'hidden'; + container.style.width = `${args.width}px`; + container.style.height = `${args.height}px`; + container.style.background = 'url(/images/grid.gif)'; + container.style.cursor = 'default'; + div.appendChild(container); + + // Checks if the browser is supported + const fileSupport = + window.File != null && + window.FileReader != null && + window.FileList != null; + + if (!fileSupport || !mxClient.isBrowserSupported()) { + // Displays an error message if the browser is not supported. + mxUtils.error('Browser is not supported!', 200, false); + } else { + // Disables the built-in context menu + if (!args.contextMenu) + mxEvent.disableContextMenu(container); + + // Creates the graph inside the given this.el + const graph = new mxGraph(container); + + // Enables rubberband selection + if (args.rubberBand) + new mxRubberband(graph); + + mxEvent.addListener(container, 'dragover', function(evt) { + if (graph.isEnabled()) { + evt.stopPropagation(); + evt.preventDefault(); + } + }); + + mxEvent.addListener(container, 'drop', evt => { + if (graph.isEnabled()) { + evt.stopPropagation(); + evt.preventDefault(); + + // Gets drop location point for vertex + const pt = mxUtils.convertPoint( + graph.container, + mxEventUtils.getClientX(evt), + mxEventUtils.getClientY(evt) + ); + const tr = graph.view.translate; + const { scale } = graph.view; + const x = pt.x / scale - tr.x; + const y = pt.y / scale - tr.y; + + // Converts local images to data urls + const filesArray = evt.dataTransfer.files; + + for (let i = 0; i < filesArray.length; i++) { + handleDrop(graph, filesArray[i], x + i * 10, y + i * 10); + } + } + }); + } + + return div; +} + +function handleDrop(graph, file, x, y) { + // Handles each file as a separate insert for simplicity. + // Use barrier to handle multiple files as a single insert. + + if (file.type.substring(0, 5) === 'image') { + const reader = new FileReader(); + + reader.onload = function(e) { + // Gets size of image for vertex + let data = e.target.result; + + // SVG needs special handling to add viewbox if missing and + // find initial size from SVG attributes (only for IE11) + if (file.type.substring(0, 9) === 'image/svg') { + const comma = data.indexOf(','); + const svgText = atob(data.substring(comma + 1)); + const root = mxUtils.parseXml(svgText); + + // Parses SVG to find width and height + if (root != null) { + const svgs = root.getElementsByTagName('svg'); + + if (svgs.length > 0) { + const svgRoot = svgs[0]; + let w = parseFloat(svgRoot.getAttribute('width')); + let h = parseFloat(svgRoot.getAttribute('height')); + + // Check if viewBox attribute already exists + const vb = svgRoot.getAttribute('viewBox'); + + if (vb == null || vb.length === 0) { + svgRoot.setAttribute('viewBox', `0 0 ${w} ${h}`); + } + // Uses width and height from viewbox for + // missing width and height attributes + else if (Number.isNaN(w) || Number.isNaN(h)) { + const tokens = vb.split(' '); + + if (tokens.length > 3) { + w = parseFloat(tokens[2]); + h = parseFloat(tokens[3]); + } + } + + w = Math.max(1, Math.round(w)); + h = Math.max(1, Math.round(h)); + + data = `data:image/svg+xml,${btoa( + mxUtils.getXml(svgs[0], '\n') + )}`; + graph.insertVertex({ + position: [x, y], + size: [w, h], + style: `shape=image;image=${data};`, + }); + } + } + } else { + const img = new Image(); + + img.onload = () => { + const w = Math.max(1, img.width); + const h = Math.max(1, img.height); + + // Converts format of data url to cell style value for use in vertex + const semi = data.indexOf(';'); + + if (semi > 0) { + data = + data.substring(0, semi) + + data.substring(data.indexOf(',', semi + 1)); + } + + graph.insertVertex({ + position: [x, y], + size: [w, h], + style: `shape=image;image=${data};`, + }); + }; + + img.src = data; + } + }; + + reader.readAsDataURL(file); + } +} + +export const Default = Template.bind({}); \ No newline at end of file diff --git a/packages/html/stories/EdgeTolerance.stories.js b/packages/html/stories/EdgeTolerance.stories.js index e14ad4bc2..f10e4d218 100644 --- a/packages/html/stories/EdgeTolerance.stories.js +++ b/packages/html/stories/EdgeTolerance.stories.js @@ -1,16 +1,11 @@ import mxgraph from '@mxgraph/core'; +import { defaultArgTypes } from '../.storybook/preview'; + export default { title: 'Connections/EdgeTolerance', argTypes: { - width: { - type: 'number', - defaultValue: 800 - }, - height: { - type: 'number', - defaultValue: 600 - } + ...defaultArgTypes.argTypes } }; diff --git a/packages/html/stories/Editing.stories.js b/packages/html/stories/Editing.stories.js new file mode 100644 index 000000000..223a28781 --- /dev/null +++ b/packages/html/stories/Editing.stories.js @@ -0,0 +1,164 @@ +import mxgraph from '@mxgraph/core'; + +import { defaultArgTypes } from '../.storybook/preview'; + +export default { + title: 'Editing/Editing', + argTypes: { + ...defaultArgTypes, + contextMenu: { + type: 'boolean', + defaultValue: false + }, + rubberBand: { + type: 'boolean', + defaultValue: true + } + } +}; + +const Template = ({ label, ...args }) => { + const { + mxGraph, + mxEvent, + mxKeyHandler, + mxUtils, + mxDomUtils, + mxCloneUtils, + mxEventUtils + } = mxgraph; + + const div = document.createElement('div'); + div.innerHTML = ` +

Editing

+ This example demonstrates using the in-place editor trigger to specify + the editing value and write the new value into a specific field of the + user object. Wrapping and DOM nodes as labels are also demonstrated + here. +
+
+ Double-click the upper/lower half of the cell to edit different fields + of the user object. + `; + + const container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflow = 'hidden'; + container.style.width = `${args.width}px`; + container.style.height = `${args.height}px`; + container.style.background = 'url(/images/grid.gif)'; + container.style.cursor = 'default'; + div.appendChild(container); + + class MyCustomGraph extends mxGraph { + getLabel(cell) { + // Returns a HTML representation of the cell where the + // upper half is the first value, lower half is second + // value + + const table = document.createElement('table'); + table.style.height = '100%'; + table.style.width = '100%'; + + const body = document.createElement('tbody'); + const tr1 = document.createElement('tr'); + const td1 = document.createElement('td'); + td1.style.textAlign = 'center'; + td1.style.fontSize = '12px'; + td1.style.color = '#774400'; + mxDomUtils.write(td1, cell.value.first); + + const tr2 = document.createElement('tr'); + const td2 = document.createElement('td'); + td2.style.textAlign = 'center'; + td2.style.fontSize = '12px'; + td2.style.color = '#774400'; + mxDomUtils.write(td2, cell.value.second); + + tr1.appendChild(td1); + tr2.appendChild(td2); + body.appendChild(tr1); + body.appendChild(tr2); + table.appendChild(body); + + return table; + } + + getEditingValue(cell, evt) { + // Returns the editing value for the given cell and event + evt.fieldname = this.__getFieldnameForEvent(cell, evt); + return cell.value[evt.fieldname] || ''; + } + + __getFieldnameForEvent(cell, evt) { + // Helper method that returns the fieldname to be used for + // a mouse event + if (evt != null) { + // Finds the relative coordinates inside the cell + const point = mxUtils.convertPoint( + this.container, + mxEventUtils.getClientX(evt), + mxEventUtils.getClientY(evt) + ); + const state = this.getView().getState(cell); + + if (state != null) { + point.x -= state.x; + point.y -= state.y; + + // Returns second if mouse in second half of cell + if (point.y > state.height / 2) { + return 'second'; + } + } + } + return 'first'; + } + + labelChanged(cell, newValue, trigger) { + // Sets the new value for the given cell and trigger + const name = trigger != null ? trigger.fieldname : null; + + if (name != null) { + // Clones the user object for correct undo and puts + // the new value in the correct field. + const value = mxCloneUtils.clone(cell.value); + value[name] = newValue; + newValue = value; + + super.labelChanged(cell, newValue, trigger); + } + } + } + + // Creates the graph inside the given container + const graph = new MyCustomGraph(container); + graph.setHtmlLabels(true); + + // Adds handling of return and escape keystrokes for editing + const keyHandler = new mxKeyHandler(graph); + + // Sample user objects with 2 fields + const value = {}; + value.first = 'First value'; + value.second = 'Second value'; + + // Gets the default parent for inserting new cells. This + // is normally the first child of the root (ie. layer 0). + const parent = graph.getDefaultParent(); + + // Adds cells to the model in a single step + graph.batchUpdate(() => { + const v1 = graph.insertVertex({ + parent, + value, + position: [100, 60], + size: [120, 80], + style: 'overflow=fill;', + }); + }); + + return div; +} + +export const Default = Template.bind({}); \ No newline at end of file diff --git a/packages/html/stories/ExtendCanvas.stories.js b/packages/html/stories/ExtendCanvas.stories.js index 41d9cc3e0..f4d975356 100644 --- a/packages/html/stories/ExtendCanvas.stories.js +++ b/packages/html/stories/ExtendCanvas.stories.js @@ -1,11 +1,19 @@ import mxgraph from '@mxgraph/core'; -import HelloWorld from './HelloWorld.stories'; +import { defaultArgTypes } from '../.storybook/preview'; export default { title: 'Backgrounds/ExtendCanvas', argTypes: { - ...HelloWorld.argTypes + ...defaultArgTypes.argTypes, + contextMenu: { + type: 'boolean', + defaultValue: false + }, + rubberBand: { + type: 'boolean', + defaultValue: true + } } }; @@ -15,9 +23,7 @@ const Template = ({ label, ...args }) => { mxEvent, mxRubberband, mxRectangle, - mxGraphView, mxPoint, - mxDomHelpers, mxUtils } = mxgraph; diff --git a/packages/html/stories/FixedPoints.stories.js b/packages/html/stories/FixedPoints.stories.js new file mode 100644 index 000000000..a387a8a80 --- /dev/null +++ b/packages/html/stories/FixedPoints.stories.js @@ -0,0 +1,266 @@ +import mxgraph from '@mxgraph/core'; + +import { defaultArgTypes } from '../.storybook/preview'; + +export default { + title: 'Connections/FixedPoints', + argTypes: { + ...defaultArgTypes.argTypes, + rubberBand: { + type: 'boolean', + defaultValue: true + } + } +}; + +const Template = ({ label, ...args }) => { + const { + mxGraph, + mxRubberband, + mxConnectionHandler, + mxConnectionConstraint, + mxConstraintHandler, + mxPoint, + mxCellState, + mxEdgeHandler + } = mxgraph; + + const container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflow = 'hidden'; + container.style.width = `${args.width}px`; + container.style.height = `${args.height}px`; + container.style.background = 'url(/images/grid.gif)'; + container.style.cursor = 'default'; + + class MyCustomConstraintHandler extends mxConstraintHandler { + // Snaps to fixed points + intersects(icon, point, source, existingEdge) { + return ( + !source || existingEdge || mxUtils.intersects(icon.bounds, point) + ); + } + } + + class MyCustomConnectionHandler extends mxConnectionHandler { + // connectImage = new mxImage('images/connector.gif', 16, 16); + + isConnectableCell(cell) { + return false; + } + + /* + * Special case: Snaps source of new connections to fixed points + * Without a connect preview in connectionHandler.createEdgeState mouseMove + * and getSourcePerimeterPoint should be overriden by setting sourceConstraint + * sourceConstraint to null in mouseMove and updating it and returning the + * nearest point (cp) in getSourcePerimeterPoint (see below) + */ + updateEdgeState(pt, constraint) { + if (pt != null && this.previous != null) { + const constraints = this.graph.getAllConnectionConstraints( + this.previous + ); + let nearestConstraint = null; + let dist = null; + + for (let i = 0; i < constraints.length; i++) { + const cp = this.graph.getConnectionPoint( + this.previous, + constraints[i] + ); + + if (cp != null) { + const tmp = + (cp.x - pt.x) * (cp.x - pt.x) + (cp.y - pt.y) * (cp.y - pt.y); + + if (dist == null || tmp < dist) { + nearestConstraint = constraints[i]; + dist = tmp; + } + } + } + + if (nearestConstraint != null) { + this.sourceConstraint = nearestConstraint; + } + + // In case the edge style must be changed during the preview: + // this.edgeState.style['edgeStyle'] = 'orthogonalEdgeStyle'; + // And to use the new edge style in the new edge inserted into the graph, + // update the cell style as follows: + // this.edgeState.cell.style = mxUtils.setStyle(this.edgeState.cell.style, 'edgeStyle', this.edgeState.style['edgeStyle']); + } + return super.updateEdgeState(pt, constraint); + } + + createEdgeState(me) { + // Connect preview + const edge = this.graph.createEdge( + null, + null, + null, + null, + null, + 'edgeStyle=orthogonalEdgeStyle' + ); + + return new mxCellState( + this.graph.view, + edge, + this.graph.getCellStyle(edge) + ); + } + } + + class MyCustomEdgeHandler extends mxEdgeHandler { + // Disables floating connections (only use with no connect image) + isConnectableCell(cell) { + return graph.connectionHandler.isConnectableCell(cell); + } + } + + class MyCustomGraph extends mxGraph { + createConnectionHandler() { + const r = new MyCustomConnectionHandler(); + r.constraintHandler = new MyCustomConstraintHandler(this); + return r; + } + + createEdgeHandler(state, edgeStyle) { + const r = new MyCustomEdgeHandler(state, edgeStyle); + r.constraintHandler = new MyCustomConstraintHandler(this); + return r; + } + + getAllConnectionConstraints(terminal) { + if (terminal != null && this.model.isVertex(terminal.cell)) { + return [ + new mxConnectionConstraint(new mxPoint(0, 0), true), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(1, 0), true), + new mxConnectionConstraint(new mxPoint(0, 0.5), true), + new mxConnectionConstraint(new mxPoint(1, 0.5), true), + new mxConnectionConstraint(new mxPoint(0, 1), true), + new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(1, 1), true), + ]; + } + return null; + } + } + + // Creates the graph inside the given container + const graph = new MyCustomGraph(container); + graph.setConnectable(true); + + // Enables rubberband selection + if (args.rubberBand) + new mxRubberband(graph); + + // Gets the default parent for inserting new cells. This + // is normally the first child of the root (ie. layer 0). + const parent = graph.getDefaultParent(); + + // Adds cells to the model in a single step + graph.batchUpdate(() => { + const v1 = graph.insertVertex({ + parent, + value: 'Hello,', + position: [20, 20], + size: [80, 60], + style: 'shape=triangle;perimeter=trianglePerimeter', + }); + const v2 = graph.insertVertex({ + parent, + value: 'World!', + position: [200, 150], + size: [80, 60], + style: 'shape=ellipse;perimeter=ellipsePerimeter', + }); + const v3 = graph.insertVertex({ + parent, + value: 'Hello,', + position: [200, 20], + size: [80, 30], + }); + const e1 = graph.insertEdge({ + parent, + value: '', + source: v1, + target: v2, + style: + 'edgeStyle=elbowEdgeStyle;elbow=horizontal;' + + 'exitX=0.5;exitY=1;exitPerimeter=1;entryX=0;entryY=0;entryPerimeter=1;', + }); + const e2 = graph.insertEdge({ + parent, + value: '', + source: v3, + target: v2, + style: + 'edgeStyle=elbowEdgeStyle;elbow=horizontal;orthogonal=0;' + + 'entryX=0;entryY=0;entryPerimeter=1;', + }); + }); + + // Use this code to snap the source point for new connections without a connect preview, + // ie. without an overridden graph.connectionHandler.createEdgeState + /* + let mxConnectionHandlerMouseMove = mxConnectionHandler.prototype.mouseMove; + mxConnectionHandler.prototype.mouseMove = function(sender, me) + { + this.sourceConstraint = null; + + mxConnectionHandlerMouseMove.apply(this, arguments); + }; + + let mxConnectionHandlerGetSourcePerimeterPoint = mxConnectionHandler.prototype.getSourcePerimeterPoint; + mxConnectionHandler.prototype.getSourcePerimeterPoint = function(state, pt, me) + { + let result = null; + + if (this.previous != null && pt != null) + { + let constraints = this.graph.getAllConnectionConstraints(this.previous); + let nearestConstraint = null; + let nearest = null; + let dist = null; + + for (let i = 0; i < constraints.length; i++) + { + let cp = this.graph.getConnectionPoint(this.previous, constraints[i]); + + if (cp != null) + { + let tmp = (cp.x - pt.x) * (cp.x - pt.x) + (cp.y - pt.y) * (cp.y - pt.y); + + if (dist == null || tmp < dist) + { + nearestConstraint = constraints[i]; + nearest = cp; + dist = tmp; + } + } + } + + if (nearestConstraint != null) + { + this.sourceConstraint = nearestConstraint; + result = nearest; + } + } + + if (result == null) + { + result = mxConnectionHandlerGetSourcePerimeterPoint.apply(this, arguments); + } + + return result; + }; + */ + + return container; +} + +export const Default = Template.bind({}); \ No newline at end of file diff --git a/packages/html/stories/Grid.stories.js b/packages/html/stories/Grid.stories.js index 8efa60d75..d02f66faa 100644 --- a/packages/html/stories/Grid.stories.js +++ b/packages/html/stories/Grid.stories.js @@ -1,11 +1,19 @@ import mxgraph from '@mxgraph/core'; -import HelloWorld from './HelloWorld.stories'; +import { defaultArgTypes } from '../.storybook/preview'; export default { title: 'Backgrounds/Grid', argTypes: { - ...HelloWorld.argTypes + ...defaultArgTypes.argTypes, + contextMenu: { + type: 'boolean', + defaultValue: false + }, + rubberBand: { + type: 'boolean', + defaultValue: true + } } }; diff --git a/packages/html/stories/HelloPort.stories.js b/packages/html/stories/HelloPort.stories.js new file mode 100644 index 000000000..09e04b0a7 --- /dev/null +++ b/packages/html/stories/HelloPort.stories.js @@ -0,0 +1,115 @@ +import mxgraph from '@mxgraph/core'; + +import { defaultArgTypes } from '../.storybook/preview'; + +export default { + title: 'Connections/HelloPort', + argTypes: { + ...defaultArgTypes.argTypes, + rubberBand: { + type: 'boolean', + defaultValue: true + } + } +}; + +const Template = ({ label, ...args }) => { + const { + mxGraph, + mxRubberband, + mxEdgeStyle, + mxPoint, + mxConstants, + mxDomHelpers + } = mxgraph; + + const div = document.createElement('div'); + + const container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflow = 'hidden'; + container.style.width = `${args.width}px`; + container.style.height = `${args.height}px`; + container.style.background = 'url(/images/grid.gif)'; + container.style.cursor = 'default'; + div.appendChild(container); + + // Creates the graph inside the given container + const graph = new mxGraph(container); + graph.setConnectable(true); + graph.setTooltips(true); + + // Sets the default edge style + const style = graph.getStylesheet().getDefaultEdgeStyle(); + style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector; + + // Ports are not used as terminals for edges, they are + // only used to compute the graphical connection point + graph.isPort = function(cell) { + const geo = this.getCellGeometry(cell); + + return geo != null ? geo.relative : false; + }; + + // Implements a tooltip that shows the actual + // source and target of an edge + graph.getTooltipForCell = function(cell) { + if (this.model.isEdge(cell)) { + return `${this.convertValueToString( + this.model.getTerminal(cell, true) + )} => ${this.convertValueToString( + this.model.getTerminal(cell, false) + )}`; + } + + return mxGraph.prototype.getTooltipForCell.apply(this, arguments); + }; + + // Removes the folding icon and disables any folding + graph.isCellFoldable = function(cell) { + return false; + }; + + // Enables rubberband selection + if (args.rubberBand) + new mxRubberband(graph); + + // Gets the default parent for inserting new cells. This + // is normally the first child of the root (ie. layer 0). + const parent = graph.getDefaultParent(); + + // Adds cells to the model in a single step + graph.getModel().beginUpdate(); + try { + const v1 = graph.insertVertex(parent, null, 'Hello', 20, 80, 80, 30); + v1.setConnectable(false); + const v11 = graph.insertVertex(v1, null, '', 1, 1, 10, 10); + v11.geometry.offset = new mxPoint(-5, -5); + v11.geometry.relative = true; + const v12 = graph.insertVertex(v1, null, '', 1, 0, 10, 10); + v12.geometry.offset = new mxPoint(-5, -5); + v12.geometry.relative = true; + const v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30); + const v3 = graph.insertVertex(parent, null, 'World2', 200, 20, 80, 30); + var e1 = graph.insertEdge(parent, null, '', v11, v2); + var e1 = graph.insertEdge(parent, null, '', v12, v3); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + + const controller = document.createElement('div'); + div.appendChild(controller); + + const button = mxDomHelpers.button('View XML', function() { + const encoder = new mxCodec(); + const node = encoder.encode(graph.getModel()); + mxUtils.popup(mxUtils.getPrettyXml(node), true); + }); + + controller.appendChild(button); + + return div; +} + +export const Default = Template.bind({}); \ No newline at end of file diff --git a/packages/html/stories/HelloWorld.stories.js b/packages/html/stories/HelloWorld.stories.js index 2d4570c6c..d9a8d2c39 100644 --- a/packages/html/stories/HelloWorld.stories.js +++ b/packages/html/stories/HelloWorld.stories.js @@ -1,16 +1,11 @@ import mxgraph from '@mxgraph/core'; +import { defaultArgTypes } from '../.storybook/preview'; + export default { title: 'Basic/HelloWorld', argTypes: { - width: { - type: 'number', - defaultValue: 800 - }, - height: { - type: 'number', - defaultValue: 600 - }, + ...defaultArgTypes, contextMenu: { type: 'boolean', defaultValue: false @@ -30,6 +25,7 @@ const Template = ({ label, ...args }) => { container.style.overflow = 'hidden'; container.style.width = `${args.width}px`; container.style.height = `${args.height}px`; + container.style.background = 'url(/images/grid.gif)'; container.style.cursor = 'default'; if (!args.contextMenu) diff --git a/packages/html/stories/Orthogonal.stories.js b/packages/html/stories/Orthogonal.stories.js new file mode 100644 index 000000000..3554e43fd --- /dev/null +++ b/packages/html/stories/Orthogonal.stories.js @@ -0,0 +1,177 @@ +import mxgraph from '@mxgraph/core'; + +import { defaultArgTypes } from '../.storybook/preview'; + +export default { + title: 'Connections/Orthogonal', + argTypes: { + ...defaultArgTypes.argTypes + } +}; + +const Template = ({ label, ...args }) => { + const { + mxGraph, + mxRubberband, + mxConnectionHandler, + mxGraphHandler, + mxGuide, + mxPoint, + mxCellState, + mxEdgeHandler, + mxGraphView + } = mxgraph; + + const container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflow = 'hidden'; + container.style.width = `${args.width}px`; + container.style.height = `${args.height}px`; + container.style.background = 'url(/images/grid.gif)'; + container.style.cursor = 'default'; + + // Enables guides + mxGraphHandler.prototype.guidesEnabled = true; + + // Alt disables guides + mxGuide.prototype.isEnabledForEvent = function(evt) { + return !mxEvent.isAltDown(evt); + }; + + // Enables snapping waypoints to terminals + mxEdgeHandler.prototype.snapToTerminals = true; + + // Enables orthogonal connect preview in IE + mxConnectionHandler.prototype.movePreviewAway = false; + + // Creates the graph inside the given container + const graph = new mxGraph(container); + graph.disconnectOnMove = false; + graph.foldingEnabled = false; + graph.cellsResizable = false; + graph.extendParents = false; + graph.setConnectable(true); + + // Implements perimeter-less connection points as fixed points (computed before the edge style). + graph.view.updateFixedTerminalPoint = function( + edge, + terminal, + source, + constraint + ) { + mxGraphView.prototype.updateFixedTerminalPoint.apply(this, arguments); + + const pts = edge.absolutePoints; + const pt = pts[source ? 0 : pts.length - 1]; + + if ( + terminal != null && + pt == null && + this.getPerimeterFunction(terminal) == null + ) { + edge.setAbsoluteTerminalPoint( + new mxPoint( + this.getRoutingCenterX(terminal), + this.getRoutingCenterY(terminal) + ), + source + ); + } + }; + + // Changes the default edge style + graph.getStylesheet().getDefaultEdgeStyle().edgeStyle = + 'orthogonalEdgeStyle'; + delete graph.getStylesheet().getDefaultEdgeStyle().endArrow; + + // Implements the connect preview + graph.connectionHandler.createEdgeState = function(me) { + const edge = graph.createEdge(null, null, null, null, null); + + return new mxCellState( + this.graph.view, + edge, + this.graph.getCellStyle(edge) + ); + }; + + // Uncomment the following if you want the container + // to fit the size of the graph + // graph.setResizeContainer(true); + + // Enables rubberband selection + if (args.rubberBand) + new mxRubberband(graph); + + // Gets the default parent for inserting new cells. This + // is normally the first child of the root (ie. layer 0). + const parent = graph.getDefaultParent(); + + // Adds cells to the model in a single step + graph.getModel().beginUpdate(); + try { + const v1 = graph.insertVertex(parent, null, '', 40, 40, 40, 30); + v1.setConnectable(false); + const v11 = graph.insertVertex( + v1, + null, + '', + 0.5, + 0, + 10, + 40, + 'portConstraint=northsouth;', + true + ); + v11.geometry.offset = new mxPoint(-5, -5); + const v12 = graph.insertVertex( + v1, + null, + '', + 0, + 0.5, + 10, + 10, + 'portConstraint=west;shape=triangle;direction=west;perimeter=none;' + + 'routingCenterX=-0.5;routingCenterY=0;', + true + ); + v12.geometry.offset = new mxPoint(-10, -5); + const v13 = graph.insertVertex( + v1, + null, + '', + 1, + 0.5, + 10, + 10, + 'portConstraint=east;shape=triangle;direction=east;perimeter=none;' + + 'routingCenterX=0.5;routingCenterY=0;', + true + ); + v13.geometry.offset = new mxPoint(0, -5); + + const v2 = graph.addCell(graph.getModel().cloneCell(v1)); + v2.geometry.x = 200; + v2.geometry.y = 60; + + const v3 = graph.addCell(graph.getModel().cloneCell(v1)); + v3.geometry.x = 40; + v3.geometry.y = 150; + + const v4 = graph.addCell(graph.getModel().cloneCell(v1)); + v4.geometry.x = 200; + v4.geometry.y = 170; + + graph.insertEdge(parent, null, '', v1.getChildAt(2), v2.getChildAt(1)); + graph.insertEdge(parent, null, '', v2.getChildAt(2), v3.getChildAt(1)); + graph.insertEdge(parent, null, '', v3.getChildAt(2), v4.getChildAt(1)); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + + return container; +} + +export const Default = Template.bind({}); \ No newline at end of file diff --git a/packages/html/stories/PortRefs.stories.js b/packages/html/stories/PortRefs.stories.js new file mode 100644 index 000000000..5f7f26c49 --- /dev/null +++ b/packages/html/stories/PortRefs.stories.js @@ -0,0 +1,279 @@ +import mxgraph from '@mxgraph/core'; + +import { defaultArgTypes } from '../.storybook/preview'; + +export default { + title: 'Connections/PortRefs', + argTypes: { + ...defaultArgTypes.argTypes + } +}; + +const Template = ({ label, ...args }) => { + const { + mxGraph, + mxRubberband, + mxPoint, + mxEdgeHandler, + mxConstraintHandler, + mxImage, + mxShape, + mxTriangle, + mxConstants, + mxConnectionConstraint + } = mxgraph; + + const container = document.createElement('div'); + container.style.position = 'relative'; + container.style.overflow = 'hidden'; + container.style.width = `${args.width}px`; + container.style.height = `${args.height}px`; + container.style.background = 'url(/images/grid.gif)'; + container.style.cursor = 'default'; + + // Replaces the port image + mxConstraintHandler.prototype.pointImage = new mxImage( + '/images/dot.gif', + 10, + 10 + ); + + const graph = new mxGraph(container); + graph.setConnectable(true); + + // Disables automatic handling of ports. This disables the reset of the + // respective style in mxGraph.cellConnected. Note that this feature may + // be useful if floating and fixed connections are combined. + graph.setPortsEnabled(false); + + // Enables rubberband selection + new mxRubberband(graph); + + // Gets the default parent for inserting new cells. This + // is normally the first child of the root (ie. layer 0). + const parent = graph.getDefaultParent(); + + // Ports are equal for all shapes... + const ports = new Array(); + + // NOTE: Constraint is used later for orthogonal edge routing (currently ignored) + ports.w = { x: 0, y: 0.5, perimeter: true, constraint: 'west' }; + ports.e = { x: 1, y: 0.5, perimeter: true, constraint: 'east' }; + ports.n = { x: 0.5, y: 0, perimeter: true, constraint: 'north' }; + ports.s = { x: 0.5, y: 1, perimeter: true, constraint: 'south' }; + ports.nw = { x: 0, y: 0, perimeter: true, constraint: 'north west' }; + ports.ne = { x: 1, y: 0, perimeter: true, constraint: 'north east' }; + ports.sw = { x: 0, y: 1, perimeter: true, constraint: 'south west' }; + ports.se = { x: 1, y: 1, perimeter: true, constraint: 'south east' }; + + // ... except for triangles + const ports2 = new Array(); + + // NOTE: Constraint is used later for orthogonal edge routing (currently ignored) + ports2.in1 = { x: 0, y: 0, perimeter: true, constraint: 'west' }; + ports2.in2 = { x: 0, y: 0.25, perimeter: true, constraint: 'west' }; + ports2.in3 = { x: 0, y: 0.5, perimeter: true, constraint: 'west' }; + ports2.in4 = { x: 0, y: 0.75, perimeter: true, constraint: 'west' }; + ports2.in5 = { x: 0, y: 1, perimeter: true, constraint: 'west' }; + + ports2.out1 = { + x: 0.5, + y: 0, + perimeter: true, + constraint: 'north east', + }; + ports2.out2 = { x: 1, y: 0.5, perimeter: true, constraint: 'east' }; + ports2.out3 = { + x: 0.5, + y: 1, + perimeter: true, + constraint: 'south east', + }; + + // Extends shapes classes to return their ports + mxShape.prototype.getPorts = function() { + return ports; + }; + + mxTriangle.prototype.getPorts = function() { + return ports2; + }; + + // Disables floating connections (only connections via ports allowed) + graph.connectionHandler.isConnectableCell = function(cell) { + return false; + }; + mxEdgeHandler.prototype.isConnectableCell = function(cell) { + return graph.connectionHandler.isConnectableCell(cell); + }; + + // Disables existing port functionality + graph.view.getTerminalPort = function(state, terminal, source) { + return terminal; + }; + + // Returns all possible ports for a given terminal + graph.getAllConnectionConstraints = function(terminal, source) { + if ( + terminal != null && + terminal.shape != null && + terminal.shape.stencil != null + ) { + // for stencils with existing constraints... + if (terminal.shape.stencil != null) { + return terminal.shape.stencil.constraints; + } + } else if (terminal != null && this.model.isVertex(terminal.cell)) { + if (terminal.shape != null) { + const ports = terminal.shape.getPorts(); + const cstrs = new Array(); + + for (const id in ports) { + const port = ports[id]; + + const cstr = new mxConnectionConstraint( + new mxPoint(port.x, port.y), + port.perimeter + ); + cstr.id = id; + cstrs.push(cstr); + } + + return cstrs; + } + } + + return null; + }; + + // Sets the port for the given connection + graph.setConnectionConstraint = function( + edge, + terminal, + source, + constraint + ) { + if (constraint != null) { + const key = source + ? mxConstants.STYLE_SOURCE_PORT + : mxConstants.STYLE_TARGET_PORT; + + if (constraint == null || constraint.id == null) { + this.setCellStyles(key, null, [edge]); + } else if (constraint.id != null) { + this.setCellStyles(key, constraint.id, [edge]); + } + } + }; + + // Returns the port for the given connection + graph.getConnectionConstraint = function(edge, terminal, source) { + const key = source + ? mxConstants.STYLE_SOURCE_PORT + : mxConstants.STYLE_TARGET_PORT; + const id = edge.style[key]; + + if (id != null) { + const c = new mxConnectionConstraint(null, null); + c.id = id; + + return c; + } + + return null; + }; + + // Returns the actual point for a port by redirecting the constraint to the port + const graphGetConnectionPoint = graph.getConnectionPoint; + graph.getConnectionPoint = function(vertex, constraint) { + if (constraint.id != null && vertex != null && vertex.shape != null) { + const port = vertex.shape.getPorts()[constraint.id]; + + if (port != null) { + constraint = new mxConnectionConstraint( + new mxPoint(port.x, port.y), + port.perimeter + ); + } + } + + return graphGetConnectionPoint.apply(this, arguments); + }; + + // Adds cells to the model in a single step + graph.getModel().beginUpdate(); + try { + const v1 = graph.insertVertex(parent, null, 'A', 20, 20, 100, 40); + const v2 = graph.insertVertex( + parent, + null, + 'B', + 80, + 100, + 100, + 100, + 'shape=ellipse;perimeter=ellipsePerimeter' + ); + const v3 = graph.insertVertex( + parent, + null, + 'C', + 190, + 30, + 100, + 60, + 'shape=triangle;perimeter=trianglePerimeter;direction=south' + ); + const e1 = graph.insertEdge( + parent, + null, + '', + v1, + v2, + 'sourcePort=s;targetPort=nw' + ); + const e2 = graph.insertEdge( + parent, + null, + '', + v1, + v3, + 'sourcePort=e;targetPort=out3' + ); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + + // Comming soon... Integration with orthogonal edge style + // Sets default edge style to use port constraints (needs to be moved up when uncommented) + // graph.getStylesheet().getDefaultEdgeStyle()['edgeStyle'] = 'orthogonalEdgeStyle'; + /* let mxUtilsGetPortConstraints = mxUtils.getPortConstraints; + mxUtils.getPortConstraints = function(terminal, edge, source, defaultValue) + { + let key = (source) ? mxConstants.STYLE_SOURCE_PORT : mxConstants.STYLE_TARGET_PORT; + let id = edge.style[key]; + + let port = terminal.shape.getPorts()[id]; + + // TODO: Add support for rotation, direction + if (port != null) + { + return port.constraint; + } + + return mxUtilsGetPortConstraints.apply(this, arguments); + }; + // Connect preview + graph.connectionHandler.createEdgeState = function(me) + { + let edge = graph.createEdge(null, null, null, null, null); + + return new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge)); + }; + */ + + return container; +} + +export const Default = Template.bind({}); \ No newline at end of file diff --git a/src/pages/backgrounds/ExtendCanvas.js b/src/pages/backgrounds/ExtendCanvas.js deleted file mode 100644 index b937da038..000000000 --- a/src/pages/backgrounds/ExtendCanvas.js +++ /dev/null @@ -1,270 +0,0 @@ -/** - * Copyright (c) 2006-2013, JGraph Ltd - * Converted to ES9 syntax/React by David Morrissey 2021 - */ - -import React from 'react'; -import mxEvent from '../../mxgraph/util/event/mxEvent'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxRubberband from '../../mxgraph/handler/mxRubberband'; -import mxRectangle from '../../mxgraph/util/datatypes/mxRectangle'; -import mxUtils from '../../mxgraph/util/mxUtils'; -import mxPoint from '../../mxgraph/util/datatypes/mxPoint'; - -class ExtendCanvas extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph - return ( - <> -

Extend canvas

- This example demonstrates implementing an infinite canvas with - scrollbars. -
{ - this.el = el; - }} - style={{ - position: 'relative', - overflow: 'auto', - height: '241px', - background: "url('editors/images/grid.gif')", - cursor: 'default', - }} - /> - - ); - } - - componentDidMount() { - // Disables the built-in context menu - mxEvent.disableContextMenu(this.el); - - /** - * Specifies the size of the size for "tiles" to be used for a graph with - * scrollbars but no visible background page. A good value is large - * enough to reduce the number of repaints that is caused for auto- - * translation, which depends on this value, and small enough to give - * a small empty buffer around the graph. Default is 400x400. - */ - const scrollTileSize = new mxRectangle(0, 0, 400, 400); - - class MyCustomGraph extends mxGraph { - /** - * Returns the padding for pages in page view with scrollbars. - */ - getPagePadding() { - return new mxPoint( - Math.max(0, Math.round(this.container.offsetWidth - 34)), - Math.max(0, Math.round(this.container.offsetHeight - 34)) - ); - } - - /** - * Returns the size of the page format scaled with the page size. - */ - getPageSize() { - return this.pageVisible - ? new mxRectangle( - 0, - 0, - this.pageFormat.width * this.pageScale, - this.pageFormat.height * this.pageScale - ) - : scrollTileSize; - } - - /** - * Returns a rectangle describing the position and count of the - * background pages, where x and y are the position of the top, - * left page and width and height are the vertical and horizontal - * page count. - */ - getPageLayout() { - const size = this.pageVisible ? this.getPageSize() : scrollTileSize; - const bounds = this.getGraphBounds(); - - if (bounds.width === 0 || bounds.height === 0) { - return new mxRectangle(0, 0, 1, 1); - } - - // Computes untransformed graph bounds - const x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x); - const y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y); - const w = Math.floor(bounds.width / this.view.scale); - const h = Math.floor(bounds.height / this.view.scale); - - const x0 = Math.floor(x / size.width); - const y0 = Math.floor(y / size.height); - const w0 = Math.ceil((x + w) / size.width) - x0; - const h0 = Math.ceil((y + h) / size.height) - y0; - - return new mxRectangle(x0, y0, w0, h0); - } - - getPreferredPageSize(bounds, width, height) { - const pages = this.getPageLayout(); - const size = this.getPageSize(); - - return new mxRectangle( - 0, - 0, - pages.width * size.width, - pages.height * size.height - ); - } - - sizeDidChange() { - if (this.container != null && mxUtils.hasScrollbars(this.container)) { - const pages = this.getPageLayout(); - const pad = this.getPagePadding(); - const size = this.getPageSize(); - - // Updates the minimum graph size - const minw = Math.ceil( - (2 * pad.x) / this.view.scale + pages.width * size.width - ); - const minh = Math.ceil( - (2 * pad.y) / this.view.scale + pages.height * size.height - ); - - const min = this.minimumGraphSize; - - // LATER: Fix flicker of scrollbar size in IE quirks mode - // after delayed call in window.resize event handler - if (min == null || min.width !== minw || min.height !== minh) { - this.minimumGraphSize = new mxRectangle(0, 0, minw, minh); - } - - // Updates auto-translate to include padding and graph size - const dx = pad.x / this.view.scale - pages.x * size.width; - const dy = pad.y / this.view.scale - pages.y * size.height; - - if ( - !this.autoTranslate && - (this.view.translate.x !== dx || this.view.translate.y !== dy) - ) { - this.autoTranslate = true; - this.view.x0 = pages.x; - this.view.y0 = pages.y; - - // NOTE: THIS INVOKES THIS METHOD AGAIN. UNFORTUNATELY THERE IS NO WAY AROUND THIS SINCE THE - // BOUNDS ARE KNOWN AFTER THE VALIDATION AND SETTING THE TRANSLATE TRIGGERS A REVALIDATION. - // SHOULD MOVE TRANSLATE/SCALE TO VIEW. - const tx = this.view.translate.x; - const ty = this.view.translate.y; - - this.view.setTranslate(dx, dy); - this.container.scrollLeft += (dx - tx) * this.view.scale; - this.container.scrollTop += (dy - ty) * this.view.scale; - - this.autoTranslate = false; - return; - } - super.sizeDidChange(); - } - } - } - - // Creates the graph inside the given container - const graph = (this.graph = new MyCustomGraph(this.el)); - graph.panningHandler.ignoreCell = true; - graph.setPanning(true); - - // Fits the number of background pages to the graph - graph.view.getBackgroundPageBounds = function() { - const layout = this.graph.getPageLayout(); - const page = this.graph.getPageSize(); - - return new mxRectangle( - this.scale * (this.translate.x + layout.x * page.width), - this.scale * (this.translate.y + layout.y * page.height), - this.scale * layout.width * page.width, - this.scale * layout.height * page.height - ); - }; - - /** - * Guesses autoTranslate to avoid another repaint (see below). - * Works if only the scale of the graph changes or if pages - * are visible and the visible pages do not change. - */ - const graphViewValidate = graph.view.validate; - graph.view.validate = function() { - if ( - this.graph.container != null && - mxUtils.hasScrollbars(this.graph.container) - ) { - const pad = this.graph.getPagePadding(); - const size = this.graph.getPageSize(); - - // Updating scrollbars here causes flickering in quirks and is not needed - // if zoom method is always used to set the current scale on the graph. - const tx = this.translate.x; - const ty = this.translate.y; - this.translate.x = pad.x / this.scale - (this.x0 || 0) * size.width; - this.translate.y = pad.y / this.scale - (this.y0 || 0) * size.height; - } - - graphViewValidate.apply(this, arguments); - }; - - // Enables rubberband selection - new mxRubberband(graph); - - // Gets the default parent for inserting new cells. This - // is normally the first child of the root (ie. layer 0). - const parent = graph.getDefaultParent(); - - // Adds cells to the model in a single step - graph.batchUpdate(() => { - const v1 = graph.insertVertex({ - parent, - value: 'Hello,', - position: [20, 20], - size: [80, 30], - }); - const v2 = graph.insertVertex({ - parent, - value: 'World!', - position: [200, 150], - size: [80, 30], - }); - const e1 = graph.insertEdge({ - parent, - source: v1, - target: v2, - }); - }); - - // Sets initial scrollbar positions - window.setTimeout(() => { - const bounds = graph.getGraphBounds(); - const width = Math.max( - bounds.width, - scrollTileSize.width * graph.view.scale - ); - const height = Math.max( - bounds.height, - scrollTileSize.height * graph.view.scale - ); - graph.container.scrollTop = Math.floor( - Math.max( - 0, - bounds.y - Math.max(20, (graph.container.clientHeight - height) / 4) - ) - ); - graph.container.scrollLeft = Math.floor( - Math.max( - 0, - bounds.x - Math.max(0, (graph.container.clientWidth - width) / 2) - ) - ); - }, 0); - } -} - -export default ExtendCanvas; diff --git a/src/pages/backgrounds/Grid.js b/src/pages/backgrounds/Grid.js deleted file mode 100644 index 89782fa67..000000000 --- a/src/pages/backgrounds/Grid.js +++ /dev/null @@ -1,218 +0,0 @@ -/** - * Copyright (c) 2006-2013, JGraph Ltd - * Converted to ES9 syntax/React by David Morrissey 2021 - */ - -import React from 'react'; -import mxEvent from '../../mxgraph/util/event/mxEvent'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxRubberband from '../../mxgraph/handler/mxRubberband'; -import mxPoint from '../../mxgraph/util/datatypes/mxPoint'; -import mxLog from '../../mxgraph/util/gui/mxLog'; -import mxUtils from '../../mxgraph/util/mxUtils'; -import mxGraphView from '../../mxgraph/view/graph/mxGraphView'; - -class Grid extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph - return ( - <> -

Grid

- This example demonstrates drawing a grid dynamically using HTML 5 - canvas. -
{ - this.el = el; - }} - style={{ - overflow: 'hidden', - height: '481px', - cursor: 'default', - }} - /> -
{ - this.el2 = el; - }} - /> - - ); - } - - componentDidMount() { - mxEvent.disableContextMenu(this.el); - - // Creates the graph inside the given container - const graph = new mxGraph(this.el); - graph.graphHandler.scaleGrid = true; - graph.setPanning(true); - - // Enables rubberband selection - new mxRubberband(graph); - - let repaintGrid; - - // Create grid dynamically (requires canvas) - (function() { - try { - const canvas = document.createElement('canvas'); - canvas.style.position = 'absolute'; - canvas.style.top = '0px'; - canvas.style.left = '0px'; - canvas.style.zIndex = -1; - graph.container.appendChild(canvas); - - const ctx = canvas.getContext('2d'); - - // Modify event filtering to accept canvas as container - const mxGraphViewIsContainerEvent = - mxGraphView.prototype.isContainerEvent; - mxGraphView.prototype.isContainerEvent = function(evt) { - return ( - mxGraphViewIsContainerEvent.apply(this, arguments) || - mxEvent.getSource(evt) === canvas - ); - }; - - let s = 0; - let gs = 0; - let tr = new mxPoint(); - let w = 0; - let h = 0; - - repaintGrid = function() { - if (ctx != null) { - const bounds = graph.getGraphBounds(); - const width = Math.max( - bounds.x + bounds.width, - graph.container.clientWidth - ); - const height = Math.max( - bounds.y + bounds.height, - graph.container.clientHeight - ); - const sizeChanged = width !== w || height !== h; - - if ( - graph.view.scale !== s || - graph.view.translate.x !== tr.x || - graph.view.translate.y !== tr.y || - gs !== graph.gridSize || - sizeChanged - ) { - tr = graph.view.translate.clone(); - s = graph.view.scale; - gs = graph.gridSize; - w = width; - h = height; - - // Clears the background if required - if (!sizeChanged) { - ctx.clearRect(0, 0, w, h); - } else { - canvas.setAttribute('width', w); - canvas.setAttribute('height', h); - } - - const tx = tr.x * s; - const ty = tr.y * s; - - // Sets the distance of the grid lines in pixels - const minStepping = graph.gridSize; - let stepping = minStepping * s; - - if (stepping < minStepping) { - const count = - Math.round(Math.ceil(minStepping / stepping) / 2) * 2; - stepping = count * stepping; - } - - const xs = Math.floor((0 - tx) / stepping) * stepping + tx; - let xe = Math.ceil(w / stepping) * stepping; - const ys = Math.floor((0 - ty) / stepping) * stepping + ty; - let ye = Math.ceil(h / stepping) * stepping; - - xe += Math.ceil(stepping); - ye += Math.ceil(stepping); - - const ixs = Math.round(xs); - const ixe = Math.round(xe); - const iys = Math.round(ys); - const iye = Math.round(ye); - - // Draws the actual grid - ctx.strokeStyle = '#f6f6f6'; - ctx.beginPath(); - - for (let x = xs; x <= xe; x += stepping) { - x = Math.round((x - tx) / stepping) * stepping + tx; - const ix = Math.round(x); - - ctx.moveTo(ix + 0.5, iys + 0.5); - ctx.lineTo(ix + 0.5, iye + 0.5); - } - - for (let y = ys; y <= ye; y += stepping) { - y = Math.round((y - ty) / stepping) * stepping + ty; - const iy = Math.round(y); - - ctx.moveTo(ixs + 0.5, iy + 0.5); - ctx.lineTo(ixe + 0.5, iy + 0.5); - } - - ctx.closePath(); - ctx.stroke(); - } - } - }; - } catch (e) { - mxLog.show(); - mxLog.debug('Using background image'); - - this.el.style.backgroundImage = "url('editors/images/grid.gif')"; - } - - const mxGraphViewValidateBackground = - mxGraphView.prototype.validateBackground; - mxGraphView.prototype.validateBackground = function() { - mxGraphViewValidateBackground.apply(this, arguments); - repaintGrid(); - }; - })(); - - // Gets the default parent for inserting new cells. This - // is normally the first child of the root (ie. layer 0). - const parent = graph.getDefaultParent(); - - // Adds cells to the model in a single step - graph.getModel().beginUpdate(); - try { - const v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30); - const v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30); - const e1 = graph.insertEdge(parent, null, '', v1, v2); - } finally { - // Updates the display - graph.getModel().endUpdate(); - } - - graph.centerZoom = false; - - this.el2.appendChild( - mxUtils.button('+', function() { - graph.zoomIn(); - }) - ); - - this.el2.appendChild( - mxUtils.button('-', function() { - graph.zoomOut(); - }) - ); - } -} - -export default Grid; diff --git a/src/pages/backgrounds/index.js b/src/pages/backgrounds/index.js deleted file mode 100644 index 118a86b10..000000000 --- a/src/pages/backgrounds/index.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import Grid from './Grid'; -import Preview from '../Previews'; -import ExtendCanvas from './ExtendCanvas'; -import PageTabs from '../PageTabs'; - -export default function _Backgrounds() { - return ( - - } /> - } /> - - ); -} diff --git a/src/pages/basic/HelloWorld.js b/src/pages/basic/HelloWorld.js deleted file mode 100644 index 8266ab50e..000000000 --- a/src/pages/basic/HelloWorld.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2006-2018, JGraph Ltd - * Converted to ES9 syntax/React by David Morrissey 2021 - */ - -import React from 'react'; -import mxEvent from '../../mxgraph/util/event/mxEvent'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxRubberband from '../../mxgraph/handler/mxRubberband'; - -class HelloWorld extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph with a grid wallpaper - return ( - <> -

Hello, World!

- This example demonstrates using a DOM node to create a graph and adding - vertices and edges. -
{ - this.el = el; - }} - style={{ - position: 'relative', - overflow: 'hidden', - height: '241px', - background: "url('editors/images/grid.gif')", - cursor: 'default', - }} - /> - - ); - } - - componentDidMount() { - // Create a sample graph in the DOM node with the specified ID. - mxEvent.disableContextMenu(this.el); // Disable the built-in context menu - const graph = new mxGraph(this.el); // Create the graph inside the given container - new mxRubberband(graph); // Enable rubberband selection - - // Get the default parent for inserting new cells. This - // is normally the first child of the root (ie. layer 0). - const parent = graph.getDefaultParent(); - - graph.batchUpdate(() => { - // Add cells to the model in a single step - const vertex1 = graph.insertVertex({ - parent, - value: 'Hello', - position: [20, 20], - size: [80, 30], - relative: false, - }); - const vertex2 = graph.insertVertex({ - parent, - value: 'World!', - position: [200, 150], - size: [80, 30], - relative: false, - }); - const edge = graph.insertEdge({ - parent, - // value: 'to the', - source: vertex1, - target: vertex2, - }); - }); - } -} - -export default HelloWorld; diff --git a/src/pages/basic/Template.js b/src/pages/basic/Template.js deleted file mode 100644 index 576cddd88..000000000 --- a/src/pages/basic/Template.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2006-2013, JGraph Ltd - - Template. This is used as a template HTML file by the - backends to demonstrate the deployment of the client with a graph embedded - in the page as XML data (see graph variable in the onload-handler). - - *** THIS FILE MUST BE DEPLOYED BY ONE OF THE BACKENDS! *** - */ - -import React from 'react'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxRubberband from '../../mxgraph/handler/mxRubberband'; -import mxUtils from '../../mxgraph/util/mxUtils'; -import mxCodec from '../../mxgraph/serialization/mxCodec'; - -class Template extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph - return ( - <> -

Hello, World!

-
{ - this.el = el; - }} - style={{ - overflow: 'hidden', - position: 'relative', - height: '241px', - background: - "url('/mxgraph/javascript/examples/editors/images/grid.gif')", - cursor: 'default', - }} - /> - - ); - } - - componentDidMount() { - // Creates the graph inside the given container - const graph = new mxGraph(this.el); - - // Adds rubberband selection to the graph - new mxRubberband(graph); - - const doc = mxUtils.parseXml(xml); - const codec = new mxCodec(doc); - codec.decode(doc.documentElement, graph.getModel()); - } -} - -export default Template; diff --git a/src/pages/basic/index.js b/src/pages/basic/index.js deleted file mode 100644 index d306b4814..000000000 --- a/src/pages/basic/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import HelloWorld from './HelloWorld'; -import Preview from '../Previews'; -import PageTabs from '../PageTabs'; - -export default function _Basic() { - { - /* } /> */ - } - return ( - - } /> - - ); -} diff --git a/src/pages/connections/Anchors.js b/src/pages/connections/Anchors.js deleted file mode 100644 index b7ff28ae9..000000000 --- a/src/pages/connections/Anchors.js +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2006-2013, JGraph Ltd - * Converted to ES9 syntax/React by David Morrissey 2021 - */ - -import React from 'react'; -import mxEvent from '../../mxgraph/util/event/mxEvent'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxRubberband from '../../mxgraph/handler/mxRubberband'; -import mxShape from '../../mxgraph/shape/mxShape'; -import mxConnectionConstraint from '../../mxgraph/view/connection/mxConnectionConstraint'; -import mxPoint from '../../mxgraph/util/datatypes/mxPoint'; -import mxPolyline from '../../mxgraph/shape/edge/mxPolyline'; -import mxCellState from '../../mxgraph/view/cell/mxCellState'; -import mxGeometry from '../../mxgraph/util/datatypes/mxGeometry'; -import mxConnectionHandler from '../../mxgraph/handler/mxConnectionHandler'; - -class Anchors extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph - return ( - <> -

Anchors

- This example demonstrates defining fixed connection points for all - shapes. -
{ - this.el = el; - }} - style={{ - position: 'relative', - overflow: 'hidden', - height: '241px', - background: 'url("editors/images/grid.gif")', - cursor: 'default', - }} - /> - - ); - } - - componentDidMount() { - // Disables the built-in context menu - mxEvent.disableContextMenu(this.el); - - class MyCustomConnectionHandler extends mxConnectionHandler { - // Enables connect preview for the default edge style - createEdgeState(me) { - const edge = graph.createEdge(null, null, null, null, null); - return new mxCellState( - this.graph.view, - edge, - this.graph.getCellStyle(edge) - ); - } - } - - class MyCustomGraph extends mxGraph { - getAllConnectionConstraints(terminal, source) { - // Overridden to define per-shape connection points - if (terminal != null && terminal.shape != null) { - if (terminal.shape.stencil != null) { - if (terminal.shape.stencil.constraints != null) { - return terminal.shape.stencil.constraints; - } - } else if (terminal.shape.constraints != null) { - return terminal.shape.constraints; - } - } - return null; - } - - createConnectionHandler() { - return new MyCustomConnectionHandler(this); - } - } - - class MyCustomGeometryClass extends mxGeometry { - // Defines the default constraints for the vertices - constraints = [ - new mxConnectionConstraint(new mxPoint(0.25, 0), true), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.75, 0), true), - new mxConnectionConstraint(new mxPoint(0, 0.25), true), - new mxConnectionConstraint(new mxPoint(0, 0.5), true), - new mxConnectionConstraint(new mxPoint(0, 0.75), true), - new mxConnectionConstraint(new mxPoint(1, 0.25), true), - new mxConnectionConstraint(new mxPoint(1, 0.5), true), - new mxConnectionConstraint(new mxPoint(1, 0.75), true), - new mxConnectionConstraint(new mxPoint(0.25, 1), true), - new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(0.75, 1), true), - ]; - } - - // Edges have no connection points - mxPolyline.prototype.constraints = null; - - // Creates the graph inside the given container - const graph = new MyCustomGraph(this.el); - graph.setConnectable(true); - - // Specifies the default edge style - graph.getStylesheet().getDefaultEdgeStyle().edgeStyle = - 'orthogonalEdgeStyle'; - - // Enables rubberband selection - new mxRubberband(graph); - - // Gets the default parent for inserting new cells. This - // is normally the first child of the root (ie. layer 0). - const parent = graph.getDefaultParent(); - - // Adds cells to the model in a single step - graph.batchUpdate(() => { - const v1 = graph.insertVertex({ - parent, - value: 'Hello,', - position: [20, 20], - size: [80, 30], - geometryClass: MyCustomGeometryClass, - }); - const v2 = graph.insertVertex({ - parent, - value: 'World!', - position: [200, 150], - size: [80, 30], - geometryClass: MyCustomGeometryClass, - }); - const e1 = graph.insertEdge({ - parent, - value: '', - position: v1, - size: v2, - }); - }); - } -} - -export default Anchors; diff --git a/src/pages/connections/Orthogonal.js b/src/pages/connections/Orthogonal.js deleted file mode 100644 index 2dd2d7bb9..000000000 --- a/src/pages/connections/Orthogonal.js +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright (c) 2006-2013, JGraph Ltd - * Converted to ES9 syntax/React by David Morrissey 2021 - */ - -import React from 'react'; -import mxEvent from '../../mxgraph/util/event/mxEvent'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxRubberband from '../../mxgraph/handler/mxRubberband'; -import mxGraphHandler from '../../mxgraph/handler/mxGraphHandler'; -import mxGuide from '../../mxgraph/util/mxGuide'; -import mxEdgeHandler from '../../mxgraph/handler/mxEdgeHandler'; -import mxConnectionHandler from '../../mxgraph/handler/mxConnectionHandler'; -import mxGraphView from '../../mxgraph/view/graph/mxGraphView'; -import mxPoint from '../../mxgraph/util/datatypes/mxPoint'; -import mxCellState from '../../mxgraph/view/cell/mxCellState'; - -class Orthogonal extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph - return ( - <> -

Orthogonal

- This example demonstrates the use of port constraints and orthogonal - edge styles and handlers. -
{ - this.el = el; - }} - style={{ - overflow: 'hidden', - position: 'relative', - height: '241px', - background: "url('editors/images/grid.gif')", - cursor: 'default', - }} - /> - - ); - } - - componentDidMount() { - // Enables guides - mxGraphHandler.prototype.guidesEnabled = true; - - // Alt disables guides - mxGuide.prototype.isEnabledForEvent = function(evt) { - return !mxEvent.isAltDown(evt); - }; - - // Enables snapping waypoints to terminals - mxEdgeHandler.prototype.snapToTerminals = true; - - // Enables orthogonal connect preview in IE - mxConnectionHandler.prototype.movePreviewAway = false; - - // Creates the graph inside the given container - const graph = new mxGraph(this.el); - graph.disconnectOnMove = false; - graph.foldingEnabled = false; - graph.cellsResizable = false; - graph.extendParents = false; - graph.setConnectable(true); - - // Implements perimeter-less connection points as fixed points (computed before the edge style). - graph.view.updateFixedTerminalPoint = function( - edge, - terminal, - source, - constraint - ) { - mxGraphView.prototype.updateFixedTerminalPoint.apply(this, arguments); - - const pts = edge.absolutePoints; - const pt = pts[source ? 0 : pts.length - 1]; - - if ( - terminal != null && - pt == null && - this.getPerimeterFunction(terminal) == null - ) { - edge.setAbsoluteTerminalPoint( - new mxPoint( - this.getRoutingCenterX(terminal), - this.getRoutingCenterY(terminal) - ), - source - ); - } - }; - - // Changes the default edge style - graph.getStylesheet().getDefaultEdgeStyle().edgeStyle = - 'orthogonalEdgeStyle'; - delete graph.getStylesheet().getDefaultEdgeStyle().endArrow; - - // Implements the connect preview - graph.connectionHandler.createEdgeState = function(me) { - const edge = graph.createEdge(null, null, null, null, null); - - return new mxCellState( - this.graph.view, - edge, - this.graph.getCellStyle(edge) - ); - }; - - // Uncomment the following if you want the container - // to fit the size of the graph - // graph.setResizeContainer(true); - - // Enables rubberband selection - new mxRubberband(graph); - - // Gets the default parent for inserting new cells. This - // is normally the first child of the root (ie. layer 0). - const parent = graph.getDefaultParent(); - - // Adds cells to the model in a single step - graph.getModel().beginUpdate(); - try { - const v1 = graph.insertVertex(parent, null, '', 40, 40, 40, 30); - v1.setConnectable(false); - const v11 = graph.insertVertex( - v1, - null, - '', - 0.5, - 0, - 10, - 40, - 'portConstraint=northsouth;', - true - ); - v11.geometry.offset = new mxPoint(-5, -5); - const v12 = graph.insertVertex( - v1, - null, - '', - 0, - 0.5, - 10, - 10, - 'portConstraint=west;shape=triangle;direction=west;perimeter=none;' + - 'routingCenterX=-0.5;routingCenterY=0;', - true - ); - v12.geometry.offset = new mxPoint(-10, -5); - const v13 = graph.insertVertex( - v1, - null, - '', - 1, - 0.5, - 10, - 10, - 'portConstraint=east;shape=triangle;direction=east;perimeter=none;' + - 'routingCenterX=0.5;routingCenterY=0;', - true - ); - v13.geometry.offset = new mxPoint(0, -5); - - const v2 = graph.addCell(graph.getModel().cloneCell(v1)); - v2.geometry.x = 200; - v2.geometry.y = 60; - - const v3 = graph.addCell(graph.getModel().cloneCell(v1)); - v3.geometry.x = 40; - v3.geometry.y = 150; - - const v4 = graph.addCell(graph.getModel().cloneCell(v1)); - v4.geometry.x = 200; - v4.geometry.y = 170; - - graph.insertEdge(parent, null, '', v1.getChildAt(2), v2.getChildAt(1)); - graph.insertEdge(parent, null, '', v2.getChildAt(2), v3.getChildAt(1)); - graph.insertEdge(parent, null, '', v3.getChildAt(2), v4.getChildAt(1)); - } finally { - // Updates the display - graph.getModel().endUpdate(); - } - } -} - -export default Orthogonal; diff --git a/src/pages/connections/index.js b/src/pages/connections/index.js deleted file mode 100644 index eb2aec2ab..000000000 --- a/src/pages/connections/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import Preview from '../Previews'; -import Anchors from './Anchors'; -import EdgeTolerance from './EdgeTolerance'; -import FixedPoints from './FixedPoints'; -import HelloPort from './HelloPort'; -import Orthogonal from './Orthogonal'; -import PortRefs from './PortRefs'; -import PageTabs from '../PageTabs'; - -export default function _Connections() { - return ( - - } /> - } /> - } /> - } /> - } /> - } /> - - ); -} diff --git a/src/pages/dnd_copypaste/DragSource.js b/src/pages/dnd_copypaste/DragSource.js deleted file mode 100644 index af80bfc9c..000000000 --- a/src/pages/dnd_copypaste/DragSource.js +++ /dev/null @@ -1,225 +0,0 @@ -/** - * Copyright (c) 2006-2013, JGraph Ltd - * Converted to ES9 syntax/React by David Morrissey 2021 - */ - -import React from 'react'; -import mxEvent from '../../mxgraph/util/event/mxEvent'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxRubberband from '../../mxgraph/handler/mxRubberband'; -import mxCell from '../../mxgraph/view/cell/mxCell'; -import mxGeometry from '../../mxgraph/util/datatypes/mxGeometry'; -import mxUtils from '../../mxgraph/util/mxUtils'; -import mxDragSource from '../../mxgraph/util/drag_pan/mxDragSource'; -import mxGraphHandler from '../../mxgraph/handler/mxGraphHandler'; -import mxGuide from '../../mxgraph/util/mxGuide'; -import mxEdgeHandler from '../../mxgraph/handler/mxEdgeHandler'; - -class DragSource extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph - return ( - <> -

Dragsource

- This example demonstrates using one drag source for multiple graphs and - changing the drag icon. -
{ - this.el = el; - }} - style={{}} - /> - - ); - } - - componentDidMount() { - class MyCustomGuide extends mxGuide { - isEnabledForEvent(evt) { - // Alt disables guides - return !mxEvent.isAltDown(evt); - } - } - - class MyCustomGraphHandler extends mxGraphHandler { - // Enables guides - guidesEnabled = true; - - createGuide() { - return new MyCustomGuide(this.graph, this.getGuideStates()); - } - } - - class MyCustomEdgeHandler extends mxEdgeHandler { - // Enables snapping waypoints to terminals - snapToTerminals = true; - } - - class MyCustomGraph extends mxGraph { - createGraphHandler() { - return new MyCustomGraphHandler(this); - } - - createEdgeHandler(state, edgeStyle) { - return new MyCustomEdgeHandler(state, edgeStyle); - } - } - - const graphs = []; - - // Creates the graph inside the given container - for (let i = 0; i < 2; i++) { - const container = document.createElement('div'); - container.style.overflow = 'hidden'; - container.style.position = 'relative'; - container.style.width = '321px'; - container.style.height = '241px'; - container.style.background = "url('editors/images/grid.gif')"; - container.style.cursor = 'default'; - - this.el.appendChild(container); - - const graph = new MyCustomGraph(container); - graph.gridSize = 30; - - // Uncomment the following if you want the container - // to fit the size of the graph - // graph.setResizeContainer(true); - - // Enables rubberband selection - new mxRubberband(graph); - - // Gets the default parent for inserting new cells. This - // is normally the first child of the root (ie. layer 0). - const parent = graph.getDefaultParent(); - - // Adds cells to the model in a single step - graph.batchUpdate(() => { - const v1 = graph.insertVertex({ - parent, - value: 'Hello,', - position: [20, 20], - size: [80, 30], - }); - const v2 = graph.insertVertex({ - parent, - value: 'World!', - position: [200, 150], - size: [80, 30], - }); - const e1 = graph.insertEdge({ - parent, - source: v1, - target: v2, - }); - }); - - graphs.push(graph); - } - - // Returns the graph under the mouse - const graphF = evt => { - const x = mxEvent.getClientX(evt); - const y = mxEvent.getClientY(evt); - const elt = document.elementFromPoint(x, y); - - for (const graph of graphs) { - if (mxUtils.isAncestorNode(graph.container, elt)) { - return graph; - } - } - - return null; - }; - - // Inserts a cell at the given location - const funct = (graph, evt, target, x, y) => { - const cell = new mxCell('Test', new mxGeometry(0, 0, 120, 40)); - cell.vertex = true; - const cells = graph.importCells([cell], x, y, target); - - if (cells != null && cells.length > 0) { - graph.scrollCellToVisible(cells[0]); - graph.setSelectionCells(cells); - } - }; - - // Creates a DOM node that acts as the drag source - const img = mxUtils.createImage('images/icons48/gear.png'); - img.style.width = '48px'; - img.style.height = '48px'; - this.el.appendChild(img); - - // Creates the element that is being for the actual preview. - const dragElt = document.createElement('div'); - dragElt.style.border = 'dashed black 1px'; - dragElt.style.width = '120px'; - dragElt.style.height = '40px'; - - // Drag source is configured to use dragElt for preview and as drag icon - // if scalePreview (last) argument is true. Dx and dy are null to force - // the use of the defaults. Note that dx and dy are only used for the - // drag icon but not for the preview. - const ds = mxUtils.makeDraggable( - img, - graphF, - funct, - dragElt, - null, - null, - graphs[0].autoscroll, - true - ); - - // Redirects feature to global switch. Note that this feature should only be used - // if the the x and y arguments are used in funct to insert the cell. - ds.isGuidesEnabled = () => { - return graphs[0].graphHandler.guidesEnabled; - }; - - // Restores original drag icon while outside of graph - ds.createDragElement = mxDragSource.prototype.createDragElement; - } - - // NOTE: To enable cross-document DnD (eg. between frames), - // the following methods need to be overridden: - /* mxDragSourceMouseUp = mxDragSource.prototype.mouseUp; -mxDragSource.prototype.mouseUp = function(evt) -{ - let doc = this.element.ownerDocument; - - if (doc != document) - { - let mu = (mxClient.IS_TOUCH) ? 'touchend' : 'mouseup'; - - if (this.mouseUpHandler != null) - { - mxEvent.removeListener(doc, mu, this.mouseUpHandler); - } - } - - mxDragSourceMouseUp.apply(this, arguments); -}; */ - - /* mxDragSourceMouseDown = mxDragSource.prototype.mouseDown; -mxDragSource.prototype.mouseDown = function(evt) -{ - if (this.enabled && !mxEvent.isConsumed(evt)) - { - mxDragSourceMouseDown.apply(this, arguments); - let doc = this.element.ownerDocument; - - if (doc != document) - { - let mu = (mxClient.IS_TOUCH) ? 'touchend' : 'mouseup'; - mxEvent.addListener(doc, mu, this.mouseUpHandler); - } - } -}; */ -} - -export default DragSource; diff --git a/src/pages/dnd_copypaste/Drop.js b/src/pages/dnd_copypaste/Drop.js deleted file mode 100644 index 6de89beca..000000000 --- a/src/pages/dnd_copypaste/Drop.js +++ /dev/null @@ -1,184 +0,0 @@ -/** - * Copyright (c) 2006-2013, JGraph Ltd - * Converted to ES9 syntax/React by David Morrissey 2021 - */ - -import React from 'react'; -import mxEvent from '../../mxgraph/util/event/mxEvent'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxRubberband from '../../mxgraph/handler/mxRubberband'; -import mxUtils from '../../mxgraph/util/mxUtils'; -import mxClient from '../../mxgraph/mxClient'; - -class Drop extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph - return ( - <> -

Drop

- This example demonstrates handling native drag and drop of images - (requires modern browser). -
{ - this.el = el; - }} - style={{ - position: 'relative', - overflow: 'hidden', - height: '441px', - background: `url('editors/images/grid.gif')`, - cursor: 'default', - }} - /> - - ); - } - - componentDidMount() { - // Checks if the browser is supported - const fileSupport = - window.File != null && - window.FileReader != null && - window.FileList != null; - - if (!fileSupport || !mxClient.isBrowserSupported()) { - // Displays an error message if the browser is not supported. - mxUtils.error('Browser is not supported!', 200, false); - } else { - // Disables the built-in context menu - mxEvent.disableContextMenu(this.el); - - // Creates the graph inside the given this.el - const graph = new mxGraph(this.el); - - // Enables rubberband selection - new mxRubberband(graph); - - mxEvent.addListener(this.el, 'dragover', function(evt) { - if (graph.isEnabled()) { - evt.stopPropagation(); - evt.preventDefault(); - } - }); - - mxEvent.addListener(this.el, 'drop', evt => { - if (graph.isEnabled()) { - evt.stopPropagation(); - evt.preventDefault(); - - // Gets drop location point for vertex - const pt = mxUtils.convertPoint( - graph.container, - mxEvent.getClientX(evt), - mxEvent.getClientY(evt) - ); - const tr = graph.view.translate; - const { scale } = graph.view; - const x = pt.x / scale - tr.x; - const y = pt.y / scale - tr.y; - - // Converts local images to data urls - const filesArray = evt.dataTransfer.files; - - for (let i = 0; i < filesArray.length; i++) { - this.handleDrop(graph, filesArray[i], x + i * 10, y + i * 10); - } - } - }); - } - } - - handleDrop(graph, file, x, y) { - // Handles each file as a separate insert for simplicity. - // Use barrier to handle multiple files as a single insert. - - if (file.type.substring(0, 5) === 'image') { - const reader = new FileReader(); - - reader.onload = function(e) { - // Gets size of image for vertex - let data = e.target.result; - - // SVG needs special handling to add viewbox if missing and - // find initial size from SVG attributes (only for IE11) - if (file.type.substring(0, 9) === 'image/svg') { - const comma = data.indexOf(','); - const svgText = atob(data.substring(comma + 1)); - const root = mxUtils.parseXml(svgText); - - // Parses SVG to find width and height - if (root != null) { - const svgs = root.getElementsByTagName('svg'); - - if (svgs.length > 0) { - const svgRoot = svgs[0]; - let w = parseFloat(svgRoot.getAttribute('width')); - let h = parseFloat(svgRoot.getAttribute('height')); - - // Check if viewBox attribute already exists - const vb = svgRoot.getAttribute('viewBox'); - - if (vb == null || vb.length === 0) { - svgRoot.setAttribute('viewBox', `0 0 ${w} ${h}`); - } - // Uses width and height from viewbox for - // missing width and height attributes - else if (Number.isNaN(w) || Number.isNaN(h)) { - const tokens = vb.split(' '); - - if (tokens.length > 3) { - w = parseFloat(tokens[2]); - h = parseFloat(tokens[3]); - } - } - - w = Math.max(1, Math.round(w)); - h = Math.max(1, Math.round(h)); - - data = `data:image/svg+xml,${btoa( - mxUtils.getXml(svgs[0], '\n') - )}`; - graph.insertVertex({ - position: [x, y], - size: [w, h], - style: `shape=image;image=${data};`, - }); - } - } - } else { - const img = new Image(); - - img.onload = () => { - const w = Math.max(1, img.width); - const h = Math.max(1, img.height); - - // Converts format of data url to cell style value for use in vertex - const semi = data.indexOf(';'); - - if (semi > 0) { - data = - data.substring(0, semi) + - data.substring(data.indexOf(',', semi + 1)); - } - - graph.insertVertex({ - position: [x, y], - size: [w, h], - style: `shape=image;image=${data};`, - }); - }; - - img.src = data; - } - }; - - reader.readAsDataURL(file); - } - } -} - -export default Drop; diff --git a/src/pages/dnd_copypaste/index.js b/src/pages/dnd_copypaste/index.js deleted file mode 100644 index ea1efd7b3..000000000 --- a/src/pages/dnd_copypaste/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import Preview from '../Previews'; -import Clipboard from './Clipboard'; -import DragSource from './DragSource'; -import Drop from './Drop'; -import PageTabs from '../PageTabs'; - -export default function _DnDCopyPaste() { - return ( - - } /> - } /> - } /> - - ); -} diff --git a/src/pages/editing/Editing.js b/src/pages/editing/Editing.js deleted file mode 100644 index 9564f43d9..000000000 --- a/src/pages/editing/Editing.js +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) 2006-2013, JGraph Ltd - * Converted to ES9 syntax/React by David Morrissey 2021 - */ - -import React from 'react'; -import mxEvent from '../../mxgraph/util/event/mxEvent'; -import mxGraph from '../../mxgraph/view/graph/mxGraph'; -import mxUtils from '../../mxgraph/util/mxUtils'; -import mxKeyHandler from '../../mxgraph/handler/mxKeyHandler'; - -class Editing extends React.Component { - constructor(props) { - super(props); - } - - render() { - // A container for the graph - return ( - <> -

Editing

- This example demonstrates using the in-place editor trigger to specify - the editing value and write the new value into a specific field of the - user object. Wrapping and DOM nodes as labels are also demonstrated - here. -
-
- Double-click the upper/lower half of the cell to edit different fields - of the user object. -
{ - this.el = el; - }} - style={{ - overflow: 'hidden', - position: 'relative', - height: '241px', - background: "url('editors/images/grid.gif')", - }} - /> - - ); - } - - componentDidMount() { - class MyCustomGraph extends mxGraph { - getLabel(cell) { - // Returns a HTML representation of the cell where the - // upper half is the first value, lower half is second - // value - - const table = document.createElement('table'); - table.style.height = '100%'; - table.style.width = '100%'; - - const body = document.createElement('tbody'); - const tr1 = document.createElement('tr'); - const td1 = document.createElement('td'); - td1.style.textAlign = 'center'; - td1.style.fontSize = '12px'; - td1.style.color = '#774400'; - mxUtils.write(td1, cell.value.first); - - const tr2 = document.createElement('tr'); - const td2 = document.createElement('td'); - td2.style.textAlign = 'center'; - td2.style.fontSize = '12px'; - td2.style.color = '#774400'; - mxUtils.write(td2, cell.value.second); - - tr1.appendChild(td1); - tr2.appendChild(td2); - body.appendChild(tr1); - body.appendChild(tr2); - table.appendChild(body); - - return table; - } - - getEditingValue(cell, evt) { - // Returns the editing value for the given cell and event - evt.fieldname = this.__getFieldnameForEvent(cell, evt); - return cell.value[evt.fieldname] || ''; - } - - __getFieldnameForEvent(cell, evt) { - // Helper method that returns the fieldname to be used for - // a mouse event - if (evt != null) { - // Finds the relative coordinates inside the cell - const point = mxUtils.convertPoint( - this.container, - mxEvent.getClientX(evt), - mxEvent.getClientY(evt) - ); - const state = this.getView().getState(cell); - - if (state != null) { - point.x -= state.x; - point.y -= state.y; - - // Returns second if mouse in second half of cell - if (point.y > state.height / 2) { - return 'second'; - } - } - } - return 'first'; - } - - labelChanged(cell, newValue, trigger) { - // Sets the new value for the given cell and trigger - const name = trigger != null ? trigger.fieldname : null; - - if (name != null) { - // Clones the user object for correct undo and puts - // the new value in the correct field. - const value = mxUtils.clone(cell.value); - value[name] = newValue; - newValue = value; - - super.labelChanged(cell, newValue, trigger); - } - } - } - - // Creates the graph inside the given container - const graph = new MyCustomGraph(this.el); - graph.setHtmlLabels(true); - - // Adds handling of return and escape keystrokes for editing - const keyHandler = new mxKeyHandler(graph); - - // Sample user objects with 2 fields - const value = {}; - value.first = 'First value'; - value.second = 'Second value'; - - // Gets the default parent for inserting new cells. This - // is normally the first child of the root (ie. layer 0). - const parent = graph.getDefaultParent(); - - // Adds cells to the model in a single step - graph.batchUpdate(() => { - const v1 = graph.insertVertex({ - parent, - value, - position: [100, 60], - size: [120, 80], - style: 'overflow=fill;', - }); - }); - } -} - -export default Editing; diff --git a/src/pages/editing/index.js b/src/pages/editing/index.js deleted file mode 100644 index 7426d674b..000000000 --- a/src/pages/editing/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import Preview from '../Previews'; -import Editing from './Editing'; -import PageTabs from '../PageTabs'; - -export default function _Editing() { - return ( - - } /> - - ); -}