AWV-3: Merging changes from upstream

This commit is contained in:
Bruno Alves de Faria 2016-09-03 14:47:39 -03:00
commit 176b503c5a
153 changed files with 2377 additions and 1125 deletions

View File

@ -13,3 +13,4 @@ notices-for-facebook-graph-api-2
1.3.0-split-minifiers-package 1.3.0-split-minifiers-package
1.3.5-remove-old-dev-bundle-link 1.3.5-remove-old-dev-bundle-link
1.4.0-remove-old-dev-bundle-link 1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package

View File

@ -4,18 +4,18 @@
# 'meteor add' and 'meteor remove' will edit this file for you, # 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand. # but you can also edit it by hand.
npm-bcrypt@0.8.7 npm-bcrypt@0.9.1
meteor-base@1.0.4 # Packages every Meteor app needs to have meteor-base@1.0.4 # Packages every Meteor app needs to have
mobile-experience@1.0.4 # Packages for a great mobile UX mobile-experience@1.0.4 # Packages for a great mobile UX
mongo@1.1.10 # The database Meteor supports right now mongo@1.1.12 # The database Meteor supports right now
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
session@1.1.6 # Client-side reactive dictionary for your app session@1.1.6 # Client-side reactive dictionary for your app
jquery@1.11.9 # Helpful client-side library jquery@1.11.9 # Helpful client-side library
tracker@1.1.0 # Meteor's client-side reactive programming library tracker@1.1.0 # Meteor's client-side reactive programming library
es5-shim@4.6.13 # ECMAScript 5 compatibility for older browsers. es5-shim@4.6.14 # ECMAScript 5 compatibility for older browsers.
ecmascript@0.5.7 # Enable ECMAScript2015+ syntax in app code ecmascript@0.5.8 # Enable ECMAScript2015+ syntax in app code
clinical:router clinical:router
clinical:extended-api clinical:extended-api
@ -26,6 +26,8 @@ clinical:fonts
clinical:hipaa-audit-log clinical:hipaa-audit-log
clinical:hipaa-logger clinical:hipaa-logger
aldeed:simple-schema # Third party package to deal with schemas
design design
ohif:core ohif:core
@ -34,15 +36,15 @@ cornerstone
worklist worklist
viewerbase viewerbase
dicomweb dicomweb
accounts-base@1.2.9 accounts-base@1.2.11
accounts-password@1.2.12 accounts-password@1.3.0
ian:accounts-ui-bootstrap-3 ian:accounts-ui-bootstrap-3
twbs:bootstrap twbs:bootstrap
http@1.2.8 http@1.2.9
arsnebula:reactive-promise arsnebula:reactive-promise
promise@0.8.3 promise@0.8.4
stylus@2.513.4 stylus@2.513.5
fortawesome:fontawesome fortawesome:fontawesome
random@1.0.10 random@1.0.10
reactive-var@1.0.10 reactive-var@1.0.10
@ -56,11 +58,13 @@ check@1.2.3
aldeed:template-extension@4.0.0 aldeed:template-extension@4.0.0
zuuk:stale-session zuuk:stale-session
gilbertwat:bootstrap3-daterangepicker gilbertwat:bootstrap3-daterangepicker
email@1.1.16 email@1.1.17
peppelg:bootstrap-3-modal peppelg:bootstrap-3-modal
simple:reactive-method simple:reactive-method
hangingprotocols hangingprotocols
fastclick@1.0.12 fastclick@1.0.12
standard-minifier-css@1.1.8 standard-minifier-css@1.2.0
standard-minifier-js@1.1.8 standard-minifier-js@1.2.0
johdirr:meteor-git-rev johdirr:meteor-git-rev
u2622:persistent-session
shell-server

View File

@ -1 +1 @@
METEOR@1.4.0.1 METEOR@1.4.1.1

View File

@ -1,21 +1,22 @@
accounts-base@1.2.9 accounts-base@1.2.11
accounts-password@1.2.12 accounts-password@1.3.0
aldeed:simple-schema@1.5.3 aldeed:simple-schema@1.5.3
aldeed:template-extension@4.0.0 aldeed:template-extension@4.0.0
allow-deny@1.0.5 allow-deny@1.0.5
amplify@1.0.0
anti:gagarin@0.4.11 anti:gagarin@0.4.11
anti:i18n@0.4.3 anti:i18n@0.4.3
arsnebula:reactive-promise@0.9.1 arsnebula:reactive-promise@0.9.1
autoupdate@1.3.11 autoupdate@1.3.11
babel-compiler@6.9.0 babel-compiler@6.9.1
babel-runtime@0.1.10 babel-runtime@0.1.11
base64@1.0.9 base64@1.0.9
binary-heap@1.0.9 binary-heap@1.0.9
blaze@2.1.8 blaze@2.1.8
blaze-html-templates@1.0.4 blaze-html-templates@1.0.4
blaze-tools@1.0.9 blaze-tools@1.0.9
boilerplate-generator@1.0.9 boilerplate-generator@1.0.9
caching-compiler@1.1.6 caching-compiler@1.1.7
caching-html-compiler@1.0.6 caching-html-compiler@1.0.6
callback-hook@1.0.9 callback-hook@1.0.9
check@1.2.3 check@1.2.3
@ -31,23 +32,23 @@ clinical:router-location@2.1.0
clinical:router-middleware-stack@2.1.2 clinical:router-middleware-stack@2.1.2
clinical:router-url@2.1.0 clinical:router-url@2.1.0
clinical:theming@0.4.10 clinical:theming@0.4.10
coffeescript@1.2.3 coffeescript@1.2.4_1
cornerstone@0.0.1 cornerstone@0.0.1
ddp@1.2.5 ddp@1.2.5
ddp-client@1.3.0 ddp-client@1.3.1
ddp-common@1.2.6 ddp-common@1.2.6
ddp-rate-limiter@1.0.5 ddp-rate-limiter@1.0.5
ddp-server@1.3.9 ddp-server@1.3.10
deps@1.0.12 deps@1.0.12
design@0.0.1 design@0.0.1
dicomweb@0.0.1 dicomweb@0.0.1
diff-sequence@1.0.6 diff-sequence@1.0.6
dimseservice@0.0.1 dimseservice@0.0.1
ecmascript@0.5.7 ecmascript@0.5.8
ecmascript-runtime@0.3.12 ecmascript-runtime@0.3.14
ejson@1.0.12 ejson@1.0.12
email@1.1.16 email@1.1.17
es5-shim@4.6.13 es5-shim@4.6.14
fastclick@1.0.12 fastclick@1.0.12
fortawesome:fontawesome@4.5.0 fortawesome:fontawesome@4.5.0
geojson-utils@1.0.9 geojson-utils@1.0.9
@ -57,7 +58,7 @@ hangingprotocols@0.0.1
hot-code-push@1.0.4 hot-code-push@1.0.4
html-tools@1.0.10 html-tools@1.0.10
htmljs@1.0.10 htmljs@1.0.10
http@1.2.8 http@1.2.9
ian:accounts-ui-bootstrap-3@1.2.89 ian:accounts-ui-bootstrap-3@1.2.89
id-map@1.0.8 id-map@1.0.8
insecure@1.0.7 insecure@1.0.7
@ -71,25 +72,25 @@ launch-screen@1.0.12
lesiontracker@0.0.1 lesiontracker@0.0.1
livedata@1.0.18 livedata@1.0.18
localstorage@1.0.11 localstorage@1.0.11
logging@1.1.14 logging@1.1.15
mdg:validation-error@0.5.1 mdg:validation-error@0.5.1
meteor@1.2.16 meteor@1.2.17
meteor-base@1.0.4 meteor-base@1.0.4
meteor-platform@1.2.6 meteor-platform@1.2.6
minifier-css@1.2.13 minifier-css@1.2.14
minifier-js@1.2.13 minifier-js@1.2.14
minimongo@1.0.17 minimongo@1.0.17
mobile-experience@1.0.4 mobile-experience@1.0.4
mobile-status-bar@1.0.12 mobile-status-bar@1.0.12
modules@0.7.5 modules@0.7.6
modules-runtime@0.7.5 modules-runtime@0.7.6
momentjs:moment@2.14.4 momentjs:moment@2.14.4
mongo@1.1.10 mongo@1.1.12
mongo-id@1.0.5 mongo-id@1.0.5
mrt:moment@2.8.1 mrt:moment@2.8.1
natestrauser:select2@4.0.2 natestrauser:select2@4.0.3
npm-bcrypt@0.8.7 npm-bcrypt@0.9.1
npm-mongo@1.5.45 npm-mongo@1.5.49
observe-sequence@1.0.12 observe-sequence@1.0.12
ohif:core@0.0.1 ohif:core@0.0.1
ordered-dict@1.0.8 ordered-dict@1.0.8
@ -97,7 +98,7 @@ orthanc-remote@0.0.1
peppelg:bootstrap-3-modal@1.0.4 peppelg:bootstrap-3-modal@1.0.4
practicalmeteor:chai@2.1.0_1 practicalmeteor:chai@2.1.0_1
practicalmeteor:loglevel@1.2.0_2 practicalmeteor:loglevel@1.2.0_2
promise@0.8.3 promise@0.8.4
random@1.0.10 random@1.0.10
rate-limit@1.0.5 rate-limit@1.0.5
reactive-dict@1.1.8 reactive-dict@1.1.8
@ -109,27 +110,30 @@ rwatts:uuid@0.1.0
service-configuration@1.0.10 service-configuration@1.0.10
session@1.1.6 session@1.1.6
sha@1.0.8 sha@1.0.8
shell-server@0.2.1
silentcicero:jszip@0.0.4 silentcicero:jszip@0.0.4
simple:reactive-method@1.0.2 simple:reactive-method@1.0.2
spacebars@1.0.12 spacebars@1.0.12
spacebars-compiler@1.0.12 spacebars-compiler@1.0.12
srp@1.0.9 srp@1.0.9
standard-app-packages@1.0.9 standard-app-packages@1.0.9
standard-minifier-css@1.1.8 standard-minifier-css@1.2.0
standard-minifier-js@1.1.8 standard-minifier-js@1.2.0
stylus@2.513.4 stylus@2.513.5
templating@1.2.13 templating@1.2.14
templating-tools@1.0.4 templating-tools@1.0.4
tracker@1.1.0 tracker@1.1.0
twbs:bootstrap@3.3.6 twbs:bootstrap@3.3.6
typ:accounts-ldap@1.0.1 typ:accounts-ldap@1.0.1
typ:ldapjs@0.7.3 typ:ldapjs@0.7.3
u2622:persistent-session@0.4.4
ui@1.0.11 ui@1.0.11
underscore@1.0.9 underscore@1.0.9
url@1.0.10 url@1.0.10
validatejs@0.0.1 validatejs@0.0.1
viewerbase@0.0.1 viewerbase@0.0.1
webapp@1.3.10 wadoproxy@0.0.1
webapp@1.3.11
webapp-hashing@1.0.9 webapp-hashing@1.0.9
worklist@0.0.1 worklist@0.0.1
zuuk:stale-session@1.0.8 zuuk:stale-session@1.0.8

View File

@ -16,7 +16,8 @@ var data = {
'optionsModal', 'optionsModal',
'serverInformationModal', 'serverInformationModal',
'confirmRemoveTimepointAssociation', 'confirmRemoveTimepointAssociation',
'lastLoginModal' 'lastLoginModal',
'viewSeriesDetailsModal'
] ]
}; };

View File

@ -13,3 +13,4 @@ notices-for-facebook-graph-api-2
1.3.0-split-minifiers-package 1.3.0-split-minifiers-package
1.3.5-remove-old-dev-bundle-link 1.3.5-remove-old-dev-bundle-link
1.4.0-remove-old-dev-bundle-link 1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package

View File

@ -5,20 +5,20 @@
# but you can also edit it by hand. # but you can also edit it by hand.
twbs:bootstrap twbs:bootstrap
http@1.2.8 http@1.2.9
arsnebula:reactive-promise arsnebula:reactive-promise
promise@0.8.3 promise@0.8.4
stylus@2.513.4 stylus@2.513.5
fortawesome:fontawesome fortawesome:fontawesome
mrt:moment mrt:moment
meteor-base@1.0.4 meteor-base@1.0.4
mobile-experience@1.0.4 mobile-experience@1.0.4
mongo@1.1.10 mongo@1.1.12
blaze-html-templates@1.0.4 blaze-html-templates@1.0.4
session@1.1.6 session@1.1.6
jquery@1.11.9 jquery@1.11.9
tracker@1.1.0 tracker@1.1.0
logging@1.1.14 logging@1.1.15
reload@1.1.10 reload@1.1.10
random@1.0.10 random@1.0.10
ejson@1.0.12 ejson@1.0.12
@ -26,9 +26,10 @@ spacebars@1.0.12
check@1.2.3 check@1.2.3
cornerstone cornerstone
dicomweb dicomweb
aldeed:simple-schema # Third party package to deal with schemas
design design
ohif:core ohif:core
ecmascript@0.5.7 ecmascript@0.5.8
viewerbase viewerbase
worklist worklist
reactive-var@1.0.10 reactive-var@1.0.10
@ -37,7 +38,10 @@ hangingprotocols
reactive-dict@1.1.8 reactive-dict@1.1.8
clinical:router clinical:router
fastclick@1.0.12 fastclick@1.0.12
standard-minifier-css@1.1.8 standard-minifier-css@1.2.0
standard-minifier-js@1.1.8 standard-minifier-js@1.2.0
aldeed:simple-schema
johdirr:meteor-git-rev johdirr:meteor-git-rev
wadoproxy
aldeed:template-extension
peppelg:bootstrap-3-modal
shell-server

View File

@ -1 +1 @@
METEOR@1.4.0.1 METEOR@1.4.1.1

View File

@ -1,16 +1,17 @@
aldeed:simple-schema@1.5.3 aldeed:simple-schema@1.5.3
aldeed:template-extension@4.0.0
allow-deny@1.0.5 allow-deny@1.0.5
arsnebula:reactive-promise@0.9.1 arsnebula:reactive-promise@0.9.1
autoupdate@1.3.11 autoupdate@1.3.11
babel-compiler@6.9.0 babel-compiler@6.9.1
babel-runtime@0.1.10 babel-runtime@0.1.11
base64@1.0.9 base64@1.0.9
binary-heap@1.0.9 binary-heap@1.0.9
blaze@2.1.8 blaze@2.1.8
blaze-html-templates@1.0.4 blaze-html-templates@1.0.4
blaze-tools@1.0.9 blaze-tools@1.0.9
boilerplate-generator@1.0.9 boilerplate-generator@1.0.9
caching-compiler@1.1.6 caching-compiler@1.1.7
caching-html-compiler@1.0.6 caching-html-compiler@1.0.6
callback-hook@1.0.9 callback-hook@1.0.9
check@1.2.3 check@1.2.3
@ -18,19 +19,19 @@ clinical:router@2.0.19
clinical:router-location@2.1.0 clinical:router-location@2.1.0
clinical:router-middleware-stack@2.1.2 clinical:router-middleware-stack@2.1.2
clinical:router-url@2.1.0 clinical:router-url@2.1.0
coffeescript@1.2.3 coffeescript@1.2.4_1
cornerstone@0.0.1 cornerstone@0.0.1
ddp@1.2.5 ddp@1.2.5
ddp-client@1.3.0 ddp-client@1.3.1
ddp-common@1.2.6 ddp-common@1.2.6
ddp-server@1.3.9 ddp-server@1.3.10
deps@1.0.12 deps@1.0.12
design@0.0.1 design@0.0.1
dicomweb@0.0.1 dicomweb@0.0.1
diff-sequence@1.0.6 diff-sequence@1.0.6
dimseservice@0.0.1 dimseservice@0.0.1
ecmascript@0.5.7 ecmascript@0.5.8
ecmascript-runtime@0.3.12 ecmascript-runtime@0.3.14
ejson@1.0.12 ejson@1.0.12
fastclick@1.0.12 fastclick@1.0.12
fortawesome:fontawesome@4.5.0 fortawesome:fontawesome@4.5.0
@ -40,7 +41,7 @@ hangingprotocols@0.0.1
hot-code-push@1.0.4 hot-code-push@1.0.4
html-tools@1.0.10 html-tools@1.0.10
htmljs@1.0.10 htmljs@1.0.10
http@1.2.8 http@1.2.9
id-map@1.0.8 id-map@1.0.8
iron:controller@1.0.12 iron:controller@1.0.12
iron:core@1.0.11 iron:core@1.0.11
@ -50,31 +51,32 @@ johdirr:meteor-git-rev@0.0.4
jquery@1.11.9 jquery@1.11.9
launch-screen@1.0.12 launch-screen@1.0.12
livedata@1.0.18 livedata@1.0.18
logging@1.1.14 logging@1.1.15
mdg:validation-error@0.2.0 mdg:validation-error@0.5.1
meteor@1.2.16 meteor@1.2.17
meteor-base@1.0.4 meteor-base@1.0.4
meteor-platform@1.2.6 meteor-platform@1.2.6
minifier-css@1.2.13 minifier-css@1.2.14
minifier-js@1.2.13 minifier-js@1.2.14
minimongo@1.0.17 minimongo@1.0.17
mobile-experience@1.0.4 mobile-experience@1.0.4
mobile-status-bar@1.0.12 mobile-status-bar@1.0.12
modules@0.7.5 modules@0.7.6
modules-runtime@0.7.5 modules-runtime@0.7.6
momentjs:moment@2.14.4 momentjs:moment@2.14.4
mongo@1.1.10 mongo@1.1.12
mongo-id@1.0.5 mongo-id@1.0.5
mrt:moment@2.8.1 mrt:moment@2.8.1
natestrauser:select2@4.0.2 natestrauser:select2@4.0.3
npm-mongo@1.5.45 npm-mongo@1.5.49
observe-sequence@1.0.12 observe-sequence@1.0.12
ohif:core@0.0.1 ohif:core@0.0.1
ordered-dict@1.0.8 ordered-dict@1.0.8
orthanc-remote@0.0.1 orthanc-remote@0.0.1
peppelg:bootstrap-3-modal@1.0.4
practicalmeteor:chai@2.1.0_1 practicalmeteor:chai@2.1.0_1
practicalmeteor:loglevel@1.2.0_2 practicalmeteor:loglevel@1.2.0_2
promise@0.8.3 promise@0.8.4
random@1.0.10 random@1.0.10
reactive-dict@1.1.8 reactive-dict@1.1.8
reactive-var@1.0.10 reactive-var@1.0.10
@ -83,14 +85,15 @@ retry@1.0.8
routepolicy@1.0.11 routepolicy@1.0.11
rwatts:uuid@0.1.0 rwatts:uuid@0.1.0
session@1.1.6 session@1.1.6
shell-server@0.2.1
silentcicero:jszip@0.0.4 silentcicero:jszip@0.0.4
spacebars@1.0.12 spacebars@1.0.12
spacebars-compiler@1.0.12 spacebars-compiler@1.0.12
standard-app-packages@1.0.9 standard-app-packages@1.0.9
standard-minifier-css@1.1.8 standard-minifier-css@1.2.0
standard-minifier-js@1.1.8 standard-minifier-js@1.2.0
stylus@2.513.4 stylus@2.513.5
templating@1.2.13 templating@1.2.14
templating-tools@1.0.4 templating-tools@1.0.4
tracker@1.1.0 tracker@1.1.0
twbs:bootstrap@3.3.6 twbs:bootstrap@3.3.6
@ -99,6 +102,7 @@ underscore@1.0.9
url@1.0.10 url@1.0.10
validatejs@0.0.1 validatejs@0.0.1
viewerbase@0.0.1 viewerbase@0.0.1
webapp@1.3.10 wadoproxy@0.0.1
webapp@1.3.11
webapp-hashing@1.0.9 webapp-hashing@1.0.9
worklist@0.0.1 worklist@0.0.1

