Skip to content

Commit

Permalink
temporary fix for optional-chaining support
Browse files Browse the repository at this point in the history
  • Loading branch information
pryley committed Jan 29, 2021
1 parent 5661e1e commit d6e56bd
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 21 deletions.
2 changes: 1 addition & 1 deletion +/scripts/public/forms.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** global: CustomEvent, FormData, GLSR, HTMLFormElement, StarRating */

import Recaptcha from './recaptcha.js';
import StarRating from 'star-rating.js';
import StarRating from '@/star-rating.js';
import Validation from './validation.js';
import { addRemoveClass, classListSelector } from './helpers.js';

Expand Down
2 changes: 1 addition & 1 deletion +/scripts/site-reviews-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Prism from 'prismjs';
import Search from './admin/search.js';
import Sections from './admin/sections.js';
import Shortcode from './admin/shortcode.js';
import StarRating from 'star-rating.js';
import StarRating from '@/star-rating.js';
import Status from './admin/status.js';
import Sync from './admin/sync.js';
import Tabs from './admin/tabs.js';
Expand Down
11 changes: 11 additions & 0 deletions +/scripts/star-rating.js/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const defaults = {
classNames: {
active: 'gl-active',
base: 'gl-star-rating',
selected: 'gl-selected',
},
clearable: true,
maxStars: 10,
stars: null,
tooltip: 'Select a Rating',
};
61 changes: 61 additions & 0 deletions +/scripts/star-rating.js/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
export const addRemoveClass = (el, bool, className) => {
el.classList[bool ? 'add' : 'remove'](className);
}

export const createSpanEl = (attributes) => {
const el = document.createElement('span');
attributes = attributes || {};
for (let key in attributes) {
el.setAttribute(key, attributes[key]);
}
return el;
}

export const inRange = (value, min, max) => {
return /^\d+$/.test(value) && min <= value && value <= max;
}

export const insertSpanEl = (el, after, attributes) => {
const newEl = createSpanEl(attributes);
el.parentNode.insertBefore(newEl, after ? el.nextSibling : el);
return newEl;
}

export const isEmpty = (el) => {
return null === el.getAttribute('value') || '' === el.value;
}

export const merge = (...args) => { // adapted from https://github.com/firstandthird/aug
const results = {};
args.forEach(prop => {
Object.keys(prop || {}).forEach(propName => {
if (args[0][propName] === undefined) return; // restrict keys to the defaults
const propValue = prop[propName];
if (type(propValue) === 'Object' && type(results[propName]) === 'Object') {
results[propName] = merge(results[propName], propValue);
return;
}
results[propName] = propValue;
});
});
return results;
}

export const type = (value) => {
return {}.toString.call(value).slice(8, -1);
};

