diff --git a/modules/backend/assets/js/october.js b/modules/backend/assets/js/october.js index 458cf670e..339b93503 100644 --- a/modules/backend/assets/js/october.js +++ b/modules/backend/assets/js/october.js @@ -9,10 +9,7 @@ =require october.controls.js =require october.utils.js -=require october.foundation.baseclass.js -=require october.foundation.element.js -=require october.foundation.event.js -=require october.foundation.controlutils.js +=require ../../../system/assets/ui/js/foundation.js =require october.scrollpad.js =require october.triggerapi.js =require october.dragscroll.js diff --git a/modules/system/assets/css/styles.css b/modules/system/assets/css/styles.css index 48e07ca6e..3ca5d1efb 100644 --- a/modules/system/assets/css/styles.css +++ b/modules/system/assets/css/styles.css @@ -178,6 +178,82 @@ td, th { padding: 0; } +*, +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 62.5%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.428571429; + color: #333333; + background-color: #ffffff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +button, +input, +select[multiple], +textarea { + background-image: none; +} +a { + color: #428bca; + text-decoration: none; +} +a:hover, +a:focus { + color: #2a6496; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +img { + vertical-align: middle; +} +.img-responsive { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + border: 0; +} @media print { * { text-shadow: none !important; @@ -249,431 +325,6 @@ th { border: 1px solid #ddd !important; } } -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 62.5%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.428571429; - color: #333333; - background-color: #ffffff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #428bca; - text-decoration: none; -} -a:hover, -a:focus { - color: #2a6496; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - padding: 4px; - line-height: 1.428571429; - background-color: #ffffff; - border: 1px solid #dddddd; - border-radius: 4px; - -webkit-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; - display: inline-block; - max-width: 100%; - height: auto; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eeeeee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #999999; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 200; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -cite { - font-style: normal; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-muted { - color: #999999; -} -.text-primary { - color: #428bca; -} -a.text-primary:hover { - color: #3071a9; -} -.text-success { - color: #3c763d; -} -a.text-success:hover { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #428bca; -} -a.bg-primary:hover { - background-color: #3071a9; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eeeeee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - list-style: none; - margin-left: -5px; -} -.list-inline > li { - display: inline-block; - padding-left: 5px; - padding-right: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.428571429; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - clear: left; - text-align: right; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #999999; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eeeeee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.428571429; - color: #999999; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - border-right: 5px solid #eeeeee; - border-left: 0; - text-align: right; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -blockquote:before, -blockquote:after { - content: ""; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.428571429; -} .container { margin-right: auto; margin-left: auto; @@ -1341,6 +992,342 @@ address { margin-left: 0%; } } +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #999999; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 200; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +cite { + font-style: normal; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-muted { + color: #999999; +} +.text-primary { + color: #428bca; +} +a.text-primary:hover { + color: #3071a9; +} +.text-success { + color: #3c763d; +} +a.text-success:hover { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #428bca; +} +a.bg-primary:hover { + background-color: #3071a9; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eeeeee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + list-style: none; + margin-left: -5px; +} +.list-inline > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.428571429; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eeeeee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.428571429; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; + text-align: right; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +blockquote:before, +blockquote:after { + content: ""; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.428571429; +} @font-face { font-family: 'Open Sans'; src: url('../font/OpenSans-Light-webfont.eot'); diff --git a/modules/system/assets/less/styles.less b/modules/system/assets/less/styles.less index 5aab77dc0..97341be19 100644 --- a/modules/system/assets/less/styles.less +++ b/modules/system/assets/less/styles.less @@ -1,15 +1,5 @@ -// Core variables and mixins -@import "../vendor/bootstrap/less/variables.less"; -@import "../vendor/bootstrap/less/mixins.less"; - -// Reset -@import "../vendor/bootstrap/less/normalize.less"; -@import "../vendor/bootstrap/less/print.less"; - -// Core CSS -@import "../vendor/bootstrap/less/scaffolding.less"; -@import "../vendor/bootstrap/less/type.less"; -@import "../vendor/bootstrap/less/grid.less"; +// Basic grid system +@import "../ui/less/site.less"; @fontOpenSans: 'Open Sans', Arial, sans-serif; @colorBase: #405261; diff --git a/modules/system/assets/ui/docs/breadcrumb.md b/modules/system/assets/ui/docs/breadcrumb.md new file mode 100644 index 000000000..2a9800843 --- /dev/null +++ b/modules/system/assets/ui/docs/breadcrumb.md @@ -0,0 +1,10 @@ + +# Example + +
+ +
\ No newline at end of file diff --git a/modules/system/assets/ui/docs/button.md b/modules/system/assets/ui/docs/button.md new file mode 100644 index 000000000..b361ff51d --- /dev/null +++ b/modules/system/assets/ui/docs/button.md @@ -0,0 +1,7 @@ +# Button + +Some description about the components + +# Example + + \ No newline at end of file diff --git a/modules/system/assets/ui/docs/flag.md b/modules/system/assets/ui/docs/flag.md new file mode 100644 index 000000000..f95390e59 --- /dev/null +++ b/modules/system/assets/ui/docs/flag.md @@ -0,0 +1,2 @@ +Provides flags of various descriptions + diff --git a/modules/system/assets/ui/docs/form.md b/modules/system/assets/ui/docs/form.md new file mode 100644 index 000000000..780fd6918 --- /dev/null +++ b/modules/system/assets/ui/docs/form.md @@ -0,0 +1,172 @@ +# Example + + +
+ + +
+ + +

Example below help text here.

+
+ + +
+ + +

Example below help text here.

+
+ + +
+ +

Example above help text here.

+ +
+ + + + + + + + +
+
+ + +

Use this checkbox to enable the Googie Berry power-up specifically for this page. You can configure the Googie Berry power-up on the System Settings and Dashboard page.

+
+
+ + +
+
+ +

Use this checkbox to enable the Googie Berry power-up specifically for this page. You can configure the Googie Berry power-up on the System Settings and Dashboard page.

+
+ +
+ + +
+ +

Where should you propose to your beautiful girl?

+ +
+ + +

Do not send new comment notifications.

+
+
+ + +

Send new comment notifications only to post author.

+
+
+ + +

Notify all users who have permissions to receive blog notifications.

+
+
+ + +
+ +

What cars would you like in your garage?

+ +
+ + +

Do not send new comment notifications.

+
+
+ + +

Send new comment notifications only to post author.

+
+
+ + +

Notify all users who have permissions to receive blog notifications.

+
+
+ + + + + +
+ +

Plain as Jane multi line text input

