From 4f1a97d731c4536d7ce675c5a728710c0af9fcc7 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Fri, 30 Jun 2017 19:12:31 +0100 Subject: [PATCH] All features except adjustable redzone. --- resources/public/css/re-com.css | 1352 +++++++++++++++++ resources/public/index.html | 2 +- .../swinging_needle_meter.cljs | 86 +- src/cljs/swinging_needle_meter/views.cljs | 10 +- 4 files changed, 1421 insertions(+), 29 deletions(-) create mode 100644 resources/public/css/re-com.css diff --git a/resources/public/css/re-com.css b/resources/public/css/re-com.css new file mode 100644 index 0000000..026254e --- /dev/null +++ b/resources/public/css/re-com.css @@ -0,0 +1,1352 @@ +/*---------------------------------------------------------------------------------------- + re-com.css + + This file is required for styling the re-com UI library components. + To use the library, bootstrap.css is also required, which is best obtained from a CDN. + So, place the following lines in the section of the html file: + + + + + The following is required for constraining an application to the + height of the browser window and setting some global defaults like font... +----------------------------------------------------------------------------------------*/ + +/* The following style addresses: http://stackoverflow.com/questions/28636832/firefox-overflow-y-not-working-with-nested-flexbox */ +* { + min-height: 0px; + min-width: 0px; +} + +html, body { + font-family: Segoe UI, Roboto, sans-serif; + font-size: 14px; + font-weight: 400; + color: rgba(68, 68, 68, 1.0); + letter-spacing: 0.002em; + height: 100%; + margin: 0px; + padding: 0px; +} + +/*---------------------------------------------------------------------------------------- + Bootstrap overrides after upgrading to 3.3.5 +----------------------------------------------------------------------------------------*/ + +.popover, .tooltip { + font-family: Segoe UI, Roboto, sans-serif; +} + +.popover-title { + font-weight: normal; +} + +.form-control-feedback { + pointer-events: initial; +} + + +/*---------------------------------------------------------------------------------------- + The section immediately below is required for the drop-down components and comes from + the bootstrap-chosen library: + + https://github.com/alxlit/bootstrap-chosen + License: MIT. + + That library provides it's css as a .less file. + To compile the less file into the css below, use the following steps from + a command prompt (you'll need git and the lessc compiler (http://lesscss.org): + + git clone https://github.com/alxlit/bootstrap-chosen + + cd bootstrap-chosen + + git clone --depth=1 https://github.com/twbs/bootstrap + + Now edit bootstrap-chosen.less and add the following two lines before the first @import: + @import "bootstrap/less/variables.less"; + @import "bootstrap/less/mixins.less"; + + lessc bootstrap-chosen.less > bootstrap-chosen.css + + Finally, replace the section below with the contents of bootstrap-chosen.css. + + START OF BOOTSTRAP-CHOSEN SECTION... +----------------------------------------------------------------------------------------*/ + +.chosen-select { + width: 100%; +} +.chosen-select-deselect { + width: 100%; +} +.chosen-container { + display: inline-block; + font-size: 14px; + position: relative; + vertical-align: middle; +} +.chosen-container .chosen-drop { + background: #ffffff; + border: 1px solid #cccccc; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-box-shadow: 0 8px 8px rgba(0, 0, 0, .25); + box-shadow: 0 8px 8px rgba(0, 0, 0, .25); + margin-top: -1px; + position: absolute; + top: 100%; + left: -9000px; + z-index: 1060; +} +.chosen-container.chosen-with-drop .chosen-drop { + left: 0; + right: 0; +} +.chosen-container .chosen-results { + color: #555555; + margin: 0 4px 4px 0; + max-height: 240px; + padding: 0 0 0 4px; + position: relative; + overflow-x: hidden; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} +.chosen-container .chosen-results li { + display: none; + line-height: 1.42857143; + list-style: none; + margin: 0; + padding: 5px 6px; +} +.chosen-container .chosen-results li em { + background: #feffde; + font-style: normal; +} +.chosen-container .chosen-results li.group-result { + display: list-item; + cursor: default; + color: #999; + font-weight: bold; +} +.chosen-container .chosen-results li.group-option { + padding-left: 15px; +} +.chosen-container .chosen-results li.active-result { + cursor: pointer; + display: list-item; +} +.chosen-container .chosen-results li.highlighted { + background-color: #428bca; + background-image: none; + color: white; +} +.chosen-container .chosen-results li.highlighted em { + background: transparent; +} +.chosen-container .chosen-results li.disabled-result { + display: list-item; + color: #777777; +} +.chosen-container .chosen-results .no-results { + background: #eeeeee; + display: list-item; +} +.chosen-container .chosen-results .error { + text-align: center; + background: #f2dede; + display: list-item; +} +.chosen-container .chosen-results .loading { + text-align: center; + color: #ccc; + display: list-item; +} +.chosen-container .chosen-results-scroll { + background: white; + margin: 0 4px; + position: absolute; + text-align: center; + width: 321px; + z-index: 1; +} +.chosen-container .chosen-results-scroll span { + display: inline-block; + height: 1.42857143; + text-indent: -5000px; + width: 9px; +} +.chosen-container .chosen-results-scroll-down { + bottom: 0; +} +.chosen-container .chosen-results-scroll-down span { + background: url("chosen-sprite.png") no-repeat -4px -3px; +} +.chosen-container .chosen-results-scroll-up span { + background: url("chosen-sprite.png") no-repeat -22px -3px; +} +.chosen-container-single .chosen-single { + background-color: #ffffff; + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + border: 1px solid #cccccc; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + color: #555555; + display: block; + height: 34px; + overflow: hidden; + line-height: 34px; + padding: 0 0 0 8px; + position: relative; + text-decoration: none; + white-space: nowrap; +} +.chosen-container-single .chosen-single span { + display: block; + margin-right: 26px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.chosen-container-single .chosen-single abbr { + background: url("chosen-sprite.png") right top no-repeat; + display: block; + font-size: 1px; + height: 10px; + position: absolute; + right: 26px; + top: 12px; + width: 12px; +} +.chosen-container-single .chosen-single abbr:hover { + background-position: right -11px; +} +.chosen-container-single .chosen-single.chosen-disabled .chosen-single abbr:hover { + background-position: right 2px; +} +.chosen-container-single .chosen-single div { + display: block; + height: 100%; + position: absolute; + top: 0; + right: 0; + width: 18px; +} +.chosen-container-single .chosen-single div b { + background: url("chosen-sprite.png") no-repeat 0 7px; + display: block; + height: 100%; + width: 100%; +} +.chosen-container-single .chosen-default { + color: #777777; +} +.chosen-container-single .chosen-search { + margin: 0; + padding: 3px 4px; + position: relative; + white-space: nowrap; + z-index: 1000; +} +.chosen-container-single .chosen-search input[type="text"] { + background: url("chosen-sprite.png") no-repeat 100% -20px, #ffffff; + border: 1px solid #cccccc; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + margin: 1px 0; + padding: 4px 20px 4px 4px; + width: 100%; +} +.chosen-container-single .chosen-drop { + margin-top: -1px; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} +.chosen-container-single-nosearch .chosen-search input { + position: absolute; + left: -9000px; +} +.chosen-container-multi .chosen-choices { + background-color: #ffffff; + border: 1px solid #cccccc; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + cursor: text; + height: auto !important; + height: 1%; + margin: 0; + overflow: hidden; + padding: 0; + position: relative; +} +.chosen-container-multi .chosen-choices li { + float: left; + list-style: none; +} +.chosen-container-multi .chosen-choices .search-field { + margin: 0; + padding: 0; + white-space: nowrap; +} +.chosen-container-multi .chosen-choices .search-field input[type="text"] { + background: transparent !important; + border: 0 !important; + -webkit-box-shadow: none; + box-shadow: none; + color: #555555; + height: 32px; + margin: 0; + padding: 4px; + outline: 0; +} +.chosen-container-multi .chosen-choices .search-field .default { + color: #999; +} +.chosen-container-multi .chosen-choices .search-choice { + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + background-color: #eeeeee; + border: 1px solid #cccccc; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + background-image: -webkit-linear-gradient(top, #ffffff 0%, #eeeeee 100%); + background-image: -o-linear-gradient(top, #ffffff 0%, #eeeeee 100%); + background-image: linear-gradient(to bottom, #ffffff 0%, #eeeeee 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffeeeeee', GradientType=0); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + color: #333333; + cursor: default; + line-height: 13px; + margin: 6px 0 3px 5px; + padding: 3px 20px 3px 5px; + position: relative; +} +.chosen-container-multi .chosen-choices .search-choice .search-choice-close { + background: url("chosen-sprite.png") right top no-repeat; + display: block; + font-size: 1px; + height: 10px; + position: absolute; + right: 4px; + top: 5px; + width: 12px; + cursor: pointer; +} +.chosen-container-multi .chosen-choices .search-choice .search-choice-close:hover { + background-position: right -11px; +} +.chosen-container-multi .chosen-choices .search-choice-focus { + background: #d4d4d4; +} +.chosen-container-multi .chosen-choices .search-choice-focus .search-choice-close { + background-position: right -11px; +} +.chosen-container-multi .chosen-results { + margin: 0 0 0 0; + padding: 0; +} +.chosen-container-multi .chosen-drop .result-selected { + display: none; +} +.chosen-container-active .chosen-single { + border: 1px solid #66afe9; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); + box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); + -webkit-transition: border linear .2s, box-shadow linear .2s; + -o-transition: border linear .2s, box-shadow linear .2s; + transition: border linear .2s, box-shadow linear .2s; +} +.chosen-container-active.chosen-with-drop .chosen-single { + background-color: #ffffff; + border: 1px solid #66afe9; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); + box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); + -webkit-transition: border linear .2s, box-shadow linear .2s; + -o-transition: border linear .2s, box-shadow linear .2s; + transition: border linear .2s, box-shadow linear .2s; +} +.chosen-container-active.chosen-with-drop .chosen-single div { + background: transparent; + border-left: none; +} +.chosen-container-active.chosen-with-drop .chosen-single div b { + background-position: -18px 7px; +} +.chosen-container-active .chosen-choices { + border: 1px solid #66afe9; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); + box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); + -webkit-transition: border linear .2s, box-shadow linear .2s; + -o-transition: border linear .2s, box-shadow linear .2s; + transition: border linear .2s, box-shadow linear .2s; +} +.chosen-container-active .chosen-choices .search-field input[type="text"] { + color: #111 !important; +} +.chosen-container-active.chosen-with-drop .chosen-choices { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.chosen-disabled { + cursor: default; + opacity: 0.5 !important; +} +.chosen-disabled .chosen-single { + cursor: default; +} +.chosen-disabled .chosen-choices .search-choice .search-choice-close { + cursor: default; +} +.chosen-rtl { + text-align: right; +} +.chosen-rtl .chosen-single { + padding: 0 8px 0 0; + overflow: visible; +} +.chosen-rtl .chosen-single span { + margin-left: 26px; + margin-right: 0; + direction: rtl; +} +.chosen-rtl .chosen-single div { + left: 7px; + right: auto; +} +.chosen-rtl .chosen-single abbr { + left: 26px; + right: auto; +} +.chosen-rtl .chosen-choices .search-field input[type="text"] { + direction: rtl; +} +.chosen-rtl .chosen-choices li { + float: right; +} +.chosen-rtl .chosen-choices .search-choice { + margin: 6px 5px 3px 0; + padding: 3px 5px 3px 19px; +} +.chosen-rtl .chosen-choices .search-choice .search-choice-close { + background-position: right top; + left: 4px; + right: auto; +} +.chosen-rtl.chosen-container-single .chosen-results { + margin: 0 0 4px 4px; + padding: 0 4px 0 0; +} +.chosen-rtl .chosen-results .group-option { + padding-left: 0; + padding-right: 15px; +} +.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div { + border-right: none; +} +.chosen-rtl .chosen-search input[type="text"] { + background: url("chosen-sprite.png") no-repeat -28px -20px, #ffffff; + direction: rtl; + padding: 4px 5px 4px 20px; +} +@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) { + .chosen-rtl .chosen-search input[type="text"], + .chosen-container-single .chosen-single abbr, + .chosen-container-single .chosen-single div b, + .chosen-container-single .chosen-search input[type="text"], + .chosen-container-multi .chosen-choices .search-choice .search-choice-close, + .chosen-container .chosen-results-scroll-down span, + .chosen-container .chosen-results-scroll-up span { + background-image: url("chosen-sprite@2x.png") !important; + background-size: 52px 37px !important; + background-repeat: no-repeat !important; + } +} + +/*---------------------------------------------------------------------------------------- + ...END OF BOOTSTRAP-CHOSEN SECTION + + bootstrap-chosen additions +----------------------------------------------------------------------------------------*/ + +/* Mouse hover color in drop-downs */ +.chosen-container .chosen-results li.mouseover { + background-color: #eeeeee; + background-image: none; +} + +/*---------------------------------------------------------------------------------------- + Stylesheet for re-com.date Date Picker variants inline-picker & dropdown-picker + Day8 variation loosely based on: + Copyright 2013 Dan Grossman ( http://www.dangrossman.info ) + Licensed under the Apache License v2.0 + http://www.apache.org/licenses/LICENSE-2.0 + Built for http://www.improvely.com + http://eternicode.github.io/bootstrap-datepicker + + START OF DATE PICKER SECTION... +----------------------------------------------------------------------------------------*/ + +.datepicker.single .calendar { + float: none; +} + +.datepicker .calendar { + display: none; + max-width: 200px; +} + +.datepicker .calendar.single .calendar-date { + border: none; +} + +.datepicker .calendar th, .datepicker .calendar td { + white-space: nowrap; + text-align: center; + min-width: 32px; +} + +.datepicker .calendar-date { + border: 1px solid #ddd; + padding: 4px; + border-radius: 4px; + background: #fff; +} + +.datepicker .calendar-time { + text-align: center; + margin: 8px auto 0 auto; + line-height: 30px; +} + +.datepicker { + position: absolute; + background: #fff; + top: 100px; + left: 20px; + padding: 10px 10px 5px; + margin-top: 1px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + line-height: 16px; + border-radius: 4px; +} + +.datepicker table { + width: 100%; + margin: 0; + border-collapse: separate; +} + +.datepicker td, .datepicker th { + text-align: center; + width: 27px; + height: 26px; + max-width: 27px; + max-height: 26px; + min-width: 27px; + min-height: 26px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + padding: 4px; + cursor: default; + white-space: nowrap; + font-weight: normal; +} + +.datepicker td.off { + padding: 4px; + color: #999; +} + +.datepicker td.disabled { + color: #999; +} + +.datepicker th.disabled { + color: #999; +} + +.datepicker td.available:hover, .datepicker th.available:hover { + background: #eee; + cursor: pointer; +} + +.datepicker td.in-range { + background: #ebf4f8; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.datepicker td.start-date { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.datepicker td.end-date { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.datepicker td.start-date.end-date { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.datepicker td.active, .datepicker td.active:hover { + background-color: #357ebd; + border-color: #3071a9; + color: #fff; +} + +/* Introduced by Day8 from http://eternicode.github.io/bootstrap-datepicker */ +.datepicker td.today, .datepicker td.today:hover { + background-color: #ffcd70; + border-color: #f59e00; + border-radius: 18px; + color: #fff; +} + +.datepicker th.day-enabled, label.day-enabled { + font-weight: normal; + font-size: 10px; + color: #333; +} + +.datepicker th.selectable { + font-weight: normal; + color: #357ebd; +} + +.datepicker th.day-disabled { + font-weight: normal; + font-size: 10px; + color: #999; +} + +.datepicker td.week, .datepicker th.week { + font-size: 80%; + color: #ccc; +} + + +.datepicker th.prev { + border: 1px solid #ccc; + border-right-style: none; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + background-color: #F7F7F7; +} + +.datepicker th.next { + border: 1px solid #ccc; + border-left-style: none; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + background-color: #F7F7F7; +} + +.datepicker th.month { + width: auto; + font-size: 14px; + color: #777; + border-radius: 0; + border: 1px solid #CCC; + border-right-style: none; + border-left-style: none; + background-color: #F7F7F7; +} + +.dropdown-button { + cursor: pointer; + height: 32px; + font-size: 13px; + font-weight: normal; +} + +.dropdown-button.activator { + width: 40px; + color: #777; + background-color: #F7F7F7 +} + +/*---------------------------------------------------------------------------------------- + END OF DATE PICKER SECTION... + + + CSS THROBBER +----------------------------------------------------------------------------------------*/ +@keyframes blink { + 0% { + transform: scale(1); + opacity: 1; + } + 100% { + transform: scale(.5); + opacity: 0; + } +} +@-moz-keyframes blink { + 0% { + -moz-transform: scale(1); + opacity: 1; + } + 100% { + -moz-transform: scale(.5); + opacity: 0; + } +} +@-webkit-keyframes blink { + 0% { + -webkit-transform: scale(1); + opacity: 1; + } + 100% { + -webkit-transform: scale(.5); + opacity: 0; + } +} + +.loader { + position: relative; + display: inline-block; + vertical-align: middle; + width: 20px; + height: 20px; + margin: 20px; + padding: 0px; +} + +.loader li { + position: absolute; + display: block; + border-radius: 4px; + background-color: #999; + width: 6px; + height: 6px; + margin-top: -3px; + margin-left: -3px; + opacity: 0; + -webkit-animation: blink .8s ease infinite; + -moz-animation: blink .8s ease infinite; + animation: blink .8s ease infinite; +} + +.loader li:nth-child(1) { + top: 0%; + left: 50%; + -webkit-animation-delay: -.7s; + -moz-animation-delay: -.7s; + animation-delay: -.7s; +} + +.loader li:nth-child(2) { + top: 15%; + left: 85%; + -webkit-animation-delay: -.6s; + -moz-animation-delay: -.6s; + animation-delay: -.6s; +} + +.loader li:nth-child(3) { + top: 50%; + left: 100%; + -webkit-animation-delay: -.5s; + -moz-animation-delay: -.5s; + animation-delay: -.5s; +} + +.loader li:nth-child(4) { + top: 85%; + left: 85%; + -webkit-animation-delay: -.4s; + -moz-animation-delay: -.4s; + animation-delay: -.4s; +} + +.loader li:nth-child(5) { + top: 100%; + left: 50%; + -webkit-animation-delay: -.3s; + -moz-animation-delay: -.3s; + animation-delay: -.3s; +} + +.loader li:nth-child(6) { + top: 85%; + left: 15%; + -webkit-animation-delay: -.2s; + -moz-animation-delay: -.2s; + animation-delay: -.2s; +} + +.loader li:nth-child(7) { + top: 50%; + left: 0%; + -webkit-animation-delay: -.1s; + -moz-animation-delay: -.1s; + animation-delay: -.1s; + +} + +.loader li:nth-child(8) { + top: 15%; + left: 15%; +} + +.loader.smaller { + width: 9px; + height: 10px; + margin: 7px; +} + +.loader.smaller li { + width: 4px; + height: 4px; + margin-top: -2px; + margin-left: -2px; +} + +.loader.small { + width: 12px; + height: 12px; + margin: 10px; +} + +.loader.small li { + width: 4px; + height: 4px; + margin-top: -2px; + margin-left: -2px; +} + +.loader.large { + width: 48px; + height: 48px; + margin: 20px; +} + +.loader.large li { + border-radius: 14px; + width: 14px; + height: 14px; + margin-top: -7px; + margin-left: -7px; +} + +/*---------------------------------------------------------------------------------------- + END OF CSS THROBBER SECTION... + + + Bootstrap list-group-item variation (only used in selection-list component) +----------------------------------------------------------------------------------------*/ +/* TODO: re-demo CSS */ +.compact { + margin: 0; + height: 19px; + white-space: nowrap; + border: none; + padding: 0; + overflow: hidden; + cursor: default; + font-size: small; + text-overflow: ellipsis; +} +/* TODO: re-demo CSS */ +.list-group-item:hover { + background-color: #eeeeee; +} + +/*---------------------------------------------------------------------------------------- + Custom scrollbars + - http://css-tricks.com/custom-scrollbars-in-webkit + - http://codemug.com/html/custom-scrollbars-using-css +----------------------------------------------------------------------------------------*/ + +::-webkit-scrollbar { + width: 10px; + border-radius: 5px; + background-color: rgba(0,0,0,0.05); + } +::-webkit-scrollbar:horizontal { + height:10px; + } +::-webkit-scrollbar:hover { + background-color:rgba(0,0,0,0.20); + } +::-webkit-scrollbar-thumb:horizontal, ::-webkit-scrollbar-thumb:vertical { + background:rgba(0,0,0,0.25); + border-radius: 5px; + } +::-webkit-scrollbar-thumb:horizontal:active, ::-webkit-scrollbar-thumb:vertical:active { + background:rgba(0,0,0,0.45); + } + +/* Color of selected text*/ +/* +::-moz-selection { color: gold; background: red; } +::selection { color: gold; background: red; } +*/ + + +/*---------------------------------------------------------------------------------------- + Override bootstrap input text placeholder color (it's too dark) +----------------------------------------------------------------------------------------*/ + +.form-control::-webkit-input-placeholder { + color: #bbb; +} + +/*---------------------------------------------------------------------------------------- + Override bootstrap warning color (from horrible brown/orange to orange) +----------------------------------------------------------------------------------------*/ + +.has-warning .form-control { + border-color: #f57c00; +} + +.has-warning .form-control:focus { + border-color: #f57c00; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px rgba(245, 124, 0, 0.6); +} + +/* TODO: REMOVE */ +.has-warning .form-control-feedback { + color: #f57c00; +} + +.rc-input-text .zmdi-alert-triangle { + color: #f57c00; +} + +.has-error .form-control { + border-color: #d50000; +} + +.has-error .form-control:focus { + border-color: #d50000; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px rgba(213, 0, 0, 0.6); +} + +/* TODO: REMOVE */ +.has-error .form-control-feedback { + color: #d50000; +} + +/* Override bootstrap green validation */ + +.has-success .form-control { + border-color: #13C200; +} + +.has-success .form-control:focus { + border-color: #13C200; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px rgba(19, 194, 0, 0.6); +} + +.rc-input-text .zmdi-alert-circle { + color: #d50000; +} + +.rc-input-text .zmdi-check-circle { + color: #13C200; +} + +.alert-warning { + color: #ea6b00; + background-color: #ffecdc; + border-color: #faebcc; +} + +code { + margin-right: 0.2em; + border-radius: 4px; + color: #333; + background: #eee; + border: 1px solid #ddd; + padding: 0.1em 0.3em; +} + + +/*---------------------------------------------------------------------------------------- + Time component +----------------------------------------------------------------------------------------*/ + +.time-entry { + flex: none; + padding-left: 0.3em; + padding-top: 2px; + border: 1px solid #ccc; + border-radius: 4px 0 0 4px; + width: 3.3em; +} +.time-entry:last-child { + border-radius: 4px; +} +.time-icon { + display: flex; + padding: 0 0.3em; + border: 1px solid #ccc; + border-left: none; + border-radius: 0 4px 4px 0; + background-color: #ddd; +} + +/*---------------------------------------------------------------------------------------- + md-circle-icon-button +----------------------------------------------------------------------------------------*/ + +.rc-md-circle-icon-button { + width: 40px; /* material design dimensions */ + height: 40px; + text-align: center; /* to horizontally center the icon */ + border: 1px solid lightgrey; + border-radius: 50%; + background-color: inherit; + opacity: 0.9; +} + +.rc-md-circle-icon-button > i { + font-size : 24px; + line-height: 40px; /* to vertically center in surrounding :div */ + color: inherit; +} + +.rc-md-circle-icon-button:hover { + border: 1px solid #428bca; + color: #428bca; +} + +.rc-circle-smaller { + width: 26px; + height: 26px; +} + +.rc-circle-smaller > i { + font-size: 16px; + line-height: 24px; +} + +.rc-circle-larger { + width: 56px; + height: 56px; +} + +.rc-circle-larger > i { + font-size: 24px; + line-height: 56px; +} + +.rc-circle-emphasis { + border: 1px solid #428bca; + background-color: #428bca; + color: white; +} + +.rc-circle-emphasis:hover { + border: 1px solid #2196f3; + background-color: #2196f3; + color: white; +} + +.rc-circle-disabled { + border: none; +} + +.rc-circle-disabled:hover { + border: none; +} + +.rc-circle-disabled > i { + color: lightgrey; +} + +/*---------------------------------------------------------------------------------------- + md-icon-button +----------------------------------------------------------------------------------------*/ + +.rc-md-icon-button { + height: 24px; + font-size: 24px; + line-height: 24px; + text-align: center; /* to horizontally center the icon */ + background-color: inherit; + border-radius: 3px; +} + +.rc-md-icon-button > i { + color: inherit; +} + +.rc-md-icon-button:hover { + color: #2196f3; +} + +.rc-icon-smaller { + height: 16px; + font-size: 16px; + line-height: 16px; +} + +.rc-icon-larger { + height: 32px; + font-size: 32px; + line-height: 32px; +} + +.rc-icon-emphasis { + background-color: #428bca; + color: white; +} + +.rc-icon-emphasis:hover { + background-color: #2196f3; + color: white; +} + +.rc-icon-disabled > i { + color: lightgrey; +} + +/*---------------------------------------------------------------------------------------- + info-button + + CSS styles below are taken from https://github.com/oakmac/cljs.info... + + The MIT License (MIT) + Copyright (c) 2014 Chris Oakman and contributors + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +----------------------------------------------------------------------------------------*/ + +.rc-info-button { + padding-left: 3px; + padding-right: 3px; + fill: #bdbdbd; +} + +.rc-info-button:hover { + fill: #2962ff; +} + +.popover-content .info-heading { + font-size: 22px; +} + +.popover-content .info-subheading { + font-size: 18px; + margin-top: 8px; +} + +.popover-content .info-bold { + background-color: #555; + padding-left: 3px; + padding-right: 3px; + border-radius: 3px; + font-weight: bold; +} + +.popover-content a { + color: #ccf; +} + +.popover-content code { + color: white; + background-color: #555; + border: none; +} + +/*---------------------------------------------------------------------------------------- + row-button +----------------------------------------------------------------------------------------*/ + +.rc-row-button { + text-align: center; /* to horizontally center the icon */ + color: #2167f2; /2196f3; + background-color: inherit; + opacity: 0; + cursor: pointer; +} + +.rc-row-button > i { + color: inherit; +} + +.rc-row-mouse-over-row { + opacity: 0.4; +} + +.rc-row-button:hover, rc-row-mouse-over-row:hover { + opacity: 1; +} + +.rc-row-disabled { + opacity: 1; + color: lightgrey; + cursor: default; +} + +/*---------------------------------------------------------------------------------------- + div-table +----------------------------------------------------------------------------------------*/ + +.rc-div-table { + flex: none; + width: auto; + border: 2px solid lightgrey; + cursor: default; +} + +.rc-div-table-header > div { + background-color: #e8e8e8; + font-weight: bold; + padding: 5px; +} + +.rc-div-table-row { + border-top: 1px solid lightgrey; +} + +.rc-div-table-row:hover { + background-color: #f2f2f2; +} + +.rc-div-table-row > div { + padding: 5px; +} + +/*---------------------------------------------------------------------------------------- + General Typography +----------------------------------------------------------------------------------------*/ + +.semibold { + font-weight: 500; +} +.bold { + font-weight: 700; +} +.italic { + font-style: italic; +} +.uppercase { + text-transform: uppercase; +} +.all-small-caps { + font-variant: small-caps; +} + +.level1 { + font-family: Roboto, sans-serif; + font-weight: 200; + font-size: 40px; + color: rgba(68, 68, 68, 1.0); + letter-spacing: normal; + /*-ms-font-feature-settings: 'ss20' 1;*/ +} +.level2 { + font-family: Roboto, sans-serif; + font-weight: 200; + font-size: 26px; + color: rgba(0, 0, 0, 1.0); + letter-spacing: 0.001em; +} +.level3 { + font-family: Roboto, sans-serif; + font-weight: 500; + font-size: 15px; + color: rgba(68, 68, 68, 1.0); + letter-spacing: 0.002em; +} +.level4 { + font-family: Roboto, sans-serif; + font-weight: 500; + font-size: 15px; + color: rgba(68, 68, 68, 0.6); + letter-spacing: 0.002em; +} +.field-label { + font-family: Roboto, sans-serif; + font-weight: 400; + font-size: 14px; + color: rgba(68, 68, 68, 0.6); + letter-spacing: 0.002em; + font-variant: small-caps; + flex: none; +} + +.noselect { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +/*---------------------------------------------------------------------------------------- + Flexbox "display" styles + Requires 2 display values which we can't represent in a Clojure map +----------------------------------------------------------------------------------------*/ + +.display-flex { + display: -webkit-flex; + display: flex; +} +.display-inline-flex { + display: -webkit-inline-flex; + display: inline-flex; +} + +.zmdi-hc-fw-rc { + width: 1em; + text-align: center; +} + +.rc-typeahead-suggestions-container { + position: absolute; + width: 100%; + background-color: #eee; +} + +.rc-typeahead-suggestion { + background-color: #eee; + padding: 0.4em; +} + +.rc-typeahead-suggestion.active { + background-color: #ddd; +} diff --git a/resources/public/index.html b/resources/public/index.html index 6ed17b9..91a5ada 100644 --- a/resources/public/index.html +++ b/resources/public/index.html @@ -4,7 +4,7 @@ - + diff --git a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs index 49a521c..ee8769a 100644 --- a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs +++ b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs @@ -9,6 +9,8 @@ ;; Component: swinging-needle ;; ------------------------------------------------------------------------------------ +;;; It seems the defaults given here are just documentation; the defaults +;;; that are actually used are those given in the :or clause of the argument map. (def swinging-needle-args-desc [{:name :model :required true :type "double | atom" :validate-fn number-or-string? :description "current value of the variable being watched. A number between 0 and 100"} @@ -18,29 +20,61 @@ :validate-fn string? :description "a CSS width"} {:name :height :required false :type "string" :default "100%" :validate-fn string? :description "a CSS height"} + {:name :min-value :required false :type "double" :default 0 + :validate-fn number? :description "the minimum value model can take"} + {:name :max-value :required false :type "double" :default 100 + :validate-fn number? :description "the maximum value model can take"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated"} -;; {:name :cursor-class :required false :type "string" :default "snm-cursor" -;; :validate-fn string? :description "CSS class names, space separated, for the cursor"} -;; {:name :frame-class :required false :type "string" :default "snm-frame" -;; :validate-fn string? :description "CSS class names, space separated, for the frame"} -;; {:name :hub-class :required false :type "string" :default "snm-hub" -;; :validate-fn string? :description "CSS class names, space separated, for the hub"} -;; {:name :needle-class :required false :type "string" :default "snm-needle" -;; :validate-fn string? :description "CSS class names, space separated, for the needle"} -;; {:name :scale-class :required false :type "string" :default "snm-scale" -;; :validate-fn string? :description "CSS class names, space separated, for the scale"} -;; {:name :redzone-class :required false :type "string" :default "snm-redzone" -;; :validate-fn string? :description "CSS class names, space separated, for the redzone"} + {:name :cursor-class :required false :type "string" :default "snm-cursor" + :validate-fn string? :description "CSS class names, space separated, for the cursor"} + {:name :frame-class :required false :type "string" :default "snm-frame" + :validate-fn string? :description "CSS class names, space separated, for the frame"} + {:name :hub-class :required false :type "string" :default "snm-hub" + :validate-fn string? :description "CSS class names, space separated, for the hub"} + {:name :needle-class :required false :type "string" :default "snm-needle" + :validate-fn string? :description "CSS class names, space separated, for the needle"} + {:name :scale-class :required false :type "string" :default "snm-scale" + :validate-fn string? :description "CSS class names, space separated, for the scale"} + {:name :redzone-class :required false :type "string" :default "snm-redzone" + :validate-fn string? :description "CSS class names, space separated, for the redzone"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {:name :attr :required false :type "HTML attr map" :validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed"]}]) +(defn abs + "Return the absolute value of the (numeric) argument." + [n] (max n (- n))) + +;; the constant 140 represents the full sweep of the needle +;; from the left end of the scale to right end, in degrees. +(def full-scale-deflection 140) + +(defn deflection + "Return the deflection of a needle given this `value` on the + range `min-value`...`max-value`." + [value min-value max-value] + (let [range (- max-value min-value) + deflection (/ value range) + zero-offset (/ (- 0 min-value) range) + limited (min (max (+ zero-offset deflection) 0) 1)] + (* (- limited 0.5) full-scale-deflection))) + + (defn swinging-needle-meter "Render an SVG swinging needle meter" - [& {:keys [model setpoint width height class cursor-class frame-class hub-class needle-class scale-class redzone-class style attr] - :or {width "100%" height "100%"} + [& {:keys [model setpoint width height min-value max-value class cursor-class frame-class hub-class needle-class scale-class redzone-class style attr] + :or {width "100%" + height "100%" + min-value 0 + max-value 100 + cursor-class "snm-cursor" + frame-class "snm-frame" + hub-class "snm-hub" + needle-class "snm-needle" + scale-class "snm-scale" + redzone-class "snm-redzone"} :as args}] {:pre [(validate-args-macro swinging-needle-args-desc args "swinging-needle")]} (let [model (deref-or-value model) @@ -66,16 +100,20 @@ :x "0px" :version "1.1" :class (str "snm-meter " class)} - [:path {:class "snm-scale" + [:path {:class scale-class :d "m 11.85914,76.864488 c 0,0 14.34545,-53.795412 68.140856,-53.795412 53.795424,0 68.140864,53.795412 68.140864,53.795412"}] - [:path {:class "snm-redzone" :d "m 137.74738,54.878869 c 0,0 3.02675,3.620416 6.3911,11.14347 3.36435,7.523055 4.20612,11.198095 4.20612,11.198095"}] - [:rect {:class "snm-frame" :x "5" :y "5" :height "100" :width "150"}] - [:path {:class "snm-cursor" + [:path {:class redzone-class :d "m 137.74738,54.878869 c 0,0 3.02675,3.620416 6.3911,11.14347 3.36435,7.523055 4.20612,11.198095 4.20612,11.198095"}] + [:rect {:class frame-class :x "5" :y "5" :height "100" :width "150"}] + [:path {:class cursor-class :d "M 80,20 80,100" - :visibility (if (and (number? setpoint) (> setpoint 0)) "visible" "hidden") - :transform (str "rotate( " (* (- setpoint 50) 1.4) ", 80, 100)")}] - [:path {:class "snm-needle" + :visibility (if (and (number? setpoint) (> setpoint min-value)) "visible" "hidden") + :transform (str "rotate( " (deflection setpoint min-value max-value) ", 80, 100)")}] + [:path {:class needle-class :d "M 80,20 80,100" - :transform (str "rotate( " (* (- model 50) 1.4) ", 80, 100)") }] - [:circle {:class "snm-hub" :r "10" :cx "80" :cy "100"}]] - (str model "%")]]])) + :transform (str "rotate( " (deflection model min-value max-value) ", 80, 100)") }] + [:circle {:class hub-class :r "10" :cx "80" :cy "100"}]] +;;; Useful for debugging: +;; (str "value: " model "; min: " min-value +;; "; max: " max-value +;; "; deflection: " (int (deflection model min-value max-value))) + ]]])) diff --git a/src/cljs/swinging_needle_meter/views.cljs b/src/cljs/swinging_needle_meter/views.cljs index b2bee71..705ac2d 100644 --- a/src/cljs/swinging_needle_meter/views.cljs +++ b/src/cljs/swinging_needle_meter/views.cljs @@ -27,11 +27,13 @@ :width "450px" :children [[title2 "Notes"] [status-text "Wildly experimental"] - [p "An SVG swinging needle meter. Note that the cursor will vanish if the setpoint is null or zero; this is intentional."] + [p "An SVG swinging needle meter."] + [p "Note that the cursor will vanish if the setpoint is null or is less than or equal to min-value; this is intentional."] + [p "Note that if the value of model is lower then min-value or greater than max-value, + it will be limited as it would be on a mechanical meter."] + [p "You can hide the redzone by setting its style to the style 'snm-scale'"] [p - "TODO: You can't adjust the position of the start of the red-zone; " - "you can't override the classes for the different elements of the meter; " - "you can't override the maximum and minimum values."] + "TODO: You can't adjust the position of the start of the red-zone; "] [args-table swinging-needle-args-desc]]] [v-box :gap "10px"