diff --git a/Dockerfile b/Dockerfile index f13fff01bd1405023b5479bf74c9d55dde5774f6..084dc81ae5daf8572492d3a27fac22d0125c1649 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,15 +3,9 @@ FROM node:12 as builder ARG BACKEND_URL ENV BACKEND_URL=${BACKEND_URL} -ARG USE_LOGO -ENV USE_LOGO=${USE_LOGO:-hbp} - ARG DATASET_PREVIEW_URL ENV DATASET_PREVIEW_URL=${DATASET_PREVIEW_URL:-https://hbp-kg-dataset-previewer.apps.hbp.eu/datasetPreview} -ARG USE_LOGO -ENV USE_LOGO=${USE_LOGO:-hbp} - ARG STRICT_LOCAL ENV STRICT_LOCAL=${STRICT_LOCAL:-false} diff --git a/README.md b/README.md index a6c762757b71bd98e94d102e2a3a255ea9339340..a241ff830b3e97ea93e3d04c419a11b9a453ce4c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ As interactive atlas viewer uses [webpack define plugin](https://webpack.js.org/ | `DATASET_PREVIEW_URL` | dataset preview url used by component <https://github.com/fzj-inm1-bda/kg-dataset-previewer>. Useful for diagnosing issues with dataset previews.| https://hbp-kg-dataset-previewer.apps.hbp.eu/datasetPreview | http://localhost:1234/datasetPreview | | `MATOMO_URL` | base url for matomo analytics | `null` | https://example.com/matomo/ | | `MATOMO_ID` | application id for matomo analytics | `null` | 6 | -| `USE_LOGO` | possible values are `hbp`, `ebrains`, `fzj` | `hbp` | `ebrains` | | `STRICT_LOCAL` | hides **Explore** and **Download** buttons. Useful for offline demo's | `false` | `true` | | `KIOSK_MODE` | after 5 minutes of inactivity, shows overlay inviting users to interact | `false` | `true` | | `BUILD_TEXT` | overlay text at bottom right of the viewer. set to `''` to hide. | | @@ -52,7 +51,7 @@ It is recommended to manage your environments with `.env` file. | `LOCAL_CDN` | rewrite cdns to local server. useful for offlnie demo | | `http://localhost:7080/` | | `PLUGIN_URLS` | semi colon separated urls to be returned when user queries plugins | `''` | `STAGING_PLUGIN_URLS` | semi colon separated urls to be returned when user queries plugins | `''` - +| `USE_LOGO` | possible values are `hbp`, `ebrains`, `fzj` | `hbp` | `ebrains` | ##### ebrains user authentication diff --git a/deploy/app.js b/deploy/app.js index 917bd1fdfb43e74cc68bc0348206f50ea672f6a8..643cb485fe28a8528cc17543ad67628005f4d89b 100644 --- a/deploy/app.js +++ b/deploy/app.js @@ -151,6 +151,9 @@ app.get('/', cookieParser(), (req, res) => { } }) + +app.use('/logo', require('./logo')) + /** * User route, for user profile/management */ diff --git a/deploy/atlas/index.js b/deploy/atlas/index.js index adbb330a3505cf4aee93dc8b6abc3edbc5a43884..35f4c488f81d730399395718cb2178f0ed84bb6d 100644 --- a/deploy/atlas/index.js +++ b/deploy/atlas/index.js @@ -1,19 +1,25 @@ const router = require('express').Router() const url = require('url') +const fs = require('fs') +const path = require('path') const { detEncoding } = require('nomiseco') const { getAllAtlases, getAtlasById, isReady } = require('./query') +const HOSTNAME = process.env.HOSTNAME || 'http://localhost:3000' const { getTemplate } = require('../templates/query') const { getHandleErrorFn } = require('../util/streamHandleError') +const getPreviewFn = ({ res, lastpart }) => HOSTNAME.replace(/\/$/, '') + '/' + + (res.locals.routePathname + ? url.resolve(`${res.locals.routePathname}/`, lastpart) + : lastpart) + router.get('/', async (req, res) => { const allAtlases = await getAllAtlases() const resolvedAtlases = allAtlases.map(v => { return { '@id': v, - url: res.locals.routePathname - ? url.resolve(`${res.locals.routePathname}/`, encodeURIComponent(v)) - : encodeURIComponent(v) + url: getPreviewFn({ res, lastpart: encodeURIComponent(v) }) } }) res.status(200).json(resolvedAtlases) @@ -27,8 +33,46 @@ router.get('/ready', (req, res) => { } }) +const previewImageFIleNameMap = new Map([ + ['minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588', 'bugbrain.png'], + ['minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2', 'icbm2009c.png'], + ['minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992', 'colin27.png'], + ['minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579', 'cytoarchitectonic-maps.png'], + ['juelich/iav/atlas/v1.0.0/3', 'cortical-layers.png'], + ['juelich/iav/atlas/v1.0.0/4', 'grey-white-matter.png'], + ['juelich/iav/atlas/v1.0.0/5', 'firbe-long.png'], + ['juelich/iav/atlas/v1.0.0/6', 'firbe-short.png'], + ['minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721', 'difumo-64.png'], + ['minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8', 'difumo-128.png'], + ['minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235', 'difumo-256.png'], + ['minds/core/parcellationatlas/v1.0.0/63b5794f-79a4-4464-8dc1-b32e170f3d16', 'difumo-512.png'], + ['minds/core/parcellationatlas/v1.0.0/12fca5c5-b02c-46ce-ab9f-f12babf4c7e1', 'difumo-1024.png'], + ['minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9', 'allen-mouse.png'], + ['minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83', 'allen-mouse-2017.png'], + ['minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f', 'allen-mouse-2015.png'], + ['minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8', 'waxholm.png'], + ['minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe', 'waxholm-v3.png'], + ['minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d', 'waxholm-v2.png'], + ['minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba', 'waxholm-v1.png'], +]) + router.get('/preview', (req, res) => { - res.status(501).end() + const { id } = req.query + const filename = previewImageFIleNameMap.get(id) + if (!filename) return res.status(404).end() + + let filepath + if (process.env.NODE_ENV === 'production') { + filepath = path.join(__dirname, '../res/image', filename) + } else { + filepath = path.join(__dirname, '../../src/res/images/atlas-selection', filename) + } + try { + res.setHeader('Content-Type', 'image/png') + fs.createReadStream(filepath).pipe(res).on('error', getHandleErrorFn(req, res)) + } catch (e) { + res.status(500).send(e) + } }) router.get('/:atlasId', async (req, res) => { @@ -41,17 +85,13 @@ router.get('/:atlasId', async (req, res) => { return res.status(200).json({ ...rest, - previewUrl: res.locals.routePathname - ? url.resolve(`${res.locals.routePathname}/`, lastPathPreview) - : lastPathPreview, + previewUrl: getPreviewFn({ res, lastpart: lastPathPreview }), parcellations: parcellations.map(p => { const spSearchParam = new url.URLSearchParams() spSearchParam.set('id', p['@id']) const parcellationPreview = `preview?${spSearchParam.toString()}` return { - previewUrl: res.locals.routePathname - ? url.resolve(`${res.locals.routePathname}/`, parcellationPreview) - : parcellationPreview, + previewUrl: getPreviewFn({ res, lastpart: parcellationPreview }), ...p } }), @@ -63,15 +103,10 @@ router.get('/:atlasId', async (req, res) => { const lastPathPreview = `preview?${searchParam.toString()}` return { ...tmpl, - previewUrl: res.locals.routePathname - ? url.resolve(`${res.locals.routePathname}/`, lastPathPreview) - : lastPathPreview, - url: res.locals.routePathname - ? url.resolve(`${res.locals.routePathname}/`, lastTemplatePath) - : lastTemplatePath + previewUrl: getPreviewFn({ res, lastpart: lastPathPreview }), + url: getPreviewFn({ res, lastpart: lastTemplatePath }) } }) - }) }) diff --git a/src/res/images/HBP_Primary_RGB_BlackText.png b/deploy/logo/assets/HBP_Primary_RGB_BlackText.png similarity index 100% rename from src/res/images/HBP_Primary_RGB_BlackText.png rename to deploy/logo/assets/HBP_Primary_RGB_BlackText.png diff --git a/src/res/images/HBP_Primary_RGB_WhiteText.png b/deploy/logo/assets/HBP_Primary_RGB_WhiteText.png similarity index 100% rename from src/res/images/HBP_Primary_RGB_WhiteText.png rename to deploy/logo/assets/HBP_Primary_RGB_WhiteText.png diff --git a/src/res/images/ebrains-logo-dark.svg b/deploy/logo/assets/ebrains-logo-dark.svg similarity index 100% rename from src/res/images/ebrains-logo-dark.svg rename to deploy/logo/assets/ebrains-logo-dark.svg diff --git a/src/res/images/ebrains-logo-light.svg b/deploy/logo/assets/ebrains-logo-light.svg similarity index 100% rename from src/res/images/ebrains-logo-light.svg rename to deploy/logo/assets/ebrains-logo-light.svg diff --git a/src/res/images/fzj_black_transparent_svg.svg b/deploy/logo/assets/fzj_black_transparent_svg.svg similarity index 100% rename from src/res/images/fzj_black_transparent_svg.svg rename to deploy/logo/assets/fzj_black_transparent_svg.svg diff --git a/src/res/images/fzj_white_transparent_svg.svg b/deploy/logo/assets/fzj_white_transparent_svg.svg similarity index 100% rename from src/res/images/fzj_white_transparent_svg.svg rename to deploy/logo/assets/fzj_white_transparent_svg.svg diff --git a/deploy/logo/index.js b/deploy/logo/index.js new file mode 100644 index 0000000000000000000000000000000000000000..d4750d225f9d0a09510db3fa40175544670c2969 --- /dev/null +++ b/deploy/logo/index.js @@ -0,0 +1,40 @@ +const fs = require('fs') +const path = require('path') +const { getHandleErrorFn } = require('../util/streamHandleError') +const router = require('express').Router() + +const map = new Map([ + ['hbp', { + mimetype: 'image/png', + light: 'HBP_Primary_RGB_BlackText.png', + dark: 'HBP_Primary_RGB_WhiteText.png' + }], + ['ebrains', { + mimetype: 'image/svg+xml', + light: 'ebrains-logo-dark.svg', + dark: 'ebrains-logo-light.svg' + }], + ['fzj', { + mimetype: 'image/svg+xml', + light: 'fzj_black_transparent_svg.svg', + dark: 'fzj_white_transparent_svg.svg' + }] +]) + +router.get('/', (req, res) => { + + const USE_LOGO = process.env.USE_LOGO || 'hbp' + const { mimetype, light, dark } = map.get(USE_LOGO) || map.get('hbp') + const darktheme = !!req.query.darktheme + try { + res.setHeader('Content-type', mimetype) + fs.createReadStream( + path.join(__dirname, `assets/${darktheme ? dark : light}`) + ).pipe(res).on('error', getHandleErrorFn(req, res)) + } catch (e) { + console.error(`Fetching logo error ${e.toString()}`) + res.status(500).end() + } +}) + +module.exports = router \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.history.service.ts b/src/atlasViewer/atlasViewer.history.service.ts index aa381ffec51b286f094424e96f8a1e0c688271ec..746ddb4fc01b79d0c1c8d909b4aac9223c87d3a6 100644 --- a/src/atlasViewer/atlasViewer.history.service.ts +++ b/src/atlasViewer/atlasViewer.history.service.ts @@ -48,6 +48,7 @@ export class AtlasViewerHistoryUseEffect implements OnDestroy { return { type: GENERAL_ACTION_TYPES.APPLY_STATE, state: { + ...storeState, ...defaultRootState, viewerState: { ...defaultRootState.viewerState, diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 5836c9005d93e9991105e54ee3780ae38f17a6a7..422971347ad0284d49a033bde86c6f14ad483cc4 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -58,7 +58,7 @@ [currentOnHover]="iavMouseHoverEl.currentOnHoverObs$ | async" iav-captureClickListenerDirective [iav-captureClickListenerDirective-captureDocument]="true" - (iav-captureClickListenerDirective-onClick)="mouseClickDocument($event)"> + (iav-captureClickListenerDirective-onUnmovedClick)="mouseClickDocument($event)"> </ui-nehuba-container> <div class="z-index-10 position-absolute pe-none w-100 h-100"> @@ -100,7 +100,7 @@ </div> - <div class="fixed-bottom pe-none d-flex justify-content-end m-4"> + <div class="fixed-bottom muted-7 pe-none mb-8 d-flex justify-content-end m-4"> <ng-container *ngTemplateOutlet="logoTmpl"> </ng-container> </div> diff --git a/src/atlasViewer/atlasViewer.urlUtil.ts b/src/atlasViewer/atlasViewer.urlUtil.ts index 2fee4d50c186852f1466b67c70f753824409f061..a46f5fd3f4675a48e6ccbf3ea90cee0f4314c759 100644 --- a/src/atlasViewer/atlasViewer.urlUtil.ts +++ b/src/atlasViewer/atlasViewer.urlUtil.ts @@ -322,6 +322,5 @@ export const cvtSearchParamToState = (searchparams: URLSearchParams, state: IavR } })() - console.log(returnState) return returnState } diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index 864e2b1901019acb1bfdc5fd8a8363a357fbaec8..aaa460d345db20ed131767266762207ef448816b 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -576,6 +576,11 @@ mat-icon[fontset="far"] max-width: none!important; } +.mb-8 +{ + margin-bottom:4rem!important; +} + .min-h-2 { min-height: 1rem; @@ -705,3 +710,13 @@ kg-dataset-previewer > img { top: unset!important; } + +.fixed-top +{ + bottom: unset!important; +} + +.layerGroupMenu > .mat-menu-content +{ + width: 100%; +} diff --git a/src/res/ext/atlas/atlas_multiLevelHuman.json b/src/res/ext/atlas/atlas_multiLevelHuman.json index e463edcd352076d27f570854d51da7c4ecb0a09b..3364a77d26f18650406a1405a44114118583e2cf 100644 --- a/src/res/ext/atlas/atlas_multiLevelHuman.json +++ b/src/res/ext/atlas/atlas_multiLevelHuman.json @@ -5,7 +5,7 @@ { "@id": "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", "name": "Big Brain (Histology)", - "avilableIn": [ + "availableIn": [ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "Jülich Cytoarchitechtonic Brain Atlas (human)" @@ -23,7 +23,7 @@ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", - "avilableIn": [ + "availableIn": [ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "Jülich Cytoarchitechtonic Brain Atlas (human)" @@ -34,6 +34,7 @@ }, { "ngId": "fibre bundle short", + "name": "Fibre Bundle Atlas - Short Bundle", "@id": "juelich/iav/atlas/v1.0.0/6" }, { @@ -61,7 +62,7 @@ { "@id": "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992", "name": "MNI Colin 27", - "avilableIn": [ + "availableIn": [ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "Jülich Cytoarchitechtonic Brain Atlas (human)" @@ -74,7 +75,7 @@ "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "Jülich Cytoarchitechtonic Brain Atlas (human)", "baseLayer": true, - "avilableIn": [ + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", "name": "Big Brain (Histology)" @@ -92,7 +93,7 @@ { "name": "BigBrain Cortical Layers Segmentation", "@id": "juelich/iav/atlas/v1.0.0/3", - "avilableIn": [ + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", "name": "Big Brain (Histology)" @@ -102,7 +103,7 @@ { "name": "Grey/White matter", "@id": "juelich/iav/atlas/v1.0.0/4", - "avilableIn": [ + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", "name": "Big Brain (Histology)" @@ -112,7 +113,7 @@ { "name": "Fibre Bundle Atlas - Long Bundle", "@id": "juelich/iav/atlas/v1.0.0/5", - "avilableIn": [ + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" @@ -121,8 +122,9 @@ }, { "ngId": "fibre bundle short", + "name": "Fibre Bundle Atlas - Short Bundle", "@id": "juelich/iav/atlas/v1.0.0/6", - "avilableIn": [ + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" @@ -132,7 +134,8 @@ { "name": "DiFuMo Atlas (64 dimensions)", "@id": "minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721", - "avilableIn": [ + "groupName": "DiFuMo Atlas", + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" @@ -142,7 +145,8 @@ { "name": "DiFuMo Atlas (128 dimensions)", "@id": "minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8", - "avilableIn": [ + "groupName": "DiFuMo Atlas", + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" @@ -152,7 +156,8 @@ { "name": "DiFuMo Atlas (256 dimensions)", "@id": "minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235", - "avilableIn": [ + "groupName": "DiFuMo Atlas", + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" @@ -162,7 +167,8 @@ { "name": "DiFuMo Atlas (512 dimensions)", "@id": "minds/core/parcellationatlas/v1.0.0/63b5794f-79a4-4464-8dc1-b32e170f3d16", - "avilableIn": [ + "groupName": "DiFuMo Atlas", + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" @@ -172,7 +178,8 @@ { "name": "DiFuMo Atlas (1024 dimensions)", "@id": "minds/core/parcellationatlas/v1.0.0/12fca5c5-b02c-46ce-ab9f-f12babf4c7e1", - "avilableIn": [ + "groupName": "DiFuMo Atlas", + "availableIn": [ { "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric" diff --git a/src/res/ext/colin.json b/src/res/ext/colin.json index 1ea080d5fcefe131189e1bff2351e3dcb30ce3a2..b9c110ebc1df05b0e118855b21f5cbefabe4b836 100644 --- a/src/res/ext/colin.json +++ b/src/res/ext/colin.json @@ -10,6 +10,7 @@ "parcellations": [ { "fullId": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", + "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "JuBrain Cytoarchitectonic Atlas", "ngId": "jubrain colin v18 left", "auxillaryMeshIndices": [ diff --git a/src/res/images/atlas-selection/allen-mouse-2015.png b/src/res/images/atlas-selection/allen-mouse-2015.png new file mode 100644 index 0000000000000000000000000000000000000000..36b493023e63d3b33c2527191165174713ff7701 Binary files /dev/null and b/src/res/images/atlas-selection/allen-mouse-2015.png differ diff --git a/src/res/images/atlas-selection/allen-mouse-2017.png b/src/res/images/atlas-selection/allen-mouse-2017.png new file mode 100644 index 0000000000000000000000000000000000000000..4a8b4cad740bf6e0a5b7ee3f27661fe4941f13e3 Binary files /dev/null and b/src/res/images/atlas-selection/allen-mouse-2017.png differ diff --git a/src/res/images/atlas-selection/allen-mouse.png b/src/res/images/atlas-selection/allen-mouse.png new file mode 100644 index 0000000000000000000000000000000000000000..1d38cfce2b17f7f973d1fab5469dfba4f1b63537 Binary files /dev/null and b/src/res/images/atlas-selection/allen-mouse.png differ diff --git a/src/res/images/atlas-selection/bugbrain.png b/src/res/images/atlas-selection/bugbrain.png new file mode 100644 index 0000000000000000000000000000000000000000..416dbc79b489db22ed82868237e5a68c40ecf23a Binary files /dev/null and b/src/res/images/atlas-selection/bugbrain.png differ diff --git a/src/res/images/atlas-selection/colin27.png b/src/res/images/atlas-selection/colin27.png new file mode 100644 index 0000000000000000000000000000000000000000..d49773ece71854f93d8dcaed9ca3a710e11decb0 Binary files /dev/null and b/src/res/images/atlas-selection/colin27.png differ diff --git a/src/res/images/atlas-selection/cortical-layers.png b/src/res/images/atlas-selection/cortical-layers.png new file mode 100644 index 0000000000000000000000000000000000000000..38a9e902a8691dbb3e2d9732e42a12cda8cae2a2 Binary files /dev/null and b/src/res/images/atlas-selection/cortical-layers.png differ diff --git a/src/res/images/atlas-selection/cytoarchitectonic-maps.png b/src/res/images/atlas-selection/cytoarchitectonic-maps.png new file mode 100644 index 0000000000000000000000000000000000000000..c9645ea83f8299e1ce64e185df17d75de1032da5 Binary files /dev/null and b/src/res/images/atlas-selection/cytoarchitectonic-maps.png differ diff --git a/src/res/images/atlas-selection/difumo-1024.png b/src/res/images/atlas-selection/difumo-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..7ab4bf4ac91ac9be0eb6c96af1c45ab17cc9bddd Binary files /dev/null and b/src/res/images/atlas-selection/difumo-1024.png differ diff --git a/src/res/images/atlas-selection/difumo-128.png b/src/res/images/atlas-selection/difumo-128.png new file mode 100644 index 0000000000000000000000000000000000000000..47de2e12571b69f192f9117f41b45e1200c7f7e2 Binary files /dev/null and b/src/res/images/atlas-selection/difumo-128.png differ diff --git a/src/res/images/atlas-selection/difumo-256.png b/src/res/images/atlas-selection/difumo-256.png new file mode 100644 index 0000000000000000000000000000000000000000..08e508a6faa65bcbbc9799ff37025915992b4199 Binary files /dev/null and b/src/res/images/atlas-selection/difumo-256.png differ diff --git a/src/res/images/atlas-selection/difumo-512.png b/src/res/images/atlas-selection/difumo-512.png new file mode 100644 index 0000000000000000000000000000000000000000..b1c064664a6836b2d41c60a4407def4bfbc2e4dc Binary files /dev/null and b/src/res/images/atlas-selection/difumo-512.png differ diff --git a/src/res/images/atlas-selection/difumo-64.png b/src/res/images/atlas-selection/difumo-64.png new file mode 100644 index 0000000000000000000000000000000000000000..bf14836310113c9cb2688cb5f298a2aa87c010e3 Binary files /dev/null and b/src/res/images/atlas-selection/difumo-64.png differ diff --git a/src/res/images/atlas-selection/firbe-long.png b/src/res/images/atlas-selection/firbe-long.png new file mode 100644 index 0000000000000000000000000000000000000000..040d96a8dc7c575e1076072e800fedf08a413cb1 Binary files /dev/null and b/src/res/images/atlas-selection/firbe-long.png differ diff --git a/src/res/images/atlas-selection/firbe-short.png b/src/res/images/atlas-selection/firbe-short.png new file mode 100644 index 0000000000000000000000000000000000000000..2c52194bc5fa263114e6d4be9a2e6422ff5fa79e Binary files /dev/null and b/src/res/images/atlas-selection/firbe-short.png differ diff --git a/src/res/images/atlas-selection/grey-white-matter.png b/src/res/images/atlas-selection/grey-white-matter.png new file mode 100644 index 0000000000000000000000000000000000000000..352a7de6500d4d82a2952f911815726d7bae474b Binary files /dev/null and b/src/res/images/atlas-selection/grey-white-matter.png differ diff --git a/src/res/images/atlas-selection/icbm2009c.png b/src/res/images/atlas-selection/icbm2009c.png new file mode 100644 index 0000000000000000000000000000000000000000..1dfc2c47e84d60c7a5b4d69eba182cf003f1bf62 Binary files /dev/null and b/src/res/images/atlas-selection/icbm2009c.png differ diff --git a/src/res/images/atlas-selection/waxholm-v1.png b/src/res/images/atlas-selection/waxholm-v1.png new file mode 100644 index 0000000000000000000000000000000000000000..0e24e005e975938cbe6f3f4c658b6a306d9cce8e Binary files /dev/null and b/src/res/images/atlas-selection/waxholm-v1.png differ diff --git a/src/res/images/atlas-selection/waxholm-v2.png b/src/res/images/atlas-selection/waxholm-v2.png new file mode 100644 index 0000000000000000000000000000000000000000..e18fe6d5876c02b5ab34297c048219d6f622f0f8 Binary files /dev/null and b/src/res/images/atlas-selection/waxholm-v2.png differ diff --git a/src/res/images/atlas-selection/waxholm-v3.png b/src/res/images/atlas-selection/waxholm-v3.png new file mode 100644 index 0000000000000000000000000000000000000000..1b072a165fded37525ab849afe69ea61c3503f3b Binary files /dev/null and b/src/res/images/atlas-selection/waxholm-v3.png differ diff --git a/src/res/images/atlas-selection/waxholm.png b/src/res/images/atlas-selection/waxholm.png new file mode 100644 index 0000000000000000000000000000000000000000..fea10fee5272e99bc6cfd969a42307c38fa8b6d7 Binary files /dev/null and b/src/res/images/atlas-selection/waxholm.png differ diff --git a/src/res/images/big-brain.png b/src/res/images/big-brain.png new file mode 100644 index 0000000000000000000000000000000000000000..12e467e2211a635880a01e2fdb42c251b94320a5 Binary files /dev/null and b/src/res/images/big-brain.png differ diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts index 28894daf0ce8bb35c92e69bcd86b386f7327aa65..241d9b8d3437a0190553652e4d33636bd9d59b50 100644 --- a/src/services/state/viewerState.store.helper.ts +++ b/src/services/state/viewerState.store.helper.ts @@ -37,6 +37,16 @@ export const viewerStateSelectAtlas = createAction( props<{ atlas: { ['@id']: string } }>() ) +export const viewerStateSelectParcellationWithId = createAction( + `[viewerState] selectParcellationWithId`, + props<{ payload: { ['@id']: string } }>() +) + +export const viewerStateSelectTemplateWithId = createAction( + `[viewerState] selectTemplateWithId`, + props<{ payload: { ['@id']: string } }>() +) + export const viewerStateToggleAdditionalLayer = createAction( `[viewerState] toggleAdditionalLayer`, props<{ atlas: { ['@id']: string } }>() diff --git a/src/ui/atlasLayerWidget/alwContainer/alwContainer.component.ts b/src/ui/atlasLayerWidget/alwContainer/alwContainer.component.ts index b206c4a894aeab90e7e93a51dd2f5f4e7e2703f8..69319b14ba00962bbce8cfc52ad4eb2254771230 100644 --- a/src/ui/atlasLayerWidget/alwContainer/alwContainer.component.ts +++ b/src/ui/atlasLayerWidget/alwContainer/alwContainer.component.ts @@ -37,6 +37,7 @@ export class AtlasLayerContainer { this.overlayingParcellationLayers$ = this.store$.pipe( select(viewerStateGetOverlayingAdditionalParcellations), + filter(v => !!v), withLatestFrom(this.templateSelected$), map(([ additionalP, templateSelected ]) => { return additionalP diff --git a/src/ui/atlasLayerWidget/alwContainer/alwContainer.template.html b/src/ui/atlasLayerWidget/alwContainer/alwContainer.template.html index 6f592f1f1fe8f2359542b218136383ee18a7e6ba..b5d363f0c43ca270766734830f96845a3e8868c8 100644 --- a/src/ui/atlasLayerWidget/alwContainer/alwContainer.template.html +++ b/src/ui/atlasLayerWidget/alwContainer/alwContainer.template.html @@ -87,28 +87,34 @@ </mat-chip> </mat-chip-list> - <button class="pe-all" - [color]="visibleTab === 'hierarchy' ? 'primary' : ''" - (click)="handleClickWidget('hierarchy')" - mat-icon-button> - <i class="fas fa-sitemap"></i> - </button> - <button class="pe-all" - [color]="visibleTab === 'connectivity' ? 'primary' : ''" - (click)="handleClickWidget('connectivity')" - mat-icon-button> - <i class="fas fa-braille"></i> - </button> - <button class="pe-all" - mat-icon-button - [matBadge]="visibleTab === 'dataset' ? null : availableDatasets" - matBadgeColor="accent" - [color]="visibleTab === 'dataset' ? 'primary' : ''" - (click)="handleClickWidget('dataset')"> - <i class="fas fa-database"></i> - </button> + <ng-container *ngTemplateOutlet="btnTmpl; context: { tab: 'hierarchy', iconClass: 'fas fa-sitemap' }"> + </ng-container> + + <ng-container *ngTemplateOutlet="btnTmpl; context: { tab: 'connectivity', iconClass: 'fas fa-braille' }"> + </ng-container> + + <ng-container *ngTemplateOutlet="btnTmpl; context: { tab: 'dataset', iconClass: 'fas fa-database', badge: visibleTab === 'dataset' ? null : availableDatasets }"> + </ng-container> </div> +<ng-template + #btnTmpl + let-matBtnDisabled="matBtnDisabled" + let-tab="tab" + let-badge="badge" + let-iconClass="iconClass"> + <iav-dynamic-mat-button + class="pe-all" + [matBadge]="badge" + matBadgeColor="accent" + (click)="handleClickWidget(tab)" + [iav-dynamic-mat-button-disabled]="matBtnDisabled" + iav-dynamic-mat-button-color="primary" + [iav-dynamic-mat-button-style]="tab === visibleTab ? 'mat-mini-fab' : 'mat-icon-button'"> + <i [class]="iconClass"></i> + </iav-dynamic-mat-button> +</ng-template> + <ng-template #closeBtn> <button mat-icon-button (click)="visibleTab = null"> diff --git a/src/ui/logoContainer/logoContainer.component.ts b/src/ui/logoContainer/logoContainer.component.ts index f0b4569eb12a91c6c17f877c021f31bccc80433a..6af1710795ec7efa9d2a9b8347e38b0e4c3269f2 100644 --- a/src/ui/logoContainer/logoContainer.component.ts +++ b/src/ui/logoContainer/logoContainer.component.ts @@ -1,4 +1,7 @@ import { Component } from "@angular/core"; +import { BACKENDURL } from "src/util/constants"; +import { PureContantService } from "src/util"; +import { Subscription } from "rxjs"; @Component({ selector : 'logo-container', @@ -10,13 +13,22 @@ import { Component } from "@angular/core"; export class LogoContainer { // only used to define size - public imgSrc = USE_LOGO === 'hbp' - ? 'res/image/HBP_Primary_RGB_WhiteText.png' - : USE_LOGO === 'ebrains' - ? `res/image/ebrains-logo-light.svg` - : USE_LOGO === 'fzj' - ? 'res/image/fzj_black_transparent_svg.svg' - : null - - public useLogo = USE_LOGO + public imgSrc = `${BACKENDURL}logo` + + public containerStyle = { + backgroundImage: `url('${BACKENDURL}logo')` + } + + private subscriptions: Subscription[] = [] + constructor( + pureConstantService: PureContantService + ){ + this.subscriptions.push( + pureConstantService.darktheme$.subscribe(flag => { + this.containerStyle = { + backgroundImage: `url('${BACKENDURL}logo${!!flag ? '?darktheme=true' : ''}')` + } + }) + ) + } } diff --git a/src/ui/logoContainer/logoContainer.style.css b/src/ui/logoContainer/logoContainer.style.css index 5c170a688c2d4e815ffbb5ee4b39564dee9928bc..7c4c04468d1e04b5095f8096bca89a34b0ed3056 100644 --- a/src/ui/logoContainer/logoContainer.style.css +++ b/src/ui/logoContainer/logoContainer.style.css @@ -1,33 +1,3 @@ -:host-context([darktheme="true"]) [uselogo="ebrains"][hbpLogoContainer] -{ - background-image:url('res/image/ebrains-logo-light.svg') -} - -:host-context([darktheme="false"]) [uselogo="ebrains"][hbpLogoContainer] -{ - background-image:url('res/image/ebrains-logo-dark.svg') -} - -:host-context([darktheme="true"]) [uselogo="hbp"][hbpLogoContainer] -{ - background-image:url('res/image/HBP_Primary_RGB_WhiteText.png') -} - -:host-context([darktheme="false"]) [uselogo="hbp"][hbpLogoContainer] -{ - background-image:url('res/image/HBP_Primary_RGB_BlackText.png') -} - -:host-context([darktheme="true"]) [uselogo="fzj"][hbpLogoContainer] -{ - background-image:url('res/image/fzj_white_transparent_svg.svg') -} - -:host-context([darktheme="false"]) [uselogo="fzj"][hbpLogoContainer] -{ - background-image:url('res/image/fzj_black_transparent_svg.svg') -} - [hbpLogoContainer] { display:inline-block; diff --git a/src/ui/logoContainer/logoContainer.template.html b/src/ui/logoContainer/logoContainer.template.html index c4a53a91cd74411c941a717c8a7f1d6189ce146d..647ad7b86efe97d7edfdf11c293b32ce42b11049 100644 --- a/src/ui/logoContainer/logoContainer.template.html +++ b/src/ui/logoContainer/logoContainer.template.html @@ -1,3 +1,3 @@ -<span [attr.uselogo]="useLogo" hbpLogoContainer> +<span [ngStyle]="containerStyle" hbpLogoContainer> <img [src]="imgSrc" /> </span> \ No newline at end of file diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 84f4101e1801729d3becaac50ee004c69197b40b..af96ee110dbff8e7447eed7e7290a97e87b098a6 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -100,7 +100,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { public sliceViewLoadingMain$: Observable<[boolean, boolean, boolean]> public perspectiveViewLoading$: Observable<string|null> - private templateSelected$: Observable<any> + public templateSelected$: Observable<any> private newViewer$: Observable<any> private selectedParcellation$: Observable<any> private selectedRegions$: Observable<any[]> diff --git a/src/ui/nehubaContainer/nehubaContainer.style.css b/src/ui/nehubaContainer/nehubaContainer.style.css index 29a4d75a728298876505e6ae577aae12ac505ee7..dbb79cdf0a18e1c6d4d788e15662b3c15c35ca69 100644 --- a/src/ui/nehubaContainer/nehubaContainer.style.css +++ b/src/ui/nehubaContainer/nehubaContainer.style.css @@ -7,11 +7,7 @@ atlas-layer-container { - position: absolute; - bottom: 0; - lefT: 0; - - margin-bottom: 5em; + margin-bottom: 1em; margin-left: 1em; width: 30em; height: 50em; @@ -138,13 +134,18 @@ div#scratch-pad right: 0; } -.status-card-container +.viewer-config-container { position:absolute; left:1em; bottom:1em; - width : 20em; - pointer-events: all; +} + +.viewer-status-container +{ + position: absolute; + right: 1em; + bottom: 1em; } /* if not mobile, then show on hover */ diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index d29ca41407106f0540dae057a927007f3999cdcf..259da4bbc77c9141c8376e010d2e1207140f3bd0 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -25,16 +25,29 @@ <layout-floating-container [zIndex]="5"> - <!-- StatusCard container--> - <div *ngIf="viewerLoaded" class="status-card-container muted-7"> - <ui-status-card - [selectedTemplateName]="selectedTemplate && selectedTemplate.name" - [nehubaViewer]="nehubaViewer"> - </ui-status-card> + <div *ngIf="templateSelected$ | async" + class="viewer-config-container d-flex align-items-end pe-none"> + <!-- Viewer Selector Container--> + <viewer-selector + #viewerSelector + class="pe-all" + (iav-outsideClick)="viewerSelector.selectorExpanded = false"> + </viewer-selector> + + <atlas-layer-container class="pe-none overflow-visible"></atlas-layer-container> </div> - <atlas-layer-container class="overflow-visible"></atlas-layer-container> + <!-- StatusCard container--> + <div class="viewer-status-container pe-all"> + <div class="muted-7" *ngIf="viewerLoaded"> + <ui-status-card + [selectedTemplateName]="selectedTemplate && selectedTemplate.name" + [nehubaViewer]="nehubaViewer"> + </ui-status-card> + </div> + + </div> </layout-floating-container> <div id="scratch-pad"> diff --git a/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts b/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts index 7c0b1491d784a8aa110b7f7279c3800c364c4df6..23bfe7a32fdca74ac5df2f39fd165d8f21921f15 100644 --- a/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts +++ b/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts @@ -60,7 +60,8 @@ export class SplashScreen implements AfterViewInit { this.loadedAtlases$ = this.store.pipe( select(state => state[viewerStateHelperStoreName]), - select(state => state.fetchedAtlases) + select(state => state.fetchedAtlases), + filter(v => !!v) ) this.stillLoadingAtlases$ = this.loadedAtlases$.pipe( diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 1533e6bb20d9b4a726196c9bed08243c1fb821e5..67db58fb2c840ebfb543c11436446a1d4c85356d 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -91,6 +91,7 @@ import { APPEND_SCRIPT_TOKEN, appendScriptFactory } from "src/util/constants"; import { DOCUMENT } from "@angular/common"; import { AtlasDropdownSelector } from './atlasDropdown/atlasDropdown.component' import { AtlasLayerContainer } from "./atlasLayerWidget/alwContainer/alwContainer.component"; +import {ViewerSelectorComponent} from "src/ui/viewerSelector/viewerSelector.component"; @NgModule({ imports : [ @@ -127,7 +128,9 @@ import { AtlasLayerContainer } from "./atlasLayerWidget/alwContainer/alwContaine ConfigComponent, SigninBanner, AtlasDropdownSelector, - + ViewerSelectorComponent, + AtlasDropdownSelector, + StatusCardComponent, CookieAgreement, KGToS, diff --git a/src/ui/viewerSelector/viewerSelector.component.ts b/src/ui/viewerSelector/viewerSelector.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ed9ff95e476deea2531d16b0c3fcfa1483ba683 --- /dev/null +++ b/src/ui/viewerSelector/viewerSelector.component.ts @@ -0,0 +1,121 @@ +import { Component, ElementRef, OnInit, ViewChild, ViewChildren, QueryList } from "@angular/core"; +import { select, Store } from "@ngrx/store"; +import { safeFilter } from "src/services/stateStore.service"; +import { distinctUntilChanged, map, withLatestFrom, shareReplay } from "rxjs/operators"; +import { Observable, Subscription } from "rxjs"; +import { viewerStateGetSelectedAtlas, viewerStateSelectParcellationWithId, viewerStateSelectTemplateWithId } from "src/services/state/viewerState.store.helper"; +import { MatMenuTrigger } from "@angular/material/menu"; + +@Component({ + selector: 'viewer-selector', + templateUrl: './viewerSelector.template.html', + styleUrls: ['./viewerSelector.style.css'], +}) +export class ViewerSelectorComponent implements OnInit { + + @ViewChildren(MatMenuTrigger) matMenuTriggers: QueryList<MatMenuTrigger> + public atlas: any + + public groupedLayers = [] + + public selectedTemplateSpaceIndex: number = 0 + public selectedLayers = [] + + public selectedTemplate$: Observable<any> + private selectedParcellation$: Observable<any> + public selectedAtlas$: Observable<any> + private subscriptions: Subscription[] = [] + + public layerGroupMenuItems: any[] + + public selectorExpanded: boolean = false + + constructor(private store$: Store<any>) { + this.selectedAtlas$ = this.store$.pipe( + select(viewerStateGetSelectedAtlas), + distinctUntilChanged(), + shareReplay(1) + ) + this.selectedTemplate$ = this.store$.pipe( + select('viewerState'), + safeFilter('templateSelected'), + map(state => state.templateSelected), + withLatestFrom(this.selectedAtlas$), + map(([templateSelected, templateFromAtlas]) => { + return { + ...templateFromAtlas, + ...templateSelected + } + }) + ) + this.selectedParcellation$ = this.store$.pipe( + select('viewerState'), + safeFilter('parcellationSelected'), + map(state => state.parcellationSelected), + ) + } + + ngOnInit(): void { + this.subscriptions.push( + this.selectedTemplate$.subscribe(st => { + this.selectedTemplateSpaceIndex = this.atlas && this.atlas.templateSpaces.findIndex(ts => ts['@id'] === st['@id']) + }) + ) + this.subscriptions.push( + this.selectedParcellation$.subscribe(ps => { + this.selectedLayers = this.atlas && [this.atlas.parcellations.find(l => l['@id'] === ps['@id'])['@id']] + }) + ) + this.subscriptions.push( + this.selectedAtlas$.subscribe(sa => { + if (sa && sa !== 'undefined') this.atlas = sa + + const groupableParcellations = this.atlas?.parcellations.filter(p => p.groupName && p.groupName.length) + + const groupsDict = groupableParcellations.reduce((r, a) => { + r[a.groupName] = r[a.groupName] || [] + r[a.groupName].push(a) + return r + }, {}) + this.groupedLayers = Object.entries(groupsDict) + }) + ) + } + + selectTemplateWithName(template) { + this.store$.dispatch( + viewerStateSelectTemplateWithId({ payload: template }) + ) + } + + selectParcellationWithName(layer) { + this.store$.dispatch( + viewerStateSelectParcellationWithId({ payload: layer }) + ) + } + + templateIncludesLayer(layer, template) { + return layer.availableIn.map(a => a['@id']).includes(template['@id']) + } + + templateIncludesGroup(group, template) { + return group[1].every(v => v.availableIn.map(t => t['@id']).includes(template['@id'])) + } + + selectedOneOfTheLayers(layers) { + const includes = layers.map(l=>l['@id']).some(r=> this.selectedLayers.includes(r)) + return includes + } + + selectedLayersIncludes(id) { + return this.selectedLayers.includes(id) + } + + notGroupedParcellations(parcellations) { + return parcellations.filter(p => !p.groupName) + } + + collapseExpandedGroup(){ + this.matMenuTriggers.forEach(t => t.menuOpen && t.closeMenu()) + } +} diff --git a/src/ui/viewerSelector/viewerSelector.style.css b/src/ui/viewerSelector/viewerSelector.style.css new file mode 100644 index 0000000000000000000000000000000000000000..2533ae94d54cde966344d5ef06602ff859c459a4 --- /dev/null +++ b/src/ui/viewerSelector/viewerSelector.style.css @@ -0,0 +1,114 @@ +.singleLayerImageContainer img { + flex-shrink: 0; + /*min-width: 100%;*/ + /*min-height: 100%;*/ + width: 60px; + height: 60px; + border-radius: 10px; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; +} + +.singleLayerImageContainer { + max-width: 80px; + width: 80px; + height: 90px; +} + +.selectedTemplateDefaultContainer { + width: 100px; + height: 100px; + border-radius: 10px; +} +.selectedTemplateDefaultContainer img { + border-radius: 10px; + min-width: 100%; + min-height: 100%; + width: 100%; +} + +.selectedLayerBorder { + border: 2px solid #FED363; +} + +.cursorPointer { + cursor: pointer !important; +} + + +.scale-up-bl { + -webkit-animation: scale-up-bl .2s cubic-bezier(.39, .575, .565, 1.000) both; + animation: scale-up-bl .2s cubic-bezier(.39, .575, .565, 1.000) both; +} + +@-webkit-keyframes scale-up-bl { + 0% { + -webkit-transform: scale(.5); + transform: scale(.5); + -webkit-transform-origin: 0 100%; + transform-origin: 0 100%; + } + 100% { + -webkit-transform: scale(1); + transform: scale(1); + -webkit-transform-origin: 0 100%; + transform-origin: 0 100%; + } +} + +@keyframes scale-up-bl { + 0% { + -webkit-transform: scale(.5); + transform: scale(.5); + -webkit-transform-origin: 0 100%; + transform-origin: 0 100%; + } + 100% { + -webkit-transform: scale(1); + transform: scale(1); + -webkit-transform-origin: 0 100%; + transform-origin: 0 100%; + } +} + + +.scale-down-bl { + -webkit-animation: scale-down-bl .4s cubic-bezier(.25, .46, .45, .94) both; + animation: scale-down-bl .4s cubic-bezier(.25, .46, .45, .94) both; +} + +@-webkit-keyframes scale-down-bl { + 0% { + -webkit-transform: scale(1); + transform: scale(1); + -webkit-transform-origin: 0 100%; + transform-origin: 0 100%; + } + 100% { + -webkit-transform: scale(.5); + transform: scale(.5); + -webkit-transform-origin: 0 100%; + transform-origin: 0 100%; + } +} + +@keyframes scale-down-bl { + 0% { + -webkit-transform: scale(1); + transform: scale(1); + -webkit-transform-origin: 0 100%; + transform-origin: 0 100%; + } + 100% { + -webkit-transform: scale(.5); + transform: scale(.5); + -webkit-transform-origin: 0 100%; + transform-origin: 0 100%; + } +} + +.folder-container +{ + margin-right:0.5rem; + margin-bottom:-0.5rem; +} \ No newline at end of file diff --git a/src/ui/viewerSelector/viewerSelector.template.html b/src/ui/viewerSelector/viewerSelector.template.html new file mode 100644 index 0000000000000000000000000000000000000000..4dce8803f5b685e957118a57a443bdfc8b8c05a2 --- /dev/null +++ b/src/ui/viewerSelector/viewerSelector.template.html @@ -0,0 +1,96 @@ +<div> + <div [hidden]="selectorExpanded" + class="muted-7 m-2 selectedLayerBorder cursorPointer selectedTemplateDefaultContainer" + (click)="selectorExpanded = true"> + <img [src]="(selectedTemplate$ | async)?.previewUrl" draggable="false"/> + </div> + + + <small *ngIf="selectorExpanded" class="position-relative"> + <mat-card class="scale-up-bl"> + <div class="position-absolute cursorPointer" style="top: 10px; right: 10px;" (click)="selectorExpanded = false"> + <i class="fas fa-times-circle"></i> + </div> + <mat-card-subtitle class="mb-1">Templates</mat-card-subtitle> + + + <div class="d-flex flex-wrap w-100"> + <div class="d-flex flex-column justify-content-start w-100 mb-1 mt-1 overflow-hidden cursorPointer singleLayerImageContainer" + *ngFor="let template of (selectedAtlas$ | async)?.templateSpaces; let i = index" + [matTooltip]="template.name" + matTooltipPosition="above" + (click)="selectTemplateWithName(template)"> + <img class="layer-image align-self-center" + [ngClass]="selectedTemplateSpaceIndex === i? 'selectedLayerBorder': null " + [src]="template.previewUrl" + draggable="false"/> <!--[ngStyle]="{opacity: hover? '1' : '0.2'}" (mouseenter)="hover = true" (mouseleave)="hover = false"--> + <small class="ml-1 mr-1 mt-2 text-truncate">{{template.name}}</small> + </div> + </div> + + <mat-card-subtitle class="mb-1 mt-2">Layers</mat-card-subtitle> + + + + + <div class="d-flex flex-wrap w-100"> + <div class="d-flex flex-column justify-content-start w-100 mb-1 mt-1 overflow-hidden cursorPointer singleLayerImageContainer" + *ngFor="let layer of notGroupedParcellations((selectedAtlas$ | async)?.parcellations); let i = index;" + [matTooltip]="layer.name" + matTooltipPosition="above" + (click)="templateIncludesLayer(layer, atlas?.templateSpaces[selectedTemplateSpaceIndex]) && selectParcellationWithName(layer)" + [ngStyle]="{opacity: templateIncludesLayer(layer, atlas?.templateSpaces[selectedTemplateSpaceIndex])? '1' : '0.2'}"> + <img class="layer-image align-self-center" + [ngClass]="selectedLayersIncludes(layer['@id'])? 'selectedLayerBorder' : null" + [src]="layer.previewUrl" + draggable="false"/> + <small class="ml-1 mr-1 mt-2 mb-2 text-truncate">{{layer.name}}</small> + </div> + + <div class="d-flex flex-column justify-content-start w-100 mb-1 mt-1 overflow-hidden cursorPointer singleLayerImageContainer" + [matMenuTriggerFor]="layerGroupMenu" + iav-stop="click" + *ngFor="let group of groupedLayers; let i = index;" + [matTooltip]="group[0]" + matTooltipPosition="above" + (click)="layerGroupMenuItems = group[1]" + [ngStyle]="{opacity: templateIncludesGroup(group, atlas?.templateSpaces[selectedTemplateSpaceIndex])? '1' : '0.2'}" + #groupedMenuTrigger="matMenuTrigger"> + <img class="layer-image align-self-center" + [ngClass]="selectedOneOfTheLayers(group[1])? 'selectedLayerBorder' : null" + [src]="group[1][0].previewUrl" + draggable="false"/> + <div class="d-flex flex-row-reverse align-items-end w-100 h-0"> + <i class="fas fa-folder folder-container fa-2x"></i> + </div> + <small class="ml-1 mr-1 mt-2 mb-2 text-truncate">{{group[0]}}</small> + </div> + + <mat-menu + #layerGroupMenu="matMenu" + class="layerGroupMenu" + hasBackdrop="false"> + + <ng-template matMenuContent> + <div iav-stop="click" + class="d-flex flex-column align-items-center" + (iav-outsideClick)="collapseExpandedGroup()"> + <div class="d-flex flex-column justify-content-center w-100 mb-1 mt-1 overflow-hidden cursorPointer singleLayerImageContainer" + *ngFor="let layer of layerGroupMenuItems; let i = index;" + [matTooltip]="layer.name" + matTooltipPosition="above" + (click)="templateIncludesLayer(layer, atlas?.templateSpaces[selectedTemplateSpaceIndex]) && selectParcellationWithName(layer)" + [ngStyle]="{opacity: templateIncludesLayer(layer, atlas?.templateSpaces[selectedTemplateSpaceIndex])? '1' : '0.2'}"> + <img class="layer-image align-self-center" + [ngClass]="selectedLayersIncludes(layer['@id'])? 'selectedLayerBorder' : null" + [src]="layer.previewUrl" + draggable="false"/> + <small class="iv-custom-comp text ml-1 mr-1 mt-2 mb-2 text-truncate">{{layer.name}}</small> + </div> + </div> + </ng-template> + </mat-menu> + </div> + </mat-card> + </small> +</div> diff --git a/src/ui/viewerStateController/viewerState.base.ts b/src/ui/viewerStateController/viewerState.base.ts index fdbabfcf73a3f66e5b8338dbbcc826c77370a9e9..fb196c12b991ebbc4b19eae7bcf1ddc4fdcf76d3 100644 --- a/src/ui/viewerStateController/viewerState.base.ts +++ b/src/ui/viewerStateController/viewerState.base.ts @@ -17,10 +17,6 @@ const ACTION_TYPES = { NAVIGATETO_REGION: 'NAVIGATETO_REGION', } -const compareWith = (o, n) => !o || !n - ? false - : o.name === n.name - export class ViewerStateBase implements OnInit { @ViewChild('savedRegionBottomSheetTemplate', {read: TemplateRef}) public savedRegionBottomSheetTemplate: TemplateRef<any> @@ -40,8 +36,6 @@ export class ViewerStateBase implements OnInit { public savedRegionsSelections$: Observable<any[]> - public compareWith = compareWith - private savedRegionBottomSheetRef: MatBottomSheetRef constructor( @@ -209,3 +203,4 @@ export class ViewerStateBase implements OnInit { } export const VIEWERSTATE_CONTROLLER_ACTION_TYPES = ACTION_TYPES + diff --git a/src/ui/viewerStateController/viewerState.useEffect.spec.ts b/src/ui/viewerStateController/viewerState.useEffect.spec.ts index 6a98174940a4c1993c1be1d072897237aa01ecf7..dd055354b4e04fa8d05b06321f4f41c7dc9ffaee 100644 --- a/src/ui/viewerStateController/viewerState.useEffect.spec.ts +++ b/src/ui/viewerStateController/viewerState.useEffect.spec.ts @@ -88,42 +88,42 @@ describe('viewerState.useEffect.ts', () => { describe('selectTemplateWithName$', () => { it('if coordXform returns error', () => { - + expect(false).toBe(true) const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect) - expect( - viewerStateCtrlEffect.selectTemplateWithName$ - ).toBeObservable( - hot( - 'a', - { - a: { - type: NEWVIEWER, - selectTemplate: reconstitutedColin, - selectParcellation: reconstitutedColin.parcellations[0] - } - } - ) - ) + // expect( + // viewerStateCtrlEffect.selectTemplateWithName$ + // ).toBeObservable( + // hot( + // 'a', + // { + // a: { + // type: NEWVIEWER, + // selectTemplate: reconstitutedColin, + // selectParcellation: reconstitutedColin.parcellations[0] + // } + // } + // ) + // ) }) it('calls with correct param', () => { - + expect(false).toBe(true) // necessary for observable to fire const viewerStateCtrlEffect = TestBed.inject(ViewerStateControllerUseEffect) - expect( - viewerStateCtrlEffect.selectTemplateWithName$ - ).toBeObservable( - hot( - 'a', - { - a: { - type: NEWVIEWER, - selectTemplate: reconstitutedColin, - selectParcellation: reconstitutedColin.parcellations[0] - } - } - ) - ) + // expect( + // viewerStateCtrlEffect.selectTemplateWithName$ + // ).toBeObservable( + // hot( + // 'a', + // { + // a: { + // type: NEWVIEWER, + // selectTemplate: reconstitutedColin, + // selectParcellation: reconstitutedColin.parcellations[0] + // } + // } + // ) + // ) expect(spy).toHaveBeenCalledWith( bigbrainJson.name, reconstitutedColin.name, @@ -145,21 +145,21 @@ describe('viewerState.useEffect.ts', () => { } updatedColinNavigation.zoomFactor = zoom updatedColinNavigation.pose.orientation = orientation - - expect( - viewerStateCtrlEffect.selectTemplateWithName$ - ).toBeObservable( - hot( - 'a', - { - a: { - type: NEWVIEWER, - selectTemplate: updatedColin, - selectParcellation: updatedColin.parcellations[0] - } - } - ) - ) + expect(false).toBe(true) + // expect( + // viewerStateCtrlEffect.selectTemplateWithName$ + // ).toBeObservable( + // hot( + // 'a', + // { + // a: { + // type: NEWVIEWER, + // selectTemplate: updatedColin, + // selectParcellation: updatedColin.parcellations[0] + // } + // } + // ) + // ) }) }) }) diff --git a/src/ui/viewerStateController/viewerState.useEffect.ts b/src/ui/viewerStateController/viewerState.useEffect.ts index 741b8d6166ed82d21aa47a2465d038129542ca14..6f4a57af9db2ba60f3d5d853f2e3527530a5d925 100644 --- a/src/ui/viewerStateController/viewerState.useEffect.ts +++ b/src/ui/viewerStateController/viewerState.useEffect.ts @@ -1,7 +1,7 @@ import { Injectable, OnDestroy, OnInit } from "@angular/core"; import { Actions, Effect, ofType } from "@ngrx/effects"; import { Action, select, Store } from "@ngrx/store"; -import { Observable, Subscription, of } from "rxjs"; +import { Observable, Subscription, of, merge } from "rxjs"; import {distinctUntilChanged, filter, map, shareReplay, withLatestFrom, switchMap, mapTo } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; import { CHANGE_NAVIGATION, FETCHED_TEMPLATE, GENERAL_ACTION_TYPES, IavRootStoreInterface, isDefined, NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS } from "src/services/stateStore.service"; @@ -10,7 +10,7 @@ import { regionFlattener } from "src/util/regionFlattener"; import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "./viewerState.base"; import {TemplateCoordinatesTransformation} from "src/services/templateCoordinatesTransformation.service"; import { CLEAR_STANDALONE_VOLUMES } from "src/services/state/viewerState.store"; -import { viewerStateToggleRegionSelect } from "src/services/state/viewerState.store.helper"; +import { viewerStateToggleRegionSelect, viewerStateSelectParcellationWithId, viewerStateSelectTemplateWithId } from "src/services/state/viewerState.store.helper"; @Injectable({ providedIn: 'root', @@ -33,10 +33,10 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy { ) @Effect() - public selectTemplateWithName$: Observable<any> + public selectParcellation$: Observable<any> @Effect() - public selectParcellationWithName$: Observable<any> + public selectTemplate$: Observable<any> /** * Determines how single click on region hierarchy will affect view @@ -77,36 +77,47 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy { distinctUntilChanged(), ) - this.onTemplateSelectClearStandAloneVolumes$ = this.actions$.pipe( - ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.SELECT_TEMPLATE_WITH_NAME), - mapTo({ - type: CLEAR_STANDALONE_VOLUMES - }) + this.onTemplateSelectClearStandAloneVolumes$ = viewerState$.pipe( + select('templateSelected'), + distinctUntilChanged(), + mapTo({ type: CLEAR_STANDALONE_VOLUMES }) ) - this.selectParcellationWithName$ = this.actions$.pipe( - ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.SELECT_PARCELLATION_WITH_NAME), - map(action => { - const { payload = {} } = action as ViewerStateAction - const { name } = payload - return name - }), - filter(name => !!name), - withLatestFrom(viewerState$.pipe( - select('parcellationSelected'), - )), - filter(([name, parcellationSelected]) => { - if (parcellationSelected && parcellationSelected.name === name) { return false } - return true - }), - map(([name]) => name), + /** + * merge all sources of selecting parcellation into parcellation id + */ + this.selectParcellation$ = merge( + + this.actions$.pipe( + ofType(viewerStateSelectParcellationWithId.type), + map(({ payload }) => payload['@id']) + ), + + /** + * deprecated method... + * finding id from name + */ + this.actions$.pipe( + ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.SELECT_PARCELLATION_WITH_NAME), + withLatestFrom(viewerState$.pipe( + select('templateSelected') + )), + map(([ action, templateSelected ]) => { + const parcellationName = (action as any).payload.name + const foundParcellation = templateSelected.parcellations.find(p => p.name === parcellationName) + return foundParcellation && foundParcellation['@id'] + }), + filter(v => !!v) + ) + ).pipe( + distinctUntilChanged(), withLatestFrom(viewerState$.pipe( select('templateSelected'), )), - map(([name, templateSelected]) => { + map(([id, templateSelected]) => { const { parcellations: availableParcellations } = templateSelected - const newParcellation = availableParcellations.find(t => t.name === name) + const newParcellation = availableParcellations.find(t => t['@id'] === id) if (!newParcellation) { return { type: GENERAL_ACTION_TYPES.ERROR, @@ -119,56 +130,76 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy { type: SELECT_PARCELLATION, selectParcellation: newParcellation, } - }), + }) ) - this.selectTemplateWithName$ = this.actions$.pipe( - ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.SELECT_TEMPLATE_WITH_NAME), - map(action => { - const { payload = {} } = action as ViewerStateAction - const { name } = payload - return name - }), - filter(name => !!name), + /** + * merge all sources into single stream consisting of template id's + */ + this.selectTemplate$ = merge( + this.actions$.pipe( + ofType(viewerStateSelectTemplateWithId.type), + map(({ payload }) => payload['@id']) + ), + this.actions$.pipe( + ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.SELECT_TEMPLATE_WITH_NAME), + withLatestFrom(viewerState$.pipe( + select('fetchedTemplates') + )), + map(([ action, fetchedTemplates ]) => { + const templateName = (action as any).payload.name + const foundTemplate = fetchedTemplates.find(t => t.name === templateName) + return foundTemplate && foundTemplate['@id'] + }), + filter(v => !!v) + ) + ).pipe( + withLatestFrom( viewerState$ ), - switchMap(([newTemplateName, { templateSelected, fetchedTemplates, navigation }]) => { + switchMap(([newTemplateId, { templateSelected, fetchedTemplates, navigation, parcellationSelected }]) => { if (!templateSelected) { return of({ - newTemplateName, + newTemplateId, templateSelected: templateSelected, fetchedTemplates, translatedCoordinate: null, - navigation + navigation, + parcellationSelected }) } const position = (navigation && navigation.position) || [0, 0, 0] - if (newTemplateName === templateSelected.name) return of(null) + if (newTemplateId === templateSelected['@id']) return of(null) + + const newTemplateName = fetchedTemplates.find(t => t['@id'] === newTemplateId).name + return this.coordinatesTransformation.getPointCoordinatesForTemplate(templateSelected.name, newTemplateName, position).pipe( map(({ status, statusText, result }) => { if (status === 'error') { return { - newTemplateName, + newTemplateId, templateSelected: templateSelected, fetchedTemplates, translatedCoordinate: null, - navigation + navigation, + parcellationSelected } } return { - newTemplateName, + newTemplateId, templateSelected: templateSelected, fetchedTemplates, translatedCoordinate: result, - navigation + navigation, + parcellationSelected } }) ) }), filter(v => !!v), - map(({ newTemplateName, templateSelected, fetchedTemplates, translatedCoordinate, navigation }) => { - const newTemplateTobeSelected = fetchedTemplates.find(t => t.name === newTemplateName) + map(({ newTemplateId, templateSelected, fetchedTemplates, translatedCoordinate, navigation, parcellationSelected }) => { + const newTemplateTobeSelected = fetchedTemplates.find(t => t['@id'] === newTemplateId) if (!newTemplateTobeSelected) { return { type: GENERAL_ACTION_TYPES.ERROR, @@ -177,12 +208,16 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy { }, } } + + const selectParcellationWithTemplate = newTemplateTobeSelected.parcellations.map(p => p['@id']).includes(parcellationSelected['@id']) ? + newTemplateTobeSelected.parcellations[newTemplateTobeSelected.parcellations.findIndex(p => p['@id'] === parcellationSelected['@id'])] + : newTemplateTobeSelected.parcellations[0] if (!translatedCoordinate) { return { type: NEWVIEWER, selectTemplate: newTemplateTobeSelected, - selectParcellation: newTemplateTobeSelected.parcellations[0], + selectParcellation: selectParcellationWithTemplate, } } const deepCopiedState = JSON.parse(JSON.stringify(newTemplateTobeSelected)) diff --git a/src/util/directives/captureClickListener.directive.ts b/src/util/directives/captureClickListener.directive.ts index c432afde0a1b8aac18960e1e4a05b3cc509269ed..b8e04e77f862b0841fb23cac3b801b494a729809 100644 --- a/src/util/directives/captureClickListener.directive.ts +++ b/src/util/directives/captureClickListener.directive.ts @@ -1,6 +1,6 @@ import { Directive, ElementRef, EventEmitter, OnDestroy, OnInit, Output, Input, Inject } from "@angular/core"; import { fromEvent, Subscription } from "rxjs"; -import { switchMapTo, takeUntil, filter } from "rxjs/operators"; +import { switchMapTo, takeUntil } from "rxjs/operators"; import { DOCUMENT } from "@angular/common"; @Directive({ @@ -12,7 +12,7 @@ export class CaptureClickListenerDirective implements OnInit, OnDestroy { @Input('iav-captureClickListenerDirective-captureDocument') captureDocument: boolean = false private subscriptions: Subscription[] = [] - @Output('iav-captureClickListenerDirective-onClick') public mapClicked: EventEmitter<any> = new EventEmitter() + @Output('iav-captureClickListenerDirective-onUnmovedClick') public mapClicked: EventEmitter<any> = new EventEmitter() @Output('iav-captureClickListenerDirective-onMousedown') public mouseDownEmitter: EventEmitter<any> = new EventEmitter() constructor( @@ -27,9 +27,9 @@ export class CaptureClickListenerDirective implements OnInit, OnDestroy { } public ngOnInit() { - const mouseDownObs$ = fromEvent(this.element, 'mousedown', { capture: true }) - const mouseMoveObs$ = fromEvent(this.element, 'mousemove', { capture: true }) - const mouseUpObs$ = fromEvent(this.element, 'mouseup', { capture: true }) + const mouseDownObs$ = fromEvent(this.element, 'mousedown', { capture: this.captureDocument }) + const mouseMoveObs$ = fromEvent(this.element, 'mousemove', { capture: this.captureDocument }) + const mouseUpObs$ = fromEvent(this.element, 'mouseup', { capture: this.captureDocument }) this.subscriptions.push( mouseDownObs$.subscribe(event => { diff --git a/src/util/directives/clickOutside.directive.ts b/src/util/directives/clickOutside.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a845ee56b1cdeef625305d1fa2ba6a04f2dd8ac --- /dev/null +++ b/src/util/directives/clickOutside.directive.ts @@ -0,0 +1,20 @@ +import { Directive, ElementRef, HostListener, Output, EventEmitter } from '@angular/core'; + +@Directive({ + selector: '[iav-outsideClick]' +}) +export class ClickOutsideDirective { + + constructor(private elementRef: ElementRef) { } + + @Output('iav-outsideClick') + clickOutside: EventEmitter<any> = new EventEmitter() + + @HostListener('document:click', ['$event.target']) + onMouseClick(targetElement) { + const clickedInside = this.elementRef.nativeElement.contains(targetElement) + if (!clickedInside) { + this.clickOutside.emit() + } + } +} diff --git a/src/util/pipes/filterNull.pipe.ts b/src/util/pipes/filterNull.pipe.ts index 4ee371f340ff9aa07192fdc7f56d8fb92db19bb0..4bbde2567d3f76f550382425264374134571ba0e 100644 --- a/src/util/pipes/filterNull.pipe.ts +++ b/src/util/pipes/filterNull.pipe.ts @@ -5,7 +5,7 @@ import { Pipe, PipeTransform } from "@angular/core"; }) export class FilterNullPipe implements PipeTransform { - public transform(arr: any[]) { - return (arr && arr.filter(obj => obj !== null)) || [] + public transform(arr: any[], fn?: (item: any) => boolean) { + return (arr && arr.filter(obj => fn ? fn(obj) : obj !== null)) || [] } } diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts index 0e0f2a21b4abf73f4faabcd3598c32bdffdb8712..25668f675b7822edc9ea061ee3ed1b9e9d29b3e5 100644 --- a/src/util/pureConstant.service.ts +++ b/src/util/pureConstant.service.ts @@ -15,6 +15,7 @@ export class PureContantService implements OnDestroy{ public useTouchUI$: Observable<boolean> public fetchedAtlases$: Observable<any> + public darktheme$: Observable<boolean> public totalAtlasesLength: number @@ -25,8 +26,12 @@ export class PureContantService implements OnDestroy{ constructor( private store: Store<any>, - private http: HttpClient + private http: HttpClient, ){ + this.darktheme$ = this.store.pipe( + select(state => state?.viewerState?.templateSelected?.useTheme === 'dark') + ) + this.useTouchUI$ = this.store.pipe( select(this.useTouchUiSelector), shareReplay(1) @@ -37,7 +42,11 @@ export class PureContantService implements OnDestroy{ filter(v => !!v), tap((arr: any[]) => this.totalAtlasesLength = arr.length), switchMap(atlases => merge( - ...atlases.map(({ url }) => this.http.get(`${BACKENDURL.replace(/\/$/, '')}/${url}`, { responseType: 'json' })) + ...atlases.map(({ url }) => this.http.get( + /^http/.test(url) + ? url + : `${BACKENDURL.replace(/\/$/, '')}/${url}`, + { responseType: 'json' })) )), scan((acc, curr) => acc.concat(curr), []), shareReplay(1) diff --git a/src/util/util.module.ts b/src/util/util.module.ts index 3649ee9aac8708c8201e2e10a3a996f173c4f2c5..74bea90caf7c10bd18596c596344e03b4f94a490 100644 --- a/src/util/util.module.ts +++ b/src/util/util.module.ts @@ -14,6 +14,7 @@ import { SwitchDirective } from "./directives/switch.directive"; import { MediaQueryDirective } from './directives/mediaQuery.directive' import { LayoutModule } from "@angular/cdk/layout"; import { MapToPropertyPipe } from "./pipes/mapToProperty.pipe"; +import {ClickOutsideDirective} from "src/util/directives/clickOutside.directive"; @NgModule({ imports:[ @@ -32,7 +33,8 @@ import { MapToPropertyPipe } from "./pipes/mapToProperty.pipe"; NmToMm, SwitchDirective, MediaQueryDirective, - MapToPropertyPipe + MapToPropertyPipe, + ClickOutsideDirective ], exports: [ FilterNullPipe, @@ -47,7 +49,8 @@ import { MapToPropertyPipe } from "./pipes/mapToProperty.pipe"; NmToMm, SwitchDirective, MediaQueryDirective, - MapToPropertyPipe + MapToPropertyPipe, + ClickOutsideDirective ] }) diff --git a/typings/index.d.ts b/typings/index.d.ts index 843526a32106b0c44067b2f21c9701f00c99a29c..295f6b6846c87d1b3e7be1b46a8de83e8782459b 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -13,7 +13,6 @@ declare module '*.md' declare var VERSION : string declare var PRODUCTION: boolean declare var BACKEND_URL: string -declare var USE_LOGO: string declare var DATASET_PREVIEW_URL: string declare var MATOMO_URL: string declare var MATOMO_ID: string diff --git a/webpack.staticassets.js b/webpack.staticassets.js index c06e675fa6504c9e72adf7e3ecdb9b7169575a7d..d88a1ff8b7d973dc8f78b5af0f738dc56f1ba60e 100644 --- a/webpack.staticassets.js +++ b/webpack.staticassets.js @@ -70,7 +70,6 @@ module.exports = { SPATIAL_TRANSFORM_BACKEND: JSON.stringify(process.env.SPATIAL_TRANSFORM_BACKEND || 'https://hbp-spatial-backend.apps.hbp.eu'), MATOMO_URL: JSON.stringify(process.env.MATOMO_URL || null), MATOMO_ID: JSON.stringify(process.env.MATOMO_ID || null), - USE_LOGO: JSON.stringify(process.env.USE_LOGO || 'hbp' || 'ebrains' || 'fzj'), // strick local hides "explore" and "download" btns, which requires internet STRICT_LOCAL: process.env.STRICT_LOCAL === 'true' ? 'true' : 'false',