mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-14 18:26:26 +00:00
Compare commits
910 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfd67fb7f1 | ||
|
|
e6bd6a5077 | ||
|
|
350af844ca | ||
|
|
a49d53179a | ||
|
|
e68069fd2f | ||
|
|
a193ba3e6c | ||
|
|
9034b3ab54 | ||
|
|
e86464535d | ||
|
|
e8553caa65 | ||
|
|
044e6b7180 | ||
|
|
0266770657 | ||
|
|
2d6f7c08e8 | ||
|
|
0a27819a7f | ||
|
|
3185c25ee1 | ||
|
|
820802cdc2 | ||
|
|
2c169e6f15 | ||
|
|
49be1320f9 | ||
|
|
6bc4ecce48 | ||
|
|
0290d23832 | ||
|
|
e001c97e01 | ||
|
|
976d1bb4f3 | ||
|
|
dee6495b08 | ||
|
|
4a77f250f1 | ||
|
|
c697f19642 | ||
|
|
f7d1f9e949 | ||
|
|
27b9952f8e | ||
|
|
961dab4a97 | ||
|
|
930b6bc927 | ||
|
|
ed7d8258cf | ||
|
|
762425125f | ||
|
|
03bdf0c653 | ||
|
|
7002026190 | ||
|
|
b35395e6bd | ||
|
|
b74fb76d03 | ||
|
|
72d2df465b | ||
|
|
2592c943f7 | ||
|
|
7b3a3ba200 | ||
|
|
8369832585 | ||
|
|
d94a674343 | ||
|
|
260611ffd6 | ||
|
|
690549b57f | ||
|
|
5d9aeb4c04 | ||
|
|
eddfdea2ca | ||
|
|
a40385f87f | ||
|
|
0123526c98 | ||
|
|
275e3317a3 | ||
|
|
d0b835a825 | ||
|
|
f450260ff8 | ||
|
|
5f7b119e5c | ||
|
|
bb9eb494e9 | ||
|
|
6ea2b5e1d9 | ||
|
|
b21baf1ce5 | ||
|
|
4c3fd461e4 | ||
|
|
8365a60d5d | ||
|
|
804ac1aa96 | ||
|
|
49fd2cfc4d | ||
|
|
2bb2a52f27 | ||
|
|
8d71b28afa | ||
|
|
86eac7054d | ||
|
|
dccb92d72b | ||
|
|
2e628de9c6 | ||
|
|
2d243abc12 | ||
|
|
ae08bf4d7a | ||
|
|
cbff5fb585 | ||
|
|
2650cc2f1c | ||
|
|
ec560ceab1 | ||
|
|
ad17cb8837 | ||
|
|
4f98d9641a | ||
|
|
17535ccd4c | ||
|
|
110c1ea337 | ||
|
|
7e087bfbab | ||
|
|
85e0d4b922 | ||
|
|
265262ccbf | ||
|
|
345008e9b6 | ||
|
|
849aa05d76 | ||
|
|
5ff5ec6a71 | ||
|
|
684c3f64aa | ||
|
|
4ff73ede59 | ||
|
|
7302d83f60 | ||
|
|
f9836fd66e | ||
|
|
c0f05695fe | ||
|
|
e50a1172d6 | ||
|
|
7a4234e73c | ||
|
|
4b9640e5a3 | ||
|
|
4b58054100 | ||
|
|
8951dbf4ed | ||
|
|
5047bc94eb | ||
|
|
75fb29c594 | ||
|
|
bd75f7fd2c | ||
|
|
8b8cb3c9b4 | ||
|
|
b479b21c37 | ||
|
|
525c490704 | ||
|
|
2e3599d005 | ||
|
|
f005bb1e46 | ||
|
|
21f96febdb | ||
|
|
805829be78 | ||
|
|
a7bd3f253f | ||
|
|
81e8a290f0 | ||
|
|
f46a967b6e | ||
|
|
b78d9534aa | ||
|
|
bdefaf7427 | ||
|
|
43550c7dec | ||
|
|
1b07d393f2 | ||
|
|
44fc356775 | ||
|
|
5623c68170 | ||
|
|
d5f82943cf | ||
|
|
96a020341d | ||
|
|
99b5f75763 | ||
|
|
a128ff7cd8 | ||
|
|
59329cc5da | ||
|
|
ecd5daaf55 | ||
|
|
514232d720 | ||
|
|
93abfe3202 | ||
|
|
4d9e0e3bd7 | ||
|
|
75d9bf8e38 | ||
|
|
ccb9752254 | ||
|
|
0f2ca19c6c | ||
|
|
d5a77aade4 | ||
|
|
3c89e6a128 | ||
|
|
a36be79977 | ||
|
|
617f0e0f9c | ||
|
|
0db1a94105 | ||
|
|
f29f97a00f | ||
|
|
032b5a59f2 | ||
|
|
1e5bc395ac | ||
|
|
fef4afe660 | ||
|
|
679ed1eacf | ||
|
|
78a9320017 | ||
|
|
927981bf30 | ||
|
|
70ea2f6c14 | ||
|
|
a6779414ff | ||
|
|
b62fcf523a | ||
|
|
d0bbea5a97 | ||
|
|
212d7027fc | ||
|
|
be1a5b09da | ||
|
|
3de2db4459 | ||
|
|
f2405a5b34 | ||
|
|
94abb8f959 | ||
|
|
2e30db05bc | ||
|
|
c93f3cc5dd | ||
|
|
068f8f2ba9 | ||
|
|
0980c3b012 | ||
|
|
5288d6768f | ||
|
|
34491f4ea4 | ||
|
|
8c73ca8854 | ||
|
|
0786d8eab6 | ||
|
|
209518c815 | ||
|
|
b2753b6457 | ||
|
|
4775a920c0 | ||
|
|
0e94dc8740 | ||
|
|
cf68d202d5 | ||
|
|
d29ba2bf16 | ||
|
|
720686cef5 | ||
|
|
0625c65cf0 | ||
|
|
acaefe22d1 | ||
|
|
df1f083ebf | ||
|
|
5a1dfc2ca9 | ||
|
|
d84894f1bf | ||
|
|
0fdc444c4c | ||
|
|
4d216c6f13 | ||
|
|
1c0af8eede | ||
|
|
f443f9264a | ||
|
|
d9b2981327 | ||
|
|
68e36d2a6d | ||
|
|
68b91bf98c | ||
|
|
b91ddfad05 | ||
|
|
a110cbfb5d | ||
|
|
d69f45b0c9 | ||
|
|
07df26d5c4 | ||
|
|
d24bcb7f86 | ||
|
|
9f383ba491 | ||
|
|
c38a76d587 | ||
|
|
994ca5dd02 | ||
|
|
9281f8f6cb | ||
|
|
324f579474 | ||
|
|
10bd2d4547 | ||
|
|
d1942868e4 | ||
|
|
a409b3e48d | ||
|
|
21004aab6a | ||
|
|
eaf88c6491 | ||
|
|
17dcd1b6f1 | ||
|
|
244a06eea6 | ||
|
|
afb13af7a1 | ||
|
|
ff9b935e98 | ||
|
|
4250d6fe52 | ||
|
|
5bc2094f10 | ||
|
|
552653c0ed | ||
|
|
c9bbc61c95 | ||
|
|
41a04aa3f1 | ||
|
|
cd421c4662 | ||
|
|
063e2e02bd | ||
|
|
0c3019b52e | ||
|
|
2ee2494dc4 | ||
|
|
df6ff1fffe | ||
|
|
0d2e6a6a12 | ||
|
|
79ed55a76f | ||
|
|
cbe58b9437 | ||
|
|
afc729b1c3 | ||
|
|
5d0cb0302e | ||
|
|
29e0d121cd | ||
|
|
48ca13f82c | ||
|
|
278061e4f1 | ||
|
|
186a815821 | ||
|
|
2fea9eb874 | ||
|
|
f8b6453be9 | ||
|
|
be625f8884 | ||
|
|
2271def5d3 | ||
|
|
275a8ea7cc | ||
|
|
dc236f33b1 | ||
|
|
678d739e75 | ||
|
|
8fe05a4c24 | ||
|
|
77adfdb9f0 | ||
|
|
30c94028fb | ||
|
|
b5bf0780fa | ||
|
|
ad838d82ee | ||
|
|
d5ec0c0cdd | ||
|
|
ff29f1e0c6 | ||
|
|
1f58698a04 | ||
|
|
a275f331d0 | ||
|
|
0862c6e059 | ||
|
|
46fddfd26c | ||
|
|
c547ccb4cb | ||
|
|
86e82fb149 | ||
|
|
00a284bfbd | ||
|
|
f6fbec0a2e | ||
|
|
b24c06bee6 | ||
|
|
95e7f4f645 | ||
|
|
4b75d501f5 | ||
|
|
b28d5c8b25 | ||
|
|
86e61661e6 | ||
|
|
cdd5717e63 | ||
|
|
42bd7fb4fb | ||
|
|
66e478a001 | ||
|
|
c8259abcac | ||
|
|
d6d16a63a4 | ||
|
|
df8d1f0714 | ||
|
|
fbbc6411c3 | ||
|
|
bc1e94fcab | ||
|
|
418439e3d5 | ||
|
|
46cbd04ba7 | ||
|
|
0ade6d9ece | ||
|
|
823599192f | ||
|
|
9496ab88f7 | ||
|
|
290e6ab170 | ||
|
|
a9e3572f4f | ||
|
|
92b2b7dde3 | ||
|
|
dad1e40234 | ||
|
|
b75c8b0373 | ||
|
|
fc4c471a87 | ||
|
|
24de71f240 | ||
|
|
805c39e60c | ||
|
|
10e75041e8 | ||
|
|
2364348df4 | ||
|
|
e643443660 | ||
|
|
d72e876b23 | ||
|
|
f3612774ba | ||
|
|
67d8b02c49 | ||
|
|
7abf53009a | ||
|
|
90bb4632b6 | ||
|
|
630da00235 | ||
|
|
245e0dd7ee | ||
|
|
824e288a80 | ||
|
|
99228f2e60 | ||
|
|
0f71139eba | ||
|
|
2bbe7056d1 | ||
|
|
005d8f84fd | ||
|
|
908cd7a890 | ||
|
|
d4865adf6a | ||
|
|
20411a2fd5 | ||
|
|
19f8930f5a | ||
|
|
ffc4ca1dd4 | ||
|
|
0bc9c6fb5a | ||
|
|
983f453afd | ||
|
|
60a0293495 | ||
|
|
4807d22763 | ||
|
|
e8d451bcbb | ||
|
|
4809bb6f06 | ||
|
|
d6d694cf66 | ||
|
|
132e3c3088 | ||
|
|
3c7ff78549 | ||
|
|
adbdc2cb1c | ||
|
|
e52b74bbc9 | ||
|
|
575f1b4b33 | ||
|
|
a528c99900 | ||
|
|
e6047ed383 | ||
|
|
381b7d960a | ||
|
|
045a8bde22 | ||
|
|
e528182c2a | ||
|
|
b86cdb461a | ||
|
|
7265f76770 | ||
|
|
2ed092279d | ||
|
|
7d71819be0 | ||
|
|
e848c74511 | ||
|
|
968ed146b2 | ||
|
|
5e03f12875 | ||
|
|
09f8e5f25a | ||
|
|
89f4ed3006 | ||
|
|
8d14a9557d | ||
|
|
513042d769 | ||
|
|
c28980c2a9 | ||
|
|
1c31ff4e98 | ||
|
|
7c9d3904b3 | ||
|
|
e23707f0a0 | ||
|
|
a952b18b96 | ||
|
|
35cc07bf63 | ||
|
|
118bf18434 | ||
|
|
34da15208b | ||
|
|
e0dc62c00b | ||
|
|
b58df2f172 | ||
|
|
90846fab81 | ||
|
|
efd80d5c0c | ||
|
|
e37e28a22e | ||
|
|
a1e71b318c | ||
|
|
aee6541d45 | ||
|
|
50ad5e3791 | ||
|
|
b34d72f21a | ||
|
|
e5ae420ef6 | ||
|
|
7269750264 | ||
|
|
33fe3ff733 | ||
|
|
aa77180957 | ||
|
|
3d80b6a4cd | ||
|
|
d76f9243a3 | ||
|
|
88f1a0d5cd | ||
|
|
26c435d6da | ||
|
|
e66abdea2d | ||
|
|
c7d2eeb71a | ||
|
|
0e8d681954 | ||
|
|
433d110cf0 | ||
|
|
4a514cd7bd | ||
|
|
417fee16bd | ||
|
|
49a821d9ee | ||
|
|
6a5ce098e0 | ||
|
|
1af73eebea | ||
|
|
b7ba29ac92 | ||
|
|
133c2ec308 | ||
|
|
a70fe1bba8 | ||
|
|
6dcd653eac | ||
|
|
db2f90b1ce | ||
|
|
60e5665133 | ||
|
|
7aa982849f | ||
|
|
d94f7626c2 | ||
|
|
549c289f81 | ||
|
|
b35953d1f9 | ||
|
|
941c4aeb19 | ||
|
|
11f7fcbaef | ||
|
|
6b8488ae0f | ||
|
|
ee5268a07e | ||
|
|
28bc331318 | ||
|
|
098153b6ba | ||
|
|
878f31e91e | ||
|
|
b40d09fa86 | ||
|
|
eb48f48f6b | ||
|
|
7961008500 | ||
|
|
ff87c6b226 | ||
|
|
0c2807a08b | ||
|
|
f0cf369317 | ||
|
|
5e9954c060 | ||
|
|
0c7bdf20af | ||
|
|
9c1179a6f9 | ||
|
|
43cb290c80 | ||
|
|
f8b7b7df9f | ||
|
|
b73f0a8012 | ||
|
|
75b2c7bd2e | ||
|
|
5fd9866eef | ||
|
|
4e7204bdbc | ||
|
|
ff7024e38f | ||
|
|
8c11a0b42d | ||
|
|
2df295dc1d | ||
|
|
b695d27817 | ||
|
|
8e2fd300f6 | ||
|
|
1722e103fc | ||
|
|
71464112ce | ||
|
|
003d8a1b21 | ||
|
|
adf81175f3 | ||
|
|
39274ce483 | ||
|
|
1daf07edeb | ||
|
|
cd2dc471c7 | ||
|
|
6229ca7ac9 | ||
|
|
10043cc755 | ||
|
|
e60a5430b4 | ||
|
|
f7e0cb655f | ||
|
|
04097ecfcd | ||
|
|
2222192bcd | ||
|
|
ae93c38d46 | ||
|
|
9ff9dcbe6d | ||
|
|
94d442af7d | ||
|
|
6a711d6a71 | ||
|
|
9d8e71aeb3 | ||
|
|
cfeeba209e | ||
|
|
455029851a | ||
|
|
d0f7baaad0 | ||
|
|
e3fb236139 | ||
|
|
42296e421a | ||
|
|
c6f4ed7c8f | ||
|
|
e1fb36d64d | ||
|
|
48cf695e11 | ||
|
|
5b0e0c71a0 | ||
|
|
c1a76b6fb4 | ||
|
|
945a6306ec | ||
|
|
313bacf9dc | ||
|
|
9027f48dda | ||
|
|
eea8f7cdf4 | ||
|
|
ffef239aa7 | ||
|
|
50fc15feea | ||
|
|
2d7a37c872 | ||
|
|
db468fc095 | ||
|
|
9a9f0035c2 | ||
|
|
b9270cd040 | ||
|
|
65b1bd18c4 | ||
|
|
c87ecc3d40 | ||
|
|
e39e1648f9 | ||
|
|
091d2618a2 | ||
|
|
d039b17715 | ||
|
|
e350dca72c | ||
|
|
c07e334f03 | ||
|
|
1ccfd8e392 | ||
|
|
d22c40f0a5 | ||
|
|
3d37db6e54 | ||
|
|
22bd92916b | ||
|
|
de303cf072 | ||
|
|
b22aad0678 | ||
|
|
bf02f9b256 | ||
|
|
6440ab423e | ||
|
|
fceab73226 | ||
|
|
fded0ad3e8 | ||
|
|
294eb64686 | ||
|
|
851b28c482 | ||
|
|
63fa2f1149 | ||
|
|
816d8decbd | ||
|
|
85b4eedcd6 | ||
|
|
901f40a669 | ||
|
|
bae5f202a5 | ||
|
|
131f6780c2 | ||
|
|
a0e067cacf | ||
|
|
8cdcf537be | ||
|
|
575f5f160c | ||
|
|
d8fbac584c | ||
|
|
b73e1b04dc | ||
|
|
3dd49c287d | ||
|
|
6ce6a7036b | ||
|
|
c120633f14 | ||
|
|
6e84b24309 | ||
|
|
343e35bb54 | ||
|
|
09cf94d807 | ||
|
|
a5531e20f2 | ||
|
|
0f311120af | ||
|
|
614506cada | ||
|
|
7891d14a0a | ||
|
|
2df12b6891 | ||
|
|
97b42d6be1 | ||
|
|
39baadeb04 | ||
|
|
a6f5452a85 | ||
|
|
29dc3bd550 | ||
|
|
e103605956 | ||
|
|
c510c2e540 | ||
|
|
41d65e4132 | ||
|
|
b6cb532568 | ||
|
|
a034ea3a05 | ||
|
|
adeb45a9ce | ||
|
|
4ada755793 | ||
|
|
c4be052a49 | ||
|
|
54c2d7bac9 | ||
|
|
233ab17992 | ||
|
|
e251ec64dc | ||
|
|
32bd6d76ee | ||
|
|
3fc17634aa | ||
|
|
555d725e7b | ||
|
|
35416796b5 | ||
|
|
6c737fe25f | ||
|
|
1b58e320aa | ||
|
|
658a90bf15 | ||
|
|
d092e75f3c | ||
|
|
162fae19cc | ||
|
|
58f5035ec6 | ||
|
|
c5076e4e95 | ||
|
|
28ef1e625c | ||
|
|
97441ccacb | ||
|
|
6b29aed6c4 | ||
|
|
31eb9caee4 | ||
|
|
64cf34e673 | ||
|
|
8996ebb819 | ||
|
|
a36c62044b | ||
|
|
13418109ea | ||
|
|
fe7c05aaa5 | ||
|
|
116e27e0db | ||
|
|
6b135afe1a | ||
|
|
7a9c4951a2 | ||
|
|
041a51a70f | ||
|
|
7b4ff9906e | ||
|
|
11ab5c7598 | ||
|
|
64d53c611b | ||
|
|
657d69a0fb | ||
|
|
62d39e6715 | ||
|
|
a27d8192ee | ||
|
|
41b69afe03 | ||
|
|
54b5af0741 | ||
|
|
d4060f8a5a | ||
|
|
2935d41aba | ||
|
|
af6cd10e28 | ||
|
|
435c80d870 | ||
|
|
775cec32da | ||
|
|
e8cc7abadc | ||
|
|
c1051afdc0 | ||
|
|
573d3ce11e | ||
|
|
0a89dcc6d8 | ||
|
|
d45033ae8e | ||
|
|
313e8b8c98 | ||
|
|
25685dc8b0 | ||
|
|
d6a78dfe28 | ||
|
|
de45852790 | ||
|
|
c6a505cb44 | ||
|
|
44427a40b7 | ||
|
|
8a2ac08c0a | ||
|
|
7babf66d5f | ||
|
|
50cd0b794b | ||
|
|
720f07f62c | ||
|
|
6daffbcafa | ||
|
|
b76729e836 | ||
|
|
b2294e0fc9 | ||
|
|
3559737e8e | ||
|
|
40b7ce607b | ||
|
|
1de67a00cb | ||
|
|
6b4b44dba8 | ||
|
|
c424cc5d33 | ||
|
|
5b71d010b4 | ||
|
|
b9457d3e33 | ||
|
|
60e267409c | ||
|
|
e6a2521143 | ||
|
|
ae36ed2b46 | ||
|
|
fb2ed81fd3 | ||
|
|
a74651b515 | ||
|
|
6904c192e4 | ||
|
|
6540d2670c | ||
|
|
966ba06bc4 | ||
|
|
3b4921b848 | ||
|
|
b11e10ac07 | ||
|
|
2ec7ba04f5 | ||
|
|
095910d156 | ||
|
|
c39463aea8 | ||
|
|
87e47c7ffb | ||
|
|
359f6734c5 | ||
|
|
1d3e71cf49 | ||
|
|
1ab449cecf | ||
|
|
44dd609134 | ||
|
|
1e8e161a33 | ||
|
|
c56d232e58 | ||
|
|
8315b75587 | ||
|
|
562b0592af | ||
|
|
1fd1bed01a | ||
|
|
4f116cba34 | ||
|
|
9d1d57f183 | ||
|
|
6feeee8933 | ||
|
|
5541c0dc38 | ||
|
|
4a8054faed | ||
|
|
bbced7be25 | ||
|
|
893a92c87b | ||
|
|
4055ce19cd | ||
|
|
bcf27233bc | ||
|
|
45111e1610 | ||
|
|
2af86dfa3e | ||
|
|
1b474e1c28 | ||
|
|
c4370694cc | ||
|
|
91f24d96b9 | ||
|
|
bb0b74e889 | ||
|
|
525ab900bd | ||
|
|
31c04de7b6 | ||
|
|
dea0c4287b | ||
|
|
cec4b3132c | ||
|
|
f3ed22dd51 | ||
|
|
6aa9104076 | ||
|
|
e7fd18967b | ||
|
|
8a5558db55 | ||
|
|
4767f15e9b | ||
|
|
b7ca4668e9 | ||
|
|
70e637fada | ||
|
|
459b0ff030 | ||
|
|
2903788fd4 | ||
|
|
af0fdb9277 | ||
|
|
41a58583dc | ||
|
|
c80a26fe0b | ||
|
|
806c3bbaf9 | ||
|
|
fe1c197138 | ||
|
|
b577ca2bc2 | ||
|
|
70a97a6a2a | ||
|
|
034f46792b | ||
|
|
3dc1b59753 | ||
|
|
98b761f1d1 | ||
|
|
712301436d | ||
|
|
4243afb033 | ||
|
|
0f43485606 | ||
|
|
b91b88f16e | ||
|
|
2f2c500e4a | ||
|
|
7065fad69b | ||
|
|
6fcbca6b10 | ||
|
|
93b15f2a7a | ||
|
|
df9d8ff735 | ||
|
|
fda17e044e | ||
|
|
b4e54fc149 | ||
|
|
48514d1020 | ||
|
|
49a4ec5e16 | ||
|
|
c3e92b3b81 | ||
|
|
e78492983a | ||
|
|
0f3230110c | ||
|
|
65573bd4db | ||
|
|
928df018dc | ||
|
|
b2187b72ab | ||
|
|
aae2bddd32 | ||
|
|
b6eddf0821 | ||
|
|
fac0abaed6 | ||
|
|
a6bd239592 | ||
|
|
7845bbd881 | ||
|
|
68bc440749 | ||
|
|
6dbe3cec69 | ||
|
|
850c339bb3 | ||
|
|
57835d0e32 | ||
|
|
ac2c50c8bc | ||
|
|
7296cbe4ec | ||
|
|
16061a7eba | ||
|
|
566fe92589 | ||
|
|
83cef13f1c | ||
|
|
4bb9533049 | ||
|
|
d07c62e266 | ||
|
|
8beb661af4 | ||
|
|
5a201dd1b9 | ||
|
|
aa0ad3bb70 | ||
|
|
f7fb531902 | ||
|
|
c65db4e2b0 | ||
|
|
b32b38bb0d | ||
|
|
d6171dc502 | ||
|
|
7b5a7aabed | ||
|
|
77eb19af40 | ||
|
|
e68d535fa2 | ||
|
|
6e5f6cc739 | ||
|
|
bbeeeccb31 | ||
|
|
e9525fae22 | ||
|
|
672d409bf2 | ||
|
|
6624178864 | ||
|
|
4a66c6717c | ||
|
|
dd76bc027b | ||
|
|
74ee6ae6ce | ||
|
|
1ae3f295f3 | ||
|
|
3cb2ce41fe | ||
|
|
5534319e93 | ||
|
|
c7373c15a5 | ||
|
|
dbf1d6403b | ||
|
|
743b220953 | ||
|
|
95d74c6f5b | ||
|
|
f04b7db9fc | ||
|
|
900fa023fb | ||
|
|
ad9da44afb | ||
|
|
c827717202 | ||
|
|
7d3caa3c2e | ||
|
|
fde7fbccac | ||
|
|
56f06fa7d5 | ||
|
|
c0fba82e73 | ||
|
|
5438cd14a0 | ||
|
|
0d642b308d | ||
|
|
0b96472f72 | ||
|
|
675d0ed08c | ||
|
|
9c0f5c31c2 | ||
|
|
09ce59fd04 | ||
|
|
98cd83c4e0 | ||
|
|
1aec386656 | ||
|
|
b03cd9cd99 | ||
|
|
27265e210f | ||
|
|
c392c5d178 | ||
|
|
11fe420fac | ||
|
|
9b17a8fb5b | ||
|
|
27f3fd0032 | ||
|
|
1672d9fa5f | ||
|
|
59c9e11879 | ||
|
|
4523743150 | ||
|
|
2fdbe9de96 | ||
|
|
a617976c78 | ||
|
|
7201a98d78 | ||
|
|
472560e2bf | ||
|
|
96753fe0a0 | ||
|
|
83c2fdd161 | ||
|
|
911ce7572f | ||
|
|
0a24d7d4a7 | ||
|
|
c542062d4d | ||
|
|
eb7a195cce | ||
|
|
23a356164e | ||
|
|
de19c51061 | ||
|
|
221b6a2938 | ||
|
|
cda9d53c8e | ||
|
|
f043b0ffb3 | ||
|
|
28e0590327 | ||
|
|
ec6de1b91b | ||
|
|
2b0bdbf1c8 | ||
|
|
f48864a2e7 | ||
|
|
94c6578675 | ||
|
|
2af2399971 | ||
|
|
ebea01cecf | ||
|
|
5d1db1de31 | ||
|
|
6c528625d8 | ||
|
|
7b326b99af | ||
|
|
2a60ba95e0 | ||
|
|
6b98afaa02 | ||
|
|
cdb079dc81 | ||
|
|
2ac0d93caf | ||
|
|
41977e8726 | ||
|
|
b9e6a56a83 | ||
|
|
2468c8311f | ||
|
|
e8e05b20cd | ||
|
|
5bd0a446f1 | ||
|
|
ad4e50d542 | ||
|
|
13131a0d5c | ||
|
|
f007664745 | ||
|
|
87f9589be3 | ||
|
|
e059106a93 | ||
|
|
ada1b4de6b | ||
|
|
96413b9851 | ||
|
|
9699ef6319 | ||
|
|
dd8f4d60f0 | ||
|
|
372f2e7319 | ||
|
|
1957d87dd7 | ||
|
|
a148d17ba1 | ||
|
|
297553c240 | ||
|
|
f0fcaa6be7 | ||
|
|
cfa40f3ec1 | ||
|
|
832c43de88 | ||
|
|
1665e18edb | ||
|
|
fd1717046b | ||
|
|
7fe7c555bc | ||
|
|
411a7a8e80 | ||
|
|
196f5a7bf7 | ||
|
|
3fa326121a | ||
|
|
cce69bea3a | ||
|
|
f70cf7845d | ||
|
|
bd0a326128 | ||
|
|
897d99e043 | ||
|
|
b0f288e103 | ||
|
|
7d26d46c7b | ||
|
|
5c7804fc40 | ||
|
|
836f3af1ab | ||
|
|
67b89d4fe7 | ||
|
|
bc2d9d0fe2 | ||
|
|
79f33b9405 | ||
|
|
ed9ddee5f1 | ||
|
|
0d004b2f0a | ||
|
|
f41ff77d76 | ||
|
|
ae97a76d2e | ||
|
|
3ca18c04c6 | ||
|
|
2b03e6e956 | ||
|
|
010793a478 | ||
|
|
b136512ece | ||
|
|
9179c199fe | ||
|
|
cfa4dfa817 | ||
|
|
6a73a3af97 | ||
|
|
923c24fa6c | ||
|
|
4b1c8a3238 | ||
|
|
76508fbc3b | ||
|
|
2bfda95ed8 | ||
|
|
6e5082a470 | ||
|
|
a6cec44fc4 | ||
|
|
600fab4f23 | ||
|
|
12377b8caf | ||
|
|
250c6e488d | ||
|
|
3a9b57adae | ||
|
|
74415956ac | ||
|
|
33d7ed25a5 | ||
|
|
df2de5c081 | ||
|
|
7f4c58a84a | ||
|
|
a641a7b3e4 | ||
|
|
7437b26e3c | ||
|
|
ee6d41859f | ||
|
|
b368c3b5d8 | ||
|
|
b1ae2b0b6f | ||
|
|
27a6d53c7f | ||
|
|
4caf3a81be | ||
|
|
ab578f768f | ||
|
|
e4212e796a | ||
|
|
95b1ff9b41 | ||
|
|
684d2c411e | ||
|
|
42710cfee5 | ||
|
|
277004fd9b | ||
|
|
14f79c4c21 | ||
|
|
f88cd80dca | ||
|
|
19ada1dbf6 | ||
|
|
7754ab1a2e | ||
|
|
de0a8837eb | ||
|
|
042d059d53 | ||
|
|
cba743c895 | ||
|
|
24ee71ac06 | ||
|
|
0ec4ef3363 | ||
|
|
98120a5e40 | ||
|
|
3cb2a6bf92 | ||
|
|
4b7262cb72 | ||
|
|
eac8b13d7b | ||
|
|
4458c58066 | ||
|
|
245d603ae8 | ||
|
|
e3d959522b | ||
|
|
124544452b | ||
|
|
4534625084 | ||
|
|
3e4342eec4 | ||
|
|
0ed2d26129 | ||
|
|
1b538993db | ||
|
|
d67e4009e7 | ||
|
|
7f066c4443 | ||
|
|
2594ca984a | ||
|
|
0e089fadfb | ||
|
|
e56518e13d | ||
|
|
c9bc0c89ff | ||
|
|
b18b1171e7 | ||
|
|
d65464401c | ||
|
|
e9a9e10c81 | ||
|
|
825cd6a93b | ||
|
|
276471979a | ||
|
|
d6903edac7 | ||
|
|
4e1b4bdd6a | ||
|
|
52f0a5639d | ||
|
|
58181d02b2 | ||
|
|
1fea39f1b7 | ||
|
|
ae97ff0f98 | ||
|
|
b2d7fa9e97 | ||
|
|
93e9235bb2 | ||
|
|
094bce20e2 | ||
|
|
2f9d4c447a | ||
|
|
84eb790d93 | ||
|
|
ebd07694db | ||
|
|
2bbac8a6f4 | ||
|
|
e74f5e835f | ||
|
|
69e753cc71 | ||
|
|
b0978c772e | ||
|
|
fb4dfbadf3 | ||
|
|
d19ff3ff17 | ||
|
|
d0990be856 | ||
|
|
268d66b51d | ||
|
|
c1df311e86 | ||
|
|
cc3bd41df2 | ||
|
|
856979b455 | ||
|
|
47925489fd | ||
|
|
8aefb21123 | ||
|
|
e27751c18c | ||
|
|
a3f3fdcc71 | ||
|
|
a757576920 | ||
|
|
c4eb28d241 | ||
|
|
aba6c2eb4f | ||
|
|
ecb91b3155 | ||
|
|
7374b1cc70 | ||
|
|
e8f2972659 | ||
|
|
5bef19a306 | ||
|
|
54d9e02a42 | ||
|
|
4f479a8baf | ||
|
|
e7e6194cac | ||
|
|
113abbb94d | ||
|
|
d9d0651352 | ||
|
|
e27af9f6c1 | ||
|
|
11d820356d | ||
|
|
0945aab232 | ||
|
|
e4744221ee | ||
|
|
5f71b24f8d | ||
|
|
095a29972a | ||
|
|
76bdb708fa | ||
|
|
7c3c08fd96 | ||
|
|
a4a2e09429 | ||
|
|
4ed0ae5e2d | ||
|
|
4cb5e43357 | ||
|
|
b0fecc6b51 | ||
|
|
dd4236ca89 | ||
|
|
5da908c759 | ||
|
|
fcce1d406d | ||
|
|
5a01f39dc7 | ||
|
|
ea1d76f853 | ||
|
|
2356d8a64f | ||
|
|
969f82b903 | ||
|
|
fb90907abf | ||
|
|
cbd4cd940c | ||
|
|
6b18c6182e | ||
|
|
09164aa0c9 | ||
|
|
b83dadddb7 | ||
|
|
b3263b41ff | ||
|
|
1118149b9e | ||
|
|
b6fc24c6e7 | ||
|
|
441edf4667 | ||
|
|
74068eaa3d | ||
|
|
c11bd9e7eb | ||
|
|
b21a82ea6b | ||
|
|
cf455a13d5 | ||
|
|
c492f3529e | ||
|
|
40d7ba4bcc | ||
|
|
686bc49230 | ||
|
|
6550af698a | ||
|
|
aac5cbf53e | ||
|
|
00636db87c | ||
|
|
6231b8ad57 | ||
|
|
f2a41aa049 | ||
|
|
bb87b80a92 | ||
|
|
ebfbe29217 | ||
|
|
fad837e148 | ||
|
|
7dc84c0d6d | ||
|
|
74807fe251 | ||
|
|
bf9773be20 | ||
|
|
18961ff555 | ||
|
|
1f6e0342d6 | ||
|
|
7d2bc58ba2 | ||
|
|
556b53181f | ||
|
|
ca904d69e5 | ||
|
|
f461d459d2 | ||
|
|
6e535c11fd | ||
|
|
49d3262380 | ||
|
|
1ee3dec0cc | ||
|
|
15637642bb | ||
|
|
3c950c2b9e | ||
|
|
7811039651 | ||
|
|
ec5f7b38d0 | ||
|
|
2bb361dc19 | ||
|
|
3445e484ae | ||
|
|
efd6bf2afe |
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
compiled/
|
||||||
|
dist/
|
||||||
10
.eslintrc
Normal file
10
.eslintrc
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": ["standard", "standard-jsx"],
|
||||||
|
"rules": {
|
||||||
|
"no-useless-escape": 0,
|
||||||
|
"prefer-const": "warn",
|
||||||
|
"no-unused-vars": "warn",
|
||||||
|
"no-undef": "warn",
|
||||||
|
"no-lone-blocks": "warn"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
.travis.yml
Normal file
6
.travis.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- 'stable'
|
||||||
|
- 'lts/*'
|
||||||
|
|
||||||
|
script: npm run lint && npm run test
|
||||||
5
Backers.md
Normal file
5
Backers.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Become a [backer](https://salt.bountysource.com/teams/boostnote) and support Boostnote!
|
||||||
|
You can support Boostnote from $ 5 a month!
|
||||||
|
|
||||||
|
# Backers
|
||||||
|
[Kazu Yokomizo](https://twitter.com/kazup_bot)
|
||||||
1
ISSUE_TEMPLATE.md
Normal file
1
ISSUE_TEMPLATE.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Please paste some **screenshots** with opening the developer tool if you report a bug.
|
||||||
4
LICENSE
4
LICENSE
@@ -1,8 +1,8 @@
|
|||||||
GPL-3.0
|
GPL-3.0
|
||||||
|
|
||||||
Boostnote - the simplest note app
|
Boostnote - an open source note-taking app made for programmers just like you.
|
||||||
|
|
||||||
Copyright (C) 2016 MAISIN&CO.
|
Copyright (C) 2017 Maisin&Co., Inc.
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React, { PropTypes } from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import CodeMirror from 'codemirror'
|
import CodeMirror from 'codemirror'
|
||||||
|
import path from 'path'
|
||||||
|
import copyImage from 'browser/main/lib/dataApi/copyImage'
|
||||||
|
|
||||||
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
|
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
|
||||||
|
|
||||||
@@ -52,21 +54,42 @@ export default class CodeEditor extends React.Component {
|
|||||||
indentUnit: this.props.indentSize,
|
indentUnit: this.props.indentSize,
|
||||||
tabSize: this.props.indentSize,
|
tabSize: this.props.indentSize,
|
||||||
indentWithTabs: this.props.indentType !== 'space',
|
indentWithTabs: this.props.indentType !== 'space',
|
||||||
keyMap: 'sublime',
|
keyMap: this.props.keyMap,
|
||||||
inputStyle: 'textarea',
|
inputStyle: 'textarea',
|
||||||
|
dragDrop: false,
|
||||||
extraKeys: {
|
extraKeys: {
|
||||||
Tab: function (cm) {
|
Tab: function (cm) {
|
||||||
|
const cursor = cm.getCursor()
|
||||||
|
const line = cm.getLine(cursor.line)
|
||||||
if (cm.somethingSelected()) cm.indentSelection('add')
|
if (cm.somethingSelected()) cm.indentSelection('add')
|
||||||
else {
|
else {
|
||||||
if (cm.getOption('indentWithTabs')) {
|
const tabs = cm.getOption('indentWithTabs')
|
||||||
cm.execCommand('insertTab')
|
if (line.trimLeft() === '- ' || line.trimLeft() === '* ' || line.trimLeft() === '+ ') {
|
||||||
|
cm.execCommand('goLineStart')
|
||||||
|
if (tabs) {
|
||||||
|
cm.execCommand('insertTab')
|
||||||
|
} else {
|
||||||
|
cm.execCommand('insertSoftTab')
|
||||||
|
}
|
||||||
|
cm.execCommand('goLineEnd')
|
||||||
} else {
|
} else {
|
||||||
cm.execCommand('insertSoftTab')
|
if (tabs) {
|
||||||
|
cm.execCommand('insertTab')
|
||||||
|
} else {
|
||||||
|
cm.execCommand('insertSoftTab')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Cmd-T': function (cm) {
|
'Cmd-T': function (cm) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
},
|
||||||
|
Enter: 'newlineAndIndentContinueMarkdownList',
|
||||||
|
'Ctrl-C': (cm) => {
|
||||||
|
if (cm.getOption('keyMap').substr(0, 3) === 'vim') {
|
||||||
|
document.execCommand('copy')
|
||||||
|
}
|
||||||
|
return CodeMirror.Pass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -102,6 +125,9 @@ export default class CodeEditor extends React.Component {
|
|||||||
if (prevProps.fontFamily !== this.props.fontFamily) {
|
if (prevProps.fontFamily !== this.props.fontFamily) {
|
||||||
needRefresh = true
|
needRefresh = true
|
||||||
}
|
}
|
||||||
|
if (prevProps.keyMap !== this.props.keyMap) {
|
||||||
|
needRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
if (prevProps.indentSize !== this.props.indentSize) {
|
if (prevProps.indentSize !== this.props.indentSize) {
|
||||||
this.editor.setOption('indentUnit', this.props.indentSize)
|
this.editor.setOption('indentUnit', this.props.indentSize)
|
||||||
@@ -152,6 +178,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.editor.setValue(this.props.value)
|
this.editor.setValue(this.props.value)
|
||||||
this.editor.clearHistory()
|
this.editor.clearHistory()
|
||||||
this.editor.on('change', this.changeHandler)
|
this.editor.on('change', this.changeHandler)
|
||||||
|
this.editor.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue (value) {
|
setValue (value) {
|
||||||
@@ -160,6 +187,23 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.editor.setCursor(cursor)
|
this.editor.setCursor(cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDropImage (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
const imagePath = e.dataTransfer.files[0].path
|
||||||
|
const filename = path.basename(imagePath)
|
||||||
|
|
||||||
|
copyImage(imagePath, this.props.storageKey).then((imagePath) => {
|
||||||
|
const imageMd = `})`
|
||||||
|
this.insertImageMd(imageMd)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
insertImageMd (imageMd) {
|
||||||
|
const textarea = this.editor.getInputField()
|
||||||
|
const cm = this.editor
|
||||||
|
textarea.value = `${textarea.value.substr(0, textarea.selectionStart)}${imageMd}${textarea.value.substr(textarea.selectionEnd)}`
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { className, fontFamily, fontSize } = this.props
|
let { className, fontFamily, fontSize } = this.props
|
||||||
fontFamily = _.isString(fontFamily) && fontFamily.length > 0
|
fontFamily = _.isString(fontFamily) && fontFamily.length > 0
|
||||||
@@ -177,6 +221,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
fontFamily: fontFamily.join(', '),
|
fontFamily: fontFamily.join(', '),
|
||||||
fontSize: fontSize
|
fontSize: fontSize
|
||||||
}}
|
}}
|
||||||
|
onDrop={(e) => this.handleDropImage(e)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -194,6 +239,7 @@ CodeEditor.propTypes = {
|
|||||||
CodeEditor.defaultProps = {
|
CodeEditor.defaultProps = {
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
theme: 'xcode',
|
theme: 'xcode',
|
||||||
|
keyMap: 'sublime',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontFamily: 'Monaco, Consolas',
|
fontFamily: 'Monaco, Consolas',
|
||||||
indentSize: 4,
|
indentSize: 4,
|
||||||
|
|||||||
@@ -3,19 +3,32 @@ import CSSModules from 'browser/lib/CSSModules'
|
|||||||
import styles from './MarkdownEditor.styl'
|
import styles from './MarkdownEditor.styl'
|
||||||
import CodeEditor from 'browser/components/CodeEditor'
|
import CodeEditor from 'browser/components/CodeEditor'
|
||||||
import MarkdownPreview from 'browser/components/MarkdownPreview'
|
import MarkdownPreview from 'browser/components/MarkdownPreview'
|
||||||
|
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||||
|
const _ = require('lodash')
|
||||||
|
|
||||||
class MarkdownEditor extends React.Component {
|
class MarkdownEditor extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
|
// char codes for ctrl + w
|
||||||
|
this.escapeFromEditor = [17, 87]
|
||||||
|
|
||||||
|
// ctrl + shift + ;
|
||||||
|
this.supportMdSelectionBold = [16, 17, 186]
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
status: 'PREVIEW',
|
status: 'PREVIEW',
|
||||||
renderValue: props.value
|
renderValue: props.value,
|
||||||
|
keyPressed: new Set(),
|
||||||
|
isLocked: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.lockEditorCode = () => this.handleLockEditor()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this.value = this.refs.code.value
|
this.value = this.refs.code.value
|
||||||
|
eventEmitter.on('editor:lock', this.lockEditorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate () {
|
||||||
@@ -30,6 +43,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
this.cancelQueue()
|
this.cancelQueue()
|
||||||
|
eventEmitter.off('editor:lock', this.lockEditorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
queueRendering (value) {
|
queueRendering (value) {
|
||||||
@@ -69,11 +83,14 @@ class MarkdownEditor extends React.Component {
|
|||||||
this.refs.code.blur()
|
this.refs.code.blur()
|
||||||
this.refs.preview.focus()
|
this.refs.preview.focus()
|
||||||
}
|
}
|
||||||
|
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlur (e) {
|
handleBlur (e) {
|
||||||
|
if (this.state.isLocked) return
|
||||||
|
this.setState({ keyPressed: new Set() })
|
||||||
let { config } = this.props
|
let { config } = this.props
|
||||||
if (config.editor.switchPreview === 'BLUR') {
|
if (config.editor.switchPreview === 'BLUR') {
|
||||||
let cursorPosition = this.refs.code.editor.getCursor()
|
let cursorPosition = this.refs.code.editor.getCursor()
|
||||||
@@ -83,6 +100,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
this.refs.preview.focus()
|
this.refs.preview.focus()
|
||||||
this.refs.preview.scrollTo(cursorPosition.line)
|
this.refs.preview.scrollTo(cursorPosition.line)
|
||||||
})
|
})
|
||||||
|
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +116,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
}, () => {
|
}, () => {
|
||||||
this.refs.code.focus()
|
this.refs.code.focus()
|
||||||
})
|
})
|
||||||
|
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +153,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
this.refs.code.focus()
|
this.refs.code.focus()
|
||||||
}
|
}
|
||||||
|
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
reload () {
|
reload () {
|
||||||
@@ -142,8 +162,49 @@ class MarkdownEditor extends React.Component {
|
|||||||
this.renderPreview(this.props.value)
|
this.renderPreview(this.props.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleKeyDown (e) {
|
||||||
|
if (this.state.status !== 'CODE') return false
|
||||||
|
const keyPressed = this.state.keyPressed
|
||||||
|
keyPressed.add(e.keyCode)
|
||||||
|
this.setState({ keyPressed })
|
||||||
|
let isNoteHandlerKey = (el) => { return keyPressed.has(el) }
|
||||||
|
if (keyPressed.size === this.escapeFromEditor.length &&
|
||||||
|
!this.state.isLocked && this.state.status === 'CODE' &&
|
||||||
|
this.escapeFromEditor.every(isNoteHandlerKey)) {
|
||||||
|
document.activeElement.blur()
|
||||||
|
}
|
||||||
|
if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) {
|
||||||
|
this.addMdAroundWord('**')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addMdAroundWord (mdElement) {
|
||||||
|
if (this.refs.code.editor.getSelection()) {
|
||||||
|
return this.addMdAroundSelection(mdElement)
|
||||||
|
}
|
||||||
|
const currentCaret = this.refs.code.editor.getCursor()
|
||||||
|
const word = this.refs.code.editor.findWordAt(currentCaret)
|
||||||
|
const cmDoc = this.refs.code.editor.getDoc()
|
||||||
|
cmDoc.replaceRange(mdElement, word.anchor)
|
||||||
|
cmDoc.replaceRange(mdElement, { line: word.head.line, ch: word.head.ch + mdElement.length })
|
||||||
|
}
|
||||||
|
|
||||||
|
addMdAroundSelection (mdElement) {
|
||||||
|
this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyUp (e) {
|
||||||
|
const keyPressed = this.state.keyPressed
|
||||||
|
keyPressed.delete(e.keyCode)
|
||||||
|
this.setState({ keyPressed })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLockEditor () {
|
||||||
|
this.setState({ isLocked: !this.state.isLocked })
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { className, value, config } = this.props
|
let { className, value, config, storageKey } = this.props
|
||||||
|
|
||||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||||
@@ -153,6 +214,11 @@ class MarkdownEditor extends React.Component {
|
|||||||
let previewStyle = {}
|
let previewStyle = {}
|
||||||
if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none'
|
if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none'
|
||||||
|
|
||||||
|
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
|
||||||
|
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
|
||||||
|
const storage = _.find(cachedStorageList, {key: storageKey})
|
||||||
|
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className == null
|
<div className={className == null
|
||||||
? 'MarkdownEditor'
|
? 'MarkdownEditor'
|
||||||
@@ -160,16 +226,23 @@ class MarkdownEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
onContextMenu={(e) => this.handleContextMenu(e)}
|
onContextMenu={(e) => this.handleContextMenu(e)}
|
||||||
tabIndex='-1'
|
tabIndex='-1'
|
||||||
|
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||||
|
onKeyUp={(e) => this.handleKeyUp(e)}
|
||||||
>
|
>
|
||||||
<CodeEditor styleName='codeEditor'
|
<CodeEditor styleName={this.state.status === 'CODE'
|
||||||
|
? 'codeEditor'
|
||||||
|
: 'codeEditor--hide'
|
||||||
|
}
|
||||||
ref='code'
|
ref='code'
|
||||||
mode='GitHub Flavored Markdown'
|
mode='GitHub Flavored Markdown'
|
||||||
value={value}
|
value={value}
|
||||||
theme={config.editor.theme}
|
theme={config.editor.theme}
|
||||||
|
keyMap={config.editor.keyMap}
|
||||||
fontFamily={config.editor.fontFamily}
|
fontFamily={config.editor.fontFamily}
|
||||||
fontSize={editorFontSize}
|
fontSize={editorFontSize}
|
||||||
indentType={config.editor.indentType}
|
indentType={config.editor.indentType}
|
||||||
indentSize={editorIndentSize}
|
indentSize={editorIndentSize}
|
||||||
|
storageKey={storageKey}
|
||||||
onChange={(e) => this.handleChange(e)}
|
onChange={(e) => this.handleChange(e)}
|
||||||
onBlur={(e) => this.handleBlur(e)}
|
onBlur={(e) => this.handleBlur(e)}
|
||||||
/>
|
/>
|
||||||
@@ -179,6 +252,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
style={previewStyle}
|
style={previewStyle}
|
||||||
theme={config.ui.theme}
|
theme={config.ui.theme}
|
||||||
|
keyMap={config.editor.keyMap}
|
||||||
fontSize={config.preview.fontSize}
|
fontSize={config.preview.fontSize}
|
||||||
fontFamily={config.preview.fontFamily}
|
fontFamily={config.preview.fontFamily}
|
||||||
codeBlockTheme={config.preview.codeBlockTheme}
|
codeBlockTheme={config.preview.codeBlockTheme}
|
||||||
@@ -192,6 +266,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
onMouseUp={(e) => this.handlePreviewMouseUp(e)}
|
onMouseUp={(e) => this.handlePreviewMouseUp(e)}
|
||||||
onMouseDown={(e) => this.handlePreviewMouseDown(e)}
|
onMouseDown={(e) => this.handlePreviewMouseDown(e)}
|
||||||
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
|
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
|
||||||
|
storagePath={storage.path}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,8 +4,14 @@
|
|||||||
.codeEditor
|
.codeEditor
|
||||||
absolute top bottom left right
|
absolute top bottom left right
|
||||||
|
|
||||||
|
.hide
|
||||||
|
z-index 0
|
||||||
|
opacity 0
|
||||||
|
pointer-events none
|
||||||
|
|
||||||
.codeEditor--hide
|
.codeEditor--hide
|
||||||
@extend .codeEditor
|
@extend .codeEditor
|
||||||
|
@extend .hide
|
||||||
|
|
||||||
.preview
|
.preview
|
||||||
display block
|
display block
|
||||||
@@ -17,7 +23,5 @@
|
|||||||
|
|
||||||
.preview--hide
|
.preview--hide
|
||||||
@extend .preview
|
@extend .preview
|
||||||
z-index 0
|
@extend .hide
|
||||||
opacity 0
|
|
||||||
pointer-events none
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,26 +5,16 @@ import CodeMirror from 'codemirror'
|
|||||||
import consts from 'browser/lib/consts'
|
import consts from 'browser/lib/consts'
|
||||||
import Raphael from 'raphael'
|
import Raphael from 'raphael'
|
||||||
import flowchart from 'flowchart'
|
import flowchart from 'flowchart'
|
||||||
import SequenceDiagram from 'sequence-diagram'
|
import SequenceDiagram from 'js-sequence-diagrams'
|
||||||
|
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||||
function decodeHTMLEntities (text) {
|
import fs from 'fs'
|
||||||
var entities = [
|
import htmlTextHelper from 'browser/lib/htmlTextHelper'
|
||||||
['apos', '\''],
|
import copy from 'copy-to-clipboard'
|
||||||
['amp', '&'],
|
|
||||||
['lt', '<'],
|
|
||||||
['gt', '>']
|
|
||||||
]
|
|
||||||
|
|
||||||
for (var i = 0, max = entities.length; i < max; ++i) {
|
|
||||||
text = text.replace(new RegExp('&' + entities[i][0] + ';', 'g'), entities[i][1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
const { remote } = require('electron')
|
const { remote } = require('electron')
|
||||||
const { app } = remote
|
const { app } = remote
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const dialog = remote.dialog
|
||||||
|
|
||||||
const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
|
const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
|
||||||
const appPath = 'file://' + (process.env.NODE_ENV === 'production'
|
const appPath = 'file://' + (process.env.NODE_ENV === 'production'
|
||||||
@@ -49,11 +39,51 @@ body {
|
|||||||
}
|
}
|
||||||
code {
|
code {
|
||||||
font-family: ${codeBlockFontFamily.join(', ')};
|
font-family: ${codeBlockFontFamily.join(', ')};
|
||||||
|
background-color: rgba(0,0,0,0.04);
|
||||||
|
color: #CC305F;
|
||||||
}
|
}
|
||||||
.lineNumber {
|
.lineNumber {
|
||||||
${lineNumber && 'display: block !important;'}
|
${lineNumber && 'display: block !important;'}
|
||||||
font-family: ${codeBlockFontFamily.join(', ')};
|
font-family: ${codeBlockFontFamily.join(', ')};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clipboardButton {
|
||||||
|
color: rgba(147,147,149,0.8);;
|
||||||
|
fill: rgba(147,147,149,1);;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 7px;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
outline: none;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clipboardButton:hover {
|
||||||
|
transition: 0.2s;
|
||||||
|
color: #939395;
|
||||||
|
fill: #939395;
|
||||||
|
background-color: rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
padding-bottom: 4px;
|
||||||
|
margin: 1em 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
margin: 1em 0 0.37em;
|
||||||
|
}
|
||||||
|
|
||||||
|
body p {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +106,10 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
this.mouseUpHandler = (e) => this.handleMouseUp(e)
|
this.mouseUpHandler = (e) => this.handleMouseUp(e)
|
||||||
this.anchorClickHandler = (e) => this.handlePreviewAnchorClick(e)
|
this.anchorClickHandler = (e) => this.handlePreviewAnchorClick(e)
|
||||||
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
|
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
|
||||||
|
this.saveAsTextHandler = () => this.handleSaveAsText()
|
||||||
|
this.saveAsMdHandler = () => this.handleSaveAsMd()
|
||||||
|
|
||||||
|
this.linkClickHandler = this.handlelinkClick.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePreviewAnchorClick (e) {
|
handlePreviewAnchorClick (e) {
|
||||||
@@ -120,13 +154,38 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
if (this.props.onMouseUp != null) this.props.onMouseUp(e)
|
if (this.props.onMouseUp != null) this.props.onMouseUp(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSaveAsText () {
|
||||||
|
this.exportAsDocument('txt')
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSaveAsMd () {
|
||||||
|
this.exportAsDocument('md')
|
||||||
|
}
|
||||||
|
|
||||||
|
exportAsDocument (fileType) {
|
||||||
|
const options = {
|
||||||
|
filters: [
|
||||||
|
{ name: 'Documents', extensions: [fileType] }
|
||||||
|
],
|
||||||
|
properties: ['openFile', 'createDirectory']
|
||||||
|
}
|
||||||
|
dialog.showSaveDialog(remote.getCurrentWindow(), options,
|
||||||
|
(filename) => {
|
||||||
|
if (filename) {
|
||||||
|
fs.writeFile(filename, this.props.value, (err) => {
|
||||||
|
if (err) throw err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this.refs.root.setAttribute('sandbox', 'allow-scripts')
|
this.refs.root.setAttribute('sandbox', 'allow-scripts')
|
||||||
this.refs.root.contentWindow.document.body.addEventListener('contextmenu', this.contextMenuHandler)
|
this.refs.root.contentWindow.document.body.addEventListener('contextmenu', this.contextMenuHandler)
|
||||||
|
|
||||||
this.refs.root.contentWindow.document.head.innerHTML = `
|
this.refs.root.contentWindow.document.head.innerHTML = `
|
||||||
<style id='style'></style>
|
<style id='style'></style>
|
||||||
<link rel="stylesheet" href="${appPath}/compiled/katex-style.css">
|
<link rel="stylesheet" href="${appPath}/node_modules/katex/dist/katex.min.css">
|
||||||
<link rel="stylesheet" href="${appPath}/node_modules/codemirror/lib/codemirror.css">
|
<link rel="stylesheet" href="${appPath}/node_modules/codemirror/lib/codemirror.css">
|
||||||
<link rel="stylesheet" id="codeTheme">
|
<link rel="stylesheet" id="codeTheme">
|
||||||
`
|
`
|
||||||
@@ -135,12 +194,20 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
|
|
||||||
this.refs.root.contentWindow.document.addEventListener('mousedown', this.mouseDownHandler)
|
this.refs.root.contentWindow.document.addEventListener('mousedown', this.mouseDownHandler)
|
||||||
this.refs.root.contentWindow.document.addEventListener('mouseup', this.mouseUpHandler)
|
this.refs.root.contentWindow.document.addEventListener('mouseup', this.mouseUpHandler)
|
||||||
|
this.refs.root.contentWindow.document.addEventListener('drop', this.preventImageDroppedHandler)
|
||||||
|
this.refs.root.contentWindow.document.addEventListener('dragover', this.preventImageDroppedHandler)
|
||||||
|
eventEmitter.on('export:save-text', this.saveAsTextHandler)
|
||||||
|
eventEmitter.on('export:save-md', this.saveAsMdHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
this.refs.root.contentWindow.document.body.removeEventListener('contextmenu', this.contextMenuHandler)
|
this.refs.root.contentWindow.document.body.removeEventListener('contextmenu', this.contextMenuHandler)
|
||||||
this.refs.root.contentWindow.document.removeEventListener('mousedown', this.mouseDownHandler)
|
this.refs.root.contentWindow.document.removeEventListener('mousedown', this.mouseDownHandler)
|
||||||
this.refs.root.contentWindow.document.removeEventListener('mouseup', this.mouseUpHandler)
|
this.refs.root.contentWindow.document.removeEventListener('mouseup', this.mouseUpHandler)
|
||||||
|
this.refs.root.contentWindow.document.removeEventListener('drop', this.preventImageDroppedHandler)
|
||||||
|
this.refs.root.contentWindow.document.removeEventListener('dragover', this.preventImageDroppedHandler)
|
||||||
|
eventEmitter.off('export:save-text', this.saveAsTextHandler)
|
||||||
|
eventEmitter.off('export:save-md', this.saveAsMdHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
@@ -170,9 +237,9 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCodeTheme (theme) {
|
setCodeTheme (theme) {
|
||||||
theme = consts.THEMES.some((_theme) => _theme === theme)
|
theme = consts.THEMES.some((_theme) => _theme === theme) && theme !== 'default'
|
||||||
? theme
|
? theme
|
||||||
: 'default'
|
: 'elegant'
|
||||||
this.getWindow().document.getElementById('codeTheme').href = `${appPath}/node_modules/codemirror/theme/${theme}.css`
|
this.getWindow().document.getElementById('codeTheme').href = `${appPath}/node_modules/codemirror/theme/${theme}.css`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,11 +251,26 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
el.removeEventListener('click', this.checkboxClickHandler)
|
el.removeEventListener('click', this.checkboxClickHandler)
|
||||||
})
|
})
|
||||||
|
|
||||||
let { value, theme, indentSize, codeBlockTheme } = this.props
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
||||||
|
el.removeEventListener('click', this.linkClickHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
let { value, theme, indentSize, codeBlockTheme, storagePath } = this.props
|
||||||
|
|
||||||
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
|
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
|
||||||
|
|
||||||
|
const codeBlocks = value.match(/(```)(.|[\n])*?(```)/g)
|
||||||
|
if (codeBlocks !== null) {
|
||||||
|
codeBlocks.forEach((codeBlock) => {
|
||||||
|
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
|
||||||
|
})
|
||||||
|
}
|
||||||
this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value)
|
this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value)
|
||||||
|
|
||||||
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.taskListItem'), (el) => {
|
||||||
|
el.parentNode.parentNode.style.listStyleType = 'none'
|
||||||
|
})
|
||||||
|
|
||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
||||||
el.addEventListener('click', this.anchorClickHandler)
|
el.addEventListener('click', this.anchorClickHandler)
|
||||||
})
|
})
|
||||||
@@ -197,6 +279,15 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
el.addEventListener('click', this.checkboxClickHandler)
|
el.addEventListener('click', this.checkboxClickHandler)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
||||||
|
el.addEventListener('click', this.linkClickHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => {
|
||||||
|
if (!/\/:storage/.test(el.src)) return
|
||||||
|
el.src = `file:///${path.join(storagePath, 'images', path.basename(el.src))}`
|
||||||
|
})
|
||||||
|
|
||||||
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
|
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
|
||||||
? codeBlockTheme
|
? codeBlockTheme
|
||||||
: 'default'
|
: 'default'
|
||||||
@@ -205,7 +296,17 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
let syntax = CodeMirror.findModeByName(el.className)
|
let syntax = CodeMirror.findModeByName(el.className)
|
||||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||||
CodeMirror.requireMode(syntax.mode, () => {
|
CodeMirror.requireMode(syntax.mode, () => {
|
||||||
let content = el.innerHTML
|
let content = htmlTextHelper.decodeEntities(el.innerHTML)
|
||||||
|
const copyIcon = document.createElement('i')
|
||||||
|
copyIcon.innerHTML = '<button class="clipboardButton"><svg width="13" height="13" viewBox="0 0 1792 1792" ><path d="M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"/></svg></button>'
|
||||||
|
copyIcon.onclick = (e) => {
|
||||||
|
copy(content)
|
||||||
|
this.notify('Saved to Clipboard!', {
|
||||||
|
body: 'Paste it wherever you want!',
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
el.parentNode.appendChild(copyIcon)
|
||||||
el.innerHTML = ''
|
el.innerHTML = ''
|
||||||
el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror`
|
el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror`
|
||||||
CodeMirror.runMode(content, syntax.mime, el, {
|
CodeMirror.runMode(content, syntax.mime, el, {
|
||||||
@@ -223,7 +324,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.flowchart'), (el) => {
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.flowchart'), (el) => {
|
||||||
Raphael.setWindow(this.getWindow())
|
Raphael.setWindow(this.getWindow())
|
||||||
try {
|
try {
|
||||||
let diagram = flowchart.parse(decodeHTMLEntities(el.innerHTML))
|
let diagram = flowchart.parse(htmlTextHelper.decodeEntities(el.innerHTML))
|
||||||
el.innerHTML = ''
|
el.innerHTML = ''
|
||||||
diagram.drawSVG(el, opts)
|
diagram.drawSVG(el, opts)
|
||||||
_.forEach(el.querySelectorAll('a'), (el) => {
|
_.forEach(el.querySelectorAll('a'), (el) => {
|
||||||
@@ -239,7 +340,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.sequence'), (el) => {
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.sequence'), (el) => {
|
||||||
Raphael.setWindow(this.getWindow())
|
Raphael.setWindow(this.getWindow())
|
||||||
try {
|
try {
|
||||||
let diagram = SequenceDiagram.parse(decodeHTMLEntities(el.innerHTML))
|
let diagram = SequenceDiagram.parse(htmlTextHelper.decodeEntities(el.innerHTML))
|
||||||
el.innerHTML = ''
|
el.innerHTML = ''
|
||||||
diagram.drawSVG(el, {theme: 'simple'})
|
diagram.drawSVG(el, {theme: 'simple'})
|
||||||
_.forEach(el.querySelectorAll('a'), (el) => {
|
_.forEach(el.querySelectorAll('a'), (el) => {
|
||||||
@@ -275,6 +376,26 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preventImageDroppedHandler (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
notify (title, options) {
|
||||||
|
if (global.process.platform === 'win32') {
|
||||||
|
options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
|
||||||
|
}
|
||||||
|
return new window.Notification(title, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlelinkClick (e) {
|
||||||
|
const noteHash = e.target.href.split('/').pop()
|
||||||
|
const regexIsNoteLink = /^(.{20})-(.{20})$/
|
||||||
|
if (regexIsNoteLink.test(noteHash)) {
|
||||||
|
eventEmitter.emit('list:jump', noteHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { className, style, tabIndex } = this.props
|
let { className, style, tabIndex } = this.props
|
||||||
return (
|
return (
|
||||||
@@ -296,5 +417,6 @@ MarkdownPreview.propTypes = {
|
|||||||
onMouseUp: PropTypes.func,
|
onMouseUp: PropTypes.func,
|
||||||
onMouseDown: PropTypes.func,
|
onMouseDown: PropTypes.func,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
value: PropTypes.string
|
value: PropTypes.string,
|
||||||
|
storagePath: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|||||||
18
browser/components/ModalEscButton.js
Normal file
18
browser/components/ModalEscButton.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './ModalEscButton.styl'
|
||||||
|
|
||||||
|
const ModalEscButton = ({
|
||||||
|
handleEscButtonClick
|
||||||
|
}) => (
|
||||||
|
<button styleName='escButton' onClick={handleEscButtonClick}>
|
||||||
|
<div styleName='esc-mark'>x</div>
|
||||||
|
<div styleName='esc-text'>esc</div>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
|
||||||
|
ModalEscButton.propTypes = {
|
||||||
|
handleEscButtonClick: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(ModalEscButton, styles)
|
||||||
14
browser/components/ModalEscButton.styl
Normal file
14
browser/components/ModalEscButton.styl
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
.escButton
|
||||||
|
height 50px
|
||||||
|
position absolute
|
||||||
|
background-color transparent
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
border none
|
||||||
|
top 1px
|
||||||
|
right 10px
|
||||||
|
text-align center
|
||||||
|
width top-bar--height
|
||||||
|
height top-bar-height
|
||||||
|
|
||||||
|
.esc-mark
|
||||||
|
font-size 15px
|
||||||
100
browser/components/NoteItem.js
Normal file
100
browser/components/NoteItem.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Note item component.
|
||||||
|
*/
|
||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import { isArray } from 'lodash'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './NoteItem.styl'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Tag element component.
|
||||||
|
* @param {string} tagName
|
||||||
|
* @return {React.Component}
|
||||||
|
*/
|
||||||
|
const TagElement = ({ tagName }) => (
|
||||||
|
<span styleName='item-bottom-tagList-item' key={tagName}>
|
||||||
|
#{tagName}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Tag element list component.
|
||||||
|
* @param {Array|null} tags
|
||||||
|
* @return {React.Component}
|
||||||
|
*/
|
||||||
|
const TagElementList = (tags) => {
|
||||||
|
if (!isArray(tags)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagElements = tags.map(tag => (
|
||||||
|
TagElement({tagName: tag})
|
||||||
|
))
|
||||||
|
|
||||||
|
return tagElements
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Note item component when using normal display mode.
|
||||||
|
* @param {boolean} isActive
|
||||||
|
* @param {Object} note
|
||||||
|
* @param {Function} handleNoteClick
|
||||||
|
* @param {Function} handleDragStart
|
||||||
|
* @param {string} dateDisplay
|
||||||
|
*/
|
||||||
|
const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleDragStart }) => (
|
||||||
|
<div styleName={isActive
|
||||||
|
? 'item--active'
|
||||||
|
: 'item'
|
||||||
|
}
|
||||||
|
key={`${note.storage}-${note.key}`}
|
||||||
|
onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)}
|
||||||
|
onDragStart={e => handleDragStart(e, note)}
|
||||||
|
draggable='true'
|
||||||
|
>
|
||||||
|
<div styleName='item-wrapper'>
|
||||||
|
{note.type === 'SNIPPET_NOTE'
|
||||||
|
? <i styleName='item-title-icon' className='fa fa-fw fa-code' />
|
||||||
|
: <i styleName='item-title-icon' className='fa fa-fw fa-file-text-o' />
|
||||||
|
}
|
||||||
|
<div styleName='item-title'>
|
||||||
|
{note.title.trim().length > 0
|
||||||
|
? note.title
|
||||||
|
: <span styleName='item-title-empty'>Empty</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div styleName='item-bottom-time'>{dateDisplay}</div>
|
||||||
|
{note.isStarred
|
||||||
|
? <i styleName='item-star' className='fa fa-star' /> : ''
|
||||||
|
}
|
||||||
|
<div styleName='item-bottom'>
|
||||||
|
<div styleName='item-bottom-tagList'>
|
||||||
|
{note.tags.length > 0
|
||||||
|
? TagElementList(note.tags)
|
||||||
|
: <span styleName='item-bottom-tagList-empty' />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
NoteItem.propTypes = {
|
||||||
|
isActive: PropTypes.bool.isRequired,
|
||||||
|
dateDisplay: PropTypes.string.isRequired,
|
||||||
|
note: PropTypes.shape({
|
||||||
|
storage: PropTypes.string.isRequired,
|
||||||
|
key: PropTypes.string.isRequired,
|
||||||
|
type: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isrequired,
|
||||||
|
tags: PropTypes.array,
|
||||||
|
isStarred: PropTypes.bool.isRequired,
|
||||||
|
isTrashed: PropTypes.bool.isRequired
|
||||||
|
}),
|
||||||
|
handleNoteClick: PropTypes.func.isRequired,
|
||||||
|
handleDragStart: PropTypes.func.isRequired,
|
||||||
|
handleDragEnd: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(NoteItem, styles)
|
||||||
192
browser/components/NoteItem.styl
Normal file
192
browser/components/NoteItem.styl
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
$control-height = 30px
|
||||||
|
|
||||||
|
.root
|
||||||
|
absolute left bottom
|
||||||
|
top $topBar-height - 1
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
|
|
||||||
|
.item
|
||||||
|
position relative
|
||||||
|
padding 0 20px
|
||||||
|
user-select none
|
||||||
|
cursor pointer
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
|
transition background-color 0.2s
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||||
|
.item-title
|
||||||
|
.item-title-icon
|
||||||
|
.item-bottom-time
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-text-color
|
||||||
|
.item-bottom-tagList-item
|
||||||
|
background-color alpha(white, 0.6)
|
||||||
|
color $ui-text-color
|
||||||
|
.item-star
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
&:active
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
color $ui-text-color
|
||||||
|
.item-title
|
||||||
|
.item-title-icon
|
||||||
|
.item-bottom-time
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-text-color
|
||||||
|
.item-bottom-tagList-item
|
||||||
|
background-color alpha(white, 0.6)
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.item-wrapper
|
||||||
|
padding 15px 0
|
||||||
|
border-bottom $ui-border
|
||||||
|
|
||||||
|
.item--active
|
||||||
|
@extend .item
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
color $ui-text-color
|
||||||
|
.item-title
|
||||||
|
.item-title-empty
|
||||||
|
.item-bottom-tagList-empty
|
||||||
|
.item-bottom-time
|
||||||
|
.item-title-icon
|
||||||
|
color $ui-text-color
|
||||||
|
.item-bottom-tagList-item
|
||||||
|
background-color alpha(white, 0.6)
|
||||||
|
color $ui-text-color
|
||||||
|
.item-wrapper
|
||||||
|
border-color transparent
|
||||||
|
.item-star
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
&:hover
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
|
||||||
|
.item-title-icon
|
||||||
|
position relative
|
||||||
|
font-size 12px
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.item-title
|
||||||
|
font-size 14px
|
||||||
|
position relative
|
||||||
|
top -12px
|
||||||
|
left 20px
|
||||||
|
padding-right 15px
|
||||||
|
padding-bottom 4px
|
||||||
|
overflow ellipsis
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.item-title-empty
|
||||||
|
font-weight normal
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.item-bottom
|
||||||
|
position relative
|
||||||
|
bottom 0px
|
||||||
|
margin-top 2px
|
||||||
|
font-size 12px
|
||||||
|
line-height 20px
|
||||||
|
overflow ellipsis
|
||||||
|
display flex
|
||||||
|
|
||||||
|
.item-bottom-tagList
|
||||||
|
flex 1
|
||||||
|
overflow ellipsis
|
||||||
|
line-height 20px
|
||||||
|
padding-top 7px
|
||||||
|
padding-left 2px
|
||||||
|
margin-right 27px
|
||||||
|
|
||||||
|
.item-bottom-tagList-item
|
||||||
|
font-size 11px
|
||||||
|
margin-right 8px
|
||||||
|
padding 0
|
||||||
|
height 20px
|
||||||
|
box-sizing border-box
|
||||||
|
border-radius 2px
|
||||||
|
padding 1px 2px
|
||||||
|
vertical-align middle
|
||||||
|
background-color white
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.item-bottom-time
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
font-size 11px
|
||||||
|
padding-left 2px
|
||||||
|
padding-bottom 2px
|
||||||
|
|
||||||
|
.item-star
|
||||||
|
position absolute
|
||||||
|
right 5px
|
||||||
|
bottom 0px
|
||||||
|
width 34px
|
||||||
|
height 34px
|
||||||
|
color alpha($ui-favorite-star-button-color, 60%)
|
||||||
|
font-size 12px
|
||||||
|
padding 0
|
||||||
|
border-radius 17px
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.root
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
|
||||||
|
.item
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
&:hover
|
||||||
|
transition 0.15s
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
.item-title
|
||||||
|
.item-title-icon
|
||||||
|
.item-bottom-time
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
.item-bottom-tagList-item
|
||||||
|
transition 0.15s
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
|
||||||
|
color $ui-dark-text-color
|
||||||
|
&:active
|
||||||
|
transition 0.15s
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
.item-title
|
||||||
|
.item-title-icon
|
||||||
|
.item-bottom-time
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
.item-bottom-tagList-item
|
||||||
|
transition 0.15s
|
||||||
|
background-color alpha(white, 10%)
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.item-wrapper
|
||||||
|
border-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||||
|
|
||||||
|
.item--active
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
.item-wrapper
|
||||||
|
border-color transparent
|
||||||
|
.item-title
|
||||||
|
.item-title-icon
|
||||||
|
.item-bottom-time
|
||||||
|
color $ui-dark-text-color
|
||||||
|
.item-bottom-tagList-item
|
||||||
|
background-color alpha(white, 10%)
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.item-title
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.item-title-icon
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.item-title-empty
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.item-bottom-tagList-item
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.item-bottom-tagList-empty
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
vertical-align middle
|
||||||
50
browser/components/NoteItemSimple.js
Normal file
50
browser/components/NoteItemSimple.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Note item component with simple display mode.
|
||||||
|
*/
|
||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './NoteItemSimple.styl'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Note item component when using simple display mode.
|
||||||
|
* @param {boolean} isActive
|
||||||
|
* @param {Object} note
|
||||||
|
* @param {Function} handleNoteClick
|
||||||
|
* @param {Function} handleDragStart
|
||||||
|
*/
|
||||||
|
const NoteItemSimple = ({ isActive, note, handleNoteClick, handleDragStart }) => (
|
||||||
|
<div styleName={isActive
|
||||||
|
? 'item-simple--active'
|
||||||
|
: 'item-simple'
|
||||||
|
}
|
||||||
|
key={`${note.storage}-${note.key}`}
|
||||||
|
onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)}
|
||||||
|
onDragStart={e => handleDragStart(e, note)}
|
||||||
|
draggable='true'
|
||||||
|
>
|
||||||
|
<div styleName='item-simple-title'>
|
||||||
|
{note.type === 'SNIPPET_NOTE'
|
||||||
|
? <i styleName='item-simple-title-icon' className='fa fa-fw fa-code' />
|
||||||
|
: <i styleName='item-simple-title-icon' className='fa fa-fw fa-file-text-o' />
|
||||||
|
}
|
||||||
|
{note.title.trim().length > 0
|
||||||
|
? note.title
|
||||||
|
: <span styleName='item-simple-title-empty'>Empty</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
NoteItemSimple.propTypes = {
|
||||||
|
isActive: PropTypes.bool.isRequired,
|
||||||
|
note: PropTypes.shape({
|
||||||
|
storage: PropTypes.string.isRequired,
|
||||||
|
key: PropTypes.string.isRequired,
|
||||||
|
type: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isrequired
|
||||||
|
}),
|
||||||
|
handleNoteClick: PropTypes.func.isRequired,
|
||||||
|
handleDragStart: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(NoteItemSimple, styles)
|
||||||
105
browser/components/NoteItemSimple.styl
Normal file
105
browser/components/NoteItemSimple.styl
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
$control-height = 30px
|
||||||
|
|
||||||
|
.root
|
||||||
|
absolute left bottom
|
||||||
|
top $topBar-height - 1
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
|
|
||||||
|
.item-simple
|
||||||
|
position relative
|
||||||
|
padding 0 20px
|
||||||
|
user-select none
|
||||||
|
cursor pointer
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
|
transition background-color 0.15s
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||||
|
.item-simple-title
|
||||||
|
.item-simple-title-empty
|
||||||
|
.item-simple-title-icon
|
||||||
|
color $ui-text-color
|
||||||
|
&:active
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
color $ui-text-color
|
||||||
|
.item-simple-title
|
||||||
|
.item-simple-title-empty
|
||||||
|
.item-simple-title-icon
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.item-simple--active
|
||||||
|
@extend .item-simple
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
color $ui-text-color
|
||||||
|
.item-simple-title
|
||||||
|
.item-simple-title-empty
|
||||||
|
border-color transparent
|
||||||
|
color $ui-text-color
|
||||||
|
.item-simple-title-icon
|
||||||
|
color $ui-text-color
|
||||||
|
&:hover
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
|
||||||
|
.item-simple-title
|
||||||
|
font-size 13px
|
||||||
|
height 40px
|
||||||
|
box-sizing border-box
|
||||||
|
line-height 24px
|
||||||
|
padding-top 8px
|
||||||
|
overflow ellipsis
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
border-bottom $ui-border
|
||||||
|
|
||||||
|
.item-simple-title-icon
|
||||||
|
font-size 12px
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
padding-right 6px
|
||||||
|
|
||||||
|
.item-simple-title-empty
|
||||||
|
font-weight normal
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.root
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
|
||||||
|
.item-simple
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
&:active
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
.item-simple-title
|
||||||
|
.item-simple-title-icon
|
||||||
|
.item-simple-bottom-time
|
||||||
|
.item-simple-bottom-tagList-item
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
.item-simple-title
|
||||||
|
.item-simple-title-icon
|
||||||
|
.item-simple-bottom-time
|
||||||
|
.item-simple-bottom-tagList-item
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.item-simple--active
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
.item-simple-title
|
||||||
|
.item-simple-title-icon
|
||||||
|
.item-simple-bottom-time
|
||||||
|
color $ui-dark-text-color
|
||||||
|
.item-simple-bottom-tagList-item
|
||||||
|
background-color transparent
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.item-simple-title
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
border-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||||
|
|
||||||
|
.item-simple-title-icon
|
||||||
|
color $ui-darkinactive-text-color
|
||||||
|
|
||||||
|
.item-simple-title-empty
|
||||||
|
color $ui-dark-inactive-text-color
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import React, { PropTypes } from 'react'
|
|
||||||
import md5 from 'md5'
|
|
||||||
|
|
||||||
export default class ProfileImage extends React.Component {
|
|
||||||
render () {
|
|
||||||
let className = this.props.className == null ? 'ProfileImage' : 'ProfileImage ' + this.props.className
|
|
||||||
let email = this.props.email != null ? this.props.email : ''
|
|
||||||
let src = 'http://www.gravatar.com/avatar/' + md5(email.trim().toLowerCase()) + '?s=' + this.props.size
|
|
||||||
|
|
||||||
return (
|
|
||||||
<img
|
|
||||||
className={className}
|
|
||||||
width={this.props.size}
|
|
||||||
height={this.props.size}
|
|
||||||
src={src}/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProfileImage.propTypes = {
|
|
||||||
email: PropTypes.string,
|
|
||||||
size: PropTypes.string,
|
|
||||||
className: PropTypes.string
|
|
||||||
}
|
|
||||||
52
browser/components/SideNavFilter.js
Normal file
52
browser/components/SideNavFilter.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Filter for all notes.
|
||||||
|
*/
|
||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './SideNavFilter.styl'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} isFolded
|
||||||
|
* @param {boolean} isHomeActive
|
||||||
|
* @param {Function} handleAllNotesButtonClick
|
||||||
|
* @param {boolean} isStarredActive
|
||||||
|
* @param {Function} handleStarredButtonClick
|
||||||
|
* @return {React.Component}
|
||||||
|
*/
|
||||||
|
const SideNavFilter = ({
|
||||||
|
isFolded, isHomeActive, handleAllNotesButtonClick,
|
||||||
|
isStarredActive, handleStarredButtonClick, isTrashedActive, handleTrashedButtonClick
|
||||||
|
}) => (
|
||||||
|
<div styleName={isFolded ? 'menu--folded' : 'menu'}>
|
||||||
|
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
|
||||||
|
onClick={handleAllNotesButtonClick}
|
||||||
|
>
|
||||||
|
<i className='fa fa-archive fa-fw' />
|
||||||
|
<span styleName='menu-button-label'>All Notes</span>
|
||||||
|
</button>
|
||||||
|
<button styleName={isStarredActive ? 'menu-button-star--active' : 'menu-button'}
|
||||||
|
onClick={handleStarredButtonClick}
|
||||||
|
>
|
||||||
|
<i className='fa fa-star fa-fw' />
|
||||||
|
<span styleName='menu-button-label'>Starred</span>
|
||||||
|
</button>
|
||||||
|
<button styleName={isTrashedActive ? 'menu-button--active' : 'menu-button'}
|
||||||
|
onClick={handleTrashedButtonClick}
|
||||||
|
>
|
||||||
|
<i className='fa fa-trash fa-fw' />
|
||||||
|
<span styleName='menu-button-label'>Trash</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
SideNavFilter.propTypes = {
|
||||||
|
isFolded: PropTypes.bool,
|
||||||
|
isHomeActive: PropTypes.bool.isRequired,
|
||||||
|
handleAllNotesButtonClick: PropTypes.func.isRequired,
|
||||||
|
isStarredActive: PropTypes.bool.isRequired,
|
||||||
|
isTrashedActive: PropTypes.bool.isRequired,
|
||||||
|
handleStarredButtonClick: PropTypes.func.isRequired,
|
||||||
|
handleTrashdButtonClick: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(SideNavFilter, styles)
|
||||||
105
browser/components/SideNavFilter.styl
Normal file
105
browser/components/SideNavFilter.styl
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
.menu
|
||||||
|
margin-bottom 30px
|
||||||
|
|
||||||
|
.menu-button
|
||||||
|
navButtonColor()
|
||||||
|
height 32px
|
||||||
|
padding 0 15px
|
||||||
|
font-size 12px
|
||||||
|
width 100%
|
||||||
|
text-align left
|
||||||
|
overflow ellipsis
|
||||||
|
|
||||||
|
.menu-button--active
|
||||||
|
@extend .menu-button
|
||||||
|
color #e74c3c
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-text-color
|
||||||
|
&:hover
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
color #e74c3c
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-text-color
|
||||||
|
&:active, &:active:hover
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
color #e74c3c
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.menu-button-star--active
|
||||||
|
@extend .menu-button
|
||||||
|
color #F9BF3B
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-text-color
|
||||||
|
&:hover
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
color #F9BF3B
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-text-color
|
||||||
|
&:active, &:active:hover
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
color #F9BF3B
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.menu-button-label
|
||||||
|
margin-left 5px
|
||||||
|
|
||||||
|
.menu--folded
|
||||||
|
@extend .menu
|
||||||
|
.menu-button, .menu-button--active
|
||||||
|
text-align center
|
||||||
|
&:hover .menu-button-label
|
||||||
|
transition opacity 0.15s
|
||||||
|
opacity 1
|
||||||
|
.menu-button-label
|
||||||
|
position fixed
|
||||||
|
display inline-block
|
||||||
|
height 32px
|
||||||
|
left 44px
|
||||||
|
padding 0 10px
|
||||||
|
margin-top -8px
|
||||||
|
margin-left 0
|
||||||
|
overflow ellipsis
|
||||||
|
background-color $ui-tooltip-backgroundColor
|
||||||
|
z-index 10
|
||||||
|
color white
|
||||||
|
line-height 32px
|
||||||
|
border-top-right-radius 2px
|
||||||
|
border-bottom-right-radius 2px
|
||||||
|
pointer-events none
|
||||||
|
opacity 0
|
||||||
|
font-size 12px
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.menu-button
|
||||||
|
&:active
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
color $ui-dark-text-color
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.menu-button--active
|
||||||
|
color #c0392b
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-dark-text-color
|
||||||
|
&:hover
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
color #c0392b
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.menu-button-star--active
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-dark-text-color
|
||||||
|
&:hover
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
.menu-button-label
|
||||||
|
color $ui-dark-text-color
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React from 'react'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './SnippetTab.styl'
|
import styles from './SnippetTab.styl'
|
||||||
import context from 'browser/lib/context'
|
import context from 'browser/lib/context'
|
||||||
@@ -96,6 +96,7 @@ class SnippetTab extends React.Component {
|
|||||||
{!this.state.isRenaming
|
{!this.state.isRenaming
|
||||||
? <button styleName='button'
|
? <button styleName='button'
|
||||||
onClick={(e) => this.handleClick(e)}
|
onClick={(e) => this.handleClick(e)}
|
||||||
|
onDoubleClick={(e) => this.handleRenameClick(e)}
|
||||||
onContextMenu={(e) => this.handleContextMenu(e)}
|
onContextMenu={(e) => this.handleContextMenu(e)}
|
||||||
>
|
>
|
||||||
{snippet.name.trim().length > 0
|
{snippet.name.trim().length > 0
|
||||||
@@ -117,7 +118,7 @@ class SnippetTab extends React.Component {
|
|||||||
<button styleName='deleteButton'
|
<button styleName='deleteButton'
|
||||||
onClick={(e) => this.handleDeleteButtonClick(e)}
|
onClick={(e) => this.handleDeleteButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-times'/>
|
<i className='fa fa-times' />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -2,9 +2,6 @@
|
|||||||
position relative
|
position relative
|
||||||
flex 1
|
flex 1
|
||||||
overflow hidden
|
overflow hidden
|
||||||
border-right $ui-border
|
|
||||||
&:last-child
|
|
||||||
border-right none
|
|
||||||
&:hover
|
&:hover
|
||||||
.deleteButton
|
.deleteButton
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
@@ -13,11 +10,11 @@
|
|||||||
&:active
|
&:active
|
||||||
color white
|
color white
|
||||||
background-color $ui-active-color
|
background-color $ui-active-color
|
||||||
|
|
||||||
.root--active
|
.root--active
|
||||||
@extend .root
|
@extend .root
|
||||||
min-width 100px
|
min-width 100px
|
||||||
.button
|
border-bottom $ui-border
|
||||||
border-color $brand-color
|
|
||||||
|
|
||||||
.button
|
.button
|
||||||
width 100%
|
width 100%
|
||||||
@@ -31,8 +28,6 @@
|
|||||||
border-left 4px solid transparent
|
border-left 4px solid transparent
|
||||||
&:hover
|
&:hover
|
||||||
background-color $ui-button--hover-backgroundColor
|
background-color $ui-button--hover-backgroundColor
|
||||||
&:active, &:active:hover
|
|
||||||
border-color $brand-color
|
|
||||||
|
|
||||||
.deleteButton
|
.deleteButton
|
||||||
position absolute
|
position absolute
|
||||||
@@ -65,14 +60,12 @@ body[data-theme="dark"]
|
|||||||
&:hover
|
&:hover
|
||||||
background-color darken($ui-dark-button--hover-backgroundColor, 15%)
|
background-color darken($ui-dark-button--hover-backgroundColor, 15%)
|
||||||
&:active
|
&:active
|
||||||
color white
|
color $ui-dark-text-color
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
.root--active
|
.root--active
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
.button
|
|
||||||
border-color $brand-color
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-button--hover-backgroundColor
|
||||||
.deleteButton
|
.deleteButton
|
||||||
@@ -80,7 +73,7 @@ body[data-theme="dark"]
|
|||||||
&:hover
|
&:hover
|
||||||
background-color darken($ui-dark-button--hover-backgroundColor, 15%)
|
background-color darken($ui-dark-button--hover-backgroundColor, 15%)
|
||||||
&:active
|
&:active
|
||||||
color white
|
color $ui-dark-text-color
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
.button
|
.button
|
||||||
@@ -90,13 +83,9 @@ body[data-theme="dark"]
|
|||||||
transition color background-color 0.15s
|
transition color background-color 0.15s
|
||||||
border-left 4px solid transparent
|
border-left 4px solid transparent
|
||||||
&:hover
|
&:hover
|
||||||
color white
|
color $ui-dark-text-color
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-button--hover-backgroundColor
|
||||||
&:active
|
|
||||||
color $ui-dark-button--active-color
|
|
||||||
&:active, &:active:hover
|
|
||||||
color $ui-dark-button--active-color
|
|
||||||
|
|
||||||
.input
|
.input
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-button--hover-backgroundColor
|
||||||
color white
|
color $ui-dark-text-color
|
||||||
66
browser/components/StorageItem.js
Normal file
66
browser/components/StorageItem.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Micro component for showing storage.
|
||||||
|
*/
|
||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import styles from './StorageItem.styl'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import { isNumber } from 'lodash'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} isActive
|
||||||
|
* @param {Function} handleButtonClick
|
||||||
|
* @param {Function} handleContextMenu
|
||||||
|
* @param {string} folderName
|
||||||
|
* @param {string} folderColor
|
||||||
|
* @param {boolean} isFolded
|
||||||
|
* @param {number} noteCount
|
||||||
|
* @param {Function} handleDrop
|
||||||
|
* @param {Function} handleDragEnter
|
||||||
|
* @param {Function} handleDragOut
|
||||||
|
* @return {React.Component}
|
||||||
|
*/
|
||||||
|
const StorageItem = ({
|
||||||
|
isActive, handleButtonClick, handleContextMenu, folderName,
|
||||||
|
folderColor, isFolded, noteCount, handleDrop, handleDragEnter, handleDragLeave
|
||||||
|
}) => (
|
||||||
|
<button styleName={isActive
|
||||||
|
? 'folderList-item--active'
|
||||||
|
: 'folderList-item'
|
||||||
|
}
|
||||||
|
onClick={handleButtonClick}
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
>
|
||||||
|
<span styleName={isFolded
|
||||||
|
? 'folderList-item-name--folded' : 'folderList-item-name'
|
||||||
|
}
|
||||||
|
style={{borderColor: folderColor}}
|
||||||
|
>
|
||||||
|
{isFolded ? folderName.substring(0, 1) : folderName}
|
||||||
|
</span>
|
||||||
|
{(!isFolded && isNumber(noteCount)) &&
|
||||||
|
<span styleName='folderList-item-noteCount'>{noteCount}</span>
|
||||||
|
}
|
||||||
|
{isFolded &&
|
||||||
|
<span styleName='folderList-item-tooltip'>
|
||||||
|
{folderName}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
|
||||||
|
StorageItem.propTypes = {
|
||||||
|
isActive: PropTypes.bool.isRequired,
|
||||||
|
handleButtonClick: PropTypes.func,
|
||||||
|
handleContextMenu: PropTypes.func,
|
||||||
|
folderName: PropTypes.string.isRequired,
|
||||||
|
folderColor: PropTypes.string,
|
||||||
|
isFolded: PropTypes.bool.isRequired,
|
||||||
|
handleDragEnter: PropTypes.func.isRequired,
|
||||||
|
handleDragLeave: PropTypes.func.isRequired,
|
||||||
|
noteCount: PropTypes.number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(StorageItem, styles)
|
||||||
92
browser/components/StorageItem.styl
Normal file
92
browser/components/StorageItem.styl
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
.root
|
||||||
|
width 100%
|
||||||
|
user-select none
|
||||||
|
|
||||||
|
.folderList-item
|
||||||
|
display flex
|
||||||
|
width 100%
|
||||||
|
height 26px
|
||||||
|
background-color transparent
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
padding 0
|
||||||
|
margin-bottom 5px
|
||||||
|
text-align left
|
||||||
|
border none
|
||||||
|
overflow ellipsis
|
||||||
|
font-size 12px
|
||||||
|
&:first-child
|
||||||
|
margin-top 0
|
||||||
|
&:hover
|
||||||
|
color $ui-text-color
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||||
|
transition background-color 0.15s
|
||||||
|
&:active
|
||||||
|
color $ui-text-color
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
|
||||||
|
.folderList-item--active
|
||||||
|
@extend .folderList-item
|
||||||
|
color $ui-text-color
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
&:hover
|
||||||
|
color $ui-text-color
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
|
|
||||||
|
.folderList-item-name
|
||||||
|
display block
|
||||||
|
flex 1
|
||||||
|
padding 0 25px
|
||||||
|
height 26px
|
||||||
|
line-height 26px
|
||||||
|
border-width 0 0 0 2px
|
||||||
|
border-style solid
|
||||||
|
border-color transparent
|
||||||
|
overflow hidden
|
||||||
|
text-overflow ellipsis
|
||||||
|
|
||||||
|
.folderList-item-noteCount
|
||||||
|
float right
|
||||||
|
line-height 26px
|
||||||
|
padding-right 15px
|
||||||
|
font-size 12px
|
||||||
|
|
||||||
|
.folderList-item-tooltip
|
||||||
|
tooltip()
|
||||||
|
position fixed
|
||||||
|
padding 0 10px
|
||||||
|
left 44px
|
||||||
|
z-index 10
|
||||||
|
pointer-events none
|
||||||
|
opacity 0
|
||||||
|
border-top-right-radius 2px
|
||||||
|
border-bottom-right-radius 2px
|
||||||
|
height 26px
|
||||||
|
line-height 26px
|
||||||
|
|
||||||
|
.folderList-item:hover, .folderList-item--active:hover
|
||||||
|
.folderList-item-tooltip
|
||||||
|
opacity 1
|
||||||
|
|
||||||
|
.folderList-item-name--folded
|
||||||
|
@extend .folderList-item-name
|
||||||
|
padding-left 12px
|
||||||
|
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.folderList-item
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
color $ui-dark-text-color
|
||||||
|
&:active
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
|
.folderList-item--active
|
||||||
|
@extend .folderList-item
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
&:active
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
&:hover
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
27
browser/components/TodoListPercentage.js
Normal file
27
browser/components/TodoListPercentage.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Percentage of todo achievement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './TodoListPercentage.styl'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} percentageOfTodo
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TodoListPercentage = ({
|
||||||
|
percentageOfTodo
|
||||||
|
}) => (
|
||||||
|
<div styleName='percentageBar' style={{display: isNaN(percentageOfTodo) ? 'none' : ''}}>
|
||||||
|
<div styleName='progressBar' style={{width: `${percentageOfTodo}%`}}>
|
||||||
|
<p styleName='percentageText'>{percentageOfTodo}%</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
TodoListPercentage.propTypes = {
|
||||||
|
percentageOfTodo: PropTypes.number.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(TodoListPercentage, styles)
|
||||||
31
browser/components/TodoListPercentage.styl
Normal file
31
browser/components/TodoListPercentage.styl
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
.percentageBar
|
||||||
|
position absolute
|
||||||
|
top 58px
|
||||||
|
right: 0px
|
||||||
|
left 0px
|
||||||
|
background-color #DADFE1
|
||||||
|
width 100%
|
||||||
|
height: 15px
|
||||||
|
font-size: 12px
|
||||||
|
z-index 100
|
||||||
|
border-radius 2px
|
||||||
|
|
||||||
|
.progressBar
|
||||||
|
background-color: #6C7A89
|
||||||
|
height 15px
|
||||||
|
border-radius 2px
|
||||||
|
transition 0.3s
|
||||||
|
|
||||||
|
.percentageText
|
||||||
|
color #f4f4f4
|
||||||
|
padding: 2px 43%
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.percentageBar
|
||||||
|
background-color #363A3D
|
||||||
|
|
||||||
|
.progressBar
|
||||||
|
background-color: alpha(#939395, 50%)
|
||||||
|
|
||||||
|
.percentageText
|
||||||
|
color $ui-dark-text-color
|
||||||
@@ -54,7 +54,7 @@ body
|
|||||||
font-family helvetica, arial, sans-serif
|
font-family helvetica, arial, sans-serif
|
||||||
line-height 1.6
|
line-height 1.6
|
||||||
overflow-x hidden
|
overflow-x hidden
|
||||||
user-select all
|
background-color $ui-noteDetail-backgroundColor
|
||||||
.katex
|
.katex
|
||||||
font 400 1.2em 'KaTeX_Main'
|
font 400 1.2em 'KaTeX_Main'
|
||||||
line-height 1.2em
|
line-height 1.2em
|
||||||
@@ -77,7 +77,6 @@ body
|
|||||||
li
|
li
|
||||||
label.taskListItem
|
label.taskListItem
|
||||||
margin-left -2em
|
margin-left -2em
|
||||||
background-color white
|
|
||||||
div.math-rendered
|
div.math-rendered
|
||||||
text-align center
|
text-align center
|
||||||
.math-failed
|
.math-failed
|
||||||
@@ -271,15 +270,16 @@ table
|
|||||||
border-right solid 1px borderColor
|
border-right solid 1px borderColor
|
||||||
|
|
||||||
themeDarkBackground = darken(#21252B, 10%)
|
themeDarkBackground = darken(#21252B, 10%)
|
||||||
themeDarkText = #DDDDDD
|
themeDarkText = #f9f9f9
|
||||||
themeDarkBorder = lighten(themeDarkBackground, 20%)
|
themeDarkBorder = lighten(themeDarkBackground, 20%)
|
||||||
themeDarkPreview = #282C34
|
themeDarkPreview = $ui-dark-noteDetail-backgroundColor
|
||||||
themeDarkTableOdd = themeDarkPreview
|
themeDarkTableOdd = themeDarkPreview
|
||||||
themeDarkTableEven = darken(themeDarkPreview, 10%)
|
themeDarkTableEven = darken(themeDarkPreview, 10%)
|
||||||
themeDarkTableHead = themeDarkTableEven
|
themeDarkTableHead = themeDarkTableEven
|
||||||
themeDarkTableBorder = themeDarkBorder
|
themeDarkTableBorder = themeDarkBorder
|
||||||
themeDarkModalButtonDefault = themeDarkPreview
|
themeDarkModalButtonDefault = themeDarkPreview
|
||||||
themeDarkModalButtonDanger = #BF360C
|
themeDarkModalButtonDanger = #BF360C
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
color themeDarkText
|
color themeDarkText
|
||||||
border-color themeDarkBorder
|
border-color themeDarkBorder
|
||||||
@@ -288,8 +288,9 @@ body[data-theme="dark"]
|
|||||||
background-color alpha(lighten(brandColor, 30%), 0.2) !important
|
background-color alpha(lighten(brandColor, 30%), 0.2) !important
|
||||||
|
|
||||||
code
|
code
|
||||||
|
color #EA6730
|
||||||
border-color darken(themeDarkBorder, 10%)
|
border-color darken(themeDarkBorder, 10%)
|
||||||
background-color lighten(themeDarkPreview, 10%)
|
background-color lighten(themeDarkPreview, 5%)
|
||||||
|
|
||||||
pre
|
pre
|
||||||
border-color lighten(#21252B, 20%)
|
border-color lighten(#21252B, 20%)
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ $list-width = 250px
|
|||||||
outline none
|
outline none
|
||||||
text-align center
|
text-align center
|
||||||
background-color transparent
|
background-color transparent
|
||||||
|
|
||||||
.result
|
.result
|
||||||
absolute left right bottom
|
absolute left right bottom
|
||||||
top $search-height
|
top $search-height
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
.result-nav
|
.result-nav
|
||||||
user-select none
|
user-select none
|
||||||
@@ -32,7 +34,7 @@ $list-width = 250px
|
|||||||
background-color $ui-backgroundColor
|
background-color $ui-backgroundColor
|
||||||
|
|
||||||
.result-nav-filter
|
.result-nav-filter
|
||||||
margin-bottom 5px
|
margin-bottom 10px
|
||||||
|
|
||||||
.result-nav-filter-option
|
.result-nav-filter-option
|
||||||
height 25px
|
height 25px
|
||||||
@@ -62,7 +64,7 @@ $list-width = 250px
|
|||||||
|
|
||||||
.result-nav-storageList
|
.result-nav-storageList
|
||||||
absolute bottom left right
|
absolute bottom left right
|
||||||
top 80px + 32px + 10px
|
top 80px + 32px + 10px + 10px
|
||||||
overflow-y auto
|
overflow-y auto
|
||||||
|
|
||||||
.result-list
|
.result-list
|
||||||
@@ -70,15 +72,15 @@ $list-width = 250px
|
|||||||
absolute top bottom
|
absolute top bottom
|
||||||
left $nav-width
|
left $nav-width
|
||||||
width $list-width
|
width $list-width
|
||||||
border-width 0 1px
|
|
||||||
border-style solid
|
|
||||||
border-color $ui-borderColor
|
|
||||||
box-sizing border-box
|
box-sizing border-box
|
||||||
overflow-y auto
|
overflow-y auto
|
||||||
|
box-shadow 2px 0 15px -8px #b1b1b1
|
||||||
|
z-index 1
|
||||||
|
|
||||||
.result-detail
|
.result-detail
|
||||||
absolute top bottom right
|
absolute top bottom right
|
||||||
left $nav-width + $list-width
|
left $nav-width + $list-width
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
@@ -88,6 +90,9 @@ body[data-theme="dark"]
|
|||||||
.search-input
|
.search-input
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.result
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
|
||||||
.result-nav
|
.result-nav
|
||||||
background-color $ui-dark-backgroundColor
|
background-color $ui-dark-backgroundColor
|
||||||
label
|
label
|
||||||
@@ -104,7 +109,10 @@ body[data-theme="dark"]
|
|||||||
|
|
||||||
.result-list
|
.result-list
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
box-shadow none
|
||||||
|
top 0
|
||||||
|
|
||||||
.result-detail
|
.result-detail
|
||||||
absolute top bottom right
|
absolute top bottom right
|
||||||
left $nav-width + $list-width
|
left $nav-width + $list-width
|
||||||
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React from 'react'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './NoteDetail.styl'
|
import styles from './NoteDetail.styl'
|
||||||
import MarkdownPreview from 'browser/components/MarkdownPreview'
|
import MarkdownPreview from 'browser/components/MarkdownPreview'
|
||||||
@@ -97,9 +97,7 @@ class NoteDetail extends React.Component {
|
|||||||
let { note, config } = this.props
|
let { note, config } = this.props
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
return (
|
return (
|
||||||
<div styleName='root'>
|
<div styleName='root' />
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +106,11 @@ class NoteDetail extends React.Component {
|
|||||||
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
||||||
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
|
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
|
||||||
|
|
||||||
|
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
|
||||||
|
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
|
||||||
|
const storage = _.find(cachedStorageList, {key: note.storage})
|
||||||
|
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
|
||||||
|
|
||||||
if (note.type === 'SNIPPET_NOTE') {
|
if (note.type === 'SNIPPET_NOTE') {
|
||||||
let tabList = note.snippets.map((snippet, index) => {
|
let tabList = note.snippets.map((snippet, index) => {
|
||||||
let isActive = this.state.snippetIndex === index
|
let isActive = this.state.snippetIndex === index
|
||||||
@@ -154,6 +157,7 @@ class NoteDetail extends React.Component {
|
|||||||
fontSize={editorFontSize}
|
fontSize={editorFontSize}
|
||||||
indentType={config.editor.indentType}
|
indentType={config.editor.indentType}
|
||||||
indentSize={editorIndentSize}
|
indentSize={editorIndentSize}
|
||||||
|
keyMap={config.editor.keyMap}
|
||||||
readOnly
|
readOnly
|
||||||
ref={'code-' + index}
|
ref={'code-' + index}
|
||||||
/>
|
/>
|
||||||
@@ -193,6 +197,7 @@ class NoteDetail extends React.Component {
|
|||||||
lineNumber={config.preview.lineNumber}
|
lineNumber={config.preview.lineNumber}
|
||||||
indentSize={editorIndentSize}
|
indentSize={editorIndentSize}
|
||||||
value={note.content}
|
value={note.content}
|
||||||
|
storagePath={storage.path}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
|
@import('../main/Detail/DetailVars.styl')
|
||||||
|
|
||||||
.root
|
.root
|
||||||
absolute top bottom left right
|
absolute top bottom left right
|
||||||
width 100%
|
left $note-detail-left-margin
|
||||||
|
right $note-detail-right-margin
|
||||||
height 100%
|
height 100%
|
||||||
|
width 365px
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
.description
|
.description
|
||||||
absolute top left right
|
absolute top left right
|
||||||
height 80px
|
height 80px
|
||||||
border-bottom $ui-border
|
|
||||||
box-sizing border-box
|
box-sizing border-box
|
||||||
|
|
||||||
.description-textarea
|
.description-textarea
|
||||||
@@ -18,52 +22,43 @@
|
|||||||
padding 10px
|
padding 10px
|
||||||
line-height 1.6
|
line-height 1.6
|
||||||
box-sizing border-box
|
box-sizing border-box
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
absolute left right
|
absolute left right
|
||||||
top 80px
|
top 80px
|
||||||
box-sizing border-box
|
box-sizing border-box
|
||||||
height 30px
|
height 30px
|
||||||
border-bottom $ui-border
|
|
||||||
display flex
|
display flex
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
.tabList-item
|
.tabList-item
|
||||||
position relative
|
position relative
|
||||||
flex 1
|
flex 1
|
||||||
border-right $ui-border
|
overflow hidden
|
||||||
|
&:hover
|
||||||
|
background-color $ui-button--hover-backgroundColorg
|
||||||
|
|
||||||
.tabList-item--active
|
.tabList-item--active
|
||||||
@extend .tabList-item
|
@extend .tabList-item
|
||||||
.tabList-item-button
|
border-bottom $ui-border
|
||||||
border-color $brand-color
|
|
||||||
|
|
||||||
.tabList-item-button
|
.tabList-item-button
|
||||||
width 100%
|
width 100%
|
||||||
height 29px
|
height 29px
|
||||||
navButtonColor()
|
overflow ellipsis
|
||||||
outline none
|
text-align left
|
||||||
border-left 4px solid transparent
|
padding-right 30px
|
||||||
|
padding-left 10px
|
||||||
.tabList-item-deleteButton
|
|
||||||
position absolute
|
|
||||||
top 5px
|
|
||||||
height 20px
|
|
||||||
right 5px
|
|
||||||
width 20px
|
|
||||||
text-align center
|
|
||||||
border none
|
border none
|
||||||
padding 0
|
|
||||||
color transparent
|
|
||||||
background-color transparent
|
background-color transparent
|
||||||
border-radius 2px
|
transition 0.15s
|
||||||
.tabList-plusButton
|
&:hover
|
||||||
navButtonColor()
|
background-color $ui-button--hover-backgroundColor
|
||||||
width 30px
|
|
||||||
|
|
||||||
.tabView
|
.tabView
|
||||||
absolute left right bottom
|
absolute left right bottom
|
||||||
top 110px
|
top 130px
|
||||||
|
|
||||||
.tabView-content
|
.tabView-content
|
||||||
absolute top left right bottom
|
absolute top left right bottom
|
||||||
@@ -72,38 +67,31 @@
|
|||||||
width 100%
|
width 100%
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
|
.root
|
||||||
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
|
||||||
.description
|
.description
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
|
||||||
.description-textarea
|
.description-textarea
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
color white
|
color white
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
background-color $ui-button--active-backgroundColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
border-bottom-color $ui-dark-borderColor
|
|
||||||
background-color $ui-dark-backgroundColor
|
|
||||||
.tabList-item
|
.tabList-item
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
&:hover
|
&:hover
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-button--hover-backgroundColor
|
||||||
.tabList-item--active
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
.tabList-item-button
|
|
||||||
border-color $brand-color
|
|
||||||
.tabList-item-button
|
|
||||||
navDarkButtonColor()
|
|
||||||
border-left 4px solid transparent
|
|
||||||
.tabList-plusButton
|
|
||||||
navDarkButtonColor()
|
|
||||||
|
|
||||||
.tabView-top
|
.tabList-item-button
|
||||||
border-color $ui-dark-borderColor
|
border none
|
||||||
.tabView-top-name
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color transparent
|
||||||
.tabView-top-mode
|
transition color background-color 0.15s
|
||||||
border-color $ui-dark-borderColor
|
border-left 4px solid transparent
|
||||||
background-color $dark-default-button-background
|
&:hover
|
||||||
color $ui-dark-inactive-text-color
|
color white
|
||||||
|
background-color $ui-dark-button--hover-backgroundColor
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
import React, { PropTypes } from 'react'
|
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
|
||||||
import styles from './NoteItem.styl'
|
|
||||||
import moment from 'moment'
|
|
||||||
import _ from 'lodash'
|
|
||||||
|
|
||||||
class NoteItem extends React.Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick (e) {
|
|
||||||
this.props.onClick(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
let { note, folder, storage, isActive } = this.props
|
|
||||||
|
|
||||||
let tagList = _.isArray(note.tags)
|
|
||||||
? note.tags.map((tag) => {
|
|
||||||
return (
|
|
||||||
<span styleName='bottom-tagList-item'
|
|
||||||
key={tag}>
|
|
||||||
{tag}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
: []
|
|
||||||
return (
|
|
||||||
<div styleName={isActive
|
|
||||||
? 'root--active'
|
|
||||||
: 'root'
|
|
||||||
}
|
|
||||||
key={note.storage + '-' + note.key}
|
|
||||||
onClick={(e) => this.handleClick(e)}
|
|
||||||
>
|
|
||||||
<div styleName='info'>
|
|
||||||
<div styleName='info-left'>
|
|
||||||
<span styleName='info-left-folder'
|
|
||||||
style={{borderColor: folder.color}}
|
|
||||||
>
|
|
||||||
{folder.name}
|
|
||||||
<span styleName='info-left-folder-surfix'>in {storage.name}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='title'>
|
|
||||||
{note.type === 'SNIPPET_NOTE'
|
|
||||||
? <i styleName='title-icon' className='fa fa-fw fa-code' />
|
|
||||||
: <i styleName='title-icon' className='fa fa-fw fa-file-text-o' />
|
|
||||||
}
|
|
||||||
{note.title.trim().length > 0
|
|
||||||
? note.title
|
|
||||||
: <span styleName='title-empty'>Empty</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='bottom'>
|
|
||||||
<i styleName='bottom-tagIcon'
|
|
||||||
className='fa fa-tags fa-fw'
|
|
||||||
/>
|
|
||||||
<div styleName='bottom-tagList'>
|
|
||||||
{tagList.length > 0
|
|
||||||
? tagList
|
|
||||||
: <span styleName='bottom-tagList-empty'>Not tagged yet</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='bottom-time'>
|
|
||||||
{moment(note.updatedAt).fromNow()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NoteItem.propTypes = {
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CSSModules(NoteItem, styles)
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
.root
|
|
||||||
position relative
|
|
||||||
border-bottom $ui-border
|
|
||||||
padding 2px 5px
|
|
||||||
user-select none
|
|
||||||
cursor pointer
|
|
||||||
transition background-color 0.15s
|
|
||||||
&:hover
|
|
||||||
background-color alpha($ui-active-color, 20%)
|
|
||||||
|
|
||||||
.root--active
|
|
||||||
@extend .root
|
|
||||||
background-color $ui-active-color
|
|
||||||
&:hover
|
|
||||||
background-color $ui-active-color
|
|
||||||
color white
|
|
||||||
.info-left-folder
|
|
||||||
.info-left-folder-surfix
|
|
||||||
.title
|
|
||||||
.title-icon
|
|
||||||
.title-empty
|
|
||||||
.bottom-tagIcon
|
|
||||||
.bottom-tagList-item
|
|
||||||
.bottom-tagList-empty
|
|
||||||
.bottom-time
|
|
||||||
color white
|
|
||||||
.bottom-tagList-item
|
|
||||||
color white
|
|
||||||
background-color transparent
|
|
||||||
|
|
||||||
.border
|
|
||||||
absolute top bottom left right
|
|
||||||
border-style solid
|
|
||||||
border-width 2px
|
|
||||||
border-color transparent
|
|
||||||
transition 0.15s
|
|
||||||
|
|
||||||
.info
|
|
||||||
height 20px
|
|
||||||
clearfix()
|
|
||||||
font-size 12px
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
line-height 20px
|
|
||||||
overflow-y hidden
|
|
||||||
|
|
||||||
.info-left
|
|
||||||
float left
|
|
||||||
overflow ellipsis
|
|
||||||
|
|
||||||
.info-left-folder
|
|
||||||
border-left 4px solid transparent
|
|
||||||
padding 2px 5px
|
|
||||||
color $ui-text-color
|
|
||||||
.info-left-folder-surfix
|
|
||||||
font-size 10px
|
|
||||||
margin-left 5px
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
.info-right
|
|
||||||
float right
|
|
||||||
|
|
||||||
.title
|
|
||||||
height 24px
|
|
||||||
box-sizing border-box
|
|
||||||
line-height 24px
|
|
||||||
height 20px
|
|
||||||
line-height 20px
|
|
||||||
padding 0 5px 0 0
|
|
||||||
overflow ellipsis
|
|
||||||
color $ui-text-color
|
|
||||||
|
|
||||||
.title-icon
|
|
||||||
font-size 12px
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
padding-right 3px
|
|
||||||
|
|
||||||
.title-empty
|
|
||||||
font-weight normal
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
|
|
||||||
.bottom
|
|
||||||
margin-top 2px
|
|
||||||
height 20px
|
|
||||||
font-size 12px
|
|
||||||
line-height 20px
|
|
||||||
overflow ellipsis
|
|
||||||
display flex
|
|
||||||
|
|
||||||
.bottom-tagIcon
|
|
||||||
vertical-align middle
|
|
||||||
color $ui-button-color
|
|
||||||
height 20px
|
|
||||||
line-height 20px
|
|
||||||
|
|
||||||
.bottom-tagList
|
|
||||||
flex 1
|
|
||||||
overflow ellipsis
|
|
||||||
line-height 20px
|
|
||||||
|
|
||||||
.bottom-tagList-item
|
|
||||||
margin 0 4px
|
|
||||||
padding 0 4px
|
|
||||||
height 20px
|
|
||||||
box-sizing border-box
|
|
||||||
border-radius 3px
|
|
||||||
vertical-align middle
|
|
||||||
border-style solid
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
border-width 0 0 0 3px
|
|
||||||
background-color $ui-backgroundColor
|
|
||||||
color $ui-text-color
|
|
||||||
transition 0.15s
|
|
||||||
|
|
||||||
.bottom-tagList-empty
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
vertical-align middle
|
|
||||||
font-size 10px
|
|
||||||
margin-left 5px
|
|
||||||
|
|
||||||
.bottom-time
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
margin-left 5px
|
|
||||||
font-size 10px
|
|
||||||
|
|
||||||
body[data-theme="dark"]
|
|
||||||
.root
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
|
|
||||||
.root--active
|
|
||||||
@extend .root
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
&:hover
|
|
||||||
background-color $ui-active-color
|
|
||||||
.info-left-folder
|
|
||||||
.info-left-folder-surfix
|
|
||||||
.title
|
|
||||||
.title-icon
|
|
||||||
.title-empty
|
|
||||||
.bottom-tagIcon
|
|
||||||
.bottom-tagList-item
|
|
||||||
.bottom-tagList-empty
|
|
||||||
.bottom-time
|
|
||||||
color white
|
|
||||||
.bottom-tagList-item
|
|
||||||
color white
|
|
||||||
background-color transparent
|
|
||||||
|
|
||||||
.info
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|
||||||
.info-left-folder
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.info-left-folder-surfix
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|
||||||
.title
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.title-icon
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|
||||||
.title-empty
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|
||||||
.tagList-empty
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|
||||||
.bottom-tagIcon
|
|
||||||
color $ui-dark-button-color
|
|
||||||
|
|
||||||
.bottom-tagList-item
|
|
||||||
color $ui-dark-text-color
|
|
||||||
background-color $ui-dark-backgroundColor
|
|
||||||
|
|
||||||
.bottom-tagList-empty
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|
||||||
.bottom-time
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React from 'react'
|
||||||
import NoteItem from './NoteItem'
|
import NoteItem from 'browser/components/NoteItem'
|
||||||
import _ from 'lodash'
|
import moment from 'moment'
|
||||||
|
|
||||||
class NoteList extends React.Component {
|
class NoteList extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -54,21 +54,22 @@ class NoteList extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { storageMap, notes, index } = this.props
|
let { notes, index } = this.props
|
||||||
|
|
||||||
let notesList = notes
|
let notesList = notes
|
||||||
.slice(0, 10 + 10 * this.state.range)
|
.slice(0, 10 + 10 * this.state.range)
|
||||||
.map((note, _index) => {
|
.map((note, _index) => {
|
||||||
let storage = storageMap[note.storage]
|
const isActive = (index === _index)
|
||||||
let folder = _.find(storage.folders, {key: note.folder})
|
const key = `${note.storage}-${note.key}`
|
||||||
|
const dateDisplay = moment(note.updatedAt).fromNow()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NoteItem
|
<NoteItem
|
||||||
|
isActive={isActive}
|
||||||
note={note}
|
note={note}
|
||||||
key={`${note.storage}-${note.key}`}
|
dateDisplay={dateDisplay}
|
||||||
storage={storage}
|
key={key}
|
||||||
folder={folder}
|
handleNoteClick={(e) => this.props.handleNoteClick(e, _index)}
|
||||||
isActive={index === _index}
|
|
||||||
onClick={(e) => this.props.handleNoteClick(e, _index)}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React from 'react'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './StorageSection.styl'
|
import styles from './StorageSection.styl'
|
||||||
|
import StorageItem from 'browser/components/StorageItem'
|
||||||
|
|
||||||
class StorageSection extends React.Component {
|
class StorageSection extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -30,20 +31,17 @@ class StorageSection extends React.Component {
|
|||||||
render () {
|
render () {
|
||||||
let { storage, filter } = this.props
|
let { storage, filter } = this.props
|
||||||
let folderList = storage.folders
|
let folderList = storage.folders
|
||||||
.map((folder) => {
|
.map(folder => (
|
||||||
return (
|
<StorageItem
|
||||||
<button styleName={filter.type === 'FOLDER' && filter.folder === folder.key && filter.storage === storage.key
|
key={folder.key}
|
||||||
? 'folderList-item--active'
|
isActive={filter.type === 'FOLDER' && filter.folder === folder.key && filter.storage === storage.key}
|
||||||
: 'folderList-item'
|
handleButtonClick={(e) => this.handleFolderClick(e, folder)}
|
||||||
}
|
folderName={folder.name}
|
||||||
style={{borderColor: folder.color}}
|
folderColor={folder.color}
|
||||||
key={folder.key}
|
isFolded={false}
|
||||||
onClick={(e) => this.handleFolderClick(e, folder)}
|
/>
|
||||||
>
|
))
|
||||||
{folder.name}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return (
|
return (
|
||||||
<div styleName='root'>
|
<div styleName='root'>
|
||||||
<div styleName='header'>
|
<div styleName='header'>
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import styles from './FinderMain.styl'
|
|||||||
import StorageSection from './StorageSection'
|
import StorageSection from './StorageSection'
|
||||||
import NoteList from './NoteList'
|
import NoteList from './NoteList'
|
||||||
import NoteDetail from './NoteDetail'
|
import NoteDetail from './NoteDetail'
|
||||||
|
import SideNavFilter from 'browser/components/SideNavFilter'
|
||||||
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
require('!!style!css!stylus?sourceMap!../main/global.styl')
|
require('!!style!css!stylus?sourceMap!../main/global.styl')
|
||||||
require('../lib/customMeta')
|
require('../lib/customMeta')
|
||||||
|
|
||||||
@@ -66,12 +68,8 @@ class FinderMain extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleWindowBlur (e) {
|
handleWindowBlur (e) {
|
||||||
let { filter } = this.state
|
|
||||||
filter.type = 'ALL'
|
|
||||||
this.setState({
|
this.setState({
|
||||||
search: '',
|
search: ''
|
||||||
filter,
|
|
||||||
index: 0
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +95,7 @@ class FinderMain extends React.Component {
|
|||||||
|
|
||||||
if (e.keyCode === 13) {
|
if (e.keyCode === 13) {
|
||||||
this.refs.detail.saveToClipboard()
|
this.refs.detail.saveToClipboard()
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('COPY_FINDER')
|
||||||
hideFinder()
|
hideFinder()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
@@ -307,18 +306,12 @@ class FinderMain extends React.Component {
|
|||||||
/> Only Markdown</label>
|
/> Only Markdown</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button styleName={filter.type === 'ALL'
|
<SideNavFilter
|
||||||
? 'result-nav-menu--active'
|
isHomeActive={filter.type === 'ALL'}
|
||||||
: 'result-nav-menu'
|
handleAllNotesButtonClick={(e) => this.handleAllNotesButtonClick(e)}
|
||||||
}
|
isStarredActive={filter.type === 'STARRED'}
|
||||||
onClick={(e) => this.handleAllNotesButtonClick(e)}
|
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
|
||||||
><i className='fa fa-files-o fa-fw'/> All Notes</button>
|
/>
|
||||||
<button styleName={filter.type === 'STARRED'
|
|
||||||
? 'result-nav-menu--active'
|
|
||||||
: 'result-nav-menu'
|
|
||||||
}
|
|
||||||
onClick={(e) => this.handleStarredButtonClick(e)}
|
|
||||||
><i className='fa fa-star fa-fw'/> Starred</button>
|
|
||||||
<div styleName='result-nav-storageList'>
|
<div styleName='result-nav-storageList'>
|
||||||
{storageList}
|
{storageList}
|
||||||
</div>
|
</div>
|
||||||
@@ -356,7 +349,7 @@ function refreshData () {
|
|||||||
|
|
||||||
ReactDOM.render((
|
ReactDOM.render((
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Finder/>
|
<Finder />
|
||||||
</Provider>
|
</Provider>
|
||||||
), document.getElementById('content'), function () {
|
), document.getElementById('content'), function () {
|
||||||
refreshData()
|
refreshData()
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
import superagent from 'superagent'
|
|
||||||
import superagentPromise from 'superagent-promise'
|
|
||||||
|
|
||||||
export const SERVER_URL = 'https://b00st.io/'
|
|
||||||
// export const SERVER_URL = 'http://localhost:3333/'
|
|
||||||
|
|
||||||
export const request = superagentPromise(superagent, Promise)
|
|
||||||
|
|
||||||
export function shareViaPublicURL (input) {
|
|
||||||
return request
|
|
||||||
.post(SERVER_URL + 'apis/share')
|
|
||||||
// .set({
|
|
||||||
// Authorization: 'Bearer ' + auth.token()
|
|
||||||
// })
|
|
||||||
.send(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
SERVER_URL,
|
|
||||||
shareViaPublicURL
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import CodeMirror from 'codemirror'
|
import CodeMirror from 'codemirror'
|
||||||
import _ from 'lodash'
|
|
||||||
|
|
||||||
CodeMirror.modeInfo.push({name: 'Stylus', mime: 'text/x-styl', mode: 'stylus', ext: ['styl'], alias: ['styl']})
|
CodeMirror.modeInfo.push({name: 'Stylus', mime: 'text/x-styl', mode: 'stylus', ext: ['styl'], alias: ['styl']})
|
||||||
|
|||||||
18
browser/lib/date-formatter.js
Normal file
18
browser/lib/date-formatter.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Formatting date string.
|
||||||
|
*/
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Return date string. For example, 'Sep.9, 2016 12:00'.
|
||||||
|
* @param {mixed}
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export function formatDate (date) {
|
||||||
|
const m = moment(date)
|
||||||
|
if (!m.isValid()) {
|
||||||
|
throw Error('Invalid argument.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.format('MMM D, gggg H:mm')
|
||||||
|
}
|
||||||
33
browser/lib/findNoteTitle.js
Normal file
33
browser/lib/findNoteTitle.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
export function findNoteTitle (value) {
|
||||||
|
let splitted = value.split('\n')
|
||||||
|
let title = null
|
||||||
|
let isInsideCodeBlock = false
|
||||||
|
|
||||||
|
splitted.some((line, index) => {
|
||||||
|
let trimmedLine = line.trim()
|
||||||
|
let trimmedNextLine = splitted[index + 1] === undefined ? '' : splitted[index + 1].trim()
|
||||||
|
if (trimmedLine.match('```')) {
|
||||||
|
isInsideCodeBlock = !isInsideCodeBlock
|
||||||
|
}
|
||||||
|
if (isInsideCodeBlock === false && (trimmedLine.match(/^# +/) || trimmedNextLine.match(/^=+$/))) {
|
||||||
|
title = trimmedLine
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (title === null) {
|
||||||
|
title = ''
|
||||||
|
splitted.some((line) => {
|
||||||
|
if (line.trim().length > 0) {
|
||||||
|
title = line.trim()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return title
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
findNoteTitle
|
||||||
|
}
|
||||||
45
browser/lib/htmlTextHelper.js
Normal file
45
browser/lib/htmlTextHelper.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Text trimmer for html.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function decodeEntities (text) {
|
||||||
|
var entities = [
|
||||||
|
['apos', '\''],
|
||||||
|
['amp', '&'],
|
||||||
|
['lt', '<'],
|
||||||
|
['gt', '>'],
|
||||||
|
['#63', '\\?'],
|
||||||
|
['#36', '\\$']
|
||||||
|
]
|
||||||
|
|
||||||
|
for (var i = 0, max = entities.length; i < max; ++i) {
|
||||||
|
text = text.replace(new RegExp(`&${entities[i][0]};`, 'g'), entities[i][1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeEntities (text) {
|
||||||
|
const entities = [
|
||||||
|
['\'', 'apos'],
|
||||||
|
['<', 'lt'],
|
||||||
|
['>', 'gt'],
|
||||||
|
['\\?', '#63'],
|
||||||
|
['\\$', '#36']
|
||||||
|
]
|
||||||
|
|
||||||
|
entities.forEach((entity) => {
|
||||||
|
text = text.replace(new RegExp(entity[0], 'g'), `&${entity[1]};`)
|
||||||
|
})
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
decodeEntities,
|
||||||
|
encodeEntities
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ var md = markdownit({
|
|||||||
linkify: true,
|
linkify: true,
|
||||||
html: true,
|
html: true,
|
||||||
xhtmlOut: true,
|
xhtmlOut: true,
|
||||||
|
breaks: true,
|
||||||
highlight: function (str, lang) {
|
highlight: function (str, lang) {
|
||||||
if (lang === 'flowchart') {
|
if (lang === 'flowchart') {
|
||||||
return `<pre class="flowchart">${str}</pre>`
|
return `<pre class="flowchart">${str}</pre>`
|
||||||
@@ -56,9 +57,10 @@ md.use(math, {
|
|||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
md.use(require('markdown-it-imsize'))
|
||||||
md.use(require('markdown-it-footnote'))
|
md.use(require('markdown-it-footnote'))
|
||||||
// Override task item
|
// Override task item
|
||||||
md.block.ruler.at('paragraph', function (state, startLine/*, endLine*/) {
|
md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
|
||||||
let content, terminate, i, l, token
|
let content, terminate, i, l, token
|
||||||
let nextLine = startLine + 1
|
let nextLine = startLine + 1
|
||||||
let terminatorRules = state.md.block.ruler.getRules('paragraph')
|
let terminatorRules = state.md.block.ruler.getRules('paragraph')
|
||||||
@@ -157,7 +159,8 @@ function strip (input) {
|
|||||||
const markdown = {
|
const markdown = {
|
||||||
render: function markdown (content) {
|
render: function markdown (content) {
|
||||||
if (!_.isString(content)) content = ''
|
if (!_.isString(content)) content = ''
|
||||||
return md.render(content)
|
const renderedContent = md.render(content)
|
||||||
|
return md.normalizeLinkText(renderedContent)
|
||||||
},
|
},
|
||||||
strip
|
strip
|
||||||
}
|
}
|
||||||
|
|||||||
43
browser/lib/search.js
Normal file
43
browser/lib/search.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
export default function searchFromNotes (data, search) {
|
||||||
|
let notes = data.noteMap.map((note) => note)
|
||||||
|
if (search.trim().length === 0) return []
|
||||||
|
let searchBlocks = search.split(' ')
|
||||||
|
searchBlocks.forEach((block) => {
|
||||||
|
if (block.match(/^#.+/)) {
|
||||||
|
notes = findByTag(notes, block)
|
||||||
|
} else {
|
||||||
|
notes = findByWord(notes, block)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return notes
|
||||||
|
}
|
||||||
|
|
||||||
|
function findByTag (notes, block) {
|
||||||
|
const tag = block.match(/#(.+)/)[1]
|
||||||
|
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
|
||||||
|
return notes.filter((note) => {
|
||||||
|
if (!_.isArray(note.tags)) return false
|
||||||
|
return note.tags.some((_tag) => {
|
||||||
|
return _tag.match(regExp)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function findByWord (notes, block) {
|
||||||
|
let regExp = new RegExp(_.escapeRegExp(block), 'i')
|
||||||
|
return notes.filter((note) => {
|
||||||
|
if (_.isArray(note.tags) && note.tags.some((_tag) => {
|
||||||
|
return _tag.match(regExp)
|
||||||
|
})) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (note.type === 'SNIPPET_NOTE') {
|
||||||
|
return note.description.match(regExp)
|
||||||
|
} else if (note.type === 'MARKDOWN_NOTE') {
|
||||||
|
return note.content.match(regExp)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
.root
|
.root
|
||||||
absolute top bottom right
|
absolute top bottom right
|
||||||
border-width 1px 0
|
|
||||||
border-style solid
|
|
||||||
border-color $ui-borderColor
|
|
||||||
|
|
||||||
.empty
|
.empty
|
||||||
height 320px
|
height 320px
|
||||||
@@ -18,7 +15,6 @@
|
|||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
background-color $ui-dark-backgroundColor
|
background-color $ui-dark-backgroundColor
|
||||||
.empty-message
|
.empty-message
|
||||||
color $ui-dark-inactive-text-color
|
color $ui-dark-inactive-text-color
|
||||||
|
|||||||
9
browser/main/Detail/DetailVars.styl
Normal file
9
browser/main/Detail/DetailVars.styl
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Varibales for note detail space.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Margin on the left side and the right side for NoteDetail component.
|
||||||
|
$note-detail-left-margin = 25px
|
||||||
|
$note-detail-right-margin = 25px
|
||||||
|
|
||||||
|
$note-detail-box-shadow = 2px 0 15px -8px #b1b1b1 inset
|
||||||
@@ -128,8 +128,8 @@ class FolderSelect extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nextOption () {
|
nextOption () {
|
||||||
let { storages } = this.props
|
|
||||||
let { optionIndex } = this.state
|
let { optionIndex } = this.state
|
||||||
|
let { folders } = this.props
|
||||||
|
|
||||||
optionIndex++
|
optionIndex++
|
||||||
if (optionIndex >= folders.length) optionIndex = 0
|
if (optionIndex >= folders.length) optionIndex = 0
|
||||||
@@ -262,13 +262,11 @@ class FolderSelect extends React.Component {
|
|||||||
: <div styleName='idle'>
|
: <div styleName='idle'>
|
||||||
<div styleName='idle-label'>
|
<div styleName='idle-label'>
|
||||||
<span styleName='idle-label-name'
|
<span styleName='idle-label-name'
|
||||||
style={{borderColor: currentOption.folder.color}}
|
style={{color: currentOption.folder.color}}
|
||||||
>
|
>
|
||||||
{currentOption.folder.name}
|
{currentOption.folder.name} /
|
||||||
<span styleName='idle-label-name-surfix'>in {currentOption.storage.name}</span>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<i styleName='idle-caret' className='fa fa-fw fa-caret-down'/>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,34 @@
|
|||||||
.root
|
.root
|
||||||
position relative
|
position relative
|
||||||
border solid 1px transparent
|
border solid 1px transparent
|
||||||
line-height 34px
|
line-height 26px
|
||||||
vertical-align middle
|
vertical-align middle
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
transition 0.15s
|
transition 0.15s
|
||||||
user-select none
|
user-select none
|
||||||
&:hover
|
&:hover
|
||||||
background-color white
|
background-color $ui-button--hover-backgroundColor
|
||||||
border-color $ui-borderColor
|
|
||||||
|
|
||||||
.root--search, .root--focus
|
.root--search, .root--focus
|
||||||
@extend .root
|
@extend .root
|
||||||
background-color white
|
background-color $ui-noteDetail-backgroundColor = #F4F4F4
|
||||||
border-color $ui-input--focus-borderColor
|
border-color $ui-input--focus-borderColor
|
||||||
|
width 100px
|
||||||
&:hover
|
&:hover
|
||||||
background-color white
|
|
||||||
border-color $ui-input--focus-borderColor
|
border-color $ui-input--focus-borderColor
|
||||||
|
|
||||||
.idle
|
.idle
|
||||||
position relative
|
position relative
|
||||||
cursor pointer
|
cursor pointer
|
||||||
|
|
||||||
.idle-label
|
.idle-label
|
||||||
absolute left top
|
|
||||||
padding 0 0 0 5px
|
|
||||||
right 20px
|
right 20px
|
||||||
overflow ellipsis
|
overflow ellipsis
|
||||||
|
|
||||||
.idle-label-name
|
.idle-label-name
|
||||||
border-left solid 4px transparent
|
font-size 14px
|
||||||
padding 2px 5px
|
padding 2px
|
||||||
|
|
||||||
.idle-label-name-surfix
|
.idle-label-name-surfix
|
||||||
font-size 10px
|
font-size 10px
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
@@ -60,9 +59,9 @@
|
|||||||
max-height 450px
|
max-height 450px
|
||||||
overflow auto
|
overflow auto
|
||||||
z-index 200
|
z-index 200
|
||||||
|
border $ui-border
|
||||||
background-color white
|
background-color white
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
box-shadow 2px 2px 10px gray
|
|
||||||
|
|
||||||
.search-optionList-item
|
.search-optionList-item
|
||||||
height 34px
|
height 34px
|
||||||
@@ -82,7 +81,7 @@
|
|||||||
background-color $ui-button--active-backgroundColor
|
background-color $ui-button--active-backgroundColor
|
||||||
color $ui-button--active-color
|
color $ui-button--active-color
|
||||||
.search-optionList-item-name
|
.search-optionList-item-name
|
||||||
border-left solid 4px transparent
|
border-left solid 2px transparent
|
||||||
padding 2px 5px
|
padding 2px 5px
|
||||||
.search-optionList-item-name-surfix
|
.search-optionList-item-name-surfix
|
||||||
font-size 10px
|
font-size 10px
|
||||||
@@ -115,8 +114,8 @@ body[data-theme="dark"]
|
|||||||
|
|
||||||
.search-optionList
|
.search-optionList
|
||||||
color white
|
color white
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-button--hover-backgroundColor
|
||||||
box-shadow 2px 2px 10px black
|
|
||||||
|
|
||||||
.search-optionList-item
|
.search-optionList-item
|
||||||
&:hover
|
&:hover
|
||||||
|
|||||||
19
browser/main/Detail/InfoButton.js
Normal file
19
browser/main/Detail/InfoButton.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './InfoButton.styl'
|
||||||
|
|
||||||
|
const InfoButton = ({
|
||||||
|
onClick
|
||||||
|
}) => (
|
||||||
|
<button styleName='control-infoButton'
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<i className='fa fa-info infoButton' styleName='info-button' />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
|
||||||
|
InfoButton.propTypes = {
|
||||||
|
onClick: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(InfoButton, styles)
|
||||||
21
browser/main/Detail/InfoButton.styl
Normal file
21
browser/main/Detail/InfoButton.styl
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
.control-infoButton
|
||||||
|
float right
|
||||||
|
topBarButtonLight()
|
||||||
|
|
||||||
|
.control-infoPanel
|
||||||
|
position fixed
|
||||||
|
pointer-events none
|
||||||
|
top 50px
|
||||||
|
z-index 200
|
||||||
|
line-height normal
|
||||||
|
border-radius 2px
|
||||||
|
opacity 0
|
||||||
|
transition 0.1s
|
||||||
|
|
||||||
|
.infoButton
|
||||||
|
padding 0px
|
||||||
|
margin 15px 0
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.control-infoButton
|
||||||
|
topBarButtonDark()
|
||||||
79
browser/main/Detail/InfoPanel.js
Normal file
79
browser/main/Detail/InfoPanel.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './InfoPanel.styl'
|
||||||
|
|
||||||
|
const InfoPanel = ({
|
||||||
|
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt
|
||||||
|
}) => (
|
||||||
|
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>
|
||||||
|
Storage
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
{storageName}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>
|
||||||
|
Folder
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
{folderName}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>
|
||||||
|
Created at
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
{createdAt}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>
|
||||||
|
Updated at
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
{updatedAt}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>
|
||||||
|
Note Link
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input value={noteLink} onClick={(e) => { e.target.select() }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id='export-wrap'>
|
||||||
|
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
|
||||||
|
<i className='fa fa-file-code-o fa-fw' />
|
||||||
|
<p>.md</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
|
||||||
|
<i className='fa fa-file-text-o fa-fw' />
|
||||||
|
<p>.txt</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button styleName='export--unable'>
|
||||||
|
<i className='fa fa-file-pdf-o fa-fw' />
|
||||||
|
<p>.pdf</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
InfoPanel.propTypes = {
|
||||||
|
storageName: PropTypes.string.isRequired,
|
||||||
|
folderName: PropTypes.string.isRequired,
|
||||||
|
noteLink: PropTypes.string.isRequired,
|
||||||
|
updatedAt: PropTypes.string.isRequired,
|
||||||
|
createdAt: PropTypes.string.isRequired,
|
||||||
|
exportAsMd: PropTypes.func.isRequired,
|
||||||
|
exportAsTxt: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(InfoPanel, styles)
|
||||||
97
browser/main/Detail/InfoPanel.styl
Normal file
97
browser/main/Detail/InfoPanel.styl
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
.control-infoPanel
|
||||||
|
position fixed
|
||||||
|
pointer-events none
|
||||||
|
top 50px
|
||||||
|
z-index 200
|
||||||
|
line-height normal
|
||||||
|
border-radius 4px
|
||||||
|
opacity 0
|
||||||
|
transition 0.2s
|
||||||
|
|
||||||
|
.control-infoButton-panel
|
||||||
|
z-index 200
|
||||||
|
margin-top 45px
|
||||||
|
margin-left -210px
|
||||||
|
position absolute
|
||||||
|
padding 20px 20px 0 20px
|
||||||
|
width 340px
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
|
border 1px solid $border-color
|
||||||
|
|
||||||
|
.group-section
|
||||||
|
display flex
|
||||||
|
line-height 30px
|
||||||
|
font-size 13px
|
||||||
|
|
||||||
|
.group-section-label
|
||||||
|
width 70px
|
||||||
|
height 20px
|
||||||
|
text-align left
|
||||||
|
margin-right 50px
|
||||||
|
font-size 13px
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.group-section-control
|
||||||
|
flex 1
|
||||||
|
font-size 13px
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.group-section-control input
|
||||||
|
width 160px
|
||||||
|
height 25px
|
||||||
|
|
||||||
|
[id=export-wrap]
|
||||||
|
height 90px
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
|
margin 20px 0 10px 0
|
||||||
|
button
|
||||||
|
outline none
|
||||||
|
font-size 48px
|
||||||
|
color #A0A0A0
|
||||||
|
background-color transparent
|
||||||
|
border none
|
||||||
|
margin 0 5px
|
||||||
|
border-radius 5px
|
||||||
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
background-color alpha($ui-button--hover-backgroundColor, 30%)
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
p
|
||||||
|
font-size 13px
|
||||||
|
color #A0A0A0
|
||||||
|
font-weight light
|
||||||
|
&:hover
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.export--enable
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
.export--unable
|
||||||
|
cursor not-allowed
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.control-infoButton-panel
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
border 1px solid $ui-dark-borderColor
|
||||||
|
|
||||||
|
.group-section-label
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.group-section-control
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.group-section-control input
|
||||||
|
background-color alpha($ui-dark-borderColor, 80%)
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
[id=export-wrap]
|
||||||
|
button
|
||||||
|
color $ui-dark-inactive-text-color
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-dark-borderColor, 20%)
|
||||||
|
color $ui-dark-text-color
|
||||||
|
p
|
||||||
|
color $ui-dark-inactive-text-color
|
||||||
|
&:hover
|
||||||
|
color $ui-dark-text-color
|
||||||
@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react'
|
|||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './MarkdownNoteDetail.styl'
|
import styles from './MarkdownNoteDetail.styl'
|
||||||
import MarkdownEditor from 'browser/components/MarkdownEditor'
|
import MarkdownEditor from 'browser/components/MarkdownEditor'
|
||||||
|
import TodoListPercentage from 'browser/components/TodoListPercentage'
|
||||||
import StarButton from './StarButton'
|
import StarButton from './StarButton'
|
||||||
import TagSelect from './TagSelect'
|
import TagSelect from './TagSelect'
|
||||||
import FolderSelect from './FolderSelect'
|
import FolderSelect from './FolderSelect'
|
||||||
@@ -11,6 +12,12 @@ import ee from 'browser/main/lib/eventEmitter'
|
|||||||
import markdown from 'browser/lib/markdown'
|
import markdown from 'browser/lib/markdown'
|
||||||
import StatusBar from '../StatusBar'
|
import StatusBar from '../StatusBar'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import { findNoteTitle } from 'browser/lib/findNoteTitle'
|
||||||
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
import TrashButton from './TrashButton'
|
||||||
|
import InfoButton from './InfoButton'
|
||||||
|
import InfoPanel from './InfoPanel'
|
||||||
|
import { formatDate } from 'browser/lib/date-formatter'
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const { remote } = electron
|
const { remote } = electron
|
||||||
@@ -25,15 +32,23 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
note: Object.assign({
|
note: Object.assign({
|
||||||
title: '',
|
title: '',
|
||||||
content: ''
|
content: ''
|
||||||
}, props.note)
|
}, props.note),
|
||||||
|
isLockButtonShown: false,
|
||||||
|
isLocked: false
|
||||||
}
|
}
|
||||||
this.dispatchTimer = null
|
this.dispatchTimer = null
|
||||||
|
|
||||||
|
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
focus () {
|
||||||
this.refs.content.focus()
|
this.refs.content.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
ee.on('topbar:togglelockbutton', this.toggleLockButton)
|
||||||
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
|
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
|
||||||
if (this.saveQueue != null) this.saveNow()
|
if (this.saveQueue != null) this.saveNow()
|
||||||
@@ -41,7 +56,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
note: Object.assign({}, nextProps.note)
|
note: Object.assign({}, nextProps.note)
|
||||||
}, () => {
|
}, () => {
|
||||||
this.refs.content.reload()
|
this.refs.content.reload()
|
||||||
this.refs.tags.reset()
|
if (this.refs.tags) this.refs.tags.reset()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,42 +65,34 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
if (this.saveQueue != null) this.saveNow()
|
if (this.saveQueue != null) this.saveNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
findTitle (value) {
|
componentDidUnmount () {
|
||||||
let splitted = value.split('\n')
|
ee.off('topbar:togglelockbutton', this.toggleLockButton)
|
||||||
let title = null
|
}
|
||||||
|
|
||||||
for (let i = 0; i < splitted.length; i++) {
|
getPercentageOfCompleteTodo (noteContent) {
|
||||||
let trimmedLine = splitted[i].trim()
|
let splitted = noteContent.split('\n')
|
||||||
if (trimmedLine.match(/^# .+/)) {
|
let numberOfTodo = 0
|
||||||
title = trimmedLine.substring(1, trimmedLine.length).trim()
|
let numberOfCompletedTodo = 0
|
||||||
break
|
|
||||||
|
splitted.forEach((line) => {
|
||||||
|
let trimmedLine = line.trim()
|
||||||
|
if (trimmedLine.match(/^[\+\-\*] \[\s|x\] ./)) {
|
||||||
|
numberOfTodo++
|
||||||
}
|
}
|
||||||
}
|
if (trimmedLine.match(/^[\+\-\*] \[x\] ./)) {
|
||||||
|
numberOfCompletedTodo++
|
||||||
if (title == null) {
|
|
||||||
for (let i = 0; i < splitted.length; i++) {
|
|
||||||
let trimmedLine = splitted[i].trim()
|
|
||||||
if (trimmedLine.length > 0) {
|
|
||||||
title = trimmedLine
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (title == null) {
|
})
|
||||||
title = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
title = markdown.strip(title)
|
return Math.floor(numberOfCompletedTodo / numberOfTodo * 100)
|
||||||
|
|
||||||
return title
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange (e) {
|
handleChange (e) {
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
|
|
||||||
note.content = this.refs.content.value
|
note.content = this.refs.content.value
|
||||||
note.tags = this.refs.tags.value
|
if (this.refs.tags) note.tags = this.refs.tags.value
|
||||||
note.title = this.findTitle(note.content)
|
note.title = markdown.strip(findNoteTitle(note.content))
|
||||||
note.updatedAt = new Date()
|
note.updatedAt = new Date()
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -114,6 +121,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
type: 'UPDATE_NOTE',
|
type: 'UPDATE_NOTE',
|
||||||
note: note
|
note: note
|
||||||
})
|
})
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +160,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
|
|
||||||
handleStarButtonClick (e) {
|
handleStarButtonClick (e) {
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
|
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_STAR')
|
||||||
|
|
||||||
note.isStarred = !note.isStarred
|
note.isStarred = !note.isStarred
|
||||||
|
|
||||||
@@ -166,37 +175,26 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShareButtonClick (e) {
|
exportAsMd () {
|
||||||
let menu = new Menu()
|
ee.emit('export:save-md')
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'Export as a File',
|
|
||||||
click: (e) => this.handlePreferencesButtonClick(e)
|
|
||||||
}))
|
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'Export to Web',
|
|
||||||
disabled: true,
|
|
||||||
click: (e) => this.handlePreferencesButtonClick(e)
|
|
||||||
}))
|
|
||||||
menu.popup(remote.getCurrentWindow())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContextButtonClick (e) {
|
exportAsTxt () {
|
||||||
let menu = new Menu()
|
ee.emit('export:save-text')
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'Delete',
|
|
||||||
click: (e) => this.handleDeleteMenuClick(e)
|
|
||||||
}))
|
|
||||||
menu.popup(remote.getCurrentWindow())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteMenuClick (e) {
|
handleTrashButtonClick (e) {
|
||||||
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
let { note } = this.state
|
||||||
type: 'warning',
|
const { isTrashed } = note
|
||||||
message: 'Delete a note',
|
|
||||||
detail: 'This work cannot be undone.',
|
if (isTrashed) {
|
||||||
buttons: ['Confirm', 'Cancel']
|
let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
})
|
type: 'warning',
|
||||||
if (index === 0) {
|
message: 'Delete a note',
|
||||||
|
detail: 'This work cannot be undone.',
|
||||||
|
buttons: ['Confirm', 'Cancel']
|
||||||
|
})
|
||||||
|
if (dialogueButtonIndex === 1) return
|
||||||
let { note, dispatch } = this.props
|
let { note, dispatch } = this.props
|
||||||
dataApi
|
dataApi
|
||||||
.deleteNote(note.storage, note.key)
|
.deleteNote(note.storage, note.key)
|
||||||
@@ -209,67 +207,168 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
ee.once('list:moved', dispatchHandler)
|
ee.once('list:moved', dispatchHandler)
|
||||||
ee.emit('list:next')
|
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
note.isTrashed = true
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
note
|
||||||
|
}, () => {
|
||||||
|
this.save()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
ee.emit('list:next')
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUndoButtonClick (e) {
|
||||||
|
let { note } = this.state
|
||||||
|
|
||||||
|
note.isTrashed = false
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
note
|
||||||
|
}, () => {
|
||||||
|
this.save()
|
||||||
|
this.refs.content.reload()
|
||||||
|
ee.emit('list:next')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFullScreenButton (e) {
|
||||||
|
ee.emit('editor:fullscreen')
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLockButtonMouseDown (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
ee.emit('editor:lock')
|
||||||
|
this.setState({ isLocked: !this.state.isLocked })
|
||||||
|
if (this.state.isLocked) this.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
getToggleLockButton () {
|
||||||
|
return this.state.isLocked ? 'fa-lock' : 'fa-unlock'
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteKeyDown (e) {
|
handleDeleteKeyDown (e) {
|
||||||
if (e.keyCode === 27) this.handleDeleteCancelButtonClick(e)
|
if (e.keyCode === 27) this.handleDeleteCancelButtonClick(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleToggleLockButton (event, noteStatus) {
|
||||||
|
// first argument event is not used
|
||||||
|
if (this.props.config.editor.switchPreview === 'BLUR' && noteStatus === 'CODE') {
|
||||||
|
this.setState({isLockButtonShown: true})
|
||||||
|
} else {
|
||||||
|
this.setState({isLockButtonShown: false})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFocus (e) {
|
||||||
|
this.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInfoButtonClick (e) {
|
||||||
|
const infoPanel = document.querySelector('.infoPanel')
|
||||||
|
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { data, config } = this.props
|
let { data, config, location } = this.props
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
|
let storageKey = note.storage
|
||||||
|
let folderKey = note.folder
|
||||||
|
|
||||||
|
let options = []
|
||||||
|
data.storageMap.forEach((storage, index) => {
|
||||||
|
storage.folders.forEach((folder) => {
|
||||||
|
options.push({
|
||||||
|
storage: storage,
|
||||||
|
folder: folder
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||||
|
|
||||||
|
const trashTopBar = <div styleName='info'>
|
||||||
|
<div styleName='info-left'>
|
||||||
|
<i styleName='undo-button'
|
||||||
|
className='fa fa-undo fa-fw'
|
||||||
|
onClick={(e) => this.handleUndoButtonClick(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const detailTopBar = <div styleName='info'>
|
||||||
|
<div styleName='info-left'>
|
||||||
|
<StarButton styleName='info-left-button'
|
||||||
|
onClick={(e) => this.handleStarButtonClick(e)}
|
||||||
|
isActive={note.isStarred}
|
||||||
|
/>
|
||||||
|
<div styleName='info-left-top'>
|
||||||
|
<FolderSelect styleName='info-left-top-folderSelect'
|
||||||
|
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||||
|
ref='folder'
|
||||||
|
data={data}
|
||||||
|
onChange={(e) => this.handleFolderChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TagSelect
|
||||||
|
ref='tags'
|
||||||
|
value={this.state.note.tags}
|
||||||
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
/>
|
||||||
|
<TodoListPercentage
|
||||||
|
percentageOfTodo={this.getPercentageOfCompleteTodo(note.content)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
{(() => {
|
||||||
|
const faClassName = `fa ${this.getToggleLockButton()}`
|
||||||
|
const lockButtonComponent =
|
||||||
|
<button styleName='control-lockButton'
|
||||||
|
onFocus={(e) => this.handleFocus(e)}
|
||||||
|
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
|
||||||
|
>
|
||||||
|
<i className={faClassName} styleName='lock-button' />
|
||||||
|
<span styleName='control-lockButton-tooltip'>
|
||||||
|
{this.state.isLocked ? 'Unlock' : 'Lock'}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
return (
|
||||||
|
this.state.isLockButtonShown ? lockButtonComponent : ''
|
||||||
|
)
|
||||||
|
})()}
|
||||||
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
<button styleName='control-fullScreenButton'
|
||||||
|
onMouseDown={(e) => this.handleFullScreenButton(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-expand' styleName='fullScreen-button' />
|
||||||
|
</button>
|
||||||
|
<InfoButton
|
||||||
|
onClick={(e) => this.handleInfoButtonClick(e)}
|
||||||
|
/>
|
||||||
|
<InfoPanel
|
||||||
|
storageName={currentOption.storage.name}
|
||||||
|
folderName={currentOption.folder.name}
|
||||||
|
noteLink={`[${note.title}](${location.query.key})`}
|
||||||
|
updatedAt={formatDate(note.updatedAt)}
|
||||||
|
createdAt={formatDate(note.createdAt)}
|
||||||
|
exportAsMd={this.exportAsMd}
|
||||||
|
exportAsTxt={this.exportAsTxt}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='NoteDetail'
|
<div className='NoteDetail'
|
||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
styleName='root'
|
styleName='root'
|
||||||
>
|
>
|
||||||
<div styleName='info'>
|
|
||||||
<div styleName='info-left'>
|
{location.pathname === '/trashed' ? trashTopBar : detailTopBar}
|
||||||
<div styleName='info-left-top'>
|
|
||||||
<FolderSelect styleName='info-left-top-folderSelect'
|
|
||||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
|
||||||
ref='folder'
|
|
||||||
data={data}
|
|
||||||
onChange={(e) => this.handleFolderChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div styleName='info-left-bottom'>
|
|
||||||
<TagSelect
|
|
||||||
styleName='info-left-bottom-tagSelect'
|
|
||||||
ref='tags'
|
|
||||||
value={this.state.note.tags}
|
|
||||||
onChange={(e) => this.handleChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div styleName='info-right'>
|
|
||||||
<StarButton styleName='info-right-button'
|
|
||||||
onClick={(e) => this.handleStarButtonClick(e)}
|
|
||||||
isActive={note.isStarred}
|
|
||||||
/>
|
|
||||||
<button styleName='info-right-button'
|
|
||||||
onClick={(e) => this.handleShareButtonClick(e)}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<i className='fa fa-share-alt fa-fw' />
|
|
||||||
<span styleName='info-right-button-tooltip'
|
|
||||||
style={{right: 20}}
|
|
||||||
>Share Note</span>
|
|
||||||
</button>
|
|
||||||
<button styleName='info-right-button'
|
|
||||||
onClick={(e) => this.handleContextButtonClick(e)}
|
|
||||||
>
|
|
||||||
<i className='fa fa-ellipsis-v' />
|
|
||||||
<span styleName='info-right-button-tooltip'
|
|
||||||
style={{right: 5}}
|
|
||||||
>More Options</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='body'>
|
<div styleName='body'>
|
||||||
<MarkdownEditor
|
<MarkdownEditor
|
||||||
@@ -277,6 +376,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
styleName='body-noteEditor'
|
styleName='body-noteEditor'
|
||||||
config={config}
|
config={config}
|
||||||
value={this.state.note.content}
|
value={this.state.note.content}
|
||||||
|
storageKey={this.state.note.storage}
|
||||||
onChange={(e) => this.handleChange(e)}
|
onChange={(e) => this.handleChange(e)}
|
||||||
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
|
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
|
||||||
/>
|
/>
|
||||||
@@ -284,6 +384,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
|
|
||||||
<StatusBar
|
<StatusBar
|
||||||
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
|
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
|
||||||
|
date={note.updatedAt}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,68 +1,42 @@
|
|||||||
$info-height = 75px
|
@import('NoteDetailInfo')
|
||||||
|
@import('DetailVars')
|
||||||
|
|
||||||
.root
|
.root
|
||||||
absolute top right bottom
|
absolute top right bottom
|
||||||
border-width 0 0 1px
|
border-width 0 0 1px
|
||||||
border-style solid
|
border-style solid
|
||||||
border-color $ui-borderColor
|
border-color $ui-borderColor
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
box-shadow $note-detail-box-shadow
|
||||||
|
|
||||||
.info
|
.lock-button
|
||||||
absolute top left right
|
padding-bottom 3px
|
||||||
height $info-height
|
|
||||||
border-bottom $ui-border
|
|
||||||
background-color $ui-backgroundColor
|
|
||||||
|
|
||||||
.info-left
|
.control-lockButton
|
||||||
float left
|
topBarButtonLight()
|
||||||
padding 0 5px
|
|
||||||
|
|
||||||
.info-left-top
|
.control-lockButton-tooltip
|
||||||
height 40px
|
|
||||||
line-height 40px
|
|
||||||
|
|
||||||
.info-left-top-folderSelect
|
|
||||||
display inline-block
|
|
||||||
height 34px
|
|
||||||
width 200px
|
|
||||||
vertical-align middle
|
|
||||||
|
|
||||||
.info-left-bottom
|
|
||||||
height 30px
|
|
||||||
|
|
||||||
.info-left-bottom-tagSelect
|
|
||||||
height 30px
|
|
||||||
line-height 30px
|
|
||||||
|
|
||||||
.info-right
|
|
||||||
float right
|
|
||||||
|
|
||||||
.info-right-button
|
|
||||||
width 34px
|
|
||||||
height 34px
|
|
||||||
border-radius 17px
|
|
||||||
navButtonColor()
|
|
||||||
border $ui-border
|
|
||||||
font-size 14px
|
|
||||||
margin 8px 2px
|
|
||||||
padding 0
|
|
||||||
&:active
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
&:hover .info-right-button-tooltip
|
|
||||||
opacity 1
|
|
||||||
&:focus
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
|
|
||||||
.info-right-button-tooltip
|
|
||||||
tooltip()
|
tooltip()
|
||||||
position fixed
|
position fixed
|
||||||
top 45px
|
pointer-events none
|
||||||
|
top 50px
|
||||||
|
z-index 200
|
||||||
padding 5px
|
padding 5px
|
||||||
opacity 0
|
line-height normal
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
|
opacity 0
|
||||||
|
transition 0.1s
|
||||||
|
|
||||||
|
.control-fullScreenButton
|
||||||
|
float right
|
||||||
|
padding 0 0 2px 0
|
||||||
|
topBarButtonLight()
|
||||||
|
|
||||||
.body
|
.body
|
||||||
absolute left right
|
absolute left right
|
||||||
top $info-height
|
left $note-detail-left-margin
|
||||||
|
right $note-detail-right-margin
|
||||||
|
top $info-height + $info-margin-under-border
|
||||||
bottom $statusBar-height
|
bottom $statusBar-height
|
||||||
|
|
||||||
.body-noteEditor
|
.body-noteEditor
|
||||||
@@ -71,32 +45,14 @@ $info-height = 75px
|
|||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
box-shadow none
|
||||||
|
|
||||||
.info
|
.control-lockButton
|
||||||
border-color $ui-dark-borderColor
|
topBarButtonDark()
|
||||||
background-color $ui-dark-backgroundColor
|
|
||||||
|
|
||||||
.info-delete
|
.control-lockButton-tooltip
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.info-delete-confirmButton
|
|
||||||
colorDarkDangerButton()
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.info-delete-cancelButton
|
|
||||||
colorDarkDefaultButton()
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.info-right-button
|
|
||||||
navDarkButtonColor()
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
&:active
|
|
||||||
border-color $ui-dark-button--focus-borderColor
|
|
||||||
&:hover .info-right-button-tooltip
|
|
||||||
opacity 1
|
|
||||||
&:focus
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
|
|
||||||
.info-right-button-tooltip
|
|
||||||
darkTooltip()
|
darkTooltip()
|
||||||
|
|
||||||
|
.control-fullScreenButton
|
||||||
|
topBarButtonDark()
|
||||||
|
|||||||
99
browser/main/Detail/NoteDetailInfo.styl
Normal file
99
browser/main/Detail/NoteDetailInfo.styl
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
@import('DetailVars')
|
||||||
|
|
||||||
|
$info-height = 60px
|
||||||
|
$info-margin-under-border = 27px
|
||||||
|
|
||||||
|
.info
|
||||||
|
absolute top left right
|
||||||
|
left $note-detail-left-margin
|
||||||
|
right $note-detail-right-margin
|
||||||
|
height $info-height
|
||||||
|
border-bottom $ui-border
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
|
.info-left
|
||||||
|
float left
|
||||||
|
padding 0 5px
|
||||||
|
margin 0px 2px
|
||||||
|
.info-left-top
|
||||||
|
display inline-block
|
||||||
|
height $info-height
|
||||||
|
line-height $info-height
|
||||||
|
|
||||||
|
.info-left-top-folderSelect
|
||||||
|
padding 0 3px
|
||||||
|
height 34px
|
||||||
|
line-height 26px
|
||||||
|
display flex
|
||||||
|
align-items center
|
||||||
|
justify-content center
|
||||||
|
border-radius 3px
|
||||||
|
.info-left-button
|
||||||
|
width 34px
|
||||||
|
height 34px
|
||||||
|
navButtonColor()
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
font-size 14px
|
||||||
|
line-height 0
|
||||||
|
margin 13px 2px
|
||||||
|
padding 0
|
||||||
|
border-radius 17px
|
||||||
|
&:hover .info-left-button-tooltip
|
||||||
|
opacity 1
|
||||||
|
&:focus
|
||||||
|
border-color $ui-favorite-star-button-color
|
||||||
|
&:active, &:active:hover
|
||||||
|
background-color $ui-favorite-star-button-color
|
||||||
|
color $ui-button--color
|
||||||
|
|
||||||
|
.info-right
|
||||||
|
position absolute
|
||||||
|
right 0
|
||||||
|
top 0
|
||||||
|
background $ui-noteDetail-backgroundColor
|
||||||
|
bottom 1px
|
||||||
|
padding-left 30px
|
||||||
|
|
||||||
|
.undo-button
|
||||||
|
width 34px
|
||||||
|
height 34px
|
||||||
|
border-radius 17px
|
||||||
|
font-size 14px
|
||||||
|
margin 15px 7px
|
||||||
|
border none
|
||||||
|
color $ui-button-color
|
||||||
|
display flex
|
||||||
|
align-items center
|
||||||
|
justify-content center
|
||||||
|
fill $ui-button-color
|
||||||
|
background-color transparent
|
||||||
|
cursor pointer
|
||||||
|
&:active
|
||||||
|
border-color $ui-button--active-backgroundColor
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-button--hover-backgroundColor, 60%)
|
||||||
|
.control-lockButton-tooltip
|
||||||
|
opacity 1
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.info
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
|
||||||
|
.info-delete
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.info-delete-confirmButton
|
||||||
|
colorDarkDangerButton()
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.info-delete-cancelButton
|
||||||
|
colorDarkDefaultButton()
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.info-right
|
||||||
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
|
||||||
|
.undo-button
|
||||||
|
topBarButtonDark()
|
||||||
@@ -10,10 +10,17 @@ import dataApi from 'browser/main/lib/dataApi'
|
|||||||
import { hashHistory } from 'react-router'
|
import { hashHistory } from 'react-router'
|
||||||
import ee from 'browser/main/lib/eventEmitter'
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
import CodeMirror from 'codemirror'
|
import CodeMirror from 'codemirror'
|
||||||
import SnippetTab from './SnippetTab'
|
import SnippetTab from 'browser/components/SnippetTab'
|
||||||
import StatusBar from '../StatusBar'
|
import StatusBar from '../StatusBar'
|
||||||
import context from 'browser/lib/context'
|
import context from 'browser/lib/context'
|
||||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { findNoteTitle } from 'browser/lib/findNoteTitle'
|
||||||
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
import TrashButton from './TrashButton'
|
||||||
|
import InfoButton from './InfoButton'
|
||||||
|
import InfoPanel from './InfoPanel'
|
||||||
|
import { formatDate } from 'browser/lib/date-formatter'
|
||||||
|
|
||||||
function pass (name) {
|
function pass (name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
@@ -50,7 +57,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (nextProps.note.key !== this.props.note.key) {
|
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
|
||||||
if (this.saveQueue != null) this.saveNow()
|
if (this.saveQueue != null) this.saveNow()
|
||||||
let nextNote = Object.assign({
|
let nextNote = Object.assign({
|
||||||
description: ''
|
description: ''
|
||||||
@@ -65,7 +72,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
snippets.forEach((snippet, index) => {
|
snippets.forEach((snippet, index) => {
|
||||||
this.refs['code-' + index].reload()
|
this.refs['code-' + index].reload()
|
||||||
})
|
})
|
||||||
this.refs.tags.reset()
|
if (this.refs.tags) this.refs.tags.reset()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,41 +81,13 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
if (this.saveQueue != null) this.saveNow()
|
if (this.saveQueue != null) this.saveNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
findTitle (value) {
|
|
||||||
let splitted = value.split('\n')
|
|
||||||
let title = null
|
|
||||||
|
|
||||||
for (let i = 0; i < splitted.length; i++) {
|
|
||||||
let trimmedLine = splitted[i].trim()
|
|
||||||
if (trimmedLine.match(/^# .+/)) {
|
|
||||||
title = trimmedLine.substring(1, trimmedLine.length).trim()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (title == null) {
|
|
||||||
for (let i = 0; i < splitted.length; i++) {
|
|
||||||
let trimmedLine = splitted[i].trim()
|
|
||||||
if (trimmedLine.length > 0) {
|
|
||||||
title = trimmedLine
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (title == null) {
|
|
||||||
title = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return title
|
|
||||||
}
|
|
||||||
|
|
||||||
handleChange (e) {
|
handleChange (e) {
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
|
|
||||||
note.tags = this.refs.tags.value
|
if (this.refs.tags) note.tags = this.refs.tags.value
|
||||||
note.description = this.refs.description.value
|
note.description = this.refs.description.value
|
||||||
note.updatedAt = new Date()
|
note.updatedAt = new Date()
|
||||||
note.title = this.findTitle(note.description)
|
note.title = findNoteTitle(note.description)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
note
|
note
|
||||||
@@ -136,6 +115,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
type: 'UPDATE_NOTE',
|
type: 'UPDATE_NOTE',
|
||||||
note: note
|
note: note
|
||||||
})
|
})
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +154,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
|
|
||||||
handleStarButtonClick (e) {
|
handleStarButtonClick (e) {
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
|
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_STAR')
|
||||||
|
|
||||||
note.isStarred = !note.isStarred
|
note.isStarred = !note.isStarred
|
||||||
|
|
||||||
@@ -188,36 +169,18 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShareButtonClick (e) {
|
handleTrashButtonClick (e) {
|
||||||
let menu = new Menu()
|
let { note } = this.state
|
||||||
menu.append(new MenuItem({
|
const { isTrashed } = note
|
||||||
label: 'Export as a File',
|
|
||||||
disabled: true,
|
|
||||||
click: (e) => this.handlePreferencesButtonClick(e)
|
|
||||||
}))
|
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'Export to Web',
|
|
||||||
disabled: true,
|
|
||||||
click: (e) => this.handlePreferencesButtonClick(e)
|
|
||||||
}))
|
|
||||||
menu.popup(remote.getCurrentWindow())
|
|
||||||
}
|
|
||||||
|
|
||||||
handleContextButtonClick (e) {
|
if (isTrashed) {
|
||||||
context.popup([{
|
let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
label: 'Delete',
|
type: 'warning',
|
||||||
click: (e) => this.handleDeleteMenuClick(e)
|
message: 'Delete a note',
|
||||||
}])
|
detail: 'This work cannot be undone.',
|
||||||
}
|
buttons: ['Confirm', 'Cancel']
|
||||||
|
})
|
||||||
handleDeleteMenuClick (e) {
|
if (dialogueButtonIndex === 1) return
|
||||||
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
|
||||||
type: 'warning',
|
|
||||||
message: 'Delete a note',
|
|
||||||
detail: 'This work cannot be undone.',
|
|
||||||
buttons: ['Confirm', 'Cancel']
|
|
||||||
})
|
|
||||||
if (index === 0) {
|
|
||||||
let { note, dispatch } = this.props
|
let { note, dispatch } = this.props
|
||||||
dataApi
|
dataApi
|
||||||
.deleteNote(note.storage, note.key)
|
.deleteNote(note.storage, note.key)
|
||||||
@@ -230,9 +193,34 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
ee.once('list:moved', dispatchHandler)
|
ee.once('list:moved', dispatchHandler)
|
||||||
ee.emit('list:next')
|
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
note.isTrashed = true
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
note
|
||||||
|
}, () => {
|
||||||
|
this.save()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
ee.emit('list:next')
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUndoButtonClick (e) {
|
||||||
|
let { note } = this.state
|
||||||
|
|
||||||
|
note.isTrashed = false
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
note
|
||||||
|
}, () => {
|
||||||
|
this.save()
|
||||||
|
ee.emit('list:next')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFullScreenButton (e) {
|
||||||
|
ee.emit('editor:fullscreen')
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTabPlusButtonClick (e) {
|
handleTabPlusButtonClick (e) {
|
||||||
@@ -264,15 +252,15 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteSnippetByIndex (index) {
|
deleteSnippetByIndex (index) {
|
||||||
let snippets = this.state.note.snippets.slice()
|
const snippets = this.state.note.snippets.slice()
|
||||||
snippets.splice(index, 1)
|
snippets.splice(index, 1)
|
||||||
this.state.note.snippets = snippets
|
const note = Object.assign({}, this.state.note, {snippets})
|
||||||
let snippetIndex = this.state.snippetIndex >= snippets.length
|
const snippetIndex = this.state.snippetIndex >= snippets.length
|
||||||
? snippets.length - 1
|
? snippets.length - 1
|
||||||
: this.state.snippetIndex
|
: this.state.snippetIndex
|
||||||
this.setState({
|
this.setState({ note, snippetIndex }, () => {
|
||||||
note: this.state.note,
|
this.save()
|
||||||
snippetIndex
|
this.refs['code-' + this.state.snippetIndex].reload()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,10 +461,27 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
this.refs['code-' + this.state.snippetIndex].focus()
|
this.refs['code-' + this.state.snippetIndex].focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleInfoButtonClick (e) {
|
||||||
|
const infoPanel = document.querySelector('.infoPanel')
|
||||||
|
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
showWarning () {
|
||||||
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Sorry!',
|
||||||
|
detail: 'md/text import is available only a markdown note.',
|
||||||
|
buttons: ['OK']
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { data, config } = this.props
|
let { data, config, location } = this.props
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
|
|
||||||
|
let storageKey = note.storage
|
||||||
|
let folderKey = note.folder
|
||||||
|
|
||||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||||
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
||||||
@@ -523,6 +528,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
fontSize={editorFontSize}
|
fontSize={editorFontSize}
|
||||||
indentType={config.editor.indentType}
|
indentType={config.editor.indentType}
|
||||||
indentSize={editorIndentSize}
|
indentSize={editorIndentSize}
|
||||||
|
keyMap={config.editor.keyMap}
|
||||||
onChange={(e) => this.handleCodeChange(index)(e)}
|
onChange={(e) => this.handleCodeChange(index)(e)}
|
||||||
ref={'code-' + index}
|
ref={'code-' + index}
|
||||||
/>
|
/>
|
||||||
@@ -530,55 +536,79 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let options = []
|
||||||
|
data.storageMap.forEach((storage, index) => {
|
||||||
|
storage.folders.forEach((folder) => {
|
||||||
|
options.push({
|
||||||
|
storage: storage,
|
||||||
|
folder: folder
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||||
|
|
||||||
|
const trashTopBar = <div styleName='info'>
|
||||||
|
<div styleName='info-left'>
|
||||||
|
<i styleName='undo-button'
|
||||||
|
className='fa fa-undo fa-fw'
|
||||||
|
onClick={(e) => this.handleUndoButtonClick(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const detailTopBar = <div styleName='info'>
|
||||||
|
<div styleName='info-left'>
|
||||||
|
<StarButton styleName='info-left-button'
|
||||||
|
onClick={(e) => this.handleStarButtonClick(e)}
|
||||||
|
isActive={note.isStarred}
|
||||||
|
/>
|
||||||
|
<div styleName='info-left-top'>
|
||||||
|
<FolderSelect styleName='info-left-top-folderSelect'
|
||||||
|
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||||
|
ref='folder'
|
||||||
|
data={data}
|
||||||
|
onChange={(e) => this.handleFolderChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TagSelect
|
||||||
|
ref='tags'
|
||||||
|
value={this.state.note.tags}
|
||||||
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
<button styleName='control-fullScreenButton'
|
||||||
|
onMouseDown={(e) => this.handleFullScreenButton(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-expand' styleName='fullScreen-button' />
|
||||||
|
</button>
|
||||||
|
<InfoButton
|
||||||
|
onClick={(e) => this.handleInfoButtonClick(e)}
|
||||||
|
/>
|
||||||
|
<InfoPanel
|
||||||
|
storageName={currentOption.storage.name}
|
||||||
|
folderName={currentOption.folder.name}
|
||||||
|
noteLink={`[${note.title}](${location.query.key})`}
|
||||||
|
updatedAt={formatDate(note.updatedAt)}
|
||||||
|
createdAt={formatDate(note.createdAt)}
|
||||||
|
exportAsMd={this.showWarning}
|
||||||
|
exportAsTxt={this.showWarning}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='NoteDetail'
|
<div className='NoteDetail'
|
||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
styleName='root'
|
styleName='root'
|
||||||
onKeyDown={(e) => this.handleKeyDown(e)}
|
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||||
>
|
>
|
||||||
<div styleName='info'>
|
{location.pathname === '/trashed' ? trashTopBar : detailTopBar}
|
||||||
<div styleName='info-left'>
|
|
||||||
<div styleName='info-left-top'>
|
|
||||||
<FolderSelect styleName='info-left-top-folderSelect'
|
|
||||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
|
||||||
ref='folder'
|
|
||||||
data={data}
|
|
||||||
onChange={(e) => this.handleFolderChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div styleName='info-left-bottom'>
|
|
||||||
<TagSelect
|
|
||||||
styleName='info-left-bottom-tagSelect'
|
|
||||||
ref='tags'
|
|
||||||
value={this.state.note.tags}
|
|
||||||
onChange={(e) => this.handleChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div styleName='info-right'>
|
|
||||||
<StarButton styleName='info-right-button'
|
|
||||||
onClick={(e) => this.handleStarButtonClick(e)}
|
|
||||||
isActive={note.isStarred}
|
|
||||||
/>
|
|
||||||
<button styleName='info-right-button'
|
|
||||||
onClick={(e) => this.handleShareButtonClick(e)}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<i className='fa fa-share-alt fa-fw'/>
|
|
||||||
<span styleName='info-right-button-tooltip'
|
|
||||||
style={{right: 20}}
|
|
||||||
>Share Note</span>
|
|
||||||
</button>
|
|
||||||
<button styleName='info-right-button'
|
|
||||||
onClick={(e) => this.handleContextButtonClick(e)}
|
|
||||||
>
|
|
||||||
<i className='fa fa-ellipsis-v'/>
|
|
||||||
<span styleName='info-right-button-tooltip'
|
|
||||||
style={{right: 5}}
|
|
||||||
>More Options</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='body'>
|
<div styleName='body'>
|
||||||
<div styleName='description'>
|
<div styleName='description'>
|
||||||
@@ -600,7 +630,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
<button styleName='plusButton'
|
<button styleName='plusButton'
|
||||||
onClick={(e) => this.handleTabPlusButtonClick(e)}
|
onClick={(e) => this.handleTabPlusButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-plus'/>
|
<i className='fa fa-plus' />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{viewList}
|
{viewList}
|
||||||
@@ -614,24 +644,25 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
? 'Select Syntax...'
|
? 'Select Syntax...'
|
||||||
: this.state.note.snippets[this.state.snippetIndex].mode
|
: this.state.note.snippets[this.state.snippetIndex].mode
|
||||||
}
|
}
|
||||||
<i className='fa fa-caret-down'/>
|
<i className='fa fa-caret-down' />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => this.handleIndentTypeButtonClick(e)}
|
onClick={(e) => this.handleIndentTypeButtonClick(e)}
|
||||||
>
|
>
|
||||||
Indent: {config.editor.indentType}
|
Indent: {config.editor.indentType}
|
||||||
<i className='fa fa-caret-down'/>
|
<i className='fa fa-caret-down' />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => this.handleIndentSizeButtonClick(e)}
|
onClick={(e) => this.handleIndentSizeButtonClick(e)}
|
||||||
>
|
>
|
||||||
size: {config.editor.indentSize}
|
size: {config.editor.indentSize}
|
||||||
<i className='fa fa-caret-down'/>
|
<i className='fa fa-caret-down' />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<StatusBar
|
<StatusBar
|
||||||
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
|
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
|
||||||
|
date={note.updatedAt}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,74 +1,28 @@
|
|||||||
$info-height = 75px
|
@import('NoteDetailInfo')
|
||||||
|
@import('DetailVars')
|
||||||
|
|
||||||
.root
|
.root
|
||||||
absolute top bottom right
|
absolute top bottom right
|
||||||
border-width 0 0 1px
|
border-width 0 0 1px
|
||||||
border-style solid
|
border-style solid
|
||||||
border-color $ui-borderColor
|
border-color $ui-borderColor
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
.info
|
box-shadow $note-detail-box-shadow
|
||||||
absolute top left right
|
|
||||||
height $info-height
|
|
||||||
border-bottom $ui-border
|
|
||||||
background-color $ui-backgroundColor
|
|
||||||
|
|
||||||
.info-left
|
|
||||||
float left
|
|
||||||
padding 0 5px
|
|
||||||
|
|
||||||
.info-left-top
|
|
||||||
height 40px
|
|
||||||
line-height 40px
|
|
||||||
|
|
||||||
.info-left-top-folderSelect
|
|
||||||
display inline-block
|
|
||||||
height 34px
|
|
||||||
width 200px
|
|
||||||
vertical-align middle
|
|
||||||
|
|
||||||
.info-left-bottom
|
|
||||||
height 30px
|
|
||||||
|
|
||||||
.info-left-bottom-tagSelect
|
|
||||||
height 30px
|
|
||||||
line-height 30px
|
|
||||||
|
|
||||||
.info-right
|
|
||||||
float right
|
|
||||||
|
|
||||||
.info-right-button
|
|
||||||
width 34px
|
|
||||||
height 34px
|
|
||||||
border-radius 17px
|
|
||||||
navButtonColor()
|
|
||||||
border $ui-border
|
|
||||||
font-size 14px
|
|
||||||
margin 8px 2px
|
|
||||||
padding 0
|
|
||||||
&:active
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
&:hover .info-right-button-tooltip
|
|
||||||
opacity 1
|
|
||||||
&:focus
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
.info-right-button-tooltip
|
|
||||||
tooltip()
|
|
||||||
position fixed
|
|
||||||
top 45px
|
|
||||||
padding 5px
|
|
||||||
opacity 0
|
|
||||||
|
|
||||||
.body
|
.body
|
||||||
absolute left right
|
absolute left right
|
||||||
top $info-height
|
left $note-detail-left-margin
|
||||||
|
right $note-detail-right-margin
|
||||||
|
top $info-height + $info-margin-under-border
|
||||||
bottom $statusBar-height
|
bottom $statusBar-height
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
.body .description
|
.body .description
|
||||||
absolute top left right
|
absolute top left right
|
||||||
height 80px
|
height 80px
|
||||||
border-bottom $ui-border
|
|
||||||
|
|
||||||
.body .description textarea
|
.body .description textarea
|
||||||
|
outline none
|
||||||
display block
|
display block
|
||||||
height 100%
|
height 100%
|
||||||
width 100%
|
width 100%
|
||||||
@@ -76,20 +30,19 @@ $info-height = 75px
|
|||||||
border none
|
border none
|
||||||
padding 10px
|
padding 10px
|
||||||
line-height 1.6
|
line-height 1.6
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
absolute left right
|
absolute left right
|
||||||
top 80px
|
top 80px
|
||||||
height 30px
|
height 30px
|
||||||
border-bottom $ui-border
|
|
||||||
display flex
|
display flex
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
|
||||||
.tabList .list
|
.tabList .list
|
||||||
flex 1
|
flex 1
|
||||||
display flex
|
display flex
|
||||||
overflow hidden
|
overflow hidden
|
||||||
border-right $ui-border
|
|
||||||
|
|
||||||
.tabList .plusButton
|
.tabList .plusButton
|
||||||
navButtonColor()
|
navButtonColor()
|
||||||
@@ -97,68 +50,43 @@ $info-height = 75px
|
|||||||
|
|
||||||
.tabView
|
.tabView
|
||||||
absolute left right bottom
|
absolute left right bottom
|
||||||
top 110px
|
top 130px
|
||||||
|
|
||||||
.tabView-content
|
.tabView-content
|
||||||
absolute top left right bottom
|
absolute top left right bottom
|
||||||
|
|
||||||
.override
|
.override
|
||||||
absolute bottom left
|
absolute bottom left
|
||||||
|
left 60px
|
||||||
height 23px
|
height 23px
|
||||||
z-index 1
|
z-index 1
|
||||||
button
|
button
|
||||||
navButtonColor()
|
navButtonColor()
|
||||||
height 24px
|
height 24px
|
||||||
border-width 0 1px 0 0
|
|
||||||
border-style solid
|
|
||||||
border-color $ui-borderColor
|
|
||||||
&:active .update-icon
|
&:active .update-icon
|
||||||
color white
|
color white
|
||||||
|
|
||||||
|
.control-fullScreenButton
|
||||||
|
float right
|
||||||
|
padding 0 0 2px 0
|
||||||
|
topBarButtonLight()
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
box-shadow none
|
||||||
|
|
||||||
.info
|
.body
|
||||||
border-bottom-color $ui-dark-borderColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
background-color $ui-dark-backgroundColor
|
|
||||||
|
|
||||||
.info-delete
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.info-delete-confirmButton
|
|
||||||
colorDarkDangerButton()
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.info-delete-cancelButton
|
|
||||||
colorDarkDefaultButton()
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.info-right-button
|
|
||||||
navDarkButtonColor()
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
&:active
|
|
||||||
border-color $ui-dark-button--focus-borderColor
|
|
||||||
&:hover .info-right-button-tooltip
|
|
||||||
opacity 1
|
|
||||||
&:focus
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
|
|
||||||
.info-right-button-tooltip
|
|
||||||
darkTooltip()
|
|
||||||
|
|
||||||
.body .description
|
|
||||||
border-bottom-color $ui-dark-borderColor
|
|
||||||
|
|
||||||
.body .description textarea
|
.body .description textarea
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
color white
|
color $ui-dark-text-color
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
background-color $ui-button--active-backgroundColor
|
background-color $ui-button--active-backgroundColor
|
||||||
border-bottom-color $ui-dark-borderColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
background-color $ui-dark-backgroundColor
|
|
||||||
|
|
||||||
.tabList .list
|
.tabList .list
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
@@ -168,3 +96,10 @@ body[data-theme="dark"]
|
|||||||
.override
|
.override
|
||||||
button
|
button
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.control-fullScreenButton
|
||||||
|
topBarButtonDark()
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ class StarButton extends React.Component {
|
|||||||
: 'fa fa-star-o'
|
: 'fa fa-star-o'
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<span styleName='tooltip'>Star Note</span>
|
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,47 @@
|
|||||||
.root
|
.root
|
||||||
position relative
|
left 7px
|
||||||
|
top 0
|
||||||
padding 0
|
padding 0
|
||||||
|
color alpha($ui-favorite-star-button-color, 60%)
|
||||||
&:hover
|
&:hover
|
||||||
.icon
|
transition 0.15s
|
||||||
transform rotate(-72deg)
|
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||||
.tooltip
|
color $ui-favorite-star-button-color
|
||||||
opacity 1
|
&:active
|
||||||
|
transition 0.15s
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
|
||||||
.root--active
|
.root--active
|
||||||
@extend .root
|
@extend .root
|
||||||
color $brand-color
|
color $ui-favorite-star-button-color
|
||||||
&:hover
|
&:hover
|
||||||
color $brand-color !important
|
transition 0.15s
|
||||||
.icon
|
color $ui-favorite-star-button-color
|
||||||
transform rotate(-72deg)
|
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||||
|
&:active
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||||
|
|
||||||
.icon
|
.icon
|
||||||
transition transform 0.15s
|
transition transform 0.15s
|
||||||
|
|
||||||
.tooltip
|
body[data-theme="dark"]
|
||||||
tooltip()
|
.root
|
||||||
position fixed
|
&:hover
|
||||||
top 45px
|
transition 0.15s
|
||||||
right 65px
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
padding 5px
|
color $ui-favorite-star-button-color
|
||||||
opacity 0
|
&:active
|
||||||
border-radius 2px
|
transition 0.15s
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
|
||||||
|
.root--active
|
||||||
|
@extend .root
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
&:hover
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-favorite-star-button-color
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react'
|
|||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './TagSelect.styl'
|
import styles from './TagSelect.styl'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
|
||||||
class TagSelect extends React.Component {
|
class TagSelect extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -56,6 +57,7 @@ class TagSelect extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submitTag () {
|
submitTag () {
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_TAG')
|
||||||
let { value } = this.props
|
let { value } = this.props
|
||||||
let newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
|
let newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
|
||||||
|
|
||||||
@@ -107,12 +109,12 @@ class TagSelect extends React.Component {
|
|||||||
<span styleName='tag'
|
<span styleName='tag'
|
||||||
key={tag}
|
key={tag}
|
||||||
>
|
>
|
||||||
|
<span styleName='tag-label'>#{tag}</span>
|
||||||
<button styleName='tag-removeButton'
|
<button styleName='tag-removeButton'
|
||||||
onClick={(e) => this.handleTagRemoveButtonClick(tag)(e)}
|
onClick={(e) => this.handleTagRemoveButtonClick(tag)(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-times fa-fw'/>
|
<i className='fa fa-times fa-fw tag-removeButton-icon' />
|
||||||
</button>
|
</button>
|
||||||
<span styleName='tag-label'>{tag}</span>
|
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -125,10 +127,7 @@ class TagSelect extends React.Component {
|
|||||||
}
|
}
|
||||||
styleName='root'
|
styleName='root'
|
||||||
>
|
>
|
||||||
<i styleName='icon'
|
{tagList}
|
||||||
className='fa fa-tags'
|
|
||||||
/>
|
|
||||||
{tagList}
|
|
||||||
<input styleName='newTag'
|
<input styleName='newTag'
|
||||||
ref='newTag'
|
ref='newTag'
|
||||||
value={this.state.newTag}
|
value={this.state.newTag}
|
||||||
@@ -136,7 +135,6 @@ class TagSelect extends React.Component {
|
|||||||
onChange={(e) => this.handleNewTagInputChange(e)}
|
onChange={(e) => this.handleNewTagInputChange(e)}
|
||||||
onKeyDown={(e) => this.handleNewTagInputKeyDown(e)}
|
onKeyDown={(e) => this.handleNewTagInputKeyDown(e)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
.root
|
.root
|
||||||
position relative
|
|
||||||
user-select none
|
|
||||||
|
|
||||||
.icon
|
|
||||||
display inline-block
|
display inline-block
|
||||||
width 30px
|
user-select none
|
||||||
|
height 23px
|
||||||
vertical-align middle
|
vertical-align middle
|
||||||
text-align center
|
width 300px
|
||||||
color $ui-button-color
|
overflow-x scroll
|
||||||
|
white-space nowrap
|
||||||
|
|
||||||
|
.root::-webkit-scrollbar
|
||||||
|
display none
|
||||||
|
|
||||||
.tag
|
.tag
|
||||||
display inline-block
|
display inline-block
|
||||||
margin 0 2px
|
margin 1px 3px
|
||||||
vertical-align middle
|
padding 0
|
||||||
height 20px
|
height 20px
|
||||||
background-color white
|
background-color alpha($ui-tag-backgroundColor, 10%)
|
||||||
border-radius 3px
|
border-radius 3px
|
||||||
overflow hidden
|
overflow hidden
|
||||||
clearfix()
|
clearfix()
|
||||||
|
|
||||||
.tag-removeButton
|
.tag-removeButton
|
||||||
float left
|
float right
|
||||||
height 20px
|
height 20px
|
||||||
width 18px
|
width 18px
|
||||||
margin 0
|
margin 0
|
||||||
padding 0
|
padding 0
|
||||||
border-style solid
|
border-style solid
|
||||||
border-color $ui-button--focus-borderColor
|
border-width 0
|
||||||
border-width 0 0 0 3px
|
border-radius 20px
|
||||||
line-height 18px
|
line-height 18px
|
||||||
background-color transparent
|
background-color transparent
|
||||||
color $ui-button-color
|
color $ui-button-color
|
||||||
&:hover
|
|
||||||
background-color $ui-button--hover-backgroundColor
|
.tag-removeButton-icon
|
||||||
&:active, &:active:hover
|
width 5px
|
||||||
color $ui-button--active-color
|
padding-right 4px
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
&:focus
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
|
|
||||||
.tag-label
|
.tag-label
|
||||||
|
font-size 11px
|
||||||
|
font-weight 600
|
||||||
|
color: alpha($ui-text-color, 80%)
|
||||||
float left
|
float left
|
||||||
height 20px
|
height 20px
|
||||||
line-height 20px
|
line-height 20px
|
||||||
@@ -48,26 +48,18 @@
|
|||||||
|
|
||||||
.newTag
|
.newTag
|
||||||
display inline-block
|
display inline-block
|
||||||
margin 0 2px
|
margin 2px 0 15px 2px
|
||||||
vertical-align middle
|
vertical-align middle
|
||||||
height 24px
|
height 18px
|
||||||
box-sizing borde-box
|
box-sizing borde-box
|
||||||
border none
|
border none
|
||||||
border-bottom $ui-border
|
|
||||||
background-color transparent
|
background-color transparent
|
||||||
outline none
|
outline none
|
||||||
padding 0 4px
|
padding 0 4px
|
||||||
&:focus
|
|
||||||
border-color $ui-input--focus-borderColor = #369DCD
|
|
||||||
&:disabled
|
|
||||||
background-color $ui-input--disabled-backgroundColor = #DDD
|
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.icon
|
|
||||||
color $ui-dark-button-color
|
|
||||||
|
|
||||||
.tag
|
.tag
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color alpha($ui-dark-tag-backgroundColor, 60%)
|
||||||
|
|
||||||
.tag-removeButton
|
.tag-removeButton
|
||||||
border-color $ui-button--focus-borderColor
|
border-color $ui-button--focus-borderColor
|
||||||
@@ -78,10 +70,6 @@ body[data-theme="dark"]
|
|||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|
||||||
.newTag
|
.newTag
|
||||||
border-color $ui-dark-borderColor
|
border-color none
|
||||||
background-color transparent
|
background-color transparent
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
&:focus
|
|
||||||
border-color $ui-input--focus-borderColor = #369DCD
|
|
||||||
&:disabled
|
|
||||||
background-color $ui-input--disabled-backgroundColor = #DDD
|
|
||||||
19
browser/main/Detail/TrashButton.js
Normal file
19
browser/main/Detail/TrashButton.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './TrashButton.styl'
|
||||||
|
|
||||||
|
const TrashButton = ({
|
||||||
|
onClick
|
||||||
|
}) => (
|
||||||
|
<button styleName='control-trashButton'
|
||||||
|
onClick={(e) => onClick(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-trash trashButton' styleName='info-button' />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
|
||||||
|
TrashButton.propTypes = {
|
||||||
|
onClick: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(TrashButton, styles)
|
||||||
11
browser/main/Detail/TrashButton.styl
Normal file
11
browser/main/Detail/TrashButton.styl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.control-trashButton
|
||||||
|
float right
|
||||||
|
topBarButtonLight()
|
||||||
|
|
||||||
|
.trashButton
|
||||||
|
padding 0px
|
||||||
|
margin 15px 0
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.control-trashButton
|
||||||
|
topBarButtonDark()
|
||||||
@@ -17,7 +17,7 @@ class Detail extends React.Component {
|
|||||||
this.refs.root != null && this.refs.root.focus()
|
this.refs.root != null && this.refs.root.focus()
|
||||||
}
|
}
|
||||||
this.deleteHandler = () => {
|
this.deleteHandler = () => {
|
||||||
this.refs.root != null && this.refs.root.handleDeleteMenuClick()
|
this.refs.root != null && this.refs.root.handleTrashButtonClick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import ConfigManager from 'browser/main/lib/ConfigManager'
|
|||||||
import modal from 'browser/main/lib/modal'
|
import modal from 'browser/main/lib/modal'
|
||||||
import InitModal from 'browser/main/modals/InitModal'
|
import InitModal from 'browser/main/modals/InitModal'
|
||||||
import mixpanel from 'browser/main/lib/mixpanel'
|
import mixpanel from 'browser/main/lib/mixpanel'
|
||||||
|
import mobileAnalytics from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
function focused () {
|
function focused () {
|
||||||
mixpanel.track('MAIN_FOCUSED')
|
mixpanel.track('MAIN_FOCUSED')
|
||||||
@@ -21,14 +23,23 @@ class Main extends React.Component {
|
|||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
mobileAnalytics.initAwsMobileAnalytics()
|
||||||
|
}
|
||||||
|
|
||||||
let { config } = props
|
let { config } = props
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isRightSliderFocused: false,
|
isRightSliderFocused: false,
|
||||||
listWidth: config.listWidth,
|
listWidth: config.listWidth,
|
||||||
navWidth: config.listWidth,
|
navWidth: config.navWidth,
|
||||||
isLeftSliderFocused: false
|
isLeftSliderFocused: false,
|
||||||
|
fullScreen: false,
|
||||||
|
noteDetailWidth: 0,
|
||||||
|
mainBodyWidth: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.toggleFullScreen = () => this.handleFullScreenButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildContext () {
|
getChildContext () {
|
||||||
@@ -63,11 +74,13 @@ class Main extends React.Component {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
eventEmitter.on('editor:fullscreen', this.toggleFullScreen)
|
||||||
window.addEventListener('focus', focused)
|
window.addEventListener('focus', focused)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
window.removeEventListener('focus', focused)
|
window.removeEventListener('focus', focused)
|
||||||
|
eventEmitter.off('editor:fullscreen', this.toggleFullScreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeftSlideMouseDown (e) {
|
handleLeftSlideMouseDown (e) {
|
||||||
@@ -85,6 +98,7 @@ class Main extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleMouseUp (e) {
|
handleMouseUp (e) {
|
||||||
|
// Change width of NoteList component.
|
||||||
if (this.state.isRightSliderFocused) {
|
if (this.state.isRightSliderFocused) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isRightSliderFocused: false
|
isRightSliderFocused: false
|
||||||
@@ -99,6 +113,8 @@ class Main extends React.Component {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Change width of SideNav component.
|
||||||
if (this.state.isLeftSliderFocused) {
|
if (this.state.isLeftSliderFocused) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isLeftSliderFocused: false
|
isLeftSliderFocused: false
|
||||||
@@ -106,10 +122,10 @@ class Main extends React.Component {
|
|||||||
let { dispatch } = this.props
|
let { dispatch } = this.props
|
||||||
let navWidth = this.state.navWidth
|
let navWidth = this.state.navWidth
|
||||||
// TODO: ConfigManager should dispatch itself.
|
// TODO: ConfigManager should dispatch itself.
|
||||||
ConfigManager.set({listWidth: navWidth})
|
ConfigManager.set({ navWidth })
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_NAV_WIDTH',
|
type: 'SET_NAV_WIDTH',
|
||||||
listWidth: navWidth
|
navWidth
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -141,6 +157,34 @@ class Main extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleFullScreenButton (e) {
|
||||||
|
this.setState({ fullScreen: !this.state.fullScreen }, () => {
|
||||||
|
const noteDetail = document.querySelector('.NoteDetail')
|
||||||
|
const noteList = document.querySelector('.NoteList')
|
||||||
|
const mainBody = document.querySelector('#main-body')
|
||||||
|
|
||||||
|
if (this.state.fullScreen) {
|
||||||
|
this.hideLeftLists(noteDetail, noteList, mainBody)
|
||||||
|
} else {
|
||||||
|
this.showLeftLists(noteDetail, noteList, mainBody)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
hideLeftLists (noteDetail, noteList, mainBody) {
|
||||||
|
this.state.noteDetailWidth = noteDetail.style.left
|
||||||
|
this.state.mainBodyWidth = mainBody.style.left
|
||||||
|
noteDetail.style.left = '0px'
|
||||||
|
mainBody.style.left = '0px'
|
||||||
|
noteList.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
showLeftLists (noteDetail, noteList, mainBody) {
|
||||||
|
noteDetail.style.left = this.state.noteDetailWidth
|
||||||
|
mainBody.style.left = this.state.mainBodyWidth
|
||||||
|
noteList.style.display = 'inline'
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { config } = this.props
|
let { config } = this.props
|
||||||
|
|
||||||
@@ -162,14 +206,15 @@ class Main extends React.Component {
|
|||||||
/>
|
/>
|
||||||
{!config.isSideNavFolded &&
|
{!config.isSideNavFolded &&
|
||||||
<div styleName={this.state.isLeftSliderFocused ? 'slider--active' : 'slider'}
|
<div styleName={this.state.isLeftSliderFocused ? 'slider--active' : 'slider'}
|
||||||
style={{left: this.state.navWidth - 1}}
|
style={{left: this.state.navWidth}}
|
||||||
onMouseDown={(e) => this.handleLeftSlideMouseDown(e)}
|
onMouseDown={(e) => this.handleLeftSlideMouseDown(e)}
|
||||||
draggable='false'
|
draggable='false'
|
||||||
>
|
>
|
||||||
<div styleName='slider-hitbox'/>
|
<div styleName='slider-hitbox' />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div styleName={config.isSideNavFolded ? 'body--expanded' : 'body'}
|
<div styleName={config.isSideNavFolded ? 'body--expanded' : 'body'}
|
||||||
|
id='main-body'
|
||||||
ref='body'
|
ref='body'
|
||||||
style={{left: config.isSideNavFolded ? 44 : this.state.navWidth}}
|
style={{left: config.isSideNavFolded ? 44 : this.state.navWidth}}
|
||||||
>
|
>
|
||||||
@@ -191,15 +236,15 @@ class Main extends React.Component {
|
|||||||
'location'
|
'location'
|
||||||
])}
|
])}
|
||||||
/>
|
/>
|
||||||
<div styleName={this.state.isRightSliderFocused ? 'slider--active' : 'slider'}
|
<div styleName={this.state.isRightSliderFocused ? 'slider-right--active' : 'slider-right'}
|
||||||
style={{left: this.state.listWidth}}
|
style={{left: this.state.listWidth - 1}}
|
||||||
onMouseDown={(e) => this.handleRightSlideMouseDown(e)}
|
onMouseDown={(e) => this.handleRightSlideMouseDown(e)}
|
||||||
draggable='false'
|
draggable='false'
|
||||||
>
|
>
|
||||||
<div styleName='slider-hitbox' />
|
<div styleName='slider-hitbox' />
|
||||||
</div>
|
</div>
|
||||||
<Detail
|
<Detail
|
||||||
style={{left: this.state.listWidth + 1}}
|
style={{left: this.state.listWidth}}
|
||||||
{..._.pick(this.props, [
|
{..._.pick(this.props, [
|
||||||
'dispatch',
|
'dispatch',
|
||||||
'data',
|
'data',
|
||||||
|
|||||||
@@ -11,15 +11,20 @@
|
|||||||
|
|
||||||
.slider
|
.slider
|
||||||
absolute top bottom
|
absolute top bottom
|
||||||
|
top -2px
|
||||||
|
width 0
|
||||||
|
z-index 0
|
||||||
|
|
||||||
|
.slider-right
|
||||||
|
@extend .slider
|
||||||
width 1px
|
width 1px
|
||||||
background-color $ui-borderColor
|
z-index 0
|
||||||
border-width 0
|
|
||||||
border-style solid
|
|
||||||
border-color $ui-borderColor
|
|
||||||
|
|
||||||
.slider--active
|
.slider--active
|
||||||
@extend .slider
|
@extend .slider
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
|
.slider-right--active
|
||||||
|
@extend .slider-right
|
||||||
|
|
||||||
.slider-hitbox
|
.slider-hitbox
|
||||||
absolute top bottom left right
|
absolute top bottom left right
|
||||||
@@ -33,9 +38,6 @@ body[data-theme="dark"]
|
|||||||
.root
|
.root
|
||||||
absolute top left bottom right
|
absolute top left bottom right
|
||||||
|
|
||||||
.slider
|
.slider-right
|
||||||
background-color $ui-dark-borderColor
|
.slider-right--active
|
||||||
border-color $ui-dark-borderColor
|
box-shadow none
|
||||||
|
|
||||||
.slider--active
|
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
import React, { PropTypes } from 'react'
|
|
||||||
import ReactDOM from 'react-dom'
|
|
||||||
import api from 'browser/lib/api'
|
|
||||||
import clientKey from 'browser/lib/clientKey'
|
|
||||||
import activityRecord from 'browser/lib/activityRecord'
|
|
||||||
const clipboard = require('electron').clipboard
|
|
||||||
|
|
||||||
function notify (...args) {
|
|
||||||
return new window.Notification(...args)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDefault () {
|
|
||||||
return {
|
|
||||||
openDropdown: false,
|
|
||||||
isSharing: false,
|
|
||||||
// Fetched url
|
|
||||||
url: null,
|
|
||||||
// for tooltip Copy -> Copied!
|
|
||||||
copied: false,
|
|
||||||
failed: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ShareButton extends React.Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
this.state = getDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
|
||||||
this.setState(getDefault())
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
this.dropdownInterceptor = e => {
|
|
||||||
this.dropdownClicked = true
|
|
||||||
}
|
|
||||||
ReactDOM.findDOMNode(this.refs.dropdown).addEventListener('click', this.dropdownInterceptor)
|
|
||||||
this.shareViaPublicURLHandler = e => {
|
|
||||||
this.handleShareViaPublicURLClick(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
document.removeEventListener('click', this.dropdownHandler)
|
|
||||||
ReactDOM.findDOMNode(this.refs.dropdown).removeEventListener('click', this.dropdownInterceptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOpenButtonClick (e) {
|
|
||||||
this.openDropdown()
|
|
||||||
if (this.dropdownHandler == null) {
|
|
||||||
this.dropdownHandler = e => {
|
|
||||||
if (!this.dropdownClicked) {
|
|
||||||
this.closeDropdown()
|
|
||||||
} else {
|
|
||||||
this.dropdownClicked = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.removeEventListener('click', this.dropdownHandler)
|
|
||||||
document.addEventListener('click', this.dropdownHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
openDropdown () {
|
|
||||||
this.setState({openDropdown: true})
|
|
||||||
}
|
|
||||||
|
|
||||||
closeDropdown () {
|
|
||||||
document.removeEventListener('click', this.dropdownHandler)
|
|
||||||
this.setState({openDropdown: false})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClipboardButtonClick (e) {
|
|
||||||
activityRecord.emit('MAIN_DETAIL_COPY')
|
|
||||||
clipboard.writeText(this.props.article.content)
|
|
||||||
notify('Saved to Clipboard!', {
|
|
||||||
body: 'Paste it wherever you want!'
|
|
||||||
})
|
|
||||||
this.setState({openDropdown: false})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleShareViaPublicURLClick (e) {
|
|
||||||
let { user } = this.props
|
|
||||||
let input = Object.assign({}, this.props.article, {
|
|
||||||
clientKey: clientKey.get(),
|
|
||||||
writerName: user.name
|
|
||||||
})
|
|
||||||
this.setState({
|
|
||||||
isSharing: true,
|
|
||||||
failed: false
|
|
||||||
}, () => {
|
|
||||||
api.shareViaPublicURL(input)
|
|
||||||
.then(res => {
|
|
||||||
let url = res.body.url
|
|
||||||
this.setState({url: url, isSharing: false})
|
|
||||||
activityRecord.emit('ARTICLE_SHARE')
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log(err)
|
|
||||||
this.setState({isSharing: false, failed: true})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCopyURLClick () {
|
|
||||||
clipboard.writeText(this.state.url)
|
|
||||||
this.setState({copied: true})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore copy url tooltip
|
|
||||||
handleCopyURLMouseLeave () {
|
|
||||||
this.setState({copied: false})
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
let hasPublicURL = this.state.url != null
|
|
||||||
return (
|
|
||||||
<div className='ShareButton'>
|
|
||||||
<button ref='openButton' onClick={e => this.handleOpenButtonClick(e)} className='ShareButton-open-button'>
|
|
||||||
<i className='fa fa-fw fa-share-alt'/>
|
|
||||||
{
|
|
||||||
this.state.openDropdown ? null : (
|
|
||||||
<span className='tooltip'>Share</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</button>
|
|
||||||
<div ref='dropdown' className={'ShareButton-dropdown' + (this.state.openDropdown ? '' : ' hide')}>
|
|
||||||
{
|
|
||||||
!hasPublicURL ? (
|
|
||||||
<button
|
|
||||||
onClick={e => this.shareViaPublicURLHandler(e)}
|
|
||||||
ref='sharePublicURL'
|
|
||||||
disabled={this.state.isSharing}>
|
|
||||||
<i className='fa fa-fw fa-external-link'/> {this.state.failed ? 'Failed : Click to Try again' : !this.state.isSharing ? 'Share via public URL' : 'Sharing...'}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<div className='ShareButton-url'>
|
|
||||||
<input className='ShareButton-url-input' value={this.state.url} readOnly/>
|
|
||||||
<button
|
|
||||||
onClick={e => this.handleCopyURLClick(e)}
|
|
||||||
className='ShareButton-url-button'
|
|
||||||
onMouseLeave={e => this.handleCopyURLMouseLeave(e)}
|
|
||||||
>
|
|
||||||
<i className='fa fa-fw fa-clipboard'/>
|
|
||||||
<div className='ShareButton-url-button-tooltip'>{this.state.copied ? 'Copied!' : 'Copy URL'}</div>
|
|
||||||
</button>
|
|
||||||
<div className='ShareButton-url-alert'>This url is valid for 7 days.</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<button onClick={e => this.handleClipboardButtonClick(e)}>
|
|
||||||
<i className='fa fa-fw fa-clipboard'/> Copy to clipboard
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShareButton.propTypes = {
|
|
||||||
article: PropTypes.shape({
|
|
||||||
publicURL: PropTypes.string,
|
|
||||||
content: PropTypes.string
|
|
||||||
}),
|
|
||||||
user: PropTypes.shape({
|
|
||||||
name: PropTypes.string
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,225 +1,84 @@
|
|||||||
|
$control-height = 30px
|
||||||
|
|
||||||
.root
|
.root
|
||||||
absolute left bottom
|
absolute left bottom
|
||||||
border-top $ui-border
|
|
||||||
top $topBar-height - 1
|
top $topBar-height - 1
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
|
|
||||||
.control
|
.control
|
||||||
absolute top left right
|
absolute top left right
|
||||||
user-select none
|
user-select none
|
||||||
height 25px
|
height $control-height
|
||||||
font-size 10px
|
font-size 12px
|
||||||
border-bottom $ui-border
|
|
||||||
line-height 25px
|
line-height 25px
|
||||||
display flex
|
display flex
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-noteList-backgroundColor
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
.control-sortBy
|
.control-sortBy
|
||||||
flex 1
|
flex 1
|
||||||
padding-left 5px
|
padding-left 22px
|
||||||
|
|
||||||
.control-sortBy-select
|
.control-sortBy-select
|
||||||
margin-left 5px
|
appearance: none;
|
||||||
|
margin-left 3px
|
||||||
|
color $ui-inactive-text-color
|
||||||
padding 0
|
padding 0
|
||||||
border none
|
border none
|
||||||
background-color transparent
|
background-color transparent
|
||||||
font-size 10px
|
outline none
|
||||||
|
cursor pointer
|
||||||
|
font-size 11px
|
||||||
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.control-button-area
|
||||||
|
margin-right 12px
|
||||||
|
|
||||||
.control-button
|
.control-button
|
||||||
width 25px
|
width 25px
|
||||||
padding 0
|
padding 0
|
||||||
background-color transparent
|
background-color transparent
|
||||||
border none
|
border none
|
||||||
color $ui-inactive-text-color
|
color alpha($ui-inactive-text-color, 60%)
|
||||||
transition 0.15s
|
transition 0.15s
|
||||||
&:active, &:active:hover
|
&:active, &:active:hover
|
||||||
color $ui-active-color
|
color $ui-inactive-text-color
|
||||||
&:hover
|
&:hover
|
||||||
color $ui-text-color
|
color $ui-inactive-text-color
|
||||||
.control-button-tooltip
|
|
||||||
opacity 1
|
|
||||||
|
|
||||||
.control-button--active
|
.control-button--active
|
||||||
@extend .control-button
|
@extend .control-button
|
||||||
color $ui-active-color
|
color $ui-inactive-text-color
|
||||||
&:hover
|
&:hover
|
||||||
color $ui-active-color
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
.control-button-tooltip
|
|
||||||
tooltip()
|
|
||||||
position absolute
|
|
||||||
top 20px
|
|
||||||
right 5px
|
|
||||||
padding 5px
|
|
||||||
opacity 0
|
|
||||||
white-space nowrap
|
|
||||||
border-radius 2px
|
|
||||||
z-index 1
|
|
||||||
|
|
||||||
.list
|
.list
|
||||||
absolute left right bottom
|
absolute left right bottom
|
||||||
top 24px
|
top $control-height
|
||||||
overflow auto
|
overflow auto
|
||||||
|
|
||||||
.item
|
|
||||||
position relative
|
|
||||||
border-bottom $ui-border
|
|
||||||
padding 2px 5px
|
|
||||||
user-select none
|
|
||||||
cursor pointer
|
|
||||||
transition background-color 0.15s
|
|
||||||
&:hover
|
|
||||||
background-color alpha($ui-active-color, 20%)
|
|
||||||
&:active
|
|
||||||
background-color $ui-active-color
|
|
||||||
color white
|
|
||||||
.item-title
|
|
||||||
.item-title-empty
|
|
||||||
.item-title-icon
|
|
||||||
.item-bottom-tagIcon
|
|
||||||
.item-bottom-tagList-empty
|
|
||||||
.item-bottom-time
|
|
||||||
color white
|
|
||||||
.item-bottom-tagList-item
|
|
||||||
background-color transparent
|
|
||||||
color white
|
|
||||||
|
|
||||||
.item--active
|
|
||||||
@extend .item
|
|
||||||
background-color $ui-active-color
|
|
||||||
color white
|
|
||||||
.item-title
|
|
||||||
.item-title-empty
|
|
||||||
.item-title-icon
|
|
||||||
.item-bottom-tagIcon
|
|
||||||
.item-bottom-tagList-empty
|
|
||||||
.item-bottom-time
|
|
||||||
color white
|
|
||||||
.item-bottom-tagList-item
|
|
||||||
background-color transparent
|
|
||||||
color white
|
|
||||||
&:hover
|
|
||||||
background-color $ui-active-color
|
|
||||||
|
|
||||||
.item-border
|
|
||||||
absolute top bottom left right
|
|
||||||
border-style solid
|
|
||||||
border-width 2px
|
|
||||||
border-color transparent
|
|
||||||
transition 0.15s
|
|
||||||
|
|
||||||
.item-title
|
|
||||||
height 24px
|
|
||||||
box-sizing border-box
|
|
||||||
line-height 24px
|
|
||||||
padding 0
|
|
||||||
overflow ellipsis
|
|
||||||
color $ui-text-color
|
|
||||||
|
|
||||||
.item-title-icon
|
|
||||||
font-size 12px
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
padding-right 3px
|
|
||||||
|
|
||||||
.item-title-empty
|
|
||||||
font-weight normal
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
|
|
||||||
.item-bottom
|
|
||||||
margin-top 2px
|
|
||||||
height 20px
|
|
||||||
font-size 12px
|
|
||||||
line-height 20px
|
|
||||||
overflow ellipsis
|
|
||||||
display flex
|
|
||||||
|
|
||||||
.item-bottom-tagIcon
|
|
||||||
vertical-align middle
|
|
||||||
color $ui-button-color
|
|
||||||
height 20px
|
|
||||||
line-height 20px
|
|
||||||
|
|
||||||
.item-bottom-tagList
|
|
||||||
flex 1
|
|
||||||
overflow ellipsis
|
|
||||||
line-height 20px
|
|
||||||
|
|
||||||
.item-bottom-tagList-item
|
|
||||||
margin 0 4px
|
|
||||||
padding 0 4px
|
|
||||||
height 20px
|
|
||||||
box-sizing border-box
|
|
||||||
border-radius 3px
|
|
||||||
vertical-align middle
|
|
||||||
border-style solid
|
|
||||||
border-color $ui-button--focus-borderColor
|
|
||||||
border-width 0 0 0 3px
|
|
||||||
background-color $ui-backgroundColor
|
|
||||||
color $ui-text-color
|
|
||||||
|
|
||||||
.item-bottom-tagList-empty
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
vertical-align middle
|
|
||||||
font-size 10px
|
|
||||||
margin-left 5px
|
|
||||||
|
|
||||||
.item-bottom-time
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
margin-left 5px
|
|
||||||
font-size 10px
|
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
background-color $ui-dark-backgroundColor
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
|
||||||
.item
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
&:hover
|
|
||||||
background-color alpha($ui-active-color, 20%)
|
|
||||||
|
|
||||||
.item--active
|
|
||||||
@extend .item
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
.item-title
|
|
||||||
color white
|
|
||||||
.item-bottom-tagList-item
|
|
||||||
background-color transparent
|
|
||||||
color white
|
|
||||||
.item-bottom-tagList-empty
|
|
||||||
color white
|
|
||||||
&:hover
|
|
||||||
background-color $ui-active-color
|
|
||||||
|
|
||||||
.item-title
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.item-title-icon
|
|
||||||
color $ui-darkinactive-text-color
|
|
||||||
|
|
||||||
.item-title-empty
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|
||||||
.item-bottom-tagIcon
|
|
||||||
color $ui-dark-button-color
|
|
||||||
|
|
||||||
.item-bottom-tagList-item
|
|
||||||
border-color $ui-dark-button--focus-borderColor
|
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
|
||||||
color $ui-dark-text-color
|
|
||||||
|
|
||||||
.item-bottom-tagList-empty
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
vertical-align middle
|
|
||||||
|
|
||||||
.control
|
.control
|
||||||
background-color $ui-dark-backgroundColor
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
|
||||||
.control-sortBy-select
|
.control-sortBy-select
|
||||||
color $ui-dark-text-color
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
.control-button
|
.control-button
|
||||||
color $ui-dark-inactive-text-color
|
color $ui-dark-inactive-text-color
|
||||||
&:hover
|
&:hover
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.control-button--active
|
||||||
|
color $ui-dark-text-color
|
||||||
|
&:active
|
||||||
|
color $ui-dark-text-color
|
||||||
@@ -6,6 +6,13 @@ import _ from 'lodash'
|
|||||||
import ee from 'browser/main/lib/eventEmitter'
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
|
import NoteItem from 'browser/components/NoteItem'
|
||||||
|
import NoteItemSimple from 'browser/components/NoteItemSimple'
|
||||||
|
import searchFromNotes from 'browser/lib/search'
|
||||||
|
import fs from 'fs'
|
||||||
|
import { hashHistory } from 'react-router'
|
||||||
|
import markdown from 'browser/lib/markdown'
|
||||||
|
import { findNoteTitle } from 'browser/lib/findNoteTitle'
|
||||||
|
|
||||||
const { remote } = require('electron')
|
const { remote } = require('electron')
|
||||||
const { Menu, MenuItem, dialog } = remote
|
const { Menu, MenuItem, dialog } = remote
|
||||||
@@ -36,6 +43,11 @@ class NoteList extends React.Component {
|
|||||||
this.focusHandler = () => {
|
this.focusHandler = () => {
|
||||||
this.refs.list.focus()
|
this.refs.list.focus()
|
||||||
}
|
}
|
||||||
|
this.alertIfSnippetHandler = () => {
|
||||||
|
this.alertIfSnippet()
|
||||||
|
}
|
||||||
|
this.importFromFileHandler = this.importFromFile.bind(this)
|
||||||
|
this.jumpNoteByHash = this.jumpNoteByHashHandler.bind(this)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
}
|
}
|
||||||
@@ -46,6 +58,9 @@ class NoteList extends React.Component {
|
|||||||
ee.on('list:next', this.selectNextNoteHandler)
|
ee.on('list:next', this.selectNextNoteHandler)
|
||||||
ee.on('list:prior', this.selectPriorNoteHandler)
|
ee.on('list:prior', this.selectPriorNoteHandler)
|
||||||
ee.on('list:focus', this.focusHandler)
|
ee.on('list:focus', this.focusHandler)
|
||||||
|
ee.on('list:isMarkdownNote', this.alertIfSnippetHandler)
|
||||||
|
ee.on('import:file', this.importFromFileHandler)
|
||||||
|
ee.on('list:jump', this.jumpNoteByHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
@@ -64,6 +79,9 @@ class NoteList extends React.Component {
|
|||||||
ee.off('list:next', this.selectNextNoteHandler)
|
ee.off('list:next', this.selectNextNoteHandler)
|
||||||
ee.off('list:prior', this.selectPriorNoteHandler)
|
ee.off('list:prior', this.selectPriorNoteHandler)
|
||||||
ee.off('list:focus', this.focusHandler)
|
ee.off('list:focus', this.focusHandler)
|
||||||
|
ee.off('list:isMarkdownNote', this.alertIfSnippetHandler)
|
||||||
|
ee.off('import:file', this.importFromFileHandler)
|
||||||
|
ee.off('list:jump', this.jumpNoteByHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
@@ -156,6 +174,31 @@ class NoteList extends React.Component {
|
|||||||
ee.emit('list:moved')
|
ee.emit('list:moved')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jumpNoteByHashHandler (event, noteHash) {
|
||||||
|
// first argument event isn't used.
|
||||||
|
if (this.notes === null || this.notes.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { router } = this.context
|
||||||
|
const { location } = this.props
|
||||||
|
|
||||||
|
let targetIndex = _.findIndex(this.notes, (note) => {
|
||||||
|
return note.storage + '-' + note.key === noteHash
|
||||||
|
})
|
||||||
|
|
||||||
|
if (targetIndex < 0) targetIndex = 0
|
||||||
|
|
||||||
|
router.push({
|
||||||
|
pathname: location.pathname,
|
||||||
|
query: {
|
||||||
|
key: this.notes[targetIndex].storage + '-' + this.notes[targetIndex].key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ee.emit('list:moved')
|
||||||
|
}
|
||||||
|
|
||||||
handleNoteListKeyDown (e) {
|
handleNoteListKeyDown (e) {
|
||||||
if (e.metaKey || e.ctrlKey) return true
|
if (e.metaKey || e.ctrlKey) return true
|
||||||
|
|
||||||
@@ -187,6 +230,7 @@ class NoteList extends React.Component {
|
|||||||
|
|
||||||
getNotes () {
|
getNotes () {
|
||||||
let { data, params, location } = this.props
|
let { data, params, location } = this.props
|
||||||
|
let { router } = this.context
|
||||||
|
|
||||||
if (location.pathname.match(/\/home/)) {
|
if (location.pathname.match(/\/home/)) {
|
||||||
return data.noteMap.map((note) => note)
|
return data.noteMap.map((note) => note)
|
||||||
@@ -197,6 +241,19 @@ class NoteList extends React.Component {
|
|||||||
.map((uniqueKey) => data.noteMap.get(uniqueKey))
|
.map((uniqueKey) => data.noteMap.get(uniqueKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (location.pathname.match(/\/searched/)) {
|
||||||
|
const searchInputText = document.getElementsByClassName('searchInput')[0].value
|
||||||
|
if (searchInputText === '') {
|
||||||
|
router.push('/home')
|
||||||
|
}
|
||||||
|
return searchFromNotes(this.props.data, searchInputText)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location.pathname.match(/\/trashed/)) {
|
||||||
|
return data.trashedSet.toJS()
|
||||||
|
.map((uniqueKey) => data.noteMap.get(uniqueKey))
|
||||||
|
}
|
||||||
|
|
||||||
let storageKey = params.storageKey
|
let storageKey = params.storageKey
|
||||||
let folderKey = params.folderKey
|
let folderKey = params.folderKey
|
||||||
let storage = data.storageMap.get(storageKey)
|
let storage = data.storageMap.get(storageKey)
|
||||||
@@ -232,49 +289,6 @@ class NoteList extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNoteContextMenu (e, uniqueKey) {
|
|
||||||
let menu = new Menu()
|
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'Delete Note',
|
|
||||||
click: (e) => this.handleDeleteNote(e, uniqueKey)
|
|
||||||
}))
|
|
||||||
menu.popup()
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDeleteNote (e, uniqueKey) {
|
|
||||||
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
|
||||||
type: 'warning',
|
|
||||||
message: 'Delete a note',
|
|
||||||
detail: 'This work cannot be undone.',
|
|
||||||
buttons: ['Confirm', 'Cancel']
|
|
||||||
})
|
|
||||||
if (index === 0) {
|
|
||||||
let { dispatch, location } = this.props
|
|
||||||
let splitted = uniqueKey.split('-')
|
|
||||||
let storageKey = splitted.shift()
|
|
||||||
let noteKey = splitted.shift()
|
|
||||||
|
|
||||||
dataApi
|
|
||||||
.deleteNote(storageKey, noteKey)
|
|
||||||
.then((data) => {
|
|
||||||
let dispatchHandler = () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'DELETE_NOTE',
|
|
||||||
storageKey: data.storageKey,
|
|
||||||
noteKey: data.noteKey
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location.query.key === uniqueKey) {
|
|
||||||
ee.once('list:moved', dispatchHandler)
|
|
||||||
ee.emit('list:next')
|
|
||||||
} else {
|
|
||||||
dispatchHandler()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSortByChange (e) {
|
handleSortByChange (e) {
|
||||||
let { dispatch } = this.props
|
let { dispatch } = this.props
|
||||||
|
|
||||||
@@ -303,8 +317,73 @@ class NoteList extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alertIfSnippet () {
|
||||||
|
let { location } = this.props
|
||||||
|
const targetIndex = _.findIndex(this.notes, (note) => {
|
||||||
|
return `${note.storage}-${note.key}` === location.query.key
|
||||||
|
})
|
||||||
|
if (this.notes[targetIndex].type === 'SNIPPET_NOTE') {
|
||||||
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Sorry!',
|
||||||
|
detail: 'md/text import is available only a markdown note.',
|
||||||
|
buttons: ['OK', 'Cancel']
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDragStart (e, note) {
|
||||||
|
const noteData = JSON.stringify(note)
|
||||||
|
e.dataTransfer.setData('note', noteData)
|
||||||
|
}
|
||||||
|
|
||||||
|
importFromFile () {
|
||||||
|
const { dispatch, location } = this.props
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
filters: [
|
||||||
|
{ name: 'Documents', extensions: ['md', 'txt'] }
|
||||||
|
],
|
||||||
|
properties: ['openFile', 'multiSelections']
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetIndex = _.findIndex(this.notes, (note) => {
|
||||||
|
return note !== null && `${note.storage}-${note.key}` === location.query.key
|
||||||
|
})
|
||||||
|
|
||||||
|
const storageKey = this.notes[targetIndex].storage
|
||||||
|
const folderKey = this.notes[targetIndex].folder
|
||||||
|
|
||||||
|
dialog.showOpenDialog(remote.getCurrentWindow(), options, (filepaths) => {
|
||||||
|
if (filepaths === undefined) return
|
||||||
|
filepaths.forEach((filepath) => {
|
||||||
|
fs.readFile(filepath, (err, data) => {
|
||||||
|
if (err) throw Error('File reading error: ', err)
|
||||||
|
const content = data.toString()
|
||||||
|
const newNote = {
|
||||||
|
content: content,
|
||||||
|
folder: folderKey,
|
||||||
|
title: markdown.strip(findNoteTitle(content)),
|
||||||
|
type: 'MARKDOWN_NOTE'
|
||||||
|
}
|
||||||
|
dataApi.createNote(storageKey, newNote)
|
||||||
|
.then((note) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'UPDATE_NOTE',
|
||||||
|
note: note
|
||||||
|
})
|
||||||
|
hashHistory.push({
|
||||||
|
pathname: location.pathname,
|
||||||
|
query: {key: `${note.storage}-${note.key}`}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { location, notes, config } = this.props
|
let { location, notes, config, dispatch } = this.props
|
||||||
let sortFunc = config.sortBy === 'CREATED_AT'
|
let sortFunc = config.sortBy === 'CREATED_AT'
|
||||||
? sortByCreatedAt
|
? sortByCreatedAt
|
||||||
: config.sortBy === 'ALPHABETICAL'
|
: config.sortBy === 'ALPHABETICAL'
|
||||||
@@ -312,59 +391,46 @@ class NoteList extends React.Component {
|
|||||||
: sortByUpdatedAt
|
: sortByUpdatedAt
|
||||||
this.notes = notes = this.getNotes()
|
this.notes = notes = this.getNotes()
|
||||||
.sort(sortFunc)
|
.sort(sortFunc)
|
||||||
|
.filter((note) => {
|
||||||
|
// this is for the trash box
|
||||||
|
if (note.isTrashed !== true || location.pathname === '/trashed') return true
|
||||||
|
})
|
||||||
|
|
||||||
let noteList = notes
|
let noteList = notes
|
||||||
.map((note) => {
|
.map(note => {
|
||||||
if (note == null) return null
|
if (note == null) {
|
||||||
let tagElements = _.isArray(note.tags)
|
return null
|
||||||
? note.tags.map((tag) => {
|
}
|
||||||
return (
|
|
||||||
<span styleName='item-bottom-tagList-item'
|
const isDefault = config.listStyle === 'DEFAULT'
|
||||||
key={tag}>
|
const isActive = location.query.key === note.storage + '-' + note.key
|
||||||
{tag}
|
const dateDisplay = moment(
|
||||||
</span>
|
config.sortBy === 'CREATED_AT'
|
||||||
)
|
? note.createdAt : note.updatedAt
|
||||||
})
|
).fromNow()
|
||||||
: []
|
const key = `${note.storage}-${note.key}`
|
||||||
let isActive = location.query.key === note.storage + '-' + note.key
|
|
||||||
|
if (isDefault) {
|
||||||
|
return (
|
||||||
|
<NoteItem
|
||||||
|
isActive={isActive}
|
||||||
|
note={note}
|
||||||
|
dateDisplay={dateDisplay}
|
||||||
|
key={key}
|
||||||
|
handleNoteClick={this.handleNoteClick.bind(this)}
|
||||||
|
handleDragStart={this.handleDragStart.bind(this)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div styleName={isActive
|
<NoteItemSimple
|
||||||
? 'item--active'
|
isActive={isActive}
|
||||||
: 'item'
|
note={note}
|
||||||
}
|
key={key}
|
||||||
key={note.storage + '-' + note.key}
|
handleNoteClick={this.handleNoteClick.bind(this)}
|
||||||
onClick={(e) => this.handleNoteClick(e, note.storage + '-' + note.key)}
|
handleDragStart={this.handleDragStart.bind(this)}
|
||||||
onContextMenu={(e) => this.handleNoteContextMenu(e, note.storage + '-' + note.key)}
|
/>
|
||||||
>
|
|
||||||
<div styleName='item-title'>
|
|
||||||
{note.type === 'SNIPPET_NOTE'
|
|
||||||
? <i styleName='item-title-icon' className='fa fa-fw fa-code'/>
|
|
||||||
: <i styleName='item-title-icon' className='fa fa-fw fa-file-text-o'/>
|
|
||||||
}
|
|
||||||
{note.title.trim().length > 0
|
|
||||||
? note.title
|
|
||||||
: <span styleName='item-title-empty'>Empty</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{config.listStyle === 'DEFAULT' &&
|
|
||||||
<div styleName='item-bottom'>
|
|
||||||
<i styleName='item-bottom-tagIcon'
|
|
||||||
className='fa fa-tags fa-fw'
|
|
||||||
/>
|
|
||||||
<div styleName='item-bottom-tagList'>
|
|
||||||
{tagElements.length > 0
|
|
||||||
? tagElements
|
|
||||||
: <span styleName='item-bottom-tagList-empty'>Not tagged yet</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='item-bottom-time'>
|
|
||||||
{moment(config.sortBy === 'CREATED_AT' ? note.createdAt : note.updatedAt).fromNow()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -375,7 +441,7 @@ class NoteList extends React.Component {
|
|||||||
>
|
>
|
||||||
<div styleName='control'>
|
<div styleName='control'>
|
||||||
<div styleName='control-sortBy'>
|
<div styleName='control-sortBy'>
|
||||||
Sort by
|
<i className='fa fa-bolt' />
|
||||||
<select styleName='control-sortBy-select'
|
<select styleName='control-sortBy-select'
|
||||||
value={config.sortBy}
|
value={config.sortBy}
|
||||||
onChange={(e) => this.handleSortByChange(e)}
|
onChange={(e) => this.handleSortByChange(e)}
|
||||||
@@ -385,28 +451,24 @@ class NoteList extends React.Component {
|
|||||||
<option value='ALPHABETICAL'>Alphabetical</option>
|
<option value='ALPHABETICAL'>Alphabetical</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button styleName={config.listStyle === 'DEFAULT'
|
<div styleName='control-button-area'>
|
||||||
? 'control-button--active'
|
<button styleName={config.listStyle === 'DEFAULT'
|
||||||
: 'control-button'
|
? 'control-button--active'
|
||||||
}
|
: 'control-button'
|
||||||
onClick={(e) => this.handleListStyleButtonClick(e, 'DEFAULT')}
|
}
|
||||||
>
|
onClick={(e) => this.handleListStyleButtonClick(e, 'DEFAULT')}
|
||||||
<i className='fa fa-th-list'/>
|
>
|
||||||
<span styleName='control-button-tooltip'>
|
<i className='fa fa-th-large' />
|
||||||
Default Size
|
</button>
|
||||||
</span>
|
<button styleName={config.listStyle === 'SMALL'
|
||||||
</button>
|
? 'control-button--active'
|
||||||
<button styleName={config.listStyle === 'SMALL'
|
: 'control-button'
|
||||||
? 'control-button--active'
|
}
|
||||||
: 'control-button'
|
onClick={(e) => this.handleListStyleButtonClick(e, 'SMALL')}
|
||||||
}
|
>
|
||||||
onClick={(e) => this.handleListStyleButtonClick(e, 'SMALL')}
|
<i className='fa fa-list-ul' />
|
||||||
>
|
</button>
|
||||||
<i className='fa fa-list'/>
|
</div>
|
||||||
<span styleName='control-button-tooltip'>
|
|
||||||
Small Size
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div styleName='list'
|
<div styleName='list'
|
||||||
ref='list'
|
ref='list'
|
||||||
|
|||||||
@@ -1,53 +1,34 @@
|
|||||||
.root
|
.root
|
||||||
absolute top left bottom
|
absolute top left bottom
|
||||||
width $sideNav-width
|
width $sideNav-width
|
||||||
border-right $ui-border
|
background-color #f9f9f9
|
||||||
background-color $ui-backgroundColor
|
|
||||||
user-select none
|
user-select none
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
|
|
||||||
.top
|
.top
|
||||||
height $topBar-height
|
height $topBar-height
|
||||||
border-bottom $ui-border
|
|
||||||
|
|
||||||
.top-menu
|
.top-menu
|
||||||
navButtonColor()
|
navButtonColor()
|
||||||
height $topBar-height - 1
|
height $topBar-height
|
||||||
padding 0 10px
|
padding 0 15px
|
||||||
font-size 14px
|
font-size 12px
|
||||||
width 100%
|
width 100%
|
||||||
text-align left
|
text-align left
|
||||||
|
&:hover
|
||||||
|
color $ui-text-color
|
||||||
|
&:active, &:active:hover
|
||||||
|
color $ui-text-color
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||||
|
|
||||||
.top-menu-label
|
.top-menu-label
|
||||||
margin-left 5px
|
margin-left 5px
|
||||||
overflow ellipsis
|
overflow ellipsis
|
||||||
|
|
||||||
.menu
|
|
||||||
margin 0
|
|
||||||
|
|
||||||
.menu-button
|
|
||||||
navButtonColor()
|
|
||||||
height 32px
|
|
||||||
padding 0 10px
|
|
||||||
font-size 14px
|
|
||||||
width 100%
|
|
||||||
text-align left
|
|
||||||
overflow ellipsis
|
|
||||||
|
|
||||||
.menu-button--active
|
|
||||||
@extend .menu-button
|
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
color $ui-button--active-color
|
|
||||||
&:hover
|
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
|
|
||||||
.menu-button-label
|
|
||||||
margin-left 5px
|
|
||||||
|
|
||||||
.storageList
|
.storageList
|
||||||
absolute left right
|
absolute left right
|
||||||
bottom 32px
|
bottom 37px
|
||||||
top 120px
|
top 160px
|
||||||
overflow-y auto
|
overflow-y auto
|
||||||
|
|
||||||
.storageList-empty
|
.storageList-empty
|
||||||
@@ -134,20 +115,16 @@ body[data-theme="dark"]
|
|||||||
|
|
||||||
.top-menu
|
.top-menu
|
||||||
navDarkButtonColor()
|
navDarkButtonColor()
|
||||||
|
&:active
|
||||||
.menu-button
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
navDarkButtonColor()
|
|
||||||
|
|
||||||
.menu-button--active
|
|
||||||
@extend .menu-button
|
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
color $ui-dark-button--active-color
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
|
||||||
.storageList-empty
|
.storageList-empty
|
||||||
color $ui-dark-inactive-text-color
|
color $ui-dark-inactive-text-color
|
||||||
|
|
||||||
.navToggle
|
.navToggle
|
||||||
navDarkButtonColor()
|
&:hover
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
@@ -6,6 +6,8 @@ import modal from 'browser/main/lib/modal'
|
|||||||
import CreateFolderModal from 'browser/main/modals/CreateFolderModal'
|
import CreateFolderModal from 'browser/main/modals/CreateFolderModal'
|
||||||
import RenameFolderModal from 'browser/main/modals/RenameFolderModal'
|
import RenameFolderModal from 'browser/main/modals/RenameFolderModal'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
|
import StorageItemChild from 'browser/components/StorageItem'
|
||||||
|
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
const { remote } = require('electron')
|
const { remote } = require('electron')
|
||||||
const { Menu, MenuItem, dialog } = remote
|
const { Menu, MenuItem, dialog } = remote
|
||||||
@@ -130,38 +132,77 @@ class StorageItem extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDragEnter (e) {
|
||||||
|
e.dataTransfer.setData('defaultColor', e.target.style.backgroundColor)
|
||||||
|
e.target.style.backgroundColor = 'rgba(129, 130, 131, 0.08)'
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDragLeave (e) {
|
||||||
|
e.target.style.opacity = '1'
|
||||||
|
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDrop (e, storage, folder, dispatch, location) {
|
||||||
|
e.target.style.opacity = '1'
|
||||||
|
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
|
||||||
|
const noteData = JSON.parse(e.dataTransfer.getData('note'))
|
||||||
|
const newNoteData = Object.assign({}, noteData, {storage: storage, folder: folder.key})
|
||||||
|
if (folder.key === noteData.folder) return
|
||||||
|
dataApi
|
||||||
|
.createNote(storage.key, newNoteData)
|
||||||
|
.then((note) => {
|
||||||
|
dataApi
|
||||||
|
.deleteNote(noteData.storage, noteData.key)
|
||||||
|
.then((data) => {
|
||||||
|
let dispatchHandler = () => {
|
||||||
|
dispatch({
|
||||||
|
type: 'DELETE_NOTE',
|
||||||
|
storageKey: data.storageKey,
|
||||||
|
noteKey: data.noteKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
eventEmitter.once('list:moved', dispatchHandler)
|
||||||
|
eventEmitter.emit('list:next')
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
dispatch({
|
||||||
|
type: 'UPDATE_NOTE',
|
||||||
|
note: note
|
||||||
|
})
|
||||||
|
hashHistory.push({
|
||||||
|
pathname: location.pathname,
|
||||||
|
query: {key: `${note.storage}-${note.key}`}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { storage, location, isFolded, data } = this.props
|
let { storage, location, isFolded, data, dispatch } = this.props
|
||||||
let { folderNoteMap } = data
|
let { folderNoteMap } = data
|
||||||
let folderList = storage.folders.map((folder) => {
|
let folderList = storage.folders.map((folder) => {
|
||||||
let isActive = location.pathname.match(new RegExp('\/storages\/' + storage.key + '\/folders\/' + folder.key))
|
let isActive = !!(location.pathname.match(new RegExp('\/storages\/' + storage.key + '\/folders\/' + folder.key)))
|
||||||
let noteSet = folderNoteMap.get(storage.key + '-' + folder.key)
|
let noteSet = folderNoteMap.get(storage.key + '-' + folder.key)
|
||||||
|
|
||||||
let noteCount = noteSet != null
|
let noteCount = noteSet != null
|
||||||
? noteSet.size
|
? noteSet.size
|
||||||
: 0
|
: 0
|
||||||
return <button styleName={isActive
|
return (
|
||||||
? 'folderList-item--active'
|
<StorageItemChild
|
||||||
: 'folderList-item'
|
key={folder.key}
|
||||||
}
|
isActive={isActive}
|
||||||
key={folder.key}
|
handleButtonClick={(e) => this.handleFolderButtonClick(folder.key)(e)}
|
||||||
onClick={(e) => this.handleFolderButtonClick(folder.key)(e)}
|
handleContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)}
|
||||||
onContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)}
|
folderName={folder.name}
|
||||||
>
|
folderColor={folder.color}
|
||||||
<span styleName='folderList-item-name'
|
isFolded={isFolded}
|
||||||
style={{borderColor: folder.color}}
|
noteCount={noteCount}
|
||||||
>
|
handleDrop={(e) => this.handleDrop(e, storage, folder, dispatch, location)}
|
||||||
{isFolded ? folder.name.substring(0, 1) : folder.name}
|
handleDragEnter={this.handleDragEnter}
|
||||||
</span>
|
handleDragLeave={this.handleDragLeave}
|
||||||
{!isFolded &&
|
/>
|
||||||
<span styleName='folderList-item-noteCount'>{noteCount}</span>
|
)
|
||||||
}
|
|
||||||
{isFolded &&
|
|
||||||
<span styleName='folderList-item-tooltip'>
|
|
||||||
{folder.name}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</button>
|
|
||||||
})
|
})
|
||||||
|
|
||||||
let isActive = location.pathname.match(new RegExp('\/storages\/' + storage.key + '$'))
|
let isActive = location.pathname.match(new RegExp('\/storages\/' + storage.key + '$'))
|
||||||
@@ -190,7 +231,7 @@ class StorageItem extends React.Component {
|
|||||||
<button styleName='header-addFolderButton'
|
<button styleName='header-addFolderButton'
|
||||||
onClick={(e) => this.handleAddFolderButtonClick(e)}
|
onClick={(e) => this.handleAddFolderButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-plus'/>
|
<i className='fa fa-plus' />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,139 +1,79 @@
|
|||||||
.root
|
.root
|
||||||
width 100%
|
width 100%
|
||||||
user-select none
|
user-select none
|
||||||
|
padding-top 20px
|
||||||
|
|
||||||
.header
|
.header
|
||||||
position relative
|
position relative
|
||||||
height 26px
|
height 25px
|
||||||
width 100%
|
width 100%
|
||||||
|
margin-bottom 5px
|
||||||
transition 0.15s
|
transition 0.15s
|
||||||
&:hover
|
|
||||||
background-color $ui-button--hover-backgroundColor
|
|
||||||
&:active
|
|
||||||
.header-toggleButton
|
|
||||||
.header-addFolderButton
|
|
||||||
color white
|
|
||||||
&:active
|
|
||||||
color $ui-active-color
|
|
||||||
|
|
||||||
.header--active
|
.header--active
|
||||||
@extend .header
|
margin-bottom 5px
|
||||||
.header-info
|
background-color $ui-button--active-backgroundColor
|
||||||
color $ui-button--active-color
|
transition color background-color 0.15s
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
|
.header--active
|
||||||
.header-toggleButton
|
.header-toggleButton
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.header--active
|
||||||
|
.header-info
|
||||||
|
color $ui-text-color
|
||||||
|
|
||||||
|
.header--active
|
||||||
.header-addFolderButton
|
.header-addFolderButton
|
||||||
color white
|
color $ui-text-color
|
||||||
&:active
|
|
||||||
&:hover
|
|
||||||
&:hover:active
|
|
||||||
color white
|
|
||||||
|
|
||||||
.header-toggleButton
|
.header-toggleButton
|
||||||
|
navButtonColor()
|
||||||
position absolute
|
position absolute
|
||||||
left 0
|
left 0
|
||||||
width 25px
|
width 25px
|
||||||
height 26px
|
height 25px
|
||||||
padding 0
|
padding 0
|
||||||
border none
|
border none
|
||||||
color $ui-inactive-text-color
|
border-radius 50%
|
||||||
background-color transparent
|
|
||||||
&:hover
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
&:active
|
|
||||||
color $ui-active-color
|
|
||||||
|
|
||||||
.header-info
|
.header-info
|
||||||
|
navButtonColor()
|
||||||
display block
|
display block
|
||||||
width 100%
|
width 100%
|
||||||
height 26px
|
height 25px
|
||||||
padding-left 25px
|
padding-left 23px
|
||||||
padding-right 10px
|
padding-right 10px
|
||||||
line-height 26px
|
line-height 22px
|
||||||
cursor pointer
|
cursor pointer
|
||||||
font-size 14px
|
font-size 13px
|
||||||
border none
|
border none
|
||||||
overflow ellipsis
|
overflow ellipsis
|
||||||
text-align left
|
text-align left
|
||||||
background-color transparent
|
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||||
color $ui-inactive-text-color
|
|
||||||
&:active
|
|
||||||
color $ui-button--active-color
|
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
|
|
||||||
.header-info-path
|
.header-info-path
|
||||||
font-size 10px
|
font-size 10px
|
||||||
margin 0 5px
|
margin 0 5px
|
||||||
|
|
||||||
.header-addFolderButton
|
.header-addFolderButton
|
||||||
|
navButtonColor()
|
||||||
position absolute
|
position absolute
|
||||||
right 0
|
right 0
|
||||||
width 25px
|
width 25px
|
||||||
height 26px
|
height 25px
|
||||||
padding 0
|
padding 0
|
||||||
border none
|
border none
|
||||||
color $ui-inactive-text-color
|
margin-right 5px
|
||||||
background-color transparent
|
border-radius 50%
|
||||||
&:hover
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
&:active
|
|
||||||
color $ui-active-color
|
|
||||||
|
|
||||||
.folderList-item
|
|
||||||
display flex
|
|
||||||
width 100%
|
|
||||||
height 26px
|
|
||||||
background-color transparent
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
padding 0
|
|
||||||
margin-bottom 2px
|
|
||||||
text-align left
|
|
||||||
border none
|
|
||||||
overflow ellipsis
|
|
||||||
font-size 14px
|
|
||||||
&:first-child
|
|
||||||
margin-top 0
|
|
||||||
&:hover
|
|
||||||
background-color $ui-button--hover-backgroundColor
|
|
||||||
&:active
|
|
||||||
color $ui-button--active-color
|
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
|
|
||||||
.folderList-item--active
|
|
||||||
@extend .folderList-item
|
|
||||||
color $ui-button--active-color
|
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
&:hover
|
|
||||||
color $ui-button--active-color
|
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
|
|
||||||
.folderList-item-name
|
|
||||||
display block
|
|
||||||
flex 1
|
|
||||||
padding 0 10px
|
|
||||||
height 26px
|
|
||||||
line-height 26px
|
|
||||||
border-width 0 0 0 6px
|
|
||||||
border-style solid
|
|
||||||
border-color transparent
|
|
||||||
|
|
||||||
.folderList-item-noteCount
|
|
||||||
float right
|
|
||||||
line-height 26px
|
|
||||||
padding-right 5px
|
|
||||||
font-size 12px
|
|
||||||
|
|
||||||
.folderList-item-tooltip
|
|
||||||
tooltip()
|
|
||||||
position fixed
|
|
||||||
padding 0 10px
|
|
||||||
left 44px
|
|
||||||
z-index 10
|
|
||||||
pointer-events none
|
|
||||||
opacity 0
|
|
||||||
border-top-right-radius 2px
|
|
||||||
border-bottom-right-radius 2px
|
|
||||||
height 26px
|
|
||||||
line-height 26px
|
|
||||||
|
|
||||||
.root--folded
|
.root--folded
|
||||||
@extend .root
|
@extend .root
|
||||||
@@ -161,22 +101,52 @@
|
|||||||
.header-info--folded-tooltip-path
|
.header-info--folded-tooltip-path
|
||||||
font-size 10px
|
font-size 10px
|
||||||
margin 0 5px
|
margin 0 5px
|
||||||
.folderList-item:hover, .folderList-item--active:hover
|
|
||||||
.folderList-item-tooltip
|
|
||||||
opacity 1
|
|
||||||
.folderList-item-name
|
|
||||||
padding-left 14px
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.header-toggleButton
|
.header--active
|
||||||
.header-addFolderButton
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
color $ui-dark-inactive-text-color
|
transition color background-color 0.15s
|
||||||
&:hover
|
|
||||||
color $ui-dark-text-color
|
|
||||||
&:active
|
|
||||||
color $ui-dark-active-color
|
|
||||||
.header--active
|
.header--active
|
||||||
.header-toggleButton
|
.header-toggleButton
|
||||||
.header-addFolderButton
|
color $ui-dark-text-color
|
||||||
color white
|
|
||||||
|
.header--active
|
||||||
|
.header-info
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
&:active
|
&:active
|
||||||
color white
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
|
.header--active
|
||||||
|
.header-addFolderButton
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.header-toggleButton
|
||||||
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||||
|
&:active, &:active:hover
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
|
.header-info
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
&:active, &:active:hover
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
|
.header-addFolderButton
|
||||||
|
&:hover
|
||||||
|
transition 0.2s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||||
|
&:active, &:active:hover
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
@@ -5,9 +5,7 @@ import { openModal } from 'browser/main/lib/modal'
|
|||||||
import PreferencesModal from '../modals/PreferencesModal'
|
import PreferencesModal from '../modals/PreferencesModal'
|
||||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
import StorageItem from './StorageItem'
|
import StorageItem from './StorageItem'
|
||||||
|
import SideNavFilter from 'browser/components/SideNavFilter'
|
||||||
const electron = require('electron')
|
|
||||||
const { remote } = electron
|
|
||||||
|
|
||||||
class SideNav extends React.Component {
|
class SideNav extends React.Component {
|
||||||
// TODO: should not use electron stuff v0.7
|
// TODO: should not use electron stuff v0.7
|
||||||
@@ -35,12 +33,18 @@ class SideNav extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleTrashedButtonClick (e) {
|
||||||
|
let { router } = this.context
|
||||||
|
router.push('/trashed')
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { data, location, config, dispatch } = this.props
|
let { data, location, config, dispatch } = this.props
|
||||||
|
|
||||||
let isFolded = config.isSideNavFolded
|
let isFolded = config.isSideNavFolded
|
||||||
let isHomeActive = location.pathname.match(/^\/home$/)
|
let isHomeActive = !!location.pathname.match(/^\/home$/)
|
||||||
let isStarredActive = location.pathname.match(/^\/starred$/)
|
let isStarredActive = !!location.pathname.match(/^\/starred$/)
|
||||||
|
let isTrashedActive = !!location.pathname.match(/^\/trashed$/)
|
||||||
|
|
||||||
let storageList = data.storageMap.map((storage, key) => {
|
let storageList = data.storageMap.map((storage, key) => {
|
||||||
return <StorageItem
|
return <StorageItem
|
||||||
@@ -64,25 +68,20 @@ class SideNav extends React.Component {
|
|||||||
<button styleName='top-menu'
|
<button styleName='top-menu'
|
||||||
onClick={(e) => this.handleMenuButtonClick(e)}
|
onClick={(e) => this.handleMenuButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-navicon fa-fw'/>
|
<i className='fa fa-wrench fa-fw' />
|
||||||
<span styleName='top-menu-label'>Menu</span>
|
<span styleName='top-menu-label'>Preferences</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div styleName='menu'>
|
<SideNavFilter
|
||||||
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
|
isFolded={isFolded}
|
||||||
onClick={(e) => this.handleHomeButtonClick(e)}
|
isHomeActive={isHomeActive}
|
||||||
>
|
handleAllNotesButtonClick={(e) => this.handleHomeButtonClick(e)}
|
||||||
<i className='fa fa-files-o fa-fw'/>
|
isStarredActive={isStarredActive}
|
||||||
<span styleName='menu-button-label'>All Notes</span>
|
isTrashedActive={isTrashedActive}
|
||||||
</button>
|
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
|
||||||
<button styleName={isStarredActive ? 'menu-button--active' : 'menu-button'}
|
handleTrashedButtonClick={(e) => this.handleTrashedButtonClick(e)}
|
||||||
onClick={(e) => this.handleStarredButtonClick(e)}
|
/>
|
||||||
>
|
|
||||||
<i className='fa fa-star fa-fw'/>
|
|
||||||
<span styleName='menu-button-label'>Starred</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div styleName='storageList'>
|
<div styleName='storageList'>
|
||||||
{storageList.length > 0 ? storageList : (
|
{storageList.length > 0 ? storageList : (
|
||||||
@@ -93,8 +92,8 @@ class SideNav extends React.Component {
|
|||||||
onClick={(e) => this.handleToggleButtonClick(e)}
|
onClick={(e) => this.handleToggleButtonClick(e)}
|
||||||
>
|
>
|
||||||
{isFolded
|
{isFolded
|
||||||
? <i className='fa fa-angle-double-right'/>
|
? <i className='fa fa-angle-double-right' />
|
||||||
: <i className='fa fa-angle-double-left'/>
|
: <i className='fa fa-angle-double-left' />
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
@import('../Detail/DetailVars')
|
||||||
|
|
||||||
.root
|
.root
|
||||||
absolute bottom left right
|
absolute bottom left right
|
||||||
height $statusBar-height
|
height $statusBar-height
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-noteDetail-backgroundColor
|
||||||
border-top $ui-border
|
|
||||||
display flex
|
display flex
|
||||||
|
|
||||||
.blank
|
.blank
|
||||||
@@ -21,9 +22,6 @@
|
|||||||
.zoom
|
.zoom
|
||||||
navButtonColor()
|
navButtonColor()
|
||||||
height 24px
|
height 24px
|
||||||
border-width 0 1px
|
|
||||||
border-style solid
|
|
||||||
border-color $ui-borderColor
|
|
||||||
|
|
||||||
.update
|
.update
|
||||||
navButtonColor()
|
navButtonColor()
|
||||||
@@ -39,11 +37,16 @@
|
|||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
background-color $ui-dark-backgroundColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
box-shadow none
|
||||||
|
|
||||||
.zoom
|
.zoom
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
|
||||||
.help
|
.help
|
||||||
navButtonColor()
|
navButtonColor()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const electron = require('electron')
|
|||||||
const { remote, ipcRenderer } = electron
|
const { remote, ipcRenderer } = electron
|
||||||
const { Menu, MenuItem, dialog } = remote
|
const { Menu, MenuItem, dialog } = remote
|
||||||
|
|
||||||
const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3]
|
const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
|
||||||
|
|
||||||
class StatusBar extends React.Component {
|
class StatusBar extends React.Component {
|
||||||
updateApp () {
|
updateApp () {
|
||||||
@@ -52,22 +52,21 @@ class StatusBar extends React.Component {
|
|||||||
<div className='StatusBar'
|
<div className='StatusBar'
|
||||||
styleName='root'
|
styleName='root'
|
||||||
>
|
>
|
||||||
<div styleName='blank' />
|
|
||||||
{status.updateReady
|
|
||||||
? <button onClick={this.updateApp} styleName='update'>
|
|
||||||
<i styleName='update-icon' className='fa fa-cloud-download' /> Ready to Update!
|
|
||||||
</button>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
{/*<button styleName='help'>
|
|
||||||
<i className='fa fa-info-circle' />
|
|
||||||
</button>*/}
|
|
||||||
<button styleName='zoom'
|
<button styleName='zoom'
|
||||||
onClick={(e) => this.handleZoomButtonClick(e)}
|
onClick={(e) => this.handleZoomButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-search-plus' />
|
<i className='fa fa-search-plus' />
|
||||||
{Math.floor(config.zoom * 100)}%
|
{Math.floor(config.zoom * 100)}%
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div styleName='blank' />
|
||||||
|
|
||||||
|
{status.updateReady
|
||||||
|
? <button onClick={this.updateApp} styleName='update'>
|
||||||
|
<i styleName='update-icon' className='fa fa-cloud-download' /> Ready to Update!
|
||||||
|
</button>
|
||||||
|
: null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -77,7 +76,8 @@ StatusBar.contextTypes = {
|
|||||||
status: PropTypes.shape({
|
status: PropTypes.shape({
|
||||||
updateReady: PropTypes.bool.isRequired
|
updateReady: PropTypes.bool.isRequired
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
config: PropTypes.shape({}).isRequired
|
config: PropTypes.shape({}).isRequired,
|
||||||
|
date: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusBar.propTypes = {
|
StatusBar.propTypes = {
|
||||||
|
|||||||
@@ -1,53 +1,55 @@
|
|||||||
.root
|
.root
|
||||||
position relative
|
position relative
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-noteList-backgroundColor
|
||||||
height $topBar-height - 1
|
height $topBar-height - 1
|
||||||
|
|
||||||
.root--expanded
|
.root--expanded
|
||||||
@extend .root
|
@extend .root
|
||||||
|
|
||||||
$control-height = 34px
|
$control-height = 34px
|
||||||
|
|
||||||
.control
|
.control
|
||||||
position absolute
|
position absolute
|
||||||
top 8px
|
top 13px
|
||||||
left 8px
|
left 8px
|
||||||
right 8px
|
right 8px
|
||||||
height $control-height
|
height $control-height
|
||||||
border $ui-border
|
|
||||||
border-radius 20px
|
|
||||||
overflow hidden
|
overflow hidden
|
||||||
display flex
|
display flex
|
||||||
|
|
||||||
.control-search
|
.control-search
|
||||||
height 32px
|
height 32px
|
||||||
|
width 1px
|
||||||
flex 1
|
flex 1
|
||||||
background-color white
|
background-color white
|
||||||
position relative
|
position relative
|
||||||
|
|
||||||
.control-search-icon
|
|
||||||
absolute top bottom left
|
|
||||||
line-height 32px
|
|
||||||
width 35px
|
|
||||||
color $ui-inactive-text-color
|
|
||||||
|
|
||||||
.control-search-input
|
.control-search-input
|
||||||
display block
|
display block
|
||||||
absolute top bottom right
|
absolute top bottom right
|
||||||
left 30px
|
width 100%
|
||||||
|
padding-left 12px
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
input
|
input
|
||||||
width 100%
|
width 100%
|
||||||
height 100%
|
height 100%
|
||||||
outline none
|
outline none
|
||||||
border none
|
border none
|
||||||
|
color $ui-text-color
|
||||||
|
font-size 16px
|
||||||
|
padding-bottom 2px
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
|
|
||||||
.control-search-optionList
|
.control-search-optionList
|
||||||
position fixed
|
position fixed
|
||||||
z-index 200
|
z-index 200
|
||||||
width 275px
|
width 500px
|
||||||
height 175px
|
height 250px
|
||||||
overflow-y auto
|
overflow-y auto
|
||||||
background-color $modal-background
|
background-color $modal-background
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
box-shadow 2px 2px 10px gray
|
border-none
|
||||||
|
box-shadow 0 0 1px rgba(76,86,103,.25), 0 2px 18px rgba(31,37,50,.32)
|
||||||
|
|
||||||
.control-search-optionList-item
|
.control-search-optionList-item
|
||||||
height 50px
|
height 50px
|
||||||
@@ -57,50 +59,39 @@ $control-height = 34px
|
|||||||
cursor pointer
|
cursor pointer
|
||||||
overflow ellipsis
|
overflow ellipsis
|
||||||
&:hover
|
&:hover
|
||||||
background-color alpha($ui-active-color, 10%)
|
background-color alpha(#D4D4D4, 30%)
|
||||||
|
|
||||||
.control-search-optionList-item-folder
|
.control-search-optionList-item-folder
|
||||||
border-left 4px solid transparent
|
border-left 2px solid transparent
|
||||||
padding 2px 5px
|
padding 2px 5px
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
overflow ellipsis
|
overflow ellipsis
|
||||||
font-size 12px
|
font-size 12px
|
||||||
height 16px
|
height 16px
|
||||||
margin-bottom 4px
|
margin-bottom 4px
|
||||||
|
|
||||||
.control-search-optionList-item-folder-surfix
|
.control-search-optionList-item-folder-surfix
|
||||||
font-size 10px
|
font-size 10px
|
||||||
margin-left 5px
|
margin-left 5px
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
.control-search-optionList-item-type
|
.control-search-optionList-item-type
|
||||||
font-size 12px
|
font-size 12px
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
padding-right 3px
|
padding-right 3px
|
||||||
|
|
||||||
.control-search-optionList-empty
|
.control-search-optionList-empty
|
||||||
height 150px
|
height 150px
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
line-height 150px
|
line-height 150px
|
||||||
text-align center
|
text-align center
|
||||||
|
|
||||||
.control-contextButton
|
|
||||||
display block
|
|
||||||
width 20px
|
|
||||||
height $control-height - 2
|
|
||||||
navButtonColor()
|
|
||||||
border-left $ui-border
|
|
||||||
font-size 14px
|
|
||||||
line-height 28px
|
|
||||||
padding 0
|
|
||||||
&:active
|
|
||||||
border-color $ui-button--active-backgroundColor
|
|
||||||
&:hover .control-newPostButton-tooltip
|
|
||||||
opacity 1
|
|
||||||
|
|
||||||
.control-newPostButton
|
.control-newPostButton
|
||||||
display block
|
display block
|
||||||
width 36px
|
width 32px
|
||||||
height $control-height - 2
|
height $control-height - 2
|
||||||
navButtonColor()
|
navButtonColor()
|
||||||
border-left $ui-border
|
font-size 16px
|
||||||
font-size 14px
|
|
||||||
line-height 28px
|
line-height 28px
|
||||||
padding 0
|
padding 0
|
||||||
&:active
|
&:active
|
||||||
@@ -112,17 +103,18 @@ $control-height = 34px
|
|||||||
tooltip()
|
tooltip()
|
||||||
position fixed
|
position fixed
|
||||||
pointer-events none
|
pointer-events none
|
||||||
top 45px
|
top 50px
|
||||||
left 385px
|
left 433px
|
||||||
z-index 10
|
z-index 200
|
||||||
padding 5px
|
padding 5px
|
||||||
line-height normal
|
line-height normal
|
||||||
|
border-radius 2px
|
||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root, .root--expanded
|
.root, .root--expanded
|
||||||
background-color $ui-dark-backgroundColor
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
|
||||||
.control
|
.control
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
@@ -134,11 +126,14 @@ body[data-theme="dark"]
|
|||||||
line-height 32px
|
line-height 32px
|
||||||
width 35px
|
width 35px
|
||||||
color $ui-dark-inactive-text-color
|
color $ui-dark-inactive-text-color
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
|
||||||
.control-search-input
|
.control-search-input
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
input
|
input
|
||||||
background-color $dark-background-color
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|
||||||
.control-search-optionList
|
.control-search-optionList
|
||||||
color white
|
color white
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-button--hover-backgroundColor
|
||||||
@@ -148,7 +143,7 @@ body[data-theme="dark"]
|
|||||||
.control-search-optionList-item
|
.control-search-optionList-item
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
&:hover
|
&:hover
|
||||||
background-color lighten($ui-dark-button--hover-backgroundColor, 15%)
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
.control-search-optionList-item-folder
|
.control-search-optionList-item-folder
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
.control-search-optionList-item-folder-surfix
|
.control-search-optionList-item-folder-surfix
|
||||||
@@ -162,11 +157,15 @@ body[data-theme="dark"]
|
|||||||
.control-search-optionList-empty
|
.control-search-optionList-empty
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
.control-contextButton,
|
|
||||||
.control-newPostButton
|
.control-newPostButton
|
||||||
colorDarkDefaultButton()
|
color $ui-inactive-text-color
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
&:hover
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
&:active
|
&:active
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
border-color $ui-dark-button--active-backgroundColor
|
border-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
.control-newPostButton-tooltip
|
.control-newPostButton-tooltip
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import ee from 'browser/main/lib/eventEmitter'
|
|||||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
|
|
||||||
const OSX = window.process.platform === 'darwin'
|
|
||||||
const { remote } = require('electron')
|
const { remote } = require('electron')
|
||||||
const { Menu, MenuItem } = remote
|
const { dialog } = remote
|
||||||
|
|
||||||
|
const OSX = window.process.platform === 'darwin'
|
||||||
|
|
||||||
class TopBar extends React.Component {
|
class TopBar extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -20,24 +21,40 @@ class TopBar extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
search: '',
|
search: '',
|
||||||
searchOptions: [],
|
searchOptions: [],
|
||||||
searchPopupOpen: false
|
isSearching: false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newNoteHandler = () => {
|
this.newNoteHandler = () => {
|
||||||
this.handleNewPostButtonClick()
|
this.handleNewPostButtonClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.focusSearchHandler = () => {
|
||||||
|
this.handleOnSearchFocus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
ee.on('top:new-note', this.newNoteHandler)
|
ee.on('top:new-note', this.newNoteHandler)
|
||||||
|
ee.on('top:focus-search', this.focusSearchHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
ee.off('top:new-note', this.newNoteHandler)
|
ee.off('top:new-note', this.newNoteHandler)
|
||||||
|
ee.off('top:focus-search', this.focusSearchHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewPostButtonClick (e) {
|
handleNewPostButtonClick (e) {
|
||||||
let { config } = this.props
|
let { config, location } = this.props
|
||||||
|
|
||||||
|
if (location.pathname === '/trashed') {
|
||||||
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Cannot create new note',
|
||||||
|
detail: 'You cannot create new note in trash box.',
|
||||||
|
buttons: ['OK']
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch (config.ui.defaultNote) {
|
switch (config.ui.defaultNote) {
|
||||||
case 'MARKDOWN_NOTE':
|
case 'MARKDOWN_NOTE':
|
||||||
@@ -71,10 +88,10 @@ class TopBar extends React.Component {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (storage == null) alert('No storage to create a note')
|
if (storage == null) window.alert('No storage to create a note')
|
||||||
let folder = _.find(storage.folders, {key: params.folderKey})
|
let folder = _.find(storage.folders, {key: params.folderKey})
|
||||||
if (folder == null) folder = storage.folders[0]
|
if (folder == null) folder = storage.folders[0]
|
||||||
if (folder == null) alert('No folder to create a note')
|
if (folder == null) window.alert('No folder to create a note')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
storage,
|
storage,
|
||||||
@@ -83,79 +100,17 @@ class TopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSearchChange (e) {
|
handleSearchChange (e) {
|
||||||
|
let { router } = this.context
|
||||||
|
router.push('/searched')
|
||||||
this.setState({
|
this.setState({
|
||||||
search: this.refs.searchInput.value
|
search: this.refs.searchInput.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptions () {
|
|
||||||
let { data } = this.props
|
|
||||||
let { search } = this.state
|
|
||||||
let notes = data.noteMap.map((note) => note)
|
|
||||||
if (search.trim().length === 0) return []
|
|
||||||
let searchBlocks = search.split(' ')
|
|
||||||
searchBlocks.forEach((block) => {
|
|
||||||
if (block.match(/^!#.+/)) {
|
|
||||||
let tag = block.match(/^!#(.+)/)[1]
|
|
||||||
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
|
|
||||||
notes = notes
|
|
||||||
.filter((note) => {
|
|
||||||
if (!_.isArray(note.tags)) return false
|
|
||||||
return note.tags.some((_tag) => {
|
|
||||||
return _tag.match(regExp)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else if (block.match(/^!.+/)) {
|
|
||||||
let block = block.match(/^!(.+)/)[1]
|
|
||||||
let regExp = new RegExp(_.escapeRegExp(block), 'i')
|
|
||||||
notes = notes.filter((note) => {
|
|
||||||
if (!_.isArray(note.tags) || !note.tags.some((_tag) => {
|
|
||||||
return _tag.match(regExp)
|
|
||||||
})) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (note.type === 'SNIPPET_NOTE') {
|
|
||||||
return !note.description.match(regExp)
|
|
||||||
} else if (note.type === 'MARKDOWN_NOTE') {
|
|
||||||
return !note.content.match(regExp)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
} else if (block.match(/^#.+/)) {
|
|
||||||
let tag = block.match(/#(.+)/)[1]
|
|
||||||
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
|
|
||||||
notes = notes
|
|
||||||
.filter((note) => {
|
|
||||||
if (!_.isArray(note.tags)) return false
|
|
||||||
return note.tags.some((_tag) => {
|
|
||||||
return _tag.match(regExp)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
let regExp = new RegExp(_.escapeRegExp(block), 'i')
|
|
||||||
notes = notes.filter((note) => {
|
|
||||||
if (_.isArray(note.tags) && note.tags.some((_tag) => {
|
|
||||||
return _tag.match(regExp)
|
|
||||||
})) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (note.type === 'SNIPPET_NOTE') {
|
|
||||||
return note.description.match(regExp)
|
|
||||||
} else if (note.type === 'MARKDOWN_NOTE') {
|
|
||||||
return note.content.match(regExp)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return notes
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOptionClick (uniqueKey) {
|
handleOptionClick (uniqueKey) {
|
||||||
return (e) => {
|
return (e) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchPopupOpen: false
|
isSearching: false
|
||||||
}, () => {
|
}, () => {
|
||||||
let { location } = this.props
|
let { location } = this.props
|
||||||
hashHistory.push({
|
hashHistory.push({
|
||||||
@@ -170,7 +125,7 @@ class TopBar extends React.Component {
|
|||||||
|
|
||||||
handleSearchFocus (e) {
|
handleSearchFocus (e) {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchPopupOpen: true
|
isSearching: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
handleSearchBlur (e) {
|
handleSearchBlur (e) {
|
||||||
@@ -187,52 +142,11 @@ class TopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
if (!isStillFocused) {
|
if (!isStillFocused) {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchPopupOpen: false
|
isSearching: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContextButtonClick (e) {
|
|
||||||
let { config } = this.props
|
|
||||||
|
|
||||||
let menu = new Menu()
|
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'Create Markdown Note',
|
|
||||||
click: (e) => this.createNote('MARKDOWN_NOTE')
|
|
||||||
}))
|
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'Create Snippet Note',
|
|
||||||
click: (e) => this.createNote('SNIPPET_NOTE')
|
|
||||||
}))
|
|
||||||
menu.append(new MenuItem({
|
|
||||||
type: 'separator'
|
|
||||||
}))
|
|
||||||
menu.append(new MenuItem({
|
|
||||||
label: 'Change Default Note',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
type: 'radio',
|
|
||||||
label: 'Markdown Note',
|
|
||||||
checked: config.ui.defaultNote === 'MARKDOWN_NOTE',
|
|
||||||
click: (e) => this.setDefaultNote('MARKDOWN_NOTE')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'radio',
|
|
||||||
label: 'Snippet Note',
|
|
||||||
checked: config.ui.defaultNote === 'SNIPPET_NOTE',
|
|
||||||
click: (e) => this.setDefaultNote('SNIPPET_NOTE')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'radio',
|
|
||||||
label: 'Always Ask',
|
|
||||||
checked: config.ui.defaultNote === 'ALWAYS_ASK',
|
|
||||||
click: (e) => this.setDefaultNote('ALWAYS_ASK')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}))
|
|
||||||
menu.popup(remote.getCurrentWindow())
|
|
||||||
}
|
|
||||||
|
|
||||||
createNote (noteType) {
|
createNote (noteType) {
|
||||||
let { dispatch, location } = this.props
|
let { dispatch, location } = this.props
|
||||||
if (noteType !== 'MARKDOWN_NOTE' && noteType !== 'SNIPPET_NOTE') throw new Error('Invalid note type.')
|
if (noteType !== 'MARKDOWN_NOTE' && noteType !== 'SNIPPET_NOTE') throw new Error('Invalid note type.')
|
||||||
@@ -287,29 +201,16 @@ class TopBar extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOnSearchFocus () {
|
||||||
|
if (this.state.isSearching) {
|
||||||
|
this.refs.search.childNodes[0].blur()
|
||||||
|
} else {
|
||||||
|
this.refs.search.childNodes[0].focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { config, style, data } = this.props
|
let { config, style, data } = this.props
|
||||||
let searchOptionList = this.getOptions()
|
|
||||||
.map((note) => {
|
|
||||||
let storage = data.storageMap.get(note.storage)
|
|
||||||
let folder = _.find(storage.folders, {key: note.folder})
|
|
||||||
return <div styleName='control-search-optionList-item'
|
|
||||||
key={note.storage + '-' + note.key}
|
|
||||||
onClick={(e) => this.handleOptionClick(note.storage + '-' + note.key)(e)}
|
|
||||||
>
|
|
||||||
<div styleName='control-search-optionList-item-folder'
|
|
||||||
style={{borderColor: folder.color}}>
|
|
||||||
{folder.name}
|
|
||||||
<span styleName='control-search-optionList-item-folder-surfix'>in {storage.name}</span>
|
|
||||||
</div>
|
|
||||||
{note.type === 'SNIPPET_NOTE'
|
|
||||||
? <i styleName='control-search-optionList-item-type' className='fa fa-code'/>
|
|
||||||
: <i styleName='control-search-optionList-item-type' className='fa fa-file-text-o'/>
|
|
||||||
}
|
|
||||||
{note.title}
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='TopBar'
|
<div className='TopBar'
|
||||||
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
|
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
|
||||||
@@ -317,7 +218,6 @@ class TopBar extends React.Component {
|
|||||||
>
|
>
|
||||||
<div styleName='control'>
|
<div styleName='control'>
|
||||||
<div styleName='control-search'>
|
<div styleName='control-search'>
|
||||||
<i styleName='control-search-icon' className='fa fa-search fa-fw'/>
|
|
||||||
<div styleName='control-search-input'
|
<div styleName='control-search-input'
|
||||||
onFocus={(e) => this.handleSearchFocus(e)}
|
onFocus={(e) => this.handleSearchFocus(e)}
|
||||||
onBlur={(e) => this.handleSearchBlur(e)}
|
onBlur={(e) => this.handleSearchBlur(e)}
|
||||||
@@ -330,37 +230,25 @@ class TopBar extends React.Component {
|
|||||||
onChange={(e) => this.handleSearchChange(e)}
|
onChange={(e) => this.handleSearchChange(e)}
|
||||||
placeholder='Search'
|
placeholder='Search'
|
||||||
type='text'
|
type='text'
|
||||||
|
className='searchInput'
|
||||||
/>
|
/>
|
||||||
{this.state.searchPopupOpen &&
|
|
||||||
<div styleName='control-search-optionList'>
|
|
||||||
{searchOptionList.length > 0
|
|
||||||
? searchOptionList
|
|
||||||
: <div styleName='control-search-optionList-empty'>Empty List</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
{this.state.search > 0 &&
|
{this.state.search > 0 &&
|
||||||
<button styleName='left-search-clearButton'
|
<button styleName='left-search-clearButton'
|
||||||
onClick={(e) => this.handleSearchClearButton(e)}
|
onClick={(e) => this.handleSearchClearButton(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-times'/>
|
<i className='fa fa-times' />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button styleName='control-newPostButton'
|
<button styleName='control-newPostButton'
|
||||||
onClick={(e) => this.handleNewPostButtonClick(e)}>
|
onClick={(e) => this.handleNewPostButtonClick(e)}>
|
||||||
<i className='fa fa-plus'/>
|
<i className='fa fa-pencil-square-o' />
|
||||||
<span styleName='control-newPostButton-tooltip'>
|
<span styleName='control-newPostButton-tooltip'>
|
||||||
New Note {OSX ? '⌘' : '^'} + n
|
Make a Note {OSX ? '⌘' : '^'} + n
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button styleName='control-contextButton'
|
|
||||||
onClick={(e) => this.handleContextButtonClick(e)}
|
|
||||||
>
|
|
||||||
<i className='fa fa-caret-down'/>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
global-reset()
|
global-reset()
|
||||||
|
|
||||||
DEFAULT_FONTS = 'Lato', helvetica, arial, sans-serif
|
DEFAULT_FONTS = 'OpenSans', helvetica, arial, sans-serif
|
||||||
|
|
||||||
html, body
|
html, body
|
||||||
width 100%
|
width 100%
|
||||||
@@ -11,7 +11,7 @@ body
|
|||||||
font-family DEFAULT_FONTS
|
font-family DEFAULT_FONTS
|
||||||
color textColor
|
color textColor
|
||||||
font-size fontSize
|
font-size fontSize
|
||||||
font-weight 400
|
font-weight 200
|
||||||
|
|
||||||
button, input, select, textarea
|
button, input, select, textarea
|
||||||
font-family DEFAULT_FONTS
|
font-family DEFAULT_FONTS
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ document.addEventListener('dragover', function (e) {
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
document.addEventListener('click', function (e) {
|
||||||
|
const className = e.target.className
|
||||||
|
if (!className && typeof (className) !== 'string') return
|
||||||
|
const isInfoButton = className.includes('infoButton')
|
||||||
|
const isInfoPanel = e.target.offsetParent.className.includes('infoPanel')
|
||||||
|
if (isInfoButton || isInfoPanel) return
|
||||||
|
const infoPanel = document.querySelector('.infoPanel')
|
||||||
|
if (infoPanel) infoPanel.style.display = 'none'
|
||||||
|
})
|
||||||
|
|
||||||
let el = document.getElementById('content')
|
let el = document.getElementById('content')
|
||||||
const history = syncHistoryWithStore(hashHistory, store)
|
const history = syncHistoryWithStore(hashHistory, store)
|
||||||
|
|
||||||
@@ -50,6 +60,8 @@ ReactDOM.render((
|
|||||||
<IndexRedirect to='/home' />
|
<IndexRedirect to='/home' />
|
||||||
<Route path='home' />
|
<Route path='home' />
|
||||||
<Route path='starred' />
|
<Route path='starred' />
|
||||||
|
<Route path='searched' />
|
||||||
|
<Route path='trashed' />
|
||||||
<Route path='storages'>
|
<Route path='storages'>
|
||||||
<IndexRedirect to='/home' />
|
<IndexRedirect to='/home' />
|
||||||
<Route path=':storageKey'>
|
<Route path=':storageKey'>
|
||||||
|
|||||||
41
browser/main/lib/AwsMobileAnalyticsConfig.js
Normal file
41
browser/main/lib/AwsMobileAnalyticsConfig.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
const AWS = require('aws-sdk')
|
||||||
|
const AMA = require('aws-sdk-mobile-analytics')
|
||||||
|
const ConfigManager = require('browser/main/lib/ConfigManager')
|
||||||
|
|
||||||
|
AWS.config.region = 'us-east-1'
|
||||||
|
if (process.env.NODE_ENV === 'production' && ConfigManager.default.get().amaEnabled) {
|
||||||
|
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
|
||||||
|
IdentityPoolId: 'us-east-1:xxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||||
|
})
|
||||||
|
const mobileAnalyticsClient = new AMA.Manager({
|
||||||
|
appId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||||
|
appTitle: 'xxxxxxxxxx'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function initAwsMobileAnalytics () {
|
||||||
|
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
|
||||||
|
AWS.config.credentials.get((err) => {
|
||||||
|
if (!err) {
|
||||||
|
console.log('Cognito Identity ID: ' + AWS.config.credentials.identityId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
recordStaticCustomEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordDynamicCustomEvent (type) {
|
||||||
|
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
|
||||||
|
mobileAnalyticsClient.recordEvent(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordStaticCustomEvent () {
|
||||||
|
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
|
||||||
|
mobileAnalyticsClient.recordEvent('UI_COLOR_THEME', {
|
||||||
|
uiColorTheme: ConfigManager.default.get().ui.theme
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
initAwsMobileAnalytics,
|
||||||
|
recordDynamicCustomEvent
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
const OSX = global.process.platform === 'darwin'
|
const OSX = global.process.platform === 'darwin'
|
||||||
|
const win = global.process.platform === 'win32'
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const { ipcRenderer } = electron
|
const { ipcRenderer } = electron
|
||||||
const consts = require('browser/lib/consts')
|
const consts = require('browser/lib/consts')
|
||||||
@@ -10,10 +11,11 @@ let isInitialized = false
|
|||||||
export const DEFAULT_CONFIG = {
|
export const DEFAULT_CONFIG = {
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
isSideNavFolded: false,
|
isSideNavFolded: false,
|
||||||
listWidth: 250,
|
listWidth: 280,
|
||||||
navWidth: 200,
|
navWidth: 200,
|
||||||
sortBy: 'UPDATED_AT', // 'CREATED_AT', 'UPDATED_AT', 'APLHABETICAL'
|
sortBy: 'UPDATED_AT', // 'CREATED_AT', 'UPDATED_AT', 'APLHABETICAL'
|
||||||
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
|
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
|
||||||
|
amaEnabled: true,
|
||||||
hotkey: {
|
hotkey: {
|
||||||
toggleFinder: OSX ? 'Cmd + Alt + S' : 'Super + Alt + S',
|
toggleFinder: OSX ? 'Cmd + Alt + S' : 'Super + Alt + S',
|
||||||
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
|
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
|
||||||
@@ -24,17 +26,18 @@ export const DEFAULT_CONFIG = {
|
|||||||
defaultNote: 'ALWAYS_ASK' // 'ALWAYS_ASK', 'SNIPPET_NOTE', 'MARKDOWN_NOTE'
|
defaultNote: 'ALWAYS_ASK' // 'ALWAYS_ASK', 'SNIPPET_NOTE', 'MARKDOWN_NOTE'
|
||||||
},
|
},
|
||||||
editor: {
|
editor: {
|
||||||
theme: 'default',
|
theme: 'base16-light',
|
||||||
|
keyMap: 'sublime',
|
||||||
fontSize: '14',
|
fontSize: '14',
|
||||||
fontFamily: 'Monaco, Consolas',
|
fontFamily: win ? 'Segoe UI' : 'Monaco, Consolas',
|
||||||
indentType: 'space',
|
indentType: 'space',
|
||||||
indentSize: '2',
|
indentSize: '2',
|
||||||
switchPreview: 'BLUR' // Available value: RIGHTCLICK, BLUR
|
switchPreview: 'BLUR' // Available value: RIGHTCLICK, BLUR
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
fontSize: '14',
|
fontSize: '14',
|
||||||
fontFamily: 'Lato',
|
fontFamily: win ? 'Segoe UI' : 'Lato',
|
||||||
codeBlockTheme: 'xcode',
|
codeBlockTheme: 'dracula',
|
||||||
lineNumber: true
|
lineNumber: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
browser/main/lib/dataApi/copyImage.js
Normal file
36
browser/main/lib/dataApi/copyImage.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
const _ = require('lodash')
|
||||||
|
const sander = require('sander')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description To copy an image and return the path.
|
||||||
|
* @param {String} filePath
|
||||||
|
* @param {String} storageKey
|
||||||
|
* @return {String} an image path
|
||||||
|
*/
|
||||||
|
function copyImage (filePath, storageKey) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
|
||||||
|
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
|
||||||
|
const storage = _.find(cachedStorageList, {key: storageKey})
|
||||||
|
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
|
||||||
|
const targetStorage = storage
|
||||||
|
|
||||||
|
const inputImage = fs.createReadStream(filePath)
|
||||||
|
const imageExt = path.extname(filePath)
|
||||||
|
const imageName = Math.random().toString(36).slice(-16)
|
||||||
|
const basename = `${imageName}${imageExt}`
|
||||||
|
const imageDir = path.join(targetStorage.path, 'images')
|
||||||
|
if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir)
|
||||||
|
const outputImage = fs.createWriteStream(path.join(imageDir, basename))
|
||||||
|
inputImage.pipe(outputImage)
|
||||||
|
resolve(basename)
|
||||||
|
} catch (e) {
|
||||||
|
return reject(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = copyImage
|
||||||
@@ -10,6 +10,7 @@ function validateInput (input) {
|
|||||||
input.tags = input.tags.filter((tag) => _.isString(tag) && tag.trim().length > 0)
|
input.tags = input.tags.filter((tag) => _.isString(tag) && tag.trim().length > 0)
|
||||||
if (!_.isString(input.title)) input.title = ''
|
if (!_.isString(input.title)) input.title = ''
|
||||||
input.isStarred = !!input.isStarred
|
input.isStarred = !!input.isStarred
|
||||||
|
input.isTrashed = !!input.isTrashed
|
||||||
|
|
||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case 'MARKDOWN_NOTE':
|
case 'MARKDOWN_NOTE':
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function init () {
|
|||||||
.then((notes) => {
|
.then((notes) => {
|
||||||
let unknownCount = 0
|
let unknownCount = 0
|
||||||
notes.forEach((note) => {
|
notes.forEach((note) => {
|
||||||
if (!storage.folders.some((folder) => note.folder === folder.key)) {
|
if (note && !storage.folders.some((folder) => note.folder === folder.key)) {
|
||||||
unknownCount++
|
unknownCount++
|
||||||
storage.folders.push({
|
storage.folders.push({
|
||||||
key: note.folder,
|
key: note.folder,
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ function resolveStorageNotes (storage) {
|
|||||||
return data
|
return data
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(notePath)
|
console.error(notePath)
|
||||||
throw err
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ function validateInput (input) {
|
|||||||
validatedInput.isStarred = !!input.isStarred
|
validatedInput.isStarred = !!input.isStarred
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (input.isTrashed != null) {
|
||||||
|
validatedInput.isTrashed = !!input.isTrashed
|
||||||
|
}
|
||||||
|
|
||||||
validatedInput.type = input.type
|
validatedInput.type = input.type
|
||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case 'MARKDOWN_NOTE':
|
case 'MARKDOWN_NOTE':
|
||||||
@@ -101,6 +105,7 @@ function updateNote (storageKey, noteKey, input) {
|
|||||||
noteData.createdAt = new Date()
|
noteData.createdAt = new Date()
|
||||||
noteData.updatedAt = new Date()
|
noteData.updatedAt = new Date()
|
||||||
noteData.isStarred = false
|
noteData.isStarred = false
|
||||||
|
noteData.isTrashed = false
|
||||||
noteData.tags = []
|
noteData.tags = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,18 @@ class ModalBase extends React.Component {
|
|||||||
|
|
||||||
close () {
|
close () {
|
||||||
if (modalBase != null) modalBase.setState({component: null, componentProps: null, isHidden: true})
|
if (modalBase != null) modalBase.setState({component: null, componentProps: null, isHidden: true})
|
||||||
|
// Toggle overflow style on NoteList
|
||||||
|
let list = document.querySelector('.NoteList__list___browser-main-NoteList-')
|
||||||
|
list.style.overflow = 'auto'
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className={'ModalBase' + (this.state.isHidden ? ' hide' : '')}>
|
<div className={'ModalBase' + (this.state.isHidden ? ' hide' : '')}>
|
||||||
<div onClick={(e) => this.close(e)} className='modalBack'/>
|
<div onClick={(e) => this.close(e)} className='modalBack' />
|
||||||
{this.state.component == null ? null : (
|
{this.state.component == null ? null : (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<this.state.component {...this.state.componentProps} close={this.close}/>
|
<this.state.component {...this.state.componentProps} close={this.close} />
|
||||||
</Provider>
|
</Provider>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -33,11 +36,13 @@ class ModalBase extends React.Component {
|
|||||||
|
|
||||||
let el = document.createElement('div')
|
let el = document.createElement('div')
|
||||||
document.body.appendChild(el)
|
document.body.appendChild(el)
|
||||||
let modalBase = ReactDOM.render(<ModalBase/>, el)
|
let modalBase = ReactDOM.render(<ModalBase />, el)
|
||||||
|
|
||||||
export function openModal (component, props) {
|
export function openModal (component, props) {
|
||||||
if (modalBase == null) { return }
|
if (modalBase == null) { return }
|
||||||
|
// Hide scrollbar by removing overflow when modal opens
|
||||||
|
let list = document.querySelector('.NoteList__list___browser-main-NoteList-')
|
||||||
|
list.style.overflow = 'hidden'
|
||||||
document.body.setAttribute('data-modal', 'open')
|
document.body.setAttribute('data-modal', 'open')
|
||||||
modalBase.setState({component: component, componentProps: props, isHidden: false})
|
modalBase.setState({component: component, componentProps: props, isHidden: false})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import styles from './CreateFolderModal.styl'
|
|||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
import store from 'browser/main/store'
|
import store from 'browser/main/store'
|
||||||
import consts from 'browser/lib/consts'
|
import consts from 'browser/lib/consts'
|
||||||
|
import ModalEscButton from 'browser/components/ModalEscButton'
|
||||||
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
|
||||||
class CreateFolderModal extends React.Component {
|
class CreateFolderModal extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -47,6 +49,7 @@ class CreateFolderModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
confirm () {
|
confirm () {
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_FOLDER')
|
||||||
if (this.state.name.trim().length > 0) {
|
if (this.state.name.trim().length > 0) {
|
||||||
let { storage } = this.props
|
let { storage } = this.props
|
||||||
let input = {
|
let input = {
|
||||||
@@ -75,24 +78,23 @@ class CreateFolderModal extends React.Component {
|
|||||||
onKeyDown={(e) => this.handleKeyDown(e)}
|
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||||
>
|
>
|
||||||
<div styleName='header'>
|
<div styleName='header'>
|
||||||
<div styleName='title'>New Folder</div>
|
<div styleName='title'>Create new folder</div>
|
||||||
</div>
|
</div>
|
||||||
<button styleName='closeButton'
|
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
|
||||||
onClick={(e) => this.handleCloseButtonClick(e)}
|
|
||||||
>Close</button>
|
|
||||||
|
|
||||||
<div styleName='control'>
|
<div styleName='control'>
|
||||||
<input styleName='control-input'
|
<div styleName='control-folder'>
|
||||||
placeholder='Folder Name'
|
<div styleName='control-folder-label'>Folder name</div>
|
||||||
ref='name'
|
<input styleName='control-folder-input'
|
||||||
value={this.state.name}
|
ref='name'
|
||||||
onChange={(e) => this.handleChange(e)}
|
value={this.state.name}
|
||||||
onKeyDown={(e) => this.handleInputKeyDown(e)}
|
onChange={(e) => this.handleChange(e)}
|
||||||
/>
|
onKeyDown={(e) => this.handleInputKeyDown(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<button styleName='control-confirmButton'
|
<button styleName='control-confirmButton'
|
||||||
onClick={(e) => this.handleConfirmButtonClick(e)}
|
onClick={(e) => this.handleConfirmButtonClick(e)}
|
||||||
>
|
>
|
||||||
Confirm
|
Create
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,47 +1,37 @@
|
|||||||
.root
|
.root
|
||||||
modal()
|
modal()
|
||||||
max-width 340px
|
width 500px
|
||||||
|
height 270px
|
||||||
overflow hidden
|
overflow hidden
|
||||||
position relative
|
position relative
|
||||||
|
padding 0 40px
|
||||||
|
|
||||||
.header
|
.header
|
||||||
height 50px
|
height 70px
|
||||||
|
margin-bottom 10px
|
||||||
|
margin-top 20px
|
||||||
font-size 18px
|
font-size 18px
|
||||||
line-height 50px
|
line-height 50px
|
||||||
padding 0 15px
|
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-backgroundColor
|
||||||
border-bottom solid 1px $ui-borderColor
|
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
|
|
||||||
.closeButton
|
.control-folder-label
|
||||||
position absolute
|
text-align left
|
||||||
top 10px
|
font-size 12px
|
||||||
right 10px
|
|
||||||
height 30px
|
|
||||||
width 0 25px
|
|
||||||
border $ui-border
|
|
||||||
border-radius 2px
|
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
colorDefaultButton()
|
|
||||||
|
|
||||||
.control
|
.control-folder-input
|
||||||
padding 25px 15px 15px
|
|
||||||
text-align center
|
|
||||||
|
|
||||||
.control-input
|
|
||||||
display block
|
display block
|
||||||
height 30px
|
height 30px
|
||||||
width 240px
|
width 420px
|
||||||
padding 0 5px
|
padding 0 5px
|
||||||
margin 0 auto 15px
|
margin 10px auto 15px
|
||||||
border none
|
border 1px solid #C9C9C9 // TODO: use variable.
|
||||||
border-bottom solid 1px $border-color
|
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
background-color transparent
|
background-color transparent
|
||||||
outline none
|
outline none
|
||||||
vertical-align middle
|
vertical-align middle
|
||||||
font-size 18px
|
font-size 14px
|
||||||
text-align center
|
|
||||||
&:disabled
|
&:disabled
|
||||||
background-color $ui-input--disabled-backgroundColor
|
background-color $ui-input--disabled-backgroundColor
|
||||||
&:focus, &:active
|
&:focus, &:active
|
||||||
@@ -49,31 +39,39 @@
|
|||||||
|
|
||||||
.control-confirmButton
|
.control-confirmButton
|
||||||
display block
|
display block
|
||||||
|
float right
|
||||||
height 30px
|
height 30px
|
||||||
|
width 100px
|
||||||
border none
|
border none
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
padding 0 25px
|
padding 0 25px
|
||||||
margin 0 auto
|
margin 20px auto
|
||||||
|
font-size 12px
|
||||||
colorPrimaryButton()
|
colorPrimaryButton()
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
modalDark()
|
modalDark()
|
||||||
|
width 500px
|
||||||
|
height 270px
|
||||||
|
overflow hidden
|
||||||
|
position relative
|
||||||
|
padding 0 40px
|
||||||
|
|
||||||
.header
|
.header
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color transparent
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|
||||||
.closeButton
|
.control-folder-label
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
colorDarkDefaultButton()
|
|
||||||
|
|
||||||
.description
|
.control-folder-input
|
||||||
color $ui-inactive-text-color
|
border 1px solid #C9C9C9 // TODO: use variable.
|
||||||
|
|
||||||
.control-input
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
color white
|
color white
|
||||||
|
|
||||||
|
.description
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
|
||||||
|
.control-confirmButton
|
||||||
|
colorDarkPrimaryButton()
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React, { PropTypes } from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import store from '../store'
|
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const ipc = electron.ipcRenderer
|
const ipc = electron.ipcRenderer
|
||||||
@@ -26,20 +25,19 @@ export default class DeleteArticleModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleYesButtonClick (e) {
|
handleYesButtonClick (e) {
|
||||||
// store.dispatch(destroyArticle(this.props.articleKey))
|
|
||||||
this.props.close()
|
this.props.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className='DeleteArticleModal modal'>
|
<div className='DeleteArticleModal modal'>
|
||||||
<div className='title'><i className='fa fa-fw fa-trash'/> Delete an article.</div>
|
<div className='title'><i className='fa fa-fw fa-trash' /> Delete an article.</div>
|
||||||
|
|
||||||
<div className='message'>Do you really want to delete?</div>
|
<div className='message'>Do you really want to delete?</div>
|
||||||
|
|
||||||
<div className='control'>
|
<div className='control'>
|
||||||
<button ref='no' onClick={(e) => this.handleNoButtonClick(e)}><i className='fa fa-fw fa-close'/> No</button>
|
<button ref='no' onClick={(e) => this.handleNoButtonClick(e)}><i className='fa fa-fw fa-close' /> No</button>
|
||||||
<button ref='yes' onClick={(e) => this.handleYesButtonClick(e)} className='danger'><i className='fa fa-fw fa-check'/> Yes</button>
|
<button ref='yes' onClick={(e) => this.handleYesButtonClick(e)} className='danger'><i className='fa fa-fw fa-check' /> Yes</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React from 'react'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './InitModal.styl'
|
import styles from './InitModal.styl'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
import store from 'browser/main/store'
|
import store from 'browser/main/store'
|
||||||
import { hashHistory } from 'react-router'
|
import { hashHistory } from 'react-router'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import keygen from 'browser/lib/keygen'
|
import ModalEscButton from 'browser/components/ModalEscButton'
|
||||||
|
|
||||||
const CSON = require('@rokt33r/season')
|
const CSON = require('@rokt33r/season')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
@@ -42,10 +42,6 @@ class InitModal extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCloseButtonClick (e) {
|
|
||||||
this.props.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePathChange (e) {
|
handlePathChange (e) {
|
||||||
this.setState({
|
this.setState({
|
||||||
path: e.target.value
|
path: e.target.value
|
||||||
@@ -107,7 +103,7 @@ class InitModal extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
return dataApi
|
return dataApi
|
||||||
.createFolder(data.storage.key, {
|
.createFolder(data.storage.key, {
|
||||||
color: '#6AA5E9',
|
color: '#1278BD',
|
||||||
name: 'Default'
|
name: 'Default'
|
||||||
})
|
})
|
||||||
.then((_data) => {
|
.then((_data) => {
|
||||||
@@ -126,12 +122,24 @@ class InitModal extends React.Component {
|
|||||||
notes: data.notes
|
notes: data.notes
|
||||||
})
|
})
|
||||||
|
|
||||||
let defaultMarkdownNote = dataApi
|
let defaultSnippetNote = dataApi
|
||||||
.createNote(data.storage.key, {
|
.createNote(data.storage.key, {
|
||||||
type: 'MARKDOWN_NOTE',
|
type: 'SNIPPET_NOTE',
|
||||||
folder: data.storage.folders[0].key,
|
folder: data.storage.folders[0].key,
|
||||||
title: 'Welcome to Boostnote :)',
|
title: 'Snippet note example',
|
||||||
content: '# Welcome to Boostnote :)\nThis is a markdown note.\n\nClick to edit this note.'
|
description: 'Snippet note example\nYou can store a series of snippets as a single note, like Gist.',
|
||||||
|
snippets: [
|
||||||
|
{
|
||||||
|
name: 'example.html',
|
||||||
|
mode: 'html',
|
||||||
|
content: '<html>\n<body>\n<h1 id=\'hello\'>Enjoy Boostnote!</h1>\n</body>\n</html>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'example.js',
|
||||||
|
mode: 'javascript',
|
||||||
|
content: 'var boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)'
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
.then((note) => {
|
.then((note) => {
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
@@ -139,24 +147,12 @@ class InitModal extends React.Component {
|
|||||||
note: note
|
note: note
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
let defaultSnippetNote = dataApi
|
let defaultMarkdownNote = dataApi
|
||||||
.createNote(data.storage.key, {
|
.createNote(data.storage.key, {
|
||||||
type: 'SNIPPET_NOTE',
|
type: 'MARKDOWN_NOTE',
|
||||||
folder: data.storage.folders[0].key,
|
folder: data.storage.folders[0].key,
|
||||||
title: 'Snippet note example',
|
title: 'Welcome to Boostnote!',
|
||||||
description: 'Snippet note example\nYou can store a series of snippet as a single note like Gist.',
|
content: '# Welcome to Boostnote! \n### _Click to edit this note._\n\n---\n\nBoostnote is an *open source* note-taking app. \nRepository is published on [GitHub](https://github.com/BoostIO/Boostnote), and tweeting everyday on [@Boostnoteapp](https://twitter.com/boostnoteapp)!\n\n## Features \n- [x] No Internet and Registration Required. \n- [ ] Quick search and copy the content of note. `macOS: Cmd + Alt + S / windows: Ctrl + Alt + S` \n- [ ] Markdown & Snippet note. \n- [ ] Available for `vim` and `emacs` mode. \n- [ ] Choose your favorite theme on UI, Editor and Code Block! \n--- \n\n- Copy Codeblock on Makrdown Preview.\n```javascript\nvar boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)\n```'
|
||||||
snippets: [
|
|
||||||
{
|
|
||||||
name: 'example.html',
|
|
||||||
mode: 'html',
|
|
||||||
content: '<html>\n<body>\n<h1 id=\'hello\'>Hello World</h1>\n</body>\n</html>'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'example.js',
|
|
||||||
mode: 'javascript',
|
|
||||||
content: 'var html = document.getElementById(\'hello\').innerHTML\n\nconsole.log(html)'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
.then((note) => {
|
.then((note) => {
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
@@ -188,16 +184,10 @@ class InitModal extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyDown (e) {
|
|
||||||
if (e.keyCode === 27) {
|
|
||||||
this.props.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
if (this.state.isLoading) {
|
if (this.state.isLoading) {
|
||||||
return <div styleName='root--loading'>
|
return <div styleName='root--loading'>
|
||||||
<i styleName='spinner' className='fa fa-spin fa-spinner'/>
|
<i styleName='spinner' className='fa fa-spin fa-spinner' />
|
||||||
<div styleName='loadingMessage'>Preparing initialization...</div>
|
<div styleName='loadingMessage'>Preparing initialization...</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -210,15 +200,13 @@ class InitModal extends React.Component {
|
|||||||
<div styleName='header'>
|
<div styleName='header'>
|
||||||
<div styleName='header-title'>Initialize Storage</div>
|
<div styleName='header-title'>Initialize Storage</div>
|
||||||
</div>
|
</div>
|
||||||
<button styleName='closeButton'
|
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
|
||||||
onClick={(e) => this.handleCloseButtonClick(e)}
|
|
||||||
>Close</button>
|
|
||||||
<div styleName='body'>
|
<div styleName='body'>
|
||||||
<div styleName='body-welcome'>
|
<div styleName='body-welcome'>
|
||||||
Welcome you!
|
Welcome!
|
||||||
</div>
|
</div>
|
||||||
<div styleName='body-description'>
|
<div styleName='body-description'>
|
||||||
Boostnote will use this directory as a default storage.
|
Please select a directory for Boostnote storage.
|
||||||
</div>
|
</div>
|
||||||
<div styleName='body-path'>
|
<div styleName='body-path'>
|
||||||
<input styleName='body-path-input'
|
<input styleName='body-path-input'
|
||||||
@@ -235,7 +223,7 @@ class InitModal extends React.Component {
|
|||||||
|
|
||||||
{this.state.legacyStorageExists &&
|
{this.state.legacyStorageExists &&
|
||||||
<div styleName='body-migration'>
|
<div styleName='body-migration'>
|
||||||
<label><input type='checkbox' checked={this.state.migrationRequested} onChange={(e) => this.handleMigrationRequestedChange(e)}/> Migrate old data from the legacy app v0.5</label>
|
<label><input type='checkbox' checked={this.state.migrationRequested} onChange={(e) => this.handleMigrationRequestedChange(e)} /> Migrate old data from the legacy app v0.5</label>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +235,7 @@ class InitModal extends React.Component {
|
|||||||
>
|
>
|
||||||
{this.state.isSending
|
{this.state.isSending
|
||||||
? <span>
|
? <span>
|
||||||
<i className='fa fa-spin fa-spinner'/> Loading...
|
<i className='fa fa-spin fa-spinner' /> Loading...
|
||||||
</span>
|
</span>
|
||||||
: 'Let\'s Go!'
|
: 'Let\'s Go!'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,17 +22,6 @@
|
|||||||
border-bottom solid 1px $ui-borderColor
|
border-bottom solid 1px $ui-borderColor
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
|
|
||||||
.closeButton
|
|
||||||
position absolute
|
|
||||||
top 10px
|
|
||||||
right 10px
|
|
||||||
height 30px
|
|
||||||
padding 0 25px
|
|
||||||
border $ui-border
|
|
||||||
border-radius 2px
|
|
||||||
color $ui-text-color
|
|
||||||
colorDefaultButton()
|
|
||||||
|
|
||||||
.body
|
.body
|
||||||
padding 30px
|
padding 30px
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React from 'react'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './NewNoteModal.styl'
|
import styles from './NewNoteModal.styl'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
import { hashHistory } from 'react-router'
|
import { hashHistory } from 'react-router'
|
||||||
import ee from 'browser/main/lib/eventEmitter'
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
import ModalEscButton from 'browser/components/ModalEscButton'
|
||||||
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
|
||||||
class NewNoteModal extends React.Component {
|
class NewNoteModal extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -22,6 +24,8 @@ class NewNoteModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleMarkdownNoteButtonClick (e) {
|
handleMarkdownNoteButtonClick (e) {
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_MARKDOWN')
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
|
||||||
let { storage, folder, dispatch, location } = this.props
|
let { storage, folder, dispatch, location } = this.props
|
||||||
dataApi
|
dataApi
|
||||||
.createNote(storage, {
|
.createNote(storage, {
|
||||||
@@ -52,6 +56,8 @@ class NewNoteModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSnippetNoteButtonClick (e) {
|
handleSnippetNoteButtonClick (e) {
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_SNIPPET')
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
|
||||||
let { storage, folder, dispatch, location } = this.props
|
let { storage, folder, dispatch, location } = this.props
|
||||||
|
|
||||||
dataApi
|
dataApi
|
||||||
@@ -100,12 +106,9 @@ class NewNoteModal extends React.Component {
|
|||||||
onKeyDown={(e) => this.handleKeyDown(e)}
|
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||||
>
|
>
|
||||||
<div styleName='header'>
|
<div styleName='header'>
|
||||||
<div styleName='title'>New Note</div>
|
<div styleName='title'>Make a Note</div>
|
||||||
</div>
|
</div>
|
||||||
<button styleName='closeButton'
|
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
|
||||||
onClick={(e) => this.handleCloseButtonClick(e)}
|
|
||||||
>Close</button>
|
|
||||||
|
|
||||||
<div styleName='control'>
|
<div styleName='control'>
|
||||||
<button styleName='control-button'
|
<button styleName='control-button'
|
||||||
onClick={(e) => this.handleMarkdownNoteButtonClick(e)}
|
onClick={(e) => this.handleMarkdownNoteButtonClick(e)}
|
||||||
@@ -114,9 +117,9 @@ class NewNoteModal extends React.Component {
|
|||||||
>
|
>
|
||||||
<i styleName='control-button-icon'
|
<i styleName='control-button-icon'
|
||||||
className='fa fa-file-text-o'
|
className='fa fa-file-text-o'
|
||||||
/><br/>
|
/><br />
|
||||||
<span styleName='control-button-label'>Markdown Note</span><br/>
|
<span styleName='control-button-label'>Markdown Note</span><br />
|
||||||
<span styleName='control-button-description'>It is good for any type of documents. Check List, Code block and Latex block are available.</span>
|
<span styleName='control-button-description'>This format is for creating text documents. Checklists, code blocks and Latex blocks are available.</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button styleName='control-button'
|
<button styleName='control-button'
|
||||||
@@ -126,14 +129,14 @@ class NewNoteModal extends React.Component {
|
|||||||
>
|
>
|
||||||
<i styleName='control-button-icon'
|
<i styleName='control-button-icon'
|
||||||
className='fa fa-code'
|
className='fa fa-code'
|
||||||
/><br/>
|
/><br />
|
||||||
<span styleName='control-button-label'>Snippet Note</span><br/>
|
<span styleName='control-button-label'>Snippet Note</span><br />
|
||||||
<span styleName='control-button-description'>This format is specialized on managing snippets like Gist. Multiple snippets can be grouped as a note.
|
<span styleName='control-button-description'>This format is for creating code snippets. Multiple snippets can be grouped into a single note.
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div styleName='description'><i className='fa fa-arrows-h'/> Tab to switch format</div>
|
<div styleName='description'><i className='fa fa-arrows-h' /> Tab to switch format</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,17 +13,6 @@
|
|||||||
border-bottom solid 1px $ui-borderColor
|
border-bottom solid 1px $ui-borderColor
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
|
|
||||||
.closeButton
|
|
||||||
position absolute
|
|
||||||
top 10px
|
|
||||||
right 10px
|
|
||||||
height 30px
|
|
||||||
width 0 25px
|
|
||||||
border $ui-border
|
|
||||||
border-radius 2px
|
|
||||||
color $ui-text-color
|
|
||||||
colorDefaultButton()
|
|
||||||
|
|
||||||
.control
|
.control
|
||||||
padding 25px 15px 15px
|
padding 25px 15px 15px
|
||||||
text-align center
|
text-align center
|
||||||
@@ -64,17 +53,12 @@ body[data-theme="dark"]
|
|||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|
||||||
.closeButton
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
color $ui-dark-text-color
|
|
||||||
colorDarkDefaultButton()
|
|
||||||
|
|
||||||
.control-button
|
.control-button
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
background-color transparent
|
background-color transparent
|
||||||
&:focus
|
&:focus
|
||||||
colorPrimaryButton()
|
colorDarkPrimaryButton()
|
||||||
|
|
||||||
.description
|
.description
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
|
|||||||
@@ -1,40 +1,51 @@
|
|||||||
|
@import('./Tab')
|
||||||
|
|
||||||
.root
|
.root
|
||||||
padding 15px
|
padding 15px
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
|
margin-bottom 30px
|
||||||
|
|
||||||
.group
|
.group
|
||||||
margin-bottom 45px
|
margin-bottom 45px
|
||||||
|
|
||||||
.group-header
|
.group-header
|
||||||
font-size 24px
|
@extend .header
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
padding 5px
|
|
||||||
border-bottom $default-border
|
|
||||||
margin-bottom 15px
|
|
||||||
|
|
||||||
.group-header2
|
.group-header2
|
||||||
font-size 18px
|
font-size 20px
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
padding 5px
|
|
||||||
margin-bottom 15px
|
margin-bottom 15px
|
||||||
|
margin-top 30px
|
||||||
|
|
||||||
.group-section
|
.group-section
|
||||||
margin-bottom 15px
|
margin-bottom 20px
|
||||||
display flex
|
display flex
|
||||||
line-height 30px
|
line-height 30px
|
||||||
|
|
||||||
.group-section-label
|
.group-section-label
|
||||||
width 150px
|
width 150px
|
||||||
text-align right
|
text-align left
|
||||||
margin-right 10px
|
margin-right 10px
|
||||||
font-size 14px
|
font-size 14px
|
||||||
|
|
||||||
.group-section-control
|
.group-section-control
|
||||||
flex 1
|
flex 1
|
||||||
|
|
||||||
|
.group-section-control select
|
||||||
|
outline none
|
||||||
|
border 1px solid $ui-borderColor
|
||||||
|
background-color transparent
|
||||||
|
|
||||||
.group-section-control-input
|
.group-section-control-input
|
||||||
height 30px
|
height 30px
|
||||||
vertical-align middle
|
vertical-align middle
|
||||||
width 150px
|
width 400px
|
||||||
font-size 12px
|
font-size $tab--button-font-size
|
||||||
border solid 1px $border-color
|
border solid 1px $border-color
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
padding 0 5px
|
padding 0 5px
|
||||||
|
outline none
|
||||||
&:disabled
|
&:disabled
|
||||||
background-color $ui-input--disabled-backgroundColor
|
background-color $ui-input--disabled-backgroundColor
|
||||||
|
|
||||||
@@ -45,7 +56,6 @@
|
|||||||
padding-left 15px
|
padding-left 15px
|
||||||
|
|
||||||
.group-control
|
.group-control
|
||||||
border-top $default-border
|
|
||||||
padding-top 10px
|
padding-top 10px
|
||||||
box-sizing border-box
|
box-sizing border-box
|
||||||
height 40px
|
height 40px
|
||||||
@@ -56,27 +66,31 @@
|
|||||||
line-height 30px
|
line-height 30px
|
||||||
padding 0 5px
|
padding 0 5px
|
||||||
float right
|
float right
|
||||||
|
|
||||||
.group-control-leftButton
|
.group-control-leftButton
|
||||||
float left
|
|
||||||
colorDefaultButton()
|
colorDefaultButton()
|
||||||
border $default-border
|
border none
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
height 30px
|
font-size $tab--button-font-size
|
||||||
|
height $tab--button-height
|
||||||
padding 0 15px
|
padding 0 15px
|
||||||
margin-right 5px
|
margin-right 10px
|
||||||
|
|
||||||
.group-control-rightButton
|
.group-control-rightButton
|
||||||
float right
|
float right
|
||||||
colorPrimaryButton()
|
colorPrimaryButton()
|
||||||
border none
|
border none
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
height 30px
|
font-size $tab--button-font-size
|
||||||
padding 0 15px
|
height 35px
|
||||||
margin-right 5px
|
width 100px
|
||||||
|
margin-right 10px
|
||||||
|
|
||||||
.group-hint
|
.group-hint
|
||||||
border $ui-border
|
border $ui-border
|
||||||
padding 10px 15px
|
padding 10px 15px
|
||||||
margin 15px 0
|
margin 15px 0
|
||||||
border-radius 5px
|
border-radius 2px
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-backgroundColor
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
ul
|
ul
|
||||||
@@ -86,10 +100,21 @@
|
|||||||
p
|
p
|
||||||
line-height 1.2
|
line-height 1.2
|
||||||
|
|
||||||
|
.note-for-keymap
|
||||||
|
margin-left: 10px
|
||||||
|
font-size: 12px
|
||||||
|
|
||||||
|
.code-mirror
|
||||||
|
width 400px
|
||||||
|
height 120px
|
||||||
|
margin 5px 0
|
||||||
|
font-size 12px
|
||||||
|
|
||||||
colorDarkControl()
|
colorDarkControl()
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
background-color $ui-dark-backgroundColor
|
background-color $ui-dark-backgroundColor
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|||||||
156
browser/main/modals/PreferencesModal/HotkeyTab.js
Normal file
156
browser/main/modals/PreferencesModal/HotkeyTab.js
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './ConfigTab.styl'
|
||||||
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
|
import store from 'browser/main/store'
|
||||||
|
|
||||||
|
const electron = require('electron')
|
||||||
|
const ipc = electron.ipcRenderer
|
||||||
|
|
||||||
|
class HotkeyTab extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isHotkeyHintOpen: false,
|
||||||
|
config: props.config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
this.handleSettingDone = () => {
|
||||||
|
this.setState({keymapAlert: {
|
||||||
|
type: 'success',
|
||||||
|
message: 'Successfully applied!'
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
this.handleSettingError = (err) => {
|
||||||
|
this.setState({keymapAlert: {
|
||||||
|
type: 'error',
|
||||||
|
message: err.message != null ? err.message : 'Error occurs!'
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
ipc.addListener('APP_SETTING_DONE', this.handleSettingDone)
|
||||||
|
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone)
|
||||||
|
ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSaveButtonClick (e) {
|
||||||
|
let newConfig = {
|
||||||
|
hotkey: this.state.config.hotkey
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.set(newConfig)
|
||||||
|
|
||||||
|
store.dispatch({
|
||||||
|
type: 'SET_UI',
|
||||||
|
config: newConfig
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHintToggleButtonClick (e) {
|
||||||
|
this.setState({
|
||||||
|
isHotkeyHintOpen: !this.state.isHotkeyHintOpen
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHotkeyChange (e) {
|
||||||
|
let { config } = this.state
|
||||||
|
config.hotkey = {
|
||||||
|
toggleFinder: this.refs.toggleFinder.value,
|
||||||
|
toggleMain: this.refs.toggleMain.value
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
config
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
let keymapAlert = this.state.keymapAlert
|
||||||
|
let keymapAlertElement = keymapAlert != null
|
||||||
|
? <p className={`alert ${keymapAlert.type}`}>
|
||||||
|
{keymapAlert.message}
|
||||||
|
</p>
|
||||||
|
: null
|
||||||
|
let { config } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div styleName='root'>
|
||||||
|
<div styleName='group'>
|
||||||
|
<div styleName='group-header'>Hotkey</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>Toggle Main</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input styleName='group-section-control-input'
|
||||||
|
onChange={(e) => this.handleHotkeyChange(e)}
|
||||||
|
ref='toggleMain'
|
||||||
|
value={config.hotkey.toggleMain}
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>Toggle Finder(popup)</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input styleName='group-section-control-input'
|
||||||
|
onChange={(e) => this.handleHotkeyChange(e)}
|
||||||
|
ref='toggleFinder'
|
||||||
|
value={config.hotkey.toggleFinder}
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='group-control'>
|
||||||
|
<button styleName='group-control-leftButton'
|
||||||
|
onClick={(e) => this.handleHintToggleButtonClick(e)}
|
||||||
|
>
|
||||||
|
{this.state.isHotkeyHintOpen
|
||||||
|
? 'Hide Hint'
|
||||||
|
: 'Hint?'
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
<button styleName='group-control-rightButton'
|
||||||
|
onClick={(e) => this.handleSaveButtonClick(e)}>Save
|
||||||
|
</button>
|
||||||
|
{keymapAlertElement}
|
||||||
|
</div>
|
||||||
|
{this.state.isHotkeyHintOpen &&
|
||||||
|
<div styleName='group-hint'>
|
||||||
|
<p>Available Keys</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>0</code> to <code>9</code></li>
|
||||||
|
<li><code>A</code> to <code>Z</code></li>
|
||||||
|
<li><code>F1</code> to <code>F24</code></li>
|
||||||
|
<li>Punctuations like <code>~</code>, <code>!</code>, <code>@</code>, <code>#</code>, <code>$</code>, etc.</li>
|
||||||
|
<li><code>Plus</code></li>
|
||||||
|
<li><code>Space</code></li>
|
||||||
|
<li><code>Backspace</code></li>
|
||||||
|
<li><code>Delete</code></li>
|
||||||
|
<li><code>Insert</code></li>
|
||||||
|
<li><code>Return</code> (or <code>Enter</code> as alias)</li>
|
||||||
|
<li><code>Up</code>, <code>Down</code>, <code>Left</code> and <code>Right</code></li>
|
||||||
|
<li><code>Home</code> and <code>End</code></li>
|
||||||
|
<li><code>PageUp</code> and <code>PageDown</code></li>
|
||||||
|
<li><code>Escape</code> (or <code>Esc</code> for short)</li>
|
||||||
|
<li><code>VolumeUp</code>, <code>VolumeDown</code> and <code>VolumeMute</code></li>
|
||||||
|
<li><code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>, <code>MediaStop</code> and <code>MediaPlayPause</code></li>
|
||||||
|
<li><code>Control</code> (or <code>Ctrl</code> for short)</li>
|
||||||
|
<li><code>Shift</code></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HotkeyTab.propTypes = {
|
||||||
|
dispatch: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(HotkeyTab, styles)
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React from 'react'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './InfoTab.styl'
|
import styles from './InfoTab.styl'
|
||||||
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
|
import store from 'browser/main/store'
|
||||||
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const { shell, remote } = electron
|
const { shell, remote } = electron
|
||||||
@@ -11,6 +14,7 @@ class InfoTab extends React.Component {
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
config: this.props.config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,34 +23,89 @@ class InfoTab extends React.Component {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleConfigChange (e) {
|
||||||
|
const newConfig = { amaEnabled: this.refs.amaEnabled.checked }
|
||||||
|
|
||||||
|
this.setState({ config: newConfig })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSaveButtonClick (e) {
|
||||||
|
let newConfig = {
|
||||||
|
amaEnabled: this.state.config.amaEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.set(newConfig)
|
||||||
|
|
||||||
|
store.dispatch({
|
||||||
|
type: 'SET_CONFIG',
|
||||||
|
config: newConfig
|
||||||
|
})
|
||||||
|
if (!newConfig.amaEnabled) {
|
||||||
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('DISABLE_AMA')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div styleName='root'>
|
<div styleName='root'>
|
||||||
|
<div styleName='header'>Info</div>
|
||||||
|
|
||||||
<div styleName='top'>
|
<div styleName='top'>
|
||||||
<img styleName='icon' src='../resources/app.png' width='150' height='150'/>
|
<div styleName='icon-space'>
|
||||||
<div styleName='appId'>Boostnote {appVersion}</div>
|
<img styleName='icon' src='../resources/app.png' width='92' height='92' />
|
||||||
<div styleName='description'>
|
<div styleName='icon-right'>
|
||||||
A simple markdown/snippet note app for developer.
|
<div styleName='appId'>Boostnote {appVersion}</div>
|
||||||
|
<div styleName='description'>
|
||||||
|
An open source note-taking app made for programmers just like you.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div styleName='madeBy'>Made by
|
|
||||||
<a href='http://maisin.co/'
|
|
||||||
onClick={(e) => this.handleLinkClick(e)}
|
|
||||||
>MAISIN&CO.</a></div>
|
|
||||||
<div styleName='copyright'>Copyright 2016 MAISIN&CO. All rights reserved.</div>
|
|
||||||
</div>
|
</div>
|
||||||
<ul styleName='list'>
|
<ul styleName='list'>
|
||||||
<li>
|
<li>
|
||||||
The codes of this app is published under GPLv3 license.
|
<a href='https://boostnote.io'
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Any kinds of feedback, creating a new issue or a pull request, would be welcomed.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Issue Tracker : <a href='https://github.com/BoostIO/Boostnote/issues'
|
|
||||||
onClick={(e) => this.handleLinkClick(e)}
|
onClick={(e) => this.handleLinkClick(e)}
|
||||||
>https://github.com/BoostIO/Boostnote/issues</a>
|
>Website</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href='https://boostnote.paintory.com/'
|
||||||
|
onClick={(e) => this.handleLinkClick(e)}
|
||||||
|
>Boostnote Shop</a> : Products are shipped to all over the world 🌏
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href='https://salt.bountysource.com/teams/boostnote'
|
||||||
|
onClick={(e) => this.handleLinkClick(e)}
|
||||||
|
>Donate via Bountysource</a> : Thank you for your support 🎉
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href='https://github.com/BoostIO/Boostnote/issues'
|
||||||
|
onClick={(e) => this.handleLinkClick(e)}
|
||||||
|
>GitHub Issues</a> : We'd love to hear your feedback 🙌
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href='https://github.com/BoostIO/Boostnote/blob/master/docs/build.md'
|
||||||
|
onClick={(e) => this.handleLinkClick(e)}
|
||||||
|
>Development</a> : Development configurations for Boostnote 🚀
|
||||||
|
</li>
|
||||||
|
<li styleName='cc'>
|
||||||
|
Copyright (C) 2017 Maisin&Co.
|
||||||
|
</li>
|
||||||
|
<li styleName='cc'>
|
||||||
|
License: GPL v3
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<hr />
|
||||||
|
<div styleName='policy'>Data collection policy</div>
|
||||||
|
<p>We collect only the number of DAU for Boostnote and DO NOT collect any detail information</p>
|
||||||
|
<p>such as your note content. You can see how it works on <a href='https://github.com/BoostIO/Boostnote' onClick={(e) => this.handleLinkClick(e)}>GitHub</a>.</p>
|
||||||
|
<p>These data are only used for Boostnote improvements.</p>
|
||||||
|
<input onChange={(e) => this.handleConfigChange(e)}
|
||||||
|
checked={this.state.config.amaEnabled}
|
||||||
|
ref='amaEnabled'
|
||||||
|
type='checkbox'
|
||||||
|
/>
|
||||||
|
Enable to send analytics to our servers<br />
|
||||||
|
<button styleName='policy-submit' onClick={(e) => this.handleSaveButtonClick(e)}>Save</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,54 @@
|
|||||||
|
@import('./Tab')
|
||||||
|
|
||||||
.root
|
.root
|
||||||
padding 15px
|
padding 15px
|
||||||
white-space pre
|
white-space pre
|
||||||
line-height 1.4
|
line-height 1.4
|
||||||
color $ui-text-color
|
color alpha($ui-text-color, 90%)
|
||||||
width 100%
|
width 100%
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
.top
|
.top
|
||||||
text-align center
|
text-align left
|
||||||
margin-bottom 25px
|
margin-bottom 20px
|
||||||
|
|
||||||
|
.icon-space
|
||||||
|
margin 20px 0
|
||||||
|
height 100px
|
||||||
|
|
||||||
|
.icon
|
||||||
|
display inline-block
|
||||||
|
vertical-align middle
|
||||||
|
|
||||||
|
.icon-right
|
||||||
|
display inline-block
|
||||||
|
vertical-align middle
|
||||||
|
margin-left 20px
|
||||||
|
|
||||||
.appId
|
.appId
|
||||||
font-size 24px
|
font-size 24px
|
||||||
|
margin-bottom 13px
|
||||||
|
|
||||||
.description
|
.description
|
||||||
overflow hidden
|
|
||||||
white-space normal
|
|
||||||
line-height 1.5
|
|
||||||
margin 5px auto 10px
|
|
||||||
font-size 14px
|
font-size 14px
|
||||||
text-align center
|
|
||||||
|
|
||||||
.madeBy
|
|
||||||
font-size 12px
|
|
||||||
$ui-inactive-text-color
|
|
||||||
|
|
||||||
.copyright
|
|
||||||
font-size 12px
|
|
||||||
$ui-inactive-text-color
|
|
||||||
|
|
||||||
.list
|
.list
|
||||||
list-style square
|
list-style square
|
||||||
padding-left 2em
|
padding-left 2em
|
||||||
li
|
li
|
||||||
white-space normal
|
white-space normal
|
||||||
|
padding-bottom 10px
|
||||||
|
a
|
||||||
|
color #4E8EC6
|
||||||
|
text-decoration none
|
||||||
|
|
||||||
|
.policy
|
||||||
|
font-size 20px
|
||||||
|
margin-bottom 10px
|
||||||
|
|
||||||
|
.policy-submit
|
||||||
|
margin-top 10px
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
color $ui-dark-text-color
|
color alpha($tab--dark-text-color, 80%)
|
||||||
|
|
||||||
.madeBy
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|
||||||
.copyright
|
|
||||||
color $ui-dark-inactive-text-color
|
|
||||||
|
|||||||
@@ -1,51 +1,87 @@
|
|||||||
|
@import('./Tab')
|
||||||
|
|
||||||
|
top-bar--height = 50px
|
||||||
|
|
||||||
.root
|
.root
|
||||||
modal()
|
modal()
|
||||||
max-width 540px
|
max-width 800px
|
||||||
min-height 400px
|
min-height 500px
|
||||||
height 80%
|
height 80%
|
||||||
overflow hidden
|
overflow hidden
|
||||||
position relative
|
position relative
|
||||||
|
|
||||||
.nav
|
.top-bar
|
||||||
absolute top left right
|
absolute top left right
|
||||||
height 50px
|
height top-bar--height
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-backgroundColor
|
||||||
border-bottom solid 1px $ui-borderColor
|
border-bottom solid 1px $ui-borderColor
|
||||||
|
p
|
||||||
|
text-align center
|
||||||
|
font-size 18px
|
||||||
|
line-height top-bar--height
|
||||||
|
|
||||||
|
.nav
|
||||||
|
absolute top left right
|
||||||
|
top top-bar--height
|
||||||
|
left 0
|
||||||
|
width 140px
|
||||||
|
margin-left 30px
|
||||||
|
margin-top 20px
|
||||||
|
background-color $ui-backgroundColor
|
||||||
|
|
||||||
.nav-button
|
.nav-button
|
||||||
width 80px
|
|
||||||
height 50px
|
|
||||||
border none
|
|
||||||
background-color transparent
|
|
||||||
color #939395
|
|
||||||
font-size 14px
|
font-size 14px
|
||||||
&:hover
|
text-align left
|
||||||
color #515151
|
width 100px
|
||||||
|
margin 4px 0
|
||||||
|
padding 5px 0
|
||||||
|
padding-left 10px
|
||||||
|
border none
|
||||||
|
border-radius 2px
|
||||||
|
background-color transparent
|
||||||
|
color $ui-text-color
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
.nav-button--active
|
.nav-button--active
|
||||||
@extend .nav-button
|
@extend .nav-button
|
||||||
color #6AA5E9
|
color $ui-text-color
|
||||||
|
background-color $ui-button--active-backgroundColor
|
||||||
&:hover
|
&:hover
|
||||||
color #6AA5E9
|
color $ui-text-color
|
||||||
|
|
||||||
.nav-button-icon
|
.nav-button-icon
|
||||||
display block
|
display block
|
||||||
|
|
||||||
.content
|
.content
|
||||||
absolute left right bottom
|
absolute left right bottom
|
||||||
top 50px
|
top top-bar--height
|
||||||
|
left 140px
|
||||||
|
margin-top 10px
|
||||||
overflow-y auto
|
overflow-y auto
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
modalDark()
|
modalDark()
|
||||||
|
|
||||||
|
.top-bar
|
||||||
|
background-color transparent
|
||||||
|
border-color #4A4D52
|
||||||
|
p
|
||||||
|
color $tab--dark-text-color
|
||||||
|
|
||||||
.nav
|
.nav
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color transparent
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
|
||||||
.nav-button
|
.nav-button
|
||||||
background-color transparent
|
background-color transparent
|
||||||
color #939395
|
color $tab--dark-text-color
|
||||||
&:hover
|
&:hover
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
|
||||||
|
.nav-button--active
|
||||||
|
@extend .nav-button
|
||||||
|
color white
|
||||||
|
background-color $dark-primary-button-background--active
|
||||||
|
&:hover
|
||||||
|
color white
|
||||||
|
|||||||
@@ -106,7 +106,10 @@ class UnstyledFolderItem extends React.Component {
|
|||||||
const popover = { position: 'absolute', zIndex: 2 }
|
const popover = { position: 'absolute', zIndex: 2 }
|
||||||
const cover = {
|
const cover = {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
top: 0, right: 0, bottom: 0, left: 0
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0
|
||||||
}
|
}
|
||||||
const pickerStyle = Object.assign({}, {
|
const pickerStyle = Object.assign({}, {
|
||||||
position: 'absolute'
|
position: 'absolute'
|
||||||
@@ -137,7 +140,7 @@ class UnstyledFolderItem extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
<i className='fa fa-square'/>
|
<i className='fa fa-square' />
|
||||||
</button>
|
</button>
|
||||||
<input styleName='folderList-item-left-nameInput'
|
<input styleName='folderList-item-left-nameInput'
|
||||||
value={this.state.folder.name}
|
value={this.state.folder.name}
|
||||||
@@ -368,17 +371,17 @@ class StorageItem extends React.Component {
|
|||||||
: <div styleName='header-label'
|
: <div styleName='header-label'
|
||||||
onClick={(e) => this.handleLabelClick(e)}
|
onClick={(e) => this.handleLabelClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-folder-open'/>
|
<i className='fa fa-folder-open' />
|
||||||
{storage.name}
|
{storage.name}
|
||||||
<span styleName='header-label-path'>({storage.path})</span>
|
<span styleName='header-label-path'>({storage.path})</span>
|
||||||
<i styleName='header-label-editButton' className='fa fa-pencil'/>
|
<i styleName='header-label-editButton' className='fa fa-pencil' />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div styleName='header-control'>
|
<div styleName='header-control'>
|
||||||
<button styleName='header-control-button'
|
<button styleName='header-control-button'
|
||||||
onClick={(e) => this.handleNewFolderButtonClick(e)}
|
onClick={(e) => this.handleNewFolderButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-plus'/>
|
<i className='fa fa-plus' />
|
||||||
<span styleName='header-control-button-tooltip'
|
<span styleName='header-control-button-tooltip'
|
||||||
style={{left: -20}}
|
style={{left: -20}}
|
||||||
>Add Folder</span>
|
>Add Folder</span>
|
||||||
@@ -386,7 +389,7 @@ class StorageItem extends React.Component {
|
|||||||
<button styleName='header-control-button'
|
<button styleName='header-control-button'
|
||||||
onClick={(e) => this.handleExternalButtonClick(e)}
|
onClick={(e) => this.handleExternalButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-external-link'/>
|
<i className='fa fa-external-link' />
|
||||||
<span styleName='header-control-button-tooltip'
|
<span styleName='header-control-button-tooltip'
|
||||||
style={{left: -50}}
|
style={{left: -50}}
|
||||||
>Open Storage folder</span>
|
>Open Storage folder</span>
|
||||||
@@ -394,7 +397,7 @@ class StorageItem extends React.Component {
|
|||||||
<button styleName='header-control-button'
|
<button styleName='header-control-button'
|
||||||
onClick={(e) => this.handleUnlinkButtonClick(e)}
|
onClick={(e) => this.handleUnlinkButtonClick(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-unlink'/>
|
<i className='fa fa-unlink' />
|
||||||
<span styleName='header-control-button-tooltip'
|
<span styleName='header-control-button-tooltip'
|
||||||
style={{left: -10}}
|
style={{left: -10}}
|
||||||
>Unlink</span>
|
>Unlink</span>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
border $ui-border
|
border $ui-border
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
padding 0 5px
|
padding 0 5px
|
||||||
|
outline none
|
||||||
|
|
||||||
.header-control
|
.header-control
|
||||||
float right
|
float right
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
background-color darken(white, 3%)
|
background-color darken(white, 3%)
|
||||||
.folderList-item-left
|
.folderList-item-left
|
||||||
height 30px
|
height 30px
|
||||||
border-left solid 4px transparent
|
border-left solid 2px transparent
|
||||||
padding 0 10px
|
padding 0 10px
|
||||||
line-height 30px
|
line-height 30px
|
||||||
float left
|
float left
|
||||||
@@ -105,6 +105,7 @@
|
|||||||
border $ui-border
|
border $ui-border
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
padding 0 5px
|
padding 0 5px
|
||||||
|
outline none
|
||||||
|
|
||||||
.folderList-item-right
|
.folderList-item-right
|
||||||
float right
|
float right
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class StoragesTab extends React.Component {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<div styleName='list'>
|
<div styleName='list'>
|
||||||
|
<div styleName='header'>Storages</div>
|
||||||
{storageList.length > 0
|
{storageList.length > 0
|
||||||
? storageList
|
? storageList
|
||||||
: <div styleName='list-empty'>No storage found.</div>
|
: <div styleName='list-empty'>No storage found.</div>
|
||||||
@@ -71,7 +72,7 @@ class StoragesTab extends React.Component {
|
|||||||
<button styleName='list-control-addStorageButton'
|
<button styleName='list-control-addStorageButton'
|
||||||
onClick={(e) => this.handleAddStorageButton(e)}
|
onClick={(e) => this.handleAddStorageButton(e)}
|
||||||
>
|
>
|
||||||
<i className='fa fa-plus'/> Add Storage
|
<i className='fa fa-plus' /> Add Storage
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@import('./Tab')
|
||||||
|
|
||||||
.root
|
.root
|
||||||
padding 15px
|
padding 15px
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
@@ -26,10 +28,14 @@
|
|||||||
.list-control
|
.list-control
|
||||||
height 30px
|
height 30px
|
||||||
.list-control-addStorageButton
|
.list-control-addStorageButton
|
||||||
height 30px
|
position absolute
|
||||||
|
top 7px
|
||||||
|
right 20px
|
||||||
|
height $tab--button-height
|
||||||
padding 0 15px
|
padding 0 15px
|
||||||
border $ui-border
|
border $ui-border
|
||||||
colorDefaultButton()
|
colorDefaultButton()
|
||||||
|
font-size $tab--button-font-size
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
|
|
||||||
.addStorage
|
.addStorage
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user