mirror of
https://github.com/a2nt/silverstripe-webpack.git
synced 2024-10-22 17:05:31 +02:00
Bunch of updates
This commit is contained in:
parent
d638843f92
commit
ff87ba7f16
@ -4,7 +4,8 @@ use SilverStripe\Forms\HTMLEditor\HtmlEditorConfig;
|
||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
||||
use SilverStripe\ORM\Search\FulltextSearchable;
|
||||
|
||||
HtmlEditorConfig::get('cms')->enablePlugins([
|
||||
$config = HtmlEditorConfig::get('cms');
|
||||
$config->enablePlugins([
|
||||
'template',
|
||||
'fullscreen',
|
||||
'hr',
|
||||
@ -12,11 +13,12 @@ HtmlEditorConfig::get('cms')->enablePlugins([
|
||||
'charmap',
|
||||
'visualblocks',
|
||||
'lists',
|
||||
'anchor',
|
||||
'charcount' => ModuleResourceLoader::resourceURL(
|
||||
'drmartingonzo/ss-tinymce-charcount:client/dist/js/bundle.js'
|
||||
),
|
||||
]);
|
||||
$config->addButtonsToLine(2, 'hr');
|
||||
$config->setOption('block_formats', 'Paragraph=p;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;Address=address;Pre=pre');
|
||||
$config->setOption('invalid_elements', 'h1');
|
||||
|
||||
HtmlEditorConfig::get('cms')->insertButtonsAfter('sslink', 'anchor');
|
||||
FulltextSearchable::enable();
|
||||
|
@ -157,13 +157,13 @@ const AjaxUI = (($) => {
|
||||
});
|
||||
}
|
||||
|
||||
static preload(items, type = 'text', cache = true) {
|
||||
static preload(items, type = 'text', cache = true, itemCallback = false) {
|
||||
if (!items.length) {
|
||||
return $.Deferred().resolve().promise();
|
||||
}
|
||||
|
||||
const dfds = [];
|
||||
items.forEach((url) => {
|
||||
items.forEach((url, i) => {
|
||||
const dfd = $.Deferred();
|
||||
|
||||
$.ajax({
|
||||
@ -172,6 +172,9 @@ const AjaxUI = (($) => {
|
||||
url,
|
||||
}).always(() => {
|
||||
dfd.resolve();
|
||||
if (itemCallback) {
|
||||
itemCallback(i, url);
|
||||
}
|
||||
});
|
||||
|
||||
dfds.push(dfd);
|
||||
|
111
app/client/src/js/_components/_ui.form.basics.js
Normal file
111
app/client/src/js/_components/_ui.form.basics.js
Normal file
@ -0,0 +1,111 @@
|
||||
import 'bootstrap-select/js/bootstrap-select';
|
||||
|
||||
import $ from 'jquery';
|
||||
import Events from "../_events";
|
||||
|
||||
const FormBasics = (($) => {
|
||||
// Constants
|
||||
const NAME = 'jsFormBasics';
|
||||
const DATA_KEY = NAME;
|
||||
const $Html = $('html, body');
|
||||
|
||||
class FormBasics {
|
||||
|
||||
constructor(element) {
|
||||
const ui = this;
|
||||
const $element = $(element);
|
||||
|
||||
ui._element = element;
|
||||
$element.data(DATA_KEY, this);
|
||||
|
||||
const $fields = $element.find('input,textarea,select');
|
||||
const $selectFields = $element.find('select:not([readonly])');
|
||||
const $radioOptions = $element.find('input[type="radio"]');
|
||||
|
||||
$selectFields.each((i, el) => {
|
||||
const $el = $(el);
|
||||
|
||||
$el.selectpicker({
|
||||
liveSearch: $el.data('live-search')
|
||||
});
|
||||
});
|
||||
|
||||
$fields.each((e, el) => {
|
||||
const $el = $(el);
|
||||
|
||||
if ($el.hasClass('required') || $el.attr('aria-required')) {
|
||||
$el.closest('.field').addClass('required');
|
||||
}
|
||||
});
|
||||
|
||||
$radioOptions.each((e, el) => {
|
||||
const $el = $(el);
|
||||
|
||||
if ($el.is(':checked')) {
|
||||
$el.parents('.radio').addClass('checked');
|
||||
}
|
||||
});
|
||||
|
||||
$radioOptions.on('change', (e) => {
|
||||
const $el = $(e.currentTarget);
|
||||
const $parent = $el.parents('.radio');
|
||||
|
||||
$parent.siblings('.radio').removeClass('checked');
|
||||
if ($el.is(':checked')) {
|
||||
$parent.addClass('checked');
|
||||
}
|
||||
});
|
||||
|
||||
$element.addClass(`${NAME}-active`);
|
||||
$element.trigger(Events.FORM_INIT_BASICS);
|
||||
}
|
||||
|
||||
// Public methods
|
||||
dispose() {
|
||||
const $element = $(this._element);
|
||||
|
||||
$element.removeClass(`${NAME}-active`);
|
||||
$.removeData(this._element, DATA_KEY);
|
||||
this._element = null;
|
||||
}
|
||||
|
||||
static _jQueryInterface() {
|
||||
return this.each(function() {
|
||||
// attach functionality to element
|
||||
const $element = $(this);
|
||||
let data = $element.data(DATA_KEY);
|
||||
|
||||
if (!data) {
|
||||
data = new FormBasics(this);
|
||||
$element.data(DATA_KEY, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// jQuery interface
|
||||
$.fn[NAME] = FormBasics._jQueryInterface;
|
||||
$.fn[NAME].Constructor = FormBasics;
|
||||
$.fn[NAME].noConflict = function() {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT;
|
||||
return FormBasics._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.jsFormBasics();
|
||||
});
|
||||
});
|
||||
|
||||
return FormBasics;
|
||||
})($);
|
||||
|
||||
export default FormBasics;
|
@ -4,7 +4,7 @@ import $ from 'jquery';
|
||||
|
||||
import Events from '../_events';
|
||||
import Spinner from '../_components/_ui.spinner';
|
||||
import FormValidate from './_ui.form.validate';
|
||||
import FormValidateField from "./_ui.form.validate.field";
|
||||
|
||||
import '../../thirdparty/jquery-te/jquery-te.js';
|
||||
|
||||
@ -29,25 +29,19 @@ const JqteUI = (($) => {
|
||||
constructor(element) {
|
||||
const ui = this;
|
||||
const $element = $(element);
|
||||
const $jqteFields = $element.find('textarea.jqte-field');
|
||||
const validationUI = $element.parents('form').data('jsFormValidate');
|
||||
const validationUI = $element.data('jsFormValidateField');
|
||||
|
||||
$element.data(DATA_KEY, this);
|
||||
ui._element = element;
|
||||
$element.data(DATA_KEY, this);
|
||||
$element.jqte(jqteOptions);
|
||||
|
||||
// dynamic error control
|
||||
if (validationUI) {
|
||||
$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);
|
||||
}
|
||||
validationUI.validate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static dispose() {
|
||||
console.log(`Destroying: ${NAME}`);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import $ from 'jquery';
|
||||
import Events from '../_events';
|
||||
import LANG from '../lang/_en';
|
||||
import FormValidate from './_ui.form.validate';
|
||||
import FormValidateField from "./_ui.form.validate.field";
|
||||
|
||||
const SteppedForm = (($) => {
|
||||
// Constants
|
||||
@ -13,6 +13,7 @@ const SteppedForm = (($) => {
|
||||
constructor(element) {
|
||||
const ui = this;
|
||||
const $element = $(element);
|
||||
|
||||
$element.data(DATA_KEY, this);
|
||||
|
||||
if (!$element.find('.steps-counter').length) {
|
||||
@ -28,16 +29,33 @@ const SteppedForm = (($) => {
|
||||
|
||||
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._totalSteps = ui._steps.last().data('step') || ui._steps.length;
|
||||
ui._stepsOrder = [];
|
||||
|
||||
ui._totalStepsCounter.text(ui._totalSteps);
|
||||
|
||||
// check if one of the steps already has an error
|
||||
const $hasError = ui._steps
|
||||
.find('.field.error,.field.holder-error,.field.holder-validation,.field.holder-info,.field.holder-warning,.field.holder-good')
|
||||
.first();
|
||||
if ($hasError.length) {
|
||||
const $modal = $element.parents('.modal');
|
||||
|
||||
// show modal
|
||||
if ($modal.length && typeof $modal.modal !== 'undefined') {
|
||||
$modal.modal('show');
|
||||
}
|
||||
|
||||
ui._currentStep = $hasError.parents('.step').data('step') || ui._currentStep;
|
||||
}
|
||||
//
|
||||
|
||||
ui.step('.step[data-step="' + ui._currentStep + '"]');
|
||||
|
||||
ui._stepNext.on('click', (e) => {
|
||||
@ -78,8 +96,7 @@ const SteppedForm = (($) => {
|
||||
return;
|
||||
}
|
||||
|
||||
ui._currentStep++;
|
||||
ui.step('.step[data-step="' + ui._currentStep + '"]');
|
||||
ui.step('.step[data-step="' + (ui._currentStep + 1) + '"]');
|
||||
}
|
||||
|
||||
prev() {
|
||||
@ -89,17 +106,37 @@ const SteppedForm = (($) => {
|
||||
return;
|
||||
}
|
||||
|
||||
ui._currentStep--;
|
||||
ui.step(ui._stepsOrder[ui._currentStep]);
|
||||
ui.step(ui._stepsOrder[ui._currentStep - 1]);
|
||||
}
|
||||
|
||||
step(target) {
|
||||
const ui = this;
|
||||
const $element = $(ui._element);
|
||||
const $target = $element.find(target);
|
||||
const targetStep = parseInt($target.data('step'));
|
||||
|
||||
// validate current step
|
||||
let valid = true;
|
||||
|
||||
if (targetStep > ui._currentStep) {
|
||||
ui.currentStep().find('input,textarea,select').each((i, el) => {
|
||||
const $el = $(el);
|
||||
const fieldUI = $el.data('jsFormValidateField');
|
||||
|
||||
if (fieldUI && !fieldUI.validate()) {
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
//
|
||||
|
||||
if (parseInt($target.data('step')) <= '1') {
|
||||
ui._stepPrev.hide();
|
||||
$element.trigger(Events.FORM_STEPPED_FIRST_STEP);
|
||||
} else {
|
||||
ui._stepPrev.show();
|
||||
}
|
||||
@ -107,18 +144,30 @@ const SteppedForm = (($) => {
|
||||
if (parseInt($target.data('step')) >= ui._totalSteps) {
|
||||
ui._stepNext.hide();
|
||||
ui._actions.show();
|
||||
|
||||
$element.trigger(Events.FORM_STEPPED_LAST_STEP);
|
||||
} else {
|
||||
ui._stepNext.show();
|
||||
ui._actions.hide();
|
||||
}
|
||||
|
||||
ui._currentStep = parseInt($target.data('step'));
|
||||
ui._currentStep = targetStep;
|
||||
ui._stepsOrder[ui._currentStep] = $target;
|
||||
|
||||
ui._steps.removeClass('active');
|
||||
$target.addClass('active');
|
||||
|
||||
ui._currentStepCounter.text(ui._currentStep);
|
||||
|
||||
$target.trigger(Events.FORM_STEPPED_NEW_STEP);
|
||||
$element.trigger(Events.FORM_STEPPED_NEW_STEP);
|
||||
}
|
||||
|
||||
currentStep() {
|
||||
const ui = this;
|
||||
const $element = $(ui._element);
|
||||
|
||||
return $element.find('.step.active');
|
||||
}
|
||||
|
||||
static _jQueryInterface() {
|
||||
|
@ -31,6 +31,10 @@ const FormStorage = (($) => {
|
||||
const type = $el.attr('type');
|
||||
const val = STORAGE.getItem(NAME + id);
|
||||
|
||||
if (type === 'file') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (id && val && type) {
|
||||
if (type && (type === 'checkbox' || type === 'radio')) {
|
||||
$el.prop('checked', val);
|
||||
|
120
app/client/src/js/_components/_ui.form.validate.field.js
Normal file
120
app/client/src/js/_components/_ui.form.validate.field.js
Normal file
@ -0,0 +1,120 @@
|
||||
import $ from 'jquery';
|
||||
import Events from "../_events";
|
||||
|
||||
const FormValidateField = (($) => {
|
||||
// Constants
|
||||
const NAME = 'jsFormValidateField';
|
||||
const DATA_KEY = NAME;
|
||||
const $Html = $('html, body');
|
||||
|
||||
class FormValidateField {
|
||||
|
||||
constructor(element) {
|
||||
const ui = this;
|
||||
const $element = $(element);
|
||||
|
||||
ui._element = element;
|
||||
ui._actions = $element.parents('form').children('.btn-toolbar,.form-actions');
|
||||
$element.data(DATA_KEY, this);
|
||||
|
||||
// prevent browsers checks (will do it using JS)
|
||||
$element.attr('novalidate', 'novalidate');
|
||||
|
||||
$element.on('change', (e) => {
|
||||
ui.validate(false);
|
||||
});
|
||||
|
||||
$element.addClass(`${NAME}-active`);
|
||||
$element.trigger(Events.FORM_INIT_VALIDATE_FIELD);
|
||||
}
|
||||
|
||||
// Public methods
|
||||
dispose() {
|
||||
const $element = $(this._element);
|
||||
|
||||
$element.removeClass(`${NAME}-active`);
|
||||
$.removeData(this._element, DATA_KEY);
|
||||
this._element = null;
|
||||
}
|
||||
|
||||
validate(scrollTo = true) {
|
||||
const ui = this;
|
||||
const $el = $(ui._element);
|
||||
const $field = $el.closest('.field');
|
||||
const extraChecks = $el.data(`${NAME}-extra`);
|
||||
let valid = true;
|
||||
|
||||
// browser checks + required
|
||||
if (!ui._element.checkValidity() ||
|
||||
($el.hasClass('required') && !$el.val().trim().length)
|
||||
) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
// extra checks
|
||||
if (extraChecks) {
|
||||
extraChecks.forEach((check) => {
|
||||
valid = valid && check();
|
||||
});
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
this.removeError();
|
||||
return true;
|
||||
}
|
||||
|
||||
this.setError(scrollTo);
|
||||
return false;
|
||||
}
|
||||
|
||||
setError(scrollTo = true) {
|
||||
const ui = this;
|
||||
const $field = $(ui._element).closest('.field');
|
||||
const pos = $field.offset().top;
|
||||
|
||||
$field.addClass('error');
|
||||
|
||||
if (scrollTo) {
|
||||
$field.focus();
|
||||
$Html.scrollTop(pos - 100);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
removeError() {
|
||||
const ui = this;
|
||||
const $field = $(ui._element).closest('.field');
|
||||
|
||||
$field.removeClass('error');
|
||||
|
||||
$field.removeClass('holder-error');
|
||||
$field.removeClass('holder-validation');
|
||||
$field.find('.message').remove();
|
||||
}
|
||||
|
||||
static _jQueryInterface() {
|
||||
return this.each(function() {
|
||||
// attach functionality to element
|
||||
const $element = $(this);
|
||||
let data = $element.data(DATA_KEY);
|
||||
|
||||
if (!data) {
|
||||
data = new FormValidateField(this);
|
||||
$element.data(DATA_KEY, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// jQuery interface
|
||||
$.fn[NAME] = FormValidateField._jQueryInterface;
|
||||
$.fn[NAME].Constructor = FormValidateField;
|
||||
$.fn[NAME].noConflict = function() {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT;
|
||||
return FormValidateField._jQueryInterface;
|
||||
};
|
||||
|
||||
return FormValidateField;
|
||||
})($);
|
||||
|
||||
export default FormValidateField;
|
@ -1,5 +1,6 @@
|
||||
import $ from 'jquery';
|
||||
import Events from "../_events";
|
||||
import FormValidateField from "./_ui.form.validate.field";
|
||||
|
||||
const FormValidate = (($) => {
|
||||
// Constants
|
||||
@ -19,23 +20,22 @@ const FormValidate = (($) => {
|
||||
|
||||
ui._fields = $fields;
|
||||
ui._stepped_form = $element.data('jsSteppedForm');
|
||||
|
||||
// prevent browsers checks (will do it using JS)
|
||||
$element.attr('novalidate', 'novalidate');
|
||||
|
||||
$element.on(Events.FORM_INIT_STEPPED, () => {
|
||||
ui._stepped_form = $element.data('jsSteppedForm');
|
||||
});
|
||||
|
||||
// prevent browsers checks (will do it using JS)
|
||||
$element.attr('novalidate', 'novalidate');
|
||||
// init fields validation
|
||||
$fields.each((i, el) => {
|
||||
el.required = false;
|
||||
});
|
||||
|
||||
$fields.on('change', (e) => {
|
||||
ui.validateField($(e.target), false);
|
||||
new FormValidateField(el);
|
||||
});
|
||||
|
||||
// check form
|
||||
$element.on('submit', (e) => {
|
||||
ui.validateForm($element, true, () => {
|
||||
ui.validate(true, () => {
|
||||
e.preventDefault();
|
||||
|
||||
// switch to step
|
||||
@ -64,52 +64,24 @@ const FormValidate = (($) => {
|
||||
this._element = null;
|
||||
}
|
||||
|
||||
validateForm($form, scrollTo = true, badCallback = false) {
|
||||
validate(scrollTo = true, badCallback = false) {
|
||||
console.log('Checking the form ...');
|
||||
const ui = this;
|
||||
|
||||
ui._fields.each(function(i, el) {
|
||||
const $el = $(el);
|
||||
const fieldUI = $el.data('jsFormValidateField');
|
||||
|
||||
if (!ui.validateField($el)) {
|
||||
if (fieldUI && !fieldUI.validate()) {
|
||||
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
|
||||
|
@ -5,9 +5,15 @@
|
||||
module.exports = {
|
||||
AJAX: 'ajax-load',
|
||||
LOADED: 'load',
|
||||
SET_TARGET_UPDATE: 'set-target-update',
|
||||
RESTORE_FIELD: 'restore-field',
|
||||
FORM_INIT_BASICS: 'form-basics',
|
||||
FORM_INIT_STEPPED: 'form-init-stepped',
|
||||
FORM_INIT_VALIDATE: 'form-init-validate',
|
||||
FORM_INIT_VALIDATE_FIELD: 'form-init-validate-field',
|
||||
FORM_INIT_STORAGE: 'form-init-storage',
|
||||
FORM_VALIDATION_FAILED: 'form-validation-failed',
|
||||
FORM_STEPPED_NEW_STEP: 'form-new-step',
|
||||
FORM_STEPPED_FIRST_STEP: 'form-first-step',
|
||||
FORM_STEPPED_LAST_STEP: 'form-last-step',
|
||||
};
|
||||
|
@ -12,10 +12,16 @@ import './_components/routes/index';
|
||||
import Events from './_events';
|
||||
import Spinner from './_components/_ui.spinner';
|
||||
|
||||
import './_components/_ui.shrink';
|
||||
import './_components/_ui.carousel';
|
||||
import './_components/_ui.menu';
|
||||
import './_components/_ui.form.storage';
|
||||
|
||||
import FormBasics from './_components/_ui.form.basics';
|
||||
import FormDatetime from './_components/_ui.form.datetime';
|
||||
import FormStepped from './_components/_ui.form.stepped';
|
||||
import FormValidate from './_components/_ui.form.validate';
|
||||
import FormStorage from './_components/_ui.form.storage';
|
||||
import FormCroppie from './_components/_ui.form.croppie';
|
||||
|
||||
import AjaxUI from './_components/_ui.ajax';
|
||||
|
||||
import SmoothScroll from 'smooth-scroll';
|
||||
@ -144,27 +150,6 @@ const MainUI = (($) => {
|
||||
// mark external links
|
||||
$('a.external,a[rel="external"]').attr('target', '_blank');
|
||||
|
||||
// data-set links
|
||||
$('[data-set-target]').on('click', (e) => {
|
||||
const $el = $(e.currentTarget);
|
||||
const $target = $($el.data('set-target'));
|
||||
|
||||
if (!$target.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$target.each((i, targetEl) => {
|
||||
const $targetEl = $(targetEl);
|
||||
const tag = $targetEl.prop('tagName').toLowerCase();
|
||||
|
||||
if (tag === 'input' || tag === 'select') {
|
||||
$targetEl.val($el.data('set-val'));
|
||||
} else if (!$targetEl.hasClass('field')) {
|
||||
$targetEl.text($el.data('set-val'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// show encoded emails
|
||||
/*$(D).find('.obm').each(function () {
|
||||
if ($(this).attr('data-val') !== undefined) {
|
||||
@ -189,18 +174,23 @@ const MainUI = (($) => {
|
||||
//
|
||||
|
||||
// scroll links
|
||||
$(D).on('click', '.js-scrollTo', function(e) {
|
||||
$(D).on('click', '.js-scrollTo', (e) => {
|
||||
e.preventDefault();
|
||||
ScrollTo(this, $(this).attr('data-target'));
|
||||
const el = e.currentTarget;
|
||||
const $el = $(e.currentTarget);
|
||||
|
||||
ScrollTo(el, $el.attr('data-target'));
|
||||
});
|
||||
|
||||
// load external fonts
|
||||
if ($('[data-extfont]').length) {
|
||||
$.getScript('//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js', () => {
|
||||
const fonts = [];
|
||||
$('[data-extfont]').each(function(i) {
|
||||
fonts[i] = $(this).attr('data-extfont');
|
||||
|
||||
$('[data-extfont]').each(function(i, el) {
|
||||
fonts[i] = $(el).attr('data-extfont');
|
||||
});
|
||||
|
||||
W.WebFont.load({
|
||||
google: {
|
||||
families: fonts,
|
||||
@ -209,6 +199,30 @@ const MainUI = (($) => {
|
||||
});
|
||||
}
|
||||
|
||||
// data-set links
|
||||
$('[data-set-target]').on('click', (e) => {
|
||||
const $el = $(e.currentTarget);
|
||||
const $target = $($el.data('set-target'));
|
||||
|
||||
if (!$target.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$target.each((i, targetEl) => {
|
||||
const $targetEl = $(targetEl);
|
||||
const tag = $targetEl.prop('tagName').toLowerCase();
|
||||
|
||||
if (tag === 'input' || tag === 'select') {
|
||||
$targetEl.val($el.data('set-val'));
|
||||
} else if (!$targetEl.hasClass('field')) {
|
||||
$targetEl.text($el.data('set-val'));
|
||||
}
|
||||
});
|
||||
|
||||
$el.trigger(Events.SET_TARGET_UPDATE);
|
||||
$target.closest('form').trigger(Events.SET_TARGET_UPDATE);
|
||||
});
|
||||
|
||||
// hide spinner
|
||||
Spinner.hide(() => {
|
||||
$Body.addClass('loaded');
|
||||
@ -276,14 +290,26 @@ const MainUI = (($) => {
|
||||
const $imgLazyUrls = [];
|
||||
|
||||
// collect image details
|
||||
$imgs.each(function() {
|
||||
const src = $(this).attr('src');
|
||||
const lazySrc = $(this).data('lazy-src');
|
||||
if (src.length) {
|
||||
$imgs.each((i, el) => {
|
||||
const $el = $(el);
|
||||
const src = $el.attr('src');
|
||||
const lazySrc = $el.data('lazy-src');
|
||||
|
||||
if (src && src.length) {
|
||||
$imgUrls.push(src);
|
||||
}
|
||||
if (lazySrc) {
|
||||
if (lazySrc && lazySrc.length) {
|
||||
$imgLazyUrls.push(lazySrc);
|
||||
$el.addClass('loading');
|
||||
|
||||
AjaxUI.preload([lazySrc]).then(() => {
|
||||
$el.attr('src', lazySrc);
|
||||
|
||||
$el.addClass('loaded');
|
||||
$el.removeClass('loading');
|
||||
|
||||
$el.trigger('image-lazy-loaded');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -293,14 +319,6 @@ const MainUI = (($) => {
|
||||
|
||||
// load lazy images
|
||||
AjaxUI.preload($imgLazyUrls).then(() => {
|
||||
// update lazy img src
|
||||
$('img[data-lazy-src]').each(function() {
|
||||
if (!$(this).attr('src')) {
|
||||
return;
|
||||
}
|
||||
$(this).attr('src', $(this).data('lazy-src'));
|
||||
});
|
||||
|
||||
console.log('All images are loaded!');
|
||||
|
||||
$(W).trigger('images-lazy-loaded');
|
||||
|
@ -8,10 +8,11 @@ img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus,
|
||||
button:hover,
|
||||
button:focus {
|
||||
a,
|
||||
button,
|
||||
.jqte_tool_icon {
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: .8;
|
||||
|
||||
.fas,
|
||||
@ -23,10 +24,19 @@ button:focus {
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jqte_tool_icon {
|
||||
&:hover,
|
||||
&:focus {
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// transactions
|
||||
.transition,
|
||||
a, a img,
|
||||
.jqte_tool_icon,
|
||||
a .fas, a .fab, a .far,
|
||||
a.fas, a.fab, a.far,
|
||||
button .fas, button .fab, button .far,
|
||||
@ -43,16 +53,73 @@ button, input, optgroup, select, textarea,
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
|
||||
.alert-fixed-top {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
left: 4rem;
|
||||
right: 4rem;
|
||||
}
|
||||
|
||||
.btn-toolbar {
|
||||
margin-top: $grid-gutter-height / 2;
|
||||
}
|
||||
|
||||
.field {
|
||||
position: relative;
|
||||
margin: 2rem 0;
|
||||
margin: ($grid-gutter-height / 4) 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&.required {
|
||||
&:after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
right: .5rem;
|
||||
content: "*";
|
||||
color: $red;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
&.holder-error,
|
||||
&.error {
|
||||
input, select, textarea {
|
||||
border-color: $red;
|
||||
}
|
||||
|
||||
label {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
@extend .alert;
|
||||
|
||||
@extend .alert-info;
|
||||
|
||||
display: block;
|
||||
margin: .5rem 0;
|
||||
}
|
||||
|
||||
.message.required,
|
||||
.message.error {
|
||||
@extend .alert;
|
||||
|
||||
@extend .alert-danger;
|
||||
}
|
||||
|
||||
// stick navbar to top using mobile layout
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import "~bootstrap-select/sass/bootstrap-select.scss";
|
||||
|
||||
@import "~bootstrap-datepicker/dist/css/bootstrap-datepicker.css";
|
||||
@import "~bootstrap-timepicker/css/bootstrap-timepicker.css";
|
||||
|
||||
|
@ -1 +1,48 @@
|
||||
@import "../_variables";
|
||||
|
||||
@import "~bootstrap/scss/tables";
|
||||
|
||||
.image {
|
||||
&.left {
|
||||
float: left;
|
||||
clear: left;
|
||||
}
|
||||
|
||||
&.center {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
clear: right;
|
||||
}
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
@extend .table;
|
||||
|
||||
@extend .table-bordered;
|
||||
|
||||
@extend .table-striped;
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
namespace Site\Extensions;
|
||||
|
||||
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
class SocialExtension extends DataExtension
|
||||
{
|
||||
@ -59,4 +59,18 @@ class SocialExtension extends DataExtension
|
||||
|
||||
$fields->addFieldsToTab('Root.Social', $linkFields);
|
||||
}
|
||||
|
||||
public static function byPhone($phone)
|
||||
{
|
||||
$links = Link::get()->filter('Phone', $phone);
|
||||
|
||||
if ($links->exists()) {
|
||||
return Member::get()->filter(
|
||||
'PhoneNumberID',
|
||||
array_keys($links->map('ID', 'Title')->toArray())
|
||||
)->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,10 @@ class PageController extends ContentController
|
||||
public function getSiteWideMessage()
|
||||
{
|
||||
$session = $this->getRequest()->getSession();
|
||||
$message = $session->get('SiteWideMessage');
|
||||
$session->clear('SiteWideMessage');
|
||||
|
||||
return $session->get('SiteWideMessage');
|
||||
return $message;
|
||||
}
|
||||
|
||||
public function CurrentTime()
|
||||
|
@ -1,5 +1,10 @@
|
||||
<% if $SiteWideMessage %>
|
||||
<% with $SiteWideMessage %>
|
||||
<div class="alert alert-fixed-top alert-{$Type}">
|
||||
{$Message}
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<% end_with %>
|
||||
<% end_if %>
|
||||
|
@ -19,7 +19,6 @@
|
||||
"dnadesign/silverstripe-elemental-virtual": "*",
|
||||
"dnadesign/silverstripe-elemental-userforms": "*",
|
||||
"dynamic/silverstripe-elemental-blocks": "*",
|
||||
"hestec/silverstripe-cookiebar": "^1.0",
|
||||
"drmartingonzo/ss-tinymce-charcount": "^1.0",
|
||||
"axllent/silverstripe-version-truncator": "*",
|
||||
"firesphere/googlemapsfield": "^1.0@dev",
|
||||
|
@ -27,6 +27,7 @@
|
||||
"bootstrap": "^4.1.1",
|
||||
"bootstrap-datepicker": "^1.8.0",
|
||||
"bootstrap-offcanvas": "^1.0.0",
|
||||
"bootstrap-select": "^1.13.2",
|
||||
"bootstrap-timepicker": "^0.5.2",
|
||||
"core-util-is": "^1.0.2",
|
||||
"font-awesome": "^4.7.0",
|
||||
@ -35,6 +36,7 @@
|
||||
"jquery": "^3.3.1",
|
||||
"jquery-hammerjs": "^2.0.0",
|
||||
"jquery-zoom": "^1.7.21",
|
||||
"jquery.appear": "^1.0.1",
|
||||
"mapbox-gl": "^0.48.0",
|
||||
"meta-lightbox": "^1.0.0",
|
||||
"offcanvas-bootstrap": "^2.5.2",
|
||||
|
Loading…
Reference in New Issue
Block a user