NEW Add sass-lint configuration and refactor. Refactor JS for AirBnB ES6 syntax

This commit is contained in:
Sacha Judd 2017-08-21 12:13:19 +12:00
parent c8698d99bd
commit 51631a167a
16 changed files with 1273 additions and 1042 deletions

View File

@ -10,7 +10,7 @@ indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[{*.yml,package.json}]
[{*.yml,package.json,*.js,*.scss}]
indent_size = 2
# The indent size used in the package.json file cannot be changed:

179
.sass-lint.yml Normal file
View File

@ -0,0 +1,179 @@
# sass-lint config to match the AirBNB style guide
# See silverstripe-admin
files:
include: '**/client/src/**/*.scss'
ignore:
- 'client/src/styles/legacy/*'
- 'src/**/*'
options:
formatter: stylish
merge-default-rules: false
rules:
# Warnings
# Things that require actual refactoring are marked as warnings
class-name-format:
- 1
- convention: hyphenatedbem
placeholder-name-format:
- 1
- convention: hyphenatedlowercase
nesting-depth:
- 1
- max-depth: 3
no-ids: 1
no-important: 1
no-misspelled-properties:
- 1
- extra-properties:
- "-moz-border-radius-topleft"
- "-moz-border-radius-topright"
- "-moz-border-radius-bottomleft"
- "-moz-border-radius-bottomright"
variable-name-format:
- 1
- allow-leading-underscore: true
convention: hyphenatedlowercase
no-extends: 1
# Warnings: these things are preferential rather than mandatory
no-css-comments: 1
# Errors
# Things that can be easily fixed are marked as errors
indentation:
- 2
- size: 2
final-newline:
- 2
- include: true
no-trailing-whitespace: 2
border-zero:
- 2
- convention: '0'
brace-style:
- 2
- allow-single-line: true
clean-import-paths:
- 2
- filename-extension: false
leading-underscore: false
no-debug: 2
no-empty-rulesets: 2
no-invalid-hex: 2
no-mergeable-selectors: 2
# no-qualifying-elements:
# - 1
# - allow-element-with-attribute: false
# allow-element-with-class: false
# allow-element-with-id: false
no-trailing-zero: 2
no-url-protocols: 2
quotes:
- 2
- style: double
space-after-bang:
- 2
- include: false
space-after-colon:
- 2
- include: true
space-after-comma:
- 2
- include: true
space-before-bang:
- 2
- include: true
space-before-brace:
- 2
- include: true
space-before-colon: 2
space-between-parens:
- 2
- include: false
trailing-semicolon: 2
url-quotes: 2
zero-unit: 2
single-line-per-selector: 2
one-declaration-per-line: 2
empty-line-between-blocks:
- 2
- ignore-single-line-rulesets: true
# Missing rules
# There are no sass-lint rules for the following AirBNB style items, but thess
# - Put comments on their own line
# - Put property delcarations before mixins
# Disabled rules
# These are other rules that we may wish to consider using in the future
# They are not part of the AirBNB CSS standard but they would introduce some strictness
# bem-depth: 0
# variable-for-property: 0
# no-transition-all: 0
# hex-length:
# - 1
# - style: short
# hex-notation:
# - 1
# - style: lowercase
# property-units:
# - 1
# - global:
# - ch
# - em
# - ex
# - rem
# - cm
# - in
# - mm
# - pc
# - pt
# - px
# - q
# - vh
# - vw
# - vmin
# - vmax
# - deg
# - grad
# - rad
# - turn
# - ms
# - s
# - Hz
# - kHz
# - dpi
# - dpcm
# - dppx
# - '%'
# per-property: {}
# force-attribute-nesting: 1
# force-element-nesting: 1
# force-pseudo-nesting: 1
# function-name-format:
# - 1
# - allow-leading-underscore: true
# convention: hyphenatedlowercase
# no-color-literals: 1
# no-duplicate-properties: 1
# mixin-name-format:
# - 1
# - allow-leading-underscore: true
# convention: hyphenatedlowercase
# shorthand-values:
# - 1
# - allowed-shorthands:
# - 1
# - 2
# - 3
# leading-zero:
# - 1
# - include: false
# no-vendor-prefixes:
# - 1
# - additional-identifiers: []
# excluded-identifiers: []
# placeholder-in-extend: 1
# no-color-keywords: 2

View File

