Compare commits
418 Commits
html-viz
...
choropleth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
435787281a | ||
|
|
bc9dd814c9 | ||
|
|
c7b13459e8 | ||
|
|
813d97a62c | ||
|
|
ddb0ef15c1 | ||
|
|
9646156965 | ||
|
|
5c7cb1af3d | ||
|
|
2bd8771188 | ||
|
|
86f8f32ab4 | ||
|
|
fdccaabbe9 | ||
|
|
2f5920d5e4 | ||
|
|
e97510b2ee | ||
|
|
80cfa3c557 | ||
|
|
9b71b569e2 | ||
|
|
f2159472da | ||
|
|
c961c33e49 | ||
|
|
5afc94c562 | ||
|
|
cee1a07320 | ||
|
|
42b1eadeb2 | ||
|
|
7edac9ca89 | ||
|
|
69893f0304 | ||
|
|
b089f5f0ef | ||
|
|
7a34a76817 | ||
|
|
b331c4c922 | ||
|
|
6187448e6a | ||
|
|
2de3895986 | ||
|
|
3f280b1f6e | ||
|
|
3b29f0c0a7 | ||
|
|
4911764663 | ||
|
|
6260601213 | ||
|
|
8f7d1d8281 | ||
|
|
713fd2d0fb | ||
|
|
19c6d331b6 | ||
|
|
a36b10173c | ||
|
|
7d11fae9ea | ||
|
|
35e41385dc | ||
|
|
cdefa847c0 | ||
|
|
a90b8c7443 | ||
|
|
1ba3a23457 | ||
|
|
8a5e0ea3f4 | ||
|
|
cbc56264ea | ||
|
|
c92bb63f8b | ||
|
|
ff0dbd5f01 | ||
|
|
80bfd405fd | ||
|
|
945f53fea3 | ||
|
|
56b51be64a | ||
|
|
a682265e13 | ||
|
|
a891160b4d | ||
|
|
086798bbb7 | ||
|
|
ebcec85c0c | ||
|
|
2b5bfad054 | ||
|
|
479b277b91 | ||
|
|
94ac11c787 | ||
|
|
a7ef3ad72a | ||
|
|
afe8c95f4d | ||
|
|
fe06f7f63e | ||
|
|
5e01211852 | ||
|
|
375ffd3250 | ||
|
|
674f057c59 | ||
|
|
aa17681af2 | ||
|
|
13c3531956 | ||
|
|
350716c525 | ||
|
|
fe11b8cc35 | ||
|
|
465dbc03b7 | ||
|
|
76f0dcb085 | ||
|
|
99c276fc9a | ||
|
|
fc9e8fe2aa | ||
|
|
260bfca767 | ||
|
|
f85490cf50 | ||
|
|
29582e3212 | ||
|
|
329e85987c | ||
|
|
ff34dedf46 | ||
|
|
d0fb377ed6 | ||
|
|
30bc1e2ff6 | ||
|
|
fd46194580 | ||
|
|
f5900a1929 | ||
|
|
c2b39db03e | ||
|
|
f420e02cee | ||
|
|
0aa176e2e5 | ||
|
|
97d523e348 | ||
|
|
88d21e9461 | ||
|
|
40c1ef0f59 | ||
|
|
10ba2ddbaa | ||
|
|
e7eedd0556 | ||
|
|
c3299ff0ad | ||
|
|
6b2f23f357 | ||
|
|
0819f80e72 | ||
|
|
7223f60ddf | ||
|
|
38b6b47594 | ||
|
|
49dcb7f689 | ||
|
|
425e79fdd2 | ||
|
|
bc52b78889 | ||
|
|
944adb95ba | ||
|
|
8cb49158bf | ||
|
|
ca098172e9 | ||
|
|
a3beac0b78 | ||
|
|
56d3be2248 | ||
|
|
81b14a58ef | ||
|
|
2dff8b9a00 | ||
|
|
37a964c8d9 | ||
|
|
1b9b3032ca | ||
|
|
0385b6fb64 | ||
|
|
7c05a730dc | ||
|
|
263305214e | ||
|
|
3494e21cf4 | ||
|
|
15e8b88996 | ||
|
|
ba36b4e671 | ||
|
|
94bd03dc42 | ||
|
|
041d05d18b | ||
|
|
c14e7ab4ca | ||
|
|
4d6c30ef13 | ||
|
|
36ab8eae89 | ||
|
|
e82373ac1d | ||
|
|
d3feba69b2 | ||
|
|
80f3ec1c99 | ||
|
|
c612bba19c | ||
|
|
f5a40827aa | ||
|
|
5de291a98d | ||
|
|
c70a48db9c | ||
|
|
be56035bd6 | ||
|
|
7c97d8eafa | ||
|
|
0563ecf648 | ||
|
|
e72d7a8cca | ||
|
|
a7a933946b | ||
|
|
7cfd362a7a | ||
|
|
4d1b359713 | ||
|
|
818649bbec | ||
|
|
c6a2725f0a | ||
|
|
5cd6913e40 | ||
|
|
0aebb37317 | ||
|
|
aa06b32e17 | ||
|
|
b44fa51829 | ||
|
|
1a95904ffd | ||
|
|
ef56e4e920 | ||
|
|
d5a3f0de57 | ||
|
|
cf274d96c8 | ||
|
|
c00410768c | ||
|
|
dda5a9d58f | ||
|
|
a0a32be3dd | ||
|
|
e0e94d79ac | ||
|
|
f19d24287e | ||
|
|
80878abf7b | ||
|
|
6716bb390c | ||
|
|
a33d11de3a | ||
|
|
6f791a092b | ||
|
|
cce6546a62 | ||
|
|
5fd78fdb23 | ||
|
|
74dbb8acf3 | ||
|
|
36638be1dd | ||
|
|
82f488d231 | ||
|
|
7b3943052e | ||
|
|
96a95b7090 | ||
|
|
accf0f7ac5 | ||
|
|
88ae639ee4 | ||
|
|
ba413c210e | ||
|
|
7157244eec | ||
|
|
9f7844640a | ||
|
|
246eca1121 | ||
|
|
7ffb97232e | ||
|
|
8b9fa53efe | ||
|
|
43b35b6fb4 | ||
|
|
f0f85ece42 | ||
|
|
612833404b | ||
|
|
5d58503623 | ||
|
|
3dfad87266 | ||
|
|
0659ef1079 | ||
|
|
a2e21dd1c3 | ||
|
|
f165cad9ff | ||
|
|
e0a2705c1a | ||
|
|
0aca649cb5 | ||
|
|
79b37e8843 | ||
|
|
72bb5d29a0 | ||
|
|
5a5fdecdde | ||
|
|
f6e1470a7c | ||
|
|
27cd76797e | ||
|
|
29b113005c | ||
|
|
53d971bf87 | ||
|
|
a102e93e50 | ||
|
|
74beed80d2 | ||
|
|
39f038f992 | ||
|
|
da2ed56281 | ||
|
|
9d8812a598 | ||
|
|
204447a9f5 | ||
|
|
3b7efb8c1f | ||
|
|
69dc761c60 | ||
|
|
2f42b8154c | ||
|
|
3f9d49dbd1 | ||
|
|
0a5dca5d72 | ||
|
|
8ea285dda9 | ||
|
|
569c325aa0 | ||
|
|
d8a0af1a95 | ||
|
|
648847df0b | ||
|
|
3f31bf3fc0 | ||
|
|
f6ad3d9d24 | ||
|
|
e8ccdc23c7 | ||
|
|
a8af968d70 | ||
|
|
cb14459881 | ||
|
|
780fbceba5 | ||
|
|
2c77c219c6 | ||
|
|
874e0d1ce3 | ||
|
|
401d164622 | ||
|
|
ff041b77cf | ||
|
|
b2d1636f8e | ||
|
|
a3e8477410 | ||
|
|
ed22b63f22 | ||
|
|
d636b29ba9 | ||
|
|
6173a2a619 | ||
|
|
fd435d2182 | ||
|
|
cb654b3f21 | ||
|
|
e8d40bbdac | ||
|
|
e5d52055d9 | ||
|
|
c5e414e6ba | ||
|
|
b9a40d1808 | ||
|
|
033dd0d15e | ||
|
|
95795d93c7 | ||
|
|
75e48b0bd6 | ||
|
|
75883a1a02 | ||
|
|
75a5546741 | ||
|
|
54071e4b87 | ||
|
|
6458a1eb62 | ||
|
|
ecf160c9bc | ||
|
|
2c98f0425d | ||
|
|
8f01988c8c | ||
|
|
b8741f6cff | ||
|
|
424751d9e9 | ||
|
|
e048a69392 | ||
|
|
2c1e846837 | ||
|
|
4b9e26de5a | ||
|
|
17f50192e7 | ||
|
|
dcdec0abb5 | ||
|
|
302c6dd02e | ||
|
|
4c56900248 | ||
|
|
1f1f853297 | ||
|
|
43f63b1b57 | ||
|
|
5ae80835b1 | ||
|
|
df3da82afd | ||
|
|
98e33b7780 | ||
|
|
8a3f6f90eb | ||
|
|
cab011def9 | ||
|
|
31c888ea8e | ||
|
|
443054428f | ||
|
|
ef9a4d5eed | ||
|
|
a2b68a3569 | ||
|
|
e7b707eb25 | ||
|
|
1786273344 | ||
|
|
d38ca803c5 | ||
|
|
a1f11cb8d9 | ||
|
|
b426e4fdc4 | ||
|
|
e5e926bac5 | ||
|
|
24d68008fa | ||
|
|
0e90b89acc | ||
|
|
2c2f241671 | ||
|
|
d49514abe9 | ||
|
|
934a145ced | ||
|
|
f7c70c2b91 | ||
|
|
69ba165565 | ||
|
|
7b5696dc75 | ||
|
|
4698408a08 | ||
|
|
be142d60df | ||
|
|
aceea6516f | ||
|
|
685b53672e | ||
|
|
7dd62ef948 | ||
|
|
7c2acc34c9 | ||
|
|
c5a90876f3 | ||
|
|
8abaf89394 | ||
|
|
aa2bd0042e | ||
|
|
a7b14bfb9a | ||
|
|
4e5f55a4b7 | ||
|
|
76fbe858ba | ||
|
|
cf7aef1e16 | ||
|
|
77625b2a13 | ||
|
|
c4dcf01b3c | ||
|
|
a167c590b6 | ||
|
|
8e23f93433 | ||
|
|
e41d40bbe0 | ||
|
|
6fc4d5b551 | ||
|
|
f0576a3623 | ||
|
|
9eabf89771 | ||
|
|
11cc274c1c | ||
|
|
8ad08a566a | ||
|
|
ef31d0d768 | ||
|
|
4640c33387 | ||
|
|
9b290913a6 | ||
|
|
db89c4f7bc | ||
|
|
eae1fb7d73 | ||
|
|
4f742aeaac | ||
|
|
5ddad862be | ||
|
|
6f811f163a | ||
|
|
7fb33e3ebb | ||
|
|
f165168860 | ||
|
|
86b0608fde | ||
|
|
cd4daf8823 | ||
|
|
78cae474e0 | ||
|
|
c518c7a4bc | ||
|
|
8c2f51d09d | ||
|
|
6f6c68bd79 | ||
|
|
64f274f58e | ||
|
|
dd89bd885f | ||
|
|
b2295197cf | ||
|
|
ea0e411053 | ||
|
|
9bdb3412a5 | ||
|
|
ad4a760545 | ||
|
|
c1f4147807 | ||
|
|
c054ae8be0 | ||
|
|
d1edd3d068 | ||
|
|
4989bfae60 | ||
|
|
f20a020003 | ||
|
|
01da8c158a | ||
|
|
c83e40b047 | ||
|
|
c3cc65a21d | ||
|
|
5929139ab8 | ||
|
|
66794acd1f | ||
|
|
bce0832e48 | ||
|
|
9f006997a0 | ||
|
|
51d8131db5 | ||
|
|
c793b5dd11 | ||
|
|
4e9da3f116 | ||
|
|
15a8eecdde | ||
|
|
a8ff2500be | ||
|
|
7bf84e856c | ||
|
|
5149bf67ca | ||
|
|
93449db325 | ||
|
|
df57d22e81 | ||
|
|
de0a44ee85 | ||
|
|
261062d491 | ||
|
|
1878e8bf90 | ||
|
|
47fc8a942a | ||
|
|
addecbdd8f | ||
|
|
baec5d56f5 | ||
|
|
1f4325ba8d | ||
|
|
5e5b56ed6a | ||
|
|
45a3b72730 | ||
|
|
cc48de0d8f | ||
|
|
300f3f6780 | ||
|
|
2e4a69cba4 | ||
|
|
6748e9a15d | ||
|
|
7ceb68a468 | ||
|
|
3c1d1e3d4e | ||
|
|
92391e3cbc | ||
|
|
17438002d7 | ||
|
|
a00c5a8857 | ||
|
|
a696fa55f3 | ||
|
|
27259b5abe | ||
|
|
9ee393ec75 | ||
|
|
cfafa97218 | ||
|
|
be580b24a5 | ||
|
|
a6960c5f19 | ||
|
|
6dd321beeb | ||
|
|
27c64b42ac | ||
|
|
99bf6d122c | ||
|
|
d617f57f7d | ||
|
|
21a27ee0b1 | ||
|
|
ac293c7f92 | ||
|
|
8e38dcd244 | ||
|
|
2bab144107 | ||
|
|
4e0a251034 | ||
|
|
7a9f4b07e0 | ||
|
|
1630cbb904 | ||
|
|
f8d05dda9f | ||
|
|
2af8b39d21 | ||
|
|
3faed0fdfe | ||
|
|
e45f49b86e | ||
|
|
e33ad3b164 | ||
|
|
6605f62f3a | ||
|
|
ed2ac407ab | ||
|
|
dda75cce24 | ||
|
|
5b780ac460 | ||
|
|
c0e8ef3000 | ||
|
|
a82fd0cabc | ||
|
|
0e3e2eaf38 | ||
|
|
05f6ef0fb6 | ||
|
|
e433efebc4 | ||
|
|
a9588eac79 | ||
|
|
090b570a71 | ||
|
|
60b12e3121 | ||
|
|
3f8c7333be | ||
|
|
be8dec5f04 | ||
|
|
10b3b50f3d | ||
|
|
6f290ddfa1 | ||
|
|
10b62ebe02 | ||
|
|
04453409da | ||
|
|
b27df216f4 | ||
|
|
a0c76d777b | ||
|
|
2e96e2fb98 | ||
|
|
c2e31f040d | ||
|
|
816f4d912f | ||
|
|
9292ae8d3f | ||
|
|
9480d89e4c | ||
|
|
5dff5b929c | ||
|
|
28e9740e2f | ||
|
|
7679df63ba | ||
|
|
07c9530984 | ||
|
|
aecd0bf37a | ||
|
|
4143bd3f20 | ||
|
|
020dc35faf | ||
|
|
d7b03bac02 | ||
|
|
29875e66d4 | ||
|
|
d97ce15837 | ||
|
|
b263bb7077 | ||
|
|
606cf12e74 | ||
|
|
4508975749 | ||
|
|
c76955be28 | ||
|
|
4f402379e8 | ||
|
|
733b60102d | ||
|
|
b9b30a39d2 | ||
|
|
c74d469181 | ||
|
|
95f11e6686 | ||
|
|
ad6f7109de | ||
|
|
b09ae46a9f | ||
|
|
0cda0369f0 | ||
|
|
50f11069ce | ||
|
|
6bf764be07 | ||
|
|
3159410694 | ||
|
|
76bd2e3c50 | ||
|
|
50a6f723b1 | ||
|
|
0ee20797c8 | ||
|
|
d7515562a4 | ||
|
|
feafbbe318 |
@@ -1,18 +1,24 @@
|
|||||||
version: 2.0
|
version: 2.0
|
||||||
|
|
||||||
flake8-steps: &steps
|
build-docker-image-job: &build-docker-image-job
|
||||||
|
docker:
|
||||||
|
- image: circleci/node:8
|
||||||
|
steps:
|
||||||
|
- setup_remote_docker
|
||||||
|
- checkout
|
||||||
|
- run: sudo apt install python3-pip
|
||||||
|
- run: sudo pip3 install -r requirements_bundles.txt
|
||||||
|
- run: .circleci/update_version
|
||||||
|
- run: npm run bundle
|
||||||
|
- run: .circleci/docker_build
|
||||||
|
jobs:
|
||||||
|
backend-lint:
|
||||||
|
docker:
|
||||||
|
- image: circleci/python:3.7.0
|
||||||
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: sudo pip install flake8
|
- run: sudo pip install flake8
|
||||||
- run: ./bin/flake8_tests.sh
|
- run: ./bin/flake8_tests.sh
|
||||||
jobs:
|
|
||||||
python-flake8-tests:
|
|
||||||
docker:
|
|
||||||
- image: circleci/python:3.7.0
|
|
||||||
steps: *steps
|
|
||||||
legacy-python-flake8-tests:
|
|
||||||
docker:
|
|
||||||
- image: circleci/python:2.7.15
|
|
||||||
steps: *steps
|
|
||||||
backend-unit-tests:
|
backend-unit-tests:
|
||||||
environment:
|
environment:
|
||||||
COMPOSE_FILE: .circleci/docker-compose.circle.yml
|
COMPOSE_FILE: .circleci/docker-compose.circle.yml
|
||||||
@@ -32,6 +38,9 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Create Test Database
|
name: Create Test Database
|
||||||
command: docker-compose run --rm postgres psql -h postgres -U postgres -c "create database tests;"
|
command: docker-compose run --rm postgres psql -h postgres -U postgres -c "create database tests;"
|
||||||
|
- run:
|
||||||
|
name: List Enabled Query Runners
|
||||||
|
command: docker-compose run --rm redash manage ds list_types
|
||||||
- run:
|
- run:
|
||||||
name: Run Tests
|
name: Run Tests
|
||||||
command: docker-compose run --name tests redash tests --junitxml=junit.xml --cov-report xml --cov=redash --cov-config .coveragerc tests/
|
command: docker-compose run --name tests redash tests --junitxml=junit.xml --cov-report xml --cov=redash --cov-config .coveragerc tests/
|
||||||
@@ -41,6 +50,7 @@ jobs:
|
|||||||
mkdir -p /tmp/test-results/unit-tests
|
mkdir -p /tmp/test-results/unit-tests
|
||||||
docker cp tests:/app/coverage.xml ./coverage.xml
|
docker cp tests:/app/coverage.xml ./coverage.xml
|
||||||
docker cp tests:/app/junit.xml /tmp/test-results/unit-tests/results.xml
|
docker cp tests:/app/junit.xml /tmp/test-results/unit-tests/results.xml
|
||||||
|
when: always
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: /tmp/test-results
|
path: /tmp/test-results
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
@@ -60,7 +70,8 @@ jobs:
|
|||||||
- image: circleci/node:8
|
- image: circleci/node:8
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: sudo apt install python-pip
|
- run: sudo apt install python3-pip
|
||||||
|
- run: sudo pip3 install -r requirements_bundles.txt
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm run bundle
|
- run: npm run bundle
|
||||||
- run: npm test
|
- run: npm test
|
||||||
@@ -89,43 +100,25 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Execute Cypress tests
|
name: Execute Cypress tests
|
||||||
command: npm run cypress run-ci
|
command: npm run cypress run-ci
|
||||||
build-tarball:
|
build-docker-image: *build-docker-image-job
|
||||||
docker:
|
build-preview-docker-image: *build-docker-image-job
|
||||||
- image: circleci/node:8
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: sudo apt install python-pip
|
|
||||||
- run: npm install
|
|
||||||
- run: .circleci/update_version
|
|
||||||
- run: npm run bundle
|
|
||||||
- run: npm run build
|
|
||||||
- run: rm -rf ./node_modules/
|
|
||||||
- run: .circleci/pack
|
|
||||||
- store_artifacts:
|
|
||||||
path: /tmp/artifacts/
|
|
||||||
build-docker-image:
|
|
||||||
docker:
|
|
||||||
- image: circleci/buildpack-deps:xenial
|
|
||||||
steps:
|
|
||||||
- setup_remote_docker
|
|
||||||
- checkout
|
|
||||||
- run: .circleci/update_version
|
|
||||||
- run: .circleci/docker_build
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build:
|
build:
|
||||||
jobs:
|
jobs:
|
||||||
- python-flake8-tests
|
- backend-lint
|
||||||
- legacy-python-flake8-tests
|
- backend-unit-tests:
|
||||||
- backend-unit-tests
|
requires:
|
||||||
|
- backend-lint
|
||||||
- frontend-lint
|
- frontend-lint
|
||||||
- frontend-unit-tests:
|
- frontend-unit-tests:
|
||||||
requires:
|
requires:
|
||||||
|
- backend-lint
|
||||||
- frontend-lint
|
- frontend-lint
|
||||||
- frontend-e2e-tests:
|
- frontend-e2e-tests:
|
||||||
requires:
|
requires:
|
||||||
- frontend-lint
|
- frontend-lint
|
||||||
- build-tarball:
|
- build-preview-docker-image:
|
||||||
requires:
|
requires:
|
||||||
- backend-unit-tests
|
- backend-unit-tests
|
||||||
- frontend-unit-tests
|
- frontend-unit-tests
|
||||||
@@ -134,15 +127,16 @@ workflows:
|
|||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
- hold:
|
||||||
|
type: approval
|
||||||
|
requires:
|
||||||
|
- backend-unit-tests
|
||||||
|
- frontend-unit-tests
|
||||||
|
- frontend-e2e-tests
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
- /release\/.*/
|
- /release\/.*/
|
||||||
- build-docker-image:
|
- build-docker-image:
|
||||||
requires:
|
requires:
|
||||||
- backend-unit-tests
|
- hold
|
||||||
- frontend-unit-tests
|
|
||||||
- frontend-e2e-tests
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- preview-image
|
|
||||||
- /release\/.*/
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: '3'
|
|||||||
services:
|
services:
|
||||||
server:
|
server:
|
||||||
build: ../
|
build: ../
|
||||||
command: dev_server
|
command: server
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
@@ -14,9 +14,16 @@ services:
|
|||||||
REDASH_REDIS_URL: "redis://redis:6379/0"
|
REDASH_REDIS_URL: "redis://redis:6379/0"
|
||||||
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
|
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
|
||||||
REDASH_RATELIMIT_ENABLED: "false"
|
REDASH_RATELIMIT_ENABLED: "false"
|
||||||
worker:
|
scheduler:
|
||||||
build: ../
|
build: ../
|
||||||
command: scheduler
|
command: scheduler
|
||||||
|
depends_on:
|
||||||
|
- server
|
||||||
|
environment:
|
||||||
|
REDASH_REDIS_URL: "redis://redis:6379/0"
|
||||||
|
worker:
|
||||||
|
build: ../
|
||||||
|
command: worker
|
||||||
depends_on:
|
depends_on:
|
||||||
- server
|
- server
|
||||||
environment:
|
environment:
|
||||||
@@ -24,8 +31,6 @@ services:
|
|||||||
REDASH_LOG_LEVEL: "INFO"
|
REDASH_LOG_LEVEL: "INFO"
|
||||||
REDASH_REDIS_URL: "redis://redis:6379/0"
|
REDASH_REDIS_URL: "redis://redis:6379/0"
|
||||||
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
|
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
|
||||||
QUEUES: "queries,scheduled_queries,celery,schemas"
|
|
||||||
WORKERS_COUNT: 2
|
|
||||||
cypress:
|
cypress:
|
||||||
build:
|
build:
|
||||||
context: ../
|
context: ../
|
||||||
@@ -33,12 +38,17 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- server
|
- server
|
||||||
- worker
|
- worker
|
||||||
|
- scheduler
|
||||||
environment:
|
environment:
|
||||||
CYPRESS_baseUrl: "http://server:5000"
|
CYPRESS_baseUrl: "http://server:5000"
|
||||||
PERCY_TOKEN: ${PERCY_TOKEN}
|
PERCY_TOKEN: ${PERCY_TOKEN}
|
||||||
PERCY_BRANCH: ${CIRCLE_BRANCH}
|
PERCY_BRANCH: ${CIRCLE_BRANCH}
|
||||||
PERCY_COMMIT: ${CIRCLE_SHA1}
|
PERCY_COMMIT: ${CIRCLE_SHA1}
|
||||||
PERCY_PULL_REQUEST: ${CIRCLE_PR_NUMBER}
|
PERCY_PULL_REQUEST: ${CIRCLE_PR_NUMBER}
|
||||||
|
COMMIT_INFO_BRANCH: ${CIRCLE_BRANCH}
|
||||||
|
COMMIT_INFO_AUTHOR: ${CIRCLE_USERNAME}
|
||||||
|
COMMIT_INFO_SHA: ${CIRCLE_SHA1}
|
||||||
|
COMMIT_INFO_REMOTE: ${CIRCLE_REPOSITORY_URL}
|
||||||
CYPRESS_PROJECT_ID: ${CYPRESS_PROJECT_ID}
|
CYPRESS_PROJECT_ID: ${CYPRESS_PROJECT_ID}
|
||||||
CYPRESS_RECORD_KEY: ${CYPRESS_RECORD_KEY}
|
CYPRESS_RECORD_KEY: ${CYPRESS_RECORD_KEY}
|
||||||
redis:
|
redis:
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
version: "2"
|
|
||||||
checks:
|
|
||||||
complex-logic:
|
|
||||||
enabled: false
|
|
||||||
file-lines:
|
|
||||||
enabled: false
|
|
||||||
method-complexity:
|
|
||||||
enabled: false
|
|
||||||
method-count:
|
|
||||||
enabled: false
|
|
||||||
method-lines:
|
|
||||||
config:
|
|
||||||
threshold: 100
|
|
||||||
nested-control-flow:
|
|
||||||
enabled: false
|
|
||||||
identical-code:
|
|
||||||
enabled: false
|
|
||||||
similar-code:
|
|
||||||
enabled: false
|
|
||||||
plugins:
|
|
||||||
pep8:
|
|
||||||
enabled: true
|
|
||||||
eslint:
|
|
||||||
enabled: false
|
|
||||||
exclude_patterns:
|
|
||||||
- "tests/**/*.py"
|
|
||||||
- "migrations/**/*.py"
|
|
||||||
- "setup/**/*"
|
|
||||||
- "bin/**/*"
|
|
||||||
- "**/node_modules/"
|
|
||||||
- "client/dist/"
|
|
||||||
- "**/*.pyc"
|
|
||||||
61
.restyled.yaml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
enabled: true
|
||||||
|
|
||||||
|
auto: false
|
||||||
|
|
||||||
|
# Open Restyle PRs?
|
||||||
|
pull_requests: true
|
||||||
|
|
||||||
|
# Leave comments on the original PR linking to the Restyle PR?
|
||||||
|
comments: true
|
||||||
|
|
||||||
|
# Set commit statuses on the original PR?
|
||||||
|
statuses:
|
||||||
|
# Red status in the case of differences found
|
||||||
|
differences: true
|
||||||
|
# Green status in the case of no differences found
|
||||||
|
no_differences: true
|
||||||
|
# Red status if we encounter errors restyling
|
||||||
|
error: true
|
||||||
|
|
||||||
|
# Request review on the Restyle PR?
|
||||||
|
#
|
||||||
|
# Possible values:
|
||||||
|
#
|
||||||
|
# author: From the author of the original PR
|
||||||
|
# owner: From the owner of the repository
|
||||||
|
# none: Don't
|
||||||
|
#
|
||||||
|
# One value will apply to both origin and forked PRs, but you can also specify
|
||||||
|
# separate values.
|
||||||
|
#
|
||||||
|
# request_review:
|
||||||
|
# origin: author
|
||||||
|
# forked: owner
|
||||||
|
#
|
||||||
|
request_review: author
|
||||||
|
|
||||||
|
# Add labels to any created Restyle PRs
|
||||||
|
#
|
||||||
|
# These can be used to tell other automation to avoid our PRs.
|
||||||
|
#
|
||||||
|
labels: ["Skip CI"]
|
||||||
|
|
||||||
|
# Labels to ignore
|
||||||
|
#
|
||||||
|
# PRs with any of these labels will be ignored by Restyled.
|
||||||
|
#
|
||||||
|
# ignore_labels:
|
||||||
|
# - restyled-ignore
|
||||||
|
|
||||||
|
# Restylers to run, and how
|
||||||
|
restylers:
|
||||||
|
- name: black
|
||||||
|
include:
|
||||||
|
- redash
|
||||||
|
- tests
|
||||||
|
- migrations/versions
|
||||||
|
- name: prettier
|
||||||
|
include:
|
||||||
|
- client/app/**/*.js
|
||||||
|
- client/app/**/*.jsx
|
||||||
|
- client/cypress/**/*.js
|
||||||
117
CHANGELOG.md
@@ -1,5 +1,122 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## v8.0.0 - 2019-10-27
|
||||||
|
|
||||||
|
There were no changes in this release since `v8.0.0-beta.2`. This is just to mark a stable release.
|
||||||
|
|
||||||
|
## v8.0.0-beta.2 - 2019-09-16
|
||||||
|
|
||||||
|
This is an update to the previous beta release, which includes:
|
||||||
|
|
||||||
|
* Add options for users to share anonymous usage information with us (see [docs](https://redash.io/help/open-source/admin-guide/usage-data) for details).
|
||||||
|
* Visualizations:
|
||||||
|
- Allow the user to decide how to handle null values in charts.
|
||||||
|
* Upgrade Sentry-SDK to latest version.
|
||||||
|
* Make horizontal table scroll visible in dashboard widgets without scrolling.
|
||||||
|
* Data Sources:
|
||||||
|
* Add support for Azure Data Explorer (Kusto).
|
||||||
|
* MySQL: fix connections without SSL configuration failing.
|
||||||
|
* Amazon Redshift: option to set query group for adhoc/scheduled queries.
|
||||||
|
* Hive: make error message more friendly.
|
||||||
|
* Qubole: add support to run Quantum queries.
|
||||||
|
* Display data source icon in query editor.
|
||||||
|
* Fix: allow users with view only acces to use the queries in Query Results
|
||||||
|
* Dashboard: when updating parameters refersh only widgets that use those parameters.
|
||||||
|
|
||||||
|
This release had contributions from 12 people: @arikfr, @cclauss, @gabrieldutra, @justinclift, @kravets-levko, @ranbena, @rauchy, @sandeepV2, @shinsuke-nara, @spacentropy, @sphenlee, @swfz.
|
||||||
|
|
||||||
|
|
||||||
|
## v8.0.0-beta - 2019-08-18
|
||||||
|
|
||||||
|
After months of being heads down with hard work, it's finally time to wrap up the V8 release 🤩 This release includes many long awaited improvements to parameters, UX improvements, further React migration and other changes, fixes and improvements.
|
||||||
|
|
||||||
|
While this version is already running on the hosted platform to make sure it's stable, we're excited to put this in the hands of our Open Source users.
|
||||||
|
|
||||||
|
Starting from this release we will no longer build a tarball distribution of the codebase and recommend everyone to switch over to using our Docker images. We're planning on dropping Python 2 support towards its EOL this year and switching over to the Docker image will make this transition much simpler.
|
||||||
|
|
||||||
|
This release was made possible by contributions from over 40 people: @aidarbek, @AntonZarutsky, @ariarijp, @arikfr, @combineads, @deecay, @fmy, @gabrieldutra, @guwenqing, @guyco33, @ialeinikov, @Jakdaw, @jezdez, @justinclift, @k-tomoyasu, @katty0324, @koooge, @kravets-levko, @ktmud, @KumanoTanaka, @kyoshidajp, @nason, @oldPadavan, @openjck, @osule, @otsaloma, @ranbena, @rauchy, @rueian, @sekiyama58, @shinsuke-nara, @taminif, @The-Alchemist, @vv-p, @washort, @wudi-ayuan, @ygrishaev, @yoavbls, @yoshiken, @yusukegoto and the support of over 500 organizations who subscribed to our hosted version and by that sponsor the team's work.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- Parameter UI improvements:
|
||||||
|
- Support for multi-select in dropdown (and query dropdown) parameters.
|
||||||
|
- Support for dynamic values in date and date-range parameters.
|
||||||
|
- Search dropdown parameter values.
|
||||||
|
- New UX for applying parameter changes in queries and dashboards.
|
||||||
|
- Allow using Safe Parameters in visualization embeds and public dashboards. Safe Parameters are any parameter type except for the a text parameter (dropdowns are safe).
|
||||||
|
|
||||||
|
### Data Sources
|
||||||
|
|
||||||
|
- New Data Sources: Couchbase, Phoenix and Dgraph.
|
||||||
|
- New JSON data source (and deprecated old URL data source).
|
||||||
|
- Snowflake: update connector to latest version.
|
||||||
|
- PostgreSQL: show only accessible tables in schema.
|
||||||
|
- BigQuery:
|
||||||
|
- Correctly handle NaN values.
|
||||||
|
- Treat repeated fields as rrays.
|
||||||
|
- [BigQuery] Fix: in some queries there is no mode field
|
||||||
|
- DynamoDB:
|
||||||
|
- Support for Unicode in queries.
|
||||||
|
- Safe loading of schema.
|
||||||
|
- Rockset: better handling of query errors.
|
||||||
|
- Google Sheets:
|
||||||
|
- Support for Team Drive.
|
||||||
|
- Friendlier error message in case of an API error and more reliable test connection.
|
||||||
|
- MySQL:
|
||||||
|
- Support for calling Stored Procedures and better handling of query cancellation.
|
||||||
|
- Switch to using `mysqlclient` (a maintained fork of `Python-MySQL`).
|
||||||
|
- MongoDB: Support serializing Decimal128 values.
|
||||||
|
- Presto: support for passwords in connection settings.
|
||||||
|
- Amazon Athena: allow to specify custom work group.
|
||||||
|
- Query Results: querying a column with a dictionary or array fails
|
||||||
|
- Clickhouse: make sure we don't show password in error messages.
|
||||||
|
- Enable Cassandra support by default.
|
||||||
|
|
||||||
|
### Visualizations
|
||||||
|
|
||||||
|
- Charts:
|
||||||
|
- Fix: legend overlapping chart on small screens.
|
||||||
|
- Fix: Pie chart not rendering when series doesn't exist in options.
|
||||||
|
- Pie Chart: add option to set direction of slices.
|
||||||
|
- WordCloud: rewritten to support new options (provide frequency in query, limits), scale when resizing, handle long words and more.
|
||||||
|
- Pivot Table: support hiding totals.
|
||||||
|
- Counters: apply formatting to target value.
|
||||||
|
- Maps:
|
||||||
|
- Ability to customize marker icon and color.
|
||||||
|
- Customization options for Choropleth maps.
|
||||||
|
- New Visualization: Details View.
|
||||||
|
|
||||||
|
### **UX**
|
||||||
|
|
||||||
|
- Replace blank screen with a loading indicator when the application is doing its first load.
|
||||||
|
- Multiple improvements to dashboards editing: auto-save, grid markings and better refresh indicator.
|
||||||
|
- Admin can now edit user's groups from the user page.
|
||||||
|
- Add keyboard shortcut (Ctrl/Cmd+Shift+F) to trigger query formatting.
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
- Query Result API response minimized to only required fields when called with a non user API key.
|
||||||
|
- Prefer API key over cookies in authentication.
|
||||||
|
- User can now regenerate Query API Key.
|
||||||
|
|
||||||
|
### Other Changes
|
||||||
|
|
||||||
|
- Sends CSP headers to prevent various kinds of security attacks via the browser. Might break unusual usages and embeds of Redash.
|
||||||
|
- New Failed Scheduled Queries email report (can be enabled from organization settings screen).
|
||||||
|
- Deprecated HipChat Alert Destination.
|
||||||
|
- Add options to hide different parts of a Visualization embed UI (parameters, title, link to query).
|
||||||
|
- Support multi-byte search for query names and descriptions (needs to be enabled in Organization settings screen).
|
||||||
|
- CSV query results download: correctly serialize booleans and date values.
|
||||||
|
- Dashboard filters now collect values from all widgets with the same filter.
|
||||||
|
- Support for custom message and description in alert notifications (currently disabled behind a feature flag until we improve the alert UX).
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: adding widget to dashboard from a query page is broken.
|
||||||
|
- Fix: default time format option was wrong.
|
||||||
|
- Fix: when too many errors of a scheduled queries occur it causes an OverflowError.
|
||||||
|
- Fix: when forking a query maintain the same visualizations order.
|
||||||
|
|
||||||
## v7.0.0 - 2019-03-17
|
## v7.0.0 - 2019-03-17
|
||||||
|
|
||||||
We're trying a new format for the CHANGELOG in this release. Focusing on the bigger changes, but for whoever interested, you can see all the changes [here](https://github.com/getredash/redash/compare/v6.0.0...master).
|
We're trying a new format for the CHANGELOG in this release. Focusing on the bigger changes, but for whoever interested, you can see all the changes [here](https://github.com/getredash/redash/compare/v6.0.0...master).
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ When creating a new bug report, please make sure to:
|
|||||||
|
|
||||||
If you would like to suggest an enhancement or ask for a new feature:
|
If you would like to suggest an enhancement or ask for a new feature:
|
||||||
|
|
||||||
- Please check [the roadmap](https://trello.com/b/b2LUHU7A/redash-roadmap) for existing Trello card for what you want to suggest/ask. If there is, feel free to upvote it to signal interest or add your comments.
|
- Please check [the forum](https://discuss.redash.io/c/feature-requests/5) for existing threads about what you want to suggest/ask. If there is, feel free to upvote it to signal interest or add your comments.
|
||||||
- If there is no existing card, open a thread in [the forum](https://discuss.redash.io/c/feature-requests) to start a discussion about what you want to suggest. Try to provide as much details and context as possible and include information about *the problem you want to solve* rather only *your proposed solution*.
|
- If there is no open thread, you're welcome to start one to have a discussion about what you want to suggest. Try to provide as much details and context as possible and include information about *the problem you want to solve* rather only *your proposed solution*.
|
||||||
|
|
||||||
### Pull Requests
|
### Pull Requests
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ If you would like to suggest an enhancement or ask for a new feature:
|
|||||||
- Include screenshots and animated GIFs in your pull request whenever possible.
|
- Include screenshots and animated GIFs in your pull request whenever possible.
|
||||||
- Please add [documentation](#documentation) for new features or changes in functionality along with the code.
|
- Please add [documentation](#documentation) for new features or changes in functionality along with the code.
|
||||||
- Please follow existing code style:
|
- Please follow existing code style:
|
||||||
- Python: we use PEP8 for Python.
|
- Python: we use [Black](https://github.com/psf/black) to auto format the code.
|
||||||
- Javascript: we use Airbnb's style guides for [JavaScript](https://github.com/airbnb/javascript#naming-conventions) and [React](https://github.com/airbnb/javascript/blob/master/react) (currently we don't follow Airbnb's convention for naming files, but we're gradually fixing this). To make it automatic and easy, we recommend using [Prettier](https://github.com/prettier/prettier).
|
- Javascript: we use [Prettier](https://github.com/prettier/prettier) to auto-format the code.
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
@@ -66,9 +66,9 @@ The project's documentation can be found at [https://redash.io/help/](https://re
|
|||||||
|
|
||||||
### Release Method
|
### Release Method
|
||||||
|
|
||||||
We publish a stable release every ~2 months, although the goal is to get to a stable release every month. You can see the change log on [GitHub releases page](https://github.com/getredash/redash/releases).
|
We publish a stable release every ~3-4 months, although the goal is to get to a stable release every month.
|
||||||
|
|
||||||
Every build of the master branch updates the latest *RC release*. These releases are usually stable, but might contain regressions and therefore recommended for "advanced users" only.
|
Every build of the master branch updates the *redash/redash:preview* Docker Image. These releases are usually stable, but might contain regressions and therefore recommended for "advanced users" only.
|
||||||
|
|
||||||
When we release a new stable release, we also update the *latest* Docker image tag, the EC2 AMIs and GCE images.
|
When we release a new stable release, we also update the *latest* Docker image tag, the EC2 AMIs and GCE images.
|
||||||
|
|
||||||
|
|||||||
38
Dockerfile
@@ -1,20 +1,50 @@
|
|||||||
FROM node:10 as frontend-builder
|
FROM node:12 as frontend-builder
|
||||||
|
|
||||||
WORKDIR /frontend
|
WORKDIR /frontend
|
||||||
COPY package.json package-lock.json /frontend/
|
COPY package.json package-lock.json /frontend/
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY . /frontend
|
COPY client /frontend/client
|
||||||
|
COPY webpack.config.js /frontend/
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM redash/base:latest
|
FROM python:3.7-slim
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
# Controls whether to install extra dependencies needed for all data sources.
|
# Controls whether to install extra dependencies needed for all data sources.
|
||||||
ARG skip_ds_deps
|
ARG skip_ds_deps
|
||||||
|
|
||||||
|
RUN useradd --create-home redash
|
||||||
|
|
||||||
|
# Ubuntu packages
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y \
|
||||||
|
curl \
|
||||||
|
gnupg \
|
||||||
|
build-essential \
|
||||||
|
pwgen \
|
||||||
|
libffi-dev \
|
||||||
|
sudo \
|
||||||
|
git-core \
|
||||||
|
wget \
|
||||||
|
# Postgres client
|
||||||
|
libpq-dev \
|
||||||
|
# for SAML
|
||||||
|
xmlsec1 \
|
||||||
|
# Additional packages required for data sources:
|
||||||
|
libssl-dev \
|
||||||
|
default-libmysqlclient-dev \
|
||||||
|
freetds-dev \
|
||||||
|
libsasl2-dev && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
# We first copy only the requirements file, to avoid rebuilding on every file
|
# We first copy only the requirements file, to avoid rebuilding on every file
|
||||||
# change.
|
# change.
|
||||||
COPY requirements.txt requirements_dev.txt requirements_all_ds.txt ./
|
COPY requirements.txt requirements_bundles.txt requirements_dev.txt requirements_all_ds.txt ./
|
||||||
RUN pip install -r requirements.txt -r requirements_dev.txt
|
RUN pip install -r requirements.txt -r requirements_dev.txt
|
||||||
RUN if [ "x$skip_ds_deps" = "x" ] ; then pip install -r requirements_all_ds.txt ; else echo "Skipping pip install -r requirements_all_ds.txt" ; fi
|
RUN if [ "x$skip_ds_deps" = "x" ] ; then pip install -r requirements_all_ds.txt ; else echo "Skipping pip install -r requirements_all_ds.txt" ; fi
|
||||||
|
|
||||||
|
|||||||
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2013-2019, Arik Fraimovich.
|
Copyright (c) 2013-2020, Arik Fraimovich.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img title="Redash" src='https://redash.io/assets/images/logo.png' width="200px"/>
|
<img title="Redash" src='https://redash.io/assets/images/logo.png' width="200px"/>
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
|
||||||
<img title="Build Status" src='https://circleci.com/gh/getredash/redash.png?circle-token=8a695aa5ec2cbfa89b48c275aea298318016f040'/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
[](https://redash.io/help/)
|
[](https://redash.io/help/)
|
||||||
|
[](https://datree.io/?src=badge)
|
||||||
|
[](https://circleci.com/gh/getredash/redash/tree/master)
|
||||||
|
|
||||||
**_Redash_** is our take on freeing the data within our company in a way that will better fit our culture and usage patterns.
|
**_Redash_** is our take on freeing the data within our company in a way that will better fit our culture and usage patterns.
|
||||||
|
|
||||||
|
|||||||
5
SECURITY.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Please email security@redash.io to report any security vulnerabilities. We will acknowledge receipt of your vulnerability and strive to send you regular updates about our progress. If you're curious about the status of your disclosure please feel free to email us again. If you want to encrypt your disclosure email, you can use [this PGP key](https://keybase.io/arikfr/key.asc).
|
||||||
@@ -1,39 +1,117 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
"""Copy bundle extension files to the client/app/extension directory"""
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
from subprocess import call
|
from pathlib import Path
|
||||||
from distutils.dir_util import copy_tree
|
from shutil import copy
|
||||||
|
from collections import OrderedDict as odict
|
||||||
|
|
||||||
from pkg_resources import iter_entry_points, resource_filename, resource_isdir
|
from importlib_metadata import entry_points
|
||||||
|
from importlib_resources import contents, is_resource, path
|
||||||
|
|
||||||
|
# Name of the subdirectory
|
||||||
|
BUNDLE_DIRECTORY = "bundle"
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# Make a directory for extensions and set it as an environment variable
|
# Make a directory for extensions and set it as an environment variable
|
||||||
# to be picked up by webpack.
|
# to be picked up by webpack.
|
||||||
EXTENSIONS_RELATIVE_PATH = os.path.join('client', 'app', 'extensions')
|
extensions_relative_path = Path('client', 'app', 'extensions')
|
||||||
EXTENSIONS_DIRECTORY = os.path.join(
|
extensions_directory = Path(__file__).parent.parent / extensions_relative_path
|
||||||
os.path.dirname(os.path.dirname(__file__)),
|
|
||||||
EXTENSIONS_RELATIVE_PATH)
|
|
||||||
|
|
||||||
if not os.path.exists(EXTENSIONS_DIRECTORY):
|
if not extensions_directory.exists():
|
||||||
os.makedirs(EXTENSIONS_DIRECTORY)
|
extensions_directory.mkdir()
|
||||||
os.environ["EXTENSIONS_DIRECTORY"] = EXTENSIONS_RELATIVE_PATH
|
os.environ["EXTENSIONS_DIRECTORY"] = str(extensions_relative_path)
|
||||||
|
|
||||||
for entry_point in iter_entry_points('redash.extensions'):
|
|
||||||
# This is where the frontend code for an extension lives
|
|
||||||
# inside of its package.
|
|
||||||
content_folder_relative = os.path.join(
|
|
||||||
entry_point.name, 'bundle')
|
|
||||||
(root_module, _) = os.path.splitext(entry_point.module_name)
|
|
||||||
|
|
||||||
if not resource_isdir(root_module, content_folder_relative):
|
def resource_isdir(module, resource):
|
||||||
|
"""Whether a given resource is a directory in the given module
|
||||||
|
|
||||||
|
https://importlib-resources.readthedocs.io/en/latest/migration.html#pkg-resources-resource-isdir
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return resource in contents(module) and not is_resource(module, resource)
|
||||||
|
except (ImportError, TypeError):
|
||||||
|
# module isn't a package, so can't have a subdirectory/-package
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def entry_point_module(entry_point):
|
||||||
|
"""Returns the dotted module path for the given entry point"""
|
||||||
|
return entry_point.pattern.match(entry_point.value).group("module")
|
||||||
|
|
||||||
|
|
||||||
|
def load_bundles():
|
||||||
|
""""Load bundles as defined in Redash extensions.
|
||||||
|
|
||||||
|
The bundle entry point can be defined as a dotted path to a module
|
||||||
|
or a callable, but it won't be called but just used as a means
|
||||||
|
to find the files under its file system path.
|
||||||
|
|
||||||
|
The name of the directory it looks for files in is "bundle".
|
||||||
|
|
||||||
|
So a Python package with an extension bundle could look like this::
|
||||||
|
|
||||||
|
my_extensions/
|
||||||
|
├── __init__.py
|
||||||
|
└── wide_footer
|
||||||
|
├── __init__.py
|
||||||
|
└── bundle
|
||||||
|
├── extension.js
|
||||||
|
└── styles.css
|
||||||
|
|
||||||
|
and would then need to register the bundle with an entry point
|
||||||
|
under the "redash.bundles" group, e.g. in your setup.py::
|
||||||
|
|
||||||
|
setup(
|
||||||
|
# ...
|
||||||
|
entry_points={
|
||||||
|
"redash.bundles": [
|
||||||
|
"wide_footer = my_extensions.wide_footer",
|
||||||
|
]
|
||||||
|
# ...
|
||||||
|
},
|
||||||
|
# ...
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
bundles = odict()
|
||||||
|
for entry_point in entry_points().get("redash.bundles", []):
|
||||||
|
logger.info('Loading Redash bundle "%s".', entry_point.name)
|
||||||
|
module = entry_point_module(entry_point)
|
||||||
|
# Try to get a list of bundle files
|
||||||
|
if not resource_isdir(module, BUNDLE_DIRECTORY):
|
||||||
|
logger.error(
|
||||||
|
'Redash bundle directory "%s" could not be found.', entry_point.name
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
with path(module, BUNDLE_DIRECTORY) as bundle_dir:
|
||||||
|
bundles[entry_point.name] = list(bundle_dir.rglob("*"))
|
||||||
|
|
||||||
|
return bundles
|
||||||
|
|
||||||
|
|
||||||
|
bundles = load_bundles().items()
|
||||||
|
if bundles:
|
||||||
|
print('Number of extension bundles found: {}'.format(len(bundles)))
|
||||||
|
else:
|
||||||
|
print('No extension bundles found.')
|
||||||
|
|
||||||
|
for bundle_name, paths in bundles:
|
||||||
|
# Shortcut in case not paths were found for the bundle
|
||||||
|
if not paths:
|
||||||
|
print('No paths found for bundle "{}".'.format(bundle_name))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
content_folder = resource_filename(root_module, content_folder_relative)
|
# The destination for the bundle files with the entry point name as the subdirectory
|
||||||
|
destination = Path(extensions_directory, bundle_name)
|
||||||
|
if not destination.exists():
|
||||||
|
destination.mkdir()
|
||||||
|
|
||||||
# This is where we place our extensions folder.
|
# Copy the bundle directory from the module to its destination.
|
||||||
destination = os.path.join(
|
print('Copying "{}" bundle to {}:'.format(bundle_name, destination.resolve()))
|
||||||
EXTENSIONS_DIRECTORY,
|
for src_path in paths:
|
||||||
entry_point.name)
|
dest_path = destination / src_path.name
|
||||||
|
print(" - {} -> {}".format(src_path, dest_path))
|
||||||
copy_tree(content_folder, destination)
|
copy(str(src_path), str(dest_path))
|
||||||
|
|||||||
@@ -1,35 +1,46 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
worker() {
|
scheduler() {
|
||||||
WORKERS_COUNT=${WORKERS_COUNT:-2}
|
echo "Starting RQ scheduler..."
|
||||||
QUEUES=${QUEUES:-queries,scheduled_queries,celery,schemas}
|
|
||||||
WORKER_EXTRA_OPTIONS=${WORKER_EXTRA_OPTIONS:-}
|
|
||||||
|
|
||||||
echo "Starting $WORKERS_COUNT workers for queues: $QUEUES..."
|
exec /app/manage.py rq scheduler
|
||||||
exec /usr/local/bin/celery worker --app=redash.worker -c$WORKERS_COUNT -Q$QUEUES -linfo --max-tasks-per-child=10 -Ofair $WORKER_EXTRA_OPTIONS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduler() {
|
dev_scheduler() {
|
||||||
WORKERS_COUNT=${WORKERS_COUNT:-1}
|
echo "Starting dev RQ scheduler..."
|
||||||
QUEUES=${QUEUES:-celery}
|
|
||||||
SCHEDULE_DB=${SCHEDULE_DB:-celerybeat-schedule}
|
|
||||||
|
|
||||||
echo "Starting scheduler and $WORKERS_COUNT workers for queues: $QUEUES..."
|
exec watchmedo auto-restart --directory=./redash/ --pattern=*.py --recursive -- ./manage.py rq scheduler
|
||||||
|
}
|
||||||
|
|
||||||
exec /usr/local/bin/celery worker --app=redash.worker --beat -s$SCHEDULE_DB -c$WORKERS_COUNT -Q$QUEUES -linfo --max-tasks-per-child=10 -Ofair
|
worker() {
|
||||||
|
echo "Starting RQ worker..."
|
||||||
|
|
||||||
|
export WORKERS_COUNT=${WORKERS_COUNT:-2}
|
||||||
|
export QUEUES=${QUEUES:-}
|
||||||
|
|
||||||
|
supervisord -c worker.conf
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_worker() {
|
||||||
|
echo "Starting dev RQ worker..."
|
||||||
|
|
||||||
|
exec watchmedo auto-restart --directory=./redash/ --pattern=*.py --recursive -- ./manage.py rq worker $QUEUES
|
||||||
}
|
}
|
||||||
|
|
||||||
server() {
|
server() {
|
||||||
exec /usr/local/bin/gunicorn -b 0.0.0.0:5000 --name redash -w${REDASH_WEB_WORKERS:-4} redash.wsgi:app
|
# Recycle gunicorn workers every n-th request. See http://docs.gunicorn.org/en/stable/settings.html#max-requests for more details.
|
||||||
|
MAX_REQUESTS=${MAX_REQUESTS:-1000}
|
||||||
|
MAX_REQUESTS_JITTER=${MAX_REQUESTS_JITTER:-100}
|
||||||
|
exec /usr/local/bin/gunicorn -b 0.0.0.0:5000 --name redash -w${REDASH_WEB_WORKERS:-4} redash.wsgi:app --max-requests $MAX_REQUESTS --max-requests-jitter $MAX_REQUESTS_JITTER
|
||||||
}
|
}
|
||||||
|
|
||||||
create_db() {
|
create_db() {
|
||||||
exec /app/manage.py database create_tables
|
exec /app/manage.py database create_tables
|
||||||
}
|
}
|
||||||
|
|
||||||
celery_healthcheck() {
|
rq_healthcheck() {
|
||||||
exec /usr/local/bin/celery inspect ping --app=redash.worker -d celery@$HOSTNAME
|
exec /app/manage.py rq healthcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
help() {
|
help() {
|
||||||
@@ -39,9 +50,11 @@ help() {
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo "server -- start Redash server (with gunicorn)"
|
echo "server -- start Redash server (with gunicorn)"
|
||||||
echo "worker -- start Celery worker"
|
echo "worker -- start a single RQ worker"
|
||||||
echo "scheduler -- start Celery worker with a beat (scheduler) process"
|
echo "dev_worker -- start a single RQ worker with code reloading"
|
||||||
echo "celery_healthcheck -- runs a Celery healthcheck. Useful for Docker's HEALTHCHECK mechanism."
|
echo "scheduler -- start an rq-scheduler instance"
|
||||||
|
echo "dev_scheduler -- start an rq-scheduler instance with code reloading"
|
||||||
|
echo "rq_healthcheck -- runs a RQ healthcheck that verifies that all local workers are active. Useful for Docker's HEALTHCHECK mechanism."
|
||||||
echo ""
|
echo ""
|
||||||
echo "shell -- open shell"
|
echo "shell -- open shell"
|
||||||
echo "dev_server -- start Flask development server with debugger and auto reload"
|
echo "dev_server -- start Flask development server with debugger and auto reload"
|
||||||
@@ -75,6 +88,18 @@ case "$1" in
|
|||||||
shift
|
shift
|
||||||
scheduler
|
scheduler
|
||||||
;;
|
;;
|
||||||
|
dev_scheduler)
|
||||||
|
shift
|
||||||
|
dev_scheduler
|
||||||
|
;;
|
||||||
|
dev_worker)
|
||||||
|
shift
|
||||||
|
dev_worker
|
||||||
|
;;
|
||||||
|
rq_healthcheck)
|
||||||
|
shift
|
||||||
|
rq_healthcheck
|
||||||
|
;;
|
||||||
dev_server)
|
dev_server)
|
||||||
export FLASK_DEBUG=1
|
export FLASK_DEBUG=1
|
||||||
exec /app/manage.py runserver --debugger --reload -h 0.0.0.0
|
exec /app/manage.py runserver --debugger --reload -h 0.0.0.0
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -o errexit # fail the build if any task fails
|
||||||
|
|
||||||
flake8 --version ; pip --version
|
flake8 --version ; pip --version
|
||||||
# stop the build if there are Python syntax errors or undefined names
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#!/bin/env python
|
#!/bin/env python3
|
||||||
from __future__ import print_function
|
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
def get_change_log(previous_sha):
|
def get_change_log(previous_sha):
|
||||||
args = ['git', '--no-pager', 'log', '--merges', '--grep', 'Merge pull request', '--pretty=format:"%h|%s|%b|%p"', 'master...{}'.format(previous_sha)]
|
args = ['git', '--no-pager', 'log', '--merges', '--grep', 'Merge pull request', '--pretty=format:"%h|%s|%b|%p"', 'master...{}'.format(previous_sha)]
|
||||||
log = subprocess.check_output(args)
|
log = subprocess.check_output(args)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from __future__ import print_function
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
import urllib
|
import urllib
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
@@ -27,7 +27,7 @@ def run(cmd, cwd=None):
|
|||||||
|
|
||||||
|
|
||||||
def confirm(question):
|
def confirm(question):
|
||||||
reply = str(raw_input(question + ' (y/n): ')).lower().strip()
|
reply = str(input(question + ' (y/n): ')).lower().strip()
|
||||||
|
|
||||||
if reply[0] == 'y':
|
if reply[0] == 'y':
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
"@babel/preset-react"
|
"@babel/preset-react"
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"angularjs-annotate",
|
|
||||||
"@babel/plugin-proposal-class-properties",
|
"@babel/plugin-proposal-class-properties",
|
||||||
"@babel/plugin-transform-object-assign",
|
"@babel/plugin-transform-object-assign",
|
||||||
["babel-plugin-transform-builtin-extend", {
|
["babel-plugin-transform-builtin-extend", {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
build/*.js
|
build/*.js
|
||||||
|
dist
|
||||||
config/*.js
|
config/*.js
|
||||||
client/dist
|
client/dist
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
extends: ["airbnb", "plugin:compat/recommended"],
|
extends: ["react-app", "plugin:compat/recommended", "prettier"],
|
||||||
plugins: ["jest", "compat"],
|
plugins: ["jest", "compat", "no-only-tests"],
|
||||||
settings: {
|
settings: {
|
||||||
"import/resolver": "webpack"
|
"import/resolver": "webpack"
|
||||||
},
|
},
|
||||||
parser: "babel-eslint",
|
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
node: true
|
node: true
|
||||||
@@ -13,54 +12,6 @@ module.exports = {
|
|||||||
rules: {
|
rules: {
|
||||||
// allow debugger during development
|
// allow debugger during development
|
||||||
"no-debugger": process.env.NODE_ENV === "production" ? 2 : 0,
|
"no-debugger": process.env.NODE_ENV === "production" ? 2 : 0,
|
||||||
"no-param-reassign": 0,
|
|
||||||
"no-mixed-operators": 0,
|
|
||||||
"no-underscore-dangle": 0,
|
|
||||||
"no-use-before-define": ["error", "nofunc"],
|
|
||||||
"prefer-destructuring": "off",
|
|
||||||
"prefer-template": "off",
|
|
||||||
"no-restricted-properties": "off",
|
|
||||||
"no-restricted-globals": "off",
|
|
||||||
"no-multi-assign": "off",
|
|
||||||
"no-lonely-if": "off",
|
|
||||||
"consistent-return": "off",
|
|
||||||
"no-control-regex": "off",
|
|
||||||
"no-multiple-empty-lines": "warn",
|
|
||||||
"no-script-url": "off", // some <a> tags should have href="javascript:void(0)"
|
|
||||||
"operator-linebreak": "off",
|
|
||||||
"react/destructuring-assignment": "off",
|
|
||||||
"react/jsx-filename-extension": "off",
|
|
||||||
"react/jsx-one-expression-per-line": "off",
|
|
||||||
"react/jsx-uses-react": "error",
|
|
||||||
"react/jsx-uses-vars": "error",
|
|
||||||
"react/jsx-wrap-multilines": "warn",
|
|
||||||
"react/no-access-state-in-setstate": "warn",
|
|
||||||
"react/prefer-stateless-function": "warn",
|
|
||||||
"react/forbid-prop-types": "warn",
|
|
||||||
"react/prop-types": "warn",
|
|
||||||
"jsx-a11y/anchor-is-valid": "off",
|
"jsx-a11y/anchor-is-valid": "off",
|
||||||
"jsx-a11y/click-events-have-key-events": "off",
|
|
||||||
"jsx-a11y/label-has-associated-control": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
controlComponents: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"jsx-a11y/label-has-for": "off",
|
|
||||||
"jsx-a11y/no-static-element-interactions": "off",
|
|
||||||
"max-len": [
|
|
||||||
"error",
|
|
||||||
120,
|
|
||||||
2,
|
|
||||||
{
|
|
||||||
ignoreUrls: true,
|
|
||||||
ignoreComments: false,
|
|
||||||
ignoreRegExpLiterals: true,
|
|
||||||
ignoreStrings: true,
|
|
||||||
ignoreTemplateLiterals: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-else-return": ["error", { allowElseIf: true }],
|
|
||||||
"object-curly-newline": ["error", { consistent: true }]
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,4 +4,7 @@ module.exports = {
|
|||||||
env: {
|
env: {
|
||||||
"jest/globals": true,
|
"jest/globals": true,
|
||||||
},
|
},
|
||||||
|
rules: {
|
||||||
|
"jest/no-focused-tests": "off",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { configure } from 'enzyme';
|
import { configure } from "enzyme";
|
||||||
import Adapter from 'enzyme-adapter-react-16';
|
import Adapter from "enzyme-adapter-react-16";
|
||||||
|
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import MockDate from 'mockdate';
|
import MockDate from "mockdate";
|
||||||
|
|
||||||
const date = new Date('2000-01-01T02:00:00.000');
|
const date = new Date("2000-01-01T02:00:00.000");
|
||||||
|
|
||||||
MockDate.set(date);
|
MockDate.set(date);
|
||||||
|
|||||||
BIN
client/app/assets/images/db-logos/azure_kusto.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
client/app/assets/images/db-logos/bigquery_gce.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
client/app/assets/images/db-logos/cloudwatch.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
client/app/assets/images/db-logos/cloudwatch_insights.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
client/app/assets/images/db-logos/dgraph.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
client/app/assets/images/db-logos/exasol.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
client/app/assets/images/db-logos/json.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
client/app/assets/images/db-logos/redshift_iam.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 9.3 KiB |
@@ -33,14 +33,39 @@
|
|||||||
@import "~antd/lib/spin/style/index";
|
@import "~antd/lib/spin/style/index";
|
||||||
@import "~antd/lib/tabs/style/index";
|
@import "~antd/lib/tabs/style/index";
|
||||||
@import "~antd/lib/notification/style/index";
|
@import "~antd/lib/notification/style/index";
|
||||||
|
@import "~antd/lib/collapse/style/index";
|
||||||
|
@import "~antd/lib/progress/style/index";
|
||||||
|
@import "~antd/lib/typography/style/index";
|
||||||
@import 'inc/ant-variables';
|
@import 'inc/ant-variables';
|
||||||
|
|
||||||
|
// Increase z-indexes to avoid conflicts with some other libraries (e.g. Plotly)
|
||||||
|
@zindex-modal: 2000;
|
||||||
|
@zindex-modal-mask: 2000;
|
||||||
|
@zindex-message: 2010;
|
||||||
|
@zindex-notification: 2010;
|
||||||
|
@zindex-popover: 2030;
|
||||||
|
@zindex-dropdown: 2050;
|
||||||
|
@zindex-picker: 2050;
|
||||||
|
@zindex-tooltip: 2060;
|
||||||
|
@item-hover-bg: #e5f8ff;
|
||||||
|
|
||||||
|
.@{drawer-prefix-cls} {
|
||||||
|
&.help-drawer {
|
||||||
|
z-index: @zindex-tooltip; // help drawer should be topmost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove bold in labels for Ant checkboxes and radio buttons
|
// Remove bold in labels for Ant checkboxes and radio buttons
|
||||||
.ant-checkbox-wrapper,
|
.ant-checkbox-wrapper,
|
||||||
.ant-radio-wrapper {
|
.ant-radio-wrapper {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-select-dropdown-menu-item em {
|
||||||
|
color: @input-color-placeholder;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
// Fix for disabled button styles inside Tooltip component.
|
// Fix for disabled button styles inside Tooltip component.
|
||||||
// Tooltip wraps disabled buttons with `<span>` and moves all styles
|
// Tooltip wraps disabled buttons with `<span>` and moves all styles
|
||||||
// and classes to that `<span>`. This resets all button styles and
|
// and classes to that `<span>`. This resets all button styles and
|
||||||
@@ -58,14 +83,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix for Ant dropdowns when they are used in Boootstrap modals
|
|
||||||
.ant-dropdown-in-bootstrap-modal {
|
|
||||||
z-index: 1050;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Button overrides
|
// Button overrides
|
||||||
.@{btn-prefix-cls} {
|
.@{btn-prefix-cls} {
|
||||||
transition-duration: 150ms;
|
transition-duration: 150ms;
|
||||||
|
|
||||||
|
&.icon-button {
|
||||||
|
width: 32px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix ant input number showing duplicate arrows
|
// Fix ant input number showing duplicate arrows
|
||||||
@@ -136,6 +161,10 @@
|
|||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
color: @pagination-color;
|
color: @pagination-color;
|
||||||
line-height: @pagination-item-size - 2px;
|
line-height: @pagination-item-size - 2px;
|
||||||
|
|
||||||
|
.@{pagination-prefix-cls}.mini & {
|
||||||
|
line-height: @pagination-item-size-sm - 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus .@{pagination-prefix-cls}-item-link,
|
&:focus .@{pagination-prefix-cls}-item-link,
|
||||||
@@ -288,10 +317,6 @@
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-popover {
|
|
||||||
z-index: 1000; // make sure it doesn't cover drawer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notification overrides
|
// Notification overrides
|
||||||
.@{notification-prefix-cls} {
|
.@{notification-prefix-cls} {
|
||||||
// vertical centering
|
// vertical centering
|
||||||
@@ -306,5 +331,72 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.@{btn-prefix-cls} .@{iconfont-css-prefix}-ellipsis {
|
.@{btn-prefix-cls} .@{iconfont-css-prefix}-ellipsis {
|
||||||
margin: 0 -7px;
|
margin: 0 -7px 0 -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapse
|
||||||
|
|
||||||
|
.@{collapse-prefix-cls} {
|
||||||
|
&&-headerless {
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
.@{collapse-prefix-cls}-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{collapse-prefix-cls}-item,
|
||||||
|
.@{collapse-prefix-cls}-content {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{collapse-prefix-cls}-content-box {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// overrides for tall form components such as ace editor
|
||||||
|
.@{form-prefix-cls}-item {
|
||||||
|
&-children {
|
||||||
|
display: block; // so feeback icon positions correctly
|
||||||
|
}
|
||||||
|
|
||||||
|
// no change for short components, sticks to body for tall ones
|
||||||
|
&-children-icon {
|
||||||
|
top: auto !important;
|
||||||
|
bottom: 8px;
|
||||||
|
|
||||||
|
// makes the icon white instead of see-through
|
||||||
|
& svg {
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for form items that contain text
|
||||||
|
&.form-item-line-height-normal .@{form-prefix-cls}-item-control {
|
||||||
|
line-height: 20px;
|
||||||
|
margin-top: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{menu-prefix-cls} {
|
||||||
|
// invert stripe position with class .invert-stripe-position
|
||||||
|
&-inline.invert-stripe-position {
|
||||||
|
.@{menu-prefix-cls}-item {
|
||||||
|
&::after {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// overrides for checkbox
|
||||||
|
@checkbox-prefix-cls: ~'@{ant-prefix}-checkbox';
|
||||||
|
|
||||||
|
.@{checkbox-prefix-cls}-wrapper + span,
|
||||||
|
.@{checkbox-prefix-cls} + span {
|
||||||
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,25 @@
|
|||||||
.ace_editor {
|
.ace_editor {
|
||||||
border: 1px solid #eee;
|
border: 1px solid fade(@redash-gray, 15%);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&.ace_autocomplete .ace_completion-highlight {
|
||||||
|
text-shadow: none !important;
|
||||||
|
background: #ffff005e;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ace-tm {
|
||||||
|
.ace_gutter {
|
||||||
|
background: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_gutter-active-line {
|
||||||
|
background-color: fade(@redash-gray, 20%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_marker-layer .ace_active-line {
|
||||||
|
background: fade(@redash-gray, 9%) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,53 @@
|
|||||||
.alert {
|
.alert-page h3 {
|
||||||
padding-left: 30px;
|
flex-grow: 1;
|
||||||
padding-right: 30px;
|
|
||||||
|
|
||||||
span {
|
input {
|
||||||
cursor: pointer;
|
margin: -0.2em 0;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 170px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-dismissable,
|
.btn-create-alert[disabled] {
|
||||||
.alert-dismissible {
|
display: block;
|
||||||
padding-right: 44px;
|
margin-top: -20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-inverse {
|
.alert-state {
|
||||||
.alert-variant(@alert-inverse-bg; @alert-inverse-border; @alert-inverse-text);
|
border-bottom: 1px solid @input-border;
|
||||||
}
|
padding-bottom: 30px;
|
||||||
|
|
||||||
.alert-link {
|
.alert-state-indicator {
|
||||||
color: #fff !important;
|
text-transform: uppercase;
|
||||||
font-weight: normal !important;
|
font-size: 14px;
|
||||||
text-decoration: underline;
|
padding: 5px 8px;
|
||||||
}
|
|
||||||
|
|
||||||
.growl-animated {
|
|
||||||
&.alert-inverse {
|
|
||||||
box-shadow: 0 0 5px fade(@alert-inverse-bg, 50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.alert-info {
|
.alert-last-triggered {
|
||||||
box-shadow: 0 0 5px fade(@alert-info-bg, 50%);
|
color: @headings-color;
|
||||||
}
|
|
||||||
|
|
||||||
&.alert-success {
|
|
||||||
box-shadow: 0 0 5px fade(@alert-success-bg, 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.alert-warning {
|
|
||||||
box-shadow: 0 0 5px fade(@alert-warning-bg, 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.alert-danger {
|
|
||||||
box-shadow: 0 0 5px fade(@alert-danger-bg, 50%);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.alert-query-selector {
|
||||||
|
min-width: 250px;
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow form item labels to gracefully break line
|
||||||
|
.alert-form-item label {
|
||||||
|
white-space: initial;
|
||||||
|
padding-right: 8px;
|
||||||
|
line-height: 21px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-actions {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: -15px;
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
a[ng-click] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Immediately apply ng-cloak, instead of waiting for angular.js to load: */
|
|
||||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,12 @@
|
|||||||
@font-size-base: 13px;
|
@font-size-base: 13px;
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------
|
||||||
|
Borders
|
||||||
|
-----------------------------------------------------------*/
|
||||||
|
@border-color-split: #f0f0f0;
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
Typograpgy
|
Typograpgy
|
||||||
-----------------------------------------------------------*/
|
-----------------------------------------------------------*/
|
||||||
@@ -30,6 +36,7 @@
|
|||||||
-----------------------------------------------------------*/
|
-----------------------------------------------------------*/
|
||||||
@input-height-base: 35px;
|
@input-height-base: 35px;
|
||||||
@input-color: #595959;
|
@input-color: #595959;
|
||||||
|
@input-color-placeholder: #b4b4b4;
|
||||||
@border-radius-base: 2px;
|
@border-radius-base: 2px;
|
||||||
@border-color-base: #E8E8E8;
|
@border-color-base: #E8E8E8;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
*, button, input, i, a {
|
*,
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
i,
|
||||||
|
a {
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,26 +18,39 @@ html {
|
|||||||
-ms-overflow-style: auto;
|
-ms-overflow-style: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
padding-top: @header-height;
|
|
||||||
position: relative;
|
|
||||||
&.headless {
|
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
.nav.app-header {
|
background: #f6f8f9;
|
||||||
|
font-family: @redash-font;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
#application-root {
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.headless {
|
||||||
|
#application-root {
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app-view {
|
#application-root {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-view, #app-content {
|
#application-root,
|
||||||
|
#app-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -72,10 +89,52 @@ strong {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fixed width layout for specific pages
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.settings-screen,
|
||||||
|
.home-page,
|
||||||
|
.page-dashboard-list,
|
||||||
|
.page-queries-list,
|
||||||
|
.page-alerts-list,
|
||||||
|
.alert-page,
|
||||||
|
.fixed-container {
|
||||||
|
.container {
|
||||||
|
width: 750px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.settings-screen,
|
||||||
|
.home-page,
|
||||||
|
.page-dashboard-list,
|
||||||
|
.page-queries-list,
|
||||||
|
.page-alerts-list,
|
||||||
|
.alert-page,
|
||||||
|
.fixed-container {
|
||||||
|
.container {
|
||||||
|
width: 970px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.settings-screen,
|
||||||
|
.home-page,
|
||||||
|
.page-dashboard-list,
|
||||||
|
.page-queries-list,
|
||||||
|
.page-alerts-list,
|
||||||
|
.alert-page,
|
||||||
|
.fixed-container {
|
||||||
|
.container {
|
||||||
|
width: 1170px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.scrollbox {
|
.scrollbox {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.clickable {
|
.clickable {
|
||||||
@@ -95,3 +154,120 @@ strong {
|
|||||||
resize: both !important;
|
resize: both !important;
|
||||||
transition: height 0s, width 0s !important;
|
transition: height 0s, width 0s !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-ace {
|
||||||
|
background-color: fade(@redash-gray, 12%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resizeable
|
||||||
|
.rg-top span,
|
||||||
|
.rg-bottom span {
|
||||||
|
height: 3px;
|
||||||
|
border-color: #b1c1ce; // TODO: variable
|
||||||
|
}
|
||||||
|
|
||||||
|
.rg-bottom {
|
||||||
|
bottom: 15px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin: 1.5px 0 0 -10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plotly
|
||||||
|
text.slicetext {
|
||||||
|
text-shadow: 1px 1px 5px #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
// markdown
|
||||||
|
.markdown strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu > li > a:hover,
|
||||||
|
.dropdown-menu > li > a:focus {
|
||||||
|
background-color: fade(@redash-gray, 15%);
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile__image--sidebar {
|
||||||
|
border-radius: 100%;
|
||||||
|
margin-right: 3px;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile__image--settings {
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile__image_thumb {
|
||||||
|
border-radius: 100%;
|
||||||
|
margin-right: 3px;
|
||||||
|
margin-top: -2px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error state
|
||||||
|
.error-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 25vh;
|
||||||
|
padding: 35px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 21px;
|
||||||
|
|
||||||
|
.error-state__icon {
|
||||||
|
.zmdi {
|
||||||
|
font-size: 64px;
|
||||||
|
color: @redash-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
margin-top: 10vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-icon-danger {
|
||||||
|
color: @red !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// page
|
||||||
|
.page-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-right: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-top: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorites-control {
|
||||||
|
font-size: 19px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header-wrapper,
|
||||||
|
.page-header--new {
|
||||||
|
h3 {
|
||||||
|
margin: 0.2em 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-option-divider {
|
||||||
|
margin: 10px 0 !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
.collapsing,
|
.collapsing,
|
||||||
.collapse.in {
|
.collapse.in {
|
||||||
padding: 5px 10px;
|
padding: 0;
|
||||||
transition: all 0.35s ease;
|
transition: all 0.35s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,3 +122,21 @@
|
|||||||
top: 1px;
|
top: 1px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-default {
|
||||||
|
background-color: fade(@redash-gray, 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-transparent {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default {
|
||||||
|
background-color: fade(@redash-gray, 25%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus {
|
||||||
|
color: #333;
|
||||||
|
background-color: fade(@redash-gray, 45%);
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.edit-in-place span.editable {
|
.edit-in-place span.editable {
|
||||||
|
display: inline-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,32 +24,3 @@
|
|||||||
.edit-in-place {
|
.edit-in-place {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-in-place {
|
|
||||||
.rd-form-control {
|
|
||||||
padding: 0px 6px;
|
|
||||||
width: 30vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
textarea.rd-form-control {
|
|
||||||
height: 29px;
|
|
||||||
width: 40vw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 880px) {
|
|
||||||
.edit-in-place {
|
|
||||||
.rd-form-control {
|
|
||||||
width: 50vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
textarea.rd-form-control {
|
|
||||||
width: 50vw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,24 +29,6 @@ textarea.v-resizable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* light version of bootstrap's form-control */
|
|
||||||
.rd-form-control {
|
|
||||||
display: block;
|
|
||||||
padding: 6px 12px;
|
|
||||||
line-height: 1.428571429;
|
|
||||||
color: #555555;
|
|
||||||
vertical-align: middle;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: 1px solid #cccccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
|
||||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
Input Fields
|
Input Fields
|
||||||
-----------------------------------------------------------*/
|
-----------------------------------------------------------*/
|
||||||
@@ -55,19 +37,23 @@ textarea.v-resizable {
|
|||||||
.transition-duration(300ms);
|
.transition-duration(300ms);
|
||||||
resize: none;
|
resize: none;
|
||||||
box-shadow: 0 0 0 40px rgba(0, 0, 0, 0) !important;
|
box-shadow: 0 0 0 40px rgba(0, 0, 0, 0) !important;
|
||||||
border-radius: 0;
|
border-radius: @redash-input-radius;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: 0 0 1px -2px rgba(121,194,255,0.5) !important;
|
box-shadow: none !important;
|
||||||
|
border-color: @blue;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
border-color: @blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
Custom Checkbox + Radio
|
Custom Checkbox + Radio
|
||||||
-----------------------------------------------------------*/
|
-----------------------------------------------------------*/
|
||||||
.cra-validatation(@color) {
|
.cra-validatation(@color) {
|
||||||
input[type="checkbox"], input[type="radio"] {
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
& + .input-helper {
|
& + .input-helper {
|
||||||
border-color: @color;
|
border-color: @color;
|
||||||
}
|
}
|
||||||
@@ -94,15 +80,14 @@ textarea.v-resizable {
|
|||||||
|
|
||||||
&.has-warning {
|
&.has-warning {
|
||||||
.cra-validatation(@orange);
|
.cra-validatation(@orange);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.has-error {
|
&.has-error {
|
||||||
.cra-validatation(@red);
|
.cra-validatation(@red);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"], input[type="radio"] {
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
.opacity(0);
|
.opacity(0);
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@@ -128,7 +113,7 @@ textarea.v-resizable {
|
|||||||
content: "";
|
content: "";
|
||||||
width: 9px;
|
width: 9px;
|
||||||
height: 9px;
|
height: 9px;
|
||||||
background: #31ACFF;
|
background: #31acff;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 4px;
|
left: 4px;
|
||||||
top: 4px;
|
top: 4px;
|
||||||
@@ -155,7 +140,6 @@ textarea.v-resizable {
|
|||||||
padding-left: 27px;
|
padding-left: 27px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
Input Addon
|
Input Addon
|
||||||
-----------------------------------------------------------*/
|
-----------------------------------------------------------*/
|
||||||
@@ -173,11 +157,10 @@ textarea.v-resizable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
Toggle Switch
|
Toggle Switch
|
||||||
-----------------------------------------------------------*/
|
-----------------------------------------------------------*/
|
||||||
.ts-color(@color){
|
.ts-color(@color) {
|
||||||
input {
|
input {
|
||||||
&:not(:disabled) {
|
&:not(:disabled) {
|
||||||
&:checked {
|
&:checked {
|
||||||
@@ -190,7 +173,7 @@ textarea.v-resizable {
|
|||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
&:before {
|
&:before {
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.28), 0 0 0 20px fade(@color, 20%);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px fade(@color, 20%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,24 +201,26 @@ textarea.v-resizable {
|
|||||||
width: 40px;
|
width: 40px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: rgba(0,0,0,0.26);
|
background: rgba(0, 0, 0, 0.26);
|
||||||
-webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
-webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -4px;
|
top: -4px;
|
||||||
left: -4px;
|
left: -4px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.28);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
webkit-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
webkit-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +228,7 @@ textarea.v-resizable {
|
|||||||
.ts-helper {
|
.ts-helper {
|
||||||
&:active {
|
&:active {
|
||||||
&:before {
|
&:before {
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.28), 0 0 0 20px rgba(128,128,128,0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28), 0 0 0 20px rgba(128, 128, 128, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,7 +252,7 @@ textarea.v-resizable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not([data-ts-color]){
|
&:not([data-ts-color]) {
|
||||||
.ts-color(@teal);
|
.ts-color(@teal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,5 +291,4 @@ textarea.v-resizable {
|
|||||||
&[data-ts-color="green"] {
|
&[data-ts-color="green"] {
|
||||||
.ts-color(@green);
|
.ts-color(@green);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,8 @@
|
|||||||
|
|
||||||
.font-size(20, 8px, 8);
|
.font-size(20, 8px, 8);
|
||||||
|
|
||||||
|
.f-inherit { font-size: inherit !important; }
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
Font Weight
|
Font Weight
|
||||||
@@ -154,3 +156,9 @@
|
|||||||
Border Radius
|
Border Radius
|
||||||
-----------------------------------------------------------*/
|
-----------------------------------------------------------*/
|
||||||
.brd-2 { border-radius: 2px; }
|
.brd-2 { border-radius: 2px; }
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------
|
||||||
|
Alignment
|
||||||
|
-----------------------------------------------------------*/
|
||||||
|
.va-top { vertical-align: top; }
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/* angular-growl */
|
|
||||||
.growl {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 10px;
|
|
||||||
right: 10px;
|
|
||||||
float: right;
|
|
||||||
width: 250px;
|
|
||||||
z-index: 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.growl-item.ng-enter,
|
|
||||||
.growl-item.ng-leave {
|
|
||||||
-webkit-transition: 0.5s linear all;
|
|
||||||
-moz-transition: 0.5s linear all;
|
|
||||||
-o-transition: 0.5s linear all;
|
|
||||||
transition: 0.5s linear all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.growl-item.ng-enter,
|
|
||||||
.growl-item.ng-leave.ng-leave-active {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.growl-item.ng-leave,
|
|
||||||
.growl-item.ng-enter.ng-enter-active {
|
|
||||||
opacity: 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,37 @@
|
|||||||
.label {
|
.label {
|
||||||
border-radius: 1px;
|
|
||||||
padding: 4px 5px 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
.label {
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
padding: 3px 6px 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-default {
|
||||||
|
background: fade(@redash-gray, 85%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-tag-unpublished {
|
||||||
|
background: fade(@redash-gray, 85%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-tag-archived {
|
||||||
|
.label-warning();
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-tag {
|
||||||
|
background: fade(@redash-gray, 10%);
|
||||||
|
color: fade(@redash-gray, 75%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-tag-unpublished,
|
||||||
|
.label-tag-archived,
|
||||||
|
.label-tag {
|
||||||
|
margin-right: 3px;
|
||||||
|
display: inline;
|
||||||
|
margin-top: 2px;
|
||||||
|
max-width: 24ch;
|
||||||
|
.text-overflow();
|
||||||
|
}
|
||||||
@@ -17,20 +17,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tags-list {
|
|
||||||
a {
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags-list__name {
|
|
||||||
display: inline-block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
width: 88%;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.max-character {
|
.max-character {
|
||||||
.text-overflow();
|
.text-overflow();
|
||||||
}
|
}
|
||||||
@@ -45,6 +31,11 @@ tags-list {
|
|||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.active, &.active:hover, &.active:focus {
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: inset 3px 0px 0px @brand-primary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-group-item-heading {
|
.list-group-item-heading {
|
||||||
@@ -76,3 +67,18 @@ tags-list {
|
|||||||
height: 38px;
|
height: 38px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-select-choices-row.disabled > span {
|
||||||
|
background-color: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item.inactive,
|
||||||
|
.ui-select-choices-row.disabled {
|
||||||
|
background-color: #eee !important;
|
||||||
|
border-color: transparent;
|
||||||
|
opacity: 0.5;
|
||||||
|
box-shadow: none;
|
||||||
|
color: #333;
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
@@ -234,4 +234,9 @@
|
|||||||
.hide-in-percy, .pace {
|
.hide-in-percy, .pace {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hide tooltips in Percy
|
||||||
|
.ant-tooltip {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
a.navbar-brand {
|
|
||||||
padding: 5px 5px 0px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .fa {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .collapse.in {
|
|
||||||
background: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.navbar-brand img {
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar img {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logout {
|
|
||||||
color: white;
|
|
||||||
position: relative;
|
|
||||||
left: -9px;
|
|
||||||
bottom: -11px;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
.overlay {
|
|
||||||
background-color: #808080;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
padding: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
.pagination {
|
|
||||||
border-radius: 0;
|
|
||||||
|
|
||||||
& > li {
|
|
||||||
margin: 0 2px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
|
|
||||||
& > a,
|
|
||||||
& > span {
|
|
||||||
border-radius: 50% !important;
|
|
||||||
padding: 0;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
line-height: 38px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
|
||||||
z-index: 1;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
& > .zmdi {
|
|
||||||
font-size: 22px;
|
|
||||||
line-height: 39px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
.opacity(0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
|
||||||
Listview Pagination
|
|
||||||
-----------------------------------------------------------*/
|
|
||||||
.lv-pagination {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px 0;
|
|
||||||
border-top: 1px solid #F0F0F0;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
|
||||||
Pager
|
|
||||||
-----------------------------------------------------------*/
|
|
||||||
.pager li > a, .pager li > span {
|
|
||||||
padding: 5px 10px 6px;
|
|
||||||
color: @pagination-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
.popover {
|
.popover {
|
||||||
box-shadow: 0 2px 30px rgba(0, 0, 0, 0.2);
|
box-shadow: fade(@redash-gray, 25%) 0px 0px 15px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-title {
|
.popover-title {
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ div.table-name {
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
border: none;
|
border: none;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
.collapse.in {
|
.collapse.in {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -72,10 +74,11 @@ div.table-name {
|
|||||||
|
|
||||||
.schema-control {
|
.schema-control {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.form-control {
|
.ant-btn {
|
||||||
margin-right: 5px;
|
height: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
.tab-nav {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
overflow: auto;
|
|
||||||
box-shadow: inset 0 -2px 0 0 #eee;
|
|
||||||
|
|
||||||
& > li {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
|
|
||||||
& > a {
|
|
||||||
display: inline-block;
|
|
||||||
color: #7a7a7a;
|
|
||||||
text-transform: uppercase;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
height: 2px;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: @screen-sm-min) {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: @screen-sm-min) {
|
|
||||||
padding: 15px 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
& > a {
|
|
||||||
color: #000;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.tab-nav-right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.tn-justified {
|
|
||||||
& > li {
|
|
||||||
display: table-cell;
|
|
||||||
width: 1%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.tn-icon {
|
|
||||||
& > li {
|
|
||||||
.zmdi {
|
|
||||||
font-size: 22px;
|
|
||||||
line-height: 100%;
|
|
||||||
min-height: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not([data-tab-color]) {
|
|
||||||
& > li > a:after {
|
|
||||||
background: @blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-tab-color="green"] {
|
|
||||||
& > li > a:after {
|
|
||||||
background: @green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-tab-color="red"] {
|
|
||||||
& > li > a:after {
|
|
||||||
background: @red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-tab-color="teal"] {
|
|
||||||
& > li > a:after {
|
|
||||||
background: @teal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-tab-color="amber"] {
|
|
||||||
& > li > a:after {
|
|
||||||
background: @amber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-tab-color="black"] {
|
|
||||||
& > li > a:after {
|
|
||||||
background: @black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-tab-color="cyan"] {
|
|
||||||
& > li > a:after {
|
|
||||||
background: @cyan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content {
|
|
||||||
padding: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rd-tab {
|
|
||||||
.remove {
|
|
||||||
cursor: pointer;
|
|
||||||
color: #A09797;
|
|
||||||
padding: 0 3px 1px 4px;
|
|
||||||
font-size: 11px;
|
|
||||||
&:hover {
|
|
||||||
color: white;
|
|
||||||
background-color: #FF8080;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-nav {
|
|
||||||
> li.rd-tab-btn {
|
|
||||||
float: right;
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -97,3 +97,53 @@
|
|||||||
background-color: #f4f4f4;
|
background-color: #f4f4f4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-data {
|
||||||
|
tbody > tr > td {
|
||||||
|
padding-top: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-favourite, .btn-archive {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-main-title {
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-favourite {
|
||||||
|
color: #d4d4d4;
|
||||||
|
transition: all .25s ease-in-out;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
color: @yellow-darker;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-star {
|
||||||
|
color: @yellow-darker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-archive {
|
||||||
|
color: #d4d4d4;
|
||||||
|
transition: all .25s ease-in-out;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
color: @gray-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-archive {
|
||||||
|
color: @gray-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > thead > tr > th {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-data .label-tag {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 135px;
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
margin-bottom: @grid-gutter-width;
|
margin-bottom: @grid-gutter-width;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: @tile-shadow;
|
border-radius: 3px;
|
||||||
|
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
|
||||||
|
|
||||||
&[class*="bg-"] {
|
&[class*="bg-"] {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -12,6 +13,10 @@
|
|||||||
margin-bottom: @grid-gutter-width/2;
|
margin-bottom: @grid-gutter-width/2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.tiled {
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
|
||||||
|
}
|
||||||
|
|
||||||
.t-header {
|
.t-header {
|
||||||
.th-title {
|
.th-title {
|
||||||
@@ -74,6 +79,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.t-header:not(.th-alt) {
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-bottom: 0;
|
||||||
|
line-height: 2.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tb-padding {
|
.tb-padding {
|
||||||
padding: 20px 23px 30px;
|
padding: 20px 23px 30px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
#toast-container .toast {
|
|
||||||
margin: 0 6px 6px 0;
|
|
||||||
box-shadow: none;
|
|
||||||
color: #ffffff;
|
|
||||||
opacity: 0.75;
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: opacity 0.35s ease-in-out;
|
|
||||||
}
|
|
||||||
#toast-container .toast:hover {
|
|
||||||
box-shadow: none;
|
|
||||||
opacity: 1;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast {
|
|
||||||
background-color: #030303;
|
|
||||||
}
|
|
||||||
.toast-success {
|
|
||||||
background-color: #3BD973;
|
|
||||||
}
|
|
||||||
.toast-error {
|
|
||||||
background-color: #E92828;
|
|
||||||
}
|
|
||||||
.toast-info {
|
|
||||||
background-color: #356AFF;
|
|
||||||
}
|
|
||||||
.toast-warning {
|
|
||||||
background-color: #FB8D3D;
|
|
||||||
}
|
|
||||||
@@ -23,6 +23,8 @@
|
|||||||
@logo-height: @header-height;
|
@logo-height: @header-height;
|
||||||
@boxed-width: 1170px;
|
@boxed-width: 1170px;
|
||||||
@body-bg: #edecec;
|
@body-bg: #edecec;
|
||||||
|
@spacing: 15px;
|
||||||
|
@redash-radius: 3px;
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
@@ -39,6 +41,7 @@
|
|||||||
-----------------------------------------------------------*/
|
-----------------------------------------------------------*/
|
||||||
@font-icon: 'Material-Design-Iconic-Font';
|
@font-icon: 'Material-Design-Iconic-Font';
|
||||||
@font-family-sans-serif: 'Roboto', sans-serif;
|
@font-family-sans-serif: 'Roboto', sans-serif;
|
||||||
|
@redash-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||||
@font-size-base: 13px;
|
@font-size-base: 13px;
|
||||||
|
|
||||||
|
|
||||||
@@ -59,6 +62,7 @@
|
|||||||
@input-border: #e8e8e8;
|
@input-border: #e8e8e8;
|
||||||
@input-border-radius: 0;
|
@input-border-radius: 0;
|
||||||
@input-border-radius-large: 0px;
|
@input-border-radius-large: 0px;
|
||||||
|
@redash-input-radius: 2px;
|
||||||
@input-height-large: 40px;
|
@input-height-large: 40px;
|
||||||
@input-height-base: 35px;
|
@input-height-base: 35px;
|
||||||
@input-height-small: 30px;
|
@input-height-small: 30px;
|
||||||
@@ -94,6 +98,11 @@
|
|||||||
@gray-light: #828282;
|
@gray-light: #828282;
|
||||||
@ace: #f8f8f8;
|
@ace: #f8f8f8;
|
||||||
|
|
||||||
|
@redash-gray: rgba(102, 136, 153, 1);
|
||||||
|
@redash-orange: rgba(255, 120, 100, 1);
|
||||||
|
@redash-black: rgba(0, 0, 0, 1);
|
||||||
|
@redash-yellow: rgba(252, 252, 161, 0.75);
|
||||||
|
|
||||||
/** Form States **/
|
/** Form States **/
|
||||||
@state-success-text: @green;
|
@state-success-text: @green;
|
||||||
@state-info-text: @blue;
|
@state-info-text: @blue;
|
||||||
@@ -192,7 +201,6 @@
|
|||||||
@pagination-hover-color: #333;
|
@pagination-hover-color: #333;
|
||||||
@pagination-hover-bg: #d7d7d7;
|
@pagination-hover-bg: #d7d7d7;
|
||||||
@pagination-hover-border: @pagination-border;
|
@pagination-hover-border: @pagination-border;
|
||||||
@pager-border-radius: 5px;
|
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
.bootgrid-table {
|
|
||||||
margin: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bootgrid-footer .infoBar,
|
|
||||||
.bootgrid-header .actionBar {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bootgrid-footer .search,
|
|
||||||
.bootgrid-header .search {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bootgrid-header {
|
|
||||||
margin: 0;
|
|
||||||
padding: 25px;
|
|
||||||
|
|
||||||
.search {
|
|
||||||
border: 1px solid @input-border;
|
|
||||||
|
|
||||||
.form-control, .input-group-addon {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-addon {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #333;
|
|
||||||
padding-right: 0 !important;
|
|
||||||
min-width: 26px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: @screen-xs-min) {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: @screen-xs-min) {
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 90px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
box-shadow: none;
|
|
||||||
|
|
||||||
.btn-group {
|
|
||||||
.btn {
|
|
||||||
height: 37px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 0;
|
|
||||||
border: 1px solid @input-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
@media (min-width: @screen-sm-min) {
|
|
||||||
left: 0;
|
|
||||||
margin-top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-item {
|
|
||||||
padding: 5px 10px;
|
|
||||||
|
|
||||||
.input-helper {
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.caret {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zmdi {
|
|
||||||
line-height: 100%;
|
|
||||||
font-size: 18px;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: @screen-xs-min) {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bootgrid-footer {
|
|
||||||
border-top: 1px solid @table-border-color;
|
|
||||||
margin-top: 0;
|
|
||||||
|
|
||||||
.col-sm-6 {
|
|
||||||
padding: 25px;
|
|
||||||
|
|
||||||
@media (max-width: @screen-sm-min) {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.infoBar {
|
|
||||||
@media (max-width: @screen-sm-min) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.infos {
|
|
||||||
border: 1px solid #EEE;
|
|
||||||
display: inline-block;
|
|
||||||
float: right;
|
|
||||||
padding: 7px 30px;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-cell .checkbox {
|
|
||||||
margin: 0px 0 0 -19px;
|
|
||||||
top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
.bootstrap-datetimepicker-widget {
|
|
||||||
padding: 0 !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
width: auto !important;
|
|
||||||
|
|
||||||
&:after, &:before { display: none !important; }
|
|
||||||
|
|
||||||
table td {
|
|
||||||
text-shadow: none;
|
|
||||||
|
|
||||||
span {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
&:hover { background: transparent; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.glyphicon { font-family: @font-icon; font-size: 18px; }
|
|
||||||
.glyphicon-chevron-left:before { content: "\f2ff"; }
|
|
||||||
.glyphicon-chevron-right:before { content: "\f301"; }
|
|
||||||
.glyphicon-time:before { content: "\f337"; }
|
|
||||||
.glyphicon-calendar:before { content: "\f32e"; }
|
|
||||||
.glyphicon-chevron-up:before { content: "\f1e5"; }
|
|
||||||
.glyphicon-chevron-down:before { content: "\f1e4"; }
|
|
||||||
|
|
||||||
[data-action="togglePicker"] span {
|
|
||||||
font-size: 25px;
|
|
||||||
color: #ccc;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a[data-action] {
|
|
||||||
color: @blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.timepicker-picker {
|
|
||||||
.btn { box-shadow: none !important; }
|
|
||||||
|
|
||||||
table {
|
|
||||||
tbody tr + tr:not(:last-child) {
|
|
||||||
background: @blue;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
td {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
background: #fff;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.datepicker {
|
|
||||||
&.top {
|
|
||||||
.transform-origin(0 100%) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
thead {
|
|
||||||
tr {
|
|
||||||
th {
|
|
||||||
border-radius: 0;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.glyphicon {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
|
||||||
line-height: 29px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .glyphicon {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
th {
|
|
||||||
background: @blue;
|
|
||||||
padding: 20px 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: @blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.picker-switch {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
th {
|
|
||||||
&:first-child { padding-left: 20px; }
|
|
||||||
&:last-child { padding-right: 20px; }
|
|
||||||
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:only-child) {
|
|
||||||
background: darken(@blue, 3%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody {
|
|
||||||
tr {
|
|
||||||
&:last-child {
|
|
||||||
td {
|
|
||||||
padding-bottom: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
&:first-child {
|
|
||||||
padding-left: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-right: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
|
|
||||||
&.day {
|
|
||||||
width: 35px;
|
|
||||||
height: 35px;
|
|
||||||
line-height: 20px;
|
|
||||||
color: #333;
|
|
||||||
position: relative;
|
|
||||||
padding: 0;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: "";
|
|
||||||
width: 35px;
|
|
||||||
height: 35px;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-bottom: -33px;
|
|
||||||
display: inline-block;
|
|
||||||
background: transparent;
|
|
||||||
position: static;
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.old, &.new {
|
|
||||||
color: #CDCDCD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.today):not(.active) {
|
|
||||||
&:hover:before {
|
|
||||||
background: #F0F0F0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.today {
|
|
||||||
color: #333;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
background-color: #E2E2E2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
background-color: @blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.datepicker-months .month,
|
|
||||||
.datepicker-years .year,
|
|
||||||
.timepicker-minutes .minute,
|
|
||||||
.timepicker-hours .hour {
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
&:not(.active) {
|
|
||||||
&:hover {
|
|
||||||
background: #F0F0F0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background: @blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.timepicker-minutes .minute,
|
|
||||||
.timepicker-hours .hour {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
.bootstrap-select {
|
|
||||||
|
|
||||||
.bs-searchbox {
|
|
||||||
padding: 0 18px;
|
|
||||||
margin: 5px 0 10px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
position: absolute;
|
|
||||||
left: 14px;
|
|
||||||
top: 2px;
|
|
||||||
width: 30px;
|
|
||||||
height: 100%;
|
|
||||||
content: "\f1c3";
|
|
||||||
font-family: @font-icon;
|
|
||||||
font-size: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
padding-left: 25px;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.btn-group {
|
|
||||||
.dropdown-menu li a.opt {
|
|
||||||
padding-left: 17px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-mark {
|
|
||||||
margin-top: -5px !important;
|
|
||||||
font-size: 19px;
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 11px;
|
|
||||||
right: 15px;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: "\f26b";
|
|
||||||
font-family: @font-icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected {
|
|
||||||
.check-mark {
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notify {
|
|
||||||
bottom: 0 !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
width: 100% !important;
|
|
||||||
border: 0 !important;
|
|
||||||
background: @red !important;
|
|
||||||
color: #fff !important;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not([class*=col-]):not([class*=form-control]):not(.input-group-btn) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-default {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 0;
|
|
||||||
border: 1px solid @input-border;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
.chosen-container {
|
|
||||||
.chosen-drop {
|
|
||||||
border-color: @input-border;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chosen-results {
|
|
||||||
margin: 10px 0 0 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding: 10px 17px;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&.highlighted {
|
|
||||||
background: @dropdown-link-hover-bg;
|
|
||||||
color: @dropdown-link-hover-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.result-selected {
|
|
||||||
background: @lightblue;
|
|
||||||
color: @white;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: "\f26b";
|
|
||||||
font-family: @font-icon;
|
|
||||||
position: absolute;
|
|
||||||
right: 15px;
|
|
||||||
top: 10px;
|
|
||||||
font-size: 19px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.group-result {
|
|
||||||
&:not(:first-child) {
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
color: #B2B2B2;
|
|
||||||
font-weight: normal;
|
|
||||||
padding: 16px 15px 6px;
|
|
||||||
margin-top: 9px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chosen-container-single {
|
|
||||||
.chosen-single {
|
|
||||||
border-radius: 0;
|
|
||||||
height: 35px;
|
|
||||||
padding: 7px 12px 6px;
|
|
||||||
line-height: 1.42857143;
|
|
||||||
border-color: @input-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chosen-search {
|
|
||||||
padding: 5px 12px;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: "\f1c3";
|
|
||||||
font-family: @font-icon;
|
|
||||||
position: absolute;
|
|
||||||
left: 25px;
|
|
||||||
top: 9px;
|
|
||||||
font-size: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text] {
|
|
||||||
border-color: @input-border;
|
|
||||||
padding: 8px 10px 8px 35px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chosen-container-multi {
|
|
||||||
.chosen-choices {
|
|
||||||
padding: 0 4px;
|
|
||||||
border-color: @input-border;
|
|
||||||
|
|
||||||
li {
|
|
||||||
&.search-choice {
|
|
||||||
border-radius: 0;
|
|
||||||
margin: 4px 4px 0 0;
|
|
||||||
background: @blue;
|
|
||||||
border-color: @blue;
|
|
||||||
color: #fff;
|
|
||||||
padding: 5px 23px 5px 8px;
|
|
||||||
|
|
||||||
.search-choice-close {
|
|
||||||
&:before {
|
|
||||||
display: inline-block;
|
|
||||||
font-family: @font-icon;
|
|
||||||
content: "\f135";
|
|
||||||
position: relative;
|
|
||||||
top: 1px;
|
|
||||||
color: #fff;
|
|
||||||
z-index: 2;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.search-field {
|
|
||||||
input[type=text] {
|
|
||||||
padding: 0 8px;
|
|
||||||
height: 31px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
.cp-container {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
& > .input-group {
|
|
||||||
|
|
||||||
input.cp-value {
|
|
||||||
color: #000 !important;
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
padding: 20px;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i.cp-value {
|
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
|
||||||
border-radius: 50%;
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
.fileinput {
|
|
||||||
position: relative;
|
|
||||||
padding-right: 35px;
|
|
||||||
|
|
||||||
.close {
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
float: none;
|
|
||||||
opacity: 1;
|
|
||||||
font-weight: 500;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
width: 19px;
|
|
||||||
text-align: center;
|
|
||||||
height: 19px;
|
|
||||||
line-height: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #eee;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-file {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-addon {
|
|
||||||
padding: 0 10px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fileinput-preview {
|
|
||||||
width: 200px;
|
|
||||||
height: 150px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-top: -13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
/** CALENDAR WIDGET **/
|
|
||||||
#calendar-widget {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
box-shadow: 0 1px 1px rgba(0,0,0,.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
#fc-actions {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 23px;
|
|
||||||
right: 22px;
|
|
||||||
|
|
||||||
& > li > a {
|
|
||||||
font-size: 20px;
|
|
||||||
color: #fff;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
|
||||||
line-height: 30px;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
& > li.open > a,
|
|
||||||
& > li > a:hover {
|
|
||||||
background: darken(@teal, 7%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc {
|
|
||||||
background-color: #fff;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
td {
|
|
||||||
border-color: @table-border-color !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
background: darken(@teal, 7%);
|
|
||||||
color: #fff;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 6px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr {
|
|
||||||
& > td:first-child {
|
|
||||||
border-left-width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-widget-header {
|
|
||||||
border-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-day-number {
|
|
||||||
color: #CCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-event-container {
|
|
||||||
padding: 0 2px 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-toolbar {
|
|
||||||
background: @teal;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding: 25px 7px 25px;
|
|
||||||
position: relative;
|
|
||||||
.user-select(none);
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: "";
|
|
||||||
bottom: -30px;
|
|
||||||
height: 30px;
|
|
||||||
width: 100%;
|
|
||||||
background: darken(@teal, 7%);
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
margin-top: 7px;
|
|
||||||
font-size: 19px;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-button {
|
|
||||||
border: 0;
|
|
||||||
background: 0 0;
|
|
||||||
padding: 0;
|
|
||||||
outline: none !important;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
& > span {
|
|
||||||
position: relative;
|
|
||||||
font-family: @font-icon;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #FFF;
|
|
||||||
line-height: 100%;
|
|
||||||
width: 31px;
|
|
||||||
height: 31px;
|
|
||||||
border-radius: 50%;
|
|
||||||
padding-top: 6px;
|
|
||||||
display: block;
|
|
||||||
margin-top: 2px;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.ui-icon-circle-triangle-w:before {
|
|
||||||
content: "\f2fa";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.ui-icon-circle-triangle-e:before {
|
|
||||||
content: "\f2fb";
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: darken(@teal, 7%);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fc-event {
|
|
||||||
padding: 0;
|
|
||||||
font-size: 11px;
|
|
||||||
border-radius: 0;
|
|
||||||
border: 0;
|
|
||||||
|
|
||||||
.fc-title {
|
|
||||||
padding: 3px 5px 2px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-time {
|
|
||||||
float: left;
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
padding: 2px 6px;
|
|
||||||
margin: 0 0 0 -1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-view, .fc-view > table {
|
|
||||||
border: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-content-skeleton {
|
|
||||||
table {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#calendar {
|
|
||||||
.fc-day-number {
|
|
||||||
@media screen and (min-width: @screen-sm-max) {
|
|
||||||
font-size: 25px;
|
|
||||||
letter-spacing: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
padding-left: 10px !important;
|
|
||||||
text-align: left !important;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Even Tag Color */
|
|
||||||
.event-tag {
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
& > span {
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
margin-right: 3px;
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.opacity(0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
&:before {
|
|
||||||
font-family: @font-icon;
|
|
||||||
content: "\f26b";
|
|
||||||
position: absolute;
|
|
||||||
text-align: center;
|
|
||||||
top: 3px;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 17px;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Height Fix */
|
|
||||||
.fc-day-grid-container {
|
|
||||||
height: auto !important;
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
.lg-outer .lg-item {
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lg-slide {
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
-webkit-animation-fill-mode: both;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
height: 50px;
|
|
||||||
width: 50px;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 2px solid @white;
|
|
||||||
-webkit-animation: ball-scale-ripple 1s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8);
|
|
||||||
animation: ball-scale-ripple 1s 0s infinite cubic-bezier(0.21, 0.53, 0.56, 0.8);
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -25px;
|
|
||||||
top: 50%;
|
|
||||||
margin-top: -25px;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
em {
|
|
||||||
font-style: normal;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
color: #D2D2D2;
|
|
||||||
font-weight: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: #6B6B6B;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes ball-scale-ripple {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(0.1);
|
|
||||||
transform: scale(0.1);
|
|
||||||
opacity: 1; }
|
|
||||||
|
|
||||||
70% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
opacity: 0.7; }
|
|
||||||
|
|
||||||
100% {
|
|
||||||
opacity: 0.0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ball-scale-ripple {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(0.1);
|
|
||||||
transform: scale(0.1);
|
|
||||||
opacity: 1; }
|
|
||||||
|
|
||||||
70% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
opacity: 0.7; }
|
|
||||||
|
|
||||||
100% {
|
|
||||||
opacity: 0.0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
.mCSB_container,
|
|
||||||
.mCustomScrollBox {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mCSB_scrollTools {
|
|
||||||
width: 12px;
|
|
||||||
|
|
||||||
.mCSB_draggerRail,
|
|
||||||
.mCSB_dragger .mCSB_dragger_bar {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mCSB_draggerRail {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mCS-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar {
|
|
||||||
background: rgba(0,0,0,0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mCSB_inside > .mCSB_container {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
.noUi-target {
|
|
||||||
border-radius: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noUi-background {
|
|
||||||
background: #d4d4d4;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noUi-horizontal {
|
|
||||||
height: 3px;
|
|
||||||
|
|
||||||
.noUi-handle {
|
|
||||||
top: -8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.noUi-vertical {
|
|
||||||
width: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noUi-horizontal,
|
|
||||||
.noUi-vertical {
|
|
||||||
.noUi-handle {
|
|
||||||
width: 19px;
|
|
||||||
height: 19px;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 100%;
|
|
||||||
box-shadow: none;
|
|
||||||
.transition(box-shadow);
|
|
||||||
.transition-duration(200ms);
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:before,
|
|
||||||
&:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: #ccc !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-tooltip {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 32px;
|
|
||||||
height: 35px;
|
|
||||||
border-radius: 2px;
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 33px;
|
|
||||||
width: 50px;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -25px;
|
|
||||||
padding: 0 10px;
|
|
||||||
.transition(all);
|
|
||||||
.transition-duration(200ms);
|
|
||||||
.backface-visibility(hidden);
|
|
||||||
.opacity(0);
|
|
||||||
.scale(0);
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 15px 10px 0 10px;
|
|
||||||
position: absolute;
|
|
||||||
bottom: -8px;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -9px;
|
|
||||||
content: "";
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.noUi-active {
|
|
||||||
box-shadow: 0 0 0 13px rgba(0,0,0,0.1);
|
|
||||||
|
|
||||||
.is-tooltip {
|
|
||||||
.scale(1);
|
|
||||||
bottom: 40px;
|
|
||||||
.opacity(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-slider,
|
|
||||||
.input-slider-range,
|
|
||||||
.input-slider-values {
|
|
||||||
&:not([data-is-color]) {
|
|
||||||
.noUi-handle,
|
|
||||||
.noUi-connect, {
|
|
||||||
background: @teal !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-tooltip {
|
|
||||||
background: @teal;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
border-color: @teal transparent transparent transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=red] {
|
|
||||||
.is-color-handle(@red);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=blue] {
|
|
||||||
.is-color-handle(@blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=cyan] {
|
|
||||||
.is-color-handle(@cyan);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=amber] {
|
|
||||||
.is-color-handle(@amber);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=green] {
|
|
||||||
.is-color-handle(@green);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-slider {
|
|
||||||
.noUi-origin {
|
|
||||||
background: #d4d4d4;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not([data-is-color]) {
|
|
||||||
.noUi-base {
|
|
||||||
background: @teal !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=red] {
|
|
||||||
.is-color-base(@red);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=blue] {
|
|
||||||
.is-color-base(@blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=cyan] {
|
|
||||||
.is-color-base(@cyan);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=amber] {
|
|
||||||
.is-color-base(@amber);
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-is-color=green] {
|
|
||||||
.is-color-base(@green);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-color-handle(@color) {
|
|
||||||
.noUi-handle,
|
|
||||||
.noUi-connect {
|
|
||||||
background: @color !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-color-base(@color) {
|
|
||||||
.noUi-base {
|
|
||||||
background: @color !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
.note-editor,
|
|
||||||
.note-popover {
|
|
||||||
.note-toolbar,
|
|
||||||
.popover-content {
|
|
||||||
background: #fff;
|
|
||||||
border-color: #e4e4e4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px 0 15px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
& > .btn-group {
|
|
||||||
display: inline-block;
|
|
||||||
float: none;
|
|
||||||
box-shadow: none;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
margin: 0 1px;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .active {
|
|
||||||
background: @cyan;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 2px !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-palette-title {
|
|
||||||
margin: 0 !important;
|
|
||||||
padding: 10px 0 !important;
|
|
||||||
font-size: 13px !important;
|
|
||||||
text-align: center !important;
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-color-reset {
|
|
||||||
padding: 0 0 10px !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
background: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-color {
|
|
||||||
.dropdown-menu {
|
|
||||||
min-width: 335px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-statusbar {
|
|
||||||
.note-resizebar {
|
|
||||||
border-color: #E8E8E8;
|
|
||||||
|
|
||||||
.note-icon-bar {
|
|
||||||
border-color: #BCBCBC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa {
|
|
||||||
font-style: normal;
|
|
||||||
font-size: 20px;
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
font-family: @font-icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-magic:before {
|
|
||||||
content: "\f16a";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-bold:before {
|
|
||||||
content: "\f23d";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-italic:before {
|
|
||||||
content: "\f245";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-underline:before {
|
|
||||||
content: "\f24f";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-font:before {
|
|
||||||
content: "\f242";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-list-ul:before {
|
|
||||||
content: "\f247";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-list-ol:before {
|
|
||||||
content: "\f248";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-align-left:before {
|
|
||||||
content: "\f23b";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-align-right:before {
|
|
||||||
content: "\f23c";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-align-center:before {
|
|
||||||
content: "\f239";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-align-justify:before {
|
|
||||||
content: "\f23a";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-indent:before {
|
|
||||||
content: "\f244";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-outdent:before {
|
|
||||||
content: "\f243";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-text-height:before {
|
|
||||||
content: "\f246";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-table:before {
|
|
||||||
content: "\f320";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-link:before {
|
|
||||||
content: "\f18e";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-picture-o:before {
|
|
||||||
content: "\f17f";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-minus:before {
|
|
||||||
content: "\f22f";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-arrows-alt:before {
|
|
||||||
content: "\f16d";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-code:before {
|
|
||||||
content: "\f13a";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-question:before {
|
|
||||||
content: "\f1f5";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-eraser:before {
|
|
||||||
content: "\f23f";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-square:before {
|
|
||||||
content: "\f279";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-circle-o:before {
|
|
||||||
content: "\f26c";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fa-times:before {
|
|
||||||
content: "\f136";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-air-popover {
|
|
||||||
.arrow {
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-editor {
|
|
||||||
border: 1px solid #e4e4e4;
|
|
||||||
|
|
||||||
.note-editable {
|
|
||||||
padding: 20px 23px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
.sweet-alert {
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 10px 30px;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lead {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 6px 12px;
|
|
||||||
font-size: 13px;
|
|
||||||
margin: 20px 2px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
.twitter-typeahead {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.tt-menu {
|
|
||||||
min-width: 200px;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tt-suggestion:hover,
|
|
||||||
.tt-cursor {
|
|
||||||
background-color: rgba(0,0,0,0.075);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tt-suggestion {
|
|
||||||
padding: 8px 17px;
|
|
||||||
color: #333;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tt-hint {
|
|
||||||
color: #818181 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/* ui-select adjustments for SuperFlat */
|
|
||||||
.clearable button {
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Same definition as .form-control */
|
|
||||||
.ui-select-toggle.btn-default {
|
|
||||||
height: 35px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.42857143;
|
|
||||||
color: #9E9E9E;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #e8e8e8;
|
|
||||||
border-radius: 2px;
|
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
|
||||||
-webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
|
||||||
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
|
||||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
|
||||||
|
|
||||||
&:hover, &:active, &.active, &:focus, &.focus {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-default-focus {
|
|
||||||
outline: none;
|
|
||||||
outline-offset: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
cohort-renderer {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cornelius-container {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
.cornelius-table {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
tr, th, td {
|
|
||||||
border-color: @table-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cornelius-time, .cornelius-label, .cornelius-people {
|
|
||||||
background-color: fade(@redash-gray, 3%) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
counter-renderer {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
padding: 15px 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
counter {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 80px;
|
|
||||||
line-height: normal;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
value,
|
|
||||||
counter-target {
|
|
||||||
font-size: 1em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
counter-name {
|
|
||||||
font-size: 0.5em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.positive value {
|
|
||||||
color: #5cb85c;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.negative value {
|
|
||||||
color: #d9534f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
counter-target {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
counter-name {
|
|
||||||
font-size: 0.5em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
.col-table .missing-value {
|
|
||||||
color: #b94a48;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-table .super-small-input {
|
|
||||||
padding-left: 3px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-table .ui-select-toggle, .col-table .ui-select-search {
|
|
||||||
padding: 2px;
|
|
||||||
padding-left: 5px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -6,32 +6,9 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.map-custom-control.leaflet-bar {
|
|
||||||
background: #fff;
|
.leaflet-popup-content img {
|
||||||
padding: 10px;
|
max-width: 100%;
|
||||||
margin: 10px;
|
height: auto;
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
&.top-left {
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.top-right {
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom-left {
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom-right {
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
visualization-renderer {
|
.visualization-renderer {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
.pagination,
|
.pagination,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
.pivot-table-renderer > table, grid-renderer > div, visualization-renderer > div {
|
.pivot-table-visualization-container > table,
|
||||||
|
.visualization-renderer > .visualization-renderer-wrapper {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +1,57 @@
|
|||||||
/** LESS Plugins **/
|
/** LESS Plugins **/
|
||||||
@import 'inc/less-plugins/for';
|
@import "inc/less-plugins/for";
|
||||||
|
|
||||||
/** Load Main Bootstrap LESS files **/
|
/** Load Main Bootstrap LESS files **/
|
||||||
@import '~bootstrap/less/bootstrap';
|
@import "~bootstrap/less/bootstrap";
|
||||||
|
|
||||||
/** Load Vendors Dependencies **/
|
/** Load Vendors Dependencies **/
|
||||||
@import '~font-awesome/less/font-awesome';
|
@import "~font-awesome/less/font-awesome";
|
||||||
@import '~ui-select/dist/select.css';
|
@import "~material-design-iconic-font/dist/css/material-design-iconic-font.css";
|
||||||
@import '~angular-resizable/src/angular-resizable.css';
|
@import "~pace-progress/themes/blue/pace-theme-minimal.css";
|
||||||
@import '~material-design-iconic-font/dist/css/material-design-iconic-font.css';
|
|
||||||
@import '~pace-progress/themes/blue/pace-theme-minimal.css';
|
|
||||||
|
|
||||||
@import 'inc/angular';
|
@import "inc/variables";
|
||||||
@import 'inc/variables';
|
@import "inc/mixins";
|
||||||
@import 'inc/mixins';
|
@import "inc/font";
|
||||||
@import 'inc/font';
|
@import "inc/print";
|
||||||
@import 'inc/print';
|
|
||||||
|
|
||||||
@import 'inc/bootstrap-overrides';
|
@import "inc/bootstrap-overrides";
|
||||||
@import 'inc/base';
|
@import "inc/base";
|
||||||
@import 'inc/generics';
|
@import "inc/generics";
|
||||||
@import 'inc/form';
|
@import "inc/form";
|
||||||
@import 'inc/button';
|
@import "inc/button";
|
||||||
@import 'inc/list';
|
@import "inc/list";
|
||||||
@import 'inc/header';
|
@import "inc/header";
|
||||||
@import 'inc/tile';
|
@import "inc/tile";
|
||||||
@import 'inc/label';
|
@import "inc/label";
|
||||||
@import 'inc/dropdown';
|
@import "inc/dropdown";
|
||||||
@import 'inc/list-group';
|
@import "inc/list-group";
|
||||||
@import 'inc/misc';
|
@import "inc/misc";
|
||||||
@import 'inc/progress-bar';
|
@import "inc/progress-bar";
|
||||||
@import 'inc/widgets';
|
@import "inc/widgets";
|
||||||
@import 'inc/table';
|
@import "inc/table";
|
||||||
@import 'inc/pagination';
|
@import "inc/alert";
|
||||||
@import 'inc/alert';
|
@import "inc/media";
|
||||||
@import 'inc/media';
|
@import "inc/modal";
|
||||||
@import 'inc/modal';
|
@import "inc/panel";
|
||||||
@import 'inc/tab';
|
@import "inc/tooltips";
|
||||||
@import 'inc/panel';
|
@import "inc/popover";
|
||||||
@import 'inc/tooltips';
|
@import "inc/breadcrumb";
|
||||||
@import 'inc/popover';
|
@import "inc/jumbotron";
|
||||||
@import 'inc/breadcrumb';
|
@import "inc/profile";
|
||||||
@import 'inc/jumbotron';
|
@import "inc/404";
|
||||||
@import 'inc/profile';
|
@import "inc/ie-warning";
|
||||||
@import 'inc/404';
|
@import "inc/edit-in-place";
|
||||||
@import 'inc/ie-warning';
|
@import "inc/flex";
|
||||||
@import 'inc/navbar';
|
@import "inc/ace-editor";
|
||||||
@import 'inc/edit-in-place';
|
@import "inc/schema-browser";
|
||||||
@import 'inc/growl';
|
@import "inc/visualizations/box";
|
||||||
@import 'inc/flex';
|
@import "inc/visualizations/pivot-table";
|
||||||
@import 'inc/ace-editor';
|
@import "inc/visualizations/map";
|
||||||
@import 'inc/overlay';
|
@import "inc/visualizations/misc";
|
||||||
@import 'inc/schema-browser';
|
|
||||||
@import 'inc/toast';
|
|
||||||
@import 'inc/visualizations/box';
|
|
||||||
@import 'inc/visualizations/counter-render';
|
|
||||||
@import 'inc/visualizations/sankey';
|
|
||||||
@import 'inc/visualizations/pivot-table';
|
|
||||||
@import 'inc/visualizations/map';
|
|
||||||
@import 'inc/visualizations/chart';
|
|
||||||
@import 'inc/visualizations/sunburst';
|
|
||||||
@import 'inc/visualizations/cohort';
|
|
||||||
@import 'inc/visualizations/misc';
|
|
||||||
|
|
||||||
/** VENDOR OVERRIDES **/
|
|
||||||
@import 'inc/vendor-overrides/bootstrap-select';
|
|
||||||
@import 'inc/vendor-overrides/bootstrap-datetimepicker';
|
|
||||||
@import 'inc/vendor-overrides/typeahead';
|
|
||||||
@import 'inc/vendor-overrides/sweetalert';
|
|
||||||
@import 'inc/vendor-overrides/ui-select';
|
|
||||||
|
|
||||||
/** REDASH STYLING **/
|
/** REDASH STYLING **/
|
||||||
@import 'redash/redash-newstyle';
|
@import "redash/redash-table";
|
||||||
@import 'redash/redash-table';
|
@import "redash/query";
|
||||||
@import 'redash/query';
|
@import "redash/tags-control";
|
||||||
@import 'redash/tags-control';
|
@import "redash/css-logo";
|
||||||
|
@import "redash/loading-indicator";
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
88
client/app/assets/less/redash/css-logo.less
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// based on https://github.com/outbrain/tech-companies-logos-in-css/pull/28
|
||||||
|
|
||||||
|
@primary: #ff7964;
|
||||||
|
@shadow: #ef6c58;
|
||||||
|
@bar: white;
|
||||||
|
|
||||||
|
#css-logo {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
#circle {
|
||||||
|
width: 79px;
|
||||||
|
height: 79px;
|
||||||
|
background-color: @shadow;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
width: 79px;
|
||||||
|
height: 73px;
|
||||||
|
background-color: @primary;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#bars {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 24px;
|
||||||
|
right: 0;
|
||||||
|
height: 33px;
|
||||||
|
display: flex;
|
||||||
|
padding: 0 22px 0;
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
background: @bar;
|
||||||
|
box-shadow: 0px 2px 0 0 @shadow;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 1px;
|
||||||
|
align-self: flex-end;
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 2px;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
height: 32%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
height: 71%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(3) {
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(4) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#point,
|
||||||
|
#point > div {
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border: 17px solid @shadow;
|
||||||
|
border-right-color: transparent !important;
|
||||||
|
border-bottom-color: transparent !important;
|
||||||
|
bottom: 0;
|
||||||
|
left: 48px;
|
||||||
|
transform: scaleX(0.87);
|
||||||
|
transform-origin: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#point > div {
|
||||||
|
bottom: -12px;
|
||||||
|
border-color: @primary;
|
||||||
|
transform: scaleX(1.04);
|
||||||
|
left: -17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
51
client/app/assets/less/redash/loading-indicator.less
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
.loading-indicator {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin: -50px 0 0 -50px; // center
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
transition-duration: 150ms;
|
||||||
|
transition-timing-function: linear;
|
||||||
|
transition-property: opacity, transform;
|
||||||
|
|
||||||
|
#css-logo {
|
||||||
|
animation: hover 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shadow {
|
||||||
|
width: 33px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: black;
|
||||||
|
opacity: 0.25;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: 34px;
|
||||||
|
top: 115px;
|
||||||
|
animation: shadow 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes hover {
|
||||||
|
50% {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes shadow {
|
||||||
|
50% {
|
||||||
|
transform: scaleX(0.9);
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide indicator when application has content
|
||||||
|
#application-root:not(:empty) ~ .loading-indicator {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9);
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
* {
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,10 @@ body.fixed-layout {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
app-view {
|
#application-root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
padding-bottom: 0;
|
||||||
|
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@@ -16,24 +17,15 @@ body.fixed-layout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-nav .tab-new-vis {
|
|
||||||
margin: 0 5px;
|
|
||||||
|
|
||||||
> a {
|
|
||||||
color: @headings-color;
|
|
||||||
margin-top: 8px;
|
|
||||||
padding: 7px;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-controller {
|
.bottom-controller {
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
button, div, span {
|
button,
|
||||||
|
div,
|
||||||
|
span {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +35,7 @@ body.fixed-layout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: '';
|
content: "";
|
||||||
height: 50px;
|
height: 50px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -64,89 +56,9 @@ body.fixed-layout {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-metadata__bottom {
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-controller, .bottom-controller-container {
|
|
||||||
.query-metadata__property {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Editor
|
// Editor
|
||||||
edit-in-place p, span.editable {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
edit-in-place p.editable:hover {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor__control {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
.dropdown-toggle {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-container {
|
.filter-container {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 5px;
|
||||||
}
|
|
||||||
|
|
||||||
.ace_editor.ace_autocomplete .ace_completion-highlight {
|
|
||||||
text-shadow: none !important;
|
|
||||||
background: #ffff005e;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-metadata {
|
|
||||||
background: #fff;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding: 3px 0;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr {
|
|
||||||
td:first-of-type {
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
&:last-of-type {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-metadata__property {
|
|
||||||
width: 60px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
._query-metadata__time {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor__control {
|
|
||||||
.form-control {
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.schema-container {
|
.schema-container {
|
||||||
@@ -158,7 +70,7 @@ edit-in-place p.editable:hover {
|
|||||||
|
|
||||||
.editor__left {
|
.editor__left {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
width: calc(~'25% - 10px');
|
width: calc(~"25% - 10px");
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
@@ -190,7 +102,6 @@ edit-in-place p.editable:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.embed__vis {
|
.embed__vis {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.query__vis {
|
.query__vis {
|
||||||
@@ -208,18 +119,18 @@ edit-in-place p.editable:hover {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.visualization-renderer {
|
|
||||||
.pagination,
|
|
||||||
.ant-pagination {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.embed__vis {
|
.embed__vis {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.embed-heading {
|
||||||
|
h3 {
|
||||||
|
line-height: 1.75;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.widget-wrapper {
|
.widget-wrapper {
|
||||||
.body-container {
|
.body-container {
|
||||||
.filters-wrapper {
|
.filters-wrapper {
|
||||||
@@ -245,6 +156,17 @@ edit-in-place p.editable:hover {
|
|||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tags-control a {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.tags-control a {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.label-tag {
|
a.label-tag {
|
||||||
@@ -275,7 +197,22 @@ a.label-tag {
|
|||||||
display: flex;
|
display: flex;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
|
||||||
.tile, .tiled {
|
.resizable-component.react-resizable {
|
||||||
|
.react-resizable-handle-horizontal {
|
||||||
|
border-right: 1px solid #efefef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-resizable-handle-vertical {
|
||||||
|
border-bottom: 1px solid #efefef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-metadata.query-metadata-horizontal {
|
||||||
|
border-bottom: 1px solid #efefef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile,
|
||||||
|
.tiled {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
padding: 15px 0 !important;
|
padding: 15px 0 !important;
|
||||||
}
|
}
|
||||||
@@ -290,22 +227,26 @@ a.label-tag {
|
|||||||
min-width: 10px;
|
min-width: 10px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
.schema-container {
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor__left__data-source,
|
.editor__left__data-source,
|
||||||
.schema-control,
|
.schema-control,
|
||||||
.query-metadata--history,
|
|
||||||
.editor {
|
.editor {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-metadata {
|
.editor__left__schema,
|
||||||
border-top: 1px solid #efefef;
|
.editor__left__data-source {
|
||||||
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-metadata, .editor__left__schema, .editor__left__data-source {
|
.editor__left__data-source {
|
||||||
padding: 15px;
|
.ant-select {
|
||||||
|
.ant-select-selection-selected-value {
|
||||||
|
img,
|
||||||
|
span {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor__left__schema {
|
.editor__left__schema {
|
||||||
@@ -316,7 +257,7 @@ a.label-tag {
|
|||||||
padding-top: 0 !important;
|
padding-top: 0 !important;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
schema-browser {
|
.schema-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 15px;
|
left: 15px;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -325,10 +266,6 @@ a.label-tag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.content {
|
.content {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -339,21 +276,13 @@ a.label-tag {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
.editor {
|
.pivot-table-visualization-container > table,
|
||||||
border-bottom: 1px solid #efefef;
|
.visualization-renderer > .visualization-renderer-wrapper {
|
||||||
}
|
|
||||||
|
|
||||||
.pivot-table-renderer > table, grid-renderer > div, visualization-renderer > div {
|
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-nav {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.row {
|
.row {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
z-index: 9;
|
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
|
|
||||||
&.resizable {
|
&.resizable {
|
||||||
@@ -366,6 +295,10 @@ a.label-tag {
|
|||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
align-content: space-around;
|
align-content: space-around;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
min-height: 10px;
|
||||||
|
max-height: 70vh;
|
||||||
|
flex: 0 0 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
@@ -392,7 +325,10 @@ a.label-tag {
|
|||||||
transition: none !important;
|
transition: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.rg-right, .rg-left, .rg-top, .rg-bottom {
|
.rg-right,
|
||||||
|
.rg-left,
|
||||||
|
.rg-top,
|
||||||
|
.rg-bottom {
|
||||||
display: block;
|
display: block;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
@@ -407,20 +343,22 @@ a.label-tag {
|
|||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.rg-right, .rg-left {
|
.rg-right,
|
||||||
|
.rg-left {
|
||||||
span {
|
span {
|
||||||
border-width: 0 1px;
|
border-width: 0 1px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin: -10px 0 0 @spacing/4;
|
margin: -10px 0 0 @spacing / 4;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 3px;
|
width: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.rg-top, .rg-bottom {
|
.rg-top,
|
||||||
|
.rg-bottom {
|
||||||
span {
|
span {
|
||||||
border-width: 1px 0;
|
border-width: 1px 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin: @spacing/4 0 0 -10px;
|
margin: @spacing / 4 0 0 -10px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
}
|
}
|
||||||
@@ -430,7 +368,7 @@ a.label-tag {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
margin-top: -@spacing/2;
|
margin-top: -@spacing / 2;
|
||||||
}
|
}
|
||||||
.rg-right {
|
.rg-right {
|
||||||
cursor: col-resize;
|
cursor: col-resize;
|
||||||
@@ -469,11 +407,6 @@ a.label-tag {
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-fullscreen .query-metadata__mobile {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Visualization editor
|
// Visualization editor
|
||||||
.modal-xl .modal-content {
|
.modal-xl .modal-content {
|
||||||
border: none;
|
border: none;
|
||||||
@@ -510,32 +443,6 @@ nav .rg-bottom {
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-metadata--description {
|
|
||||||
max-height: 125px;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.edit-in-place.active {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-in-place .rd-form-control {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-metadata--refresh {
|
|
||||||
height: 50px;
|
|
||||||
border: none !important;
|
|
||||||
|
|
||||||
.query-metadata__property {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-tags {
|
.query-tags {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@@ -544,7 +451,6 @@ nav .rg-bottom {
|
|||||||
|
|
||||||
.query-tags__mobile {
|
.query-tags__mobile {
|
||||||
display: none;
|
display: none;
|
||||||
margin: -5px 0 0 0;
|
|
||||||
padding: 0 0 0 23px;
|
padding: 0 0 0 23px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,6 +468,14 @@ nav .rg-bottom {
|
|||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 880px) {
|
||||||
|
.query-fullscreen {
|
||||||
|
.query-metadata.query-metadata-horizontal {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Smaller screens
|
// Smaller screens
|
||||||
|
|
||||||
@media (max-width: 880px) {
|
@media (max-width: 880px) {
|
||||||
@@ -589,10 +503,6 @@ nav .rg-bottom {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-nav .tab-new-vis {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-fullscreen {
|
.query-fullscreen {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -605,25 +515,6 @@ nav .rg-bottom {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-metadata__mobile {
|
|
||||||
border-bottom: 1px solid #efefef;
|
|
||||||
min-height: 0 !important;
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding: 10px 15px;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.profile__image_thumb {
|
|
||||||
margin: 0 5px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-metadata__property {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
|
|
||||||
@@ -669,15 +560,21 @@ nav .rg-bottom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.editor__left__schema, .editor__left__data-source {
|
.editor__left__schema,
|
||||||
|
.editor__left__data-source {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-container {
|
.filter-container {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.btn-edit-visualisation {
|
// Responsive fixes
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.query-page-wrapper {
|
||||||
|
h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,939 +0,0 @@
|
|||||||
@import (reference, less) '~bootstrap/less/labels.less';
|
|
||||||
|
|
||||||
// Variables
|
|
||||||
@redash-gray: rgba(102, 136, 153, 1);
|
|
||||||
@redash-orange: rgba(255, 120, 100, 1);
|
|
||||||
@redash-black: rgba(0, 0, 0, 1);
|
|
||||||
@redash-yellow: rgba(252, 252, 161, 0.75);
|
|
||||||
@redash-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
||||||
|
|
||||||
@spacing: 15px;
|
|
||||||
|
|
||||||
//Default spacing (between tiles)
|
|
||||||
@redash-space: 10px;
|
|
||||||
|
|
||||||
@redash-radius: 3px;
|
|
||||||
@redash-input-radius: 2px;
|
|
||||||
|
|
||||||
// General
|
|
||||||
body {
|
|
||||||
padding-top: 0;
|
|
||||||
background: #F6F8F9;
|
|
||||||
font-family: @redash-font;
|
|
||||||
|
|
||||||
&.headless {
|
|
||||||
padding-top: 10px;
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.word-wrap-break {
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clearboth {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.callout {
|
|
||||||
padding: 20px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
border-left-width: 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.callout-warning {
|
|
||||||
border-left-color: #aa6708;
|
|
||||||
}
|
|
||||||
|
|
||||||
.callout-info {
|
|
||||||
border-left-color: #1b809e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixed width layout for specific pages
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
|
||||||
.container {
|
|
||||||
width: 750px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
|
||||||
.container {
|
|
||||||
width: 970px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
|
||||||
.container {
|
|
||||||
width: 1170px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.creation-container {
|
|
||||||
h5 {
|
|
||||||
color: #a7a7a7;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-widget-container {
|
|
||||||
background: #fff;
|
|
||||||
border-radius: @redash-radius;
|
|
||||||
padding: 15px;
|
|
||||||
position: fixed;
|
|
||||||
left: 15px;
|
|
||||||
bottom: 20px;
|
|
||||||
width: calc(~'100% - 30px');
|
|
||||||
z-index: 99;
|
|
||||||
box-shadow: fade(@redash-gray, 50%) 0px 7px 29px -3px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 2.1;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
.zmdi {
|
|
||||||
margin: 0;
|
|
||||||
margin-right: 5px;
|
|
||||||
font-size: 24px;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
.ace-tm .ace_gutter {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ace_editor {
|
|
||||||
border: 1px solid fade(@redash-gray, 15%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ace-tm .ace_gutter-active-line {
|
|
||||||
background-color: fade(@redash-gray, 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ace-tm .ace_marker-layer .ace_active-line {
|
|
||||||
background: fade(@redash-gray, 9%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus {
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: inset 3px 0px 0px @brand-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-data {
|
|
||||||
tbody > tr > td {
|
|
||||||
padding-top: 5px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-favourite, .btn-archive {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-main-title {
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.7 !important;
|
|
||||||
a {
|
|
||||||
//font-size: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-favourite, .btn-archive {
|
|
||||||
color: #d4d4d4;
|
|
||||||
transition: all .25s ease-in-out;
|
|
||||||
|
|
||||||
&:hover, &:focus {
|
|
||||||
color: @yellow-darker;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-star {
|
|
||||||
color: @yellow-darker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-archive {
|
|
||||||
color: #d4d4d4;
|
|
||||||
transition: all .25s ease-in-out;
|
|
||||||
|
|
||||||
&:hover, &:focus {
|
|
||||||
color: @gray-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-archive {
|
|
||||||
color: @gray-light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-header--new .btn-favourite, .page-header--new .btn-archive {
|
|
||||||
font-size: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin-right: 5px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
margin-top: 3px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
favorites-control {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
favorites-control {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 5px !important;
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar li a .btn-favourite .fa, .navbar li a .btn-archive .fa {
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.float-right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.visual-card {
|
|
||||||
background: #FFFFFF;
|
|
||||||
border: 1px solid fade(@redash-gray, 15%);
|
|
||||||
border-radius: 3px;
|
|
||||||
margin: 5px;
|
|
||||||
width: 212px;
|
|
||||||
padding: 15px 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: none;
|
|
||||||
transition: transform 0.12s ease-out;
|
|
||||||
transition-duration: 0.3s;
|
|
||||||
transition-property: box-shadow;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: rgba(102, 136, 153, 0.15) 0px 4px 9px -3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 64px !important;
|
|
||||||
height: 64px !important;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #323232;
|
|
||||||
margin: 0 !important;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
|
||||||
.visual-card {
|
|
||||||
width: 217px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 755px) {
|
|
||||||
.visual-card {
|
|
||||||
width: 47%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 515px) {
|
|
||||||
.visual-card {
|
|
||||||
width: 47%;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 408px) {
|
|
||||||
.visual-card {
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px;
|
|
||||||
margin: 5px 0;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.t-header:not(.th-alt) {
|
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin-bottom: 0;
|
|
||||||
line-height: 2.2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-header-wrapper, .page-header--new {
|
|
||||||
h3 {
|
|
||||||
margin: 0.2em 0;
|
|
||||||
line-height: 1.3;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dynamic-table__pagination {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rg-top span, .rg-bottom span {
|
|
||||||
height: 3px;
|
|
||||||
border-color: #b1c1ce; // TODO: variable
|
|
||||||
}
|
|
||||||
|
|
||||||
.rg-bottom {
|
|
||||||
bottom: 15px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
margin: 1.5px 0 0 -10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover {
|
|
||||||
box-shadow: fade(@redash-gray, 25%) 0px 0px 15px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile__bottom-control a {
|
|
||||||
color: fade(@redash-black, 65%);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: fade(@redash-black, 95%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination {
|
|
||||||
.disabled a {
|
|
||||||
background-color: fade(@redash-gray, 14%);
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
a {
|
|
||||||
background-color: fade(@redash-gray, 15%);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: fade(@redash-gray, 25%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-default {
|
|
||||||
background-color: fade(@redash-gray, 15%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-transparent {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-default:hover, .btn-default:focus, .btn-default.focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default {
|
|
||||||
background-color: fade(@redash-gray, 25%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus {
|
|
||||||
color: #333;
|
|
||||||
background-color: fade(@redash-gray, 45%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 3px 6px 4px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-default {
|
|
||||||
background: fade(@redash-gray, 85%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-tag-unpublished {
|
|
||||||
background: fade(@redash-gray, 85%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-tag-archived {
|
|
||||||
.label-warning();
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-tag {
|
|
||||||
background: fade(@redash-gray, 10%);
|
|
||||||
color: fade(@redash-gray, 75%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-tag-unpublished,
|
|
||||||
.label-tag-archived,
|
|
||||||
.label-tag {
|
|
||||||
margin-right: 3px;
|
|
||||||
display: inline;
|
|
||||||
margin-top: 2px;
|
|
||||||
max-width: 24ch;
|
|
||||||
.text-overflow();
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-nav > li > a {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table > thead > tr > th {
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-header {
|
|
||||||
position: -webkit-sticky; // required for Safari
|
|
||||||
position: sticky;
|
|
||||||
background: #f6f7f9;
|
|
||||||
z-index: 99;
|
|
||||||
width: 100%;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard__control {
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editing-mode {
|
|
||||||
a.query-link {
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
|
|
||||||
.th-title {
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-header {
|
|
||||||
position: -webkit-sticky; // required for Safari
|
|
||||||
position: sticky;
|
|
||||||
background: #f6f7f9;
|
|
||||||
z-index: 99;
|
|
||||||
width: 100%;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-wrapper {
|
|
||||||
.parameter-container {
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-ace {
|
|
||||||
background-color: fade(@redash-gray, 12%) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tiled {
|
|
||||||
border-radius: 3px;
|
|
||||||
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile {
|
|
||||||
border-radius: 3px;
|
|
||||||
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
|
|
||||||
|
|
||||||
.widget-menu-regular, .btn__refresh {
|
|
||||||
opacity: 0 !important;
|
|
||||||
transition: opacity 0.35s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.t-header {
|
|
||||||
.th-title {
|
|
||||||
a {
|
|
||||||
color: fade(@redash-black, 80%);
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.query--description {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-style: italic;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.t-header.widget {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.widget-menu-regular, .btn__refresh {
|
|
||||||
opacity: 1 !important;
|
|
||||||
transition: opacity 0.35s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile__bottom-control {
|
|
||||||
padding: 10px 15px;
|
|
||||||
line-height: 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.embed-heading {
|
|
||||||
h3 {
|
|
||||||
line-height: 1.75;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-container {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigation
|
|
||||||
.caret--nav {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.caret--nav:after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
right: 5px;
|
|
||||||
top: 9px;
|
|
||||||
width: 13px;
|
|
||||||
height: 13px;
|
|
||||||
display: block;
|
|
||||||
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='11px' height='6px' viewBox='0 0 11 6' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3C!-- Generator: Sketch 42 %2836781%29 - http://www.bohemiancoding.com/sketch --%3E%3Ctitle%3EShape%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cdefs%3E%3C/defs%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M5.296,4.288 L9.382,0.2 C9.66086822,-0.0716916976 10.1065187,-0.068122925 10.381,0.208 C10.661,0.488 10.661,0.932 10.388,1.206 L5.792,5.803 C5.6602899,5.93388911 5.48167943,6.00662966 5.296,6.005 C5.10997499,6.00689786 4.93095449,5.93413702 4.799,5.803 L0.204,1.207 C0.072163111,1.07394937 -0.00121750401,0.893846387 9.62313189e-05,0.706545264 C0.00140996665,0.519244142 0.0773097323,0.340188219 0.211,0.209 C0.485365732,-0.0664648737 0.930253538,-0.0700311086 1.209,0.201 L5.296,4.288 L5.296,4.288 Z' id='Shape' fill='%23000000'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
|
||||||
background-size: 100% 100%;
|
|
||||||
transition: transform .2s cubic-bezier(.75,0,.25,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .caret--nav:after {
|
|
||||||
top: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown--profile .caret--nav:after {
|
|
||||||
right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn--create {
|
|
||||||
padding-right: 20px;
|
|
||||||
|
|
||||||
.caret--nav:after {
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='11px' height='6px' viewBox='0 0 11 6' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3C!-- Generator: Sketch 42 %2836781%29 - http://www.bohemiancoding.com/sketch --%3E%3Ctitle%3EShape%3C/title%3E%3Cdesc%3ECreated with Sketch.%3C/desc%3E%3Cdefs%3E%3C/defs%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cpath d='M5.29592111,4.28945339 L9.38192111,0.201453387 C9.66078932,-0.0702383105 10.1064398,-0.0666695379 10.3809211,0.209453387 C10.6609211,0.489453387 10.6609211,0.933453387 10.3879211,1.20745339 L5.79192111,5.80445339 C5.66021101,5.9353425 5.48160054,6.00808305 5.29592111,6.00645339 C5.1098961,6.00835125 4.9308756,5.9355904 4.79892111,5.80445339 L0.203921109,1.20845339 C0.0720842204,1.07540275 -0.00129639464,0.895299774 1.73406884e-05,0.707998651 C0.00133107602,0.520697529 0.0772308417,0.341641606 0.210921109,0.210453387 C0.485286842,-0.0650114866 0.930174648,-0.0685777215 1.20892111,0.202453387 L5.29592111,4.28945339 L5.29592111,4.28945339 Z' id='Shape' fill='%23FCFCFC'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown.open .caret--nav:after {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsing, .collapse.in {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
min-height: initial;
|
|
||||||
height: 50px;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-top: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background: #fff;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
.btn-group.open .dropdown-toggle {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-group .btn:active {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-link-ANGULAR_REMOVE_ME {
|
|
||||||
line-height: 18px;
|
|
||||||
padding: 10px 15px;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
padding-top: 16px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-link-ANGULAR_REMOVE_ME,
|
|
||||||
.navbar-default .navbar-nav > li > a {
|
|
||||||
color: #000;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
&:active, &:hover, &:focus {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .btn__new button {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .navbar-nav > li > a:hover {
|
|
||||||
//background-color: fade(@redash-gray, 10%);
|
|
||||||
//text-decoration: underline;
|
|
||||||
//border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus {
|
|
||||||
background-color: fade(@redash-gray, 15%);
|
|
||||||
color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus {
|
|
||||||
background-color: fade(@redash-gray, 15%);
|
|
||||||
color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-nav {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile__image--navbar {
|
|
||||||
border-radius: 100%;
|
|
||||||
margin-right: 3px;
|
|
||||||
margin-top: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile__image--settings {
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile__image_thumb {
|
|
||||||
border-radius: 100%;
|
|
||||||
margin-right: 3px;
|
|
||||||
margin-top: -2px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user_list__user--invitation-pending {
|
|
||||||
color: fade(@alert-danger-bg, 75%);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn__new {
|
|
||||||
margin-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-btn {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -25px !important; // center
|
|
||||||
display: block;
|
|
||||||
zoom: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.va-top {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
box-shadow: fade(@redash-gray, 15%) 0px 4px 9px -3px;
|
|
||||||
|
|
||||||
.navbar-collapse {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.dropdown--profile {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
line-height: 2.35;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-inverse {
|
|
||||||
background-color: @redash-gray;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-search {
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags-list {
|
|
||||||
|
|
||||||
.badge-light {
|
|
||||||
background: fade(@redash-gray, 10%);
|
|
||||||
color: fade(@redash-gray, 75%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu--profile {
|
|
||||||
li {
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .collapse.in {
|
|
||||||
background: #fff;
|
|
||||||
position: relative;
|
|
||||||
z-index: 999;
|
|
||||||
padding: 0 10px 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
.pagination > li > a, .pagination > li > span {
|
|
||||||
border-radius: 3px !important;
|
|
||||||
width: 33px;
|
|
||||||
height: 33px;
|
|
||||||
line-height: 31px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error state
|
|
||||||
.error-state {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 25vh;
|
|
||||||
padding: 35px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 21px;
|
|
||||||
|
|
||||||
.error-state__icon {
|
|
||||||
.zmdi {
|
|
||||||
font-size: 64px;
|
|
||||||
color: @redash-gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
margin-top: 10vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forms
|
|
||||||
.form-control {
|
|
||||||
border-radius: @redash-input-radius;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
border-color: @blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: @blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plotly
|
|
||||||
text.slicetext {
|
|
||||||
text-shadow: 1px 1px 5px #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Responsive fixes
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
.text-center-xs {
|
|
||||||
text-align: center !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-page-wrapper {
|
|
||||||
h3 {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
favorites-control {
|
|
||||||
margin-top: -3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
left: 2%;
|
|
||||||
margin-left: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Fix navbar collapse
|
|
||||||
.navbar .collapse.in {
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
.dropdown-menu--profile {
|
|
||||||
li {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown--profile {
|
|
||||||
.caret--nav:after {
|
|
||||||
right: initial !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown--profile__username {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav__main li a {
|
|
||||||
padding: 10px 15px;
|
|
||||||
display: block;
|
|
||||||
text-align: left;
|
|
||||||
float: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-form {
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-right {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
@media (max-width: 880px) {
|
|
||||||
.navbar-link-ANGULAR_REMOVE_ME,
|
|
||||||
.navbar-default .navbar-nav > li > a,
|
|
||||||
.navbar-form {
|
|
||||||
padding-left: 10px !important;
|
|
||||||
padding-right: 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.navbar-brand {
|
|
||||||
margin-left: -15px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 810px) {
|
|
||||||
.menu-search {
|
|
||||||
width: 175px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.navbar-brand {
|
|
||||||
margin-left: 13px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1084px) {
|
|
||||||
.dropdown--profile__username {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Cross-browser fixes
|
|
||||||
|
|
||||||
// Firefox
|
|
||||||
@-moz-document url-prefix() {
|
|
||||||
.caret--nav::after {
|
|
||||||
height: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .caret--nav::after {
|
|
||||||
top: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .btn--create .caret--nav::after {
|
|
||||||
top: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IE10+
|
|
||||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
|
||||||
.caret--nav::after {
|
|
||||||
height: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .caret--nav::after {
|
|
||||||
top: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .btn--create .caret--nav::after {
|
|
||||||
top: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-select-choices-row.disabled > span {
|
|
||||||
background-color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group-item.inactive,
|
|
||||||
.ui-select-choices-row.disabled {
|
|
||||||
background-color: #eee !important;
|
|
||||||
border-color: transparent;
|
|
||||||
opacity: 0.5;
|
|
||||||
box-shadow: none;
|
|
||||||
color: #333;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-option-divider {
|
|
||||||
margin: 10px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-data .label-tag {
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 135px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
@@ -14,9 +14,3 @@
|
|||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is for using .inline-tags-control in Angular which renders
|
|
||||||
// a little differently than React (e.g. in Alert.html)
|
|
||||||
.inline-tags-control .tags-control {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
@import 'inc/ie-warning';
|
@import 'inc/ie-warning';
|
||||||
@import 'inc/flex';
|
@import 'inc/flex';
|
||||||
|
|
||||||
@import 'redash/redash-newstyle';
|
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
22
client/app/components/AceEditorInput.jsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React, { forwardRef } from "react";
|
||||||
|
import AceEditor from "react-ace";
|
||||||
|
|
||||||
|
import "./AceEditorInput.less";
|
||||||
|
|
||||||
|
function AceEditorInput(props, ref) {
|
||||||
|
return (
|
||||||
|
<div className="ace-editor-input">
|
||||||
|
<AceEditor
|
||||||
|
ref={ref}
|
||||||
|
mode="sql"
|
||||||
|
theme="textmate"
|
||||||
|
height="100px"
|
||||||
|
editorProps={{ $blockScrolling: Infinity }}
|
||||||
|
showPrintMargin={false}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default forwardRef(AceEditorInput);
|
||||||
11
client/app/components/AceEditorInput.less
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.ace-editor-input {
|
||||||
|
// hide ghost cursor when not focused
|
||||||
|
.ace_hidden-cursors {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow Ant Form feedback icon to hover scrollbar
|
||||||
|
.ace_scrollbar {
|
||||||
|
z-index: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import React, { useState, useMemo, useCallback, useEffect } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { isEmpty, template } from "lodash";
|
||||||
|
|
||||||
|
import Dropdown from "antd/lib/dropdown";
|
||||||
|
import Icon from "antd/lib/icon";
|
||||||
|
import Menu from "antd/lib/menu";
|
||||||
|
|
||||||
|
import HelpTrigger from "@/components/HelpTrigger";
|
||||||
|
|
||||||
|
export default function FavoritesDropdown({ fetch, urlTemplate }) {
|
||||||
|
const [items, setItems] = useState();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const noItems = isEmpty(items);
|
||||||
|
const urlCompiled = useMemo(() => template(urlTemplate), [urlTemplate]);
|
||||||
|
|
||||||
|
const fetchItems = useCallback(
|
||||||
|
(showLoadingState = true) => {
|
||||||
|
setLoading(showLoadingState);
|
||||||
|
fetch()
|
||||||
|
.then(({ results }) => {
|
||||||
|
setItems(results);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[fetch]
|
||||||
|
);
|
||||||
|
|
||||||
|
// fetch items on init
|
||||||
|
useEffect(() => {
|
||||||
|
fetchItems(false);
|
||||||
|
}, [fetchItems]);
|
||||||
|
|
||||||
|
// fetch items on click
|
||||||
|
const onVisibleChange = visible => visible && fetchItems();
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu className="favorites-dropdown">
|
||||||
|
{noItems ? (
|
||||||
|
<Menu.Item>
|
||||||
|
<span className="btn-favourite m-r-5">
|
||||||
|
<i className="fa fa-star" />
|
||||||
|
</span>
|
||||||
|
No favorites selected yet <HelpTrigger type="FAVORITES" />
|
||||||
|
</Menu.Item>
|
||||||
|
) : (
|
||||||
|
items.map(item => (
|
||||||
|
<Menu.Item key={item.id}>
|
||||||
|
<a href={urlCompiled(item)}>
|
||||||
|
<span className="btn-favourite m-r-5">
|
||||||
|
<i className="fa fa-star" />
|
||||||
|
</span>
|
||||||
|
{item.name}
|
||||||
|
</a>
|
||||||
|
</Menu.Item>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
disabled={loading}
|
||||||
|
trigger={["click"]}
|
||||||
|
placement="bottomLeft"
|
||||||
|
onVisibleChange={onVisibleChange}
|
||||||
|
overlay={menu}>
|
||||||
|
{loading ? <Icon type="loading" spin /> : <Icon type="down" />}
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FavoritesDropdown.propTypes = {
|
||||||
|
fetch: PropTypes.func.isRequired,
|
||||||
|
urlTemplate: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
/* eslint-disable no-template-curly-in-string */
|
||||||
|
|
||||||
|
import React, { useCallback, useRef } from "react";
|
||||||
|
|
||||||
|
import Dropdown from "antd/lib/dropdown";
|
||||||
|
import Button from "antd/lib/button";
|
||||||
|
import Icon from "antd/lib/icon";
|
||||||
|
import Menu from "antd/lib/menu";
|
||||||
|
import Input from "antd/lib/input";
|
||||||
|
import Tooltip from "antd/lib/tooltip";
|
||||||
|
|
||||||
|
import HelpTrigger from "@/components/HelpTrigger";
|
||||||
|
import CreateDashboardDialog from "@/components/dashboards/CreateDashboardDialog";
|
||||||
|
import navigateTo from "@/components/ApplicationArea/navigateTo";
|
||||||
|
|
||||||
|
import { currentUser, Auth, clientConfig } from "@/services/auth";
|
||||||
|
import { Dashboard } from "@/services/dashboard";
|
||||||
|
import { Query } from "@/services/query";
|
||||||
|
import frontendVersion from "@/version.json";
|
||||||
|
import logoUrl from "@/assets/images/redash_icon_small.png";
|
||||||
|
|
||||||
|
import FavoritesDropdown from "./FavoritesDropdown";
|
||||||
|
import "./index.less";
|
||||||
|
|
||||||
|
function onSearch(q) {
|
||||||
|
navigateTo(`queries?q=${encodeURIComponent(q)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DesktopNavbar() {
|
||||||
|
const showCreateDashboardDialog = useCallback(() => {
|
||||||
|
CreateDashboardDialog.showModal().result.catch(() => {}); // ignore dismiss
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app-header" data-platform="desktop">
|
||||||
|
<div>
|
||||||
|
<Menu mode="horizontal" selectable={false}>
|
||||||
|
{currentUser.hasPermission("list_dashboards") && (
|
||||||
|
<Menu.Item key="dashboards" className="dropdown-menu-item">
|
||||||
|
<Button href="dashboards">Dashboards</Button>
|
||||||
|
<FavoritesDropdown fetch={Dashboard.favorites} urlTemplate="dashboard/${slug}" />
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("view_query") && (
|
||||||
|
<Menu.Item key="queries" className="dropdown-menu-item">
|
||||||
|
<Button href="queries">Queries</Button>
|
||||||
|
<FavoritesDropdown fetch={Query.favorites} urlTemplate="queries/${id}" />
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("list_alerts") && (
|
||||||
|
<Menu.Item key="alerts">
|
||||||
|
<Button href="alerts">Alerts</Button>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
</Menu>
|
||||||
|
{currentUser.canCreate() && (
|
||||||
|
<Dropdown
|
||||||
|
trigger={["click"]}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
{currentUser.hasPermission("create_query") && (
|
||||||
|
<Menu.Item key="new-query">
|
||||||
|
<a href="queries/new">New Query</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("create_dashboard") && (
|
||||||
|
<Menu.Item key="new-dashboard">
|
||||||
|
<a onMouseUp={showCreateDashboardDialog}>New Dashboard</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("list_alerts") && (
|
||||||
|
<Menu.Item key="new-alert">
|
||||||
|
<a href="alerts/new">New Alert</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
</Menu>
|
||||||
|
}>
|
||||||
|
<Button type="primary" data-test="CreateButton">
|
||||||
|
Create <Icon type="down" />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="header-logo">
|
||||||
|
<a href="./">
|
||||||
|
<img src={logoUrl} alt="Redash" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Input.Search
|
||||||
|
className="searchbar"
|
||||||
|
placeholder="Search queries..."
|
||||||
|
data-test="AppHeaderSearch"
|
||||||
|
onSearch={onSearch}
|
||||||
|
/>
|
||||||
|
<Menu mode="horizontal" selectable={false}>
|
||||||
|
<Menu.Item key="help">
|
||||||
|
<HelpTrigger type="HOME" className="menu-item-button" />
|
||||||
|
</Menu.Item>
|
||||||
|
{currentUser.isAdmin && (
|
||||||
|
<Menu.Item key="settings">
|
||||||
|
<Tooltip title="Settings">
|
||||||
|
<Button href="data_sources" className="menu-item-button">
|
||||||
|
<i className="fa fa-sliders" />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
<Menu.Item key="profile">
|
||||||
|
<Dropdown
|
||||||
|
overlayStyle={{ minWidth: 200 }}
|
||||||
|
placement="bottomRight"
|
||||||
|
trigger={["click"]}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item key="profile">
|
||||||
|
<a href="users/me">Edit Profile</a>
|
||||||
|
</Menu.Item>
|
||||||
|
{currentUser.hasPermission("super_admin") && <Menu.Divider />}
|
||||||
|
{currentUser.isAdmin && (
|
||||||
|
<Menu.Item key="datasources">
|
||||||
|
<a href="data_sources">Data Sources</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("list_users") && (
|
||||||
|
<Menu.Item key="groups">
|
||||||
|
<a href="groups">Groups</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("list_users") && (
|
||||||
|
<Menu.Item key="users">
|
||||||
|
<a href="users">Users</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("create_query") && (
|
||||||
|
<Menu.Item key="snippets">
|
||||||
|
<a href="query_snippets">Query Snippets</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.isAdmin && (
|
||||||
|
<Menu.Item key="destinations">
|
||||||
|
<a href="destinations">Alert Destinations</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("super_admin") && <Menu.Divider />}
|
||||||
|
{currentUser.hasPermission("super_admin") && (
|
||||||
|
<Menu.Item key="status">
|
||||||
|
<a href="admin/status">System Status</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item key="logout" onClick={() => Auth.logout()}>
|
||||||
|
Log out
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item key="version" disabled>
|
||||||
|
Version: {clientConfig.version}
|
||||||
|
{frontendVersion !== clientConfig.version && ` (${frontendVersion.substring(0, 8)})`}
|
||||||
|
{clientConfig.newVersionAvailable && currentUser.hasPermission("super_admin") && (
|
||||||
|
<Tooltip title="Update Available" placement="rightTop">
|
||||||
|
{" "}
|
||||||
|
{/* eslint-disable react/jsx-no-target-blank */}
|
||||||
|
<a
|
||||||
|
href="https://version.redash.io/"
|
||||||
|
className="update-available"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener">
|
||||||
|
<i className="fa fa-arrow-circle-down" />
|
||||||
|
</a>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}>
|
||||||
|
<Button data-test="ProfileDropdown" className="profile-dropdown">
|
||||||
|
<img src={currentUser.profile_image_url} alt={currentUser.name} />
|
||||||
|
<span>{currentUser.name}</span>
|
||||||
|
<Icon type="down" />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MobileNavbar() {
|
||||||
|
const ref = useRef();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app-header" data-platform="mobile" ref={ref}>
|
||||||
|
<div className="header-logo">
|
||||||
|
<a href="./">
|
||||||
|
<img src={logoUrl} alt="Redash" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Dropdown
|
||||||
|
overlayStyle={{ minWidth: 200 }}
|
||||||
|
trigger={["click"]}
|
||||||
|
getPopupContainer={() => ref.current} // so the overlay menu stays with the fixed header when page scrolls
|
||||||
|
overlay={
|
||||||
|
<Menu mode="vertical" selectable={false}>
|
||||||
|
{currentUser.hasPermission("list_dashboards") && (
|
||||||
|
<Menu.Item key="dashboards">
|
||||||
|
<a href="dashboards">Dashboards</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("view_query") && (
|
||||||
|
<Menu.Item key="queries">
|
||||||
|
<a href="queries">Queries</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("list_alerts") && (
|
||||||
|
<Menu.Item key="alerts">
|
||||||
|
<a href="alerts">Alerts</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
<Menu.Item key="profile">
|
||||||
|
<a href="users/me">Edit Profile</a>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
{currentUser.isAdmin && (
|
||||||
|
<Menu.Item key="settings">
|
||||||
|
<a href="data_sources">Settings</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("super_admin") && (
|
||||||
|
<Menu.Item key="status">
|
||||||
|
<a href="admin/status">System Status</a>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{currentUser.hasPermission("super_admin") && <Menu.Divider />}
|
||||||
|
<Menu.Item key="help">
|
||||||
|
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||||
|
<a href="https://redash.io/help" target="_blank" rel="noopener">
|
||||||
|
Help
|
||||||
|
</a>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="logout" onClick={() => Auth.logout()}>
|
||||||
|
Log out
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}>
|
||||||
|
<Button>
|
||||||
|
<Icon type="menu" />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ApplicationHeader() {
|
||||||
|
return (
|
||||||
|
<nav className="app-header-wrapper">
|
||||||
|
<DesktopNavbar />
|
||||||
|
<MobileNavbar />
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
@mobileBreakpoint: ~"(max-width: 767px)";
|
||||||
|
|
||||||
|
nav .app-header {
|
||||||
|
height: 49px;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 4px 9px -3px rgba(102, 136, 153, 0.15);
|
||||||
|
|
||||||
|
.darker {
|
||||||
|
color: #333 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #2196f3 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-platform="mobile"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-button {
|
||||||
|
padding: 0 15px;
|
||||||
|
font-size: 18px;
|
||||||
|
.darker();
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-root {
|
||||||
|
margin: 0 10px;
|
||||||
|
line-height: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn {
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-platform="desktop"] .ant-btn:not(.ant-btn-primary) {
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
background-color: transparent; //so it doesn't interfere with click animation of adjacent buttons
|
||||||
|
.darker();
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-item {
|
||||||
|
padding: 0;
|
||||||
|
height: 52px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.anticon-down {
|
||||||
|
font-size: 13px !important;
|
||||||
|
transform: none;
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transition: transform 0.2s cubic-bezier(0.75, 0, 0.25, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-dropdown-open .anticon-down svg,
|
||||||
|
.anticon-down.ant-dropdown-open svg {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu-item {
|
||||||
|
.ant-btn {
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-left: 5px;
|
||||||
|
margin-right: 30px;
|
||||||
|
margin-left: 10px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a trick to get the dropdown menu to be placed at the bottom left
|
||||||
|
// of the menu item and not the dropdown trigger
|
||||||
|
.ant-dropdown-trigger {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 0;
|
||||||
|
left: 10px;
|
||||||
|
bottom: 5px;
|
||||||
|
text-align: right;
|
||||||
|
padding-top: 14px;
|
||||||
|
padding-right: 10px;
|
||||||
|
margin-right: 0;
|
||||||
|
user-select: none; // or else double clicking it causes the header logo to get selected
|
||||||
|
.darker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-logo img {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchbar {
|
||||||
|
width: 185px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-dropdown {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
max-width: 130px; // arbitrary, prevents layout mess up if username long
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.ant-btn,
|
||||||
|
.menu-item-button {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-root {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-dropdown {
|
||||||
|
span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.searchbar {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media @mobileBreakpoint {
|
||||||
|
&[data-platform="desktop"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-platform="mobile"] {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 15px;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media @mobileBreakpoint {
|
||||||
|
.app-header-wrapper {
|
||||||
|
margin-top: 59px !important; // compensate for app header fixed position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-available {
|
||||||
|
display: inline !important;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: #52c41a;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-dropdown-menu-item .help-trigger {
|
||||||
|
display: inline;
|
||||||
|
color: #2196f3;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-dropdown-menu.favorites-dropdown {
|
||||||
|
margin-left: -10px;
|
||||||
|
}
|
||||||
59
client/app/components/ApplicationArea/ErrorMessage.jsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { isObject, get } from "lodash";
|
||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
function getErrorMessageByStatus(status, defaultMessage) {
|
||||||
|
switch (status) {
|
||||||
|
case 404:
|
||||||
|
return "It seems like the page you're looking for cannot be found.";
|
||||||
|
case 401:
|
||||||
|
case 403:
|
||||||
|
return "It seems like you don’t have permission to see this page.";
|
||||||
|
default:
|
||||||
|
return defaultMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getErrorMessage(error) {
|
||||||
|
const message = "It seems like we encountered an error. Try refreshing this page or contact your administrator.";
|
||||||
|
if (isObject(error)) {
|
||||||
|
// HTTP errors
|
||||||
|
if (error.isAxiosError && isObject(error.response)) {
|
||||||
|
return getErrorMessageByStatus(error.response.status, get(error, "response.data.message", message));
|
||||||
|
}
|
||||||
|
// Router errors
|
||||||
|
if (error.status) {
|
||||||
|
return getErrorMessageByStatus(error.status, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ErrorMessage({ error }) {
|
||||||
|
if (!error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed-container" data-test="ErrorMessage">
|
||||||
|
<div className="container">
|
||||||
|
<div className="col-md-8 col-md-push-2">
|
||||||
|
<div className="error-state bg-white tiled">
|
||||||
|
<div className="error-state__icon">
|
||||||
|
<i className="zmdi zmdi-alert-circle-o" />
|
||||||
|
</div>
|
||||||
|
<div className="error-state__details">
|
||||||
|
<h4>{getErrorMessage(error)}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorMessage.propTypes = {
|
||||||
|
error: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
151
client/app/components/ApplicationArea/Router.jsx
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import { isFunction, map, fromPairs, extend, startsWith, trimStart, trimEnd } from "lodash";
|
||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import UniversalRouter from "universal-router";
|
||||||
|
import ErrorBoundary from "@/components/ErrorBoundary";
|
||||||
|
import location from "@/services/location";
|
||||||
|
import url from "@/services/url";
|
||||||
|
|
||||||
|
import ErrorMessage from "./ErrorMessage";
|
||||||
|
|
||||||
|
function generateRouteKey() {
|
||||||
|
return Math.random()
|
||||||
|
.toString(32)
|
||||||
|
.substr(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stripBase(href) {
|
||||||
|
// Resolve provided link and '' (root) relative to document's base.
|
||||||
|
// If provided href is not related to current document (does not
|
||||||
|
// start with resolved root) - return false. Otherwise
|
||||||
|
// strip root and return relative url.
|
||||||
|
|
||||||
|
const baseHref = trimEnd(url.normalize(""), "/") + "/";
|
||||||
|
href = url.normalize(href);
|
||||||
|
|
||||||
|
if (startsWith(href, baseHref)) {
|
||||||
|
return "/" + trimStart(href.substr(baseHref.length), "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRouteDependencies(route) {
|
||||||
|
return Promise.all(
|
||||||
|
map(route.resolve, (value, key) => {
|
||||||
|
value = isFunction(value) ? value(route.routeParams, route, location) : value;
|
||||||
|
return Promise.resolve(value).then(result => [key, result]);
|
||||||
|
})
|
||||||
|
).then(results => {
|
||||||
|
route.routeParams = extend(route.routeParams, fromPairs(results));
|
||||||
|
return route;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Router({ routes, onRouteChange }) {
|
||||||
|
const [currentRoute, setCurrentRoute] = useState(null);
|
||||||
|
|
||||||
|
const currentPathRef = useRef(null);
|
||||||
|
const errorHandlerRef = useRef();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let isAbandoned = false;
|
||||||
|
|
||||||
|
const router = new UniversalRouter(routes, {
|
||||||
|
resolveRoute({ route }, routeParams) {
|
||||||
|
if (isFunction(route.render)) {
|
||||||
|
return { ...route, routeParams };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function resolve(action) {
|
||||||
|
if (!isAbandoned) {
|
||||||
|
if (errorHandlerRef.current) {
|
||||||
|
errorHandlerRef.current.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathname = stripBase(location.path);
|
||||||
|
|
||||||
|
// This is a optimization for route resolver: if current route was already resolved
|
||||||
|
// from this path - do nothing. It also prevents router from using outdated route in a case
|
||||||
|
// when user navigated to another path while current one was still resolving.
|
||||||
|
// Note: this lock uses only `path` fragment of URL to distinguish routes because currently
|
||||||
|
// all pages depend only on this fragment and handle search/hash on their own. If router
|
||||||
|
// should reload page on search/hash change - this fragment (and few checks below) should be updated
|
||||||
|
if (pathname === currentPathRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentPathRef.current = pathname;
|
||||||
|
|
||||||
|
// Don't reload controller if URL was replaced
|
||||||
|
if (action === "REPLACE") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
router
|
||||||
|
.resolve({ pathname })
|
||||||
|
.then(route => {
|
||||||
|
return isAbandoned || currentPathRef.current !== pathname ? null : resolveRouteDependencies(route);
|
||||||
|
})
|
||||||
|
.then(route => {
|
||||||
|
if (route) {
|
||||||
|
setCurrentRoute({ ...route, key: generateRouteKey() });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if (!isAbandoned && currentPathRef.current === pathname) {
|
||||||
|
setCurrentRoute({
|
||||||
|
render: currentRoute => <ErrorMessage {...currentRoute.routeParams} />,
|
||||||
|
routeParams: { error },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("PUSH");
|
||||||
|
|
||||||
|
const unlisten = location.listen((unused, action) => resolve(action));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isAbandoned = true;
|
||||||
|
unlisten();
|
||||||
|
};
|
||||||
|
}, [routes]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onRouteChange(currentRoute);
|
||||||
|
}, [currentRoute, onRouteChange]);
|
||||||
|
|
||||||
|
if (!currentRoute) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorBoundary ref={errorHandlerRef} renderError={error => <ErrorMessage error={error} />}>
|
||||||
|
{currentRoute.render(currentRoute)}
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Router.propTypes = {
|
||||||
|
routes: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
|
render: PropTypes.func, // (routeParams: PropTypes.object; currentRoute; location) => PropTypes.node
|
||||||
|
// Additional props to be injected into route component.
|
||||||
|
// Object keys are props names. Object values will become prop values:
|
||||||
|
// - if value is a function - it will be called without arguments, and result will be used; otherwise value will be used;
|
||||||
|
// - after previous step, if value is a promise - router will wait for it to resolve; resolved value then will be used;
|
||||||
|
// otherwise value will be used directly.
|
||||||
|
resolve: PropTypes.objectOf(PropTypes.any),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
onRouteChange: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
Router.defaultProps = {
|
||||||
|
routes: [],
|
||||||
|
onRouteChange: () => {},
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { isString } from "lodash";
|
||||||
|
import navigateTo from "./navigateTo";
|
||||||
|
|
||||||
|
export default function handleNavigationIntent(event) {
|
||||||
|
let element = event.target;
|
||||||
|
while (element) {
|
||||||
|
if (element.tagName === "A") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
element = element.parentNode;
|
||||||
|
}
|
||||||
|
if (!element || !element.hasAttribute("href")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep some default behaviour
|
||||||
|
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = element.getAttribute("target");
|
||||||
|
if (isString(target) && target.toLowerCase() === "_blank") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
navigateTo(element.href);
|
||||||
|
}
|
||||||
37
client/app/components/ApplicationArea/index.jsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import routes from "@/pages";
|
||||||
|
import Router from "./Router";
|
||||||
|
import handleNavigationIntent from "./handleNavigationIntent";
|
||||||
|
import ErrorMessage from "./ErrorMessage";
|
||||||
|
|
||||||
|
export default function ApplicationArea() {
|
||||||
|
const [currentRoute, setCurrentRoute] = useState(null);
|
||||||
|
const [unhandledError, setUnhandledError] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentRoute && currentRoute.title) {
|
||||||
|
document.title = currentRoute.title;
|
||||||
|
}
|
||||||
|
}, [currentRoute]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function globalErrorHandler(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
setUnhandledError(event.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.addEventListener("click", handleNavigationIntent, false);
|
||||||
|
window.addEventListener("error", globalErrorHandler, false);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.body.removeEventListener("click", handleNavigationIntent, false);
|
||||||
|
window.removeEventListener("error", globalErrorHandler, false);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (unhandledError) {
|
||||||
|
return <ErrorMessage error={unhandledError} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Router routes={routes} onRouteChange={setCurrentRoute} />;
|
||||||
|
}
|
||||||
25
client/app/components/ApplicationArea/navigateTo.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import location from "@/services/location";
|
||||||
|
import url from "@/services/url";
|
||||||
|
import { stripBase } from "./Router";
|
||||||
|
|
||||||
|
// When `replace` is set to `true` - it will just replace current URL
|
||||||
|
// without reloading current page (router will skip this location change)
|
||||||
|
export default function navigateTo(href, replace = false) {
|
||||||
|
// Allow calling chain to roll up, and then navigate
|
||||||
|
setTimeout(() => {
|
||||||
|
const isExternal = stripBase(href) === false;
|
||||||
|
if (isExternal) {
|
||||||
|
window.location = href;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
href = url.parse(href);
|
||||||
|
location.update(
|
||||||
|
{
|
||||||
|
path: href.pathname,
|
||||||
|
search: href.search,
|
||||||
|
hash: href.hash,
|
||||||
|
},
|
||||||
|
replace
|
||||||
|
);
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import React, { useEffect, useState, useContext } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { ErrorBoundaryContext } from "@/components/ErrorBoundary";
|
||||||
|
import { Auth } from "@/services/auth";
|
||||||
|
|
||||||
|
// This wrapper modifies `route.render` function and instead of passing `currentRoute` passes an object
|
||||||
|
// that contains:
|
||||||
|
// - `currentRoute.routeParams`
|
||||||
|
// - `pageTitle` field which is equal to `currentRoute.title`
|
||||||
|
// - `onError` field which is a `handleError` method of nearest error boundary
|
||||||
|
// - `apiKey` field
|
||||||
|
|
||||||
|
function ApiKeySessionWrapper({ apiKey, currentRoute, renderChildren }) {
|
||||||
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
const { handleError } = useContext(ErrorBoundaryContext);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let isCancelled = false;
|
||||||
|
Auth.setApiKey(apiKey);
|
||||||
|
Auth.loadConfig()
|
||||||
|
.then(() => {
|
||||||
|
if (!isCancelled) {
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (!isCancelled) {
|
||||||
|
setIsAuthenticated(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
isCancelled = true;
|
||||||
|
};
|
||||||
|
}, [apiKey]);
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment key={currentRoute.key}>
|
||||||
|
{renderChildren({ ...currentRoute.routeParams, pageTitle: currentRoute.title, onError: handleError, apiKey })}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiKeySessionWrapper.propTypes = {
|
||||||
|
apiKey: PropTypes.string.isRequired,
|
||||||
|
renderChildren: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiKeySessionWrapper.defaultProps = {
|
||||||
|
renderChildren: () => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function routeWithApiKeySession({ render, getApiKey, ...rest }) {
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
render: currentRoute => (
|
||||||
|
<ApiKeySessionWrapper apiKey={getApiKey(currentRoute)} currentRoute={currentRoute} renderChildren={render} />
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import ErrorBoundary, { ErrorBoundaryContext } from "@/components/ErrorBoundary";
|
||||||
|
import { Auth } from "@/services/auth";
|
||||||
|
import organizationStatus from "@/services/organizationStatus";
|
||||||
|
import ApplicationHeader from "./ApplicationHeader";
|
||||||
|
import ErrorMessage from "./ErrorMessage";
|
||||||
|
|
||||||
|
// This wrapper modifies `route.render` function and instead of passing `currentRoute` passes an object
|
||||||
|
// that contains:
|
||||||
|
// - `currentRoute.routeParams`
|
||||||
|
// - `pageTitle` field which is equal to `currentRoute.title`
|
||||||
|
// - `onError` field which is a `handleError` method of nearest error boundary
|
||||||
|
|
||||||
|
function UserSessionWrapper({ bodyClass, currentRoute, renderChildren }) {
|
||||||
|
const [isAuthenticated, setIsAuthenticated] = useState(!!Auth.isAuthenticated());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let isCancelled = false;
|
||||||
|
Promise.all([Auth.requireSession(), organizationStatus.refresh()])
|
||||||
|
.then(() => {
|
||||||
|
if (!isCancelled) {
|
||||||
|
setIsAuthenticated(!!Auth.isAuthenticated());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (!isCancelled) {
|
||||||
|
setIsAuthenticated(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
isCancelled = true;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (bodyClass) {
|
||||||
|
document.body.classList.toggle(bodyClass, true);
|
||||||
|
return () => {
|
||||||
|
document.body.classList.toggle(bodyClass, false);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [bodyClass]);
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<ApplicationHeader />
|
||||||
|
<React.Fragment key={currentRoute.key}>
|
||||||
|
<ErrorBoundary renderError={error => <ErrorMessage error={error} />}>
|
||||||
|
<ErrorBoundaryContext.Consumer>
|
||||||
|
{({ handleError }) =>
|
||||||
|
renderChildren({ ...currentRoute.routeParams, pageTitle: currentRoute.title, onError: handleError })
|
||||||
|
}
|
||||||
|
</ErrorBoundaryContext.Consumer>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</React.Fragment>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserSessionWrapper.propTypes = {
|
||||||
|
bodyClass: PropTypes.string,
|
||||||
|
renderChildren: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSessionWrapper.defaultProps = {
|
||||||
|
bodyClass: null,
|
||||||
|
renderChildren: () => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function routeWithUserSession({ render, bodyClass, ...rest }) {
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
render: currentRoute => (
|
||||||
|
<UserSessionWrapper bodyClass={bodyClass} currentRoute={currentRoute} renderChildren={render} />
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Tooltip from 'antd/lib/tooltip';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import '@/redash-font/style.less';
|
|
||||||
import recordEvent from '@/services/recordEvent';
|
|
||||||
|
|
||||||
export default function AutocompleteToggle({ state, disabled, onToggle }) {
|
|
||||||
let tooltipMessage = 'Live Autocomplete Enabled';
|
|
||||||
let icon = 'icon-flash';
|
|
||||||
if (!state) {
|
|
||||||
tooltipMessage = 'Live Autocomplete Disabled';
|
|
||||||
icon = 'icon-flash-off';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disabled) {
|
|
||||||
tooltipMessage = 'Live Autocomplete Not Available (Use Ctrl+Space to Trigger)';
|
|
||||||
icon = 'icon-flash-off';
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggle = (newState) => {
|
|
||||||
recordEvent('toggle_autocomplete', 'screen', 'query_editor', { state: newState });
|
|
||||||
onToggle(newState);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip placement="top" title={tooltipMessage}>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={'btn btn-default m-r-5' + (disabled ? ' disabled' : '')}
|
|
||||||
onClick={() => toggle(!state)}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
<i className={'icon ' + icon} />
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AutocompleteToggle.propTypes = {
|
|
||||||
state: PropTypes.bool.isRequired,
|
|
||||||
disabled: PropTypes.bool.isRequired,
|
|
||||||
onToggle: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
78
client/app/components/BeaconConsent.jsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import Card from "antd/lib/card";
|
||||||
|
import Button from "antd/lib/button";
|
||||||
|
import Typography from "antd/lib/typography";
|
||||||
|
import { clientConfig } from "@/services/auth";
|
||||||
|
import HelpTrigger from "@/components/HelpTrigger";
|
||||||
|
import DynamicComponent from "@/components/DynamicComponent";
|
||||||
|
import OrgSettings from "@/services/organizationSettings";
|
||||||
|
|
||||||
|
const Text = Typography.Text;
|
||||||
|
|
||||||
|
function BeaconConsent() {
|
||||||
|
const [hide, setHide] = useState(false);
|
||||||
|
|
||||||
|
if (!clientConfig.showBeaconConsentMessage || hide) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideConsentCard = () => {
|
||||||
|
clientConfig.showBeaconConsentMessage = false;
|
||||||
|
setHide(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmConsent = confirm => {
|
||||||
|
let message = "🙏 Thank you.";
|
||||||
|
|
||||||
|
if (!confirm) {
|
||||||
|
message = "Settings Saved.";
|
||||||
|
}
|
||||||
|
|
||||||
|
OrgSettings.save({ beacon_consent: confirm }, message)
|
||||||
|
// .then(() => {
|
||||||
|
// // const settings = get(response, 'settings');
|
||||||
|
// // this.setState({ settings, formValues: { ...settings } });
|
||||||
|
// })
|
||||||
|
.finally(hideConsentCard);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DynamicComponent name="BeaconConsent">
|
||||||
|
<div className="m-t-10 tiled">
|
||||||
|
<Card
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
Would you be ok with sharing anonymous usage data with the Redash team?{" "}
|
||||||
|
<HelpTrigger type="USAGE_DATA_SHARING" />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
bordered={false}>
|
||||||
|
<Text>Help Redash improve by automatically sending anonymous usage data:</Text>
|
||||||
|
<div className="m-t-5">
|
||||||
|
<ul>
|
||||||
|
<li> Number of users, queries, dashboards, alerts, widgets and visualizations.</li>
|
||||||
|
<li> Types of data sources, alert destinations and visualizations.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<Text>All data is aggregated and will never include any sensitive or private data.</Text>
|
||||||
|
<div className="m-t-5">
|
||||||
|
<Button type="primary" className="m-r-5" onClick={() => confirmConsent(true)}>
|
||||||
|
Yes
|
||||||
|
</Button>
|
||||||
|
<Button type="default" onClick={() => confirmConsent(false)}>
|
||||||
|
No
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="m-t-15">
|
||||||
|
<Text type="secondary">
|
||||||
|
You can change this setting anytime from the <a href="settings/organization">Organization Settings</a>{" "}
|
||||||
|
page.
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</DynamicComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BeaconConsent;
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from "prop-types";
|
||||||
import { react2angular } from 'react2angular';
|
|
||||||
|
|
||||||
export function BigMessage({ message, icon, children, className }) {
|
function BigMessage({ message, icon, children, className }) {
|
||||||
return (
|
return (
|
||||||
<div className={'p-15 text-center ' + className}>
|
<div className={"p-15 text-center " + className}>
|
||||||
<h3 className="m-t-0 m-b-0">
|
<h3 className="m-t-0 m-b-0">
|
||||||
<i className={'fa ' + icon} />
|
<i className={"fa " + icon} />
|
||||||
</h3>
|
</h3>
|
||||||
<br />
|
<br />
|
||||||
{message}
|
{message}
|
||||||
@@ -23,13 +22,9 @@ BigMessage.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
BigMessage.defaultProps = {
|
BigMessage.defaultProps = {
|
||||||
message: '',
|
message: "",
|
||||||
children: null,
|
children: null,
|
||||||
className: 'tiled bg-white',
|
className: "tiled bg-white",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function init(ngModule) {
|
export default BigMessage;
|
||||||
ngModule.component('bigMessage', react2angular(BigMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
init.init = true;
|
|
||||||
|
|||||||
75
client/app/components/CodeBlock.jsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import Button from "antd/lib/button";
|
||||||
|
import Tooltip from "antd/lib/tooltip";
|
||||||
|
import "./CodeBlock.less";
|
||||||
|
|
||||||
|
export default class CodeBlock extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
copyable: PropTypes.bool,
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
copyable: false,
|
||||||
|
children: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = { copied: null };
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.ref = React.createRef();
|
||||||
|
this.copyFeatureEnabled = props.copyable && document.queryCommandSupported("copy");
|
||||||
|
this.resetCopyState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.resetCopyState) {
|
||||||
|
clearTimeout(this.resetCopyState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy = () => {
|
||||||
|
// select text
|
||||||
|
window.getSelection().selectAllChildren(this.ref.current);
|
||||||
|
|
||||||
|
// copy
|
||||||
|
try {
|
||||||
|
const success = document.execCommand("copy");
|
||||||
|
if (!success) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
this.setState({ copied: "Copied!" });
|
||||||
|
} catch (err) {
|
||||||
|
this.setState({
|
||||||
|
copied: "Copy failed",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset selection
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
|
||||||
|
// reset tooltip
|
||||||
|
this.resetCopyState = setTimeout(() => this.setState({ copied: null }), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { copyable, children, ...props } = this.props;
|
||||||
|
|
||||||
|
const copyButton = (
|
||||||
|
<Tooltip title={this.state.copied || "Copy"}>
|
||||||
|
<Button icon="copy" type="dashed" size="small" onClick={this.copy} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="code-block">
|
||||||
|
<code {...props} ref={this.ref}>
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
{this.copyFeatureEnabled && copyButton}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
client/app/components/CodeBlock.less
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
@import '~antd/lib/button/style/index';
|
||||||
|
|
||||||
|
.code-block {
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 3px 27px 3px 3px;
|
||||||
|
position: relative;
|
||||||
|
min-height: 32px;
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{btn-prefix-cls} {
|
||||||
|
position: absolute;
|
||||||
|
right: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
padding-left: 3px !important;
|
||||||
|
padding-right: 3px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||