View File

@ -27,11 +27,11 @@ $lesionsSidebarMenuWidth = 450px
transform(translateX(0%)) transform(translateX(0%))
.sidebar-left .sidebar-left
max-width: $studiesSidebarMenuWidth theme('border-right', '%s solid $uiBorderColor' % $uiBorderThickness)
order: 1
flex: 1 flex: 1
margin-left: - $studiesSidebarMenuWidth margin-left: - $studiesSidebarMenuWidth
border-right: $uiBorderThickness $uiBorderColor solid max-width: $studiesSidebarMenuWidth
order: 1
&.sidebar-open &.sidebar-open
margin-left: 0 margin-left: 0
@ -62,7 +62,7 @@ $lesionsSidebarMenuWidth = 450px
max-width: $lesionsSidebarMenuWidth + 270px max-width: $lesionsSidebarMenuWidth + 270px
&:before &:before
background-color: $uiBorderColor theme('background-color', '$uiBorderColor')
content: '' content: ''
display: block display: block
height: 100% height: 100%
@ -75,6 +75,6 @@ $lesionsSidebarMenuWidth = 450px
margin-right: 0 margin-right: 0
.studiesListedChanger .studiesListedChanger
theme('border-bottom', '%s solid $uiBorderColor' % $uiBorderThickness)
padding: 20px 10px padding: 20px 10px
text-align: center text-align: center
border-bottom: $uiBorderThickness $uiBorderColor solid

View File

@ -25,4 +25,5 @@
</div> </div>
{{> worklist }} {{> worklist }}
{{> aboutModal }} {{> aboutModal }}
{{> viewSeriesDetailsModal }}
</template> </template>

View File

@ -9,8 +9,8 @@ $expandedHeight = 160px
.topBar .topBar
height: $topBarHeight height: $topBarHeight
color: $textPrimaryColor theme('color', '$textPrimaryColor')
background-color: $primaryBackgroundColor theme('background-color', '$primaryBackgroundColor')
transition(all 0.5s ease) transition(all 0.5s ease)
.brandSection .brandSection
@ -26,14 +26,14 @@ $expandedHeight = 160px
display: inline-block display: inline-block
font-family: $logoFontFamily font-family: $logoFontFamily
font-weight: $logoFontWeight font-weight: $logoFontWeight
color: $textPrimaryColor theme('color', '$textPrimaryColor')
line-height: 30px line-height: 30px
.studyListLinkSection .studyListLinkSection
border-left: $uiBorderThickness solid $uiBorderColor; theme('border-left', '%s solid $uiBorderColor' % $uiBorderThickness)
.btn .btn
color: $textSecondaryColor theme('color', '$textSecondaryColor')
cursor: pointer cursor: pointer
font-size: 13px font-size: 13px
font-weight: 500 font-weight: 500
@ -41,10 +41,10 @@ $expandedHeight = 160px
margin-top: 3px margin-top: 3px
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')
&.studyList &.studyList
background-color: rgba(21, 25, 30, 0.7) background-color: rgba(21, 25, 30, 0.7)
@ -75,5 +75,5 @@ $expandedHeight = 160px
.tab-pane .tab-pane
.viewerContainer .viewerContainer
.loadingTextDiv .loadingTextDiv
color: $textSecondaryColor theme('color', '$textSecondaryColor')
font-size: 30px font-size: 30px

View File

@ -1,7 +1,7 @@
@import "{design}/app" @import "{design}/app"
.toolbarSection .toolbarSection
border-bottom: $uiBorderColor $uiBorderThickness solid theme('border-bottom', '%s solid $uiBorderColor' % $uiBorderThickness)
flex: 0 0 auto flex: 0 0 auto
height: $toolbarHeight height: $toolbarHeight
overflow: hidden overflow: hidden
@ -11,4 +11,4 @@
width: 100% width: 100%
&.expanded &.expanded
height: $toolbarHeight + $toolbarDrawerHeight height: $toolbarHeight + $toolbarDrawerHeight

View File

@ -6,5 +6,5 @@
width: 100% width: 100%
.loadingTextDiv .loadingTextDiv
color: $textSecondaryColor theme('color', '$textSecondaryColor')
font-size: 30px font-size: 30px

View File

