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