feature (share): frontend of the share feature
@ -1,8 +1,8 @@
|
||||
@mixin ripple($color) {
|
||||
@mixin ripple($color, $bg:"#ffffff00") {
|
||||
background-position: center;
|
||||
transition: background 0.4s;
|
||||
&:hover{
|
||||
background: #ffffff00 radial-gradient(circle, transparent 1%, $color 1%) center/15000%;
|
||||
transition: background 0.4s;
|
||||
background: unquote($bg) radial-gradient(circle, transparent 1%, $color 1%) center/15000%;
|
||||
}
|
||||
&:active{
|
||||
background-color: #00000033;
|
||||
|
||||
@ -131,3 +131,4 @@ select:-moz-focusring {
|
||||
scrollbar-shadow-color:#2d2c4d;
|
||||
scrollbar-track-color:rgba(0,0,0,.1);
|
||||
}
|
||||
.pointer{cursor: pointer;}
|
||||
|
||||
61
client/assets/img/arrow_bottom.svg
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
fill="#000000"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
version="1.1"
|
||||
id="svg855"
|
||||
sodipodi:docname="arrow_bottom.svg"
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||
<metadata
|
||||
id="metadata861">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs859" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="940"
|
||||
inkscape:window-height="1027"
|
||||
id="namedview857"
|
||||
showgrid="false"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="11.491525"
|
||||
inkscape:cy="12"
|
||||
inkscape:window-x="964"
|
||||
inkscape:window-y="37"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg855" />
|
||||
<path
|
||||
d="m 7.705,8.045 4.59,4.58 4.59,-4.58 1.41,1.41 -6,6 -6,-6 z"
|
||||
id="path851"
|
||||
style="fill:#000000;fill-opacity:0.53333321"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="M0-.25h24v24H0z"
|
||||
fill="none"
|
||||
id="path853" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
61
client/assets/img/arrow_top.svg
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
fill="#000000"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
version="1.1"
|
||||
id="svg855"
|
||||
sodipodi:docname="arrow_top.svg"
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||
<metadata
|
||||
id="metadata861">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs859" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="940"
|
||||
inkscape:window-height="1027"
|
||||
id="namedview857"
|
||||
showgrid="false"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="11.491525"
|
||||
inkscape:cy="12"
|
||||
inkscape:window-x="964"
|
||||
inkscape:window-y="37"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg855" />
|
||||
<path
|
||||
d="m 16.885,15.455 -4.59,-4.58 -4.59,4.58 -1.41,-1.41 6,-6 6,6 z"
|
||||
id="path851"
|
||||
style="fill:#000000;fill-opacity:0.53333309"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="M0-.25h24v24H0z"
|
||||
fill="none"
|
||||
id="path853" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@ -22,7 +22,7 @@
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11"><metadata
|
||||
id="metadata1544"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs1542" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
@ -32,24 +32,24 @@
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1894"
|
||||
inkscape:window-width="940"
|
||||
inkscape:window-height="1027"
|
||||
id="namedview1540"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.84375"
|
||||
inkscape:cx="211.15042"
|
||||
inkscape:cy="276.97144"
|
||||
inkscape:window-x="10"
|
||||
inkscape:zoom="0.65186406"
|
||||
inkscape:cx="254.37191"
|
||||
inkscape:cy="87.860425"
|
||||
inkscape:window-x="964"
|
||||
inkscape:window-y="37"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g1507" />
|
||||
<g
|
||||
id="g1507">
|
||||
<path
|
||||
d="m 36.241,36.241 c -0.781,0.781 -2.047,0.781 -2.828,0 l -7.425,-7.425 -7.778,7.778 c -0.781,0.781 -2.047,0.781 -2.828,0 -0.781,-0.781 -0.781,-2.047 0,-2.828 l 7.778,-7.778 -7.425,-7.425 c -0.781,-0.781 -0.781,-2.048 0,-2.828 0.781,-0.781 2.047,-0.781 2.828,0 l 7.425,7.425 7.071,-7.071 c 0.781,-0.781 2.047,-0.781 2.828,0 0.781,0.781 0.781,2.047 0,2.828 l -7.071,7.071 7.425,7.425 c 0.781,0.781 0.781,2.047 0,2.828 z"
|
||||
d="m 41.00531,40.844062 c -1.137768,1.137765 -2.982088,1.137765 -4.119861,0 L 26.068628,30.027234 14.737551,41.35831 c -1.137771,1.137771 -2.982093,1.137771 -4.119861,0 -1.1377722,-1.137768 -1.1377722,-2.982088 0,-4.119861 L 21.948766,25.907372 11.131938,15.090551 c -1.1377647,-1.137771 -1.1377647,-2.983553 0,-4.119861 1.137774,-1.1377721 2.982098,-1.1377721 4.119865,0 L 26.068628,21.787512 36.369739,11.486399 c 1.137768,-1.137768 2.982093,-1.137768 4.119862,0 1.137767,1.137769 1.137767,2.982094 0,4.119862 L 30.188489,25.907372 41.00531,36.724197 c 1.137771,1.137767 1.137771,2.982091 0,4.119865 z"
|
||||
id="path1505"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000;fill-opacity:0.53333336"
|
||||
style="fill:#000000;fill-opacity:0.53333285;stroke-width:1.45681119"
|
||||
sodipodi:nodetypes="sscssscscscssscss" />
|
||||
</g>
|
||||
<g
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.7 KiB |
@ -1,43 +1,117 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 482.428 482.429" style="enable-background:new 0 0 482.428 482.429;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M381.163,57.799h-75.094C302.323,25.316,274.686,0,241.214,0c-33.471,0-61.104,25.315-64.85,57.799h-75.098 c-30.39,0-55.111,24.728-55.111,55.117v2.828c0,23.223,14.46,43.1,34.83,51.199v260.369c0,30.39,24.724,55.117,55.112,55.117 h210.236c30.389,0,55.111-24.729,55.111-55.117V166.944c20.369-8.1,34.83-27.977,34.83-51.199v-2.828 C436.274,82.527,411.551,57.799,381.163,57.799z M241.214,26.139c19.037,0,34.927,13.645,38.443,31.66h-76.879 C206.293,39.783,222.184,26.139,241.214,26.139z M375.305,427.312c0,15.978-13,28.979-28.973,28.979H136.096 c-15.973,0-28.973-13.002-28.973-28.979V170.861h268.182V427.312z M410.135,115.744c0,15.978-13,28.979-28.973,28.979H101.266 c-15.973,0-28.973-13.001-28.973-28.979v-2.828c0-15.978,13-28.979,28.973-28.979h279.897c15.973,0,28.973,13.001,28.973,28.979 V115.744z" fill="#6F6F6F"/>
|
||||
<path d="M171.144,422.863c7.218,0,13.069-5.853,13.069-13.068V262.641c0-7.216-5.852-13.07-13.069-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C158.074,417.012,163.926,422.863,171.144,422.863z" fill="#6F6F6F"/>
|
||||
<path d="M241.214,422.863c7.218,0,13.07-5.853,13.07-13.068V262.641c0-7.216-5.854-13.07-13.07-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C228.145,417.012,233.996,422.863,241.214,422.863z" fill="#6F6F6F"/>
|
||||
<path d="M311.284,422.863c7.217,0,13.068-5.853,13.068-13.068V262.641c0-7.216-5.852-13.07-13.068-13.07 c-7.219,0-13.07,5.854-13.07,13.07v147.154C298.213,417.012,304.067,422.863,311.284,422.863z" fill="#6F6F6F"/>
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="512px"
|
||||
height="512px"
|
||||
viewBox="0 0 482.428 482.429"
|
||||
style="enable-background:new 0 0 482.428 482.429;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="delete.svg"
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11"><metadata
|
||||
id="metadata12072"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs12070" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="940"
|
||||
inkscape:window-height="1027"
|
||||
id="namedview12068"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.4609375"
|
||||
inkscape:cx="345.71234"
|
||||
inkscape:cy="256"
|
||||
inkscape:window-x="964"
|
||||
inkscape:window-y="37"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g12033" />
|
||||
<g
|
||||
id="g12035">
|
||||
<g
|
||||
id="g12033">
|
||||
<path
|
||||
style="fill:#6f6f6f;stroke-width:0.94720054"
|
||||
d="m 239.71038,10.858567 c -29.28633,0.148959 -56.22821,23.158167 -61.13883,51.996129 -0.0359,5.52244 -8.11936,1.523852 -11.81411,2.734301 -21.6593,0.35718 -43.3802,-0.676875 -65.00719,0.438452 -25.743961,2.814896 -47.041084,26.38176 -47.173172,52.292131 -1.722118,22.32277 11.67841,44.77809 32.328768,53.53532 1.502767,7.1355 0.21419,16.11228 0.6438,23.95568 0.110145,75.28311 -0.218433,150.57737 0.16095,225.85367 1.489805,25.85192 23.952414,48.2976 49.805724,49.76687 68.99532,0.27998 138.01654,0.22966 207.01317,0.0247 26.01852,-1.27691 48.7205,-23.83443 50.19249,-49.83767 0.36528,-83.15398 0.0497,-166.32508 0.15538,-249.48558 20.84859,-8.52199 34.59567,-30.97499 32.97936,-53.41131 0.0752,-26.071611 -21.32469,-49.900442 -47.22991,-52.699206 -24.66109,-1.093651 -49.41288,-0.08487 -74.1055,-0.4329 -3.94837,0.612911 -2.30787,-5.374859 -3.87914,-7.978923 -7.2617,-27.587042 -34.37102,-47.8548238 -62.93179,-46.751643 z m 1.50404,28.530717 c 15.47006,-0.305619 30.26667,11.084948 34.03447,26.199713 -22.6834,-0.0059 -45.39692,0.01233 -68.06154,-0.0092 3.80702,-15.070204 18.34117,-26.419823 34.02707,-26.190463 z M 108.67851,94.136363 c 89.45257,0.139166 178.92883,-0.277651 268.36669,0.2072 13.74131,1.418578 23.92664,15.176077 22.26662,28.708317 0.0325,14.55054 -14.07514,26.56026 -28.40492,24.87142 -88.5066,-0.14037 -177.03691,0.28006 -265.5288,-0.20906 C 91.12559,146.24223 80.989285,131.70163 83.181794,117.70168 83.970887,104.7413 95.577131,93.920596 108.67851,94.136363 Z M 366.33,176.47066 c -0.14077,81.42848 0.28062,162.8807 -0.20905,244.29447 -1.43862,13.91151 -15.47685,24.10588 -29.15232,22.27588 -66.19678,-0.14162 -132.41743,0.28175 -198.59945,-0.20906 -13.89169,-1.4488 -24.11909,-15.47575 -22.27216,-29.15786 0,-79.06781 0,-158.13561 0,-237.20343 83.41098,0 166.82198,0 250.23298,0 z"
|
||||
id="path12025"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#6f6f6f;stroke-width:0.982081"
|
||||
d="m 171.68644,247.47379 c -9.34676,0.15644 -15.74032,9.88805 -14.08673,18.71133 0.12351,47.62701 -0.24401,95.27903 0.17839,142.89087 1.20764,10.97136 15.91803,16.52794 24.07249,9.08425 8.41759,-6.81887 4.47469,-18.88392 5.34774,-28.08138 -0.12439,-43.37127 0.2452,-86.76784 -0.17839,-130.12381 -1.03795,-7.31439 -7.95054,-12.95705 -15.3335,-12.48126 z"
|
||||
id="path12027"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#6f6f6f;stroke-width:0.982081"
|
||||
d="m 240.50116,247.47379 c -9.34649,0.15616 -15.74067,9.88817 -14.08673,18.71133 0.12352,47.62701 -0.24401,95.27903 0.17839,142.89087 1.80504,17.56489 30.37412,15.34227 29.42023,-2.30176 -0.12333,-48.93642 0.24377,-97.8982 -0.17838,-146.81918 -1.03735,-7.3141 -7.95101,-12.95694 -15.33351,-12.48126 z"
|
||||
id="path12029"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#6f6f6f;stroke-width:0.982081"
|
||||
d="m 309.31588,247.47379 c -9.34711,0.1553 -15.74314,9.88713 -14.08672,18.71133 0.12354,47.62499 -0.24406,95.27581 0.17838,142.88511 1.19856,10.97321 15.91664,16.53606 24.07058,9.09001 8.41933,-6.81787 4.47366,-18.8842 5.34774,-28.08138 -0.12442,-43.37124 0.24524,-86.76804 -0.17839,-130.12381 -1.03674,-7.31321 -7.95012,-12.95658 -15.33159,-12.48126 z"
|
||||
id="path12031"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12037">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12039">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12041">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12043">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12045">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12047">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12049">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12051">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12053">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12055">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12057">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12059">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12061">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12063">
|
||||
</g>
|
||||
<g>
|
||||
<g
|
||||
id="g12065">
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 5.1 KiB |
83
client/assets/img/share.svg
Normal file
@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg2121"
|
||||
sodipodi:docname="share.svg"
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||
<defs
|
||||
id="defs2115" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="349.03513"
|
||||
inkscape:cy="449.48307"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1894"
|
||||
inkscape:window-height="1027"
|
||||
inkscape:window-x="10"
|
||||
inkscape:window-y="37"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata2118">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<circle
|
||||
style="fill:#6f6f6f;fill-opacity:0.06640625;stroke:#6f6f6f;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path6378"
|
||||
cx="171.96956"
|
||||
cy="70.839935"
|
||||
r="28.324646" />
|
||||
<circle
|
||||
style="fill:#6f6f6f;fill-opacity:0.06640625;stroke:#6f6f6f;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path6378-3"
|
||||
cx="171.96956"
|
||||
cy="225.38493"
|
||||
r="28.324646" />
|
||||
<circle
|
||||
style="fill:#6f6f6f;fill-opacity:0.06640625;stroke:#6f6f6f;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path6378-6"
|
||||
cx="38.030434"
|
||||
cy="148.11235"
|
||||
r="28.324646" />
|
||||
<path
|
||||
style="fill:none;stroke:#6f6f6f;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 65.35794,134.60213 149.47465,83.197409"
|
||||
id="path7970"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#6f6f6f;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647"
|
||||
d="m 61.46365,160.30446 86.45329,50.62581"
|
||||
id="path7972"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@ -8,6 +8,7 @@ import img_save from "../assets/img/save.svg";
|
||||
import img_power from "../assets/img/power.svg";
|
||||
import img_edit from "../assets/img/edit.svg";
|
||||
import img_delete from "../assets/img/delete.svg";
|
||||
import img_share from "../assets/img/share.svg";
|
||||
import img_bucket from "../assets/img/bucket.svg";
|
||||
import img_link from "../assets/img/link.svg";
|
||||
import img_loading from "../assets/img/loader.svg";
|
||||
@ -23,6 +24,8 @@ import img_alarm from '../assets/img/alarm.svg';
|
||||
import img_arrow_right from '../assets/img/arrow_right.svg';
|
||||
import img_arrow_right_white from '../assets/img/arrow_right_white.svg';
|
||||
import img_arrow_left_white from '../assets/img/arrow_left_white.svg';
|
||||
import img_arrow_top from '../assets/img/arrow_top.svg';
|
||||
import img_arrow_bottom from '../assets/img/arrow_bottom.svg';
|
||||
import img_more from '../assets/img/more.svg';
|
||||
import img_close from '../assets/img/close.svg';
|
||||
import img_close_dark from '../assets/img/close_dark.svg';
|
||||
@ -59,6 +62,8 @@ export const Icon = (props) => {
|
||||
img = img_edit;
|
||||
}else if(props.name === 'delete'){
|
||||
img = img_delete;
|
||||
}else if(props.name === 'share'){
|
||||
img = img_share;
|
||||
}else if(props.name === 'bucket'){
|
||||
img = img_bucket;
|
||||
}else if(props.name === 'link'){
|
||||
@ -83,6 +88,10 @@ export const Icon = (props) => {
|
||||
img = img_alarm;
|
||||
}else if(props.name === 'todo_white'){
|
||||
img = img_todo_white;
|
||||
}else if(props.name === 'arrow_bottom'){
|
||||
img = img_arrow_bottom;
|
||||
}else if(props.name === 'arrow_top'){
|
||||
img = img_arrow_top;
|
||||
}else if(props.name === 'arrow_right'){
|
||||
img = img_arrow_right;
|
||||
}else if(props.name === 'arrow_right_white'){
|
||||
|
||||
@ -24,5 +24,6 @@ export class NgIf extends React.Component {
|
||||
}
|
||||
|
||||
NgIf.propTypes = {
|
||||
cond: PropTypes.bool.isRequired
|
||||
cond: PropTypes.bool.isRequired,
|
||||
type: PropTypes.string
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
left: 20px;
|
||||
right: 0;
|
||||
font-size: 0.95em;
|
||||
z-index: 10;
|
||||
z-index: 1001;
|
||||
|
||||
.component_notification--container{
|
||||
overflow: hidden;
|
||||
|
||||
@ -10,7 +10,7 @@ export { prepare } from './navigate';
|
||||
export { invalidate, http_get, http_post, http_delete } from './ajax';
|
||||
export { prompt, alert, confirm } from './popup';
|
||||
export { notify } from './notify';
|
||||
export { gid } from './random';
|
||||
export { gid, randomString } from './random';
|
||||
export { leftPad } from './common';
|
||||
export { getMimeType } from './mimetype';
|
||||
export { settings_get, settings_put } from './settings';
|
||||
|
||||
@ -4,3 +4,17 @@ export function gid(prefix){
|
||||
id += parseInt(Math.random()*Math.pow(10,16)).toString(32);
|
||||
return id;
|
||||
}
|
||||
|
||||
export function randomString(size = 16){
|
||||
const alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p",
|
||||
"q","r","s","t","u","v","x","y","z","A","B","C","D","E","F","G",
|
||||
"H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W",
|
||||
"X","Y","Z","0","1","2","3","4","5","6","7","8","9"];
|
||||
const alphabet_size = alphabet.length;
|
||||
|
||||
let str = "";
|
||||
for(let i=0; i<size; i++){
|
||||
str += alphabet[Math.floor(Math.random()*alphabet_size)]
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
export { Files } from './files';
|
||||
export { Session } from './session';
|
||||
export { Share } from './share';
|
||||
|
||||
22
client/model/share.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { http_get, http_post, http_delete } from '../helpers/';
|
||||
|
||||
class ShareModel {
|
||||
constructor(){}
|
||||
|
||||
all(path = "/"){
|
||||
const url = `api/share?path=${path}`;
|
||||
return http_get(url);
|
||||
}
|
||||
|
||||
upsert(obj){
|
||||
const url = `/api/share/${obj.id}`
|
||||
return http_post(url, obj);
|
||||
}
|
||||
|
||||
remove(id){
|
||||
const url = `/api/share/${id}`;
|
||||
return http_delete(url);
|
||||
}
|
||||
}
|
||||
|
||||
export const Share = new ShareModel()
|
||||
164
client/pages/filespage/share.js
Normal file
@ -0,0 +1,164 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { NgIf, Icon } from '../../components/';
|
||||
import { Share } from '../../model/';
|
||||
import { randomString, notify } from '../../helpers/';
|
||||
import './share.scss';
|
||||
|
||||
export class ShareComponent extends React.Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state = {
|
||||
show_advanced: false,
|
||||
role: null,
|
||||
id: randomString(7),
|
||||
existings: [
|
||||
{id: "dflkjse", role: "UPLOADER", path: "./test/test"},
|
||||
{id: "dflkjse", role: "VIEWER", path: "./test/test", password: "xxxx"},
|
||||
{id: "dflkjse", role: "EDITOR", path: "./test/test"},
|
||||
{id: "dflkjse", role: "VIEWER", path: "./test/test", password: "xxxx"},
|
||||
{id: "dflkjse", role: "EDITOR", path: "./test/test"},
|
||||
{id: "dflkjse", role: "UPLOADER", path: "./test/test"},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
updateState(key, value){
|
||||
if(this.state[key] === value){
|
||||
this.setState({[key]: null});
|
||||
}else{
|
||||
this.setState({[key]: value});
|
||||
}
|
||||
|
||||
if(key === "role" && value && window.innerHeight < 500){
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}
|
||||
}
|
||||
|
||||
registerLink(e){
|
||||
e.target.setSelectionRange(0, e.target.value.length);
|
||||
let st = Object.assign({}, this.state);
|
||||
delete st.existings;
|
||||
delete st.show_advanced;
|
||||
this.setState({existing: [st].concat(this.state.existings)});
|
||||
return Share.upsert(st)
|
||||
.catch((err) => {
|
||||
notify.send(err, "error");
|
||||
this.setState({
|
||||
existings: this.state.existings.slice(0, this.state.existings.length)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onLoad(link){
|
||||
let st = Object.assign({}, link);
|
||||
st.show_advanced = false;
|
||||
st.link_id = st.id;
|
||||
st.role = (st.role || "").toLowerCase();
|
||||
this.setState(st);
|
||||
}
|
||||
|
||||
onDelete(link_id){
|
||||
return Share.remove(link_id)
|
||||
.then(() => {
|
||||
console.log("HERE");
|
||||
})
|
||||
.catch((err) => notify.send(err, "error"));
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div className="component_share">
|
||||
<h2>Create a New Link</h2>
|
||||
|
||||
<div className="share--content link-type no-select">
|
||||
<div onClick={this.updateState.bind(this, 'role', 'uploader')} className={this.state.role === "uploader" ? "active" : ""}>
|
||||
Uploader
|
||||
</div>
|
||||
<div onClick={this.updateState.bind(this, 'role', 'viewer')} className={this.state.role === "viewer" ? "active" : ""}>
|
||||
Viewer
|
||||
</div>
|
||||
<div onClick={this.updateState.bind(this, 'role', 'editor')} className={this.state.role === "editor" ? "active" : ""}>
|
||||
Editor
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NgIf cond={this.state.role === null && !!this.state.existings && this.state.existings.length > 0}>
|
||||
<h2>Existing Links</h2>
|
||||
<div className="share--content existing-links" style={{"maxHeight": this.state.existings && this.state.existings.length > 5 ? '90px' : 'inherit'}}>
|
||||
{
|
||||
this.state.existings && this.state.existings.map((link, i) => {
|
||||
return (
|
||||
<div className="link-details" key={i}>
|
||||
<span className="role">{link.role}</span>
|
||||
<span>{link.path}</span>
|
||||
<Icon onClick={this.onDelete.bind(this, link.id)} name="delete"/>
|
||||
<Icon onClick={this.onLoad.bind(this, link)} name="edit"/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</NgIf>
|
||||
|
||||
<NgIf cond={this.state.role !== null}>
|
||||
<h2>Restrictions</h2>
|
||||
<div className="share--content advanced-settings no-select">
|
||||
<SuperCheckbox value={this.state.users} label="Only for users" placeholder="list of users who can access the link" onChange={this.updateState.bind(this, 'users')} inputType="text"/>
|
||||
<SuperCheckbox value={this.state.password} label="Password" placeholder="protect access with a password" onChange={this.updateState.bind(this, 'password')} inputType="password"/>
|
||||
</div>
|
||||
|
||||
<h2 className="no-select pointer" onClick={this.updateState.bind(this, 'show_advanced', !this.state.show_advanced)}>
|
||||
Advanced
|
||||
<NgIf type="inline" cond={!!this.state.show_advanced}><Icon name="arrow_top"/></NgIf>
|
||||
<NgIf type="inline" cond={!this.state.show_advanced}><Icon name="arrow_bottom"/></NgIf>
|
||||
</h2>
|
||||
<div className="share--content advanced-settings no-select">
|
||||
<NgIf cond={this.state.show_advanced === true}>
|
||||
<SuperCheckbox value={this.state.can_manage_own} label="Can Manage Own" onChange={this.updateState.bind(this, 'can_manage_own')}/>
|
||||
<SuperCheckbox value={this.state.can_share} label="Can Share" onChange={this.updateState.bind(this, 'can_share')}/>
|
||||
<SuperCheckbox value={this.state.expiration} label="Expiration" placeholder="The link won't be valid after" onChange={this.updateState.bind(this, 'expiration')} inputType="date"/>
|
||||
<SuperCheckbox value={this.state.url} label="Custom Link url" placeholder="beautiful_url" onChange={this.updateState.bind(this, 'url')} inputType="text"/>
|
||||
</NgIf>
|
||||
</div>
|
||||
|
||||
<div className="shared-link">
|
||||
<input onClick={this.registerLink.bind(this)} type="text" value={window.location.origin+"/s/"+(this.state.url || this.state.id)} onChange={() => {}}/>
|
||||
</div>
|
||||
</NgIf>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SuperCheckbox = (props) => {
|
||||
const onCheckboxTick = (e) => {
|
||||
return props.onChange(e.target.checked ? "" : null);
|
||||
};
|
||||
const onValueChange = (e) => {
|
||||
props.onChange(e.target.value);
|
||||
};
|
||||
|
||||
const _is_expended = function(val){
|
||||
return val === null || val === undefined ? false : true;
|
||||
}(props.value);
|
||||
return (
|
||||
<div className="component_supercheckbox">
|
||||
<label>
|
||||
<input type="checkbox" checked={_is_expended} onChange={onCheckboxTick}/>
|
||||
{props.label}
|
||||
</label>
|
||||
<NgIf cond={_is_expended && props.inputType !== undefined}>
|
||||
<input type={props.inputType} placeholder={props.placeholder} value={props.value || ""} onChange={onValueChange}/>
|
||||
</NgIf>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
SuperCheckbox.PropTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
inputType: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
value: PropTypes.string
|
||||
};
|
||||
98
client/pages/filespage/share.scss
Normal file
@ -0,0 +1,98 @@
|
||||
@import "../../assets/css/mixin.scss";
|
||||
|
||||
.component_share{
|
||||
h2{
|
||||
margin: 0 0 5px 0;
|
||||
font-size: 1.2em;
|
||||
font-weight: 100;
|
||||
.component_icon{
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
.share--content{
|
||||
margin-bottom: 10px;
|
||||
|
||||
&.link-type{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
> div{
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
margin-right: 4px;
|
||||
padding: 15px 0;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
background: var(--bg-color);
|
||||
color: var(--emphasis);
|
||||
&.active{
|
||||
background: var(--primary);
|
||||
}
|
||||
@include ripple(var(--emphasis-primary), var(--primary));
|
||||
}
|
||||
}
|
||||
&.existing-links{
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
.link-details{
|
||||
padding: 3px 5px;
|
||||
border: 2px solid var(--super-light);
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
margin-top: -2px;
|
||||
line-height: 20px;
|
||||
.role{
|
||||
margin-left: -5px;
|
||||
margin-right: 5px;
|
||||
font-size: 0.8em;
|
||||
color: var(--light);
|
||||
display: inline-block;
|
||||
min-width: 75px;
|
||||
}
|
||||
.component_icon{
|
||||
width: 20px;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.advanced-settings{
|
||||
max-height: 115px;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
.shared-link{
|
||||
input{
|
||||
width: 100%;
|
||||
font-size: 0.9em;
|
||||
background: var(--emphasis-primary);
|
||||
color: var(--emphasis);
|
||||
padding: 3px 5px;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.component_supercheckbox{
|
||||
> label{
|
||||
color: var(--emphasis);
|
||||
font-size: 0.95em;
|
||||
font-style: italic;
|
||||
input{
|
||||
margin-right: 5px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
> div > input{
|
||||
width: calc(100% - 10px);
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 5px;
|
||||
border: 1px solid var(--light);
|
||||
border-radius: 2px;
|
||||
font-size: 0.9em;
|
||||
padding: 2px 5px;
|
||||
color: var(--emphasis-secondary);
|
||||
}
|
||||
}
|
||||
@ -5,9 +5,10 @@ import { Link } from 'react-router-dom';
|
||||
import { DragSource, DropTarget } from 'react-dnd';
|
||||
|
||||
import './thing.scss';
|
||||
import { Card, NgIf, Icon, EventEmitter } from '../../components/';
|
||||
import { pathBuilder, prompt, leftPad, getMimeType, debounce, memory } from '../../helpers/';
|
||||
import { Card, NgIf, Icon, EventEmitter, Button } from '../../components/';
|
||||
import { pathBuilder, prompt, alert, leftPad, getMimeType, debounce, memory } from '../../helpers/';
|
||||
import { Files } from '../../model/';
|
||||
import { ShareComponent } from './share';
|
||||
import img_placeholder from '../../assets/img/placeholder.png';
|
||||
|
||||
const fileSource = {
|
||||
@ -181,6 +182,14 @@ export class ExistingThing extends React.Component {
|
||||
onDeleteCancel(){
|
||||
this.setState({delete_request: false});
|
||||
}
|
||||
|
||||
onShareRequest(filename){
|
||||
alert.now(
|
||||
<ShareComponent/>,
|
||||
(ok) => {}
|
||||
);
|
||||
}
|
||||
|
||||
_confirm_delete_text(){
|
||||
return this.props.file.name.length > 16? this.props.file.name.substring(0, 10).toLowerCase() : this.props.file.name;
|
||||
}
|
||||
@ -213,7 +222,7 @@ export class ExistingThing extends React.Component {
|
||||
<Filename filename={this.props.file.name} filesize={this.props.file.size} filetype={this.props.file.type} onRename={this.onRename.bind(this)} is_renaming={this.state.is_renaming} onRenameCancel={this.onRenameRequest.bind(this, false)}/>
|
||||
<Message message={this.state.message} />
|
||||
<DateTime show={this.state.icon !== 'loading'} timestamp={this.props.file.time} />
|
||||
<ActionButton onClickRename={this.onRenameRequest.bind(this)} onClickDelete={this.onDeleteRequest.bind(this)} is_renaming={this.state.is_renaming} can_rename={this.props.metadata.can_rename !== false} can_delete={this.props.metadata.can_delete !== false} />
|
||||
<ActionButton onClickRename={this.onRenameRequest.bind(this)} onClickDelete={this.onDeleteRequest.bind(this)} onClickShare={this.onShareRequest.bind(this)} is_renaming={this.state.is_renaming} can_rename={this.props.metadata.can_rename !== false} can_delete={this.props.metadata.can_delete !== false} />
|
||||
</Card>
|
||||
</Link>
|
||||
</div>
|
||||
@ -291,6 +300,11 @@ const ActionButton = (props) => {
|
||||
props.onClickDelete();
|
||||
};
|
||||
|
||||
const onShare = (e) => {
|
||||
e.preventDefault();
|
||||
props.onClickShare();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="component_action">
|
||||
<NgIf cond={props.can_rename !== false && props.is_renaming === false} type="inline">
|
||||
@ -299,6 +313,9 @@ const ActionButton = (props) => {
|
||||
<NgIf cond={props.can_delete !== false} type="inline">
|
||||
<Icon name="delete" onClick={onDelete} className="component_updater--icon"/>
|
||||
</NgIf>
|
||||
<NgIf cond={props.can_share !== false} type="inline">
|
||||
<Icon name="share" onClick={onShare} className="component_updater--icon"/>
|
||||
</NgIf>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -26,6 +26,11 @@ func Init(a *App) *http.Server {
|
||||
files.HandleFunc("/mkdir", APIHandler(LoggedInOnly(FileMkdir), *a)).Methods("GET")
|
||||
files.HandleFunc("/touch", APIHandler(LoggedInOnly(FileTouch), *a)).Methods("GET")
|
||||
|
||||
share := r.PathPrefix("/api/share").Subrouter()
|
||||
share.HandleFunc("", APIHandler(ShareList, *a)).Methods("GET")
|
||||
share.HandleFunc("/{id}", APIHandler(ShareInsert, *a)).Methods("POST")
|
||||
share.HandleFunc("/{id}", APIHandler(ShareInsert, *a)).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/api/config", CtxInjector(ConfigHandler, *a))
|
||||
|
||||
r.PathPrefix("/assets").Handler(StaticHandler("./data/public/", *a))
|
||||
|
||||