Add miniPaint as new frontend base

This commit is contained in:
motion
2026-01-26 12:20:16 -05:00
parent bb859105ff
commit 6ae4cbcb77
306 changed files with 60729 additions and 4050 deletions
@@ -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);
+6
View File
@@ -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);
+234
View File
@@ -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);
+170
View File
@@ -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);