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 insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[{*.yml,package.json}] [{*.yml,package.json,*.js,*.scss}]
indent_size = 2 indent_size = 2
# The indent size used in the package.json file cannot be changed: # 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. * form builder behaviour.
*/ */
(function($) { import $ from 'jquery';
$.entwine('ss', function($) {
var stickyHeaderInterval;
$(".uf-field-editor tbody").entwine({ $.entwine('ss', () => {
onmatch: function() { let stickyHeaderInterval = null;
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;
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._super();
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).toggleClass('inFieldGroup', thisLevel > 0); // Loop through all rows and set necessary styles
for(i = 1; i <= 5; i++) { this.find('.ss-gridfield-item').each(() => {
$(this).toggleClass('inFieldGroup-level-'+i, thisLevel >= i); 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 $(this).toggleClass('infieldgroup', thisLevel > 0);
stickyHeaderInterval = setInterval(function () { for (i = 1; i <= 5; i++) {
var offsetTop = fieldEditor.offset().top; $(this).toggleClass(`infieldgroup-level-${i}`, thisLevel >= i);
$buttonrow.width('100%'); }
if (offsetTop > navHeight || offsetTop === 0) { });
$buttonrow.removeClass('stickyButtons');
} else {
$buttonrow.addClass('stickyButtons');
};
}, 300);
},
onunmatch: function () {
this._super();
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.. clearInterval(stickyHeaderInterval);
$('.uf-field-editor .ss-gridfield-buttonrow .action').entwine({ },
onclick: function (e) { });
this._super(e);
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({ this.trigger('addnewinline');
onmatch: function () { },
var self = this; });
this._super(); $('.uf-field-editor').entwine({
onmatch: () => {
this._super();
// When the 'Add field' button is clicked set a one time listener. // When the 'Add field' button is clicked set a one time listener.
// When the GridField is reloaded focus on the newly added field. // When the GridField is reloaded focus on the newly added field.
this.on('addnewinline', function () { this.on('addnewinline', () => {
self.one('reload', function () { this.one('reload', () => {
//If fieldgroup, focus on the start marker // If fieldgroup, focus on the start marker
var $newField = self.find('.ss-gridfield-item').last(), $groupEnd; let $newField = this.find('.ss-gridfield-item').last();
if ($newField.attr('data-class') === 'EditableFieldGroupEnd') { let $groupEnd = null;
$groupEnd = $newField; if ($newField.attr('data-class') === 'EditableFieldGroupEnd') {
$groupEnd.prev().find('.col-Title input').focus(); $groupEnd = $newField;
$newField = $groupEnd.add($groupEnd.prev()); $groupEnd.prev().find('.col-Title input').focus();
$groupEnd.css('visibility', 'hidden'); $newField = $groupEnd.add($groupEnd.prev());
} else { $groupEnd.css('visibility', 'hidden');
$newField.find('.col-Title input').focus(); } else {
} $newField.find('.col-Title input').focus();
}
$newField.addClass('flashBackground'); $newField.addClass('flashBackground');
$(".cms-content-fields").scrollTop($(".cms-content-fields")[0].scrollHeight); $('.cms-content-fields').scrollTop($('.cms-content-fields')[0].scrollHeight);
if($groupEnd) { if ($groupEnd) {
$groupEnd.css('visibility', 'visible'); $groupEnd.css('visibility', 'visible');
} }
}); });
}); });
}, },
onummatch: function () { onummatch: () => {
this._super(); this._super();
} },
}); });
}); });
}(jQuery));

View File

@ -2,32 +2,32 @@
* Email recipient behaviour. * Email recipient behaviour.
*/ */
(function($) { import $ from 'jquery';
$.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');
$('.field.toggle-html-only')[sendPlainChecked ? 'hide' : 'show'](); $.entwine('ss', () => {
$('.field.toggle-plain-only')[sendPlainChecked ? 'show' : 'hide'](); 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({ $('.field.toggle-html-only')[sendPlainChecked ? 'hide' : 'show']();
onmatch: function () { $('.field.toggle-plain-only')[sendPlainChecked ? 'show' : 'hide']();
recipient.updateFormatSpecificFields(); },
}, };
onunmatch: function () { $('#Form_ItemEditForm .EmailRecipientForm').entwine({
this._super(); onmatch: () => {
} recipient.updateFormatSpecificFields();
}); },
$('#Form_ItemEditForm .EmailRecipientForm input[name="SendPlain"]').entwine({ onunmatch: () => {
onchange: function () { this._super();
recipient.updateFormatSpecificFields(); },
} });
});
}); $('#Form_ItemEditForm .EmailRecipientForm input[name="SendPlain"]').entwine({
}(jQuery)); onchange: () => {
recipient.updateFormatSpecificFields();
},
});
});

View File

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

View File

@ -1,87 +1,85 @@
/** // Lightweight base styles for the front-end form.
* Lightweight base styles for the front-end form.
*/
.userform-progress { .userform-progress {
.progress { .progress {
position: relative; position: relative;
height: 1em; height: 1em;
background: #eee; background: $gray-lighter;
} }
.progress-bar { .progress-bar {
position: absolute; position: absolute;
height: 1em; height: 1em;
background: #666; background: $gray-progress-bg;
} }
.step-buttons { .step-buttons {
margin-left: 0; margin-left: 0;
position: relative; position: relative;
} }
.step-button-wrapper { .step-button-wrapper {
display: inline-block; display: inline-block;
list-style-type: none; list-style-type: none;
&.viewed .step-button-jump { &.viewed .step-button-jump {
opacity: 1; opacity: 1;
} }
} }
.step-button-jump { .step-button-jump {
position: absolute; position: absolute;
top: 0; top: 0;
opacity: .7; opacity: .7;
} }
} }
.step-navigation { .step-navigation {
.step-buttons { .step-buttons {
margin-left: 0; margin-left: 0;
} }
.step-button-wrapper { .step-button-wrapper {
display: inline-block; display: inline-block;
list-style-type: none; list-style-type: none;
} }
} }
.userform { .userform {
clear: both; clear: both;
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
.field label.right { .field label.right {
color: #555; color: $gray-dark-label;
} }
} }
.userformsgroup { .userformsgroup {
border: 1px solid #ccc; border: 1px solid $gray-light-border;
border-radius: 4px; border-radius: $padding-xs;
padding: 8px; padding: $padding-base-horizontal;
margin-top: 12px; margin-top: $padding-base;
margin-bottom: 12px; margin-bottom: $padding-base;
> legend { > legend {
padding-left: 4px; padding-left: $padding-xs;
padding-right: 4px; padding-right: $padding-xs;
border: 0; border: 0;
width: auto; width: auto;
} }
} }
.right-title { .right-title {
clear: both; clear: both;
display: block; display: block;
} }
.checkbox .right-title { .checkbox .right-title {
display: inline; display: inline;
} }
.userform .left { .userform .left {
margin-bottom: 5px; margin-bottom: $padding-sm;
font-weight: bold; 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", "build": "yarn && NODE_ENV=production webpack -p --bail --progress",
"watch": "yarn && NODE_ENV=development webpack --watch --progress", "watch": "yarn && NODE_ENV=development webpack --watch --progress",
"css": "WEBPACK_CHILD=css npm run build", "css": "WEBPACK_CHILD=css npm run build",
"lint": "eslint client/src && sass-lint -v" "lint": "eslint client/src; sass-lint -v"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

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