mirror of
https://github.com/a2nt/silverstripe-webpack.git
synced 2024-10-22 17:05:31 +02:00
A lot of improvements
This commit is contained in:
parent
843e457e78
commit
d638843f92
86
app/client/src/js/_components/_ui.form.datetime.js
Normal file
86
app/client/src/js/_components/_ui.form.datetime.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import $ from 'jquery';
|
||||||
|
|
||||||
|
import Events from '../_events';
|
||||||
|
|
||||||
|
import 'bootstrap-datepicker/dist/js/bootstrap-datepicker.js';
|
||||||
|
import 'bootstrap-timepicker/js/bootstrap-timepicker.js';
|
||||||
|
|
||||||
|
const DatetimeUI = (($) => {
|
||||||
|
// Constants
|
||||||
|
const W = window;
|
||||||
|
const D = document;
|
||||||
|
const $Body = $('body');
|
||||||
|
|
||||||
|
const NAME = 'jsDatetimeUI';
|
||||||
|
const DATA_KEY = NAME;
|
||||||
|
|
||||||
|
const datepickerOptions = {
|
||||||
|
autoclose: true,
|
||||||
|
startDate: 0,
|
||||||
|
//todayBtn: true,
|
||||||
|
todayHighlight: true,
|
||||||
|
clearBtn: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatetimeUI {
|
||||||
|
constructor(element) {
|
||||||
|
const ui = this;
|
||||||
|
const $element = $(element);
|
||||||
|
|
||||||
|
$element.data(DATA_KEY, this);
|
||||||
|
ui._element = element;
|
||||||
|
|
||||||
|
// datepicker
|
||||||
|
if ($element.hasClass('date')) {
|
||||||
|
const defaultDate = ($element.attr('name').toLowerCase().indexOf('end') !== -1) ?
|
||||||
|
'+4d' :
|
||||||
|
'+3d';
|
||||||
|
|
||||||
|
$element.attr('readonly', 'true');
|
||||||
|
$element.datepicker($.extend(datepickerOptions, {
|
||||||
|
defaultViewDate: defaultDate
|
||||||
|
}));
|
||||||
|
} else
|
||||||
|
|
||||||
|
// timepicker
|
||||||
|
if ($element.hasClass('time')) {
|
||||||
|
$element.attr('readonly', 'true');
|
||||||
|
$element.timepicker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static dispose() {
|
||||||
|
console.log(`Destroying: ${NAME}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _jQueryInterface() {
|
||||||
|
return this.each(function() {
|
||||||
|
// attach functionality to element
|
||||||
|
const $elementement = $(this);
|
||||||
|
let data = $elementement.data(DATA_KEY);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
data = new DatetimeUI(this);
|
||||||
|
$elementement.data(DATA_KEY, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jQuery interface
|
||||||
|
$.fn[NAME] = DatetimeUI._jQueryInterface;
|
||||||
|
$.fn[NAME].Constructor = DatetimeUI;
|
||||||
|
$.fn[NAME].noConflict = function() {
|
||||||
|
$.fn[NAME] = JQUERY_NO_CONFLICT;
|
||||||
|
return DatetimeUI._jQueryInterface;
|
||||||
|
};
|
||||||
|
|
||||||
|
// auto-apply
|
||||||
|
$(window).on(`${Events.AJAX} ${Events.LOADED}`, () => {
|
||||||
|
$('input.date, input.time').jsDatetimeUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
return DatetimeUI;
|
||||||
|
})($);
|
||||||
|
|
||||||
|
export default DatetimeUI;
|
86
app/client/src/js/_components/_ui.form.jqte.js
Normal file
86
app/client/src/js/_components/_ui.form.jqte.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import $ from 'jquery';
|
||||||
|
|
||||||
|
import Events from '../_events';
|
||||||
|
import Spinner from '../_components/_ui.spinner';
|
||||||
|
import FormValidate from './_ui.form.validate';
|
||||||
|
|
||||||
|
import '../../thirdparty/jquery-te/jquery-te.js';
|
||||||
|
|
||||||
|
const JqteUI = (($) => {
|
||||||
|
|
||||||
|
const NAME = 'jsJqteUI';
|
||||||
|
const DATA_KEY = NAME;
|
||||||
|
|
||||||
|
const jqteOptions = {
|
||||||
|
color: false,
|
||||||
|
fsize: false,
|
||||||
|
funit: 'em',
|
||||||
|
format: false,
|
||||||
|
rule: false,
|
||||||
|
source: false,
|
||||||
|
sub: false,
|
||||||
|
sup: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
class JqteUI {
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
const ui = this;
|
||||||
|
const $element = $(element);
|
||||||
|
const $jqteFields = $element.find('textarea.jqte-field');
|
||||||
|
const validationUI = $element.parents('form').data('jsFormValidate');
|
||||||
|
|
||||||
|
$element.data(DATA_KEY, this);
|
||||||
|
ui._element = element;
|
||||||
|
$element.jqte(jqteOptions);
|
||||||
|
|
||||||
|
// dynamic error control
|
||||||
|
$element.parents('.jqte').find('.jqte_editor').on('change', (e) => {
|
||||||
|
const $field = $(e.target);
|
||||||
|
const $container = $field.closest('.field');
|
||||||
|
|
||||||
|
if (!$field.text().length && $container.hasClass('required')) {
|
||||||
|
validationUI.setError($container);
|
||||||
|
} else {
|
||||||
|
validationUI.removeError($container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static dispose() {
|
||||||
|
console.log(`Destroying: ${NAME}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _jQueryInterface() {
|
||||||
|
return this.each(function() {
|
||||||
|
// attach functionality to element
|
||||||
|
const $element = $(this);
|
||||||
|
let data = $element.data(DATA_KEY);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
data = new JqteUI(this);
|
||||||
|
$element.data(DATA_KEY, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jQuery interface
|
||||||
|
$.fn[NAME] = JqteUI._jQueryInterface;
|
||||||
|
$.fn[NAME].Constructor = JqteUI;
|
||||||
|
$.fn[NAME].noConflict = function() {
|
||||||
|
$.fn[NAME] = JQUERY_NO_CONFLICT;
|
||||||
|
return JqteUI._jQueryInterface;
|
||||||
|
};
|
||||||
|
|
||||||
|
// auto-apply
|
||||||
|
$(window).on(`${Events.AJAX} ${Events.LOADED}`, () => {
|
||||||
|
$('textarea.jqte-field').jsJqteUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
return JqteUI;
|
||||||
|
})($);
|
||||||
|
|
||||||
|
export default JqteUI;
|
154
app/client/src/js/_components/_ui.form.stepped.js
Normal file
154
app/client/src/js/_components/_ui.form.stepped.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import $ from 'jquery';
|
||||||
|
import Events from '../_events';
|
||||||
|
import LANG from '../lang/_en';
|
||||||
|
import FormValidate from './_ui.form.validate';
|
||||||
|
|
||||||
|
const SteppedForm = (($) => {
|
||||||
|
// Constants
|
||||||
|
const NAME = 'jsSteppedForm';
|
||||||
|
const DATA_KEY = NAME;
|
||||||
|
|
||||||
|
class SteppedForm {
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
const ui = this;
|
||||||
|
const $element = $(element);
|
||||||
|
$element.data(DATA_KEY, this);
|
||||||
|
|
||||||
|
if (!$element.find('.steps-counter').length) {
|
||||||
|
$element.prepend(LANG['en'][NAME]['STEPCOUNTER']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$element.find('.steps-buttons').length) {
|
||||||
|
$element.append(LANG['en'][NAME]['STEPBUTTONS']);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui._currentStepCounter = $element.find('.steps-counter .current-step');
|
||||||
|
ui._totalStepsCounter = $element.find('.steps-counter .total-steps');
|
||||||
|
|
||||||
|
ui._steps = $element.find('.step');
|
||||||
|
ui._stepNext = $element.find('.step-next');
|
||||||
|
ui._stepPrev = $element.find('.step-prev');
|
||||||
|
ui._actions = $element.children('.btn-toolbar,.form-actions');
|
||||||
|
|
||||||
|
ui._element = element;
|
||||||
|
ui._currentStep = 1;
|
||||||
|
ui._totalSteps = ui._steps.length;
|
||||||
|
ui._stepsOrder = [];
|
||||||
|
|
||||||
|
ui._totalStepsCounter.text(ui._totalSteps);
|
||||||
|
|
||||||
|
ui.step('.step[data-step="' + ui._currentStep + '"]');
|
||||||
|
|
||||||
|
ui._stepNext.on('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
ui.next();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui._stepPrev.on('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
ui.prev();
|
||||||
|
});
|
||||||
|
|
||||||
|
$element.find('.step-toggle').on('click', (e) => {
|
||||||
|
const $el = $(e.currentTarget);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
ui.step($el.data('target'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$element.addClass(`${NAME}-active`);
|
||||||
|
$element.trigger(Events.FORM_INIT_STEPPED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public methods
|
||||||
|
dispose() {
|
||||||
|
const ui = this;
|
||||||
|
const $element = $(ui._element);
|
||||||
|
|
||||||
|
$element.removeClass(`${NAME}-active`);
|
||||||
|
$.removeData(ui._element, DATA_KEY);
|
||||||
|
ui._element = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
next() {
|
||||||
|
const ui = this;
|
||||||
|
|
||||||
|
if (ui._currentStep >= ui._totalSteps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui._currentStep++;
|
||||||
|
ui.step('.step[data-step="' + ui._currentStep + '"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
prev() {
|
||||||
|
const ui = this;
|
||||||
|
|
||||||
|
if (ui._currentStep <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui._currentStep--;
|
||||||
|
ui.step(ui._stepsOrder[ui._currentStep]);
|
||||||
|
}
|
||||||
|
|
||||||
|
step(target) {
|
||||||
|
const ui = this;
|
||||||
|
const $element = $(ui._element);
|
||||||
|
const $target = $element.find(target);
|
||||||
|
|
||||||
|
if (parseInt($target.data('step')) <= '1') {
|
||||||
|
ui._stepPrev.hide();
|
||||||
|
} else {
|
||||||
|
ui._stepPrev.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseInt($target.data('step')) >= ui._totalSteps) {
|
||||||
|
ui._stepNext.hide();
|
||||||
|
ui._actions.show();
|
||||||
|
} else {
|
||||||
|
ui._stepNext.show();
|
||||||
|
ui._actions.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui._currentStep = parseInt($target.data('step'));
|
||||||
|
ui._stepsOrder[ui._currentStep] = $target;
|
||||||
|
|
||||||
|
ui._steps.removeClass('active');
|
||||||
|
$target.addClass('active');
|
||||||
|
|
||||||
|
ui._currentStepCounter.text(ui._currentStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _jQueryInterface() {
|
||||||
|
return this.each(function() {
|
||||||
|
// attach functionality to element
|
||||||
|
const $element = $(this);
|
||||||
|
let data = $element.data(DATA_KEY);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
data = new SteppedForm(this);
|
||||||
|
$element.data(DATA_KEY, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jQuery interface
|
||||||
|
$.fn[NAME] = SteppedForm._jQueryInterface;
|
||||||
|
$.fn[NAME].Constructor = SteppedForm;
|
||||||
|
$.fn[NAME].noConflict = function() {
|
||||||
|
$.fn[NAME] = JQUERY_NO_CONFLICT;
|
||||||
|
return SteppedForm._jQueryInterface;
|
||||||
|
};
|
||||||
|
|
||||||
|
// auto-apply
|
||||||
|
$(window).on(`${Events.AJAX} ${Events.LOADED}`, () => {
|
||||||
|
$('.form-stepped').jsSteppedForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
return SteppedForm;
|
||||||
|
})($);
|
||||||
|
|
||||||
|
export default SteppedForm;
|
152
app/client/src/js/_components/_ui.form.validate.js
Normal file
152
app/client/src/js/_components/_ui.form.validate.js
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import $ from 'jquery';
|
||||||
|
import Events from "../_events";
|
||||||
|
|
||||||
|
const FormValidate = (($) => {
|
||||||
|
// Constants
|
||||||
|
const NAME = 'jsFormValidate';
|
||||||
|
const DATA_KEY = NAME;
|
||||||
|
const $Html = $('html, body');
|
||||||
|
|
||||||
|
class FormValidate {
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
const ui = this;
|
||||||
|
const $element = $(element);
|
||||||
|
const $fields = $element.find('input,textarea,select');
|
||||||
|
|
||||||
|
ui._element = element;
|
||||||
|
$element.data(DATA_KEY, this);
|
||||||
|
|
||||||
|
ui._fields = $fields;
|
||||||
|
ui._stepped_form = $element.data('jsSteppedForm');
|
||||||
|
$element.on(Events.FORM_INIT_STEPPED, () => {
|
||||||
|
ui._stepped_form = $element.data('jsSteppedForm');
|
||||||
|
});
|
||||||
|
|
||||||
|
// prevent browsers checks (will do it using JS)
|
||||||
|
$element.attr('novalidate', 'novalidate');
|
||||||
|
$fields.each((i, el) => {
|
||||||
|
el.required = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$fields.on('change', (e) => {
|
||||||
|
ui.validateField($(e.target), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// check form
|
||||||
|
$element.on('submit', (e) => {
|
||||||
|
ui.validateForm($element, true, () => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// switch to step
|
||||||
|
if (ui._stepped_form) {
|
||||||
|
const $el = $element.find('.error').first();
|
||||||
|
|
||||||
|
if ($el.length) {
|
||||||
|
ui._stepped_form.step($el.parents('.step'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$element.trigger(Events.FORM_VALIDATION_FAILED);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$element.addClass(`${NAME}-active`);
|
||||||
|
$element.trigger(Events.FORM_INIT_VALIDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public methods
|
||||||
|
dispose() {
|
||||||
|
const $element = $(this._element);
|
||||||
|
|
||||||
|
$element.removeClass(`${NAME}-active`);
|
||||||
|
$.removeData(this._element, DATA_KEY);
|
||||||
|
this._element = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
validateForm($form, scrollTo = true, badCallback = false) {
|
||||||
|
console.log('Checking the form ...');
|
||||||
|
const ui = this;
|
||||||
|
|
||||||
|
ui._fields.each(function(i, el) {
|
||||||
|
const $el = $(el);
|
||||||
|
|
||||||
|
if (!ui.validateField($el)) {
|
||||||
|
if (badCallback) {
|
||||||
|
badCallback();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
validateField($el, scrollTo = true) {
|
||||||
|
const $field = $el.closest('.field');
|
||||||
|
|
||||||
|
if (!$el[0].checkValidity() ||
|
||||||
|
($el.hasClass('required') && !$el.val().trim().length)
|
||||||
|
) {
|
||||||
|
this.setError($field, scrollTo);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
this.removeError($field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setError($field, scrollTo = true) {
|
||||||
|
const pos = $field.offset().top;
|
||||||
|
|
||||||
|
$field.addClass('error');
|
||||||
|
|
||||||
|
if (scrollTo) {
|
||||||
|
$field.focus();
|
||||||
|
$Html.scrollTop(pos - 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeError($field) {
|
||||||
|
$field.removeClass('error');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _jQueryInterface() {
|
||||||
|
return this.each(function() {
|
||||||
|
// attach functionality to element
|
||||||
|
const $element = $(this);
|
||||||
|
let data = $element.data(DATA_KEY);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
data = new FormValidate(this);
|
||||||
|
$element.data(DATA_KEY, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jQuery interface
|
||||||
|
$.fn[NAME] = FormValidate._jQueryInterface;
|
||||||
|
$.fn[NAME].Constructor = FormValidate;
|
||||||
|
$.fn[NAME].noConflict = function() {
|
||||||
|
$.fn[NAME] = JQUERY_NO_CONFLICT;
|
||||||
|
return FormValidate._jQueryInterface;
|
||||||
|
};
|
||||||
|
|
||||||
|
// auto-apply
|
||||||
|
$(window).on(`${Events.AJAX} ${Events.LOADED}`, () => {
|
||||||
|
$('form').each((i, el) => {
|
||||||
|
const $el = $(el);
|
||||||
|
|
||||||
|
// skip some forms
|
||||||
|
if ($el.hasClass('no-validation')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$el.jsFormValidate();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return FormValidate;
|
||||||
|
})($);
|
||||||
|
|
||||||
|
export default FormValidate;
|
15
app/client/src/js/lang/_en.js
Normal file
15
app/client/src/js/lang/_en.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Add your global events here
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
en: {
|
||||||
|
jsSteppedForm: {
|
||||||
|
STEPCOUNTER: '<div class="steps-counter">Step <b class="current-step"></b> of <b class="total-steps"></b></div>',
|
||||||
|
STEPBUTTONS: '<div class="steps-buttons">' +
|
||||||
|
'<a href="#" class="step-ctrl step-prev"><i class="fas fa-chevron-left"></i> Prev</a>' +
|
||||||
|
' <a href="#" class="step-ctrl step-next">Next <i class="fas fa-chevron-right"></i></a>' +
|
||||||
|
'</div>',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
9
app/client/src/scss/_components/_ui.form.stepped.scss
Normal file
9
app/client/src/scss/_components/_ui.form.stepped.scss
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.form-stepped {
|
||||||
|
.step {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user