export const values = (selectEl) => {
const values = [];
[].forEach.call(selectEl.options, (el) => {
const value = parseInt(el.value, 10) || 0;
if (value > 0) {
values.push({
index: el.index,
text: el.text,
value: value,
})
}
});
return values.sort((a, b) => a.value - b.value);
}
52 changes: 52 additions & 0 deletions +/scripts/star-rating.js/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**!
* Star Rating
* @version: 4.0.2
* @author: Paul Ryley (http://geminilabs.io)
* @url: https://github.com/pryley/star-rating.js
* @license: MIT
*/

import { defaults } from './defaults'
import { merge, type } from './helpers'
import { Widget } from './widget'

class StarRating {
constructor (selector, props) { // (HTMLSelectElement|NodeList|string, object):void
this.destroy = this.destroy.bind(this);
this.rebuild = this.rebuild.bind(this);
this.widgets = [];
this.buildWidgets(selector, props);
}

buildWidgets(selector, props) { // (HTMLSelectElement|NodeList|string, object):void
this.queryElements(selector).forEach(el => {
const options = merge(defaults, props, JSON.parse(el.getAttribute('data-options')));
if ('SELECT' === el.tagName && !el.parentNode.classList.contains(options.classNames.base)) {
this.widgets.push(new Widget(el, options));
}
});
}

destroy () { // ():void
this.widgets.forEach(widget => widget.destroy());
}

queryElements (selector) { // (HTMLSelectElement|NodeList|string):array
if ('HTMLSelectElement' === type(selector)) {
return [selector];
}
if ('NodeList' === type(selector)) {
return [].slice.call(selector);
}
if ('String' === type(selector)) {
return [].slice.call(document.querySelectorAll(selector))
}
return []
}

rebuild () { // ():void
this.widgets.forEach(widget => widget.build());
}
}

export default StarRating
165 changes: 165 additions & 0 deletions +/scripts/star-rating.js/widget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { addRemoveClass, createSpanEl, inRange, insertSpanEl, isEmpty, values } from './helpers'
import { supportsPassiveEvents } from 'detect-it'

export class Widget {
constructor (el, props) { // (HTMLElement, object):void
this.direction = window.getComputedStyle(el, null).getPropertyValue('direction');
this.el = el;
this.events = {
change: this.onChange.bind(this),
keydown: this.onKeyDown.bind(this),
mousedown: this.onPointerDown.bind(this),
mouseleave: this.onPointerLeave.bind(this),
mousemove: this.onPointerMove.bind(this),
reset: this.onReset.bind(this),
touchend: this.onPointerDown.bind(this),
touchmove: this.onPointerMove.bind(this),
};
this.indexActive = null; // the active span index
this.indexSelected = null; // the selected span index
this.props = props;
this.tick = null;
this.ticking = false;
this.values = values(el);
this.widgetEl = null;
if (inRange(this.values.length, 1, this.props.maxStars)) {
this.build();
} else {
this.destroy();
}
}

build () { // ():void
this.destroy();
this.buildWidget();
this.changeIndexTo((this.indexSelected = this.selected()), true); // set the initial value
this.handleEvents('add');
}

buildWidget () { // ():void
const parentEl = insertSpanEl(this.el, false, { class: this.props.classNames.base });
parentEl.appendChild(this.el);
parentEl.classList.add(this.props.classNames.base + '--' + this.direction);
const widgetEl = insertSpanEl(this.el, true, { class: this.props.classNames.base + '--stars' });
this.values.forEach((item, index) => {
const el = createSpanEl({ 'data-index': index, 'data-value': item.value });
if ('function' === typeof this.props.stars) {
this.props.stars.call(this, el, item, index);
}
[].forEach.call(el.children, el => el.style.pointerEvents = 'none');
widgetEl.innerHTML += el.outerHTML;
})
if (this.props.tooltip) {
widgetEl.setAttribute('role', 'tooltip');
}
this.widgetEl = widgetEl;
}

changeIndexTo (index, force) { // (int):void
if (this.indexActive !== index || force) {
this.widgetEl.childNodes.forEach((el, i) => { // i starts at zero
addRemoveClass(el, i <= index, this.props.classNames.active);
addRemoveClass(el, i === this.indexSelected, this.props.classNames.selected);
});
if ('function' !== typeof this.props.stars) { // @v3 compat
this.widgetEl.classList.remove('s' + (10 * (this.indexActive + 1)));
this.widgetEl.classList.add('s' + (10 * (index + 1)));
}
if (this.props.tooltip) {
const label = index < 0 ? this.props.tooltip : this.values[index].text;
this.widgetEl.setAttribute('aria-label', label);
}
this.indexActive = index;
}
this.ticking = false;
}

destroy () { // ():void
this.indexActive = null; // the active span index
this.indexSelected = this.selected(); // the selected span index
const wrapEl = this.el.parentNode;
if (wrapEl.classList.contains(this.props.classNames.base)) {
this.handleEvents('remove');
wrapEl.parentNode.replaceChild(this.el, wrapEl);
}
}

eventListener (el, action, events, items) { // (HTMLElement, string, array, object):void
events.forEach(ev => el[action + 'EventListener'](ev, this.events[ev], items || false));
}

handleEvents (action) { // (string):void
const formEl = this.el.closest('form');
if (formEl && formEl.tagName === 'FORM') {
this.eventListener(formEl, action, ['reset']);
}
this.eventListener(this.el, action, ['change']); // always trigger the change event, even when SELECT is disabled
if ('add' === action && this.el.disabled) return;
this.eventListener(this.el, action, ['keydown']);
this.eventListener(this.widgetEl, action, ['mousedown', 'mouseleave', 'mousemove', 'touchend', 'touchmove'],
supportsPassiveEvents ? { passive: false } : false
);
}

indexFromEvent (ev) { // (MouseEvent|TouchEvent):void
const origin = ev.touches?.[0] || ev.changedTouches?.[0] || ev;
const el = document.elementFromPoint(origin.clientX, origin.clientY);
return parseInt(el.dataset.index || -1, 10);
}

onChange () { // ():void
this.changeIndexTo(this.selected(), true);
}

onKeyDown (ev) { // (KeyboardEvent):void
const key = ev.key.slice(5);
if (!~['Left', 'Right'].indexOf(key)) return;
let increment = key === 'Left' ? -1 : 1;
if (this.direction === 'rtl') {
increment *= -1;
}
const maxIndex = this.values.length - 1;
const minIndex = -1;
const index = Math.min(Math.max(this.selected() + increment, minIndex), maxIndex);
this.selectValue(index);
}

onPointerDown (ev) { // (MouseEvent|TouchEvent):void
ev.preventDefault();
this.el.focus(); // highlight the rating field
let index = this.indexFromEvent(ev);
if (this.props.clearable && index === this.indexSelected) {
index = -1; // remove the value
}
this.selectValue(index);
}

onPointerLeave (ev) { // (MouseEvent):void
ev.preventDefault();
cancelAnimationFrame(this.tick);
requestAnimationFrame(() => this.changeIndexTo(this.indexSelected));
}

onPointerMove (ev) { // (MouseEvent|TouchEvent):void
ev.preventDefault();
if (!this.ticking) {
this.tick = requestAnimationFrame(() => this.changeIndexTo(this.indexFromEvent(ev)));
this.ticking = true;
}
}

onReset () { // ():void
const index = this.el.querySelector('[selected]')?.index;
this.selectValue(this.values.findIndex(val => val.index === index));
}

selected () { // ():int
return this.values.findIndex(val => val.value === +this.el.value); // get the selected span index
}

selectValue (index) { // (int):void
this.el.value = this.values[index]?.value || ''; // first set the value
this.indexSelected = this.selected(); // get the actual index from the selected value
this.el.dispatchEvent(new Event('change'));
}
}
13 changes: 13 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"plugins": [
"@babel/plugin-proposal-optional-chaining",
["prismjs", {
"css": false,
"languages": ["javascript", "php", "html", "css"],
"plugins": ["line-numbers"]
}]
],
"presets": [
"@wordpress/default"
]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.*
!.babelrc
!.gitattributes
!.gitignore
!.jshintrc
Expand Down
2 changes: 1 addition & 1 deletion assets/scripts/site-reviews-admin.js

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions assets/scripts/site-reviews.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
"star-rating.js": "^4.0.2"
},
"devDependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
"@wordpress/babel-preset-default": "^5.0.0",
"babel-plugin-prismjs": "^2.0.1",
"browser-sync": "^2.26.13",
"browser-sync": "^2.26.14",
"browser-sync-webpack-plugin": "^2.2.2",
"gulp": "^4.0.2",
"gulp-bump": "^3.2.0",
Expand All @@ -16,7 +17,7 @@
"gulp-pottopo": "^1.0.1",
"gulp-sort": "^2.0.0",
"gulp-wp-pot": "^2.5.0",
"laravel-mix": "^6.0.10",
"laravel-mix": "^6.0.11",
"laravel-mix-bundle-analyzer": "^1.0.5",
"postcss-calc": "^8.0.0",
"postcss-custom-properties": "^11.0.0",
Expand Down
14 changes: 0 additions & 14 deletions webpack.mix.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,6 @@ const postCss = namespace => {

require('laravel-mix-bundle-analyzer');

mix.babelConfig({
plugins: [
['prismjs', {
'languages': ['javascript', 'php', 'html', 'css'],
'plugins': ['line-numbers'],
'css': false,
}],
],
presets: [
['@babel/preset-env', { forceAllTransforms: true, modules: false }],
"@wordpress/default",
],
});

mix.disableSuccessNotifications();

mix.options({
Expand Down

0 comments on commit d6e56bd

Please sign in to comment.