Add miniPaint as new frontend base
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import Helper_class from './../../libs/helpers.js';
|
||||
import Dialog_class from './../../libs/popup.js';
|
||||
import GUI_colors_class from './../gui/gui-colors.js';
|
||||
|
||||
const Helper = new Helper_class();
|
||||
|
||||
/**
|
||||
* This input opens a custom color picker dialog that is more tightly integrated with the application (swatch selection, etc).
|
||||
* It can also handle alpha values, whereas native color input can't.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
const template = `
|
||||
<div class="ui_color_input" tabindex="-1">
|
||||
<input type="color">
|
||||
<div class="alpha_overlay"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const on_focus_color_input = (event) => {
|
||||
const $el = $(event.target.closest('.ui_color_input'));
|
||||
$el.trigger('focus');
|
||||
};
|
||||
|
||||
const on_blur_color_input = (event) => {
|
||||
const $el = $(event.target.closest('.ui_color_input'));
|
||||
$el.trigger('blur');
|
||||
};
|
||||
|
||||
const on_click_color_input = (event) => {
|
||||
event.preventDefault();
|
||||
const $el = $(event.target.closest('.ui_color_input'));
|
||||
const { value } = $el.data();
|
||||
const POP = new Dialog_class();
|
||||
let colorsDialog = new GUI_colors_class();
|
||||
var settings = {
|
||||
title: 'Color Picker',
|
||||
on_finish() {
|
||||
set_value($el, colorsDialog.COLOR + (colorsDialog.ALPHA < 255 ? colorsDialog.ALPHA.toString(16).padStart(2, '0') : ''));
|
||||
$el.trigger('input');
|
||||
$el.trigger('change');
|
||||
colorsDialog = null;
|
||||
},
|
||||
params: [
|
||||
{
|
||||
function() {
|
||||
var html = '<div id="dialog_color_picker"></div>';
|
||||
return html;
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
let colorValue;
|
||||
let alpha = 255;
|
||||
if (/^\#[0-9A-F]{8}$/gi.test(value)) {
|
||||
// Hex with alpha
|
||||
colorValue = value.slice(0, 7);
|
||||
alpha = parseInt(value.slice(7, 9), 16);
|
||||
} else if (/^\#[0-9A-F]{6}$/gi.test(value)) {
|
||||
// Hex without alpha
|
||||
colorValue = value;
|
||||
} else {
|
||||
colorValue = '#000000';
|
||||
}
|
||||
POP.show(settings);
|
||||
colorsDialog.render_main_colors('dialog');
|
||||
colorsDialog.set_color({ hex: colorValue, a: alpha });
|
||||
};
|
||||
|
||||
const set_value = ($el, value) => {
|
||||
const trimmedValue = (value + '').trim();
|
||||
let colorValue;
|
||||
let opacity = 0;
|
||||
if (/^\#[0-9A-F]{8}$/gi.test(trimmedValue)) {
|
||||
// Hex with alpha
|
||||
colorValue = trimmedValue.slice(0, 7);
|
||||
opacity = 1 - (parseInt(value.slice(7, 9), 16) * (1 / 255));
|
||||
} else if (/^\#[0-9A-F]{6}$/gi.test(trimmedValue)) {
|
||||
// Hex without alpha
|
||||
colorValue = trimmedValue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
const { input, overlay } = $el.data();
|
||||
overlay.style.opacity = opacity;
|
||||
input.value = colorValue;
|
||||
$el.data('value', trimmedValue);
|
||||
};
|
||||
|
||||
const set_disabled = ($el, disabled) => {
|
||||
const { input } = $el.data();
|
||||
if (disabled) {
|
||||
input.setAttribute('disabled', 'disabled');
|
||||
} else {
|
||||
input.removeAttribute('disabled');
|
||||
}
|
||||
$el.data('disabled', disabled);
|
||||
};
|
||||
|
||||
$.fn.uiColorInput = function(behavior, ...args) {
|
||||
let returnValues = [];
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
let el = this[i];
|
||||
|
||||
// Constructor
|
||||
if (Object.prototype.toString.call(behavior) !== '[object String]') {
|
||||
const definition = behavior || {};
|
||||
|
||||
const classList = el.className;
|
||||
const id = definition.id != null ? definition.id : el.getAttribute('id');
|
||||
const inputId = definition.inputId || '';
|
||||
const disabled = definition.disabled != null ? definition.disabled : el.hasAttribute('disabled') ? true : false;
|
||||
const value = definition.value != null ? definition.value : el.value || 0;
|
||||
const ariaLabeledBy = el.getAttribute('aria-labelledby');
|
||||
|
||||
let $el;
|
||||
if (el.parentNode) {
|
||||
$(el).after(template);
|
||||
const oldEl = el;
|
||||
el = el.nextElementSibling;
|
||||
$(oldEl).remove();
|
||||
} else {
|
||||
const orphanedParent = document.createElement('div');
|
||||
orphanedParent.innerHTML = template;
|
||||
el = orphanedParent.firstElementChild;
|
||||
}
|
||||
this[i] = el;
|
||||
$el = $(el);
|
||||
|
||||
const input = $el.find('input[type="color"]')[0];
|
||||
const overlay = $el.find('.alpha_overlay')[0];
|
||||
|
||||
if (classList) {
|
||||
el.classList.add(classList);
|
||||
}
|
||||
if (id) {
|
||||
el.setAttribute('id', id);
|
||||
}
|
||||
if (inputId) {
|
||||
input.setAttribute('id', inputId);
|
||||
}
|
||||
if (ariaLabeledBy) {
|
||||
input.setAttribute('aria-labelledby', ariaLabeledBy);
|
||||
}
|
||||
|
||||
$el.data({
|
||||
id,
|
||||
input,
|
||||
overlay,
|
||||
value
|
||||
});
|
||||
|
||||
$(input)
|
||||
.on('click', on_click_color_input)
|
||||
.on('focus', on_focus_color_input)
|
||||
.on('blur', on_blur_color_input)
|
||||
|
||||
set_value($el, value);
|
||||
set_disabled($el, disabled);
|
||||
}
|
||||
// Behaviors
|
||||
else if (behavior === 'set_value') {
|
||||
const newValue = args[0];
|
||||
const $el = $(el);
|
||||
if ($el.data('value') !== newValue) {
|
||||
set_value($(el), newValue);
|
||||
}
|
||||
}
|
||||
else if (behavior === 'get_value') {
|
||||
returnValues.push($(el).data('value'));
|
||||
}
|
||||
else if (behavior === 'get_id') {
|
||||
returnValues.push($(el).data('id'));
|
||||
}
|
||||
}
|
||||
if (returnValues.length > 0) {
|
||||
return returnValues.length === 1 ? returnValues[0] : returnValues;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,213 @@
|
||||
import Helper_class from './../../libs/helpers.js';
|
||||
|
||||
var Helper = new Helper_class();
|
||||
|
||||
(function ($) {
|
||||
|
||||
const template = `
|
||||
<div class="ui_color_picker_gradient">
|
||||
<div class="secondary_pick" tabindex="0" role="figure" aria-label="Saturation vs value selection. Use left/right arrow keys to control saturation. Use up/down arrow keys to control value.">
|
||||
<div class="saturation_gradient"></div>
|
||||
<div class="value_gradient"></div>
|
||||
<div class="handle"></div>
|
||||
</div>
|
||||
<div class="primary_pick">
|
||||
<input aria-label="Hue" type="range" min="0" max="360" step="1" class="color_picker_thin" />
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const on_key_down_secondary_pick = (event) => {
|
||||
const $el = $(event.target.closest('.ui_color_picker_gradient'));
|
||||
const { hsv } = $el.data();
|
||||
const key = event.key;
|
||||
if (['Left', 'ArrowLeft'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_hsv($el, {
|
||||
h: hsv.h,
|
||||
s: hsv.s - 1/100,
|
||||
v: hsv.v
|
||||
});
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['Right', 'ArrowRight'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_hsv($el, {
|
||||
h: hsv.h,
|
||||
s: hsv.s + 1/100,
|
||||
v: hsv.v
|
||||
});
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['Up', 'ArrowUp'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_hsv($el, {
|
||||
h: hsv.h,
|
||||
s: hsv.s,
|
||||
v: hsv.v + 1/100
|
||||
});
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['Down', 'ArrowDown'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_hsv($el, {
|
||||
h: hsv.h,
|
||||
s: hsv.s,
|
||||
v: hsv.v - 1/100
|
||||
});
|
||||
$el.trigger('input');
|
||||
}
|
||||
};
|
||||
|
||||
const on_mouse_down_secondary_pick = (event) => {
|
||||
event.preventDefault();
|
||||
const $el = $(event.target.closest('.ui_color_picker_gradient'));
|
||||
const { secondaryPick, secondaryPickHandle, hsv } = $el.data();
|
||||
const clientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
|
||||
const clientY = event.touches && event.touches.length > 0 ? event.touches[0].clientY : event.clientY;
|
||||
const mouseDownSecondaryPickRect = secondaryPick.getBoundingClientRect();
|
||||
|
||||
const xRatio = (clientX - mouseDownSecondaryPickRect.left) / (mouseDownSecondaryPickRect.right - mouseDownSecondaryPickRect.left);
|
||||
const yRatio = (clientY - mouseDownSecondaryPickRect.top) / (mouseDownSecondaryPickRect.bottom - mouseDownSecondaryPickRect.top);
|
||||
|
||||
set_hsv($el, {
|
||||
h: hsv.h,
|
||||
s: xRatio,
|
||||
v: 1 - yRatio
|
||||
});
|
||||
|
||||
$el.trigger('input');
|
||||
|
||||
$el.data({
|
||||
mouseDownSecondaryPickRect,
|
||||
mouseMoveWindowHandler: generate_on_mouse_move_window($el),
|
||||
mouseUpWindowHandler: generate_on_mouse_up_window($el)
|
||||
});
|
||||
|
||||
const $window = $(window);
|
||||
$window.on('mousemove touchmove', $el.data('mouseMoveWindowHandler'));
|
||||
$window.on('mouseup touchend', $el.data('mouseUpWindowHandler'));
|
||||
};
|
||||
|
||||
const on_touch_move_secondary_pick = (event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const generate_on_mouse_move_window = ($el) => {
|
||||
return (event) => {
|
||||
const { hsv, mouseDownSecondaryPickRect } = $el.data();
|
||||
const clientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
|
||||
const clientY = event.touches && event.touches.length > 0 ? event.touches[0].clientY : event.clientY;
|
||||
const xRatio = (clientX - mouseDownSecondaryPickRect.left) / (mouseDownSecondaryPickRect.right - mouseDownSecondaryPickRect.left);
|
||||
const yRatio = (clientY - mouseDownSecondaryPickRect.top) / (mouseDownSecondaryPickRect.bottom - mouseDownSecondaryPickRect.top);
|
||||
set_hsv($el, {
|
||||
h: hsv.h,
|
||||
s: xRatio,
|
||||
v: 1 - yRatio
|
||||
});
|
||||
$el.trigger('input');
|
||||
};
|
||||
};
|
||||
|
||||
const generate_on_mouse_up_window = ($el) => {
|
||||
return (event) => {
|
||||
const $window = $(window);
|
||||
$window.off('mousemove touchmove', $el.data('mouseMoveWindowHandler'));
|
||||
$window.off('mouseup touchend', $el.data('mouseUpWindowHandler'));
|
||||
};
|
||||
};
|
||||
|
||||
// All hsv values range from 0 to 1.
|
||||
const set_hsv = ($el, hsv) => {
|
||||
const { secondaryPick, secondaryPickHandle, primaryRange } = $el.data();
|
||||
hsv.h = Math.max(0, Math.min(1, hsv.h));
|
||||
hsv.s = Math.max(0, Math.min(1, hsv.s));
|
||||
hsv.v = Math.max(0, Math.min(1, hsv.v));
|
||||
$el.data('hsv', hsv);
|
||||
$(primaryRange).uiRange('set_value', (1 - hsv.h) * 360);
|
||||
secondaryPick.style.background = Helper.hsvToHex(hsv.h, 1, 1);
|
||||
secondaryPickHandle.style.left = ((hsv.s) * 100) + '%';
|
||||
secondaryPickHandle.style.top = ((1 - hsv.v) * 100) + '%';
|
||||
};
|
||||
|
||||
$.fn.uiColorPickerGradient = function(behavior, ...args) {
|
||||
let returnValues = [];
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
let el = this[i];
|
||||
|
||||
// Constructor
|
||||
if (Object.prototype.toString.call(behavior) !== '[object String]') {
|
||||
const definition = behavior || {};
|
||||
|
||||
const id = definition.id != null ? definition.id : el.getAttribute('id');
|
||||
const label = definition.label != null ? definition.label : el.getAttribute('aria-label');
|
||||
const hsv = definition.hsv || { h: 0, s: 0, v: 0 };
|
||||
|
||||
$(el).after(template);
|
||||
const oldEl = el;
|
||||
el = el.nextElementSibling;
|
||||
$(oldEl).remove();
|
||||
this[i] = el;
|
||||
|
||||
if (id) {
|
||||
el.setAttribute('id', id);
|
||||
}
|
||||
if (label) {
|
||||
el.setAttribute('aria-label', label);
|
||||
}
|
||||
|
||||
const $el = $(el);
|
||||
|
||||
const $primaryRange = $($el.find('.primary_pick input').get(0));
|
||||
$primaryRange
|
||||
.uiRange({ vertical: true })
|
||||
.uiRange('set_background', 'linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%)')
|
||||
.on('input', () => {
|
||||
const { hsv } = $el.data();
|
||||
set_hsv($el, {
|
||||
h: 1 - ($primaryRange.uiRange('get_value') / 360),
|
||||
s: hsv.s,
|
||||
v: hsv.v
|
||||
});
|
||||
$el.trigger('input');
|
||||
});
|
||||
|
||||
$el.find('> input').uiRange();
|
||||
|
||||
const secondaryPick = $el.find('.secondary_pick')[0];
|
||||
|
||||
$el.data({
|
||||
primaryRange: $primaryRange[0],
|
||||
secondaryPick,
|
||||
secondaryPickHandle: $el.find('.secondary_pick .handle')[0],
|
||||
hsv
|
||||
});
|
||||
|
||||
set_hsv($el, hsv);
|
||||
|
||||
$(secondaryPick).on('keydown', on_key_down_secondary_pick);
|
||||
$(secondaryPick).on('mousedown touchstart', on_mouse_down_secondary_pick);
|
||||
$(secondaryPick).on('touchmove', on_touch_move_secondary_pick);
|
||||
}
|
||||
// Behaviors
|
||||
else if (behavior === 'set_hsv') {
|
||||
const $el = $(el);
|
||||
const hsv = $el.data('hsv');
|
||||
const newHsv = args[0];
|
||||
if (newHsv && (hsv.h !== newHsv.h || hsv.s !== newHsv.s || hsv.v !== newHsv.v)) {
|
||||
set_hsv($(el), newHsv);
|
||||
}
|
||||
}
|
||||
else if (behavior === 'get_hsv') {
|
||||
const hsv = $(el).data('hsv');
|
||||
returnValues.push(JSON.parse(JSON.stringify(hsv)));
|
||||
}
|
||||
}
|
||||
if (returnValues.length > 0) {
|
||||
return returnValues.length === 1 ? returnValues[0] : returnValues;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
import './color-input.js';
|
||||
import './color-picker-gradient.js';
|
||||
import './number-input.js';
|
||||
import './range.js';
|
||||
import './swatches.js';
|
||||
@@ -0,0 +1,306 @@
|
||||
import Helper_class from './../../libs/helpers.js';
|
||||
|
||||
var Helper = new Helper_class();
|
||||
|
||||
/**
|
||||
* The purpose of using this class vs a native input[type="number"] is for custom styling and
|
||||
* to allow for gestures on mobile that makes it easier to use with a thumb on a touch screen (future implementation)
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
const template = `
|
||||
<div class="ui_number_input">
|
||||
<input type="number">
|
||||
<button class="increase_number" tabindex="-1"><span class="sr_only">Increase</span></button>
|
||||
<button class="decrease_number" tabindex="-1"><span class="sr_only">Decrease</span></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const on_focus_number_input = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
$el.trigger('focus', event);
|
||||
};
|
||||
|
||||
const on_blur_number_input = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
$el.trigger('blur', event);
|
||||
};
|
||||
|
||||
const on_input_number_input = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const value = $el.data('input').value;
|
||||
if (value != '') {
|
||||
set_value($el, $el.data('input').value);
|
||||
}
|
||||
$el.trigger('input', event);
|
||||
};
|
||||
|
||||
const on_change_number_input = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const { input, min } = $el.data();
|
||||
let value = input.value;
|
||||
if (value === '') {
|
||||
value = 0;
|
||||
}
|
||||
set_value($el, value);
|
||||
$el.trigger('change', event);
|
||||
};
|
||||
|
||||
const on_wheel_number_input = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const { value, step, disabled } = $el.data();
|
||||
event.preventDefault();
|
||||
const delta = (event.originalEvent.deltaY > 0 ? -1 : (event.originalEvent.deltaY < 0 ? 1 : 0));
|
||||
if (!disabled && delta !== 0) {
|
||||
set_value($el, (isNaN(value) ? 0 : value) + (step * delta)); // Intentionally not using get_step_amount
|
||||
$el.trigger('input');
|
||||
}
|
||||
}
|
||||
|
||||
const on_touch_start_increase_button = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const { value, buttonRepeatTimeout, buttonRepeatInterval, disabled } = $el.data();
|
||||
if (!disabled) {
|
||||
clearTimeout(buttonRepeatTimeout);
|
||||
clearInterval(buttonRepeatInterval);
|
||||
set_value($el, (isNaN(value) ? 0 : value) + get_step_amount($el, true));
|
||||
$el.trigger('input');
|
||||
}
|
||||
};
|
||||
|
||||
const on_mouse_down_increase_button = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const { value, buttonRepeatTimeout, buttonRepeatInterval, disabled } = $el.data();
|
||||
if (!disabled) {
|
||||
clearTimeout(buttonRepeatTimeout);
|
||||
clearInterval(buttonRepeatInterval);
|
||||
set_value($el, (isNaN(value) ? 0 : value) + get_step_amount($el, true));
|
||||
$el.trigger('input');
|
||||
$el.data('buttonRepeatTimeout', setTimeout(() => {
|
||||
$el.data('buttonRepeatInterval', setInterval(() => {
|
||||
const { value } = $el.data();
|
||||
set_value($el, value + get_step_amount($el, true));
|
||||
$el.trigger('input');
|
||||
}, 50));
|
||||
}, 400));
|
||||
}
|
||||
};
|
||||
|
||||
const on_mouse_up_increase_button = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const { buttonRepeatTimeout, buttonRepeatInterval } = $el.data();
|
||||
clearTimeout(buttonRepeatTimeout);
|
||||
clearInterval(buttonRepeatInterval);
|
||||
};
|
||||
|
||||
const on_touch_start_decrease_button = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const { value, buttonRepeatTimeout, buttonRepeatInterval, disabled } = $el.data();
|
||||
if (!disabled) {
|
||||
clearTimeout(buttonRepeatTimeout);
|
||||
clearInterval(buttonRepeatInterval);
|
||||
set_value($el, (isNaN(value) ? 0 : value) - get_step_amount($el, false));
|
||||
$el.trigger('input');
|
||||
}
|
||||
};
|
||||
|
||||
const on_mouse_down_decrease_button = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const { value, buttonRepeatTimeout, buttonRepeatInterval, disabled } = $el.data();
|
||||
if (!disabled) {
|
||||
clearTimeout(buttonRepeatTimeout);
|
||||
clearInterval(buttonRepeatInterval);
|
||||
set_value($el, (isNaN(value) ? 0 : value) - get_step_amount($el, false));
|
||||
$el.trigger('input');
|
||||
$el.data('buttonRepeatTimeout', setTimeout(() => {
|
||||
$el.data('buttonRepeatInterval', setInterval(() => {
|
||||
const { value } = $el.data();
|
||||
set_value($el, value - get_step_amount($el, false));
|
||||
$el.trigger('input');
|
||||
}, 50));
|
||||
}, 400));
|
||||
}
|
||||
};
|
||||
|
||||
const on_mouse_up_decrease_button = (event) => {
|
||||
const $el = $(event.target.closest('.ui_number_input'));
|
||||
const { buttonRepeatTimeout, buttonRepeatInterval } = $el.data();
|
||||
clearTimeout(buttonRepeatTimeout);
|
||||
clearInterval(buttonRepeatInterval);
|
||||
};
|
||||
|
||||
const set_value = ($el, value) => {
|
||||
const { min, max, step, stepDecimalPlaces, input } = $el.data();
|
||||
if (typeof value === 'string') {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
if (!isNaN(value)) {
|
||||
value = parseFloat((step * Math.round(value / step)).toFixed(stepDecimalPlaces));
|
||||
value = Math.max(min, Math.min(max, value));
|
||||
if (value + '.' !== input.value) {
|
||||
input.value = value;
|
||||
}
|
||||
} else {
|
||||
value = parseFloat(null);
|
||||
input.value = '';
|
||||
}
|
||||
$el.data('value', value);
|
||||
};
|
||||
|
||||
const set_disabled = ($el, disabled) => {
|
||||
const { input } = $el.data();
|
||||
if (disabled) {
|
||||
input.setAttribute('disabled', 'disabled');
|
||||
} else {
|
||||
input.removeAttribute('disabled');
|
||||
}
|
||||
$el.data('disabled', disabled);
|
||||
};
|
||||
|
||||
const get_step_amount = ($el, increasing) => {
|
||||
const { value, step, exponentialStepButtons } = $el.data();
|
||||
if (exponentialStepButtons) {
|
||||
let amount = step;
|
||||
let absValue = Math.abs((isNaN(value) ? 0 : value));
|
||||
if (absValue >= (increasing ? 500 : 501))
|
||||
amount = 100;
|
||||
else if (absValue >= (increasing ? 100 : 101))
|
||||
amount = 50;
|
||||
else if (absValue >= (increasing ? 10 : 11))
|
||||
amount = 10;
|
||||
else if (absValue >= (increasing ? 5 : 6))
|
||||
amount = 5;
|
||||
else
|
||||
amount = 1;
|
||||
return amount;
|
||||
} else {
|
||||
return step;
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.uiNumberInput = function(behavior, ...args) {
|
||||
let returnValues = [];
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
let el = this[i];
|
||||
|
||||
// Constructor
|
||||
if (Object.prototype.toString.call(behavior) !== '[object String]') {
|
||||
const definition = behavior || {};
|
||||
|
||||
const classList = el.className;
|
||||
const id = definition.id != null ? definition.id : el.getAttribute('id');
|
||||
const min = definition.min != null ? definition.min : parseFloat(el.getAttribute('min')) || null;
|
||||
const max = definition.max != null ? definition.max : parseFloat(el.getAttribute('max')) || null;
|
||||
const step = definition.step != null ? definition.step : el.hasAttribute('step') ? parseFloat(el.getAttribute('step')) : 1;
|
||||
const exponentialStepButtons = !!definition.exponentialStepButtons;
|
||||
const disabled = definition.disabled != null ? definition.disabled : el.hasAttribute('disabled') ? true : false;
|
||||
const value = definition.value != null ? definition.value : parseFloat(el.value) || 0;
|
||||
const ariaLabeledBy = el.getAttribute('aria-labelledby');
|
||||
|
||||
let $el;
|
||||
if (el.parentNode) {
|
||||
$(el).after(template);
|
||||
const oldEl = el;
|
||||
el = el.nextElementSibling;
|
||||
$(oldEl).remove();
|
||||
} else {
|
||||
const orphanedParent = document.createElement('div');
|
||||
orphanedParent.innerHTML = template;
|
||||
el = orphanedParent.firstElementChild;
|
||||
}
|
||||
this[i] = el;
|
||||
$el = $(el);
|
||||
|
||||
const input = $el.find('input[type="number"]')[0];
|
||||
const increaseButton = $el.find('.increase_number')[0];
|
||||
const decreaseButton = $el.find('.decrease_number')[0];
|
||||
|
||||
if (classList) {
|
||||
el.classList.add(classList);
|
||||
}
|
||||
if (id) {
|
||||
el.setAttribute('id', id);
|
||||
}
|
||||
if (ariaLabeledBy) {
|
||||
input.setAttribute('aria-labelledby', ariaLabeledBy);
|
||||
}
|
||||
if (min != null) {
|
||||
input.setAttribute('min', min);
|
||||
}
|
||||
if (max != null) {
|
||||
input.setAttribute('max', max);
|
||||
}
|
||||
if (Math.floor(step) === step) {
|
||||
input.setAttribute('step', step);
|
||||
} else {
|
||||
input.setAttribute('step', 'any');
|
||||
}
|
||||
|
||||
let stepDecimalPlaces = 0;
|
||||
if ((step % 1) != 0)
|
||||
stepDecimalPlaces = step.toString().split(".")[1].length;
|
||||
|
||||
$el.data({
|
||||
id,
|
||||
input,
|
||||
increaseButton,
|
||||
decreaseButton,
|
||||
buttonRepeatTimeout: undefined,
|
||||
buttonRepeatInterval: undefined,
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
stepDecimalPlaces,
|
||||
exponentialStepButtons
|
||||
});
|
||||
|
||||
$(input)
|
||||
.on('focus', on_focus_number_input)
|
||||
.on('blur', on_blur_number_input)
|
||||
.on('input', on_input_number_input)
|
||||
.on('change', on_change_number_input)
|
||||
.on('wheel', on_wheel_number_input);
|
||||
$(increaseButton)
|
||||
.on('touchstart', on_touch_start_increase_button)
|
||||
.on('mousedown', on_mouse_down_increase_button)
|
||||
.on('mouseup mouseleave touchend', on_mouse_up_increase_button);
|
||||
$(decreaseButton)
|
||||
.on('touchstart', on_touch_start_decrease_button)
|
||||
.on('mousedown', on_mouse_down_decrease_button)
|
||||
.on('mouseup mouseleave', on_mouse_up_decrease_button);
|
||||
|
||||
set_value($el, value);
|
||||
set_disabled($el, disabled);
|
||||
}
|
||||
// Behaviors
|
||||
else if (behavior === 'set_value') {
|
||||
const newValue = parseFloat(args[0]);
|
||||
const $el = $(el);
|
||||
if ($el.data('value') !== newValue) {
|
||||
set_value($(el), newValue);
|
||||
}
|
||||
}
|
||||
else if (behavior === 'get_value') {
|
||||
returnValues.push($(el).data('value'));
|
||||
}
|
||||
else if (behavior === 'get_id') {
|
||||
returnValues.push($(el).data('id'));
|
||||
}
|
||||
else if (behavior === 'set_disabled') {
|
||||
const newValue = !!args[0];
|
||||
set_disabled($(el), newValue);
|
||||
}
|
||||
else if (behavior === 'get_disabled') {
|
||||
returnValues.push($(el).data('disabled'));
|
||||
}
|
||||
}
|
||||
if (returnValues.length > 0) {
|
||||
return returnValues.length === 1 ? returnValues[0] : returnValues;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,234 @@
|
||||
|
||||
(function ($) {
|
||||
|
||||
const template = `
|
||||
<div class="ui_range" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="1" aria-valuenow="0">
|
||||
<div class="padded_track"></div>
|
||||
<div class="bar">
|
||||
<div class="handle"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const on_keydown_range = (event) => {
|
||||
const $el = $(event.target.closest('.ui_range'));
|
||||
const key = event.key;
|
||||
const { value, step, min, max } = $el.data();
|
||||
if (['Left', 'ArrowLeft', 'Down', 'ArrowDown'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_value($el, value - step);
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['Right', 'ArrowRight', 'Up', 'ArrowUp'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_value($el, value + step);
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['PageUp'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_value($el, value + (step * 10));
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['PageDown'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_value($el, value - (step * 10));
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['Home'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_value($el, min);
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['End'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_value($el, max);
|
||||
$el.trigger('input');
|
||||
}
|
||||
};
|
||||
|
||||
const on_wheel_range = (event) => {
|
||||
const $el = $(event.target.closest('.ui_range'));
|
||||
if (document.activeElement === $el[0]) {
|
||||
const { value, step } = $el.data();
|
||||
if (event.originalEvent.deltaY < 0) {
|
||||
event.preventDefault();
|
||||
set_value($el, value + step);
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (event.originalEvent.deltaY > 0) {
|
||||
event.preventDefault();
|
||||
set_value($el, value - step);
|
||||
$el.trigger('input');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const on_mouse_down_range = (event) => {
|
||||
event.preventDefault();
|
||||
const target = event.touches && event.touches.length > 0 ? event.touches[0].target : event.target;
|
||||
const $el = $(target.closest('.ui_range'));
|
||||
const { handle, paddedTrack, value, min, max, vertical } = $el.data();
|
||||
const mouseDownClientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
|
||||
const mouseDownClientY = event.touches && event.touches.length > 0 ? event.touches[0].clientY : event.clientY;
|
||||
const mouseDownPaddedTrackRect = paddedTrack.getBoundingClientRect();
|
||||
let mouseDownValue = value;
|
||||
if (target !== handle) {
|
||||
let range, valueInRange;
|
||||
if (vertical) {
|
||||
range = mouseDownPaddedTrackRect.top - mouseDownPaddedTrackRect.bottom;
|
||||
valueInRange = mouseDownClientY - mouseDownPaddedTrackRect.bottom;
|
||||
} else {
|
||||
range = mouseDownPaddedTrackRect.right - mouseDownPaddedTrackRect.left;
|
||||
valueInRange = mouseDownClientX - mouseDownPaddedTrackRect.left;
|
||||
}
|
||||
const ratio = Math.max(0, Math.min(1, valueInRange / range));
|
||||
mouseDownValue = (max - min) * ratio;
|
||||
set_value($el, mouseDownValue);
|
||||
$el.trigger('input');
|
||||
}
|
||||
$el.data({
|
||||
mouseDownValue,
|
||||
mouseDownClientX,
|
||||
mouseDownClientY,
|
||||
mouseDownPaddedTrackRect,
|
||||
mouseMoveWindowHandler: generate_on_mouse_move_window($el),
|
||||
mouseUpWindowHandler: generate_on_mouse_up_window($el)
|
||||
});
|
||||
$el.addClass('active');
|
||||
const $window = $(window);
|
||||
$window.on('mousemove touchmove', $el.data('mouseMoveWindowHandler'));
|
||||
$window.on('mouseup touchend', $el.data('mouseUpWindowHandler'));
|
||||
$el[0].focus();
|
||||
};
|
||||
|
||||
const on_touch_move_range = (event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const generate_on_mouse_move_window = ($el) => {
|
||||
return (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const { mouseDownValue, min, max, vertical, mouseDownClientX, mouseDownClientY, mouseDownPaddedTrackRect } = $el.data();
|
||||
let range, offset, startValue;
|
||||
if (vertical) {
|
||||
const clientY = event.touches && event.touches.length > 0 ? event.touches[0].clientY : event.clientY;
|
||||
range = mouseDownPaddedTrackRect.top - mouseDownPaddedTrackRect.bottom;
|
||||
const mouseDownValueInPixelRange = ((mouseDownValue - min) / (max - min)) * range;
|
||||
startValue = mouseDownClientY - mouseDownPaddedTrackRect.bottom;
|
||||
offset = clientY - mouseDownClientY + (mouseDownValueInPixelRange - startValue);
|
||||
} else {
|
||||
const clientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
|
||||
range = mouseDownPaddedTrackRect.right - mouseDownPaddedTrackRect.left;
|
||||
const mouseDownValueInPixelRange = ((mouseDownValue - min) / (max - min)) * range;
|
||||
startValue = mouseDownClientX - mouseDownPaddedTrackRect.left;
|
||||
offset = clientX - mouseDownClientX + (mouseDownValueInPixelRange - startValue);
|
||||
}
|
||||
const ratio = Math.max(0, Math.min(1, (startValue + offset) / range));
|
||||
const value = (max - min) * ratio;
|
||||
set_value($el, value);
|
||||
$el.trigger('input');
|
||||
};
|
||||
};
|
||||
|
||||
const generate_on_mouse_up_window = ($el) => {
|
||||
return (event) => {
|
||||
const $window = $(window);
|
||||
$el.removeClass('active');
|
||||
$window.off('mousemove touchmove', $el.data('mouseMoveWindowHandler'));
|
||||
$window.off('mouseup touchend', $el.data('mouseUpWindowHandler'));
|
||||
};
|
||||
};
|
||||
|
||||
const set_value = ($el, value) => {
|
||||
const { bar, min, max, step, vertical } = $el.data();
|
||||
value = step * Math.round(value / step);
|
||||
value = Math.max(min, Math.min(max, value));
|
||||
$el.data('value', value);
|
||||
$el.attr('aria-valuemin', min);
|
||||
$el.attr('aria-valuemax', max);
|
||||
$el.attr('aria-valuenow', value);
|
||||
if (vertical) {
|
||||
bar.style.height = (((value - min) / (max - min)) * 100) + '%';
|
||||
} else {
|
||||
bar.style.width = (((value - min) / (max - min)) * 100) + '%';
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.uiRange = function(behavior, ...args) {
|
||||
let returnValues = [];
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
let el = this[i];
|
||||
|
||||
// Constructor
|
||||
if (Object.prototype.toString.call(behavior) !== '[object String]') {
|
||||
const definition = behavior || {};
|
||||
|
||||
const classList = el.className;
|
||||
const id = definition.id != null ? definition.id : el.getAttribute('id');
|
||||
const value = definition.value != null ? definition.value : parseFloat(el.value) || 0;
|
||||
const min = definition.min != null ? definition.min : parseFloat(el.getAttribute('min')) || 0;
|
||||
const max = definition.max != null ? definition.max : parseFloat(el.getAttribute('max')) || 0;
|
||||
const step = definition.step != null ? definition.step : el.hasAttribute('step') ? parseFloat(el.getAttribute('step')) : 1;
|
||||
const vertical = !!definition.vertical;
|
||||
|
||||
$(el).after(template);
|
||||
const oldEl = el;
|
||||
el = el.nextElementSibling;
|
||||
$(oldEl).remove();
|
||||
this[i] = el;
|
||||
const $el = $(el);
|
||||
|
||||
if (classList) {
|
||||
el.classList.add(classList);
|
||||
}
|
||||
if (vertical) {
|
||||
el.classList.add('vertical');
|
||||
}
|
||||
if (id) {
|
||||
el.setAttribute('id', id);
|
||||
}
|
||||
|
||||
$el.data({
|
||||
paddedTrack: $('.padded_track', el).get(0),
|
||||
bar: $('.bar', el).get(0),
|
||||
handle: $('.handle', el).get(0),
|
||||
vertical,
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
step
|
||||
});
|
||||
|
||||
set_value($el, value);
|
||||
|
||||
$el
|
||||
.on('mousedown touchstart', on_mouse_down_range)
|
||||
.on('touchmove', on_touch_move_range)
|
||||
.on('keydown', on_keydown_range)
|
||||
.on('wheel', on_wheel_range);
|
||||
}
|
||||
// Behaviors
|
||||
else if (behavior === 'set_background') {
|
||||
const backgroundStyle = args[0];
|
||||
$(el).data('paddedTrack').style.background = backgroundStyle;
|
||||
}
|
||||
else if (behavior === 'set_value') {
|
||||
const newValue = parseFloat(args[0]);
|
||||
const $el = $(el);
|
||||
if ($el.data('value') !== newValue) {
|
||||
set_value($(el), newValue);
|
||||
}
|
||||
}
|
||||
else if (behavior === 'get_value') {
|
||||
returnValues.push($(el).data('value'));
|
||||
}
|
||||
}
|
||||
if (returnValues.length > 0) {
|
||||
return returnValues.length === 1 ? returnValues[0] : returnValues;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,170 @@
|
||||
(function ($) {
|
||||
|
||||
const template = `
|
||||
<div class="ui_swatches">
|
||||
<div class="swatch_group" tabindex="0">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const on_key_down_swatches = (event) => {
|
||||
const $el = $(event.target.closest('.ui_swatches'));
|
||||
const key = event.key;
|
||||
const { rows, count, selectedIndex } = $el.data();
|
||||
if (['Left', 'ArrowLeft'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_selected_index($el, selectedIndex - 1);
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['Right', 'ArrowRight'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_selected_index($el, selectedIndex + 1);
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['Up', 'ArrowUp'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_selected_index($el, selectedIndex - Math.floor(count / rows));
|
||||
$el.trigger('input');
|
||||
}
|
||||
else if (['Down', 'ArrowDown'].includes(key)) {
|
||||
event.preventDefault();
|
||||
set_selected_index($el, selectedIndex + Math.floor(count / rows));
|
||||
$el.trigger('input');
|
||||
}
|
||||
};
|
||||
|
||||
const on_click_swatches = (event) => {
|
||||
const target = event.target;
|
||||
const $el = $(target.closest('.ui_swatches'));
|
||||
if (target.classList.contains('swatch')) {
|
||||
const { swatches } = $el.data();
|
||||
set_selected_index($el, swatches.indexOf(target));
|
||||
$el.trigger('input');
|
||||
}
|
||||
};
|
||||
|
||||
const set_selected_index = ($el, index) => {
|
||||
const { readonly, swatches } = $el.data();
|
||||
if (swatches[index]) {
|
||||
$el.data('selectedIndex', index);
|
||||
if (!readonly) {
|
||||
$el.find('.active').removeClass('active');
|
||||
$(swatches[index]).addClass('active');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const set_selected_hex = ($el, hex) => {
|
||||
const { selectedIndex, swatches } = $el.data();
|
||||
if (/^\#[0-9A-F]{6}$/gi.test(hex)) {
|
||||
const swatch = swatches[selectedIndex];
|
||||
$(swatch)
|
||||
.data('hex', hex)
|
||||
.css('background-color', hex);
|
||||
}
|
||||
};
|
||||
|
||||
const set_all_hex = ($el, hexArray) => {
|
||||
hexArray = hexArray || [];
|
||||
const { swatches } = $el.data();
|
||||
for (let i = 0; i < swatches.length; i++) {
|
||||
if (hexArray[i]) {
|
||||
const hex = hexArray[i];
|
||||
if (/^\#[0-9A-F]{6}$/gi.test(hex)) {
|
||||
$(swatches[i])
|
||||
.data('hex', hex)
|
||||
.css('background-color', hex);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$.fn.uiSwatches = function(behavior, ...args) {
|
||||
let returnValues = [];
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
let el = this[i];
|
||||
|
||||
// Constructor
|
||||
if (Object.prototype.toString.call(behavior) !== '[object String]') {
|
||||
const definition = behavior || {};
|
||||
|
||||
const id = definition.id != null ? definition.id : el.getAttribute('id');
|
||||
const cols = definition.cols;
|
||||
const rows = definition.rows || 1;
|
||||
const count = definition.count || 10;
|
||||
const readonly = definition.readonly || false;
|
||||
const selectedIndex = definition.selectedIndex != null ? definition.selectedIndex : 0;
|
||||
|
||||
$(el).after(template);
|
||||
const oldEl = el;
|
||||
el = el.nextElementSibling;
|
||||
$(oldEl).remove();
|
||||
this[i] = el;
|
||||
|
||||
const $el = $(el);
|
||||
|
||||
const swatchGroup = $el.find('.swatch_group')[0];
|
||||
|
||||
if (id) {
|
||||
el.setAttribute('id', id);
|
||||
}
|
||||
if (cols) {
|
||||
swatchGroup.classList.add('cols_' + cols);
|
||||
}
|
||||
swatchGroup.classList.add('rows_' + rows);
|
||||
|
||||
const swatches = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const swatch = document.createElement('div');
|
||||
swatch.classList.add('swatch');
|
||||
$(swatch).data('hex', '#ffffff');
|
||||
swatches.push(swatch);
|
||||
swatchGroup.appendChild(swatch);
|
||||
if (i === selectedIndex && !readonly) {
|
||||
swatch.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
$el.data({
|
||||
selectedIndex,
|
||||
swatchGroup,
|
||||
swatches,
|
||||
count,
|
||||
cols,
|
||||
rows,
|
||||
readonly
|
||||
});
|
||||
|
||||
$el
|
||||
.on('click', on_click_swatches)
|
||||
.on('keydown', on_key_down_swatches);
|
||||
}
|
||||
// Behaviors
|
||||
else if (behavior === 'set_selected_hex') {
|
||||
const newValue = args[0] + '';
|
||||
set_selected_hex($(el), newValue);
|
||||
}
|
||||
else if (behavior === 'get_selected_hex') {
|
||||
const { selectedIndex, swatches } = $(el).data();
|
||||
returnValues.push($(swatches[selectedIndex]).data('hex'));
|
||||
}
|
||||
else if (behavior === 'set_all_hex') {
|
||||
set_all_hex($(el), args[0]);
|
||||
}
|
||||
else if (behavior === 'get_all_hex') {
|
||||
const { swatches } = $(el).data();
|
||||
for (let swatch of swatches) {
|
||||
returnValues.push($(swatch).data('hex'));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (returnValues.length > 0) {
|
||||
return returnValues.length === 1 ? returnValues[0] : returnValues;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
Reference in New Issue
Block a user