+ +
+ + + + +
diff --git a/modules/system/assets/ui/docs/list.md b/modules/system/assets/ui/docs/list.md new file mode 100644 index 000000000..3ecec5eb1 --- /dev/null +++ b/modules/system/assets/ui/docs/list.md @@ -0,0 +1,154 @@ + +# Example + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
TitleCreatedAuthorCategoriesPublishedUpdated
Welcome to OctoberOct 01, 2013Sam GeorgesNewsOct 01, 2013Oct 01, 2013 
The marketplace is open!Oct 15, 2013Sam GeorgesFeaturesOct 16, 2013Oct 16, 2013 
Welcome to the Builder!Oct 21, 2013Alexey BobkovNews, FeaturesOct 21, 2013Oct 21, 2013 
Components explainedNov 12, 2013Alexey BobkovTutorialsNov 12, 2013Nov 12, 2013 
Creating a module in 90 secondsNov 15, 2013Sam GeorgesTutorialsNov 15, 2013Nov 15, 2013 
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
TitleCreatedAuthorCategoriesPublishedUpdated
Welcome to OctoberOct 01, 2013Adam PersonNewsOct 01, 2013Oct 01, 2013 
+
diff --git a/modules/system/assets/ui/docs/site.md b/modules/system/assets/ui/docs/site.md new file mode 100644 index 000000000..0c3c429ab --- /dev/null +++ b/modules/system/assets/ui/docs/site.md @@ -0,0 +1,7 @@ +Includes scaffold for a basic site. + +Reset +Normalize +Grid system +Print +Typography \ No newline at end of file diff --git a/modules/system/assets/ui/font/FontAwesome.otf b/modules/system/assets/ui/font/FontAwesome.otf new file mode 100644 index 000000000..f7936cc1e Binary files /dev/null and b/modules/system/assets/ui/font/FontAwesome.otf differ diff --git a/modules/system/assets/ui/font/fontawesome-webfont.eot b/modules/system/assets/ui/font/fontawesome-webfont.eot new file mode 100644 index 000000000..33b2bb800 Binary files /dev/null and b/modules/system/assets/ui/font/fontawesome-webfont.eot differ diff --git a/modules/system/assets/ui/font/fontawesome-webfont.svg b/modules/system/assets/ui/font/fontawesome-webfont.svg new file mode 100644 index 000000000..1ee89d436 --- /dev/null +++ b/modules/system/assets/ui/font/fontawesome-webfont.svg @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/system/assets/ui/font/fontawesome-webfont.ttf b/modules/system/assets/ui/font/fontawesome-webfont.ttf new file mode 100644 index 000000000..ed9372f8e Binary files /dev/null and b/modules/system/assets/ui/font/fontawesome-webfont.ttf differ diff --git a/modules/system/assets/ui/font/fontawesome-webfont.woff b/modules/system/assets/ui/font/fontawesome-webfont.woff new file mode 100644 index 000000000..8b280b98f Binary files /dev/null and b/modules/system/assets/ui/font/fontawesome-webfont.woff differ diff --git a/modules/system/assets/ui/font/fontawesome-webfont.woff2 b/modules/system/assets/ui/font/fontawesome-webfont.woff2 new file mode 100644 index 000000000..3311d5851 Binary files /dev/null and b/modules/system/assets/ui/font/fontawesome-webfont.woff2 differ diff --git a/modules/backend/assets/images/flag-icons-large.png b/modules/system/assets/ui/images/flag-icons-large.png similarity index 100% rename from modules/backend/assets/images/flag-icons-large.png rename to modules/system/assets/ui/images/flag-icons-large.png diff --git a/modules/backend/assets/images/flag-icons-small.png b/modules/system/assets/ui/images/flag-icons-small.png similarity index 100% rename from modules/backend/assets/images/flag-icons-small.png rename to modules/system/assets/ui/images/flag-icons-small.png diff --git a/modules/backend/assets/js/october.foundation.baseclass.js b/modules/system/assets/ui/js/foundation.baseclass.js similarity index 100% rename from modules/backend/assets/js/october.foundation.baseclass.js rename to modules/system/assets/ui/js/foundation.baseclass.js diff --git a/modules/backend/assets/js/october.foundation.controlutils.js b/modules/system/assets/ui/js/foundation.controlutils.js similarity index 100% rename from modules/backend/assets/js/october.foundation.controlutils.js rename to modules/system/assets/ui/js/foundation.controlutils.js diff --git a/modules/backend/assets/js/october.foundation.element.js b/modules/system/assets/ui/js/foundation.element.js similarity index 100% rename from modules/backend/assets/js/october.foundation.element.js rename to modules/system/assets/ui/js/foundation.element.js diff --git a/modules/backend/assets/js/october.foundation.event.js b/modules/system/assets/ui/js/foundation.event.js similarity index 100% rename from modules/backend/assets/js/october.foundation.event.js rename to modules/system/assets/ui/js/foundation.event.js diff --git a/modules/system/assets/ui/js/foundation.js b/modules/system/assets/ui/js/foundation.js new file mode 100644 index 000000000..2886e9e0b --- /dev/null +++ b/modules/system/assets/ui/js/foundation.js @@ -0,0 +1,6 @@ +/* +=require foundation.baseclass.js +=require foundation.element.js +=require foundation.event.js +=require foundation.controlutils.js +*/ \ No newline at end of file diff --git a/modules/system/assets/ui/js/inspector.js b/modules/system/assets/ui/js/inspector.js new file mode 100644 index 000000000..88f0f608b --- /dev/null +++ b/modules/system/assets/ui/js/inspector.js @@ -0,0 +1,1271 @@ +/* + * Inspector control + * + * Manages properties of inspectable controls. + * + * Data attributes: + * - data-inspectable - makes a control inspectable. + * - data-inspector-title - title for the inspector popup + * - data-inspector-description - optional description for the inspector popup + * - data-inspector-class - PHP class name of the inspectable object. Required for drop-down + * fields with dynamic options. + * - data-inspector-css-class - CSS class name to apply to the Inspector container element. + * - data-inspector-container - specifies a CSS selector for the inspector container. The default container + * is the document body. The container element should be relative positioned. The 'self' container value + * allows to inject the inspector to the inspectable element. + * - data-inspector-offset - offset in pixels to add to the calculated position, to make the position more "random" + * - data-inspector-placement - top | bottom | left | right. The placement could automatically be changed + if the popover doesn't fit into the desired position. + * - data-inspector-fallback-placement - The placement to use if the default placement + * and all other possible placements do not work. The default value is "bottom". + * - data-inspector-config - Configuration of the inspector fields as an array in the JSON format. + * Each element in the array is an object with the following properties: + * - property + * - title + * - group (optional) + * - type (currently supported types are: string, checkbox, dropdown) + * - description (optional) + * - validationPattern (regex pattern for for validating the value, supported by the text editor) + * - validationMessage (a message to display if the validation fails) + * - placeholder - placholder text, for text and dropdown properties + * - default - default value + * - depends - a list of properties the property depend on, for dropdown lists + * - options - an option list for dropdown lists, optional. If not provided the options are loaded with AJAX. + * - showExternalParam - specifies the visibility of the external parameter feature for the property. Default: true. + * Example of the configuration string (a single property): + * [{"property":"max-width","title":"Max width","type":"string"}] + * + * The Inspector requires the inspectable element to contain a hidden input element with the attribute "data-inspector-values". + * The inspector uses this field to read and write field values. The field value is a JSON string, an object with keys matching property + * names and values matching property values. + * + * Any HTML element that wraps the inspectable element can have the data-inspector-external-parameters property that enables the external + * parameters support. External parameters saved with the special syntax {{ paramName }}. The external parameter feature can be toggled + * on per-property basis with the showExternalParam option, visible by default. + * + * Events + * - change - the event is triggered on the inspectable element when it's properties are updated. + * - showing.oc.inspector - triggered before the Inspector is displayed. Allows to cancel the action with e.preventDefault() + * - hiding.oc.inspector - triggered before the Inspector is hidden. Allows to cancel the action with e.preventDefault() + * - hidden.oc.inspector - triggered after the Inspector is hidden. + * + * Dependences: + * - october.popover.js + */ ++function ($) { "use strict"; + var Base = $.oc.foundation.base, + BaseProto = Base.prototype + + if ($.oc === undefined) + $.oc = {} + + $.oc.inspector = { + editors: {}, + propertyCounter: 0 + } + + // INSPECTOR CLASS DEFINITION + // ============================ + + var Inspector = function(element, options) { + this.options = options + this.$el = $(element) + + this.title = false + this.description = false + + Base.call(this) + } + + Inspector.prototype = Object.create(BaseProto) + Inspector.prototype.constructor = Inspector + + Inspector.prototype.loadConfiguration = function(onSuccess) { + var configString = this.$el.data('inspector-config') + if (configString !== undefined) { + // If the data-inspector-config attribute is provided, + // load the configuration from it + this.parseConfiguration(configString) + + if (onSuccess !== undefined) + onSuccess(); + } else { + // If the data-inspector-config attribute is not provided, + // request the configuration from the server. + var $form = $(this.selector).closest('form'), + data = this.$el.data(), + self = this + + $.oc.stripeLoadIndicator.show() + + var request = $form.request('onGetInspectorConfiguration', { + data: data + }).done(function(data) { + self.parseConfiguration(data.configuration.properties) + + if (data.configuration.title !== undefined) + self.title = data.configuration.title + + if (data.configuration.description !== undefined) + self.description = data.configuration.description + + $.oc.stripeLoadIndicator.hide() + + if (onSuccess !== undefined) + onSuccess(); + }).always(function() { + $.oc.stripeLoadIndicator.hide() + }) + } + } + + Inspector.prototype.parseConfiguration = function(jsonString) { + if (jsonString === undefined) + throw new Error('The Inspector cannot be initialized because the Inspector configuration ' + + 'attribute is not defined on the inspectable element.'); + + if (!$.isArray(jsonString) && !$.isPlainObject(jsonString)) { + try { + this.config = $.parseJSON(jsonString) + } catch(err) { + throw new Error('Error parsing the Inspector field configuration. ' + err) + } + } else + this.config = jsonString + + this.propertyValuesField = $('input[data-inspector-values]', this.$el) + + // Inspector saves property values to data-property-xxx attributes if the input[data-inspector-values] + // doesn't exist, so the condition is not required. + // + // if (this.propertyValuesField.length === 0) + // throw new Error('Error initializing the Inspector: the inspectable element should contain ' + + // 'an hidden input element with the data-inspector-values property.') + } + + Inspector.prototype.getPopoverTemplate = function() { + return ' \ +
\ +

{{title}}

\ + {{#description}} \ +

{{description}}

\ + {{/description}} \ + \ +
\ +
\ + \ + {{#properties}} \ + \ + \ + {{#editor}}{{/editor}} \ + \ + {{/properties}} \ +
\ + {{#expandControl}}{{/expandControl}} \ + {{title}} \ + {{#info}}{{/info}} \ +
\ + \ + ' + } + + Inspector.prototype.init = function() { + // Exit if there are no properties + if (!this.config || this.config.length == 0) + return + + this.editors = [] + this.initProperties() + + this.$el.data('oc.inspectorVisible', true) + + var e = $.Event('showing.oc.inspector') + this.$el.trigger(e, [{callback: this.proxy(this.displayPopover)}]) + if (e.isDefaultPrevented()) + return + + if (!e.isPropagationStopped()) + this.displayPopover() + } + + Inspector.prototype.displayPopover = function() { + var fieldsConfig = this.preprocessConfig(), + renderEditorBound = this.proxy(this.renderEditor), + groupExpandedBound = this.proxy(this.groupExpanded), + data = { + title: this.title ? this.title : this.$el.data('inspector-title'), + description: this.description ? this.description : this.$el.data('inspector-description'), + properties: fieldsConfig.properties, + editor: function() { + return function(text, render) { + if (this.itemType == 'property') + return renderEditorBound(this, render) + } + }, + info: function() { + return function(text, render) { + if (this.description !== undefined && this.description != null) + return render('', this) + } + }, + propFormat: function() { + return function(text, render) { + return 'prop-'+render(text).replace('.', '-') + } + }, + colspan: function() { + return function(text, render) { + return this.itemType == 'group' ? 'colspan="2"' : null + } + }, + tableClass: function() { + return function(text, render) { + return fieldsConfig.hasGroups ? 'has-groups' : null + } + }, + cellClass: function() { + return function(text, render) { + var result = this.itemType + ((this.itemType == 'property' && this.groupIndex !== undefined) ? ' grouped' : '') + + if (this.itemType == 'property' && this.groupIndex !== undefined) + result += groupExpandedBound(this.group) ? ' expanded' : ' collapsed' + + if (this.itemType == 'property' && !this.showExternalParam) + result += ' no-external-parameter' + + return result + } + }, + expandControl: function() { + return function(text, render) { + if (this.itemType == 'group') { + this.itemStatus = groupExpandedBound(this.title) ? 'expanded' : '' + + return render('Expand/collapse', this) + } + } + }, + dataGroupIndex: function() { + return function(text, render) { + return this.groupIndex !== undefined && this.itemType == 'property' ? render('data-group-index={{groupIndex}}', this) : '' + } + + } + } + + var offset = this.$el.data('inspector-offset') + if (offset === undefined) + offset = 15 + + var offsetX = this.$el.data('inspector-offset-x'), + offsetY = this.$el.data('inspector-offset-y') + + var placement = this.$el.data('inspector-placement') + if (placement === undefined) + placement = 'bottom' + + var fallbackPlacement = this.$el.data('inspector-fallback-placement') + if (fallbackPlacement === undefined) + fallbackPlacement = 'bottom' + + this.$el.ocPopover({ + content: Mustache.render(this.getPopoverTemplate(), data), + highlightModalTarget: true, + modal: true, + placement: placement, + fallbackPlacement: fallbackPlacement, + containerClass: 'control-inspector', + container: this.$el.data('inspector-container'), + offset: offset, + offsetX: offsetX, + offsetY: offsetY, + width: 400 + }) + + this.$el.on('hiding.oc.popover', this.proxy(this.onBeforeHide)) + this.$el.on('hide.oc.popover', this.proxy(this.cleanup)) + this.$el.addClass('inspector-open') + + $(this.$el.data('oc.popover').$container).on('keydown', this.proxy(this.onPopoverKeyDown)) + + if (this.editors.length > 0) { + if (this.editors[0].focus !== undefined) + this.editors[0].focus() + } + + if (this.$el.closest('[data-inspector-external-parameters]').length > 0) + this.initExternalParameterEditor(this.$el.data('oc.popover').$container) + + for (var i=0, len = this.editors.length; i < len; i++) { + if (this.editors[i].init !== undefined) + this.editors[i].init() + } + + $('.with-tooltip', this.$el.data('oc.popover').$container) + .tooltip({ placement: 'auto right', container: 'body', delay: 500 }) + + var $container = this.$el.data('oc.popover').$container + $container.on('click', 'tr.group', this.proxy(this.onGroupClick)) + + var cssClass = this.options.inspectorCssClass + + if (cssClass !== undefined) + $container.addClass(cssClass) + } + + Inspector.prototype.onPopoverKeyDown = function(ev) { + if(ev.keyCode == 13) + $(ev.currentTarget).trigger('close.oc.popover') + } + + Inspector.prototype.onGroupClick = function(ev) { + var $container = this.$el.data('oc.popover').$container + this.toggleGroup($('a.expandControl', ev.target), $container) + + return false + } + + /* + * Initializes the external parameter editors for properties that support this feature. + */ + Inspector.prototype.initExternalParameterEditor = function($container) { + var self = this + + $('table.inspector-fields tr', $container).each(function(){ + if (!$(this).hasClass('no-external-parameter')) { + var property = $(this).data('property'), + $td = $('td', this), + $editorContainer = $('
'), + $editor = $( + '
\ +
\ + \ + \ + \ + \ +
\ +
') + + $editorContainer.append($td.children()) + $editorContainer.append($editor) + $td.append($editorContainer) + + var $editorLink = $('a', $editor) + + $editorLink + .click(function() { + return self.toggleExternalParameterEditor($(this)) + }) + .attr('title', 'Click to enter the external parameter name to load the property value from') + .tooltip({'container': 'body', delay: 500}) + + var $input = $editor.find('input'), + propertyValue = self.propertyValues[property] + + $input.on('focus', function(){ + var $field = $(this) + + $('td', $field.closest('table')).removeClass('active') + $field.closest('td').addClass('active') + }) + + $input.on('change', function(){ + self.markPropertyChanged(property, true) + }) + + var matches = [] + if (propertyValue) { + if (matches = propertyValue.match(/^\{\{([^\}]+)\}\}$/)) { + var value = $.trim(matches[1]) + + if (value.length > 0) { + self.showExternalParameterEditor($editorContainer, $editor, $editorLink, $td, true) + $editor.find('input').val(value) + self.writeProperty(property, null, true) + } + } + } + } + }) + } + + Inspector.prototype.showExternalParameterEditor = function($container, $editor, $editorLink, $cell, noAnimation) { + var position = $editor.position() + $('input', $editor).focus() + + if (!noAnimation) { + $editor.css({ + 'left': position.left + 'px', + 'right': 0 + }) + } else { + $editor.css('right', 0) + } + + setTimeout(function(){ + $editor.css('left', 0) + $cell.scrollTop(0) + }, 0) + + $container.addClass('editor-visible') + $editorLink.attr('data-original-title', 'Click to enter the property value') + this.toggleCellEditorVisibility($cell, false) + $editor.find('input').attr('tabindex', 0) + } + + Inspector.prototype.toggleExternalParameterEditor = function($editorLink) { + var $container = $editorLink.closest('.external-param-editor-container'), + $editor = $('.external-editor', $container), + $cell = $editorLink.closest('td'), + self = this + + $editorLink.tooltip('hide') + + if (!$container.hasClass('editor-visible')) { + self.showExternalParameterEditor($container, $editor, $editorLink, $cell) + } else { + var left = $container.width() + + $editor.css('left', left + 'px') + setTimeout(function(){ + $editor.css({ + 'left': 'auto', + 'right': '30px' + }) + $container.removeClass('editor-visible') + $container.closest('td').removeClass('active') + + var property = $container.closest('tr').data('property'), + propertyEditor = self.findEditor(property) + + if (propertyEditor && propertyEditor.onHideExternalParameterEditor !== undefined) + propertyEditor.onHideExternalParameterEditor() + }, 200) + $editorLink.attr('data-original-title', 'Click to enter the external parameter name to load the property value from') + $editor.find('input').attr('tabindex', -1) + self.toggleCellEditorVisibility($cell, true) + } + + return false + } + + Inspector.prototype.toggleCellEditorVisibility = function($cell, show) { + var $container = $('.external-param-editor-container', $cell) + + $container.children().each(function(){ + var $el = $(this) + + if ($el.hasClass('external-editor')) + return + + if (show) + $el.removeClass('hide') + else { + var height = $cell.data('inspector-cell-height') + + if (!height) { + height = $cell.height() + $cell.data('inspector-cell-height', height) + } + + $container.css('height', height + 'px') + $el.addClass('hide') + } + }) + } + + /* + * Creates group nodes in the property set + */ + Inspector.prototype.preprocessConfig = function() { + var fields = [], + result = { + hasGroups: false, + properties: [] + }, + groupIndex = 0 + + function findGroup(title) { + var groups = $.grep(fields, function(item) { + return item.itemType !== undefined && item.itemType == 'group' && item.title == title + }) + + if (groups.length > 0) + return groups[0] + + return null + } + + $.each(this.config, function() { + this.itemType = 'property' + + if (this.group === undefined) + fields.push(this) + else { + var group = findGroup(this.group) + + if (!group) { + group = { + itemType: 'group', + title: this.group, + properties: [], + groupIndex: groupIndex + } + + groupIndex++ + fields.push(group) + } + + this.groupIndex = group.groupIndex + group.properties.push(this) + } + }) + + $.each(fields, function() { + result.properties.push(this) + + if (this.itemType == 'group') { + result.hasGroups = true + + $.each(this.properties, function() { + result.properties.push(this) + }) + + delete this.properties + } + }) + + return result + } + + Inspector.prototype.toggleGroup = function(link, $container) { + var $link = $(link), + groupIndex = $link.data('group-index'), + propertyRows = $('tr[data-group-index='+groupIndex+']', $container), + duration = Math.round(100 / propertyRows.length), + collapse = true, + statuses = this.loadGroupExpandedStatuses(), + title = $('span.title-element', $link.closest('tr')).attr('title') + + if ($link.hasClass('expanded')) { + $link.removeClass('expanded') + statuses[title] = false + } else { + $link.addClass('expanded') + collapse = false + statuses[title] = true + } + + propertyRows.each(function(index){ + var self = $(this) + setTimeout(function(){ + self.toggleClass('collapsed', collapse) + self.toggleClass('expanded', !collapse) + }, index*duration) + + }) + + this.writeGroupExpandedStatuses(statuses) + } + + Inspector.prototype.loadGroupExpandedStatuses = function() { + var statuses = this.$el.data('inspector-group-statuses') + + return statuses !== undefined ? JSON.parse(statuses) : {} + } + + Inspector.prototype.writeGroupExpandedStatuses = function(statuses) { + this.$el.data('inspector-group-statuses', JSON.stringify(statuses)) + } + + Inspector.prototype.groupExpanded = function(title) { + var statuses = this.loadGroupExpandedStatuses() + + if (statuses[title] !== undefined) + return statuses[title] + + return false + } + + Inspector.prototype.normalizePropertyCode = function(code) { + var lowerCaseCode = code.toLowerCase() + + for (var index in this.config) { + var propertyInfo = this.config[index] + + if (propertyInfo.property.toLowerCase() == lowerCaseCode) + return propertyInfo.property + } + + return code + } + + Inspector.prototype.initProperties = function() { + if (!this.propertyValuesField.length) { + // Load property valies from data-property-xxx attributes + var properties = {}, + attributes = this.$el.get(0).attributes + + for (var i=0, len = attributes.length; i < len; i++) { + var attribute = attributes[i], + matches = [] + + if (matches = attribute.name.match(/^data-property-(.*)$/)) { + properties[this.normalizePropertyCode(matches[1])] = attribute.value + } + } + + this.propertyValues = properties + } else { + // Load property values from the hidden input + var propertyValuesStr = $.trim(this.propertyValuesField.val()) + this.propertyValues = propertyValuesStr.length === 0 ? {} : $.parseJSON(propertyValuesStr) + } + + try { + this.originalPropertyValues = $.extend(true, {}, this.propertyValues) + } catch(err) { + throw new Error('Error parsing the Inspector property values string. ' + err) + } + } + + Inspector.prototype.readProperty = function(property, returnUndefined) { + if (this.propertyValues[property] !== undefined) + return this.propertyValues[property] + + return returnUndefined ? undefined : null + } + + Inspector.prototype.getDefaultValue = function(property) { + for (var index in this.config) { + var propertyInfo = this.config[index] + + if (propertyInfo.itemType !== 'property') + continue + + if (propertyInfo.property == property) + return propertyInfo.default + } + + return undefined + } + + Inspector.prototype.writeProperty = function(property, value, noChangedStatusUpdate) { + this.propertyValues[property] = value + + if (this.propertyValuesField.length) + this.propertyValuesField.val(JSON.stringify(this.propertyValues)) + else { + var self = this + + $.each(this.propertyValues, function(propertyName) { + self.$el.attr('data-property-' + propertyName, this) + }) + } + + if (this.originalPropertyValues[property] === undefined || this.originalPropertyValues[property] != value) { + if (!noChangedStatusUpdate) { + this.$el.trigger('change') + this.markPropertyChanged(property, true) + } + } else { + if (!noChangedStatusUpdate) + this.markPropertyChanged(property, false) + } + + if (!noChangedStatusUpdate) + this.$el.trigger('propertyChanged.oc.Inspector', [property]) + + return value + } + + Inspector.prototype.markPropertyChanged = function(property, changed) { + $('#prop-'+property.replace('.', '-'), this.$el.data('oc.popover').$container).toggleClass('changed', changed) + } + + Inspector.prototype.renderEditor = function(data, render) { + $.oc.inspector.propertyCounter ++ + + var editorClass = 'inspectorEditor' + + data.type.charAt(0).toUpperCase() + + data.type.slice(1), + editorId = 'inspector-property-'+data.type+$.oc.inspector.propertyCounter + + if ($.oc.inspector.editors[editorClass] === undefined) + throw new Error('The Inspector editor class "' + editorClass + + '" is not defined in the $.oc.inspector.editors namespace.') + + var editor = new $.oc.inspector.editors[editorClass](editorId, this, data) + this.editors.push(editor) + editor.inspectorCellId = editorId + + return editor.renderEditor() + } + + Inspector.prototype.cleanup = function() { + this.$el.off('hiding.oc.popover') + this.$el.off('hide.oc.popover') + this.$el.off('.oc.Inspector') + this.$el.removeClass('inspector-open') + + var e = $.Event('hidden.oc.inspector') + this.$el.trigger(e) + + this.$el.data('oc.inspectorVisible', false) + + this.dispose() + } + + Inspector.prototype.dispose = function() { + for (var i=0, len=this.editors.length; i', data) + } + + InspectorEditorString.prototype.validate = function() { + var val = $.trim($(this.selector).val()) + + if (this.fieldDef.required && val.length === 0) + return this.fieldDef.validationMessage || 'Required fields were left blank.' + + if (this.fieldDef.validationPattern === undefined) + return + + var re = new RegExp(this.fieldDef.validationPattern, 'm') + + if (!val.match(re)) + return this.fieldDef.validationMessage + } + + InspectorEditorString.prototype.focus = function() { + $(this.selector).focus() + $(this.selector).closest('td').scrollLeft(0) + } + + $.oc.inspector.editors.inspectorEditorString = InspectorEditorString; + + // CHECKBOX EDITOR + // ================== + + var InspectorEditorCheckbox = function(editorId, inspector, fieldDef) { + this.inspector = inspector + this.fieldDef = fieldDef + this.editorId = editorId + this.selector = '#'+this.editorId+' input' + + Base.call(this) + + $(document).on('change', this.selector, this.proxy(this.applyValue)) + } + + InspectorEditorCheckbox.prototype = Object.create(BaseProto) + InspectorEditorCheckbox.prototype.constructor = InspectorEditorCheckbox + + InspectorEditorCheckbox.prototype.dispose = function() { + $(document).off('change', this.selector, this.proxy(this.applyValue)) + + this.inspector = null + this.fieldDef = null + this.editorId = null + this.selector = null + + BaseProto.dispose.call(this) + } + + InspectorEditorCheckbox.prototype.applyValue = function() { + this.inspector.writeProperty(this.fieldDef.property, $(this.selector).get(0).checked ? 1 : 0 ) + } + + InspectorEditorCheckbox.prototype.renderEditor = function() { + var self = this, + data = { + id: this.editorId, + cbId: this.editorId + '-cb', + title: this.fieldDef.title + } + + return Mustache.render(this.getTemplate(), data) + } + + InspectorEditorCheckbox.prototype.init = function() { + var isChecked = this.inspector.readProperty(this.fieldDef.property, true) + + if (isChecked === undefined) { + if (this.fieldDef.default !== undefined) { + isChecked = this.normalizeCheckedValue(this.fieldDef.default) + } + } else { + isChecked = this.normalizeCheckedValue(isChecked) + } + + $(this.selector).prop('checked', isChecked) + } + + InspectorEditorCheckbox.prototype.normalizeCheckedValue = function(value) { + if (value == '0' || value == 'false') + return false + + return value + } + + InspectorEditorCheckbox.prototype.focus = function() { + $(this.selector).closest('div').focus() + } + + InspectorEditorCheckbox.prototype.getTemplate = function() { + return ' \ + \ +
\ + \ + \ +
\ + \ + '; + } + + $.oc.inspector.editors.inspectorEditorCheckbox = InspectorEditorCheckbox; + + // DROPDOWN EDITOR + // ================== + + var InspectorEditorDropdown = function(editorId, inspector, fieldDef) { + this.inspector = inspector + this.fieldDef = fieldDef + this.editorId = editorId + this.selector = '#'+this.editorId+' select' + this.dynamicOptions = this.fieldDef.options ? false : true + this.initialization = false + + Base.call(this) + + $(document).on('change', this.selector, this.proxy(this.applyValue)) + } + + InspectorEditorDropdown.prototype = Object.create(BaseProto) + InspectorEditorDropdown.prototype.constructor = InspectorEditorDropdown + + InspectorEditorDropdown.prototype.dispose = function() { + $(document).off('change', this.selector, this.proxy(this.applyValue)) + + $(this.selector).select2('destroy') + + this.inspector = null + this.fieldDef = null + this.editorId = null + this.selector = null + + BaseProto.dispose.call(this) + } + + InspectorEditorDropdown.prototype.applyValue = function() { + this.inspector.writeProperty(this.fieldDef.property, $(this.selector).val(), this.initialization) + } + + InspectorEditorDropdown.prototype.renderEditor = function() { + var + self = this, + data = { + id: this.editorId, + value: $.trim(this.inspector.readProperty(this.fieldDef.property)), + selectId: this.editorId + '-select', + defaultOption: function() { + return function(text, render) { + if (self.fieldDef.placeholder == undefined) + return '' + + if (!Modernizr.touch) + return '' + } + } + } + + if (this.fieldDef.options) { + var options = [] + + if (this.fieldDef.placeholder !== undefined && Modernizr.touch) + options.push({value: null, title: this.fieldDef.placeholder}) + + $.each(this.fieldDef.options, function(value, title){ + options.push({value: value, title: title}) + }) + + data.options = options + } + + return Mustache.render(this.getTemplate(), data) + } + + InspectorEditorDropdown.prototype.getTemplate = function() { + return ' \ + \ + \ + \ + '; + } + + InspectorEditorDropdown.prototype.init = function() { + var value = this.inspector.readProperty(this.fieldDef.property, true) + + if (value === undefined) + value = this.inspector.getDefaultValue(this.fieldDef.property) + + $(this.selector).attr('data-no-auto-update-on-render', 'true') + + $(this.selector).val(value) + + if (!Modernizr.touch) { + var options = { + dropdownCssClass: 'ocInspectorDropdown' + } + + if (this.fieldDef.placeholder !== undefined) + options.placeholder = this.fieldDef.placeholder + + $(this.selector).select2(options) + } + + if (this.dynamicOptions) { + if (!Modernizr.touch) { + this.indicatorContainer = $('.select2-container', $(this.selector).closest('td')) + this.indicatorContainer.addClass('loading-indicator-container').addClass('size-small') + } + + this.loadOptions(true) + } + + if (this.fieldDef.depends) + this.inspector.$el.on('propertyChanged.oc.Inspector', $.proxy(this.onDependencyChanged, this)) + } + + InspectorEditorDropdown.prototype.onDependencyChanged = function(ev, property) { + if ($.inArray(property, this.fieldDef.depends) === -1) + return + + var self = this, + dependencyValues = this.getDependencyValues() + + if (this.prevDependencyValues === undefined || this.prevDependencyValues != dependencyValues) + this.loadOptions() + } + + InspectorEditorDropdown.prototype.saveDependencyValues = function() { + this.prevDependencyValues = this.getDependencyValues() + } + + InspectorEditorDropdown.prototype.getDependencyValues = function() { + var dependencyValues = '', + self = this + + $.each(this.fieldDef.depends, function(index, masterProperty){ + dependencyValues += masterProperty + ':' + self.inspector.readProperty(masterProperty) + '-' + }) + + return dependencyValues + } + + InspectorEditorDropdown.prototype.showLoadingIndicator = function() { + if (!Modernizr.touch) + this.indicatorContainer.loadIndicator() + } + + InspectorEditorDropdown.prototype.hideLoadingIndicator = function() { + if (!Modernizr.touch) + this.indicatorContainer.loadIndicator('hide') + } + + InspectorEditorDropdown.prototype.loadOptions = function(initialization) { + var $form = $(this.selector).closest('form'), + data = this.inspector.propertyValues, + $select = $(this.selector), + currentValue = this.inspector.readProperty(this.fieldDef.property, true), + self = this + + if (currentValue === undefined) + currentValue = this.inspector.getDefaultValue(this.fieldDef.property) + + for (var index in this.inspector.config) { + var propertyInfo = this.inspector.config[index] + + if (propertyInfo.itemType == 'property') { + if (data[propertyInfo.property] === undefined) + data[propertyInfo.property] = this.inspector.getDefaultValue(propertyInfo.property) + } + } + + if (this.fieldDef.depends) + this.saveDependencyValues() + + data.inspectorProperty = this.fieldDef.property + data.inspectorClassName = this.inspector.options.inspectorClass + + this.showLoadingIndicator() + + $form.request('onInspectableGetOptions', { + data: data, + success: function(data) { + $('option', $select).remove() + + if (self.fieldDef.placeholder !== undefined) + $select.append($('')) + + if (data.options) + $.each(data.options, function(key, obj) { + $select.append($('').attr('value', obj.value).text(obj.title)) + }) + + var hasOption = $('option[value="' + currentValue + '"]', $select).length > 0 + if (hasOption) + $select.val(currentValue) + else + $('option:first-child', $select).attr("selected", "selected"); + + self.initialization = initialization + $select.trigger('change') + self.initialization = false + + self.hideLoadingIndicator() + }, + error: function(jqXHR, textStatus, errorThrown) { + alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText) + self.hideLoadingIndicator() + } + }) + } + + InspectorEditorDropdown.prototype.onHideExternalParameterEditor = function() { + this.loadOptions(false) + } + + $.oc.inspector.editors.inspectorEditorDropdown = InspectorEditorDropdown; + + // INSPECTOR PLUGIN DEFINITION + // ============================ + + function initInspector($element) { + // For now the inspector configuration is not retained in the element's data, + // fix later (see #83) -ab Apr 27 2015 + var inspector = $element.data('oc.inspector') + + if (inspector === undefined) { + inspector = new Inspector($element.get(0), $element.data()) + + inspector.loadConfiguration(function(){ + inspector.init() + }) + + $element.data('oc.inspector', inspector) + } + // else + // inspector.init() + } + + $.fn.inspector = function (option) { + return this.each(function () { + initInspector($(this)) + }) + } + + // INSPECTOR DATA-API + // ================== + + $(document).on('click', '[data-inspectable]', function(){ + var $this = $(this) + + if ($this.data('oc.inspectorVisible')) + return false + + initInspector($this) + + return false + }) +}(window.jQuery); diff --git a/modules/system/assets/ui/js/loader.js b/modules/system/assets/ui/js/loader.js new file mode 100644 index 000000000..e69de29bb diff --git a/modules/system/assets/ui/less/breadcrumb.less b/modules/system/assets/ui/less/breadcrumb.less new file mode 100644 index 000000000..c8f0eb83d --- /dev/null +++ b/modules/system/assets/ui/less/breadcrumb.less @@ -0,0 +1,77 @@ +// +// Dependencies +// -------------------------------------------------- + +@import "icon.less"; + +// +// Breadcrumb +// -------------------------------------------------- + +@color-breadcrumb-text-active: #9da3a7; +@color-breadcrumb-text: #ecf0f1; +@color-breadcrumb-background: #2b343d; + +.control-breadcrumb { + font-size: 12px; + text-transform: uppercase; + padding: 15px 20px; + margin: -20px -20px 20px -20px; + background-color: @color-breadcrumb-background; + + ul { + padding: 0; + margin: 0; + } + + li { + list-style: none; + margin: 0; + padding: 0; + display: inline-block; + color: @color-breadcrumb-text-active; + letter-spacing: 0.05em; + + a { + color: @color-breadcrumb-text; + text-decoration: none; + &:hover { color: @color-breadcrumb-text; } + } + + &:after { + font-size: 14px; + line-height: 14px; + display: inline-block; + margin-left: 6px; + margin-right: 2px; + vertical-align: baseline; + color: @color-breadcrumb-text-active; + .icon(@angle-right); + } + + &:last-child:after { + content:''; + } + } +} + +// Breadcrumb to sit flush to the element below +body.breadcrumb-flush .control-breadcrumb, +.control-breadcrumb.breadcrumb-flush { + margin-bottom: 0; +} + +body.slim-container { + .control-breadcrumb { + margin-left: 0; + margin-right: 0; + } +} + +body.compact-container { + .control-breadcrumb { + margin-top: 0; + margin-left: 0; + margin-right: 0; + } +} \ No newline at end of file diff --git a/modules/system/assets/ui/less/button.base.less b/modules/system/assets/ui/less/button.base.less new file mode 100644 index 000000000..234b57e36 --- /dev/null +++ b/modules/system/assets/ui/less/button.base.less @@ -0,0 +1,155 @@ + +// Base styles +// -------------------------------------------------- + +.btn { + display: inline-block; + margin-bottom: 0; // For input.btn + font-weight: @btn-font-weight; + text-align: center; + vertical-align: middle; + cursor: pointer; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid transparent; + white-space: nowrap; + .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); + .user-select(none); + + &, + &:active, + &.active { + &:focus { + .tab-focus(); + } + } + + &:hover, + &:focus { + color: @btn-default-color; + text-decoration: none; + } + + &:active, + &.active { + outline: 0; + background-image: none; + .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); + } + + &.disabled, + &[disabled], + fieldset[disabled] & { + cursor: not-allowed; + pointer-events: none; // Future-proof disabling of clicks + .opacity(.65); + .box-shadow(none); + } +} + + +// Alternate buttons +// -------------------------------------------------- + +.btn-default { + .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); +} +.btn-primary { + .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); +} +// Success appears as green +.btn-success { + .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); +} +// Info appears as blue-green +.btn-info { + .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); +} +// Warning appears as orange +.btn-warning { + .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); +} +// Danger and error appear as red +.btn-danger { + .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); +} + + +// Link buttons +// ------------------------- + +// Make a button look and behave like a link +.btn-link { + color: @link-color; + font-weight: normal; + cursor: pointer; + border-radius: 0; + + &, + &:active, + &[disabled], + fieldset[disabled] & { + background-color: transparent; + .box-shadow(none); + } + &, + &:hover, + &:focus, + &:active { + border-color: transparent; + } + &:hover, + &:focus { + color: @link-hover-color; + text-decoration: underline; + background-color: transparent; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: @btn-link-disabled-color; + text-decoration: none; + } + } +} + + +// Button Sizes +// -------------------------------------------------- + +.btn-lg { + // line-height: ensure even-numbered height of button next to large input + .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); +} +.btn-sm { + // line-height: ensure proper height of button next to small input + .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); +} +.btn-xs { + .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small); +} + + +// Block button +// -------------------------------------------------- + +.btn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; +} + +// Vertically space out multiple block buttons +.btn-block + .btn-block { + margin-top: 5px; +} + +// Specificity overrides +input[type="submit"], +input[type="reset"], +input[type="button"] { + &.btn-block { + width: 100%; + } +} diff --git a/modules/system/assets/ui/less/button.groups.less b/modules/system/assets/ui/less/button.groups.less new file mode 100644 index 000000000..05b5d29a2 --- /dev/null +++ b/modules/system/assets/ui/less/button.groups.less @@ -0,0 +1,225 @@ + +// +// Button groups +// -------------------------------------------------- + +// Make the div behave like a button +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; // match .btn alignment given font-size hack above + > .btn { + position: relative; + float: left; + // Bring the "active" button to the front + &:hover, + &:focus, + &:active, + &.active { + z-index: 2; + } + &:focus { + // Remove focus outline when dropdown JS adds it after closing the menu + outline: none; + } + } +} + +// Prevent double borders when buttons are next to each other +.btn-group { + .btn + .btn, + .btn + .btn-group, + .btn-group + .btn, + .btn-group + .btn-group { + margin-left: -1px; + } +} + +// Optional: Group multiple button groups together for a toolbar +.btn-toolbar { + margin-left: -5px; // Offset the first child's margin + &:extend(.clearfix all); + + .btn-group, + .input-group { + float: left; + } + > .btn, + > .btn-group, + > .input-group { + margin-left: 5px; + } +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} + +// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match +.btn-group > .btn:first-child { + margin-left: 0; + &:not(:last-child):not(.dropdown-toggle) { + .border-right-radius(0); + } +} +// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + .border-left-radius(0); +} + +// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child { + > .btn:last-child, + > .dropdown-toggle { + .border-right-radius(0); + } +} +.btn-group > .btn-group:last-child > .btn:first-child { + .border-left-radius(0); +} + +// On active and open, don't show outline +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + + +// Sizing +// +// Remix the default button sizing classes into new ones for easier manipulation. + +.btn-group-xs > .btn { &:extend(.btn-xs); } +.btn-group-sm > .btn { &:extend(.btn-sm); } +.btn-group-lg > .btn { &:extend(.btn-lg); } + + +// Split button dropdowns +// ---------------------- + +// Give the line between buttons some depth +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} + +// The clickable button for toggling the menu +// Remove the gradient and set the same inset shadow as the :active state +.btn-group.open .dropdown-toggle { + .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); + + // Show no shadow for `.btn-link` since it has no other button styles. + &.btn-link { + .box-shadow(none); + } +} + + +// Reposition the caret +.btn .caret { + margin-left: 0; +} +// Carets in other button sizes +.btn-lg .caret { + border-width: @caret-width-large @caret-width-large 0; + border-bottom-width: 0; +} +// Upside down carets for .dropup +.dropup .btn-lg .caret { + border-width: 0 @caret-width-large @caret-width-large; +} + + +// Vertical button groups +// ---------------------- + +.btn-group-vertical { + > .btn, + > .btn-group, + > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; + } + + // Clear floats so dropdown menus can be properly placed + > .btn-group { + &:extend(.clearfix all); + > .btn { + float: none; + } + } + + > .btn + .btn, + > .btn + .btn-group, + > .btn-group + .btn, + > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; + } +} + +.btn-group-vertical > .btn { + &:not(:first-child):not(:last-child) { + border-radius: 0; + } + &:first-child:not(:last-child) { + border-top-right-radius: @border-radius-base; + .border-bottom-radius(0); + } + &:last-child:not(:first-child) { + border-bottom-left-radius: @border-radius-base; + .border-top-radius(0); + } +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) { + > .btn:last-child, + > .dropdown-toggle { + .border-bottom-radius(0); + } +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + .border-top-radius(0); +} + +// Justified button groups +// ---------------------- + +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; + > .btn, + > .btn-group { + float: none; + display: table-cell; + width: 1%; + } + > .btn-group .btn { + width: 100%; + } +} + + +// Checkbox and radio options +[data-toggle="buttons"] > .btn > input[type="radio"], +[data-toggle="buttons"] > .btn > input[type="checkbox"] { + display: none; +} diff --git a/modules/system/assets/ui/less/button.less b/modules/system/assets/ui/less/button.less new file mode 100644 index 000000000..677bb3c5a --- /dev/null +++ b/modules/system/assets/ui/less/button.less @@ -0,0 +1,6 @@ +// +// Buttons +// -------------------------------------------------- + +@import "button.base"; +@import "button.groups"; diff --git a/modules/system/assets/ui/less/button.mixins.less b/modules/system/assets/ui/less/button.mixins.less new file mode 100644 index 000000000..082f5dd42 --- /dev/null +++ b/modules/system/assets/ui/less/button.mixins.less @@ -0,0 +1,3 @@ +// Mixins +// -------------------------- + diff --git a/modules/system/assets/ui/less/button.variables.less b/modules/system/assets/ui/less/button.variables.less new file mode 100644 index 000000000..dc044d585 --- /dev/null +++ b/modules/system/assets/ui/less/button.variables.less @@ -0,0 +1,27 @@ +@btn-font-weight: normal; + +@btn-default-color: #333; +@btn-default-bg: #fff; +@btn-default-border: #ccc; + +@btn-primary-color: #fff; +@btn-primary-bg: @brand-primary; +@btn-primary-border: darken(@btn-primary-bg, 5%); + +@btn-success-color: #fff; +@btn-success-bg: @brand-success; +@btn-success-border: darken(@btn-success-bg, 5%); + +@btn-info-color: #fff; +@btn-info-bg: @brand-info; +@btn-info-border: darken(@btn-info-bg, 5%); + +@btn-warning-color: #fff; +@btn-warning-bg: @brand-warning; +@btn-warning-border: darken(@btn-warning-bg, 5%); + +@btn-danger-color: #fff; +@btn-danger-bg: @brand-danger; +@btn-danger-border: darken(@btn-danger-bg, 5%); + +@btn-link-disabled-color: @gray-light; diff --git a/modules/system/assets/ui/less/flag.less b/modules/system/assets/ui/less/flag.less new file mode 100644 index 000000000..80c5f32ee --- /dev/null +++ b/modules/system/assets/ui/less/flag.less @@ -0,0 +1,281 @@ +// +// Dependencies +// -------------------------------------------------- + +@import "global.less"; + +// +// Flag +// -------------------------------------------------- + +@flag-image-path: "../images"; + +[class^="flag-"], +[class*=" flag-"] { + .img-retina('@{flag-image-path}/flag-icons-small.png', '@{flag-image-path}/flag-icons-large.png', 16px, 3952px); + width: 16px; + height: 16px; + line-height: 16px; + vertical-align: middle; + display: inline-block; + margin: -3px 2px 0 2px; +} + +.flag-AfricanUnion { background-position:0 -16px } +.flag-ArabLeague { background-position:0 -32px } +.flag-ASEAN { background-position:0 -48px } +.flag-CARICOM { background-position:0 -64px } +.flag-CIS { background-position:0 -80px } +.flag-Commonwealth { background-position:0 -96px } +.flag-England { background-position:0 -112px } +.flag-European_Union { background-position:0 -128px } +.flag-Islamic_Conference { background-position:0 -144px } +.flag-Kosovo { background-position:0 -160px } +.flag-NATO { background-position:0 -176px } +.flag-NorthernCyprus { background-position:0 -192px } +.flag-NorthernIreland { background-position:0 -208px } +.flag-OlimpicMovement { background-position:0 -224px } +.flag-OPEC { background-position:0 -240px } +.flag-RedCross { background-position:0 -256px } +.flag-Scotland { background-position:0 -272px } +.flag-Somaliland { background-position:0 -288px } +.flag-Tibet { background-position:0 -304px } +.flag-United_Nations { background-position:0 -320px } +.flag-Wales { background-position:0 -336px } + +.flag-eu { background-position:0 -128px } +.flag-ad { background-position:0 -352px } +.flag-ae { background-position:0 -368px } +.flag-af { background-position:0 -384px } +.flag-ag { background-position:0 -400px } +.flag-ai { background-position:0 -416px } +.flag-al { background-position:0 -432px } +.flag-am { background-position:0 -448px } +.flag-ao { background-position:0 -464px } +.flag-aq { background-position:0 -480px } +.flag-ar { background-position:0 -496px } +.flag-as { background-position:0 -512px } +.flag-at { background-position:0 -528px } +.flag-au { background-position:0 -544px } +.flag-aw { background-position:0 -560px } +.flag-ax { background-position:0 -576px } +.flag-az { background-position:0 -592px } +.flag-ba { background-position:0 -608px } +.flag-bb { background-position:0 -624px } +.flag-bd { background-position:0 -640px } +.flag-be { background-position:0 -656px } +.flag-bf { background-position:0 -672px } +.flag-bg { background-position:0 -688px } +.flag-bh { background-position:0 -704px } +.flag-bi { background-position:0 -720px } +.flag-bj { background-position:0 -736px } +.flag-bm { background-position:0 -752px } +.flag-bn { background-position:0 -768px } +.flag-bo { background-position:0 -784px } +.flag-br { background-position:0 -800px } +.flag-bs { background-position:0 -816px } +.flag-bt { background-position:0 -832px } +.flag-bw { background-position:0 -848px } +.flag-by { background-position:0 -864px } +.flag-bz { background-position:0 -880px } +.flag-ca { background-position:0 -896px } +.flag-cg { background-position:0 -912px } +.flag-cf { background-position:0 -928px } +.flag-cd { background-position:0 -944px } +.flag-ch { background-position:0 -960px } +.flag-ci { background-position:0 -976px } +.flag-ck { background-position:0 -992px } +.flag-cl { background-position:0 -1008px } +.flag-cm { background-position:0 -1024px } +.flag-cn { background-position:0 -1040px } +.flag-co { background-position:0 -1056px } +.flag-cr { background-position:0 -1072px } +.flag-cu { background-position:0 -1088px } +.flag-cv { background-position:0 -1104px } +.flag-cy { background-position:0 -1120px } +.flag-cz { background-position:0 -1136px } +.flag-de { background-position:0 -1152px } +.flag-dj { background-position:0 -1168px } +.flag-dk { background-position:0 -1184px } +.flag-dm { background-position:0 -1200px } +.flag-do { background-position:0 -1216px } +.flag-dz { background-position:0 -1232px } +.flag-ec { background-position:0 -1248px } +.flag-ee { background-position:0 -1264px } +.flag-eg { background-position:0 -1280px } +.flag-eh { background-position:0 -1296px } +.flag-er { background-position:0 -1312px } +.flag-es { background-position:0 -1328px } +.flag-et { background-position:0 -1344px } +.flag-fi { background-position:0 -1360px } +.flag-fj { background-position:0 -1376px } +.flag-fm { background-position:0 -1392px } +.flag-fo { background-position:0 -1408px } +.flag-fr { background-position:0 -1424px } +.flag-bl { background-position:0 -1424px } +.flag-cp { background-position:0 -1424px } +.flag-mf { background-position:0 -1424px } +.flag-yt { background-position:0 -1424px } +.flag-ga { background-position:0 -1440px } +.flag-gb { background-position:0 -1456px } +.flag-sh { background-position:0 -1456px } +.flag-gd { background-position:0 -1472px } +.flag-ge { background-position:0 -1488px } +.flag-gg { background-position:0 -1504px } +.flag-gh { background-position:0 -1520px } +.flag-gi { background-position:0 -1536px } +.flag-gl { background-position:0 -1552px } +.flag-gm { background-position:0 -1568px } +.flag-gn { background-position:0 -1584px } +.flag-gp { background-position:0 -1600px } +.flag-gq { background-position:0 -1616px } +.flag-gr { background-position:0 -1632px } +.flag-gt { background-position:0 -1648px } +.flag-gu { background-position:0 -1664px } +.flag-gw { background-position:0 -1680px } +.flag-gy { background-position:0 -1696px } +.flag-hk { background-position:0 -1712px } +.flag-hn { background-position:0 -1728px } +.flag-hr { background-position:0 -1744px } +.flag-ht { background-position:0 -1760px } +.flag-hu { background-position:0 -1776px } +.flag-id { background-position:0 -1792px } +.flag-mc { background-position:0 -1792px } +.flag-ie { background-position:0 -1808px } +.flag-il { background-position:0 -1824px } +.flag-im { background-position:0 -1840px } +.flag-in { background-position:0 -1856px } +.flag-iq { background-position:0 -1872px } +.flag-ir { background-position:0 -1888px } +.flag-is { background-position:0 -1904px } +.flag-it { background-position:0 -1920px } +.flag-je { background-position:0 -1936px } +.flag-jm { background-position:0 -1952px } +.flag-jo { background-position:0 -1968px } +.flag-jp { background-position:0 -1984px } +.flag-ke { background-position:0 -2000px } +.flag-kg { background-position:0 -2016px } +.flag-kh { background-position:0 -2032px } +.flag-ki { background-position:0 -2048px } +.flag-km { background-position:0 -2064px } +.flag-kn { background-position:0 -2080px } +.flag-kp { background-position:0 -2096px } +.flag-kr { background-position:0 -2112px } +.flag-kw { background-position:0 -2128px } +.flag-ky { background-position:0 -2144px } +.flag-kz { background-position:0 -2160px } +.flag-la { background-position:0 -2176px } +.flag-lb { background-position:0 -2192px } +.flag-lc { background-position:0 -2208px } +.flag-li { background-position:0 -2224px } +.flag-lk { background-position:0 -2240px } +.flag-lr { background-position:0 -2256px } +.flag-ls { background-position:0 -2272px } +.flag-lt { background-position:0 -2288px } +.flag-lu { background-position:0 -2304px } +.flag-lv { background-position:0 -2320px } +.flag-ly { background-position:0 -2336px } +.flag-ma { background-position:0 -2352px } +.flag-md { background-position:0 -2368px } +.flag-me { background-position:0 -2384px } +.flag-mg { background-position:0 -2400px } +.flag-mh { background-position:0 -2416px } +.flag-mk { background-position:0 -2432px } +.flag-ml { background-position:0 -2448px } +.flag-mm { background-position:0 -2464px } +.flag-mn { background-position:0 -2480px } +.flag-mo { background-position:0 -2496px } +.flag-mq { background-position:0 -2512px } +.flag-mr { background-position:0 -2528px } +.flag-ms { background-position:0 -2544px } +.flag-mt { background-position:0 -2560px } +.flag-mu { background-position:0 -2576px } +.flag-mv { background-position:0 -2592px } +.flag-mw { background-position:0 -2608px } +.flag-mx { background-position:0 -2624px } +.flag-my { background-position:0 -2640px } +.flag-mz { background-position:0 -2656px } +.flag-na { background-position:0 -2672px } +.flag-nc { background-position:0 -2688px } +.flag-ne { background-position:0 -2704px } +.flag-ng { background-position:0 -2720px } +.flag-ni { background-position:0 -2736px } +.flag-nl { background-position:0 -2752px } +.flag-bq { background-position:0 -2752px } +.flag-no { background-position:0 -2768px } +.flag-bv { background-position:0 -2768px } +.flag-nq { background-position:0 -2768px } +.flag-sj { background-position:0 -2768px } +.flag-np { background-position:0 -2784px } +.flag-nr { background-position:0 -2800px } +.flag-nz { background-position:0 -2816px } +.flag-om { background-position:0 -2832px } +.flag-pa { background-position:0 -2848px } +.flag-pe { background-position:0 -2864px } +.flag-pf { background-position:0 -2880px } +.flag-pg { background-position:0 -2896px } +.flag-ph { background-position:0 -2912px } +.flag-pk { background-position:0 -2928px } +.flag-pl { background-position:0 -2944px } +.flag-pr { background-position:0 -2960px } +.flag-ps { background-position:0 -2976px } +.flag-pt { background-position:0 -2992px } +.flag-pw { background-position:0 -3008px } +.flag-py { background-position:0 -3024px } +.flag-qa { background-position:0 -3040px } +.flag-re { background-position:0 -3056px } +.flag-ro { background-position:0 -3072px } +.flag-rs { background-position:0 -3088px } +.flag-ru { background-position:0 -3104px } +.flag-rw { background-position:0 -3120px } +.flag-sa { background-position:0 -3136px } +.flag-sb { background-position:0 -3152px } +.flag-sc { background-position:0 -3168px } +.flag-sd { background-position:0 -3184px } +.flag-se { background-position:0 -3200px } +.flag-sg { background-position:0 -3216px } +.flag-si { background-position:0 -3232px } +.flag-sk { background-position:0 -3248px } +.flag-sl { background-position:0 -3264px } +.flag-sm { background-position:0 -3280px } +.flag-sn { background-position:0 -3296px } +.flag-so { background-position:0 -3312px } +.flag-sr { background-position:0 -3328px } +.flag-st { background-position:0 -3344px } +.flag-sv { background-position:0 -3360px } +.flag-sy { background-position:0 -3376px } +.flag-sz { background-position:0 -3392px } +.flag-tc { background-position:0 -3408px } +.flag-td { background-position:0 -3424px } +.flag-tg { background-position:0 -3440px } +.flag-th { background-position:0 -3456px } +.flag-tj { background-position:0 -3472px } +.flag-tl { background-position:0 -3488px } +.flag-tm { background-position:0 -3504px } +.flag-tn { background-position:0 -3520px } +.flag-to { background-position:0 -3536px } +.flag-tr { background-position:0 -3552px } +.flag-tt { background-position:0 -3568px } +.flag-tv { background-position:0 -3584px } +.flag-tw { background-position:0 -3600px } +.flag-tz { background-position:0 -3616px } +.flag-ua { background-position:0 -3632px } +.flag-ug { background-position:0 -3648px } +.flag-us { background-position:0 -3664px } +.flag-uy { background-position:0 -3680px } +.flag-uz { background-position:0 -3696px } +.flag-va { background-position:0 -3712px } +.flag-vc { background-position:0 -3728px } +.flag-ve { background-position:0 -3744px } +.flag-vg { background-position:0 -3760px } +.flag-vi { background-position:0 -3776px } +.flag-vn { background-position:0 -3792px } +.flag-vu { background-position:0 -3808px } +.flag-ws { background-position:0 -3824px } +.flag-ye { background-position:0 -3840px } +.flag-za { background-position:0 -3856px } +.flag-zm { background-position:0 -3872px } +.flag-zw { background-position:0 -3888px } +.flag-sx { background-position:0 -3904px } +.flag-cw { background-position:0 -3920px } +.flag-ss { background-position:0 -3936px } diff --git a/modules/system/assets/ui/less/form.base.less b/modules/system/assets/ui/less/form.base.less new file mode 100644 index 000000000..f607b8509 --- /dev/null +++ b/modules/system/assets/ui/less/form.base.less @@ -0,0 +1,438 @@ +// +// Forms +// -------------------------------------------------- + + +// Normalize non-controls +// +// Restyle and baseline non-control form elements. + +fieldset { + padding: 0; + margin: 0; + border: 0; + // Chrome and Firefox set a `min-width: -webkit-min-content;` on fieldsets, + // so we reset that to ensure it behaves more like a standard block element. + // See https://github.com/twbs/bootstrap/issues/12359. + min-width: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: @line-height-computed; + font-size: (@font-size-base * 1.5); + line-height: inherit; + color: @legend-color; + border: 0; + border-bottom: 1px solid @legend-border-color; +} + +label { + display: inline-block; + margin-bottom: 5px; + font-weight: bold; +} + + +// Normalize form controls +// +// While most of our form styles require extra classes, some basic normalization +// is required to ensure optimum display with or without those classes to better +// address browser inconsistencies. + +// Override content-box in Normalize (* isn't specific enough) +input[type="search"] { + .box-sizing(border-box); +} + +// Position radios and checkboxes better +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; /* IE8-9 */ + line-height: normal; +} + +// Set the height of file controls to match text inputs +input[type="file"] { + display: block; +} + +// Make range inputs behave like textual form controls +input[type="range"] { + display: block; + width: 100%; +} + +// Make multiple select elements height not fixed +select[multiple], +select[size] { + height: auto; +} + +// Focus for file, radio, and checkbox +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + .tab-focus(); +} + +// Adjust output element +output { + display: block; + padding-top: (@padding-base-vertical + 1); + font-size: @font-size-base; + line-height: @line-height-base; + color: @input-color; +} + + +// Common form controls +// +// Shared size and type resets for form controls. Apply `.form-control` to any +// of the following form controls: +// +// select +// textarea +// input[type="text"] +// input[type="password"] +// input[type="datetime"] +// input[type="datetime-local"] +// input[type="date"] +// input[type="month"] +// input[type="time"] +// input[type="week"] +// input[type="number"] +// input[type="email"] +// input[type="url"] +// input[type="search"] +// input[type="tel"] +// input[type="color"] + +.form-control { + display: block; + width: 100%; + height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) + padding: @padding-base-vertical @padding-base-horizontal; + font-size: @font-size-base; + line-height: @line-height-base; + color: @input-color; + background-color: @input-bg; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid @input-border; + border-radius: @input-border-radius; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); + .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); + + // Customize the `:focus` state to imitate native WebKit styles. + .form-control-focus(); + + // Placeholder + .placeholder(); + + // Disabled and read-only inputs + // + // HTML5 says that controls under a fieldset > legend:first-child won't be + // disabled if the fieldset is disabled. Due to implementation difficulty, we + // don't honor that edge case; we style them as disabled anyway. + &[disabled], + &[readonly], + fieldset[disabled] & { + cursor: not-allowed; + background-color: @input-bg-disabled; + opacity: 1; // iOS fix for unreadable disabled content + } + + // Reset height for `textarea`s + textarea& { + height: auto; + } +} + + +// Search inputs in iOS +// +// This overrides the extra rounded corners on search inputs in iOS so that our +// `.form-control` class can properly style them. Note that this cannot simply +// be added to `.form-control` as it's not specific enough. For details, see +// https://github.com/twbs/bootstrap/issues/11586. + +input[type="search"] { + -webkit-appearance: none; +} + + +// Special styles for iOS date input +// +// In Mobile Safari, date inputs require a pixel line-height that matches the +// given height of the input. + +input[type="date"] { + line-height: @input-height-base; +} + + +// Form groups +// +// Designed to help with the organization and spacing of vertical forms. For +// horizontal forms, use the predefined grid classes. + +.form-group { + margin-bottom: 15px; +} + + +// Checkboxes and radios +// +// Indent the labels to position radios/checkboxes as hanging controls. + +.radio, +.checkbox { + display: block; + min-height: @line-height-computed; // clear the floating input if there is no label text + margin-top: 10px; + margin-bottom: 10px; + padding-left: 20px; + label { + display: inline; + font-weight: normal; + cursor: pointer; + } +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + float: left; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing +} + +// Radios and checkboxes on same line +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; // space out consecutive inline controls +} + +// Apply same disabled cursor tweak as for inputs +// +// Note: Neither radios nor checkboxes can be readonly. +input[type="radio"], +input[type="checkbox"], +.radio, +.radio-inline, +.checkbox, +.checkbox-inline { + &[disabled], + fieldset[disabled] & { + cursor: not-allowed; + } +} + + +// Form control sizing +// +// Build on `.form-control` with modifier classes to decrease or increase the +// height and font-size of form controls. + +.input-sm { + .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); +} + +.input-lg { + .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); +} + + +// Form control feedback states +// +// Apply contextual and semantic states to individual form controls. + +.has-feedback { + // Enable absolute positioning + position: relative; + + // Ensure icons don't overlap text + .form-control { + padding-right: (@input-height-base * 1.25); + } + + // Feedback icon (requires .glyphicon classes) + .form-control-feedback { + position: absolute; + top: (@line-height-computed + 5); // Height of the `label` and its margin + right: 0; + display: block; + width: @input-height-base; + height: @input-height-base; + line-height: @input-height-base; + text-align: center; + } +} + +// Feedback states +.has-success { + .form-control-validation(@state-success-text; @state-success-text; @state-success-bg); +} +.has-warning { + .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg); +} +.has-error { + .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg); +} + + +// Static form control text +// +// Apply class to a `p` element to make any string of text align with labels in +// a horizontal form layout. + +.form-control-static { + margin-bottom: 0; // Remove default margin from `p` +} + + +// Help text +// +// Apply to any element you wish to create light text for placement immediately +// below a form control. Use for general help, formatting, or instructional text. + +.help-block { + display: block; // account for any element using help-block + margin-top: 5px; + margin-bottom: 10px; + color: lighten(@text-color, 25%); // lighten the text some for contrast +} + + + +// Inline forms +// +// Make forms appear inline(-block) by adding the `.form-inline` class. Inline +// forms begin stacked on extra small (mobile) devices and then go inline when +// viewports reach <768px. +// +// Requires wrapping inputs and labels with `.form-group` for proper display of +// default HTML form controls and our custom form controls (e.g., input groups). +// +// Heads up! This is mixin-ed into `.navbar-form` in navbars.less. + +.form-inline { + + // Kick in the inline + @media (min-width: @screen-sm-min) { + // Inline-block all the things for "inline" + .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + + // In navbar-form, allow folks to *not* use `.form-group` + .form-control { + display: inline-block; + width: auto; // Prevent labels from stacking above inputs in `.form-group` + vertical-align: middle; + } + // Input groups need that 100% width though + .input-group > .form-control { + width: 100%; + } + + .control-label { + margin-bottom: 0; + vertical-align: middle; + } + + // Remove default margin on radios/checkboxes that were used for stacking, and + // then undo the floating of radios and checkboxes to match (which also avoids + // a bug in WebKit: https://github.com/twbs/bootstrap/issues/1969). + .radio, + .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + padding-left: 0; + vertical-align: middle; + } + .radio input[type="radio"], + .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + + // Validation states + // + // Reposition the icon because it's now within a grid column and columns have + // `position: relative;` on them. Also accounts for the grid gutter padding. + .has-feedback .form-control-feedback { + top: 0; + } + } +} + + +// Horizontal forms +// +// Horizontal forms are built on grid classes and allow you to create forms with +// labels on the left and inputs on the right. + +.form-horizontal { + + // Consistent vertical alignment of labels, radios, and checkboxes + .control-label, + .radio, + .checkbox, + .radio-inline, + .checkbox-inline { + margin-top: 0; + margin-bottom: 0; + padding-top: (@padding-base-vertical + 1); // Default padding plus a border + } + // Account for padding we're adding to ensure the alignment and of help text + // and other content below items + .radio, + .checkbox { + min-height: (@line-height-computed + (@padding-base-vertical + 1)); + } + + // Make form groups behave like rows + .form-group { + .make-row(); + } + + .form-control-static { + padding-top: (@padding-base-vertical + 1); + } + + // Only right align form labels here when the columns stop stacking + @media (min-width: @screen-sm-min) { + .control-label { + text-align: right; + } + } + + // Validation states + // + // Reposition the icon because it's now within a grid column and columns have + // `position: relative;` on them. Also accounts for the grid gutter padding. + .has-feedback .form-control-feedback { + top: 0; + right: (@grid-gutter-width / 2); + } +} diff --git a/modules/system/assets/ui/less/form.less b/modules/system/assets/ui/less/form.less new file mode 100644 index 000000000..2dffd514b --- /dev/null +++ b/modules/system/assets/ui/less/form.less @@ -0,0 +1,854 @@ +// +// Dependencies +// -------------------------------------------------- + +@import "global.less"; +@import "icon.less"; +@import "site.variables.less"; +@import "site.mixins.less"; + +// +// Forms +// -------------------------------------------------- + +@import "form.variables.less"; +@import "form.mixins.less"; +@import "form.base.less"; + +// +// Resets to bootstrap +// + +label { + font-weight: 600; +} + +.form-control { + .transition(none); + .box-shadow(none); + .border-radius(3px); + &:focus { + .box-shadow(none); + } + + &.align-right { + text-align: right; + } +} + +// +// Form containers +// + +.form-preview { + padding: 15px; + margin-bottom: 20px; + background: white; + border: 1px solid #eee; + + >.form-group:last-child { + padding-bottom: 0; + } + + // Form to sit flush to the element above + &.form-flush { + border-top: none; + } +} + + +// +// Nice forms +// + +.form-elements, .form-tabless-fields { + .clearfix(); +} + +label { + font-size: 12px; +} + +.radio.nolabel label, +.checkbox.nolabel label { + .text-hide(); +} + +.form-control { + border: 1px solid @color-form-field-border; + position: relative; + -webkit-appearance: none; + font-size: 13px; + + &:focus { + border: 1px solid @color-form-field-border-focus; + } + + &.icon { + background-repeat: no-repeat; + background-position: right -2px; + padding-right: 30px !important; + background-image: url('../images/bitmap-icons.png'); + + &.plus { background-position: right -124px; } + &.search { background-position: right -84px; } + &.user { background-position: right -41px; } + &.lock { background-position: right 0; } + } + + &.growable { + width: 110px; + //.transition(width 0.2s); + &:focus, &:active { + width: 200px !important; + } + } +} + +.form-group { + position: relative; + + &:empty { + display: none; + } + + &, &.layout-item { + padding-bottom: 20px; + margin-bottom: 0; + } + + .box-sizing(border-box); + + &.is-required { + > label:after { + color: @color-form-required-asterisk; + vertical-align: super; + font-size: 60%; + content: " *"; + text-shadow: 0 0 2px darken(@color-form-required-asterisk, 15%); + } + } + + &.span-full { + width: 100%; + float: left; + } + + &.span-left { + float: left; + width: 48.5%; + clear: left; + } + + &.span-right { + float: right; + width: 48.5%; + clear: right; + } + + &.layout-relative { + padding-bottom: 0; + } + + &.checkbox-field { + padding-bottom: 5px; + } + + &.number-field { + > .form-control { text-align: right; } + } + + &.checkbox-align { + padding-left: 23px; + margin-top: -5px; + } + + &.field-slim { + &.span-left, &.span-right { + width: 50%; + } + } + + &.field-indent { + padding-left: 23px; + } + + &.input-sidebar-control { + padding-right: 35px; + + .sidebar-control { + position: absolute; + right: 8px; + top: 34px; + font-size: 16px; + color: @color-input-sidebar-control; + &:hover, &:focus { + text-decoration: none; + color: @color-link; + outline: none; + } + } + } +} + +.form-group-preview { + + .form-control { + background-color: @color-form-field-preview; + height: auto; + min-height: 38px; + } + + .custom-checkbox, + .custom-radio { + label { cursor: default; } + } +} + +.help-block { + font-size: 12px; + margin-bottom: 0; + &.before-field { + margin-top: 0; + margin-bottom: 10px; + } +} + +.input-with-icon { + position: relative; + + > .icon { + position: absolute; + z-index: 2; + padding: 13px; + pointer-events: none; + color: @color-form-field-icon; + font-size: 15px; + } + + &.right-align { + > .icon { right: 0; } + input { padding-right: 32px !important; } + } + + &.left-align { + > .icon { left: 0; } + input { padding-left: 32px !important; } + } +} + +.field-section { + border-bottom: 1px solid @color-form-field-border; + padding-top: 3px; + padding-bottom: 7px; + + > h4 { + color: rgba(0,0,0,.6); + } + + > p:first-child, + > h4:first-child { + margin: 0; + } + + &.is-collapsible { + cursor: pointer; + + > h4:before { + // Icon + display: inline-block; + .icon-FontAutumn(); + vertical-align: baseline; + content: @caret-down; + + margin: 0 8px; + float: right; + color: rgba(0,0,0,.4); + } + + &:hover { + border-bottom: 1px solid darken(@color-form-field-border, 10%); + > h4:before { + color: inherit; + } + } + } +} + +.field-textarea { + resize: vertical; + &.size-tiny { min-height: @size-tiny; } + &.size-small { min-height: @size-small; } + &.size-large { min-height: @size-large; } + &.size-huge { min-height: @size-huge; } + &.size-giant { min-height: @size-giant; } +} + +.field-checkboxlist { + .checkbox:last-of-type { + margin-bottom: 0; + } +} + +.field-checkboxlist-scrollable { + background: white; + border: 1px solid @color-list-border; + padding-left: 15px; + height: @size-large + 100; + + // First checkbox + .checkbox { + margin-top: 15px; + margin-bottom: 5px; + } + + // All others + .checkbox ~ .checkbox { margin-top: 0; } +} + +.field-recordfinder { + background-color: @color-form-field-bg; + border: 1px solid @color-form-field-border; + overflow: hidden; + position: relative; + .form-control { + background: transparent; + border-color: transparent; + height: auto; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + padding: 8px 30px 10px 11px; + } + .btn { + background: transparent; + position: absolute; + right: -2px; + top: 50%; + margin-top: -44px; + height: 88px; + color: lighten(@color-form-field-recordfinder-btn, 15%); + + i { + font-size: 14px; + } + + &:hover { + color: @color-form-field-recordfinder-btn; + } + } + .text-muted i { + font-size: 14px; + position: relative; + top: 1px; + display: inline-block; + margin: 0 2px; + } + .primary { + font-weight: 600; + } +} + +.recordfinder-search { + background-position: right -81px !important; + border-top: none !important; + border-left: none !important; + border-right: none !important; + border-radius: 0; +} + +.form-buttons { + .clearfix(); + padding-bottom: 20px; + font-size: 0; + + .btn { + margin-right: 10px; + + &.no-margin-right { + margin-right: 0; + } + } + + .pull-right { + margin-right: 0; + margin-left: 10px; + } + + &.buttons-offset { + padding-left: 20px; + } +} + +body.slim-container { + .form-buttons { + padding: 0 20px 20px; + } +} + +@media (max-width: @form-breakpoint-max) { + .form-group { + &.span-left, &.span-right { + width: 100%; + clear: none; + } + } +} + +// +// Nice Checkboxes & Radios +// + +.custom-checkbox, +.custom-radio { + padding-left: 23px; + margin-top: 0; + + input[type=radio], + input[type=checkbox] { + display: none; + } + + label { + display: inline-block; + cursor: pointer; + position: relative; + padding-left: 20px; + margin-right: 15px; + margin-left: -20px; + + font-size: 12px; + &:before { + content: ""; + display: inline-block; + text-align: center; + + width: 16px; + height: 16px; + + margin-right: 10px; + position: absolute; + left: -3px; + bottom: 1px; + background-color: #FFFFFF; + border: 1px solid @color-custom-input-border; + color: @color-custom-input-icon; + + } + &:hover:before { + border-color: darken(@color-custom-input-border, 10%); + color: darken(@color-custom-input-icon, 10%); + } + &:active:before { + border-color: darken(@color-custom-input-border, 20%); + color: darken(@color-custom-input-icon, 20%); + } + } + + input[type=radio]:checked + label:before { + .icon(@circle); + font-size: 9px; + line-height: 12px; + border-width: 2px; + } + + input[type=checkbox]:checked + label:before { + .icon(@check); + font-size: 10px; + line-height: 12px; + border-width: 2px; + } + + &:focus { + outline: none; + label:before { + border-color: @color-focus; + } + } + + p.help-block { + margin-top: 0; + } +} + +.custom-radio label:before { + border-radius: 8px; +} + +.custom-checkbox label:before { + border-radius: @border-radius-base; +} + +// +// ON / OFF Switcher +// + +.switch-field { + .field-switch { + padding-left: 75px; + float: left; + } +} + +.custom-switch { + display: block; + width: 58px; + height: 26px; + position: relative; + text-transform: uppercase; + border: none; + cursor: pointer; + .border-radius(3px); + + * { .box-sizing(border-box); } + &.disabled { .opacity(.5); } + .slide-button { + z-index: 4; + display: block; + position: absolute; + right: 34px; + top: 2px; + width: 22px; + height: 22px; + background-color: @color-switch-input-bg; + .border-radius(20px); + .transition(all 0.1s); + } + + label, + > span { + line-height: 23px; + vertical-align: middle; + } + + label { + z-index: 3; + width: 100%; + display: block; + position: relative; + } + + input { + z-index: 5; + position: absolute; + .opacity(0); + &:checked { + ~ .slide-button { + right: 2px; + } + ~ span { background-color: @color-switch-input-on; } + ~ span span { + &:first-of-type { + color: #FFFFFF; + display: block; + } + &:last-of-type { + color: #666666; + display: none; + } + } + } + } + + > span { + display: block; + height: 100%; + position: absolute; + left: 0; + width: 100%; + background-color: @color-switch-input-off; + font-size: 11px; + .user-select(none); + .border-radius(20px); + + span { + z-index: 5; + display: block; + width: 50%; + position: absolute; + top: 1px; + left: 0; + .box-sizing(border-box); + &:last-child { + left: 50%; + color: #FFFFFF; + display: block; + } + &:first-of-type { + padding-left: 9px; + display: none; + color: #666666; + } + } + } +} + + +// +// Nice Dropdowns +// + +.custom-select { + + // + // Allows Select2 to work with Bootstrap + // + + .select2-choice { + border: 0; + border-radius: @border-radius-base; + .select2-arrow { + border-radius: 0 @border-radius-base @border-radius-base 0; + } + } + + &.select2-container { + padding: 0px; + .select2-choices { + border: 0 !important; + border-radius: @border-radius-base; + } + + .loading-indicator { + background: transparent; + + > span { + background-image:url(../images/loading-indicator-transparent.svg); + left: auto; + right: 10px; + top: 19px; + background-size: 17px 17px; + } + } + + &.in-progress { + .select2-choice .select2-arrow { + display: none !important; + } + } + } + + &.select2-container.select2-dropdown-open { + border-color: @color-form-field-border-focus; + &,.select2-choices { + border-radius: @border-radius-base @border-radius-base 0 0; + } + + &.select2-drop-above { + border-radius: 0 0 @border-radius-base @border-radius-base; + } + } + + &.select2-container-active { + border-color: @color-form-field-border-focus; + } + + // + // Restyle Select2 + // + + &.select2-container { + .select2-choice { + height: 36px; + line-height: 36px; + padding: 0 0 0 15px; + border: none; + background: #FFFFFF; + + .select2-arrow { + width: 38px; + background: none; + background: transparent; + border-left: none; + b { + background: none !important; + text-align: center; + color: @color-custom-input-icon; + &:before { .icon(@angle-down); } + } + } + } + + &.select2-container-disabled { + background-color: #f4f4f4; + .select2-choice { + background-color: #f4f4f4; + .select2-arrow b { .opacity(.5); } + } + } + } + + &.select2-container.select2-container-active { + .select2-choice, + .select2-choices { + .box-shadow(none); + } + } + + &.select2-container.select2-dropdown-open, + &.select2-container.select2-dropdown-open.select2-drop-above { + .select2-choice { + background: @color-custom-select-bg; + .select2-arrow { + border-left-color: transparent; + b { + &:before { .icon(@angle-up); } + } + } + } + } +} + +// +// Multi select +// + +.select2-container-multi { + &.form-control { + height: auto; + } + + .select2-choices { + min-height: 28px; + line-height: 28px; + padding-left: 10px; + background: none; + + .select2-search-choice { + background: none; + padding: 8px 15px; + margin: 4px 0 4px 5px; + .box-shadow(none); + .transition(.25s linear); + + &:hover { + padding: 8px 7px 8px 23px; + .select2-search-choice-close { + opacity: 1; + } + } + } + } + + .select2-search-choice-close { + .transition(.25s linear); + opacity: 0; + left: 7px; + } +} + +.select2-drop-multi { + .select2-no-results { + padding: 7px; + } +} + +// +// Outside Select2 container +// + +.select2-drop { + .box-shadow(none); + border: 1px solid @color-custom-select-border; + border-top: none; + + &.select2-drop-above { + .box-shadow(none); + border: 1px solid @color-custom-select-border; + border-bottom: none; + } + + &.select2-drop-active { + .border-bottom-radius(@border-radius-base); + } + + &.select2-drop-above.select2-drop-active { + border-top: 1px solid @color-custom-select-border; + .border-bottom-radius(0); + } + + .select2-search { + padding: 0; + min-height: 36px; + input { + min-height: 36px; + border: none; + border-bottom: 1px solid @color-custom-select-border; + background: transparent url('../images/bitmap-icons.png') no-repeat 100% -84px !important; + } + } + + .select2-results { + padding: 0; + margin: 0; + font-size: 13px; + .select2-no-results, + .select2-searching, + .select2-selection-limit { + background: #FFFFFF; + } + + .select2-no-results { + padding: 7px!important; + } + + .select2-highlighted { + background: @color-custom-select-bg-hover; + } + + > li > div { + padding: 5px 7px 5px; + } + } +} + +// +// Controls inside toolbar +// + +[data-control=toolbar] { + .form-control { + display: inline-block; + margin-right: 15px; + + &.width-50 { + width: 50px; + } + + &.width-100 { + width: 100px; + } + + &.width-150 { + width: 150px; + } + } + + input[type=text].form-control, label { + position: relative; + top: 5px; + } + + label { + margin-right: 7px; + + &.standalone { + margin-right: 15px; + } + } + + .select2-container { + display: inline-block; + width: auto; + height: 36px; + + .select2-choice { + height: 34px; + line-height: 34px; + } + } + + select.form-control.custom-select { + display: none; + } +} \ No newline at end of file diff --git a/modules/system/assets/ui/less/form.mixins.less b/modules/system/assets/ui/less/form.mixins.less new file mode 100644 index 000000000..a532abb79 --- /dev/null +++ b/modules/system/assets/ui/less/form.mixins.less @@ -0,0 +1,83 @@ + +// Form validation states +// +// Used in forms.less to generate the form validation CSS for warnings, errors, +// and successes. + +.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) { + // Color the label and help text + .help-block, + .control-label, + .radio, + .checkbox, + .radio-inline, + .checkbox-inline { + color: @text-color; + } + // Set the border and box shadow on specific inputs to match + .form-control { + border-color: @border-color; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work + &:focus { + border-color: darken(@border-color, 10%); + @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%); + .box-shadow(@shadow); + } + } + // Set validation states also for addons + .input-group-addon { + color: @text-color; + border-color: @border-color; + background-color: @background-color; + } + // Optional feedback icon + .form-control-feedback { + color: @text-color; + } +} + +// Form control focus state +// +// Generate a customized focus state and for any input with the specified color, +// which defaults to the `@input-focus-border` variable. +// +// We highly encourage you to not customize the default value, but instead use +// this to tweak colors on an as-needed basis. This aesthetic change is based on +// WebKit's default styles, but applicable to a wider range of browsers. Its +// usability and accessibility should be taken into account with any change. +// +// Example usage: change the default blue border and shadow to white for better +// contrast against a dark gray background. + +.form-control-focus(@color: @input-border-focus) { + @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); + &:focus { + border-color: @color; + outline: 0; + .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); + } +} + +// Form control sizing +// +// Relative text size, padding, and border-radii changes for form controls. For +// horizontal sizing, wrap controls in the predefined grid classes. `