@ -1 +1 @@
!function(t){function e(i){if(n[i])return n[i].exports;var o=n[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e){t.exports=jQuery},function(t,e,n){(function(t){!function(t){t.entwine("ss",function(t){var e;t(".uf-field-editor tbody").entwine({onmatch:function(){var n,i,o=0,s=t(".uf-field-editor .ss-gridfield-buttonrow").addClass("stickyButtons"),c=t(".cms-content-header.north").height()+parseInt(t(".stickyButtons").css("padding-top"),10),r=t(".uf-field-editor");this._super(),this.find(".ss-gridfield-item").each(function(){switch(t(this).data("class")){case"EditableFormStep":return void(o=0);case"EditableFieldGroup":i=++o;break;case"EditableFieldGroupEnd":i=o--;break;default:i=o}for(t(this).toggleClass("inFieldGroup",i>0),n=1;n<=5;n++)t(this).toggleClass("inFieldGroup-level-"+n,i>=n)}),e=setInterval(function(){var t=r.offset().top;s.width("100%"),t>c||0===t?s.removeClass("stickyButtons"):s.addClass("stickyButtons")},300)},onunmatch:function(){this._super(),clearInterval(e)}}),t(".uf-field-editor .ss-gridfield-buttonrow .action").entwine({onclick:function(t){this._super(t),this.trigger("addnewinline")}}),t(".uf-field-editor").entwine({onmatch:function(){var e=this;this._super(),this.on("addnewinline",function(){e.one("reload",function(){var n,i=e.find(".ss-gridfield-item").last();"EditableFieldGroupEnd"===i.attr("data-class")?(n=i,n.prev().find(".col-Title input").focus(),i=n.add(n.prev()),n.css("visibility","hidden")):i.find(".col-Title input").focus(),void 0!==document.createElement("div").style.animationName&&i.addClass("newField"),setTimeout(function(){i.removeClass("newField").addClass("flashBackground"),t(".cms-content-fields").scrollTop(t(".cms-content-fields")[0].scrollHeight),n&&n.css("visibility","visible")},500)})})},onummatch:function(){this._super()}})})}(t)}).call(e,n(0))},function(t,e,n){(function(t){!function(t){t(document).ready(function(){var e={updateFormatSpecificFields:function(){var e=t("#SendPlain").find('input[type="checkbox"]').is(":checked");t(".field.toggle-html-only")[e?"hide":"show"](),t(".field.toggle-plain-only")[e?"show":"hide"]()}};t.entwine("udf.recipient",function(t){t("#Form_ItemEditForm").entwine({onmatch:function(){e.updateFormatSpecificFields()},onunmatch:function(){this._super()}}),t("#SendPlain").entwine({onchange:function(){e.updateFormatSpecificFields()}})})})}(t)}).call(e,n(0))},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=(n.n(i),n(2));n.n(o)}]);
!function(e){function t(i){if(n[i])return n[i].exports;var o=n[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=3)}([function(e,t){e.exports=jQuery},function(e,t,n){"use strict";var i=n(0),o=n.n(i),r=this;o.a.entwine("ss",function(){var e=null;o()(".uf-field-editor tbody").entwine({onmatch:function(){var t=0,n=0,i=0,s=o()(".uf-field-editor .ss-gridfield-buttonrow").addClass("sticky-buttons"),c=o()(".cms-content-header.north").height()+parseInt(o()(".sticky-buttons").css("padding-top"),10),u=o()(".uf-field-editor");r._super(),r.find(".ss-gridfield-item").each(function(){switch(o()(r).data("class")){case"EditableFormStep":return void(i=0);case"EditableFieldGroup":n=++i;break;case"EditableFieldGroupEnd":n=i--;break;default:n=i}for(o()(r).toggleClass("infieldgroup",n>0),t=1;t<=5;t++)o()(r).toggleClass("infieldgroup-level-"+t,n>=t)}),e=setInterval(function(){var e=u.offset().top;s.width("100%"),e>c||0===e?s.removeClass("sticky-buttons"):s.addClass("sticky-buttons")},300)},onunmatch:function(){r._super(),clearInterval(e)}}),o()(".uf-field-editor .ss-gridfield-buttonrow .action").entwine({onclick:function(e){r._super(e),r.trigger("addnewinline")}}),o()(".uf-field-editor").entwine({onmatch:function(){r._super(),r.on("addnewinline",function(){r.one("reload",function(){var e=r.find(".ss-gridfield-item").last(),t=null;"EditableFieldGroupEnd"===e.attr("data-class")?(t=e,t.prev().find(".col-Title input").focus(),e=t.add(t.prev()),t.css("visibility","hidden")):e.find(".col-Title input").focus(),e.addClass("flashBackground"),o()(".cms-content-fields").scrollTop(o()(".cms-content-fields")[0].scrollHeight),t&&t.css("visibility","visible")})})},onummatch:function(){r._super()}})})},function(e,t,n){"use strict";var i=n(0),o=n.n(i),r=this;o()(document).ready(function(){var e=o()('input[name="SendPlain"]'),t={updateFormatSpecificFields:function(){var t=e.is(":checked");o()(".field.toggle-html-only")[t?"hide":"show"](),o()(".field.toggle-plain-only")[t?"show":"hide"]()}};o.a.entwine("udf.recipient",function(){o()("#Form_ItemEditForm").entwine({onmatch:function(){t.updateFormatSpecificFields()},onunmatch:function(){r._super()}}),e.entwine({onchange:function(){t.updateFormatSpecificFields()}})})})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(1),n(2)}]);

File diff suppressed because one or more lines are too long

1
client/dist/js/userforms.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
@-webkit-keyframes rowSlide{0%{top:20%}to{top:80%}}@-o-keyframes rowSlide{0%{top:20%}to{top:80%}}@keyframes rowSlide{0%{top:20%}to{top:80%}}@-webkit-keyframes flashBackground{0%{background-color:#fff}10%{background-color:#dcfedd}70%{background-color:#dcfedd}}@-o-keyframes flashBackground{0%{background-color:#fff}10%{background-color:#dcfedd}70%{background-color:#dcfedd}}@keyframes flashBackground{0%{background-color:#fff}10%{background-color:#dcfedd}70%{background-color:#dcfedd}}.cms .uf-field-editor{padding-bottom:0}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item{height:46px}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item,.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item:hover{background:#fff}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item td{border-right-width:0;border-top:1px solid #eee}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item td:last-child{border-right-width:1px}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item .handle{min-height:46px}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.newField{position:fixed;-webkit-animation:rowSlide .5s ease forwards;-o-animation:rowSlide .5s ease forwards;animation:rowSlide .5s ease forwards}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.flashBackground{-webkit-animation:flashBackground 2s linear;-o-animation:flashBackground 2s linear;animation:flashBackground 2s linear}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.ui-sortable-placeholder{height:50px}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup,.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup:hover{background:#f2f9fd}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup td{border-bottom:0;border-top:1px solid #eee}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup .col-reorder,.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup .handle{background:#bee0f8;border-top:0}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup.inFieldGroup-level-2 .col-reorder,.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup.inFieldGroup-level-2 .handle{background:#99cef4;border-top:0}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup.inFieldGroup-level-3 .col-reorder,.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item.inFieldGroup.inFieldGroup-level-3 .handle{background:#89bef4;border-top:0}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep],.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep]:hover{background:#dae2e7}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep] label{font-weight:700;color:#000;font-size:1.1em}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep] td{border-top:1px solid #a6b6c1}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep]+.ss-gridfield-item td{border-top:1px solid #dae2e7}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroup] td,.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep]+.ss-gridfield-item[data-class=EditableFieldGroup] td{border-top:1px solid #a8d7f5}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroup] label{font-weight:700;color:#444}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroupEnd] td{border-bottom:1px solid #a8d7f5}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroupEnd]+.ss-gridfield-item[data-class=EditableFieldGroupEnd]{border-top:0}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroupEnd] .col-buttons .action{display:none}.cms .uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroupEnd] label{color:#777}.cms .uf-field-editor .stickyButtons{position:fixed;top:40px;z-index:2;background:#e6eaed;-webkit-box-shadow:0 12px 4px -8px #999;box-shadow:0 12px 4px -8px #999;padding:12px;margin-left:-12px}.cms .uf-field-editor .stickyButtons button.action{margin-bottom:0}.cms .uf-field-editor .stickyButtons~.ss-gridfield-table{margin-top:40px}
@-webkit-keyframes flash-background{0%{background-color:#fff}10%{background-color:#dcfedd}70%{background-color:#dcfedd}}@-o-keyframes flash-background{0%{background-color:#fff}10%{background-color:#dcfedd}70%{background-color:#dcfedd}}@keyframes flash-background{0%{background-color:#fff}10%{background-color:#dcfedd}70%{background-color:#dcfedd}}.uf-field-editor{padding-bottom:0}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item{height:46px}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item,.uf-field-editor table.ss-gridfield-table .ss-gridfield-item:hover{background:#fff}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item td{border-right-width:0;border-top:1px solid #999}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item td:last-child{border-right-width:1px}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item .handle{min-height:46px}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.flash-background{-webkit-animation:flash-background 2s linear;-o-animation:flash-background 2s linear;animation:flash-background 2s linear}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.ui-sortable-placeholder{height:50px}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup,.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup:hover{background:#f2f9fd}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup td{border-bottom:0;border-top:1px solid #999}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup .col-reorder,.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup .handle{background:#bee0f8;border-top:0}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup.infieldgroup-level-2 .col-reorder,.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup.infieldgroup-level-2 .handle{background:#99cef4;border-top:0}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup.infieldgroup-level-3 .col-reorder,.uf-field-editor table.ss-gridfield-table .ss-gridfield-item.infieldgroup.infieldgroup-level-3 .handle{background:#89bef4;border-top:0}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep],.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep]:hover{background:#dae2e7}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep] label{font-weight:700;color:#000;font-size:1.1em}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep] td{border-top:1px solid #a6b6c1}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep]+.ss-gridfield-item td{border-top:1px solid #dae2e7}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroup] td,.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFormStep]+.ss-gridfield-item[data-class=EditableFieldGroup] td{border-top:1px solid #a8d7f5}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroup] label{font-weight:700;color:#444}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroupEnd] td{border-bottom:1px solid #a8d7f5}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroupEnd]+.ss-gridfield-item[data-class=EditableFieldGroupEnd]{border-top:0}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroupEnd] .col-buttons .action{display:none}.uf-field-editor table.ss-gridfield-table .ss-gridfield-item[data-class=EditableFieldGroupEnd] label{color:#777}.uf-field-editor .sticky-buttons{position:fixed;top:40px;z-index:2;background:#999;-webkit-box-shadow:0 12px -4px #999;box-shadow:0 12px -4px #999;padding:12px;margin-left:-12px}.uf-field-editor .sticky-buttons button.action{margin-bottom:0}.uf-field-editor .sticky-buttons~.ss-gridfield-table{margin-top:40px}

View File

@ -2,107 +2,106 @@
* form builder behaviour.
*/
(function($) {
$.entwine('ss', function($) {
var stickyHeaderInterval;
import $ from 'jquery';
$(".uf-field-editor tbody").entwine({
onmatch: function() {
var i,
thisLevel,
depth = 0,
$buttonrow = $('.uf-field-editor .ss-gridfield-buttonrow').addClass('stickyButtons'),
navHeight = $('.cms-content-header.north').height() + parseInt($('.stickyButtons').css('padding-top'), 10),
fieldEditor = $('.uf-field-editor'),
self = this;
$.entwine('ss', () => {
let stickyHeaderInterval = null;
this._super();
$('.uf-field-editor tbody').entwine({
onmatch: () => {
let i = 0;
let thisLevel = 0;
let depth = 0;
const $buttonrow = $('.uf-field-editor .ss-gridfield-buttonrow').addClass('sticky-buttons');
const navHeight = $('.cms-content-header.north').height()
+ parseInt($('.sticky-buttons').css('padding-top'), 10);
const fieldEditor = $('.uf-field-editor');
// Loop through all rows and set necessary styles
this.find('.ss-gridfield-item').each(function() {
switch($(this).data('class')) {
case 'EditableFormStep': {
depth = 0;
return;
}
case 'EditableFieldGroup': {
thisLevel = ++depth;
break;
}
case 'EditableFieldGroupEnd': {
thisLevel = depth--;
break;
}
default: {
thisLevel = depth;
}
}
this._super();
$(this).toggleClass('inFieldGroup', thisLevel > 0);
for(i = 1; i <= 5; i++) {
$(this).toggleClass('inFieldGroup-level-'+i, thisLevel >= i);
}
});
// Loop through all rows and set necessary styles
this.find('.ss-gridfield-item').each(() => {
switch ($(this).data('class')) {
case 'EditableFormStep': {
depth = 0;
return;
}
case 'EditableFieldGroup': {
thisLevel = ++depth;
break;
}
case 'EditableFieldGroupEnd': {
thisLevel = depth--;
break;
}
default: {
thisLevel = depth;
}
}
// Make sure gridfield buttons stick to top of page when user scrolls down
stickyHeaderInterval = setInterval(function () {
var offsetTop = fieldEditor.offset().top;
$buttonrow.width('100%');
if (offsetTop > navHeight || offsetTop === 0) {
$buttonrow.removeClass('stickyButtons');
} else {
$buttonrow.addClass('stickyButtons');
};
}, 300);
},
onunmatch: function () {
this._super();
$(this).toggleClass('infieldgroup', thisLevel > 0);
for (i = 1; i <= 5; i++) {
$(this).toggleClass(`infieldgroup-level-${i}`, thisLevel >= i);
}
});
clearInterval(stickyHeaderInterval);
}
});
// Make sure gridfield buttons stick to top of page when user scrolls down
stickyHeaderInterval = setInterval(() => {
const offsetTop = fieldEditor.offset().top;
$buttonrow.width('100%');
if (offsetTop > navHeight || offsetTop === 0) {
$buttonrow.removeClass('sticky-buttons');
} else {
$buttonrow.addClass('sticky-buttons');
}
}, 300);
},
onunmatch: () => {
this._super();
// When new fields are added..
$('.uf-field-editor .ss-gridfield-buttonrow .action').entwine({
onclick: function (e) {
this._super(e);
clearInterval(stickyHeaderInterval);
},
});
this.trigger('addnewinline');
}
});
// When new fields are added.
$('.uf-field-editor .ss-gridfield-buttonrow .action').entwine({
onclick: (e) => {
this._super(e);
$('.uf-field-editor').entwine({
onmatch: function () {
var self = this;
this.trigger('addnewinline');
},
});
this._super();
$('.uf-field-editor').entwine({
onmatch: () => {
this._super();
// When the 'Add field' button is clicked set a one time listener.
// When the GridField is reloaded focus on the newly added field.
this.on('addnewinline', function () {
self.one('reload', function () {
//If fieldgroup, focus on the start marker
var $newField = self.find('.ss-gridfield-item').last(), $groupEnd;
if ($newField.attr('data-class') === 'EditableFieldGroupEnd') {
$groupEnd = $newField;
$groupEnd.prev().find('.col-Title input').focus();
$newField = $groupEnd.add($groupEnd.prev());
$groupEnd.css('visibility', 'hidden');
} else {
$newField.find('.col-Title input').focus();
}
// When the 'Add field' button is clicked set a one time listener.
// When the GridField is reloaded focus on the newly added field.
this.on('addnewinline', () => {
this.one('reload', () => {
// If fieldgroup, focus on the start marker
let $newField = this.find('.ss-gridfield-item').last();
let $groupEnd = null;
if ($newField.attr('data-class') === 'EditableFieldGroupEnd') {
$groupEnd = $newField;
$groupEnd.prev().find('.col-Title input').focus();
$newField = $groupEnd.add($groupEnd.prev());
$groupEnd.css('visibility', 'hidden');
} else {
$newField.find('.col-Title input').focus();
}
$newField.addClass('flashBackground');
$(".cms-content-fields").scrollTop($(".cms-content-fields")[0].scrollHeight);
if($groupEnd) {
$groupEnd.css('visibility', 'visible');
}
});
});
},
onummatch: function () {
this._super();
}
});
});
}(jQuery));
$newField.addClass('flashBackground');
$('.cms-content-fields').scrollTop($('.cms-content-fields')[0].scrollHeight);
if ($groupEnd) {
$groupEnd.css('visibility', 'visible');
}
});
});
},
onummatch: () => {
this._super();
},
});
});

View File

@ -2,32 +2,32 @@
* Email recipient behaviour.
*/
(function($) {
$.entwine('ss', function($) {
var recipient = {
// Some fields are only visible when HTML email are being sent.
updateFormatSpecificFields: function () {
var sendPlainChecked = $('input[name="SendPlain"]').is(':checked');
import $ from 'jquery';
$('.field.toggle-html-only')[sendPlainChecked ? 'hide' : 'show']();
$('.field.toggle-plain-only')[sendPlainChecked ? 'show' : 'hide']();
}
};
$.entwine('ss', () => {
const recipient = {
// Some fields are only visible when HTML email are being sent.
updateFormatSpecificFields: () => {
const sendPlainChecked = $('input[name="SendPlain"]').is(':checked');
$('#Form_ItemEditForm .EmailRecipientForm').entwine({
onmatch: function () {
recipient.updateFormatSpecificFields();
},
$('.field.toggle-html-only')[sendPlainChecked ? 'hide' : 'show']();
$('.field.toggle-plain-only')[sendPlainChecked ? 'show' : 'hide']();
},
};
onunmatch: function () {
this._super();
}
});
$('#Form_ItemEditForm .EmailRecipientForm').entwine({
onmatch: () => {
recipient.updateFormatSpecificFields();
},
$('#Form_ItemEditForm .EmailRecipientForm input[name="SendPlain"]').entwine({
onchange: function () {
recipient.updateFormatSpecificFields();
}
});
});
}(jQuery));
onunmatch: () => {
this._super();
},
});
$('#Form_ItemEditForm .EmailRecipientForm input[name="SendPlain"]').entwine({
onchange: () => {
recipient.updateFormatSpecificFields();
},
});
});

View File

@ -1,738 +1,740 @@
/**
* @file Manages the multi-step navigation.
*/
jQuery(function ($) {
// A reference to the UserForm instance.
var userform = null;
// Settings that come from the CMS.
var CONSTANTS = {};
// Common functions that extend multiple classes.
var commonMixin = {
/**
* @func show
* @desc Show the form step. Looks after aria attributes too.
*/
show: function () {
this.$el.attr('aria-hidden', false).show();
},
/**
* @func hide
* @desc Hide the form step. Looks after aria attributes too.
*/
hide: function () {
this.$el.attr('aria-hidden', true).hide();
}
};
/**
* @func UserForm
* @constructor
* @param {object} element
* @return {object} - The UserForm instance.
* @desc The form
*/
function UserForm(element) {
var self = this;
this.$el = element instanceof jQuery ? element : $(element);
this.steps = [];
// Add an error container which displays a list of invalid steps on form submission.
this.errorContainer = new ErrorContainer(this.$el.children('.error-container'));
// Listen for events triggered by form steps.
this.$el.on('userform.action.prev', function (e) {
self.prevStep();
});
this.$el.on('userform.action.next', function (e) {
self.nextStep();
});
// Listen for events triggered by the progress bar.
$('#userform-progress').on('userform.progress.changestep', function (e, stepNumber) {
self.jumpToStep(stepNumber - 1);
});
// When a field becomes valid, remove errors from the error container.
this.$el.on('userform.form.valid', function (e, fieldId) {
self.errorContainer.removeStepLink(fieldId);
});
this.$el.validate(this.validationOptions);
// Ensure checkbox groups are validated correctly
$('.optionset.requiredField input').each(function() {
$(this).rules('add', {
required: true
});
});
return this;
}
/*
* Default options for step validation. These get extended in main().
*/
UserForm.prototype.validationOptions = {
ignore: ':hidden,ul',
errorClass: 'error',
errorElement: 'span',
errorPlacement: function (error, element) {
error.addClass('message');
if (element.is(':radio') || element.parents('.checkboxset').length > 0) {
error.insertAfter(element.closest('ul'));
} else if (element.parents('.checkbox').length > 0) {
error.insertAfter(element.next('label'));
} else {
error.insertAfter(element);
}
},
invalidHandler: function (event, validator) {
//setTimeout 0 so it runs after errorPlacement
setTimeout(function () {
validator.currentElements.filter('.error').first().focus();
}, 0);
},
// Callback for handling the actual submit when the form is valid.
// Submission in the jQuery.validate sence is handled at step level.
// So when the final step is submitted we have to also check all previous steps are valid.
submitHandler: function (form, e) {
var isValid = true;
// validate the current step
if(userform.currentStep) {
userform.currentStep.valid = $(form).valid();
}
// Check for invalid previous steps.
$.each(userform.steps, function (i, step) {
if (!step.valid && !step.conditionallyHidden()) {
isValid = false;
userform.errorContainer.addStepLink(step);
}
});
if (isValid) {
// When using the "are you sure?" plugin, ensure the form immediately submits.
$(form).removeClass('dirty');
form.submit();
} else {
userform.errorContainer.show();
}
},
// When a field becomes valid.
success: function (error) {
var errorId = $(error).attr('id'),
fieldId = errorId.substr(0, errorId.indexOf('-error')).replace(/[\\[\\]]/, '');
// Remove square brackets since jQuery.validate.js uses idOrName,
// which breaks further on when using a selector that end with
// square brackets.
error.remove();
// Pass the field's ID with the event.
userform.$el.trigger('userform.form.valid', [fieldId]);
}
};
/**
* @func UserForm.addStep
* @param {object} step - An instance of FormStep.
* @desc Adds a step to the UserForm.
*/
UserForm.prototype.addStep = function (step) {
// Make sure we're dealing with a form step.
if (!step instanceof FormStep) {
return;
}
step.id = this.steps.length;
this.steps.push(step);
};
/**
* @func UserForm.setCurrentStep
* @param {object} step - An instance of FormStep.
* @desc Sets the step the user is currently on.
*/
UserForm.prototype.setCurrentStep = function (step) {
// Make sure we're dealing with a form step.
if (!(step instanceof FormStep)) {
return;
}
this.currentStep = step;
this.currentStep.show();
// Record the user has viewed the step.
step.viewed = true;
step.$el.addClass('viewed');
};
/**
* @func UserForm.jumpToStep
* @param {number} stepNumber
* @param {boolean} [direction] - Defaults to forward (true).
* @desc Jumps to a specific form step.
*/
UserForm.prototype.jumpToStep = function (stepNumber, direction) {
var targetStep = this.steps[stepNumber],
isValid = false,
forward = direction === void 0 ? true : direction;
// Make sure the target step exists.
if (targetStep === void 0) {
return;
}
// Make sure the step we're trying to set as current is not
// hidden by custom display rules. If it is then jump to the next step.
if (targetStep.conditionallyHidden()) {
if (forward) {
this.jumpToStep(stepNumber + 1);
} else {
this.jumpToStep(stepNumber - 1);
}
return;
}
// Validate the form.
// This well effectivly validate the current step and not the entire form.
// This is because hidden fields are excluded from validation, and all fields
// on all other steps, are currently hidden.
isValid = this.$el.valid();
// Set the 'valid' property on the current step.
this.currentStep.valid = isValid;
// Users can navigate to step's they've already viewed even if the current step is invalid.
if (isValid === false && targetStep.viewed === false) {
return;
}
this.currentStep.hide();
this.setCurrentStep(targetStep);
this.$el.trigger('userform.form.changestep', [targetStep.id]);
};
/**
* @func UserForm.nextStep
* @desc Advances the form to the next step.
*/
UserForm.prototype.nextStep = function () {
this.jumpToStep(this.steps.indexOf(this.currentStep) + 1, true);
};
/**
* @func UserForm.prevStep
* @desc Goes back one step (not bound to browser history).
*/
UserForm.prototype.prevStep = function () {
this.jumpToStep(this.steps.indexOf(this.currentStep) - 1, false);
};
/**
* @func ErrorContainer
* @constructor
* @param {object} element - The error container element.
* @return {object} - The ErrorContainer instance.
* @desc Creates an error container. Used to display step error messages at the top.
*/
function ErrorContainer(element) {
this.$el = element instanceof jQuery ? element : $(element);
// Set the error container's heading.
this.$el.find('h4').text(ss.i18n._t('UserForms.ERROR_CONTAINER_HEADER', 'Please correct the following errors and try again:'));
return this;
}
/**
* @func hasErrors
* @return boolean
* @desc Checks if the error container has any error messages.
*/
ErrorContainer.prototype.hasErrors = function () {
return this.$el.find('.error-list').children().length > 0;
};
/**
* @func removeErrorMessage
* @desc Removes an error message from the error container.
*/
ErrorContainer.prototype.removeErrorMessage = function (fieldId) {
this.$el.find('#' + fieldId + '-top-error').remove();
// If there are no more error then hide the container.
if (!this.hasErrors()) {
this.hide();
}
};
/**
* @func addStepLink
* @param {object} step - FormStep instance.
* @desc Adds a link to a form step as an error message.
*/
ErrorContainer.prototype.addStepLink = function (step) {
var self = this,
itemID = step.$el.attr('id') + '-error-link',
$itemElement = this.$el.find('#' + itemID),
stepID = step.$el.attr('id'),
stepTitle = step.$el.data('title');
// If the item already exists we don't need to do anything.
if ($itemElement.length) {
return;
}
$itemElement = $('<li id="' + itemID + '"><a href="#' + stepID + '">' + stepTitle + '</a></li>');
$itemElement.on('click', function (e) {
e.preventDefault();
userform.jumpToStep(step.id);
});
this.$el.find('.error-list').append($itemElement);
};
/**
* @func removeStepLink
* @param {object} step - FormStep instance.
* @desc Removes a step link from the error container.
*/
ErrorContainer.prototype.removeStepLink = function (fieldId) {
var stepID = $('#' + fieldId).closest('.form-step').attr('id');
this.$el.find('#' + stepID + '-error-link').remove();
// Hide the error container if we've just removed the last error.
if (this.$el.find('.error-list').is(':empty')) {
this.hide();
}
};
/**
* @func ErrorContainer.updateErrorMessage
* @param {object} $input - The jQuery input object which contains the field to validate.
* @param {object} message - The error message to display (html escaped).
* @desc Update an error message (displayed at the top of the form).
*/
ErrorContainer.prototype.updateErrorMessage = function ($input, message) {
var inputID = $input.attr('id'),
anchor = '#' + inputID,
elementID = inputID + '-top-error',
messageElement = $('#' + elementID),
describedBy = $input.attr('aria-describedby');
// The 'message' param will be an empty string if the field is valid.
if (!message) {
// Style issues as fixed if they already exist
messageElement.addClass('fixed');
return;
}
messageElement.removeClass('fixed');
this.show();
if (messageElement.length === 1) {
// Update the existing error message.
messageElement.show().find('a').html(message);
} else {
// Generate better link to field
$input.closest('.field[id]').each(function(){
anchor = '#' + $(this).attr('id');
});
// Add a new error message
messageElement = $('<li><a></a></li>');
messageElement
.attr('id', elementID)
.find('a')
.attr('href', location.pathname + location.search + anchor)
.html(message);
this.$el.find('ul').append(messageElement);
// link back to original input via aria
// Respect existing non-error aria-describedby
if (!describedBy) {
describedBy = elementID;
} else if (!describedBy.match(new RegExp('\\b' + elementID + '\\b'))) {
// Add to end of list if not already present
describedBy += " " + elementID;
}
$input.attr('aria-describedby', describedBy);
}
};
/**
* @func FormStep
* @constructor
* @param {object} element
* @return {object} - The FormStep instance.
* @desc Creates a form step.
*/
function FormStep(element) {
var self = this;
this.$el = element instanceof jQuery ? element : $(element);
// Find button for this step
this.$elButton = $(".step-button-wrapper[data-for='" + this.$el.prop('id') + "']");
// Has the step been viewed by the user?
this.viewed = false;
// Is the form step valid?
// This value is used on form submission, which fails, if any of the steps are invalid.
this.valid = false;
// The internal id of the step. Used for getting the step from the UserForm.steps array.
this.id = null;
this.hide();
if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) {
this.errorContainer = new ErrorContainer(this.$el.find('.error-container'));
// Listen for errors on the UserForm.
userform.$el.on('userform.form.error', function (e, validator) {
// The step only cares about errors if it's currently visible.
if (!self.$el.is(':visible')) {
return;
}
// Add or update each error in the list.
$.each(validator.errorList, function (i, error) {
self.errorContainer.updateErrorMessage($(error.element), error.message);
});
});
// Listen for fields becoming valid
userform.$el.on('userform.form.valid', function (e, fieldId) {
self.errorContainer.removeErrorMessage(fieldId);
});
}
// Ensure that page visibilty updates the step navigation
this
.$elButton
.on('userform.field.hide userform.field.show', function(){
userform.$el.trigger('userform.form.conditionalstep');
});
return this;
}
/**
* Determine if this step is conditionally disabled
*
* @returns {Boolean}
*/
FormStep.prototype.conditionallyHidden = function(){
// Because the element itself could be visible but 0 height, so check visibility of button
return ! this
.$elButton
.find('button')
.is(':visible');
};
/**
* @func ProgressBar
* @constructor
* @param {object} element
* @return {object} - The Progress bar instance.
* @desc Creates a progress bar.
*/
function ProgressBar(element) {
var self = this;
this.$el = element instanceof jQuery ? element : $(element);
this.$buttons = this.$el.find('.step-button-jump');
this.$jsAlign = this.$el.find('.js-align');
// Update the progress bar when 'step' buttons are clicked.
this.$buttons.each(function (i, stepButton) {
$(stepButton).on('click', function (e) {
e.preventDefault();
self.$el.trigger('userform.progress.changestep', [parseInt($(this).data('step'), 10)]);
});
});
// Update the progress bar when 'prev' and 'next' buttons are clicked.
userform.$el.on('userform.form.changestep', function (e, stepID) {
self.update(stepID);
});
// Listen for steps being conditionally shown / hidden by display rules.
// We need to update step related UI like the number of step buttons
// and any text that shows the total number of steps.
userform.$el.on('userform.form.conditionalstep', function () {
// Update the step numbers on the buttons.
var $visibleButtons = self.$buttons.filter(':visible');
$visibleButtons.each(function (i, button) {
$(button).text(i + 1);
});
// Update the actual progress bar.
self.$el.find('.progress-bar').attr('aria-valuemax', $visibleButtons.length);
// Update any text that uses the total number of steps.
self.$el.find('.total-step-number').text($visibleButtons.length);
});
// Spaces out the steps below progress bar evenly
this.$jsAlign.each(function (index, button) {
var $button = $(button),
leftPercent = (100 / (self.$jsAlign.length - 1) * index + '%'),
buttonOffset = -1 * ($button.innerWidth() / 2);
$button.css({left: leftPercent, marginLeft: buttonOffset});
// First and last buttons are kept within userform-progress container
if (index === self.$jsAlign.length - 1) {
$button.css({marginLeft: buttonOffset * 2});
} else if (index === 0) {
$button.css({marginLeft: 0});
}
});
this.update(0);
return this;
}
/**
* @func ProgressBar.update
* @param {number} stepID - Zero based index of the new step.
* @desc Update the progress element to show a new step.
*/
ProgressBar.prototype.update = function (stepID) {
var $newStepElement = $($('.form-step')[stepID]),
stepNumber = 0,
barWidth = stepID / (this.$buttons.length - 1) * 100;
// Set the current step number.
this.$buttons.each(function (i, button) {
if (i > stepID) {
return false; // break the loop
}
if ($(button).is(':visible')) {
stepNumber += 1;
}
});
// Update elements that contain the current step number.
this.$el.find('.current-step-number').each(function (i, element) {
$(element).text(stepNumber);
});
// Update aria attributes.
this.$el.find('[aria-valuenow]').each(function (i, element) {
$(element).attr('aria-valuenow', stepNumber);
});
// Update the CSS classes on step buttons.
this.$buttons.each(function (i, element) {
var $element = $(element),
$item = $element.parent();
if (parseInt($element.data('step'), 10) === stepNumber && $element.is(':visible')) {
$item.addClass('current viewed');
$element.removeAttr('disabled');
return;
}
$item.removeClass('current');
});
// Update the progress bar's title with the new step's title.
this.$el.siblings('.progress-title').text($newStepElement.data('title'));
// Update the width of the progress bar.
barWidth = barWidth ? barWidth + '%' : '';
this.$el.find('.progress-bar').width(barWidth);
};
/**
* @func FormActions
* @constructor
* @param {object} element
* @desc Creates the navigation and actions (Prev, Next, Submit buttons).
*/
function FormActions (element) {
var self = this;
this.$el = element instanceof jQuery ? element : $(element);
this.$prevButton = this.$el.find('.step-button-prev');
this.$nextButton = this.$el.find('.step-button-next');
// Show the buttons.
this.$prevButton.parent().attr('aria-hidden', false).show();
this.$nextButton.parent().attr('aria-hidden', false).show();
// Bind the step navigation event listeners.
this.$prevButton.on('click', function (e) {
e.preventDefault();
self.$el.trigger('userform.action.prev');
});
this.$nextButton.on('click', function (e) {
e.preventDefault();
self.$el.trigger('userform.action.next');
});
// Listen for changes to the current form step, or conditional pages,
// so we can show hide buttons appropriatly.
userform.$el.on('userform.form.changestep userform.form.conditionalstep', function () {
self.update();
});
this.update();
return this;
}
/**
* @func FormAcrions.update
* @param {number} stepID - Zero based ID of the current step.
* @desc Updates the form actions element to reflect the current state of the page.
*/
FormActions.prototype.update = function () {
var numberOfSteps = userform.steps.length,
stepID = userform.currentStep ? userform.currentStep.id : 0,
i, lastStep;
// Update the "Prev" button.
this.$el.find('.step-button-prev')[stepID === 0 ? 'hide' : 'show']();
// Find last step, skipping hidden ones
for(i = numberOfSteps - 1; i >= 0; i--) {
lastStep = userform.steps[i];
// Skip if step is hidden
if(lastStep.conditionallyHidden()) {
continue;
}
// Update the "Next" button.
this.$el.find('.step-button-next')[stepID >= i ? 'hide' : 'show']();
// Update the "Actions".
this.$el.find('.Actions')[stepID >= i ? 'show' : 'hide']();
// Stop processing last step
break;
}
};
/**
* @func main
* @desc Bootstraps the front-end.
*/
function main() {
var progressBar = null,
formActions = null,
$userform = $('.userform');
// If there's no userform, do nothing.
if ($userform.length === 0) {
return;
}
CONSTANTS.ENABLE_LIVE_VALIDATION = $userform.data('livevalidation') !== void 0;
CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP = $userform.data('toperrors') !== void 0;
// Extend the default validation options with conditional options
// that are set by the user in the CMS.
if (CONSTANTS.ENABLE_LIVE_VALIDATION === false) {
$.extend(UserForm.prototype.validationOptions, {
onfocusout: false
});
}
if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) {
$.extend(UserForm.prototype.validationOptions, {
// Callback for custom code when an invalid form / step is submitted.
invalidHandler: function (event, validator) {
$userform.trigger('userform.form.error', [validator]);
},
onfocusout: false
});
}
// Display all the things that are hidden when JavaScript is disabled.
$('.userform-progress, .step-navigation').attr('aria-hidden', false).show();
// Extend classes with common functionality.
$.extend(FormStep.prototype, commonMixin);
$.extend(ErrorContainer.prototype, commonMixin);
userform = new UserForm($userform);
// Conditionally hide field labels and use HTML5 placeholder instead.
if (CONSTANTS.HIDE_FIELD_LABELS) {
$userform.find('label.left').each(function () {
var $label = $(this);
$('[name="' + $label.attr('for') + '"]').attr('placeholder', $label.text());
$label.remove();
});
}
// Initialise the form steps.
userform.$el.find('.form-step').each(function (i, element) {
var step = new FormStep(element);
userform.addStep(step);
});
userform.setCurrentStep(userform.steps[0]);
// Initialise actions and progressbar
progressBar = new ProgressBar($('#userform-progress'));
formActions = new FormActions($('#step-navigation'));
// Enable jQuery UI datepickers
$(document).on('click', 'input.text[data-showcalendar]', function() {
var $element = $(this);
$element.ssDatepicker();
if($element.data('datepicker')) {
$element.datepicker('show');
}
});
// Make sure the form doesn't expire on the user. Pings every 3 mins.
setInterval(function () {
$.ajax({ url: 'UserDefinedForm_Controller/ping' });
}, 180 * 1000);
// Bind a confirmation message when navigating away from a partially completed form.
var form = $('form.userform');
if(typeof form.areYouSure != 'undefined') {
form.areYouSure({
message: ss.i18n._t('UserForms.LEAVE_CONFIRMATION', 'You have unsaved changes!')
});
}
}
main();
import $ from 'jquery';
$(document).ready(() => {
// A reference to the UserForm instance.
let userform = null;
// Settings that come from the CMS.
const CONSTANTS = {};
// Common functions that extend multiple classes.
const commonMixin = {
/**
* @func show
* @desc Show the form step. Looks after aria attributes too.
*/
show: () => {
this.$el.attr('aria-hidden', false).show();
},
/**
* @func hide
* @desc Hide the form step. Looks after aria attributes too.
*/
hide: () => {
this.$el.attr('aria-hidden', true).hide();
},
};
/**
* @func ErrorContainer
* @constructor
* @param {object} element - The error container element.
* @return {object} - The ErrorContainer instance.
* @desc Creates an error container. Used to display step error messages at the top.
*/
function ErrorContainer(element) {
this.$el = element instanceof $ ? element : $(element);
// Set the error container's heading.
this.$el.find('h4').text(window.ss.i18n._t('UserForms.ERROR_CONTAINER_HEADER',
'Please correct the following errors and try again:'));
return this;
}
/**
* @func hasErrors
* @return boolean
* @desc Checks if the error container has any error messages.
*/
ErrorContainer.prototype.hasErrors = () => (
this.$el.find('.error-list').children().length > 0
);
/**
* @func removeErrorMessage
* @desc Removes an error message from the error container.
*/
ErrorContainer.prototype.removeErrorMessage = (fieldId) => {
this.$el.find(`#${fieldId}-top-error`).remove();
// If there are no more error then hide the container.
if (!this.hasErrors()) {
this.hide();
}
};
/**
* @func addStepLink
* @param {object} step - FormStep instance.
* @desc Adds a link to a form step as an error message.
*/
ErrorContainer.prototype.addStepLink = (step) => {
const itemID = `${step.$el.attr('id')}-error-link`;
let $itemElement = this.$el.find(`#${itemID}`);
const stepID = step.$el.attr('id');
const stepTitle = step.$el.data('title');
// If the item already exists we don't need to do anything.
if ($itemElement.length) {
return;
}
$itemElement = $(`<li id="${itemID}"><a href="#${stepID}">${stepTitle}</a></li>`);
$itemElement.on('click', (e) => {
e.preventDefault();
userform.jumpToStep(step.id);
});
this.$el.find('.error-list').append($itemElement);
};
/**
* @func removeStepLink
* @param {object} step - FormStep instance.
* @desc Removes a step link from the error container.
*/
ErrorContainer.prototype.removeStepLink = (fieldId) => {
const stepID = $(`#${fieldId}`).closest('.form-step').attr('id');
this.$el.find(`#${stepID}-error-link`).remove();
// Hide the error container if we've just removed the last error.
if (this.$el.find('.error-list').is(':empty')) {
this.hide();
}
};
/**
* @func ErrorContainer.updateErrorMessage
* @param {object} $input - The jQuery input object which contains the field to validate.
* @param {object} message - The error message to display (html escaped).
* @desc Update an error message (displayed at the top of the form).
*/
ErrorContainer.prototype.updateErrorMessage = ($input, message) => {
const inputID = $input.attr('id');
let anchor = `#${inputID}`;
const elementID = `${inputID}-top-error`;
let messageElement = $(`#${elementID}`);
let describedBy = $input.attr('aria-describedby');
// The 'message' param will be an empty string if the field is valid.
if (!message) {
// Style issues as fixed if they already exist
messageElement.addClass('fixed');
return;
}
messageElement.removeClass('fixed');
this.show();
if (messageElement.length === 1) {
// Update the existing error message.
messageElement.show().find('a').html(message);
} else {
// Generate better link to field
$input.closest('.field[id]').each(() => {
anchor = `#${$(this).attr('id')}`;
});
// Add a new error message
messageElement = $('<li><a></a></li>');
messageElement
.attr('id', elementID)
.find('a')
.attr('href', location.pathname + location.search + anchor)
.html(message);
this.$el.find('ul').append(messageElement);
// Link back to original input via aria
// Respect existing non-error aria-describedby
if (!describedBy) {
describedBy = elementID;
} else if (!describedBy.match(new RegExp(`\\b${elementID}\\b`))) {
// Add to end of list if not already present
describedBy += ` ${elementID}`;
}
$input.attr('aria-describedby', describedBy);
}
};
/**
* @func FormStep
* @constructor
* @param {object} element
* @return {object} - The FormStep instance.
* @desc Creates a form step.
*/
function FormStep(element) {
const self = this;
this.$el = element instanceof $ ? element : $(element);
// Find button for this step
this.$elButton = $(`.step-button-wrapper[data-for='${this.$el.prop('id')}]`);
// Has the step been viewed by the user?
this.viewed = false;
// Is the form step valid?
// This value is used on form submission, which fails, if any of the steps are invalid.
this.valid = false;
// The internal id of the step. Used for getting the step from the UserForm.steps array.
this.id = null;
this.hide();
if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) {
this.errorContainer = new ErrorContainer(this.$el.find('.error-container'));
// Listen for errors on the UserForm.
userform.$el.on('userform.form.error', (e, validator) => {
// The step only cares about errors if it's currently visible.
if (!self.$el.is(':visible')) {
return;
}
// Add or update each error in the list.
$.each(validator.errorList, (i, error) => {
self.errorContainer.updateErrorMessage($(error.element), error.message);
});
});
// Listen for fields becoming valid
userform.$el.on('userform.form.valid', (e, fieldId) => {
self.errorContainer.removeErrorMessage(fieldId);
});
}
// Ensure that page visibilty updates the step navigation
this
.$elButton
.on('userform.field.hide userform.field.show', () => {
userform.$el.trigger('userform.form.conditionalstep');
});
return this;
}
/**
* Determine if this step is conditionally disabled
*
* @returns {Boolean}
*/
// Because the element itself could be visible but 0 height, so check visibility of button
FormStep.prototype.conditionallyHidden = () => (
!this.$elButton.find('button').is(':visible')
);
/**
* @func ProgressBar
* @constructor
* @param {object} element
* @return {object} - The Progress bar instance.
* @desc Creates a progress bar.
*/
function ProgressBar(element) {
const self = this;
this.$el = element instanceof $ ? element : $(element);
this.$buttons = this.$el.find('.step-button-jump');
this.$jsAlign = this.$el.find('.js-align');
// Update the progress bar when 'step' buttons are clicked.
this.$buttons.each((i, stepButton) => {
$(stepButton).on('click', (e) => {
e.preventDefault();
self.$el.trigger('userform.progress.changestep', [parseInt($(this).data('step'), 10)]);
});
});
// Update the progress bar when 'prev' and 'next' buttons are clicked.
userform.$el.on('userform.form.changestep', (e, stepID) => {
self.update(stepID);
});
// Listen for steps being conditionally shown / hidden by display rules.
// We need to update step related UI like the number of step buttons
// and any text that shows the total number of steps.
userform.$el.on('userform.form.conditionalstep', () => {
// Update the step numbers on the buttons.
const $visibleButtons = self.$buttons.filter(':visible');
$visibleButtons.each((i, button) => {
$(button).text(i + 1);
});
// Update the actual progress bar.
self.$el.find('.progress-bar').attr('aria-valuemax', $visibleButtons.length);
// Update any text that uses the total number of steps.
self.$el.find('.total-step-number').text($visibleButtons.length);
});
// Spaces out the steps below progress bar evenly
this.$jsAlign.each((index, button) => {
const $button = $(button);
const leftPercent = (100 / (self.$jsAlign.length - 1) * `${index}%`);
const buttonOffset = -1 * ($button.innerWidth() / 2);
$button.css({ left: leftPercent, marginLeft: buttonOffset });
// First and last buttons are kept within userform-progress container
if (index === self.$jsAlign.length - 1) {
$button.css({ marginLeft: buttonOffset * 2 });
} else if (index === 0) {
$button.css({ marginLeft: 0 });
}
});
this.update(0);
return this;
}
/**
* @func ProgressBar.update
* @param {number} stepID - Zero based index of the new step.
* @desc Update the progress element to show a new step.
*/
ProgressBar.prototype.update = (stepID) => {
const $newStepElement = $($('.form-step')[stepID]);
let stepNumber = 0;
let barWidth = stepID / (this.$buttons.length - 1) * 100;
// Set the current step number.
this.$buttons.each((i, button) => {
if (i > stepID) {
// Break the loop
return false;
}
if ($(button).is(':visible')) {
stepNumber += 1;
}
return true;
});
// Update elements that contain the current step number.
this.$el.find('.current-step-number').each((i, element) => {
$(element).text(stepNumber);
});
// Update aria attributes.
this.$el.find('[aria-valuenow]').each((i, element) => {
$(element).attr('aria-valuenow', stepNumber);
});
// Update the CSS classes on step buttons.
this.$buttons.each((i, element) => {
const $element = $(element);
const $item = $element.parent();
if (parseInt($element.data('step'), 10) === stepNumber && $element.is(':visible')) {
$item.addClass('current viewed');
$element.removeAttr('disabled');
return;
}
$item.removeClass('current');
});
// Update the progress bar's title with the new step's title.
this.$el.siblings('.progress-title').text($newStepElement.data('title'));
// Update the width of the progress bar.
barWidth = barWidth ? `${barWidth}%` : '';
this.$el.find('.progress-bar').width(barWidth);
};
/**
* @func FormActions
* @constructor
* @param {object} element
* @desc Creates the navigation and actions (Prev, Next, Submit buttons).
*/
function FormActions(element) {
const self = this;
this.$el = element instanceof $ ? element : $(element);
this.$prevButton = this.$el.find('.step-button-prev');
this.$nextButton = this.$el.find('.step-button-next');
// Show the buttons.
this.$prevButton.parent().attr('aria-hidden', false).show();
this.$nextButton.parent().attr('aria-hidden', false).show();
// Bind the step navigation event listeners.
this.$prevButton.on('click', (e) => {
e.preventDefault();
self.$el.trigger('userform.action.prev');
});
this.$nextButton.on('click', (e) => {
e.preventDefault();
self.$el.trigger('userform.action.next');
});
// Listen for changes to the current form step, or conditional pages,
// so we can show hide buttons appropriately.
userform.$el.on('userform.form.changestep userform.form.conditionalstep', () => {
self.update();
});
this.update();
return this;
}
/**
* @func FormActions.update
* @param {number} stepID - Zero based ID of the current step.
* @desc Updates the form actions element to reflect the current state of the page.
*/
FormActions.prototype.update = () => {
const numberOfSteps = userform.steps.length;
const stepID = userform.currentStep ? userform.currentStep.id : 0;
let i = null;
let lastStep = null;
// Update the "Prev" button.
this.$el.find('.step-button-prev')[stepID === 0 ? 'hide' : 'show']();
// Find last step, skipping hidden ones
for (i = numberOfSteps - 1; i >= 0; i--) {
lastStep = userform.steps[i];
// Skip if step is hidden
if (lastStep.conditionallyHidden()) {
continue;
}
// Update the "Next" button.
this.$el.find('.step-button-next')[stepID >= i ? 'hide' : 'show']();
// Update the "Actions".
this.$el.find('.Actions')[stepID >= i ? 'show' : 'hide']();
// Stop processing last step
break;
}
};
/**
* @func UserForm
* @constructor
* @param {object} element
* @return {object} - The UserForm instance.
* @desc The form
*/
function UserForm(element) {
const self = this;
this.$el = element instanceof $ ? element : $(element);
this.steps = [];
// Add an error container which displays a list of invalid steps on form submission.
this.errorContainer = new ErrorContainer(this.$el.children('.error-container'));
// Listen for events triggered by form steps.
this.$el.on('userform.action.prev', () => {
self.prevStep();
});
this.$el.on('userform.action.next', () => {
self.nextStep();
});
// Listen for events triggered by the progress bar.
$('#userform-progress').on('userform.progress.changestep', (e, stepNumber) => {
self.jumpToStep(stepNumber - 1);
});
// When a field becomes valid, remove errors from the error container.
this.$el.on('userform.form.valid', (e, fieldId) => {
self.errorContainer.removeStepLink(fieldId);
});
this.$el.validate(this.validationOptions);
// Ensure checkbox groups are validated correctly
$('.optionset.requiredField input').each(() => {
$(this).rules('add', {
required: true,
});
});
return this;
}
/*
* Default options for step validation. These get extended in main().
*/
UserForm.prototype.validationOptions = {
ignore: ':hidden,ul',
errorClass: 'error',
errorElement: 'span',
errorPlacement: (error, element) => {
error.addClass('message');
if (element.is(':radio') || element.parents('.checkboxset').length > 0) {
error.insertAfter(element.closest('ul'));
} else if (element.parents('.checkbox').length > 0) {
error.insertAfter(element.next('label'));
} else {
error.insertAfter(element);
}
},
invalidHandler: (event, validator) => {
// setTimeout 0 so it runs after errorPlacement
setTimeout(() => {
validator.currentElements.filter('.error').first().focus();
}, 0);
},
// Callback for handling the actual submit when the form is valid.
// Submission in the jQuery.validate sence is handled at step level.
// So when the final step is submitted we have to also check all previous steps are valid.
submitHandler: (form) => {
let isValid = true;
// Validate the current step
if (userform.currentStep) {
userform.currentStep.valid = $(form).valid();
}
// Check for invalid previous steps.
$.each(userform.steps, (i, step) => {
if (!step.valid && !step.conditionallyHidden()) {
isValid = false;
userform.errorContainer.addStepLink(step);
}
});
if (isValid) {
// When using the "are you sure?" plugin, ensure the form immediately submits.
$(form).removeClass('dirty');
form.submit();
} else {
userform.errorContainer.show();
}
},
// When a field becomes valid.
success: (error) => {
const errorId = $(error).attr('id');
const fieldId = errorId.substr(0, errorId.indexOf('-error')).replace(/[\\[\\]]/, '');
// Remove square brackets since jQuery.validate.js uses idOrName,
// which breaks further on when using a selector that end with
// square brackets.
error.remove();
// Pass the field's ID with the event.
userform.$el.trigger('userform.form.valid', [fieldId]);
},
};
/**
* @func UserForm.addStep
* @param {object} step - An instance of FormStep.
* @desc Adds a step to the UserForm.
*/
UserForm.prototype.addStep = (step) => {
// Make sure we're dealing with a form step.
if (!step instanceof FormStep) {
return;
}
// eslint-disable-next-line no-param-reassign
step.id = this.steps.length;
this.steps.push(step);
};
/**
* @func UserForm.setCurrentStep
* @param {object} step - An instance of FormStep.
* @desc Sets the step the user is currently on.
*/
UserForm.prototype.setCurrentStep = (step) => {
// Make sure we're dealing with a form step.
if (!(step instanceof FormStep)) {
return;
}
this.currentStep = step;
this.currentStep.show();
// Record the user has viewed the step.
this.currentStep.viewed = true;
this.currentStep.$el.addClass('viewed');
};
/**
* @func UserForm.jumpToStep
* @param {number} stepNumber
* @param {boolean} [direction] - Defaults to forward (true).
* @desc Jumps to a specific form step.
*/
UserForm.prototype.jumpToStep = (stepNumber, direction) => {
const targetStep = this.steps[stepNumber];
let isValid = false;
const forward = direction === void 0 ? true : direction;
// Make sure the target step exists.
if (targetStep === void 0) {
return;
}
// Make sure the step we're trying to set as current is not
// hidden by custom display rules. If it is then jump to the next step.
if (targetStep.conditionallyHidden()) {
if (forward) {
this.jumpToStep(stepNumber + 1);
} else {
this.jumpToStep(stepNumber - 1);
}
return;
}
// Validate the form.
// This well effectivly validate the current step and not the entire form.
// This is because hidden fields are excluded from validation, and all fields
// on all other steps, are currently hidden.
isValid = this.$el.valid();
// Set the 'valid' property on the current step.
this.currentStep.valid = isValid;
// Users can navigate to step's they've already viewed even if the current step is invalid.
if (isValid === false && targetStep.viewed === false) {
return;
}
this.currentStep.hide();
this.setCurrentStep(targetStep);
this.$el.trigger('userform.form.changestep', [targetStep.id]);
};
/**
* @func UserForm.nextStep
* @desc Advances the form to the next step.
*/
UserForm.prototype.nextStep = () => {
this.jumpToStep(this.steps.indexOf(this.currentStep) + 1, true);
};
/**
* @func UserForm.prevStep
* @desc Goes back one step (not bound to browser history).
*/
UserForm.prototype.prevStep = () => {
this.jumpToStep(this.steps.indexOf(this.currentStep) - 1, false);
};
/**
* @func main
* @desc Bootstraps the front-end.
*/
function main() {
const $userform = $('.userform');
// If there's no userform, do nothing.
if ($userform.length === 0) {
return;
}
CONSTANTS.ENABLE_LIVE_VALIDATION = $userform.data('livevalidation') !== void 0;
CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP = $userform.data('toperrors') !== void 0;
// Extend the default validation options with conditional options
// that are set by the user in the CMS.
if (CONSTANTS.ENABLE_LIVE_VALIDATION === false) {
$.extend(UserForm.prototype.validationOptions, {
onfocusout: false,
});
}
if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) {
$.extend(UserForm.prototype.validationOptions, {
// Callback for custom code when an invalid form / step is submitted.
invalidHandler: (event, validator) => {
$userform.trigger('userform.form.error', [validator]);
},
onfocusout: false,
});
}
// Display all the things that are hidden when JavaScript is disabled.
$('.userform-progress, .step-navigation').attr('aria-hidden', false).show();
// Extend classes with common functionality.
$.extend(FormStep.prototype, commonMixin);
$.extend(ErrorContainer.prototype, commonMixin);
userform = new UserForm($userform);
// Conditionally hide field labels and use HTML5 placeholder instead.
if (CONSTANTS.HIDE_FIELD_LABELS) {
$userform.find('label.left').each(() => {
const $label = $(this);
$(`[name="${$label.attr('for')}"]`).attr('placeholder', $label.text());
$label.remove();
});
}
// Initialise the form steps.
userform.$el.find('.form-step').each((i, element) => {
const step = new FormStep(element);
userform.addStep(step);
});
userform.setCurrentStep(userform.steps[0]);
// Initialise actions and progressbar
// @todo Commented out because they appear unused - are they expected to be exported to the
// global scope? Check this works on the frontend
// const progressBar = new ProgressBar($('#userform-progress'));
// const formActions = new FormActions($('#step-navigation'));
// Enable jQuery UI datepickers
$(document).on('click', 'input.text[data-showcalendar]', () => {
const $element = $(this);
$element.ssDatepicker();
if ($element.data('datepicker')) {
$element.datepicker('show');
}
});
// Make sure the form doesn't expire on the user. Pings every 3 mins.
setInterval(() => {
$.ajax({ url: 'UserDefinedForm_Controller/ping' });
}, 180 * 1000);
// Bind a confirmation message when navigating away from a partially completed form.
const form = $('form.userform');
if (typeof form.areYouSure !== 'undefined') {
form.areYouSure({
message: window.ss.i18n._t('UserForms.LEAVE_CONFIRMATION', 'You have unsaved changes!'),
});
}
}
main();
});

View File

@ -0,0 +1,3 @@
// CMS SASS bundle
@import "variables";
@import "userforms-cms";

View File

@ -0,0 +1,3 @@
// Frontend SASS bundle
@import "variables";
@import "userforms";

View File

@ -1,156 +1,151 @@
/**
* Animations
*/
// Animations
@keyframes flashBackground {
0% {background-color: white;}
10% {background-color: #dcfedd;}
70% {background-color: #dcfedd;}
@keyframes flash-background {
0% {background-color: $body-bg;}
10% {background-color: $green-bg;}
70% {background-color: $green-bg;}
}
/**
* Styles for cms
*/
.uf-field-editor {
padding-bottom: 0;
.cms {
.uf-field-editor {
padding-bottom: 0;
// Row styles
table.ss-gridfield-table {
// Standard rows
.ss-gridfield-item {
height: $height-base;
// Row styles
table.ss-gridfield-table {
// Standard rows
.ss-gridfield-item {
height: 46px;
&,
&:hover {
background: $body-bg;
}
&, &:hover {
background: white;
}
td {
border-right-width: 0;
border-top: 1px solid $gray-light;
td {
border-right-width: 0;
border-top: 1px solid #EEE;
&:last-child {
border-right-width: 1px;
}
}
&:last-child {
border-right-width: 1px;
}
}
.handle {
min-height: $height-base;
}
.handle {
min-height: 46px;
}
&.flash-background {
animation: flash-background 2s linear;
}
&.flashBackground {
animation: flashBackground 2s linear;
}
&.ui-sortable-placeholder {
height: $placeholder-height;
}
}
&.ui-sortable-placeholder {
height: 50px;
}
}
.ss-gridfield-item.infieldgroup {
&,
&:hover {
background: $blue-light;
}
.ss-gridfield-item.inFieldGroup {
&, &:hover {
background: #f2f9fd;
}
td {
border-bottom: 0;
border-top: 1px solid $gray-light;
}
td {
border-bottom: 0;
border-top: 1px solid #eee;
}
.col-reorder,
.handle {
background: $blue;
border-top: 0;
}
.col-reorder, .handle {
background: #BEE0F8;
border-top: 0;
}
&.infieldgroup-level-2 {
.col-reorder,
.handle {
background: $blue-dark;
border-top: 0;
}
}
&.inFieldGroup-level-2 {
.col-reorder, .handle {
background: #99CEF4;
border-top: 0;
}
}
&.infieldgroup-level-3 {
.col-reorder,
.handle {
background: $blue-darker;
border-top: 0;
}
}
}
&.inFieldGroup-level-3 {
.col-reorder, .handle {
background: #89BEF4;
border-top: 0;
}
}
}
.ss-gridfield-item[data-class="EditableFormStep"] {
&,
&:hover {
background: $gray-bg;
}
.ss-gridfield-item[data-class='EditableFormStep'] {
&, &:hover {
background: #dae2e7;
}
label {
font-weight: bold;
color: $gray-base;
font-size: 1.1em;
}
label {
font-weight: bold;
color: black;
font-size: 1.1em;
}
td {
border-top: 1px solid $gray;
}
td {
border-top: 1px solid #a6b6c1;
}
+ .ss-gridfield-item td {
border-top: 1px solid $gray-bg;
}
+ .ss-gridfield-item td {
border-top: 1px solid #dae2e7;
}
+ .ss-gridfield-item[data-class="EditableFieldGroup"] td {
border-top: 1px solid $blue-base;
}
}
+ .ss-gridfield-item[data-class='EditableFieldGroup'] td {
border-top: 1px solid #a8d7f5;
}
}
.ss-gridfield-item[data-class="EditableFieldGroup"] {
td {
border-top: 1px solid $blue-base;
}
.ss-gridfield-item[data-class='EditableFieldGroup'] {
td {
border-top: 1px solid #a8d7f5;
}
label {
font-weight: bold;
color: $gray-darker;
}
}
label {
font-weight: bold;
color: #444;
}
}
.ss-gridfield-item[data-class="EditableFieldGroupEnd"] {
td {
border-bottom: 1px solid $blue-base;
}
.ss-gridfield-item[data-class='EditableFieldGroupEnd'] {
td {
border-bottom: 1px solid #a8d7f5;
}
+ .ss-gridfield-item[data-class="EditableFieldGroupEnd"] {
border-top: 0;
}
+ .ss-gridfield-item[data-class='EditableFieldGroupEnd'] {
border-top: 0;
}
.col-buttons .action {
display: none;
}
.col-buttons .action{
display: none;
}
label {
color: $gray-dark;
}
}
}
label {
color: #777;
}
}
}
.sticky-buttons {
position: fixed;
top: $height-top;
z-index: 2;
background: $gray-light;
box-shadow: 0 $padding-base $padding-xs -$padding-base-horizontal $gray-light;
padding: $padding-base;
margin-left: -$padding-base;
.stickyButtons {
position: fixed;
top: 40px;
z-index: 2;
background: #E6EAED;
box-shadow: 0 12px 4px -8px #999;
padding: 12px;
margin-left: -12px;
& button.action {
margin-bottom: 0;
}
& button.action {
margin-bottom: 0;
}
~ .ss-gridfield-table {
margin-top: 40px;
}
}
}
~ .ss-gridfield-table {
margin-top: $height-top;
}
}
}

View File

@ -1,87 +1,85 @@
/**
* Lightweight base styles for the front-end form.
*/
// Lightweight base styles for the front-end form.
.userform-progress {
.progress {
position: relative;
height: 1em;
background: #eee;
}
.progress {
position: relative;
height: 1em;
background: $gray-lighter;
}
.progress-bar {
position: absolute;
height: 1em;
background: #666;
}
.progress-bar {
position: absolute;
height: 1em;
background: $gray-progress-bg;
}
.step-buttons {
margin-left: 0;
position: relative;
}
.step-buttons {
margin-left: 0;
position: relative;
}
.step-button-wrapper {
display: inline-block;
list-style-type: none;
.step-button-wrapper {
display: inline-block;
list-style-type: none;
&.viewed .step-button-jump {
opacity: 1;
}
}
&.viewed .step-button-jump {
opacity: 1;
}
}
.step-button-jump {
position: absolute;
top: 0;
opacity: .7;
}
.step-button-jump {
position: absolute;
top: 0;
opacity: .7;
}
}
.step-navigation {
.step-buttons {
margin-left: 0;
}
.step-buttons {
margin-left: 0;
}
.step-button-wrapper {
display: inline-block;
list-style-type: none;
}
.step-button-wrapper {
display: inline-block;
list-style-type: none;
}
}
.userform {
clear: both;
width: 100%;
max-width: 100%;
clear: both;
width: 100%;
max-width: 100%;
.field label.right {
color: #555;
}
.field label.right {
color: $gray-dark-label;
}
}
.userformsgroup {
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px;
margin-top: 12px;
margin-bottom: 12px;
border: 1px solid $gray-light-border;
border-radius: $padding-xs;
padding: $padding-base-horizontal;
margin-top: $padding-base;
margin-bottom: $padding-base;
> legend {
padding-left: 4px;
padding-right: 4px;
border: 0;
width: auto;
}
> legend {
padding-left: $padding-xs;
padding-right: $padding-xs;
border: 0;
width: auto;
}
}
.right-title {
clear: both;
display: block;
clear: both;
display: block;
}
.checkbox .right-title {
display: inline;
display: inline;
}
.userform .left {
margin-bottom: 5px;
font-weight: bold;
margin-bottom: $padding-sm;
font-weight: bold;
}

View File

@ -0,0 +1,51 @@
//
// Variables
// --------------------------------------------------
//== Colors
$gray-base: #000;
$gray-dark: #777;
$gray-darker: #444;
$gray: #a6b6c1;
$gray-light: #999;
$gray-lighter: #eee;
//== Custom colors
$gray-bg: #dae2e7;
$gray-progress-bg: #666;
$gray-dark-label: #555;
$gray-light-border: #ccc;
$blue-base: #a8d7f5;
$blue-dark: #99cef4;
$blue-darker: #89bef4;
$blue: #bee0f8;
$blue-light: #f2f9fd;
$green-bg: #dcfedd;
//== Scaffolding
//
//## Settings for some of the most global styles.
$body-bg: #fff;
//== Components
//
//## Define common padding and border radius sizes and more.
$padding-base-horizontal: 8px;
//== Custom Components
$padding-base: 12px;
$padding-xs: 4px;
$padding-sm: 5px;
$height-base: 46px;
$height-top: 40px;
$placeholder-height: 50px;

View File

@ -6,7 +6,7 @@
"build": "yarn && NODE_ENV=production webpack -p --bail --progress",
"watch": "yarn && NODE_ENV=development webpack --watch --progress",
"css": "WEBPACK_CHILD=css npm run build",
"lint": "eslint client/src && sass-lint -v"
"lint": "eslint client/src; sass-lint -v"
},
"repository": {
"type": "git",

View File

@ -54,8 +54,8 @@ const config = [
{
name: 'css',
entry: {
userforms: `${PATHS.SRC}/styles/userforms.scss`,
'userforms-cms': `${PATHS.SRC}/styles/userforms-cms.scss`,
userforms: `${PATHS.SRC}/styles/bundle.scss`,
'userforms-cms': `${PATHS.SRC}/styles/bundle-cms.scss`,
},
output: {
path: PATHS.DIST,