@ -26,7 +26,8 @@ Package.onUse(function (api) {
api.use([ api.use([
'zuuk:stale-session@1.0.8', 'zuuk:stale-session@1.0.8',
'typ:accounts-ldap' 'typ:accounts-ldap',
'random'
], ['client', 'server']); ], ['client', 'server']);
api.addFiles([ api.addFiles([

View File

@ -1,7 +1,7 @@
@import "{design}/app" @import "{design}/app"
$height = 25px $height = 25px
$textColorActive = #2D2D2D $textColorButtonActive = #2D2D2D
.roundedButtonGroup .roundedButtonGroup
@ -15,9 +15,9 @@ $textColorActive = #2D2D2D
.roundedButton .roundedButton
align-items: center align-items: center
background-color: $uiGrayDark theme('background-color', '$uiGrayDark')
border: 2px $uiBorderColorDark solid theme('border', '2px solid $uiBorderColorDark')
color: $textSecondaryColor theme('color', '$textSecondaryColor')
display: flex display: flex
font-size: 11px font-size: 11px
font-weight: 500 font-weight: 500
@ -29,19 +29,19 @@ $textColorActive = #2D2D2D
transition: transition($sidebarTransition) transition: transition($sidebarTransition)
svg svg
fill: $defaultColor theme('fill', '$defaultColor')
stroke: $defaultColor theme('stroke', '$defaultColor')
svg svg
span span
margin: 0 2px margin: 0 2px
i i
line-height: 15px line-height: 15px
font-size: 15px font-size: 15px
.bottomLabel .bottomLabel
color: $textSecondaryColor theme('color', '$textSecondaryColor')
font-size: 12px font-size: 12px
font-weight: 500 font-weight: 500
line-height: 12px line-height: 12px
@ -58,16 +58,16 @@ $textColorActive = #2D2D2D
border-top-right-radius: $height border-top-right-radius: $height
&:hover .roundedButton &:hover .roundedButton
background-color: $boxBackgroundColor theme('color', '$uiGrayDark')
color: $uiGrayDark theme('background-color', '$boxBackgroundColor')
&.active .roundedButton &.active .roundedButton
background-color: $activeColor theme('background-color', '$activeColor')
border-color: $uiBorderColorActive theme('border-color', '$uiBorderColorActive')
color: $textColorActive color: $textColorButtonActive
&:hover .roundedButton &:hover .roundedButton
&.active .roundedButton &.active .roundedButton
svg svg
fill: $uiGrayDark theme('fill', '$uiGrayDark')
stroke: $uiGrayDark theme('stroke', '$uiGrayDark')

View File

@ -18,7 +18,9 @@ Package.onUse(function(api) {
'styles/imports/mixins.styl', 'styles/imports/mixins.styl',
'styles/imports/spacings.styl', 'styles/imports/spacings.styl',
'styles/imports/variables.styl', 'styles/imports/variables.styl',
'styles/imports/theming.styl',
'styles/imports/theme-tide.styl', 'styles/imports/theme-tide.styl',
'styles/imports/theme-tigerlilly.styl'
], 'client', { ], 'client', {
isImport: true isImport: true
}); });
@ -35,7 +37,8 @@ Package.onUse(function(api) {
api.addFiles([ api.addFiles([
'styles/components/radio.styl', 'styles/components/radio.styl',
'styles/components/select2.styl', 'styles/components/select2.styl',
'styles/components/selectTree.styl' 'styles/components/selectTree.styl',
'styles/components/states.styl'
], 'client'); ], 'client');
// Rounded Button Group // Rounded Button Group

View File

@ -2,11 +2,26 @@
html body html body
font-family: 'Roboto', 'OpenSans', 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif font-family: 'Roboto', 'OpenSans', 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif
// Try to fix LT-273, need a monitor to test on though.
//text-rendering: optimizeLegibility
html hr html hr
border-top: 1px solid $uiBorderColor theme('border-top', '1px solid $uiBorderColor')
label.form-group
width: 100%
&>span.select2
display: block
width: 100% !important
span.select2-selection
min-height: 34px
span.select2-selection__rendered
height: 34px
line-height: 34px
span.select2-selection__arrow
height: 34px
.center-table .center-table
display: table display: table
@ -32,9 +47,9 @@ html hr
.modal-dialog .modal-dialog
.modal-content .modal-content
background: $primaryBackgroundColor theme('background', '$primaryBackgroundColor')
border-color: $uiBorderColor theme('border-color', '$uiBorderColor')
color: $textSecondaryColor theme('color', '$textSecondaryColor')
.modal-header, .modal-footer .modal-header, .modal-footer
border-color: $uiBorderColor theme('border-color', '$uiBorderColor')

View File

@ -3,12 +3,12 @@
.group-radio .group-radio
label label
cursor: pointer cursor: pointer
input input
height: 0 height: 0
width: 0 width: 0
visibility: hidden visibility: hidden
span span
padding-left: 23px padding-left: 23px
position: relative position: relative
@ -23,14 +23,14 @@
top: 50% top: 50%
transform(translateY(-50%)) transform(translateY(-50%))
width: 16px width: 16px
input:focus + span:before input:focus + span:before
// TODO: [design] define a outline for the design // TODO: [design] define a outline for the design
theme('box-shadow', '0 0 2px 2px $textSecondaryColor')
outline: none outline: none
box-shadow: 0 0 2px 2px $textSecondaryColor
input:checked + span:after input:checked + span:after
background: $uiBorderColorDark theme('background', '$uiBorderColorDark')
border-radius: 5px border-radius: 5px
content: '' content: ''
height: 10px height: 10px

View File

@ -22,8 +22,8 @@ span.select2.select2-container
padding-left: 11px padding-left: 11px
span.select2-selection__arrow span.select2-selection__arrow
theme('border-left', '1px solid $primaryBackgroundColor')
background-color: $gray1 background-color: $gray1
border-left: 1px solid $primaryBackgroundColor
border-top-right-radius: $borderRadius border-top-right-radius: $borderRadius
border-bottom-right-radius: $borderRadius border-bottom-right-radius: $borderRadius
right: 0 right: 0
@ -42,8 +42,8 @@ span.select2.select2-container
placeholder-color($gray4) placeholder-color($gray4)
li.select2-selection__choice li.select2-selection__choice
theme('border-color', '$uiGray')
background-color: #FFF background-color: #FFF
border-color: $uiGray
border-radius: $borderRadius border-radius: $borderRadius
color: $gray4 color: $gray4
font-size: 12px font-size: 12px

View File

@ -76,6 +76,7 @@ $gray6 = #303030
display: block display: block
margin: 0 margin: 0
padding: 5px padding: 5px
position: relative
text-align: center text-align: center
span span
@ -85,6 +86,14 @@ $gray6 = #303030
input input
border-color: $gray5 border-color: $gray5
font-weight: normal font-weight: normal
padding-left: 34px
& ~ i
color: $gray1
font-size: 18px
left: 16px
position: absolute
top: 37px
.tree-breadcrumb .tree-breadcrumb
line-height: 32px line-height: 32px
@ -146,7 +155,6 @@ $gray6 = #303030
&.active &.active
box-shadow: 0 0 0 200px white, inset 0 0 0 200px white box-shadow: 0 0 0 200px white, inset 0 0 0 200px white
position: fixed
transition(box-shadow 0.3s ease) transition(box-shadow 0.3s ease)
z-index: 0 z-index: 0
@ -172,7 +180,7 @@ $gray6 = #303030
border-radius: 5px border-radius: 5px
color: white color: white
opacity: 0.9 opacity: 0.9
padding: 5px 12px 12px padding: 0 15px 12px
position: absolute position: absolute
top: 50% top: 50%
transform(translateY(-50%)) transform(translateY(-50%))
@ -180,13 +188,21 @@ $gray6 = #303030
h5.title h5.title
color: $gray5 color: $gray5
font-size: 12px
font-weight: normal font-weight: normal
line-height: 20px line-height: 20px
margin: 0 margin: 12px 0 16px
label label
cursor: pointer
display: block display: block
font-weight: 300
line-height: 26px
margin: 0
padding-left: 3px
transition(padding 0.3s ease)
&:hover
padding-left: 0
.select-tree-root .select-tree-root
@ -218,7 +234,7 @@ $gray6 = #303030
.content .content
margin-left: 0 margin-left: 0
&.interacted &.interacted, &.selected
.select-tree-common .select-tree-common
z-index: 1 z-index: 1

View File

@ -0,0 +1,29 @@
@import "{design}/app"
.state-error
&+.tooltip
.tooltip-inner
color: white
theme('background-color', '$uiStateErrorBorder')
&.top .tooltip-arrow
theme('border-top-color', '$uiStateErrorBorder')
&.right .tooltip-arrow
theme('border-right-color', '$uiStateErrorBorder')
&.bottom .tooltip-arrow
theme('border-bottom-color', '$uiStateErrorBorder')
&.left .tooltip-arrow
theme('border-left-color', '$uiStateErrorBorder')
&:not(.component-group)
&.form-control, .form-control
theme('background-color', '$uiStateError')
theme('border-color', '$uiStateErrorBorder')
theme('color', '$uiStateErrorText')
.select2-selection
theme('background-color', '$uiStateError')
theme('border-color', '$uiStateErrorBorder')
theme('color', '$uiStateErrorText')

View File

@ -1,54 +1,34 @@
// Common pallete $themes['tide'] = {
$uiYellow = #E29E4A // Common pallete
$uiSkyBlue = #6FBDE2 $uiYellow: #E29E4A
$uiSkyBlue: #6FBDE2
$uiLightGray = #516873 // State pallete
$uiGray = #263340 $uiStateError: #FFCCCC
$uiGrayDark = #16202B $uiStateErrorBorder: #993333
$uiGrayDarker = #151A1F $uiStateErrorText: #661111
$uiGrayDarkest = #14191E
// Interface UI Colors $uiLightGray: #516873
$defaultColor = #9cd0fe $uiGray: #263340
$hoverColor = #ffffff $uiGrayDark: #16202B
$activeColor = #00a4d9 $uiGrayDarker: #151A1F
$uiBorderColor = #436270 $uiGrayDarkest: #14191E
$uiBorderColorDark = #3C5D80
$uiBorderColorActive = #00a4d9
$primaryBackgroundColor = #000000
$boxBackgroundColor = #344a61
$boxBackgroundColorDark = #22374D
// Text Colors // Interface UI Colors
$textColorActive = black //#89bae5 $defaultColor: #9cd0fe
$textPrimaryColor = #ffffff $hoverColor: #ffffff
$textSecondaryColor = #6a8fb1 $activeColor: #00a4d9
$uiBorderColor: #436270
$uiBorderColorDark: #3C5D80
$uiBorderColorActive: #00a4d9
$primaryBackgroundColor: #000000
$boxBackgroundColor: #344a61
// Other Colors // Text Colors
$menuBackgroundColor = #F2F2F2 $textColorActive: black //#89bae5
$imageSliderColor = #163239 $textPrimaryColor: #ffffff
$textSecondaryColor: #6a8fb1
// Viewport Settings // Other Colors
$viewportBorderThickness = 1px $imageSliderColor: #163239
$viewportBorderColor = $uiBorderColorDark }
$viewportBorderColorHover = $uiBorderColor
$viewportBorderColorActive = $uiBorderColorActive
// Sizes
$topBarHeight = 40px
$toolbarHeight = 78px
$toolbarDrawerHeight = 62px
// Thicknesses
$uiBorderThickness = 1px
// Fonts
$logoFontFamily = "Sanchez"
$logoFontWeight = 300
// Transitions
$transitionDuration = 0.3s
$transitionEffect = ease
$sidebarTransition = all $transitionDuration $transitionEffect
$studiesSidebarMenuWidth = 307px
$lesionsSidebarMenuWidth = 323px

View File

@ -1,54 +1,34 @@
// Common pallete $themes['tigerlilly'] = {
$uiYellow = #E29E4A // Common pallete
$uiSkyBlue = #6FBDE2 $uiYellow: #E29E4A
$uiSkyBlue: #6FBDE2
$uiLightGray = #516873 // State pallete
$uiGray = #263340 $uiStateError: #FFCCCC
$uiGrayDark = #16202B $uiStateErrorBorder: #CC6666
$uiGrayDarker = #151A1F $uiStateErrorText: #661111
$uiGrayDarkest = #14191E
// Interface UI Colors $uiLightGray: #516873
$defaultColor = #98ceff $uiGray: #263340
$hoverColor = #ffffff $uiGrayDark: #16202B
$activeColor = #ff8a3d $uiGrayDarker: #151A1F
$uiBorderColor = #744b71 $uiGrayDarkest: #14191E
$uiBorderColorDark = #744b71
$uiBorderColorActive = #ff8a3d
$primaryBackgroundColor = #000000
$boxBackgroundColor = #5b3a59
$boxBackgroundColorDark = #4b2f40
// Text Colors // Interface UI Colors
$textColorActive = #ff8a3d $defaultColor: #98ceff
$textPrimaryColor = #ffffff $hoverColor: #ffffff
$textSecondaryColor = #6a8fb1 $activeColor: #ff8a3d
$uiBorderColor: #744b71
$uiBorderColorDark: #744b71
$uiBorderColorActive: #ff8a3d
$primaryBackgroundColor: #000000
$boxBackgroundColor: #5b3a59
// Other Colors // Text Colors
$menuBackgroundColor = #F2F2F2 $textColorActive: black
$imageSliderColor = #4b2c3c //#163239 $textPrimaryColor: #ffffff
$textSecondaryColor: #6a8fb1
// Viewport Settings // Other Colors
$viewportBorderThickness = 1px $imageSliderColor: #4b2c3c //#163239
$viewportBorderColor = $uiBorderColorDark }
$viewportBorderColorHover = $uiBorderColor
$viewportBorderColorActive = $uiBorderColorActive
// Sizes
$topBarHeight = 40px
$toolbarHeight = 78px
$toolbarDrawerHeight = 62px
// Thicknesses
$uiBorderThickness = 1px
// Fonts
$logoFontFamily = "Sanchez"
$logoFontWeight = 300
// Transitions
$transitionDuration = 0.3s
$transitionEffect = ease
$sidebarTransition = all $transitionDuration $transitionEffect
$studiesSidebarMenuWidth = 307px
$lesionsSidebarMenuWidth = 323px

View File

@ -0,0 +1,32 @@
$themes = {}
$defaultTheme = 'tide'
@import "./theme-tide.styl"
@import "./theme-tigerlilly.styl"
parseSpaceVars($theme, $value)
$valueSplit = split(' ', $value)
$list = ''
pop($list)
for $property in $valueSplit
if ($theme[$property])
push($list, $theme[$property])
else
push($list, $property)
join(' ', $list)
parseCommaVars($theme, $value)
$valueSplit = split(',', $value)
$list = ''
pop($list)
for $sentence in $valueSplit
push($list, parseSpaceVars($theme, $sentence))
unquote(join(',', $list))
theme($property, $value)
/ {selector()}
{$property}: parseCommaVars($themes[$defaultTheme], $value)
for $themeName, $theme in $themes
for $selectorPiece in split(',', selector())
/ body.theme-{$themeName} {$selectorPiece}
{$property}: parseCommaVars($theme, $value)

View File

@ -1,2 +1,22 @@
//@import "./theme-tigerlilly.styl" @import "./theming.styl"
@import "./theme-tide.styl"
// Sizes
$topBarHeight = 40px
$toolbarHeight = 78px
$toolbarDrawerHeight = 62px
$studiesSidebarMenuWidth = 307px
$lesionsSidebarMenuWidth = 323px
// Fonts
$logoFontFamily = "Sanchez"
$logoFontWeight = 300
// Transitions
$transitionDuration = 0.3s
$transitionEffect = ease
$sidebarTransition = all $transitionDuration $transitionEffect
// Thicknesses
$viewportBorderThickness = 1px
$uiBorderThickness = 1px

View File

@ -3,12 +3,12 @@
$height = 20px $height = 20px
#protocolEditor #protocolEditor
theme('background', '$primaryBackgroundColor')
height: 100% height: 100%
width: 450px width: 450px
padding: 10px 0 padding: 10px 0
position: absolute position: absolute
top: 0 top: 0
background: $primaryBackgroundColor
.navigationButtons .navigationButtons
ul ul
@ -35,27 +35,27 @@ $height = 20px
cursor: pointer cursor: pointer
text-decoration: none text-decoration: none
outline: none outline: none
background-color: $uiGrayDark theme('background-color', '$uiGrayDark')
border: 2px $uiBorderColorDark solid theme('border', '2px solid $uiBorderColorDark')
color: $textSecondaryColor theme('color', '$textSecondaryColor')
transition: transition($sidebarTransition) transition: transition($sidebarTransition)
border-radius: $height border-radius: $height
&.active a &.active a
background-color: $activeColor theme('background-color', '$activeColor')
border-color: $uiBorderColorActive theme('border-color', '$uiBorderColorActive')
color: $textColorActive theme('color', '$textColorActive')
transition: transition($sidebarTransition) transition: transition($sidebarTransition)
p p
h2 h2
h3 h3
h4 h4
color: $defaultColor theme('color', '$defaultColor')
label label
ul ul
color: $defaultColor theme('color', '$defaultColor')
font-weight: 400 font-weight: 400
label label
@ -68,7 +68,7 @@ $height = 20px
width: 40% width: 40%
border: none border: none
background: #212121 background: #212121
color: $defaultColor theme('color', '$defaultColor')
text-align: center text-align: center
button.btn button.btn
@ -85,11 +85,11 @@ $height = 20px
bottom: 0 bottom: 0
width: 100% width: 100%
margin-bottom: 0 margin-bottom: 0
background: $uiGrayDarker theme('background', '$uiGrayDarker')
p p
font-size: 8pt font-size: 8pt
color: $defaultColor theme('color', '$defaultColor')
margin: 0 10px margin: 0 10px
display: inline-block display: inline-block
@ -107,11 +107,11 @@ $height = 20px
#selectProtocol #selectProtocol
text-align: center text-align: center
.btn-file .btn-file
position: relative position: relative
overflow: hidden overflow: hidden
.btn-file input[type=file] .btn-file input[type=file]
position: absolute position: absolute
top: 0 top: 0
right: 0 right: 0
@ -141,4 +141,4 @@ $height = 20px
padding: 40px padding: 40px
h3 h3
color: $defaultColor theme('color', '$defaultColor')

View File

@ -1,6 +1,9 @@
@import "{design}/app.styl" @import "{design}/app.styl"
.ruleEntryDialog .ruleEntryDialog
theme('background', '$uiGrayDarkest')
theme('border', '1px solid $uiBorderColor')
theme('color', '$textSecondaryColor')
display: none display: none
position: absolute position: absolute
top: 0 top: 0
@ -14,12 +17,8 @@
padding: 10px padding: 10px
background-color: rgba(255,255,255,1) background-color: rgba(255,255,255,1)
outline: none outline: none
opacity: 0.95 opacity: 0.95
border-radius: 8px border-radius: 8px
border: 1px solid $uiBorderColor
background: $uiGrayDarkest
color: $textSecondaryColor
.dialogContent .dialogContent
text-align: center text-align: center
@ -44,4 +43,4 @@
color: black color: black
height: 30px height: 30px
border-radius: 2px border-radius: 2px
border: 0 border: 0

View File

@ -4,7 +4,7 @@ table.ruleTable
thead thead
tr tr
th th
color: $defaultColor theme('color', '$defaultColor')
text-align: center text-align: center
th:first-child th:first-child
@ -13,7 +13,7 @@ table.ruleTable
tbody tbody
tr tr
td td
color: $defaultColor theme('color', '$defaultColor')
text-align: center text-align: center
.failWarning .failWarning
@ -26,10 +26,10 @@ table.ruleTable
transition(all 0.1s ease) transition(all 0.1s ease)
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')
td:first-child td:first-child
text-align: left text-align: left
@ -37,14 +37,14 @@ table.ruleTable
.addRuleContainer .addRuleContainer
margin: 10px 0 margin: 10px 0
text-align: center text-align: center
color: $defaultColor theme('color', '$defaultColor')
.addRule .addRule
cursor: pointer cursor: pointer
transition(all 0.1s ease) transition(all 0.1s ease)
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')

View File

@ -1,6 +1,9 @@
@import "{design}/app.styl" @import "{design}/app.styl"
.settingEntryDialog .settingEntryDialog
theme('border', '1px solid $uiBorderColor')
theme('background', '$uiGrayDarkest')
theme('color', '$textSecondaryColor')
display: none display: none
position: absolute position: absolute
top: 0 top: 0
@ -13,12 +16,8 @@
margin: auto margin: auto
padding: 10px padding: 10px
outline: none outline: none
opacity: 0.95 opacity: 0.95
border-radius: 8px border-radius: 8px
border: 1px solid $uiBorderColor
background: $uiGrayDarkest
color: $textSecondaryColor
.dialogContent .dialogContent
text-align: center text-align: center
@ -43,4 +42,4 @@
color: black color: black
height: 30px height: 30px
border-radius: 2px border-radius: 2px
border: 0 border: 0

View File

@ -4,7 +4,7 @@ table.settingsTable
thead thead
tr tr
th th
color: $defaultColor theme('color', '$defaultColor')
text-align: center text-align: center
th:first-child th:first-child
@ -15,7 +15,7 @@ table.settingsTable
tr tr
td td
color: $defaultColor theme('color', '$defaultColor')
text-align: center text-align: center
.editSetting .editSetting
@ -24,10 +24,10 @@ table.settingsTable
transition(all 0.1s ease) transition(all 0.1s ease)
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')
td:first-child td:first-child
text-align: left text-align: left
@ -35,14 +35,14 @@ table.settingsTable
.addSettingContainer .addSettingContainer
margin: 10px 0 margin: 10px 0
text-align: center text-align: center
color: $defaultColor theme('color', '$defaultColor')
.addSetting .addSetting
cursor: pointer cursor: pointer
transition(all 0.1s ease) transition(all 0.1s ease)
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')

View File

@ -14,19 +14,19 @@
h3 h3
label label
color: $defaultColor theme('color', '$defaultColor')
label label
margin-right: 10px margin-right: 10px
width: 25% width: 25%
input input
theme('background-color', '$boxBackgroundColor')Dark
min-width: 50px min-width: 50px
width: 40% width: 40%
border: none border: none
background: $boxBackgroundColorDark theme('color', '$defaultColor')
color: $defaultColor
text-align: center text-align: center
.stageEditorSection .stageEditorSection
margin: 30px 0 margin: 30px 0

View File

@ -8,14 +8,14 @@
padding: 3px padding: 3px
span span
color: $defaultColor theme('color', '$defaultColor')
cursor: pointer cursor: pointer
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&.active &.active
color: $activeColor theme('color', '$activeColor')
.sortable-handle .sortable-handle
cursor: move cursor: move
@ -36,44 +36,44 @@
.deleteStage .deleteStage
cursor: pointer cursor: pointer
color: $defaultColor theme('color', '$defaultColor')
transition(all 0.1s ease) transition(all 0.1s ease)
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')
.addStage .addStage
margin: 10px 0 margin: 10px 0
text-align: center text-align: center
color: $defaultColor theme('color', '$defaultColor')
#addStage #addStage
transition(all 0.1s ease) transition(all 0.1s ease)
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')
.moveStageButtons .moveStageButtons
margin: 10px 0 margin: 10px 0
text-align: center text-align: center
a a
color: $defaultColor theme('color', '$defaultColor')
cursor: pointer cursor: pointer
text-decoration: none text-decoration: none
transition(all 0.1s ease) transition(all 0.1s ease)
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')
&[disabled="true"] &[disabled="true"]
opacity: 0.7 opacity: 0.7

View File

@ -1,6 +1,9 @@
@import "{design}/app.styl" @import "{design}/app.styl"
.textEntryDialog .textEntryDialog
theme('border', '1px solid $uiBorderColor')
theme('background', '$uiGrayDarkest')
theme('color', '$textSecondaryColor')
display: none display: none
position: absolute position: absolute
top: 0 top: 0
@ -13,12 +16,8 @@
margin: auto margin: auto
padding: 10px padding: 10px
outline: none outline: none
opacity: 0.95 opacity: 0.95
border-radius: 8px border-radius: 8px
border: 1px solid $uiBorderColor
background: $uiGrayDarkest
color: $textSecondaryColor
.dialogContent .dialogContent
margin-bottom: 10px margin-bottom: 10px
@ -36,4 +35,4 @@
color: black color: black
height: 30px height: 30px
border-radius: 2px border-radius: 2px
border: 0 border: 0

View File

@ -1,5 +1,8 @@
import { Mongo } from 'meteor/mongo'; import { Mongo } from 'meteor/mongo';
// Servers describe the DICOM servers configurations
Servers = new Mongo.Collection('servers');
Timepoints = new Mongo.Collection('timepoints'); Timepoints = new Mongo.Collection('timepoints');
Studies = new Mongo.Collection('studies'); Studies = new Mongo.Collection('studies');
Measurements = new Mongo.Collection('measurements'); Measurements = new Mongo.Collection('measurements');
@ -12,6 +15,5 @@ ImageMeasurements = new Mongo.Collection('imageMeasurements');
// panel, and represents items such as data quality, // panel, and represents items such as data quality,
AdditionalFindings = new Mongo.Collection('additionalFindings'); AdditionalFindings = new Mongo.Collection('additionalFindings');
Servers = new Mongo.Collection('servers');
WorklistSubscriptions = ['studies', 'timepoints']; WorklistSubscriptions = ['studies', 'timepoints'];
Reviewers = new Mongo.Collection('reviewers'); Reviewers = new Mongo.Collection('reviewers');

View File

@ -26,8 +26,14 @@ class TimepointApi {
}); });
} }
// Return the prior timepoint
prior() { prior() {
const latestDate = this.current().latestDate; const current = this.current();
if (!current) {
return;
}
const latestDate = current.latestDate;
return this.timepoints.findOne({ return this.timepoints.findOne({
latestDate: { latestDate: {
$lt: latestDate $lt: latestDate
@ -41,9 +47,15 @@ class TimepointApi {
// Return only the current and prior Timepoints // Return only the current and prior Timepoints
currentAndPrior() { currentAndPrior() {
let timepoints = [this.current()]; const timepoints = [];
const current = this.current();
if (current) {
timepoints.push(current);
}
const prior = this.prior(); const prior = this.prior();
if (prior) { if (current && prior && prior._id !== current._id) {
timepoints.push(prior); timepoints.push(prior);
} }

View File

@ -0,0 +1,25 @@
import { Template } from 'meteor/templating';
import { ActiveEntry } from 'meteor/clinical:active-entry';
Template.entrySignIn.hooks({
rendered: function () {
// Create Test Drive button dynamically
let btnTestDrive = $('<button/>', {
id: 'btnTestDrive',
text: 'Test Drive',
class: 'btn btn-primary',
style: 'position: absolute; width: 150px; top: 20px; right: 20px; padding-left: 0;',
title: 'Login with Demo User',
click: function () {
// Login with demo user
ActiveEntry.signIn('demo@ohif.org', '12345678aA*');
}
});
const entrySignIn = this.find('#entrySignIn');
$(entrySignIn).append(btnTestDrive);
}
});

View File

@ -15,7 +15,7 @@
key='regionsOfMetastaticDisease' key='regionsOfMetastaticDisease'
multiple=true multiple=true
options=(clone options=(clone
placeholder='Choose a region...' placeholder='Choose a region'
allowClear=true allowClear=true
) )
}} }}

View File

@ -22,22 +22,22 @@
display: none display: none
.header .header
theme('background-color', '$uiGray')
theme('color', '$textPrimaryColor')
height: 33px height: 33px
line-height: 33px line-height: 33px
font-weight: bold font-weight: bold
font-size: 14px font-size: 14px
padding: 1px 13px 0 padding: 1px 13px 0
color: $textPrimaryColor
background-color: $uiGray
.form-group .form-group
color: $textPrimaryColor theme('color', '$textPrimaryColor')
margin-bottom: 0 margin-bottom: 0
padding: 7px 27px 7px 11px padding: 7px 27px 7px 11px
width: 100% width: 100%
&:not(:last-child) &:not(:last-child)
border-bottom: $uiBorderThickness solid $uiBorderColorDark theme('border-bottom', '%s solid $uiBorderColorDark' % $uiBorderThickness)
& > .wrapperText & > .wrapperText
display: block display: block
@ -81,21 +81,21 @@
.buttonSection .buttonSection
cursor: pointer cursor: pointer
height: 90px height: 90px
color: $defaultColor theme('color', '$defaultColor')
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
.svgContainer .svgContainer
svg svg
fill: $hoverColor theme('fill', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')
.svgContainer .svgContainer
svg svg
fill: $activeColor theme('fill', '$activeColor')
div div
float: left float: left
@ -112,4 +112,4 @@
svg svg
width: 36px width: 36px
height: 36px height: 36px
fill: $textPrimaryColor theme('fill', '$textPrimaryColor')

View File

@ -7,7 +7,14 @@ Template.caseProgress.onCreated(() => {
instance.progressText = new ReactiveVar(); instance.progressText = new ReactiveVar();
instance.isLocked = new ReactiveVar(); instance.isLocked = new ReactiveVar();
// Get the current timepoint
const current = instance.data.timepointApi.current(); const current = instance.data.timepointApi.current();
// Stop here if no timepoint was found
if (!current) {
return;
}
if (!current.timepointId) { if (!current.timepointId) {
console.warn('Case has no timepointId'); console.warn('Case has no timepointId');
return; return;

View File

@ -13,15 +13,15 @@
margin-left: 22px margin-left: 22px
h5 h5
color: $textPrimaryColor theme('color', '$textPrimaryColor')
line-height: 46px line-height: 46px
margin: 0 margin: 0
font-size: 15px font-size: 15px
font-weight: bold font-weight: bold
button.btn button.btn
background-color: $activeColor theme('background-color', '$activeColor')
border: 1px solid $uiBorderColorActive theme('border', '1px solid $uiBorderColorActive')
color: #000 color: #000
cursor: pointer cursor: pointer
font-size: 15px font-size: 15px

View File

@ -25,11 +25,11 @@
transform(translateX(0%)) transform(translateX(0%))
.sidebar-left .sidebar-left
theme('border-right', '%s solid $uiBorderColor' % $uiBorderThickness)
max-width: $studiesSidebarMenuWidth max-width: $studiesSidebarMenuWidth
order: 1 order: 1
flex: 1 flex: 1
margin-left: - $studiesSidebarMenuWidth margin-left: - $studiesSidebarMenuWidth
border-right: $uiBorderThickness $uiBorderColor solid
&.sidebar-open &.sidebar-open
margin-left: 0 margin-left: 0
@ -60,7 +60,7 @@
max-width: $lesionsSidebarMenuWidth + 270px max-width: $lesionsSidebarMenuWidth + 270px
&:before &:before
background-color: $uiBorderColor theme('background-color', '$uiBorderColor')
content: '' content: ''
display: block display: block
height: 100% height: 100%
@ -73,6 +73,6 @@
margin-right: 0 margin-right: 0
.studiesListedChanger .studiesListedChanger
theme('border-bottom', '%s solid $uiBorderColor' % $uiBorderThickness)
padding: 20px 10px padding: 20px 10px
text-align: center text-align: center
border-bottom: $uiBorderThickness $uiBorderColor solid

View File

@ -1,9 +0,0 @@
Template.lastLoginModal.helpers({
lastLoginDate: function() {
var userLoginDate = ReactiveMethod.call('getPriorLoginDate');
if (userLoginDate && userLoginDate.priorLoginDate) {
return moment(userLoginDate.priorLoginDate).format("MMMM Do YYYY, HH:mm:ss A");
}
return;
}
});

View File

@ -1,13 +1,17 @@
<template name="lesionTable"> <template name="lesionTable">
<div id="lesionTableContainer"> <div id="lesionTableContainer">
<div class="lesionTableLayoutChanger viewerRoundedButtonGroup"> {{#if this.timepoints.get.length}}
{{>roundedButtonGroup buttonGroupData}} <div class="lesionTableLayoutChanger viewerRoundedButtonGroup">
</div> {{>roundedButtonGroup buttonGroupData}}
<div class="lesionTableTimepointHeaderRow"> </div>
{{#each this.timepoints.get}} <div class="lesionTableTimepointHeaderRow">
{{>lesionTableTimepointHeader}} {{#each this.timepoints.get}}
{{/each}} {{>lesionTableTimepointHeader}}
</div> {{/each}}
{{>lesionTableView (clone this)}} </div>
{{>lesionTableView (clone this)}}
{{else}}
<h3 class="p-x-1 text-center">No associated timepoint</h3>
{{/if}}
</div> </div>
</template> </template>

View File

@ -1,7 +1,7 @@
@import "{design}/app" @import "{design}/app"
#lesionTableContainer #lesionTableContainer
background-color: $primaryBackgroundColor theme('background-color', '$primaryBackgroundColor')
height: 100% height: 100%
width: 100% width: 100%

View File

@ -22,9 +22,9 @@ $borderColor = rgba(77, 99, 110, 0.81)
padding-left: 0 padding-left: 0
.header .header
background: $uiGrayDarkest theme('background', '$uiGrayDarkest')
theme('color', '$textSecondaryColor')
border-bottom: $uiBorderThickness solid $borderColor border-bottom: $uiBorderThickness solid $borderColor
color: $textSecondaryColor
font-weight: 300 font-weight: 300
font-size: 20px font-size: 20px
height: 55px height: 55px
@ -34,12 +34,12 @@ $borderColor = rgba(77, 99, 110, 0.81)
width: 100% width: 100%
svg.buttonClose svg.buttonClose
color: $defaultColor theme('color', '$defaultColor')
cursor: pointer cursor: pointer
height: 14px height: 14px
position: absolute position: absolute
right: 14px right: 14px
stroke: $defaultColor theme('stroke', '$defaultColor')
top: 20px top: 20px
width: 14px width: 14px
@ -48,19 +48,19 @@ $borderColor = rgba(77, 99, 110, 0.81)
padding-bottom: 20px padding-bottom: 20px
.footer .footer
theme('background', '$uiGrayDarkest')
border-top: $uiBorderThickness solid $borderColor
width: 100% width: 100%
height: 70px height: 70px
position: absolute position: absolute
bottom: 0 bottom: 0
background: $uiGrayDarkest
border-top: $uiBorderThickness solid $borderColor
padding: 10px padding: 10px
.toolbarSectionButton .toolbarSectionButton
display: inline-block display: inline-block
color: $defaultColor theme('color', '$defaultColor')
fill: $defaultColor theme('fill', '$defaultColor')
stroke: $defaultColor theme('stroke', '$defaultColor')
padding: 0 10px padding: 0 10px
height: $toolbarHeight height: $toolbarHeight
min-width: 30px min-width: 30px
@ -85,15 +85,15 @@ $borderColor = rgba(77, 99, 110, 0.81)
height: 21px height: 21px
&:active, &.active &:active, &.active
color: $activeColor theme('color', '$activeColor')
svg svg
fill: $activeColor theme('fill', '$activeColor')
stroke: $activeColor theme('stroke', '$activeColor')
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
svg svg
fill: $hoverColor theme('fill', '$hoverColor')
stroke: $hoverColor theme('stroke', '$hoverColor')

View File

@ -16,7 +16,14 @@ Template.lesionTableHeaderRow.onCreated(() => {
const instance = Template.instance(); const instance = Template.instance();
instance.maxNumLesions = new ReactiveVar(); instance.maxNumLesions = new ReactiveVar();
// Get the current timepoint
const current = instance.data.timepointApi.current(); const current = instance.data.timepointApi.current();
// Stop here if no timepoint was found
if (!current) {
return;
}
const timepointType = current.timepointType; const timepointType = current.timepointType;
if (!instance.data.currentTimepointId) { if (!instance.data.currentTimepointId) {

View File

@ -3,10 +3,10 @@
$headerRowHeight = 63px $headerRowHeight = 63px
.lesionTableHeaderRow .lesionTableHeaderRow
background-color: $uiGrayDarker theme('background-color', '$uiGrayDarker')
color: $textSecondaryColor theme('color', '$textSecondaryColor')
display: flex display: flex
fill: $textSecondaryColor theme('fill', '$textSecondaryColor')
height: $headerRowHeight height: $headerRowHeight
line-height: $headerRowHeight line-height: $headerRowHeight
margin-top: 2px margin-top: 2px
@ -35,13 +35,13 @@ $headerRowHeight = 63px
max-width: 11px max-width: 11px
&:hover &:hover
fill: $hoverColor theme('fill', '$hoverColor')
&:active &:active
fill: $activeColor theme('fill', '$activeColor')
.type .type
color: $textSecondaryColor theme('color', '$textSecondaryColor')
font-size: 22px font-size: 22px
font-weight: 300 font-weight: 300
line-height: $headerRowHeight line-height: $headerRowHeight
@ -49,8 +49,8 @@ $headerRowHeight = 63px
text-align: left text-align: left
.numberOfLesions .numberOfLesions
theme('color', '$uiSkyBlue')
float: right float: right
color: $uiSkyBlue
font-weight: 300 font-weight: 300
font-size: 40px font-size: 40px
max-width: 54px max-width: 54px
@ -63,7 +63,7 @@ $headerRowHeight = 63px
text-align: right text-align: right
.maxNumLesions .maxNumLesions
background-color: $textSecondaryColor theme('background-color', '$textSecondaryColor')
border-radius: 3px border-radius: 3px
color: black color: black
display: table display: table
@ -79,5 +79,5 @@ $headerRowHeight = 63px
&.warning &.warning
.maxNumLesions .maxNumLesions
color: $textPrimaryColor theme('background-color', '$uiYellow')
background-color: $uiYellow theme('color', '$textPrimaryColor')

View File

@ -10,7 +10,7 @@
width: calc(100% + 6px) width: calc(100% + 6px)
&:before &:before
background-color: $activeColor theme('background-color', '$activeColor')
border-top-left-radius: 7px border-top-left-radius: 7px
border-bottom-left-radius: 7px border-bottom-left-radius: 7px
content: '' content: ''
@ -30,16 +30,16 @@
opacity: 1 opacity: 1
.lesionRowSidebar .lesionRowSidebar
color: $activeColor theme('color', '$activeColor')
.rowOptions .rowOptions
height: 35px height: 35px
visibility: visible visibility: visible
&.response-status .lesionRowSidebar .response-status-icon &.response-status .lesionRowSidebar .response-status-icon
background-color: $defaultColor theme('background-color', '$defaultColor')
theme('color', '$uiGray')
border-radius: 11px border-radius: 11px
color: $uiGray
font-size: 12px font-size: 12px
font-weight: 700 font-weight: 700
height: 21px height: 21px
@ -49,8 +49,8 @@
width: 21px width: 21px
&.warning .lesionRowSidebar &.warning .lesionRowSidebar
color: $textPrimaryColor theme('background-color', '$uiYellow')
background-color: $uiYellow theme('color', '$textPrimaryColor')
.warning-icon, svg .warning-icon, svg
width: 22px width: 22px
@ -60,31 +60,31 @@
margin: 7px auto 0 margin: 7px auto 0
svg svg
fill: $textPrimaryColor theme('fill', '$textPrimaryColor')
.rowOptions .rowOptions
background-color: $uiGrayDarker theme('background-color', '$uiGrayDarker')
height: 0 height: 0
overflow: hidden overflow: hidden
transition(all 0.3s ease) transition(all 0.3s ease)
visibility: hidden visibility: hidden
.rowAction .rowAction
color: $defaultColor theme('color', '$defaultColor')
cursor: pointer cursor: pointer
line-height: 35px line-height: 35px
transition(all 0.3s ease) transition(all 0.3s ease)
&:hover, &:active &:hover, &:active
color: $textPrimaryColor theme('color', '$textPrimaryColor')
svg svg
fill: $textPrimaryColor theme('fill', '$textPrimaryColor')
stroke: $textPrimaryColor theme('stroke', '$textPrimaryColor')
svg svg
fill: $defaultColor theme('fill', '$defaultColor')
stroke: $defaultColor theme('stroke', '$defaultColor')
transition(all 0.3s ease) transition(all 0.3s ease)
&.edit-icon &.edit-icon
@ -96,8 +96,8 @@
height: 11px height: 11px
.lesionRowSidebar .lesionRowSidebar
background: $uiGray theme('background', '$uiGray')
color: $textSecondaryColor theme('color', '$textSecondaryColor')
cursor: pointer cursor: pointer
flex: 1 flex: 1
max-width: 30px max-width: 30px
@ -120,7 +120,7 @@
.location .location
font-weight: 400 font-weight: 400
font-size: 14px font-size: 14px
color: $textSecondaryColor theme('color', '$textSecondaryColor')
height: 30px height: 30px
line-height: 30px line-height: 30px
margin-left: -2px margin-left: -2px

View File

@ -1,6 +1,6 @@
@import "{design}/app" @import "{design}/app"
.lesionTableTimepointCell .lesionTableTimepointCell
border-left: $uiBorderThickness $uiBorderColor solid theme('border-left', '%s solid $uiBorderColor' % $uiBorderThickness)
color: $textPrimaryColor theme('color', '$textPrimaryColor')
padding: 0 10px padding: 0 10px

View File

@ -5,18 +5,18 @@
justify-content: space-around justify-content: space-around
div div
border-left: $uiBorderColor $uiBorderThickness solid theme('border-left', '%s solid $uiBorderColor' % $uiBorderThickness)
font-weight: 400 font-weight: 400
padding-left: 12px padding-left: 12px
text-align: left text-align: left
.timepointName .timepointName
color: $textSecondaryColor theme('color', '$textSecondaryColor')
font-size: 12px font-size: 12px
line-height: 12px line-height: 12px
.timepointDate .timepointDate
color: $textPrimaryColor theme('color', '$textPrimaryColor')
font-size: 14px font-size: 14px
line-height: 20px line-height: 20px
padding-bottom: 6px padding-bottom: 6px

View File

@ -38,4 +38,5 @@
{{ >lastLoginModal }} {{ >lastLoginModal }}
{{ >studyContextMenu }} {{ >studyContextMenu }}
{{ >progressDialog}} {{ >progressDialog}}
{{ >viewSeriesDetailsModal }}
</template> </template>

View File

@ -9,8 +9,8 @@ $expandedHeight = 160px
.topBar .topBar
height: $topBarHeight height: $topBarHeight
color: $textPrimaryColor theme('color', '$textPrimaryColor')
background-color: $primaryBackgroundColor theme('background-color', '$primaryBackgroundColor')
transition(all 0.5s ease) transition(all 0.5s ease)
.brandSection .brandSection
@ -26,14 +26,14 @@ $expandedHeight = 160px
display: inline-block display: inline-block
font-family: $logoFontFamily font-family: $logoFontFamily
font-weight: $logoFontWeight font-weight: $logoFontWeight
color: $textPrimaryColor theme('color', '$textPrimaryColor')
line-height: 30px line-height: 30px
.studyListLinkSection .studyListLinkSection
border-left: $uiBorderThickness solid $uiBorderColor; theme('border-left', '%s solid $uiBorderColor' % $uiBorderThickness)
.btn .btn
color: $textSecondaryColor theme('color', '$textSecondaryColor')
cursor: pointer cursor: pointer
font-size: 13px font-size: 13px
font-weight: 500 font-weight: 500
@ -41,10 +41,10 @@ $expandedHeight = 160px
margin-top: 3px margin-top: 3px
&:hover &:hover
color: $hoverColor theme('color', '$hoverColor')
&:active &:active
color: $activeColor theme('color', '$activeColor')
&.studyList &.studyList
background-color: rgba(21, 25, 30, 0.7) background-color: rgba(21, 25, 30, 0.7)
@ -75,5 +75,5 @@ $expandedHeight = 160px
.tab-pane .tab-pane
.viewerContainer .viewerContainer
.loadingTextDiv .loadingTextDiv
color: $textSecondaryColor theme('color', '$textSecondaryColor')
font-size: 30px font-size: 30px

View File

@ -24,7 +24,10 @@
Remove Association Remove Association
</a> </a>
<hr/> <hr/>
<a class="disabled">View Series Details</a> <a id="viewSeriesDetails" type="button"
title="View Series Details">
View Series Details
</a>
<a class="disabled">Anonymize</a> <a class="disabled">Anonymize</a>
<a class="disabled">Send</a> <a class="disabled">Send</a>
<hr/> <hr/>

View File

@ -1,8 +1,9 @@
<template name="measureFlow"> <template name="measureFlow">
<div class="measure-flow {{instance.state.get}}" style="position:fixed; top:200px; left:400px"> <div class="measure-flow {{#if this.treeColumns}}tree-columns{{/if}} {{instance.state.get}}" style="position:fixed; top:200px; left:400px">
<button class="btn-add">Add label</button> <button class="btn-add">Add label</button>
{{#if eq instance.state.get 'selected'}} {{#if eq instance.state.get 'selected'}}
<div class="tree-leaf"> <div class="tree-leaf">
<i class="icon-check"></i>
<span>{{instance.value.label}}</span> <span>{{instance.value.label}}</span>
{{#if instance.descriptionEdit.get}} {{#if instance.descriptionEdit.get}}
<div class="description clearfix"> <div class="description clearfix">

View File

@ -55,6 +55,7 @@ Template.measureFlow.onCreated(() => {
}]; }];
const items = [ const items = [
'Abdomen/Chest Wall',
'Adrenal', 'Adrenal',
'Bladder', 'Bladder',
'Bone', 'Bone',
@ -68,9 +69,10 @@ Template.measureFlow.onCreated(() => {
'Liver', 'Liver',
'Lung', 'Lung',
'Lymph Node', 'Lymph Node',
'Mediastinum/Hilum',
'Muscle', 'Muscle',
'Neck', 'Neck',
'Other: Soft Tissue', 'Other Soft Tissue',
'Ovary', 'Ovary',
'Pancreas', 'Pancreas',
'Pelvis', 'Pelvis',
@ -90,6 +92,25 @@ Template.measureFlow.onCreated(() => {
value: item value: item
}); });
}); });
const commonItems = [
'Abdomen/Chest Wall',
'Lung',
'Lymph Node',
'Liver',
'Mediastinum/Hilum',
'Pelvis',
'Peritoneum/Omentum',
'Retroperitoneum'
];
instance.commonItems = [];
_.each(commonItems, item => {
instance.commonItems.push({
label: item,
value: item
});
});
}); });
Template.measureFlow.onRendered(() => { Template.measureFlow.onRendered(() => {
@ -116,9 +137,12 @@ Template.measureFlow.events({
const data = { const data = {
key: 'label', key: 'label',
items: instance.items, items: instance.items,
commonItems: instance.commonItems,
hideCommon: instance.data.hideCommon,
label: 'Assign label', label: 'Assign label',
searchPlaceholder: 'Search labels', searchPlaceholder: 'Search labels',
storageKey: 'measureLabelCommon', storageKey: 'measureLabelCommon',
treeColumns: instance.data.treeColumns,
position position
}; };
@ -206,14 +230,14 @@ Template.measureFlow.events({
// Change the measure flow state to selected // Change the measure flow state to selected
instance.state.set('selected'); instance.state.set('selected');
// Get the clicked label window offset
const labelOffset = $label.offset();
// Wait for the DOM re-rendering // Wait for the DOM re-rendering
Tracker.afterFlush(() => { Tracker.afterFlush(() => {
// Get the measure flow div // Get the measure flow div
const $measureFlow = instance.$('.measure-flow'); const $measureFlow = instance.$('.measure-flow');
// Get the clicked label window offset
const labelOffset = $label.offset();
// Subtract the label box shadow height from the label's position // Subtract the label box shadow height from the label's position
labelOffset.top -= 10; labelOffset.top -= 10;

View File

@ -10,10 +10,10 @@
opacity: 0 opacity: 0
.btn-add .btn-add
background-color: $activeColor theme('color', '$textColorActive')
border: 2px solid $uiBorderColor theme('background-color', '$activeColor')
theme('border', '2px solid $uiBorderColor')
border-radius: 14px border-radius: 14px
color: $textColorActive
cursor: pointer cursor: pointer
font-weight: bold font-weight: bold
height: 24px height: 24px
@ -26,14 +26,13 @@
transition(opacity 0.3s ease) transition(opacity 0.3s ease)
white-space: nowrap white-space: nowrap
// / {selector()}
// color: $themes['tide']['uiYellow'] !important
.select-tree-root>.tree-content .select-tree-root>.tree-content
width: 213px width: 213px
&>.tree-options &>.tree-options
margin-right: -17px
max-height: 250px
overflow-x: hidden
overflow-y: scroll
position: relative position: relative
z-index: 2 z-index: 2
@ -43,22 +42,11 @@
&>.tree-leaf &>.tree-leaf
position: relative position: relative
&:before .icon-check
animateZoomIn() theme('background-color', '$activeColor')
background-color: $activeColor
border-radius: 20px
color: $uiGray
content: '\f00c'
display: block
font-family: FontAwesome
font-size: 24px
height: 40px
left: -46px left: -46px
line-height: 40px
position: absolute position: absolute
text-align: center
top: 3px top: 3px
width: 40px
span span
background: white background: white
@ -82,10 +70,10 @@
animateFadeOut() animateFadeOut()
button button
theme('border', '1px solid $uiBorderColor')
theme('color', '$textPrimaryColor')
background-color: transparent background-color: transparent
border-radius: 16px border-radius: 16px
border: 1px solid $uiBorderColor
color: $textPrimaryColor
font-weight: normal font-weight: normal
height: 31px height: 31px
line-height: 31px line-height: 31px
@ -120,3 +108,80 @@
span span
display: block display: block
&.tree-columns
.select-tree-root
&.interacted .select-tree-common .content
animation-name: none
&.selected .select-tree-common .content
animation-name: selectTreeCommonCloseLeft
.tree-search
input, i
display: none
.select-tree-common
padding-left: 0
padding-right: 6px
right: auto
left: 0
width: 33.333333333333%
&>.tree-content
transform-origin(0% 50%)
width: 426px
.select-tree-common .content
right: -100%
&.started .select-tree-common .content
right: 6px
&.interacted .select-tree-common .content
animation-name: selectTreeCommonCloseRight
.tree-inputs>label
float: left
width: 50%
@keyframes selectTreeCommonCloseRight {
from {
height: 37px
display: table
right: 6px
opacity: 1
visibility: visible
}
to {
height: 100%
right: -100%
opacity: 0
visibility: hidden
width: calc(100% - 6px)
}
}
.icon-check
background-color: white
border-radius: 20px
display: block
height: 40px
overflow: hidden
position: relative
width: 40px
&:before
border: 2px solid black
border-left-width: 0
border-top-width: 0
content: ''
display: block
height: 18px
left: 15px
position: absolute
top: 9px
transform(rotate(45deg))
width: 9px

View File

@ -11,12 +11,12 @@ $circleSize = 46px
#svg circle #svg circle
stroke-dashoffset: 0 stroke-dashoffset: 0
stroke: $uiBorderColorDark theme('stroke', '$uiBorderColorDark')
stroke-width: 3px stroke-width: 3px
vendorize(transition, stroke-dashoffset 1s linear) vendorize(transition, stroke-dashoffset 1s linear)
#svg #bar #svg #bar
stroke: $activeColor theme('stroke', '$activeColor')
vendorize(transform, rotate(270deg)) vendorize(transform, rotate(270deg))
vendorize(transform-origin, center center) vendorize(transform-origin, center center)
@ -28,14 +28,14 @@ $circleSize = 46px
font-weight: 700 font-weight: 700
font-size: 17px font-size: 17px
text-align: center text-align: center
color: $activeColor theme('color', '$activeColor')
top: 0 top: 0
svg svg
fill: $activeColor theme('fill', '$activeColor')
left: 50% left: 50%
position: absolute position: absolute
stroke: $activeColor theme('stroke', '$activeColor')
top: 50% top: 50%
transform(translateX(-50%) translateY(-50%)) transform(translateX(-50%) translateY(-50%))

View File

@ -1,30 +1,27 @@
<template name="serverInformationDicomWeb"> <template name="serverInformationDicomWeb">
<div class="row"> <div class="row">
{{>serverInformationFormField name="wadoUriRoot" label="WADO URI root"}}
{{>serverInformationFormField name="wadoUriRootNOTE" label="WADO URI root note"}}
{{>serverInformationFormField name="wadoRoot" label="WADO root"}}
<div class="col-lg-6"> <div class="col-lg-6">
<div class="form-group"> {{>inputText labelClass='form-group' key='wadoUriRoot'}}
<label class="wrapper"> </div>
<strong>Image rendering</strong> <div class="col-lg-6">
<select name="imageRendering" class="form-control"> {{>inputText labelClass='form-group' key='wadoUriRootNOTE'}}
<option>Select</option> </div>
<option value="wadouri">WADO URI</option> <div class="col-lg-6">
<option value="orthanc">ORTHANC</option> {{>inputText labelClass='form-group' key='wadoRoot'}}
</select> </div>
</label> <div class="col-lg-6">
</div> {{>inputSelect labelClass='form-group' key='imageRendering' hideSearch=true
options=(clone placeholder='Select an option')}}
</div>
<div class="col-lg-6">
{{>inputText labelClass='form-group' key='qidoRoot'}}
</div> </div>
{{>serverInformationFormField name="qidoRoot" label="QIDO root"}}
<div class="col-lg-6"> <div class="col-lg-6">
<strong>QIDO options</strong> <strong>QIDO options</strong>
<div class="form-group"> <div class="form-group">
<div class="form-control"> <div class="form-control">
<div class="checkbox"> <div class="checkbox">
<label> {{>inputCheckbox key='qidoSupportsIncludeField'}}
<input type="checkbox" name="qidoSupportsIncludeField">
<span>QIDO supports including fields</span>
</label>
</div> </div>
</div> </div>
</div> </div>
@ -32,32 +29,25 @@
</div> </div>
<hr> <hr>
<h4>Request options</h4> <h4>Request options</h4>
<div class="row"> {{#group class='row' key='requestOptions'}}
{{>serverInformationFormField name="requestOptions.auth" label="Authentication"}} <div class="col-lg-6">
{{>inputText labelClass='form-group' key='auth'}}
</div>
<div class="col-lg-6"> <div class="col-lg-6">
<strong>Logging options</strong> <strong>Logging options</strong>
<div class="form-group"> <div class="form-group">
<div class="form-control clearfix"> <div class="form-control clearfix">
<div class="checkbox pull-left"> <div class="checkbox pull-left">
<label> {{>inputCheckbox key='logRequests'}}
<input type="checkbox" name="requestOptions.logRequests">
<span>Requests</span>
</label>
</div> </div>
<div class="checkbox pull-left"> <div class="checkbox pull-left">
<label> {{>inputCheckbox key='logResponses'}}
<input type="checkbox" name="requestOptions.logResponses">
<span>Responses</span>
</label>
</div> </div>
<div class="checkbox pull-left"> <div class="checkbox pull-left">
<label> {{>inputCheckbox key='logTiming'}}
<input type="checkbox" name="requestOptions.logTiming">
<span>Timing</span>
</label>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> {{/group}}
</template> </template>

View File

@ -1,10 +1,10 @@
Template.serverInformationDicomWeb.onRendered(function() { Template.serverInformationDicomWeb.onRendered(() => {
var instance = Template.instance(); const instance = Template.instance();
instance.autorun(function() { instance.autorun(function() {
var mode = instance.data.mode.get(); const mode = instance.data.mode.get();
if (mode === 'edit') { if (mode === 'edit') {
var data = instance.data.currentItem.get(); const data = instance.data.currentItem.get();
FormUtils.setFormData(instance.data.$form, data); instance.data.form.value(data);
} }
}); });
}); });

View File

@ -1,43 +1,45 @@
<template name="serverInformationDimse"> <template name="serverInformationDimse">
<div class="panel panel-default panel-body"> <div class="panel panel-default panel-body">
<div class="clearfix"> <div class="clearfix">
<h4 class="pull-left">Peer list</h4> <h4 class="pull-left">Peer List</h4>
<button class="btn btn-primary pull-right js-new-peer">New peer</button> <button class="btn btn-primary pull-right js-new-peer">New peer</button>
</div> </div>
<hr> <hr>
<div class="row"> {{#group class='row' key='peers' arrayValues=true}}
{{#each peer in instance.peers.get}} {{#each peer in instance.peers.get}}
<div class="col-lg-6"> {{#group class='col-lg-6'}}
<div class="panel panel-default panel-body"> <div class="panel panel-default panel-body">
<div class="row"> <div class="row">
{{>serverInformationFormField name=(concat "peers[]." @index ".aeTitle") label="AE title"}} <div class="col-lg-6">
{{>serverInformationFormField name=(concat "peers[]." @index ".hostAE") label="AE host"}} {{>inputText class='form-control' key='aeTitle'}}
{{>serverInformationFormField name=(concat "peers[]." @index ".host") label="Host"}} </div>
{{>serverInformationFormField name=(concat "peers[]." @index ".port") label="Port"}} <div class="col-lg-6">
{{>inputText class='form-control' key='hostAE'}}
</div>
<div class="col-lg-6">
{{>inputText class='form-control' key='host'}}
</div>
<div class="col-lg-6">
{{>inputText class='form-control' key='port'}}
</div>
<div class="col-lg-12"> <div class="col-lg-12">
<strong>Settings</strong> <strong>Settings</strong>
<div class="form-group"> <div class="form-group">
<div class="form-control clearfix"> <div class="form-control clearfix">
<div class="checkbox pull-left"> <div class="checkbox pull-left">
<label> {{>inputCheckbox key='default'}}
<input type="checkbox" name="peers[].{{@index}}.default">
<span>Default</span>
</label>
</div> </div>
<div class="checkbox pull-left"> <div class="checkbox pull-left">
<label> {{>inputCheckbox key='server'}}
<input type="checkbox" name="peers[].{{@index}}.server">
<span>Server</span>
</label>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> {{/group}}
{{/each}} {{/each}}
</div> {{/group}}
</div> </div>
<hr> <hr>
</template> </template>

View File

@ -1,29 +1,29 @@
Template.serverInformationDimse.onCreated(function() { Template.serverInformationDimse.onCreated(() => {
var instance = Template.instance(); const instance = Template.instance();
instance.peers = new ReactiveVar([]); instance.peers = new ReactiveVar([]);
instance.autorun(function() { instance.autorun(() => {
var currentItem = instance.data.currentItem.get(); const currentItem = instance.data.currentItem.get();
if (currentItem) { if (currentItem) {
instance.peers.set(currentItem.peers || []); instance.peers.set(currentItem.peers || []);
} }
}); });
}); });
Template.serverInformationDimse.onRendered(function() { Template.serverInformationDimse.onRendered(() => {
var instance = Template.instance(); const instance = Template.instance();
instance.autorun(function() { instance.autorun(() => {
var mode = instance.data.mode.get(); const mode = instance.data.mode.get();
if (mode === 'edit') { if (mode === 'edit') {
var data = instance.data.currentItem.get(); const data = instance.data.currentItem.get();
FormUtils.setFormData(instance.data.$form, data); instance.data.form.value(data);
} }
}); });
}); });
Template.serverInformationDimse.events({ Template.serverInformationDimse.events({
'click .js-new-peer': function(event, instance) { 'click .js-new-peer'(event, instance) {
event.preventDefault(); event.preventDefault();
var peers = instance.peers.get(); const peers = instance.peers.get();
peers.push({}); peers.push({});
instance.peers.set(peers); instance.peers.set(peers);
} }

View File

@ -1,33 +1,21 @@
<template name="serverInformationForm"> <template name="serverInformationForm">
<form class="server-information-form"> {{#form class='server-information-form' schema=instance.currentSchema}}
<input type="hidden" name="_id"> {{>inputHidden key='_id'}}
<div class="row"> <div class="row">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="form-group"> {{>inputText labelClass='form-group' key='name'}}
<label class="wrapper">
<strong>Server Name</strong>
<input type="text" name="name" class="form-control">
</label>
</div>
</div> </div>
<div class="col-lg-6"> <div class="col-lg-6">
<div class="form-group"> {{>inputSelect labelClass='form-group' key='type'
<label class="wrapper"> hideSearch=true disabled=(eq this.mode.get 'edit')
<strong>Server Type</strong> options=(clone placeholder='Choose a server type')}}
<select name="type" class="form-control js-server-type">
<option>Select</option>
<option value="dicomWeb">DICOM Web</option>
<option value="dimse">DIMSE</option>
</select>
</label>
</div>
</div> </div>
</div> </div>
<hr> <hr>
{{#if eq this.serverType.get "dicomWeb"}} {{#if eq this.serverType.get 'dicomWeb'}}
{{>serverInformationDicomWeb this}} {{>serverInformationDicomWeb this}}
{{/if}} {{/if}}
{{#if eq this.serverType.get "dimse"}} {{#if eq this.serverType.get 'dimse'}}
{{>serverInformationDimse this}} {{>serverInformationDimse this}}
{{/if}} {{/if}}
{{#if this.serverType.get}} {{#if this.serverType.get}}
@ -35,5 +23,5 @@
<input type="submit" class="btn btn-primary" value="Save"> <input type="submit" class="btn btn-primary" value="Save">
</div> </div>
{{/if}} {{/if}}
</form> {{/form}}
</template> </template>

View File

@ -1,24 +1,68 @@
Template.serverInformationForm.onRendered(function() { import { Template } from 'meteor/templating';
var instance = Template.instance(); import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { DICOMWebServer as dicomSchema } from 'meteor/worklist/both/schema';
import { DIMSEServer as dimseSchema } from 'meteor/worklist/both/schema';
instance.data.$form = instance.$('form'); Template.serverInformationForm.onCreated(() => {
instance.autorun(function() { const instance = Template.instance();
var mode = instance.data.mode.get();
instance.currentSchema = new ReactiveVar(dicomSchema);
});
Template.serverInformationForm.onRendered(() => {
const instance = Template.instance();
instance.data.$form = instance.$('form:first');
instance.data.form = instance.data.$form.data('component');
// Handle the server type
instance.autorun(() => {
// Get the server type component
const typeComponent = instance.$('[data-key=type] :input').data('component');
// Run this computation every time the user change the server type
typeComponent.depend();
// Get the current server type value
const type = typeComponent.value();
// Set the serverType reactive value
instance.data.serverType.set(type);
// Change the schema based on the selected server type
if (type === 'dimse') {
instance.currentSchema.set(dimseSchema);
} else {
instance.currentSchema.set(dicomSchema);
}
});
// Handle the form mode (edit or add)
instance.autorun(() => {
const mode = instance.data.mode.get();
// Check if it is on edit mode and load the saved data
if (mode === 'edit') { if (mode === 'edit') {
var data = instance.data.currentItem.get(); const data = instance.data.currentItem.get();
FormUtils.setFormData(instance.data.$form, data); instance.data.form.value(data);
} }
}); });
}); });
Template.serverInformationForm.events({ Template.serverInformationForm.events({
'change .js-server-type': function(event, instance) { submit(event, instance) {
var value = $(event.currentTarget).val();
instance.data.serverType.set(value);
},
submit: function(event, instance) {
event.preventDefault(); event.preventDefault();
var formData = FormUtils.getFormData(instance.data.$form);
// Stop here if the form validation fails
if (!instance.data.form.validate()) {
return;
}
// Get the current form data
const formData = instance.data.form.value();
// Call the save method
Meteor.call('serverSave', formData, function(error) { Meteor.call('serverSave', formData, function(error) {
if (error) { if (error) {
// TODO: check for errors: not-authorized, data-write // TODO: check for errors: not-authorized, data-write

View File

@ -15,7 +15,7 @@ $seriesSpacing = 2px
transform(translateX(-50%)) transform(translateX(-50%))
&.left &.left
border-right: 1px solid $uiBorderColor theme('border-right', '1px solid $uiBorderColor')
right: 50% right: 50%
&.right &.right
@ -26,7 +26,7 @@ $seriesSpacing = 2px
z-index: 3 z-index: 3
&:before &:before
background-color: $primaryBackgroundColor theme('background-color', '$primaryBackgroundColor')
bottom: 0 bottom: 0
content: '' content: ''
left: 0 left: 0
@ -98,10 +98,10 @@ $seriesSpacing = 2px
.studyTimepointBrowser .studyTimepointBrowser
background-color: transparent background-color: transparent
.studyTimepointStudy.active .studyModality .studyTimepointStudy.active .studyModality
box-shadow: inset 0 0 0 3px $activeColor theme('box-shadow', 'inset 0 0 0 3px $activeColor')
.studyBox .studyBox
background-color: $uiGrayDark theme('background-color', '$uiGrayDark')
border: solid 1px $activeColor theme('border', 'solid 1px $activeColor')
border-radius: 11px border-radius: 11px
display: block display: block
height: $switchSize height: $switchSize
@ -113,7 +113,7 @@ $seriesSpacing = 2px
width: $switchSize + $seriesSpacing width: $switchSize + $seriesSpacing
.seriesItem .seriesItem
background-color: $boxBackgroundColorDark theme('background-color', '$boxBackgroundColor')Dark
border-radius: 3px border-radius: 3px
height: 15px height: 15px
margin: $seriesSpacing margin: $seriesSpacing
@ -121,17 +121,17 @@ $seriesSpacing = 2px
&.count &.count
background-color: transparent background-color: transparent
color: $textPrimaryColor theme('color', '$textPrimaryColor')
font-size: 12px font-size: 12px
font-weight: 500 font-weight: 500
line-height: 17px line-height: 17px
text-align: center text-align: center
&.active &.active
background-color: $activeColor theme('background-color', '$activeColor')
.label .label
color: $textSecondaryColor theme('color', '$textSecondaryColor')
display: block display: block
font-size: 12px font-size: 12px
font-weight: 300 font-weight: 300

View File

@ -3,15 +3,15 @@
$timepointButtonHeight = 55px $timepointButtonHeight = 55px
.studyTimepointBrowser .studyTimepointBrowser
background-color: $primaryBackgroundColor theme('background-color', '$primaryBackgroundColor')
float: left float: left
height: 100% height: 100%
position: relative position: relative
width: 100% width: 100%
.timepointButtonContainer .timepointButtonContainer
background-color: $primaryBackgroundColor theme('background-color', '$primaryBackgroundColor')
border-bottom: 1px solid $uiBorderColor theme('border-bottom', '1px solid $uiBorderColor')
display: none display: none
height: $timepointButtonHeight height: $timepointButtonHeight
left: 10px left: 10px
@ -38,7 +38,7 @@ $timepointButtonHeight = 55px
transform: rotateX(180deg) transform: rotateX(180deg)
.timepointFollowupTitle .timepointFollowupTitle
font-size: 14px font-size: 14px
color: $textSecondaryColor theme('color', '$textSecondaryColor')
text-transform: uppercase text-transform: uppercase
.timepointHeader .timepointHeader
@ -50,7 +50,7 @@ $timepointButtonHeight = 55px
.timepointFollowupTitle .timepointFollowupTitle
.timepointDate .timepointDate
.timepointModalities .timepointModalities
color: $textSecondaryColor theme('color', '$textSecondaryColor')
transition($sidebarTransition) transition($sidebarTransition)
&:hover &:hover
@ -59,7 +59,7 @@ $timepointButtonHeight = 55px
.expandIcon i .expandIcon i
.timepointDate .timepointDate
.timepointModalities .timepointModalities
color: $textPrimaryColor !important theme('color', '$textPrimaryColor') !important
.timepointDetails .timepointDetails
.timepointModalities .timepointModalities
@ -70,10 +70,10 @@ $timepointButtonHeight = 55px
overflow: hidden overflow: hidden
.expandIcon i .expandIcon i
color: $defaultColor theme('color', '$defaultColor')
.timepointFollowupTitle .timepointFollowupTitle
color: $textPrimaryColor theme('color', '$textPrimaryColor')
padding-top: 2px padding-top: 2px
.studyTimepointScrollArea .studyTimepointScrollArea
@ -91,7 +91,7 @@ $timepointButtonHeight = 55px
margin-right: 16px margin-right: 16px
.studySidebarTimepoint .thumbnailEntry.active:before .studySidebarTimepoint .thumbnailEntry.active:before
background-color: $activeColor theme('background-color', '$activeColor')
border-top-right-radius: 7px border-top-right-radius: 7px
border-bottom-right-radius: 7px border-bottom-right-radius: 7px
content: '' content: ''

View File

@ -3,8 +3,6 @@
$boxBorderColor = transparent $boxBorderColor = transparent
$boxHoverBackgroundColor = #14191E $boxHoverBackgroundColor = #14191E
$boxHoverBorderColor = #2d4660 $boxHoverBorderColor = #2d4660
$boxActiveBackgroundColor = $activeColor
$boxActiveTextColor = $primaryBackgroundColor
$boxActiveBorderColor = #131D29 $boxActiveBorderColor = #131D29
$boxWidth = 50px $boxWidth = 50px
$borderThickness = 2px $borderThickness = 2px
@ -41,11 +39,11 @@ $spacerY = 12px
&.active &.active
.studyModality .studyModality
.studyModalityBox .studyModalityBox
color: $boxActiveTextColor theme('color', '$primaryBackgroundColor')
& &
&:before &:before
&:after &:after
background-color: $boxActiveBackgroundColor theme('background-color', '$activeColor')
border-color: $boxActiveBorderColor border-color: $boxActiveBorderColor
&:not(.active) .studyTimepointThumbnails &:not(.active) .studyTimepointThumbnails
@ -69,10 +67,10 @@ $spacerY = 12px
border-color: $boxHoverBorderColor border-color: $boxHoverBorderColor
&.additional &.additional
color: $textSecondaryColor theme('color', '$textSecondaryColor')
&.additional &.additional
color: $textPrimaryColor theme('color', '$textPrimaryColor')
font-size: 16px font-size: 16px
font-weight: normal font-weight: normal
height: 91px height: 91px
@ -89,13 +87,13 @@ $spacerY = 12px
top: $spacerY top: $spacerY
.studyDate .studyDate
margin-top: 8px margin-top: 8px
color: $textSecondaryColor theme('color', '$textSecondaryColor')
.studyDescription .studyDescription
margin-top: 8px margin-top: 8px
color: $textPrimaryColor theme('color', '$textPrimaryColor')
.studyModalityBox .studyModalityBox
color: $textSecondaryColor theme('color', '$textSecondaryColor')
line-height: $boxWidth line-height: $boxWidth
margin-left: $nestingMargin * 2 margin-left: $nestingMargin * 2
margin-top: $nestingMargin * 2 margin-top: $nestingMargin * 2
@ -109,8 +107,8 @@ $spacerY = 12px
& &
&:before &:before
&:after &:after
background-color: $boxBackgroundColor theme('border', '$borderThickness solid $primaryBackgroundColor')
border: $borderThickness solid $primaryBackgroundColor theme('background-color', '$boxBackgroundColor')
border-radius: $boxBorderRadius border-radius: $boxBorderRadius
height: $boxWidth + ($borderThickness * 2) height: $boxWidth + ($borderThickness * 2)
transition($sidebarTransition) transition($sidebarTransition)

View File

@ -53,7 +53,10 @@ Template.toolbarSection.helpers({
}, },
toolbarButtons() { toolbarButtons() {
var buttonData = []; // Check if the measure tools shall be disabled
const isMesureDisabled = !Template.instance().data.timepointApi.currentTimepointId;
const buttonData = [];
buttonData.push({ buttonData.push({
id: 'zoom', id: 'zoom',
title: 'Zoom', title: 'Zoom',
@ -86,14 +89,16 @@ Template.toolbarSection.helpers({
id: 'bidirectional', id: 'bidirectional',
title: 'Target', title: 'Target',
classes: 'imageViewerTool rm-l-3', classes: 'imageViewerTool rm-l-3',
svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-target' svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-target',
disabled: isMesureDisabled
}); });
buttonData.push({ buttonData.push({
id: 'nonTarget', id: 'nonTarget',
title: 'Non-Target', title: 'Non-Target',
classes: 'imageViewerTool toolbarSectionButton', classes: 'imageViewerTool toolbarSectionButton',
svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-non-target' svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-non-target',
disabled: isMesureDisabled
}); });
buttonData.push({ buttonData.push({
@ -107,28 +112,33 @@ Template.toolbarSection.helpers({
}, },
extraToolbarButtons() { extraToolbarButtons() {
let buttonData = []; // Check if the measure tools shall be disabled
const isMesureDisabled = !Template.instance().data.timepointApi.currentTimepointId;
const buttonData = [];
// TODO: Get real icons for CR / UN / EX // TODO: Get real icons for CR / UN / EX
buttonData.push({ buttonData.push({
id: 'crTool', id: 'crTool',
title: 'CR Tool', title: 'CR Tool',
classes: 'imageViewerTool toolbarSectionButton', classes: 'imageViewerTool toolbarSectionButton',
svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-temp' svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-temp',
disabled: isMesureDisabled
}); });
buttonData.push({ buttonData.push({
id: 'unTool', id: 'unTool',
title: 'UN Tool', title: 'UN Tool',
classes: 'imageViewerTool toolbarSectionButton', classes: 'imageViewerTool toolbarSectionButton',
svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-temp' svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-temp',
disabled: isMesureDisabled
}); });
buttonData.push({ buttonData.push({
id: 'exTool', id: 'exTool',
title: 'EX Tool', title: 'EX Tool',
classes: 'imageViewerTool toolbarSectionButton', classes: 'imageViewerTool toolbarSectionButton',
svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-temp' svgLink: '/packages/viewerbase/assets/icons.svg#icon-tools-measure-temp',
disabled: isMesureDisabled
}); });
return buttonData; return buttonData;

View File

@ -1,7 +1,7 @@
@import "{design}/app" @import "{design}/app"
.toolbarSection .toolbarSection
border-bottom: $uiBorderColor $uiBorderThickness solid theme('border-bottom', '%s solid $uiBorderColor' % $uiBorderThickness)
flex: 0 0 auto flex: 0 0 auto
height: $toolbarHeight height: $toolbarHeight
overflow: hidden overflow: hidden
@ -14,11 +14,11 @@
height: $toolbarHeight + $toolbarDrawerHeight height: $toolbarHeight + $toolbarDrawerHeight
.toolbarSectionEntry .toolbarSectionEntry
color: $defaultColor theme('color', '$defaultColor')
cursor: pointer cursor: pointer
display: inline-block display: inline-block
fill: $defaultColor theme('fill', '$defaultColor')
margin-top: 3px margin-top: 3px
min-width: 30px min-width: 30px
stroke: $defaultColor theme('stroke', '$defaultColor')
text-align: center text-align: center

View File

@ -1,3 +1,10 @@
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { Session } from 'meteor/session';
// Display the last login modal as default
Session.setDefault('displayLastLoginModal', true);
Template.userAccountMenu.helpers({ Template.userAccountMenu.helpers({
name: function() { name: function() {
var nameSplit = Meteor.user().profile.fullName.split(' '); var nameSplit = Meteor.user().profile.fullName.split(' ');
@ -29,68 +36,87 @@ Template.userAccountMenu.events({
$('#serverInformationModal').modal('show'); $('#serverInformationModal').modal('show');
}, },
'click #logoutButton': function() { 'click #logoutButton': function() {
// Remove reviewers info for the user
Meteor.call('removeUserFromReviewers', Meteor.userId());
Meteor.logout(function() { Meteor.logout(function() {
Router.go('/entrySignIn'); Router.go('/entrySignIn');
}); });
} }
}); });
Template.userAccountMenu.onRendered(function() { Template.userAccountMenu.onCreated(function userAccountMenuCreated() {
var oldUserId; const instance = Template.instance();
var lastLoginModalInterval;
this.autorun(function() { // Create reactive last login date
// Hook login/logout instance.lastLoginDate = new ReactiveVar();
var user = Meteor.user();
if (!user) { // We need oldUser to remove Reviewers if the user logged out
let oldUser;
// Get last login date
Meteor.call('getPriorLoginDate', function(error, lastLoginDate){
if (error) {
console.log(error);
return; return;
} }
var newUserId = user._id; // Format the last login date
const formattedLastLoginDate = moment(lastLoginDate).format("MMMM Do YYYY, HH:mm:ss A");
if (oldUserId === null && newUserId) { instance.lastLoginDate.set(formattedLastLoginDate);
Session.set('showLastLoginModal', true);
// Log });
HipaaLogger.logEvent({
eventType: 'init',
userId: user._id,
userName: user.fullName
});
} else if (newUserId === null && oldUserId) { instance.autorun(function() {
// Set showLastLoginModal as null const lastLoginDate = instance.lastLoginDate.get();
Session.set('showLastLoginModal', null); if (!lastLoginDate) {
return;
// Destroy interval for last login modal
Meteor.clearInterval(lastLoginModalInterval);
console.log('The user logged out');
// Log
// TODO: eventType is not defined for logout in hipaa-audit-log
/*HipaaLogger.logEvent({
eventType: 'logout',
userId: oldUserId,
userName: userName
});*/
// Remove the user from Reviewers
Meteor.call('removeUserFromReviewers', oldUserId);
} }
oldUserId = Meteor.userId(); // Hook login/logout
var user = Meteor.user();
if (!user) {
// Display last login modal for the next login
Session.setPersistent('displayLastLoginModal', true);
if (oldUser) {
// Log Signout
// TODO: eventType for logout is not defined
// HipaaLogger.logEvent({
// eventType: 'logout',
// userId: oldUser._id,
// userName: oldUser.profile.fullName
// });
// Remove the user by oldUserId from Reviewers
Meteor.call('removeUserFromReviewers', oldUser._id);
}
return;
}
// Set oldUser
oldUser = user;
// Trigger last login date popup // Trigger last login date popup
if (Session.get('showLastLoginModal')) { if (!Session.get('displayLastLoginModal')) {
Modal.show('lastLoginModal'); return;
lastLoginModalInterval = Meteor.setInterval(function() {
Modal.hide('lastLoginModal');
Session.set('showLastLoginModal', null);
}, 3000);
} }
// Displaye the modal
Modal.show('lastLoginModal', {
lastLoginDate: lastLoginDate
});
// Hide the modal after 5sec
Meteor.setTimeout(() => {
Modal.hide('lastLoginModal');
Session.setPersistent('displayLastLoginModal', false);
}, 5000);
// Log signin
HipaaLogger.logEvent({
eventType: 'init',
userId: user._id,
userName: user.profile.fullName
});
}); });
}); });

View File

@ -5,7 +5,7 @@
height: 100% height: 100%
.dropdown-toggle .dropdown-toggle
color: $defaultColor theme('color', '$defaultColor')
cursor: pointer cursor: pointer
font-size: 13px font-size: 13px
font-weight: 400 font-weight: 400
@ -13,19 +13,19 @@
text-decoration: none text-decoration: none
.menuButton .menuButton
border-left: solid 1px $defaultColor theme('border-left', 'solid 1px $defaultColor')
display: inline-block display: inline-block
height: 18px height: 18px
padding: 0 8px padding: 0 8px
.fa .fa
color: $defaultColor theme('color', '$defaultColor')
font-size: 17px font-size: 17px
line-height: 18px line-height: 18px
.caret-down .caret-down
theme('color', '$uiLightGray')
margin: 0 4px 4px 2px margin: 0 4px 4px 2px
color: $uiLightGray
ul.dropdown-menu ul.dropdown-menu
li li

View File

@ -19,4 +19,4 @@
&:not(:last-child) &:not(:last-child)
.removable .removable
.imageViewerViewport .imageViewerViewport
border-right: $uiBorderThickness solid $uiBorderColor theme('border-right', '%s solid $uiBorderColor' % $uiBorderThickness)

View File

@ -1,105 +0,0 @@
class Form {
// Identify the element's type and get its value
static getElementValue($element) {
var type = $element.attr('type');
var value;
switch (type) {
case 'checkbox':
value = $element.is(':checked');
break;
default:
value = $element.val();
}
return value;
};
// Identify the element's type and get its value
static setElementValue($element, value) {
if (!$element.length) {
return;
}
var type = $element.attr('type');
switch (type) {
case 'checkbox':
$element.prop('checked', !!value);
break;
default:
$element.val(value);
}
$element.trigger('change');
};
// Transforms a shallow object with keys separated by "." into a nested object
static getNestedObject(shallowObject) {
var nestedObject = {};
for (var key in shallowObject) {
var value = shallowObject[key];
var propertyArray = key.split('.');
var currentObject = nestedObject;
while (propertyArray.length) {
var currentProperty = propertyArray.shift();
if (!propertyArray.length) {
currentObject[currentProperty] = value;
} else {
if (!currentObject[currentProperty]) {
currentObject[currentProperty] = {};
}
currentObject = currentObject[currentProperty];
}
}
}
return nestedObject;
};
// Transforms a nested object into a shallowObject merging its keys with "." character
static getShallowObject(nestedObject) {
var shallowObject = {};
var putValues = function(baseKey, nestedObject, resultObject) {
for (var key in nestedObject) {
var currentKey = baseKey ? baseKey + '.' + key : key;
var currentValue = nestedObject[key];
if (typeof currentValue === 'object') {
if (currentValue instanceof Array) {
currentKey += '[]';
}
putValues(currentKey, currentValue, resultObject);
} else {
resultObject[currentKey] = currentValue;
}
}
};
putValues('', nestedObject, shallowObject);
return shallowObject;
}
// Gets the nested data for the given form
static getFormData($form) {
var data = {};
$form.find(':input[name]').each((index, element) => {
var $element = $(element);
var value = this.getElementValue($element);
var name = $element.attr('name');
data[name] = value;
});
return this.getNestedObject(data);
}
// Sets the nested data in the given form
static setFormData($form, data) {
var shallowData = this.getShallowObject(data);
for (var key in shallowData) {
var value = shallowData[key];
var $element = $form.find(':input[name="' + key + '"]');
this.setElementValue($element, value);
}
}
}
FormUtils = Form;

View File

@ -58,9 +58,6 @@ Package.onUse(function(api) {
api.addFiles('client/tools.js', 'client'); api.addFiles('client/tools.js', 'client');
// Utility classes
api.addFiles('client/utils/form.js', 'client');
// UI Components // UI Components
api.addFiles('client/components/viewer/viewer.html', 'client'); api.addFiles('client/components/viewer/viewer.html', 'client');
api.addFiles('client/components/viewer/viewer.styl', 'client'); api.addFiles('client/components/viewer/viewer.styl', 'client');
@ -101,6 +98,8 @@ Package.onUse(function(api) {
api.addFiles('client/components/activeEntry/activeEntry.styl', 'client'); api.addFiles('client/components/activeEntry/activeEntry.styl', 'client');
api.addFiles('client/components/activeEntry/activeEntry.js', 'client'); api.addFiles('client/components/activeEntry/activeEntry.js', 'client');
api.addFiles('client/components/activeEntry/activeEntrySignIn/activeEntrySignIn.js', 'client');
api.addFiles('client/components/hipaaLogPage/hipaaLogPage.styl', 'client'); api.addFiles('client/components/hipaaLogPage/hipaaLogPage.styl', 'client');
api.addFiles('client/components/hipaaLogPage/hipaaLogPage.js', 'client'); api.addFiles('client/components/hipaaLogPage/hipaaLogPage.js', 'client');
@ -197,7 +196,6 @@ Package.onUse(function(api) {
api.addFiles('client/components/confirmRemoveTimepointAssociation/confirmRemoveTimepointAssociation.js', 'client'); api.addFiles('client/components/confirmRemoveTimepointAssociation/confirmRemoveTimepointAssociation.js', 'client');
api.addFiles('client/components/lastLoginModal/lastLoginModal.html', 'client'); api.addFiles('client/components/lastLoginModal/lastLoginModal.html', 'client');
api.addFiles('client/components/lastLoginModal/lastLoginModal.js', 'client');
api.addFiles('client/components/confirmDeleteDialog/confirmDeleteDialog.html', 'client'); api.addFiles('client/components/confirmDeleteDialog/confirmDeleteDialog.html', 'client');
api.addFiles('client/components/confirmDeleteDialog/confirmDeleteDialog.styl', 'client'); api.addFiles('client/components/confirmDeleteDialog/confirmDeleteDialog.styl', 'client');
@ -208,10 +206,11 @@ Package.onUse(function(api) {
api.addFiles('client/components/emailVerification/emailVerification.js', 'client'); api.addFiles('client/components/emailVerification/emailVerification.js', 'client');
// Server functions // Server functions
api.addFiles('server/servers.js', 'server');
api.addFiles('server/publications.js', 'server'); api.addFiles('server/publications.js', 'server');
api.addFiles('server/servers.js', 'server');
api.addFiles('server/methods.js', [ 'server' ]); api.addFiles('server/methods.js', [ 'server' ]);
api.addFiles('server/reviewers.js', [ 'server' ]); api.addFiles('server/reviewers.js', [ 'server' ]);
api.addFiles('server/createDemoUser.js', [ 'server' ]);
// Both client and server functions // Both client and server functions
api.addFiles('both/collections.js', [ 'client', 'server' ]); api.addFiles('both/collections.js', [ 'client', 'server' ]);

View File

@ -0,0 +1,39 @@
import { Accounts } from 'meteor/accounts-base';
Meteor.startup(function() {
const options = {
email: 'demo@ohif.org',
password: '12345678aA*',
profile: {
fullName: 'Demo User'
}
};
createDemoUser = function() {
const user = Meteor.users.findOne({
emails: {
$elemMatch: {
address: 'demo@ohif.org'
}
}
});
if (user) {
return;
}
console.log('create user!');
// Create user
const userId = Accounts.createUser(options);
if (!userId) {
console.log('Demo user cannot be created');
return;
}
};
// Create demo user
createDemoUser();
});

View File

@ -85,7 +85,13 @@ Meteor.methods({
}, },
getPriorLoginDate: function() { getPriorLoginDate: function() {
return Meteor.users.findOne({_id: Meteor.userId()}, {fields: {priorLoginDate: 1}}); return Meteor.users.findOne({
_id: Meteor.userId()
}, {
fields: {
priorLoginDate: 1
}
}).priorLoginDate;
} }
}); });

View File

@ -69,4 +69,4 @@ Meteor.methods({
serverSave: serverSettings => ServersControl.save(serverSettings), serverSave: serverSettings => ServersControl.save(serverSettings),
serverSetActive: serverId => ServersControl.setActive(serverId), serverSetActive: serverId => ServersControl.setActive(serverId),
serverRemove: serverId => ServersControl.remove(serverId) serverRemove: serverId => ServersControl.remove(serverId)
}); });

View File

@ -0,0 +1 @@
import './schema.js';

View File

@ -0,0 +1,34 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
/*
Extend the available options on schema definitions:
* valuesLabels: Used in conjunction with allowedValues to define the text
label for each value (used on forms)
* textOptional: Used to allow empty strings
*/
SimpleSchema.extendOptions({
valuesLabels: Match.Optional([String]),
textOptional: Match.Optional(Boolean)
});
// Add default required validation for empty strings which can be bypassed
// using textOptional=true definition
SimpleSchema.addValidator(function() {
if (
this.definition.optional !== true &&
this.definition.textOptional !== true &&
this.value === ''
) {
return 'required';
}
});
// Including [label] for some messages
SimpleSchema.messages({
maxCount: '[label] can not have more than [maxCount] values',
minCount: '[label] must have at least [minCount] values',
notAllowed: '[label] has an invalid value: "[value]"'
});

View File

@ -10,6 +10,7 @@ import './mixins/formItem.js';
import './mixins/group.js'; import './mixins/group.js';
import './mixins/groupRadio.js'; import './mixins/groupRadio.js';
import './mixins/input.js'; import './mixins/input.js';
import './mixins/checkbox.js';
import './mixins/schemaData.js'; import './mixins/schemaData.js';
import './mixins/select.js'; import './mixins/select.js';
import './mixins/select2.js'; import './mixins/select2.js';

View File

@ -0,0 +1,26 @@
import { OHIF } from 'meteor/ohif:core';
import { Template } from 'meteor/templating';
import { _ } from 'meteor/underscore';
/*
* inputCheckbox: controls a checkbox input
*/
OHIF.mixins.checkbox = new OHIF.Mixin({
dependencies: 'input',
composition: {
onCreated() {
const instance = Template.instance();
const component = instance.component;
// Get or set the checked state using jQuery's prop method
component.value = value => {
const isGet = _.isUndefined(value);
if (isGet) {
return component.parseData(component.$element.is(':checked'));
}
component.$element.prop('checked', value);
};
}
}
});

View File

@ -1,5 +1,8 @@
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { Tracker } from 'meteor/tracker';
import { _ } from 'meteor/underscore';
import { $ } from 'meteor/jquery';
/* /*
* form: controls a form and its registered inputs * form: controls a form and its registered inputs
@ -11,40 +14,83 @@ OHIF.mixins.form = new OHIF.Mixin({
const instance = Template.instance(); const instance = Template.instance();
const component = instance.component; const component = instance.component;
// Define the form's data schema // Set the form identifier flag
const schema = instance.data.schema; component.isForm = true;
component.schema = schema && schema.newContext();
// Check if the form data is valid in its schema component.validationObserver = new Tracker.Dependency();
// Reset the pathKey
instance.data.pathKey = '';
// Debound the observer call to prevent tons of re-rendering
component.validationRan = _.debounce(() => {
// Enable reactivity by changing a Tracker.Dependency observer
component.validationObserver.changed();
}, 200);
// Change the validation function to focus the fields with error
const validateSelf = component.validate;
component.validate = () => { component.validate = () => {
// Assume validation result as true // Call the original validation function
let result = true; validateSelf();
// Return true if there's no data schema defined // Focus the first error field if some validation failed
if (!component.schema) { if (component.schema && component.schema._invalidKeys.length) {
return result; instance.$('.state-error :input:first').focus();
} }
// Iterate over each registered form item and validate it
component.registeredItems.forEach(child => {
const key = child.templateInstance.data.key;
// Change result to false if any form item is invalid
if (key && !child.validate()) {
result = false;
}
});
// Return the validation result
return result;
}; };
}, },
onRendered() { onRendered() {
const instance = Template.instance(); const instance = Template.instance();
const component = instance.component; const component = instance.component;
// Set the component main and style elements // Set the component main and style elements
component.$style = component.$element = instance.$('form:first'); component.$style = component.$element = instance.$('form:first');
},
events: {
'click .validation-error-container a'(event, instance) {
// Get the target key
const targetKey = $(event.currentTarget).attr('data-target');
// Focus the first input inside the element with error state
instance.$(`.state-error[data-key="${targetKey}"] :input:first`).focus();
}
},
helpers: {
validationErrors() {
const instance = Template.instance();
const component = instance.component;
// Create a dependency on child components validation
component.validationObserver.depend();
// Stop here if no schema was defined for the form
if (!component.schema) {
return;
}
// Check if there were some validation errors
if (component.schema._invalidKeys.length) {
const result = [];
// Iterate over each validation error and add to result
component.schema._invalidKeys.forEach(item => {
const label = component.schema._schema[item.name].label;
let message = component.schema.keyErrorMessage(item.name);
message = message.replace(label, `<strong>${label}</strong>`);
result.push({
key: item.name,
message: Spacebars.SafeString(message)
});
});
// Return the resulting validation errors
return result;
}
}
} }
} }
}); });

View File

@ -1,6 +1,7 @@
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { Tracker } from "meteor/tracker"; import { Tracker } from 'meteor/tracker';
import { Meteor } from 'meteor/meteor';
import { _ } from 'meteor/underscore'; import { _ } from 'meteor/underscore';
import { $ } from 'meteor/jquery'; import { $ } from 'meteor/jquery';
@ -31,7 +32,7 @@ OHIF.mixins.formItem = new OHIF.Mixin({
component.value = value => { component.value = value => {
const isGet = _.isUndefined(value); const isGet = _.isUndefined(value);
if (isGet) { if (isGet) {
return component.$element.val(); return component.parseData(component.$element.val());
} }
component.$element.val(value).trigger('change'); component.$element.val(value).trigger('change');
@ -53,6 +54,19 @@ OHIF.mixins.formItem = new OHIF.Mixin({
component.$wrapper[method](); component.$wrapper[method]();
}; };
// Check if the focus is inside this element
component.hasFocus = () => {
// Get the focused element
const focused = $(':focus')[0];
// Check if the focused element is inside the component
const contains = $.contains(component.$wrapper[0], focused);
const isEqual = component.$wrapper[0] === focused;
// Return true if he component has the focus
return contains || isEqual;
};
// Add or remove a state from the component // Add or remove a state from the component
component.state = (state, flag) => { component.state = (state, flag) => {
component.$wrapper.toggleClass(`state-${state}`, !!flag); component.$wrapper.toggleClass(`state-${state}`, !!flag);
@ -65,24 +79,77 @@ OHIF.mixins.formItem = new OHIF.Mixin({
// Set or remove the error message // Set or remove the error message
if (errorMessage) { if (errorMessage) {
component.$element.trigger('errorin');
component.$wrapper.attr('data-error', errorMessage); component.$wrapper.attr('data-error', errorMessage);
} else { } else {
component.$element.trigger('errorout');
component.$wrapper.removeAttr('data-error', errorMessage); component.$wrapper.removeAttr('data-error', errorMessage);
} }
}; };
// Toggle the tooltip over the component
component.toggleTooltip = (isShow, message) => {
if (isShow && message) {
// Stop here if the tooltip is already created
if (component.$wrapper.next('.tooltip').length) {
return;
}
// Create the tooltip
component.$wrapper.tooltip({
trigger: 'manual',
title: message
}).tooltip('show');
} else {
// Destroy the tooltip
component.$wrapper.tooltip('destroy');
}
};
// Toggle a state message as a tooltip over the component
component.toggleMessage = isShow => {
// Check if the action is to hide
if (!isShow) {
Meteor.setTimeout(() => {
// Check if the component has the focus
if (component.hasFocus()) {
// Prevent the tooltip from being hidden
return;
}
// Hide the tooltip
component.toggleTooltip(false);
}, 100);
return;
}
// Check for error state and message
const errorMessage = component.$wrapper.attr('data-error');
if (errorMessage) {
// Show the tooltip with the error message
component.toggleTooltip(true, errorMessage);
}
},
// Search for the parent form component
component.getForm = () => {
let currentComponent = component;
while (currentComponent) {
currentComponent = currentComponent.parent;
if (currentComponent && currentComponent.isForm) {
return currentComponent;
}
}
};
// Check if the component value is valid in its form's schema // Check if the component value is valid in its form's schema
component.validate = () => { component.validate = () => {
// Get the component's form // Get the component's form
const form = component.parent; const form = component.getForm();
// Get the form's data schema // Get the form's data schema
const schema = form && form.schema; const schema = form && form.schema;
// Get the current component's key // Get the current component's key
const key = instance.data.key; const key = instance.data.pathKey;
// Return true if validation is not needed // Return true if validation is not needed
if (!key || !schema || !component.$wrapper.is(':visible')) { if (!key || !schema || !component.$wrapper.is(':visible')) {
@ -90,12 +157,18 @@ OHIF.mixins.formItem = new OHIF.Mixin({
} }
// Create the data document for validation // Create the data document for validation
const document = { const document = OHIF.blaze.getNestedObject({
[key]: component.value() [key]: component.value()
}; });
// Get the validation result
const validationResult = schema.validateOne(document, key);
// Notify the form that the validation ran
form.validationRan();
// Check if the document validation failed // Check if the document validation failed
if (!schema.validateOne(document, key)) { if (!validationResult) {
// Set the component in error state and display the message // Set the component in error state and display the message
component.error(schema.keyErrorMessage(key)); component.error(schema.keyErrorMessage(key));
@ -125,6 +198,9 @@ OHIF.mixins.formItem = new OHIF.Mixin({
// Set the most outer wrapper element // Set the most outer wrapper element
component.$wrapper = instance.wrapper.$('*').first(); component.$wrapper = instance.wrapper.$('*').first();
// Add the pathKey to the wrapper element
component.$wrapper.attr('data-key', instance.data.pathKey);
}, },
onDestroyed() { onDestroyed() {
@ -143,24 +219,55 @@ OHIF.mixins.formItem = new OHIF.Mixin({
component.$style = component.$element; component.$style = component.$element;
} }
// Set the component in jQuery data after all mixins are rendered // Set the component in element and wrapper jQuery data
component.$element.data('component', component); component.$element.data('component', component);
component.$wrapper.data('component', component);
}, },
events: { events: {
// Enable reactivity by changing a Tracker.Dependency observer // Handle the change event for the component
change(event, instance) { change(event, instance) {
instance.component.changeObserver.changed(); const component = instance.component;
// Prevent execution on upper components
if (event.currentTarget === component.$element[0]) {
// Enable reactivity by changing a Tracker.Dependency observer
component.changeObserver.changed();
// Revalidate the component
component.validate();
}
}, },
// TODO: [design] remove log, show error box/hint over the wrapper focus(event, instance) {
errorin(event, instance) { const component = instance.component;
console.log('ERROR when validating component', instance.component);
// Stop here if it is an group
if (component.isGroup || component.isCustomFocus) {
return;
}
// Prevent event bubbling
event.stopPropagation();
// Check for state messages and show it
component.toggleMessage(true);
}, },
// TODO: [design] hide error box/hint blur(event, instance) {
errorout(event, instance) { const component = instance.component;
// Stop here if it is an group
if (component.isGroup || component.isCustomFocus) {
return;
}
// Prevent event bubbling
event.stopPropagation();
// Hide state messages
component.toggleMessage(false);
} }
} }

View File

@ -12,29 +12,124 @@ OHIF.mixins.group = new OHIF.Mixin({
const instance = Template.instance(); const instance = Template.instance();
const component = instance.component; const component = instance.component;
// Set the group identifier flag
component.isGroup = true;
// Run this computation every time the schema property is changed
instance.autorun(() => {
let schema = instance.data.schema;
// Check if the schema is reactive
if (schema instanceof ReactiveVar) {
// Register a dependency on schema property
schema = schema.get();
}
// Set the form's data schema
component.schema = schema && schema.newContext();
});
// Get or set the child components values // Get or set the child components values
component.value = value => { component.value = value => {
const isGet = _.isUndefined(value); const isGet = _.isUndefined(value);
const isArray = instance.data.arrayValues;
// Create the result data as array or object
const result = isArray ? [] : {};
// Get the group current value and return it
if (isGet) { if (isGet) {
const result = {}; // Iterate over each registered item and extract its value
component.registeredItems.forEach(child => { component.registeredItems.forEach(child => {
const key = child.templateInstance.data.key; // Check if it is an array or an object group
if (key) { if (isArray) {
result[key] = child.value(); // Push the item value to the result array
result.push(child.value());
} else {
// Get the item key
const key = child.templateInstance.data.key;
//Check if a key is set for the item
if (key) {
// Add the item value to the result object
result[key] = child.value();
}
} }
}); });
// Return the resulting data as array or object
return result; return result;
} }
const groupValue = typeof value === 'object' ? value : {}; // Get the group current value
const groupValue = typeof value === 'object' ? value : result;
// Stop here if there is no value defined for this group
if (!groupValue) {
return;
}
// Iterate over each registered item and set its value
let i = 0;
component.registeredItems.forEach(child => { component.registeredItems.forEach(child => {
const key = child.templateInstance.data.key; const key = isArray ? i : child.templateInstance.data.key;
const childValue = _.isUndefined(groupValue[key]) ? null : groupValue[key]; const childValue = _.isUndefined(groupValue[key]) ? null : groupValue[key];
child.value(childValue); child.value(childValue);
i++;
}); });
// Trigger the change event after setting the new value
component.$element.trigger('change'); component.$element.trigger('change');
}; };
// Get a registered item in form by its key
component.item = itemKey => {
let found;
// Iterate over each registered form item
component.registeredItems.forEach(child => {
const key = child.templateInstance.data.key;
// Change the found item if current key is the same as given
if (key === itemKey) {
found = child;
}
});
// Return the found item or undefined if it was not found
return found;
};
// Check if the form data is valid in its schema
const validateSelf = component.validate;
component.validate = () => {
// Assume validation result as true
let result = true;
// Return true if there's no data schema defined
if (component.isForm && !component.schema) {
return result;
}
// Validate the component itself if it has a key
if (instance.data.pathKey && !validateSelf()) {
result = false;
}
// Iterate over each registered form item and validate it
component.registeredItems.forEach(child => {
const key = child.templateInstance.data.key;
// Change result to false if any form item is invalid
if ((key || instance.data.arrayValues) && !child.validate()) {
result = false;
}
});
// Return the validation result
return result;
};
// Disable or enable the component // Disable or enable the component
component.disable = isDisable => { component.disable = isDisable => {
component.registeredItems.forEach(child => child.disable(isDisable)); component.registeredItems.forEach(child => child.disable(isDisable));

View File

@ -21,7 +21,7 @@ OHIF.mixins.groupRadio = new OHIF.Mixin({
component.registeredItems.forEach(child => elements.push(child.$element[0])); component.registeredItems.forEach(child => elements.push(child.$element[0]));
const $elements = $(elements); const $elements = $(elements);
if (isGet) { if (isGet) {
return $elements.filter(':checked').val(); return component.parseData($elements.filter(':checked').val());
} }
$elements.filter(`[value='${value}']`).prop('checked', true).trigger('change'); $elements.filter(`[value='${value}']`).prop('checked', true).trigger('change');

View File

@ -7,7 +7,13 @@ import { _ } from 'meteor/underscore';
// Helper function to get the component's current schema // Helper function to get the component's current schema
const getCurrentSchema = (parentComponent, key) => { const getCurrentSchema = (parentComponent, key) => {
// Get the parent component schema // Get the parent component schema
const schema = parentComponent && parentComponent.schema; let schema = parentComponent && parentComponent.schema;
// Try to get the form schema if it was not found
if (parentComponent && !schema) {
const form = parentComponent.getForm();
schema = form && form.schema;
}
// Stop here if there's no key or schema defined // Stop here if there's no key or schema defined
if (!key || !schema) { if (!key || !schema) {
@ -17,6 +23,11 @@ const getCurrentSchema = (parentComponent, key) => {
// Get the current schema data using component's key // Get the current schema data using component's key
const currentSchema = _.clone(schema._schema[key]); const currentSchema = _.clone(schema._schema[key]);
// Stop here if no schema was found for the given key
if (!currentSchema) {
return;
}
// Merge the sub-schema properties if it's an array // Merge the sub-schema properties if it's an array
if (Array.isArray(currentSchema.type())) { if (Array.isArray(currentSchema.type())) {
_.extend(currentSchema, schema._schema[key + '.$']); _.extend(currentSchema, schema._schema[key + '.$']);
@ -40,8 +51,21 @@ OHIF.mixins.schemaData = new OHIF.Mixin({
// Get the parent component // Get the parent component
const parent = OHIF.blaze.getParentComponent(Blaze.currentView); const parent = OHIF.blaze.getParentComponent(Blaze.currentView);
// Get he parent component key
const parentKey = parent && parent.templateInstance.data.pathKey;
// Check if the parent is an array group
const isParentArray = parent && parent.templateInstance.data.arrayValues;
// Set the path key for this component
data.pathKey = data.key || (isParentArray ? '$' : '');
if (data.pathKey && typeof parentKey === 'string') {
const prefix = parentKey ? `${parentKey}.` : '';
data.pathKey = `${prefix}${data.pathKey}`;
}
// Get the current schema data using component's key // Get the current schema data using component's key
const currentSchema = getCurrentSchema(parent, data.key); const currentSchema = getCurrentSchema(parent, data.pathKey);
// Stop here if there's no schema data for current key // Stop here if there's no schema data for current key
if (!currentSchema) { if (!currentSchema) {
@ -53,6 +77,11 @@ OHIF.mixins.schemaData = new OHIF.Mixin({
data.label = new ReactiveVar(currentSchema.label); data.label = new ReactiveVar(currentSchema.label);
} }
// Set the emptyOption data attribute if given on schema
if (currentSchema.emptyOption) {
data.emptyOption = currentSchema.emptyOption;
}
// Fill the items if it's an array schema // Fill the items if it's an array schema
if (!data.items && Array.isArray(currentSchema.allowedValues)) { if (!data.items && Array.isArray(currentSchema.allowedValues)) {
// Initialize the items array // Initialize the items array
@ -76,12 +105,41 @@ OHIF.mixins.schemaData = new OHIF.Mixin({
} }
}, },
onCreated() {
const instance = Template.instance();
const component = instance.component;
// Create a data parser according to current schema key
component.parseData = value => {
// Get the current schema data using component's key
const currentSchema = getCurrentSchema(component.parent, instance.data.pathKey);
// Stop here if there's no schema data for current key
if (!currentSchema) {
return;
}
// Check if the schema is a Number
if (currentSchema.type === Number) {
return parseFloat(value);
}
// Check if the schema is a Boolean
if (currentSchema.type === Boolean) {
return !!value;
}
// Return the original value if none of the checks matched
return value;
};
},
onMixins() { onMixins() {
const instance = Template.instance(); const instance = Template.instance();
const component = instance.component; const component = instance.component;
// Get the current schema data using component's key // Get the current schema data using component's key
const currentSchema = getCurrentSchema(component.parent, instance.data.key); const currentSchema = getCurrentSchema(component.parent, instance.data.pathKey);
// Stop here if there's no schema data for current key // Stop here if there's no schema data for current key
if (!currentSchema) { if (!currentSchema) {

View File

@ -8,6 +8,46 @@ import { $ } from 'meteor/jquery';
OHIF.mixins.select2 = new OHIF.Mixin({ OHIF.mixins.select2 = new OHIF.Mixin({
dependencies: 'select', dependencies: 'select',
composition: { composition: {
onCreated() {
const instance = Template.instance();
// Set the custom focus flag
instance.component.isCustomFocus = true;
// Check if this select will include a placeholder
const placeholder = instance.data.options && instance.data.options.placeholder;
if (placeholder) {
// Get the option items
let items = instance.data.items;
// Check if the items are reactive and get them if true
const isReactive = items instanceof ReactiveVar;
if (isReactive) {
items = items.get();
}
// Check if there is already an empty option on items list
const query = {
value: ''
};
if (!_.findWhere(items, query)) {
// Clone the current items
const newItems = _.clone(items) || [];
newItems.unshift({
label: placeholder,
value: ''
});
// Set the new items list including the empty option
if (isReactive) {
instance.data.items.set(newItems);
} else {
instance.data.items = newItems;
}
}
}
},
onRendered() { onRendered() {
const instance = Template.instance(); const instance = Template.instance();
const component = instance.component; const component = instance.component;
@ -17,6 +57,22 @@ OHIF.mixins.select2 = new OHIF.Mixin({
// Store the select2 instance to allow its further destruction // Store the select2 instance to allow its further destruction
component.select2Instance = component.$element.data('select2'); component.select2Instance = component.$element.data('select2');
// Get the focusable elements
const elements = [];
elements.push(component.$element[0]);
elements.push(component.$element.nextAll('.select2:first').find('.select2-selection')[0]);
// Attach focus and blur handlers to focusable elements
$(elements).on('focus', event => {
event.stopPropagation();
// Show the state message on elements focus
component.toggleMessage(true);
}).on('blur', event => {
event.stopPropagation();
// Hide the state message on elements blur
component.toggleMessage(false);
});
}, },
onDestroyed() { onDestroyed() {
@ -25,14 +81,6 @@ OHIF.mixins.select2 = new OHIF.Mixin({
// Destroy the select2 instance to remove unwanted DOM elements // Destroy the select2 instance to remove unwanted DOM elements
component.select2Instance.destroy(); component.select2Instance.destroy();
},
events: {
'focus .select2-hidden-accessible'(event, instance) {
// Redirect the focus to select2 focus control in case of hidden
// accessible being focused (e.g. clicking on outer label)
$(event.currentTarget).nextAll('.select2:first').find('.select2-selection').focus();
}
} }
} }
}); });

View File

@ -30,7 +30,7 @@ OHIF.mixins.selectTree = new OHIF.Mixin({
// Return the current value // Return the current value
if (isGet) { if (isGet) {
return rootInstance.currentValue; return component.parseData(rootInstance.currentValue);
} }
// Change the current value // Change the current value

View File

@ -11,6 +11,13 @@
target="{{this.target}}" target="{{this.target}}"
{{this.tagAttributes}} {{this.tagAttributes}}
> >
{{#if validationErrors}}
<div class="validation-error-container alert alert-danger" role="alert">
{{#each error in validationErrors}}
<p><a href="#" class="text-danger" data-target="{{error.key}}">{{error.message}}</a></p>
{{/each}}
</div>
{{/if}}
{{>UI.contentBlock}} {{>UI.contentBlock}}
</form> </form>
</template> </template>

View File

@ -4,6 +4,7 @@
class="{{this.class}}" class="{{this.class}}"
name="{{this.name}}" name="{{this.name}}"
multiple="{{#if this.multiple}}multiple{{/if}}" multiple="{{#if this.multiple}}multiple{{/if}}"
disabled="{{#if this.disabled}}disabled{{/if}}"
{{this.tagAttributes}} {{this.tagAttributes}}
> >
{{>UI.contentBlock}} {{>UI.contentBlock}}

View File

@ -1,6 +1,8 @@
import './form/form.html'; import './form/form.html';
import './form/group.html'; import './form/group.html';
import './input/checkbox.html';
import './input/hidden.html';
import './input/groupRadio.html'; import './input/groupRadio.html';
import './input/radio.html'; import './input/radio.html';
import './input/select.html'; import './input/select.html';

View File

@ -0,0 +1,15 @@
<template name="inputCheckbox">
{{#baseComponent (extend this
base='baseInput'
type='checkbox'
mixins=(concat 'checkbox ' this.mixins)
labelAfter=(valueIf (isUndefined this.labelAfter) true this.labelAfter)
labelClass=(concat 'checkboxLabel ' this.labelClass)
wrappers=(concat ''
(valueIf reactive this.label 'wrapperLabel ' '')
this.wrappers
)
)}}
{{>UI.contentBlock}}
{{/baseComponent}}
</template>

View File

@ -0,0 +1,7 @@
<template name="inputHidden">
{{>baseComponent (extend this
base='baseInput'
type='hidden'
mixins=(concat 'input ' this.mixins)
)}}
</template>

View File

@ -4,7 +4,7 @@
type='radio' type='radio'
mixins=(concat 'input ' this.mixins) mixins=(concat 'input ' this.mixins)
labelAfter=(valueIf (isUndefined this.labelAfter) true this.labelAfter) labelAfter=(valueIf (isUndefined this.labelAfter) true this.labelAfter)
labelClass=(concat 'checkboxLabel ' this.labelClass) labelClass=(concat 'radioLabel ' this.labelClass)
wrappers=(concat '' wrappers=(concat ''
(valueIf reactive this.label 'wrapperLabel ' '') (valueIf reactive this.label 'wrapperLabel ' '')
this.wrappers this.wrappers

View File

@ -11,17 +11,19 @@
<div class="tree-content"> <div class="tree-content">
{{#let currentNode=this.currentNode.get}} {{#let currentNode=this.currentNode.get}}
{{#if (and (not this.root) (ne this.search false))}} {{#if (and (not this.root) (ne this.search false))}}
{{>inputText {{#inputText
label=this.label label=this.label
labelClass='tree-search' labelClass='tree-search'
placeholder=(choose this.searchPlaceholder 'Search') placeholder=(choose this.searchPlaceholder 'Search')
}} }}
<i class="fa fa-search"></i>
{{/inputText}}
{{/if}} {{/if}}
<div class="tree-options {{#if currentNode}}collapsed{{/if}}"> <div class="tree-options {{#if currentNode}}collapsed{{/if}}">
{{#if this.root}} {{#if this.root}}
{{>selectTreeBreadcrumb component=this.component}} {{>selectTreeBreadcrumb component=this.component}}
{{/if}} {{/if}}
<div class="tree-inputs"> <div class="tree-inputs clearfix">
{{#each item in treeItems}} {{#each item in treeItems}}
{{>inputRadio {{>inputRadio
id=(concat this.storageKey '_' (encodeId item.value)) id=(concat this.storageKey '_' (encodeId item.value))
@ -45,8 +47,8 @@
</div> </div>
{{/let}} {{/let}}
</div> </div>
{{#if (and (not this.root) this.storageKey)}} {{#if (and (not this.root) (or this.storageKey this.commonItems))}}
{{>selectTreeCommon (clone this component=this.component)}} {{>selectTreeCommon (clone this component=this.component treeInstance=instance)}}
{{/if}} {{/if}}
{{/group}} {{/group}}
</template> </template>

View File

@ -11,6 +11,45 @@ import transition from 'meteor/ohif:core/client/lib/third-party/transition-to-fr
Template.selectTree.onCreated(() => { Template.selectTree.onCreated(() => {
const instance = Template.instance(); const instance = Template.instance();
// Return the common items
instance.getCommonItems = () => {
const instance = Template.instance();
// Return the common items if given
if (instance.data.commonItems) {
return instance.data.commonItems;
}
// Get all the tree leaves
const leaves = instance.data.component.getLeaves();
// Generate an object with encoded keys from the tree leaves
leavesObject = {};
_.each(leaves, leaf => {
leavesObject[OHIF.string.encodeId(leaf.value)] = leaf;
});
// Get the current items ranking
const ranking = OHIF.user.getData(instance.data.storageKey);
// Sort the items based on how many times each one was used
const sorted = [];
_.each(ranking, (count, key) => sorted.push([key, count]));
sorted.sort((a, b) => b[1] - a[1]);
// Create the result and push every item respecting the ranking order
const result = [];
_.each(sorted, item => {
const current = leavesObject[item[0]];
if (current) {
result.push(current);
}
});
// Return the resulting array
return result;
};
// Create a reactive variable to control the search // Create a reactive variable to control the search
instance.searchTerm = new ReactiveVar(''); instance.searchTerm = new ReactiveVar('');
}); });
@ -25,7 +64,10 @@ Template.selectTree.onRendered(() => {
instance.component = component; instance.component = component;
// Set the margin to display the common section // Set the margin to display the common section
$treeRoot.children('.tree-content').css('margin-right', $treeRoot.width()); const isTreeColumns = instance.data.treeColumns;
const marginProperty = isTreeColumns ? 'margin-left' : 'margin-right';
const marginWidth = isTreeColumns ? $treeRoot.width() / 2 : $treeRoot.width();
$treeRoot.children('.tree-content').css(marginProperty, marginWidth);
// Make the component respect the window boundaries // Make the component respect the window boundaries
$treeRoot.bounded(); $treeRoot.bounded();
@ -144,6 +186,18 @@ Template.selectTree.events({
$treeRoot.addClass('interacted'); $treeRoot.addClass('interacted');
}, },
'keydown .tree-search input'(event, instance) {
// Get the search term
const searchTerm = $(event.currentTarget).val().trim();
const $labels = instance.$('.tree-inputs:first>label');
// Select the label if ENTER was pressed and there is only one result
if (searchTerm.length && $labels.length === 1 && event.which === 13) {
$labels.find('input').click();
}
},
'change .select-tree:first>.tree-content>.tree-options>.tree-inputs>label>input'(event, instance) { 'change .select-tree:first>.tree-content>.tree-options>.tree-inputs>label>input'(event, instance) {
const component = instance.component; const component = instance.component;
const $target = $(event.currentTarget); const $target = $(event.currentTarget);
@ -235,27 +289,6 @@ Template.selectTree.events({
currentInstance = currentInstance.component.parent.templateInstance; currentInstance = currentInstance.component.parent.templateInstance;
} }
} }
},
'click .select-tree-common label'(event, instance) {
// Get the clicked label
const $target = $(event.currentTarget);
// Build the input selector based on the label target
const inputSelector = '#' + $target.attr('for');
// Check if the input is not rendered
if (!$(inputSelector).length) {
// Change the search terms with the clicked label string
instance.$('.tree-search input').val($target.text()).trigger('input');
// Wait for options rerendering
Tracker.afterFlush(() => {
// Wait for components initialization
Meteor.defer(() => $(inputSelector).click());
});
}
} }
}); });
@ -276,6 +309,23 @@ Template.selectTree.helpers({
// Filter only the tree leaves // Filter only the tree leaves
items = _.filter(items, item => !item.items); items = _.filter(items, item => !item.items);
} else if (instance.data.hideCommon) {
// Get the values of common items
const commonValues = _.pluck(instance.getCommonItems(), 'value');
// Filter only the items that are not common
items = _.filter(items, item => !_.contains(commonValues, item.value));
}
// Return the items sorted for tree columns
if (instance.data.treeColumns) {
const begin = items.splice(0, Math.ceil(items.length / 2));
const sortedItems = [];
_.each(begin, (item, index) => {
sortedItems.push(item);
items[index] && sortedItems.push(items[index]);
});
items = sortedItems;
} }
// Return the tree items // Return the tree items

View File

@ -2,38 +2,35 @@ import { OHIF } from 'meteor/ohif:core';
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { _ } from 'meteor/underscore'; import { _ } from 'meteor/underscore';
Template.selectTreeCommon.helpers({ Template.selectTreeCommon.events({
// Get the common items ordered by use history 'click .select-tree-common label'(event, instance) {
items() { // Get the clicked label
const instance = Template.instance(); const $target = $(event.currentTarget);
// Get all the tree leaves // Build the input selector based on the label target
const leaves = instance.data.component.getLeaves(); const inputSelector = '#' + $target.attr('for');
// Generate an object with encoded keys from the tree leaves // Get the tree instance
leavesObject = {}; const treeInstance = instance.data.treeInstance;
_.each(leaves, leaf => {
leavesObject[OHIF.string.encodeId(leaf.value)] = leaf;
});
// Get the current items ranking // Check if the input is not rendered
const ranking = OHIF.user.getData(instance.data.storageKey); if (!$(inputSelector).length) {
// Change the search terms with the clicked label string
// Sort the items based on how many times each one was used treeInstance.$('.tree-search input').val($target.text()).trigger('input');
const sorted = []; // Wait for options rerendering
_.each(ranking, (count, key) => sorted.push([key, count]));
sorted.sort((a, b) => b[1] - a[1]);
// Create the result and push every item respecting the ranking order Tracker.afterFlush(() => {
const result = []; // Wait for components initialization
_.each(sorted, item => { Meteor.defer(() => $(inputSelector).click());
const current = leavesObject[item[0]]; });
if (current) { }
result.push(current); }
} });
});
Template.selectTreeCommon.helpers({
// Return the resulting array // Get the common items
return result; items() {
return Template.instance().data.treeInstance.getCommonItems();
} }
}); });

View File

@ -1,6 +1,6 @@
<template name="componentPlayground"> <template name="componentPlayground">
<div class="component-playground"> <div class="component-playground">
{{>measureFlow}} {{>measureFlow treeColumns=true hideCommon=true}}
<!-- <!--
<div class="p-a-3"> <div class="p-a-3">
{{>selectTree {{>selectTree

View File

@ -31,7 +31,7 @@ OHIF.blaze.getParentView = (view, parentViewName) => {
}; };
// Search for the parent component of the given view // Search for the parent component of the given view
OHIF.blaze.getParentComponent = (view) => { OHIF.blaze.getParentComponent = view => {
let currentView = view; let currentView = view;
while (currentView) { while (currentView) {
currentView = currentView.originalParentView || currentView.parentView; currentView = currentView.originalParentView || currentView.parentView;

View File

@ -1,4 +1,5 @@
import './blaze.js'; import './blaze.js';
import './object.js';
import './string.js'; import './string.js';
import './ui.js'; import './ui.js';
import './user.js'; import './user.js';

Some files were not shown because too many files have changed in this diff Show More