Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@vaadin-component-factory/vcf-slider

Package Overview
Dependencies
Maintainers
5
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vaadin-component-factory/vcf-slider - npm Package Compare versions

Comparing version 1.0.5 to 1.0.6

out-tsc/src/mixins/CustomEventMixin.d.ts

142

package.json
{
"name": "@vaadin-component-factory/vcf-slider",
"version": "1.0.5",
"version": "1.0.6",
"description": "Slider web component for the Vaadin platform.",
"main": "dist/vcf-slider.js",
"module": "dist/vcf-slider.js",
"exports": {
".": "./dist/vcf-slider.js",
"./vcf-slider.js": "./dist/src/vcf-slider.js"
},
"main": "out-tsc/vcf-slider.js",
"module": "out-tsc/vcf-slider.js",
"types": "out-tsc/vcf-slider.d.ts",
"type": "module",
"author": "Vaadin Ltd",

@@ -15,70 +13,94 @@ "license": "Apache-2.0",

"type": "git",
"url": "git+https://github.com/vaadin/vcf-slider.git"
"url": "git+https://github.com/vaadin-component-factory/vcf-slider.git"
},
"files": [
"out-tsc",
"src",
"vcf-slider.ts"
],
"keywords": [
"Vaadin",
"lit",
"typescript",
"vaadin",
"vaadin-component-factory",
"web-component",
"vcf-slider",
"lit-element"
"web-component"
],
"scripts": {
"start": "run-p build:watch start:dev",
"start:dev": "wds --app-index demo/index.html --node-resolve --open --watch",
"prestart": "npm run analyze",
"analyze": "wca analyze \"src/**/*.ts\" --outFile demo/custom-elements.json",
"build": "tsc",
"build:demo": "NODE_ENV=production rollup -c",
"build:prod": "run-s analyze build:demo",
"build:watch": "tsc --watch",
"analyze": "cem analyze --litelement --globs \"src/**/*.ts\" --outdir demo",
"analyze:watch": "run-s \"analyze -- --watch\"",
"build": "run-s clean analyze build:compile",
"build:bundle": "rollup -c",
"build:compile": "tsc",
"build:prod": "run-s build build:bundle",
"build:watch": "run-s \"build:compile -- --watch\"",
"clean": "rimraf build out-tsc demo/custom-elements.json",
"format": "run-s format:**",
"format:eslint": "eslint --ext .ts,.html . --fix --ignore-path .gitignore",
"format:prettier": "prettier \"**/*.js\" \"**/*.ts\" --write --ignore-path .gitignore",
"lint": "run-s lint:**",
"format": "run-s format:**",
"lint:eslint": "eslint --ext .ts,.html . --ignore-path .gitignore",
"format:eslint": "eslint --ext .ts,.html . --fix --ignore-path .gitignore",
"lint:prettier": "prettier \"**/*.js\" \"**/*.ts\" --check --ignore-path .gitignore",
"format:prettier": "prettier \"**/*.js\" \"**/*.ts\" --write --ignore-path .gitignore",
"test": "run-s build \"web-test-runner --coverage\"",
"test:watch": "web-test-runner --watch",
"prepublishOnly": "run-s build analyze",
"publish": "node util/publish.js",
"prepublish": "tsc"
"serve": "wds --app-index build/index.html --node-resolve --open --watch",
"start": "run-p analyze:watch build:watch start:dev",
"start:dev": "wds --app-index demo/index.html --node-resolve --open --watch",
"test": "run-s build:compile \"test:run -- --coverage\"",
"test:dev": "run-p \"build:watch -- --preserveWatchOutput\" \"test:run -- --watch\"",
"test:run": "wtr",
"test:watch": "run-s build:compile test:dev"
},
"dependencies": {
"lit": "^2.0.0"
"@vaadin/vaadin-themable-mixin": "^23.2.10",
"lit": "^2.4.1"
},
"devDependencies": {
"@open-wc/building-rollup": "^1.9.4",
"@open-wc/eslint-config": "^4.0.0",
"@open-wc/testing": "^2.0.0",
"@types/node": "13.11.1",
"@typescript-eslint/eslint-plugin": "^2.20.0",
"@typescript-eslint/parser": "^2.20.0",
"@vaadin-component-factory/vcf-anchor-nav": "^1.1.0",
"@vaadin-component-factory/vcf-element-util": "0.2.1-beta",
"@web/dev-server": "^0.0.12",
"@web/test-runner": "^0.7.41",
"@webcomponents/webcomponentsjs": "^2.0.0",
"api-viewer-element": "0.5.0",
"eslint": "^6.1.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.22.1",
"husky": "^1.0.0",
"lint-staged": "^10.0.0",
"@api-viewer/demo": "^1.0.0-pre.6",
"@api-viewer/docs": "^1.0.0-pre.6",
"@custom-elements-manifest/analyzer": "^0.6.6",
"@open-wc/building-rollup": "^2.2.1",
"@open-wc/testing": "^3.1.6",
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^5.42.1",
"@vaadin-component-factory/vcf-anchor-nav": "^23.2.1",
"@vaadin-component-factory/vcf-element-util": "^0.3.5",
"@vaadin/vaadin-lumo-styles": "^23.2.8",
"@web/dev-server": "^0.1.34",
"@web/test-runner": "^0.14.0",
"@webcomponents/webcomponentsjs": "^2.7.0",
"concurrently": "^5.3.0",
"deepmerge": "^4.2.2",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"husky": "^4.3.8",
"lint-staged": "^10.5.4",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.4",
"rollup-plugin-copy": "^3.3.0",
"tslib": "^1.11.0",
"typescript": "~4.0.3",
"web-component-analyzer": "^1.1.6"
"prettier": "^2.4.1",
"rimraf": "^3.0.2",
"rollup-plugin-copy": "^3.4.0",
"tslib": "^2.3.1",
"typescript": "^4.5.2"
},
"customElements": "demo/custom-elements.json",
"eslintConfig": {
"parser": "@typescript-eslint/parser",
"extends": [
"@open-wc/eslint-config",
"eslint-config-prettier"
]
"prettier"
],
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/no-unused-vars": [
"error"
]
},
"env": {
"browser": true,
"es6": true
}
},
"prettier": {
"singleQuote": true,
"arrowParens": "avoid",
"trailingComma": "none",
"printWidth": 120
"arrowParens": "avoid"
},

@@ -93,11 +115,5 @@ "husky": {

"eslint --fix",
"prettier --write",
"git add"
"prettier --write"
]
},
"files": [
"vcf-slider.ts",
"src",
"dist"
]
}
}

@@ -1,14 +0,6 @@

import { html, css, PropertyValues, LitElement } from 'lit';
import { query, property, state, customElement } from 'lit/decorators';
import { html, css, PropertyValues, LitElement, render, TemplateResult } from 'lit';
import { query, property, customElement } from 'lit/decorators.js';
import { CustomEventMixin, CustomEvents, ValueChangedEvent } from './mixins/CustomEventMixin';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin';
type PointerEvent = MouseEvent | TouchEvent;
// const TOUCH_DEVICE = (() => {
// try {
// document.createEvent('TouchEvent');
// return true;
// } catch (e) {
// return false;
// }
// })();
/**

@@ -21,7 +13,23 @@ * `<vcf-slider>` Slider web component for the Vaadin platform.

* @csspart knob-n - Nth knob element.
*
* @cssprop [--vcf-slider-knob-alt-color=var(--lumo-error-color)] - Color of `::part(alt-knob)`.
* @cssprop [--vcf-slider-knob-color=var(--lumo-primary-color)] - Color of `::part(knob)`.
* @cssprop [--vcf-slider-knob-size=var(--lumo-space-m)] - Size (width, height) of `::part(knob)`.
* @cssprop [--vcf-slider-label-font-size=var(--lumo-font-size-s)] - Font size of `::part(label)`.
* @cssprop [--vcf-slider-line-alt-color=var(--lumo-contrast-30pct)] - Secondary background color of `::part(line)`.
* @cssprop [--vcf-slider-line-color=var(--lumo-contrast-50pct)] - Background color of `::part(line)`.
* @cssprop [--vcf-slider-line-height=var(--lumo-space-s)] - Width of `::part(line)`.
* @cssprop [--vcf-slider-padding=var(--lumo-space-xs)] - Padding of `::part(container)`.
* @cssprop [--vcf-slider-width=100%] - Width of `:host`.
*
* @event {ValueChangedEvent} value-changed - Fired when the slider value changes. Returns a single knob value and index.
*/
@customElement('vcf-slider')
export class VcfSlider extends LitElement {
@property({ type: Boolean, reflect: true }) labels = true;
@property({ type: Number }) value: number | number[] = 0;
export class Slider extends CustomEventMixin(ThemableMixin(LitElement)) {
/**
*
*/
@property({ type: Boolean, reflect: true }) labels = false;
@property({ type: Boolean, reflect: true }) vertical = false;
@property({ type: Number }) value: string | number | number[] = 0;
@property({ type: Number }) ranges = 0;

@@ -31,40 +39,75 @@ @property({ type: Number }) step = 1;

@property({ type: Number }) max = 100;
@state() private knobs = 1;
// private touchDevice = TOUCH_DEVICE;
@query('#knobs') private knobsContainer?: HTMLElement;
@query('#line') private line?: HTMLElement;
@query('#line-color') private lineColorElement?: HTMLElement;
protected static is() {
return 'vcf-slider';
}
protected static get version() {
return '1.0.6';
}
private knob?: HTMLElement;
private originalKnobOffsetX = 0;
private originalPointerX = 0;
private originalKnobOffsetXY = 0;
private originalPointerXY: number | null = 0;
private knobCount = 1;
@query('[part="knobs"]') private knobsContainer?: HTMLElement;
@query('[part="line"]') private line?: HTMLElement;
@query('[part="line-color"]') private lineColor?: HTMLElement;
private get xy() {
return this.vertical ? 'y' : 'x';
}
static get version() {
return '1.0.5';
private get pageXY() {
return this.vertical ? 'pageY' : 'pageX';
}
private set knobs(value: number) {
this.knobCount = value > 0 ? value : 1;
}
private get knobs() {
return this.knobCount;
}
static get styles() {
return css`
:host {
display: block;
display: flex;
margin: var(--lumo-space-s) 0;
--vcf-slider-padding: var(--lumo-space-xs);
--vcf-slider-line-width: calc(100% - 2 * var(--vcf-slider-padding));
--vcf-slider-line-height: var(--lumo-space-s);
width: var(--vcf-slider-width);
/* PUBLIC */
--vcf-slider-knob-alt-color: var(--lumo-error-color);
--vcf-slider-knob-color: var(--lumo-primary-color);
--vcf-slider-knob-size: var(--lumo-space-m);
--vcf-slider-label-font-size: var(--lumo-font-size-s);
--vcf-slider-line-alt-color: var(--lumo-contrast-30pct);
--vcf-slider-line-color: var(--lumo-contrast-50pct);
--vcf-slider-line-alternate-color: var(--lumo-contrast-30pct);
--vs-l-height: var(--vcf-slider-line-height);
--vs-k-size: var(--vcf-slider-knob-size);
--vcf-slider-line-height: var(--lumo-space-s);
--vcf-slider-padding: var(--lumo-space-xs);
--vcf-slider-width: 100%;
--vcf-slider-vertical-height: 200px;
/* PRIVATE */
--l-height: var(--vcf-slider-line-height);
--k-size: calc(var(--vcf-slider-knob-size) * 2);
}
[part='container'] {
width: var(--vcf-slider-line-width);
:host * {
box-sizing: border-box;
}
:host([labels]) {
padding-top: calc(var(--vcf-slider-label-font-size) + 4px + var(--lumo-space-s));
}
#container {
width: 100%;
padding: var(--vcf-slider-padding);
}
[part='line'] {
#line {
position: relative;
width: 100%;
height: var(--vcf-slider-line-height);
height: var(--l-height);
border-radius: var(--lumo-border-radius-m);

@@ -74,3 +117,3 @@ background-color: var(--lumo-contrast-30pct);

[part='line-color'] {
#line-color {
position: absolute;

@@ -86,3 +129,11 @@ top: 0;

position: absolute;
top: calc(-0.5 * var(--vs-k-size) + calc(0.5 * var(--vs-l-height)));
display: flex;
top: calc(-0.5 * var(--k-size) + calc(0.5 * var(--l-height)));
width: var(--k-size);
height: var(--k-size);
user-select: none;
}
[part~='knob']::after {
content: '';
width: var(--vcf-slider-knob-size);

@@ -92,8 +143,8 @@ height: var(--vcf-slider-knob-size);

box-shadow: var(--lumo-box-shadow-s);
user-select: none;
background-color: var(--lumo-primary-color);
background-color: var(--vcf-slider-knob-color);
margin: auto;
}
[part~='knob'].alternate {
background-color: var(--lumo-error-color);
[part~='alt-knob']::after {
background-color: var(--vcf-slider-knob-alt-color);
}

@@ -103,14 +154,89 @@

display: none;
flex-flow: column;
align-items: center;
position: absolute;
top: calc(-0.5 * var(--vs-k-size) + calc(0.5 * var(--vs-l-height)) - calc(2 * var(--lumo-space-m)));
border-radius: var(--lumo-border-radius-s);
bottom: calc(var(--k-size) * 0.5 + var(--lumo-space-xs));
border-radius: 2px;
box-shadow: var(--lumo-box-shadow-xs);
background-color: var(--lumo-base-color);
padding: 0 var(--lumo-space-s);
font-size: var(--lumo-font-size-s);
padding: 2px var(--lumo-space-s);
font-size: var(--vcf-slider-label-font-size);
pointer-events: none;
user-select: none;
}
[part~='label-triangle'] {
position: relative;
margin: 0;
box-sizing: border-box;
background: var(--lumo-base-color);
}
[part~='label-triangle']::after {
content: '';
position: absolute;
left: -4px;
width: 0;
height: 0;
box-sizing: border-box;
border: 3px solid transparent;
border-color: transparent transparent var(--lumo-base-color) var(--lumo-base-color);
transform-origin: 2px 1px;
transform: rotate(-45deg);
box-shadow: -2px 2px 2px 0 var(--lumo-shade-20pct);
}
:host([labels]) [part~='label'] {
display: block;
display: flex;
}
/* VERTICAL */
:host([labels][vertical]) {
padding-top: 0px;
padding-right: calc(var(--vcf-slider-label-width) + 4px + var(--lumo-space-xs));
}
:host([vertical]) {
display: inline-flex;
width: max-content;
margin: var(--lumo-space-s);
height: var(--vcf-slider-vertical-height);
}
:host([vertical]) #line {
width: var(--l-height);
height: 100%;
}
:host([vertical]) [part~='knob'] {
left: calc(-0.5 * var(--k-size) + calc(0.5 * var(--l-height)));
}
:host([vertical]) [part~='label'] {
flex-flow: row;
left: calc(var(--k-size) * 0.5 + var(--lumo-space-s));
bottom: unset;
align-items: center;
border-radius: var(--lumo-border-radius-s);
}
:host([vertical]) [part~='label-triangle']::after {
content: unset;
}
:host([vertical]) [part~='label']::after {
content: '';
position: absolute;
left: 1px;
width: 0px;
height: 0px;
box-sizing: border-box;
border: 7px solid transparent;
border-color: transparent transparent var(--lumo-base-color) var(--lumo-base-color);
transform-origin: 4px -1px;
transform: rotate(45deg);
box-shadow: -2px 2px 2px 0 var(--lumo-shade-10pct);
border-radius: 2px;
}
`;

@@ -121,6 +247,6 @@ }

return html`
<div part="container">
<div part="line">
<div part="line-color"></div>
<div part="knobs"></div>
<div id="container" part="container">
<div id="line" part="line">
<div id="line-color" part="line-color"></div>
<div id="knobs" part="knobs"></div>
</div>

@@ -132,57 +258,110 @@ </div>

updated(props: PropertyValues) {
props.forEach(async (_, prop) => {
switch (prop) {
case 'labels': {
if (this.labels) this.style.paddingTop = 'var(--lumo-space-l)';
else this.style.paddingTop = '0';
break;
}
case 'ranges': {
const { ranges } = this;
if (typeof ranges === 'number' && ranges >= 0) this.knobs = 2 * ranges || 1;
else this.ranges = 0;
break;
}
case 'min':
case 'max':
case 'knobs': {
if (this.knobs) {
this.setKnobElements();
this.setInitialValue();
}
break;
}
case 'step': {
const { step } = this;
if (step.toString().includes('.')) this.step = Math.round(step);
if (step < 1) this.step = 1;
break;
}
case 'value': {
this.setLabelValues();
this.knobIndexes.forEach(i => {
this.setAriaValues(i);
this.setLabelPosition(i);
this.setKnobPostion(i);
this.setLineColors();
});
const { ranges, step } = this;
// TODO Add events...
this.dispatchEvent(new CustomEvent('change', { detail: { value: this.value } }));
break;
}
}
});
if (props.has('labels') && this.labels) {
this.knobIndexes.forEach(i => this.setLabelPosition(i, this.values));
}
if (props.has('ranges')) {
if (typeof ranges === 'number' && ranges >= 0) this.knobs = 2 * ranges || 1;
else this.ranges = 0;
}
if (props.has('ranges') || props.has('min') || props.has('max')) {
this.setKnobElements();
this.setValue((this.value = this.initialValue));
}
if (props.has('step')) {
if (!Number.isSafeInteger(step)) this.step = Math.round(step);
if (step < 1) this.step = 1;
}
if (props.has('value') || props.has('vertical')) {
if (!this.isSorted()) this.sort();
else this.setValue();
}
}
private setLabelValues() {
const { knobIndexes, values } = this;
/** @private */
passive = true;
/** @private */
handleEvent(e: Event) {
switch (e.type) {
case 'mousedown':
case 'touchstart':
if (!Slider.isValid(e) || this.isKnobClick(e)) return;
this.knob?.focus();
this.startDrag(e);
this.dragging = true;
break;
case 'mousemove':
case 'touchmove':
this.drag(e);
break;
case 'mouseup':
case 'touchend':
this.dragging = false;
break;
case 'keydown':
this.keyMove(e, this.knobIndex);
break;
}
}
private static hasTouched = false;
/**
* Check if an event was triggered by touch.
*/
private static isTouch(e: Event): e is TouchEvent {
return 'touches' in e;
}
/**
* Prevent mobile browsers from handling mouse events (conflicting with touch ones).
* If we detected a touch interaction before, we prefer reacting to touch events only.
*/
private static isValid(event: Event) {
const { hasTouched, isTouch } = Slider;
if (hasTouched && !isTouch(event)) return false;
if (!hasTouched) Slider.hasTouched = isTouch(event);
return true;
}
private get valueChangedEvent() {
const detail = {
index: this.knobIndex,
value: this.values[this.knobIndex],
values: this.values,
};
const event = new CustomEvent(CustomEvents.valueChanged, { detail });
return event as ValueChangedEvent;
}
private static getKnobIndex(knob: HTMLElement) {
const idMatch = /knob-(.)/.exec(knob.getAttribute('part') || '');
return idMatch ? Number(idMatch[1]) : 0;
}
private get knobIndex() {
return this.knob ? Slider.getKnobIndex(this.knob) : 0;
}
private isKnobClick(e: Event) {
return !Slider.hasTouched && (e as MouseEvent).button !== 0;
}
private setLabelValues(values = this.values) {
const { knobIndexes } = this;
knobIndexes.forEach(i => {
const labelElement = this.labelElement(i) as HTMLElement;
if (labelElement) labelElement.innerText = `${values[i]}`;
const labelElementValue = labelElement.firstElementChild as HTMLSpanElement;
if (labelElement) labelElementValue.innerText = `${values[i]}`;
});
}
private setAriaValues(i = 0) {
const { values, knobs, min, max } = this;
private setAriaValues(i = 0, values = this.values) {
const { knobs, min, max } = this;
const knob = this.knobElement(i) as HTMLElement;

@@ -198,9 +377,11 @@ if (knob) {

const { knobs, min, max, step } = this;
const valueAttr = this.getAttribute('value');
const valueStep = (max - min) / (knobs - 1);
const values: number[] = [];
this.knobIndexes.map(i => {
this.knobIndexes.forEach(i => {
let init = Math.round(i === 0 ? min : i < knobs - 1 ? i * valueStep : max);
init = init - (init % step);
init -= init % step;
values.push(init < min ? min : init > max ? max : init);
});
if (valueAttr && !this.ranges) values[0] = Number(valueAttr);
return values;

@@ -210,39 +391,63 @@ }

private get values() {
const { value } = this;
return (Array.isArray(value) ? value : [value || 0]) as number[];
let { value } = this;
if (typeof value === 'string') value = JSON.parse(value[0] === '[' ? value : `[${value}]`);
else value = (Array.isArray(value) ? value : [value || 0]) as number[];
return value as number[];
}
private setInitialValue() {
this.value = this.initialValue;
this.knobIndexes.map(i => this.setKnobPostion(i));
this.setLineColors();
private setValue(values = this.values) {
values = values.sort((a, b) => a - b);
this.setLabelValues(values);
this.knobIndexes.forEach(i => {
this.setAriaValues(i, values);
this.setLabelPosition(i, values);
this.setKnobPostion(i, values);
this.setLineColors(values);
});
this.dispatchEvent(this.valueChangedEvent);
}
private setKnobPostion(i = 0) {
const { min, max, values } = this;
const lineWidth = this.lineBounds!.width;
private setKnobPostion(i = 0, values = this.initialValue) {
const { min, max, lineBounds } = this;
const knob = this.knobElement(i) as HTMLElement;
if (knob) {
const knobWidth = this.getBounds(knob).width;
const position = ((values[i] - min) / (max - min)) * lineWidth - knobWidth / 2;
knob.style.left = `${position}px`;
const knobBounds = this.getBounds(knob);
if (this.vertical) {
const position = ((values[i] - min) / (max - min)) * lineBounds.height - knobBounds.height / 2;
knob.style.top = 'unset';
knob.style.bottom = `${position}px`;
knob.style.removeProperty('left');
} else {
const position = ((values[i] - min) / (max - min)) * lineBounds.width - knobBounds.width / 2;
knob.style.left = `${position}px`;
knob.style.removeProperty('top');
knob.style.removeProperty('bottom');
}
}
}
private setLabelPosition(i = 0) {
const { min, max, values } = this;
const lineWidth = this.lineBounds!.width;
private setLabelPosition(i = 0, values = this.values) {
const { min, max, lineBounds } = this;
const label = this.labelElement(i) as HTMLElement;
if (label) {
const labelWidth = this.getBounds(label).width;
const position = ((values[i] - min) / (max - min)) * lineWidth - labelWidth / 2;
label.style.left = `${position}px`;
const labelBounds = this.getBounds(label);
if (this.vertical) {
const position = ((values[i] - min) / (max - min)) * lineBounds.height - labelBounds.height / 2;
label.style.bottom = `${position}px`;
label.style.removeProperty('left');
} else {
const position = ((values[i] - min) / (max - min)) * lineBounds.width - labelBounds.width / 2;
label.style.left = `${position}px`;
label.style.removeProperty('bottom');
}
this.style.setProperty('--vcf-slider-label-width', `${labelBounds.width}px`);
}
}
private setLineColors() {
const { knobs, min, max, values } = this;
private setLineColors(values = this.values) {
const { knobs, min, max, lineColorElement } = this;
const length = max - min;
const lineColor = getComputedStyle(this).getPropertyValue('--vcf-slider-line-color').trim();
const altLineColor = getComputedStyle(this).getPropertyValue('--vcf-slider-line-alternate-color').trim();
const altLineColor = getComputedStyle(this).getPropertyValue('--vcf-slider-line-alt-color').trim();
const direction = this.vertical ? 'top' : 'right';
let colors = '';

@@ -259,38 +464,40 @@ let prevStop = '';

colors += `transparent ${prevStop} 100%`;
this.lineColor!.style.background = `linear-gradient(to right, ${colors})`;
if (lineColorElement) lineColorElement.style.background = `linear-gradient(to ${direction}, ${colors})`;
}
private getKnobIndex(knob: HTMLElement) {
const idMatch = /knob-(.)/.exec(knob.getAttribute('part') || '');
return idMatch ? Number(idMatch[1]) : 0;
private set dragging(state: boolean) {
const toggleEvent = state ? document.addEventListener : document.removeEventListener;
toggleEvent(Slider.hasTouched ? 'touchmove' : 'mousemove', this);
toggleEvent(Slider.hasTouched ? 'touchend' : 'mouseup', this);
}
private startDrag = (e: PointerEvent) => {
private getPointerXY(e: Event) {
let pointerPos: number | null = null;
if (e instanceof MouseEvent) pointerPos = (e as MouseEvent)[this.pageXY];
else if (e instanceof TouchEvent) pointerPos = (e as TouchEvent).touches[0][this.pageXY];
return pointerPos;
}
private startDrag = (e: Event) => {
const { knobsContainer, xy } = this;
this.knob = e.target as HTMLElement;
const { knob, label, knobsContainer } = this;
const button = (e as MouseEvent).button;
const touches = (e as TouchEvent).touches;
this.originalPointerXY = this.getPointerXY(e);
this.originalKnobOffsetXY = this.getBounds(this.knob)[xy] - this.lineBounds[xy];
this.originalPointerX = (e as MouseEvent).pageX;
this.originalKnobOffsetX = this.getBounds(knob).x - this.lineBounds!.x;
if (button === 0 || touches) {
window.addEventListener('mouseup', this.endDrag);
window.addEventListener('touchend', this.endDrag);
window.addEventListener('mousemove', this.drag);
window.addEventListener('touchmove', this.drag);
}
// Move current knob and label to top
knobsContainer?.appendChild(knob);
knobsContainer?.appendChild(label);
knobsContainer?.appendChild(this.knob);
knobsContainer?.appendChild(this.label);
};
private drag = (e: PointerEvent) => {
const { knob, knobs, originalKnobOffsetX, originalPointerX, line, lineBounds } = this;
if (knob) {
const i = this.getKnobIndex(knob);
private drag = (e: Event) => {
const { knobIndex: i, vertical, knob, knobs, originalKnobOffsetXY, originalPointerXY, xy, line, lineBounds } = this;
if (knob && line) {
const knobBounds = this.getBounds(knob);
let startX = -knobBounds.width / 2;
let endX = lineBounds!.width - knobBounds.width / 2;
const knobSize = vertical ? knobBounds.height : knobBounds.width;
const lineSize = vertical ? lineBounds.height : lineBounds.width;
const lineStart = -knobSize / 2;
const lineEnd = lineSize - knobSize / 2;
const part = `knob-${i}`;
let start = vertical ? lineEnd : lineStart;
let end = vertical ? lineStart : lineEnd;

@@ -301,4 +508,4 @@ // Set knob limits

if (knobs > 1) {
const toKnob = line!.querySelector('[part~="knob-1"]') as HTMLElement;
endX = this.getBounds(toKnob).x - lineBounds!.x;
const toKnob = line.querySelector('[part~="knob-1"]') as HTMLElement;
end = this.getBounds(toKnob)[xy] - lineBounds[xy];
}

@@ -309,4 +516,4 @@ break;

if (knobs > 1) {
const fromKnob = line!.querySelector(`[part~="knob-${knobs - 2}"]`) as HTMLElement;
startX = this.getBounds(fromKnob).x - lineBounds!.x;
const fromKnob = line.querySelector(`[part~="knob-${knobs - 2}"]`) as HTMLElement;
start = this.getBounds(fromKnob)[xy] - lineBounds[xy];
}

@@ -316,6 +523,6 @@ break;

default: {
const fromKnob = line!.querySelector(`[part~="knob-${i - 1}"]`) as HTMLElement;
const toKnob = line!.querySelector(`[part~="knob-${i + 1}"]`) as HTMLElement;
startX = this.getBounds(fromKnob).x - lineBounds!.x;
endX = this.getBounds(toKnob).x - lineBounds!.x;
const fromKnob = line.querySelector(`[part~="knob-${i - 1}"]`) as HTMLElement;
const toKnob = line.querySelector(`[part~="knob-${i + 1}"]`) as HTMLElement;
start = this.getBounds(fromKnob)[xy] - lineBounds[xy];
end = this.getBounds(toKnob)[xy] - lineBounds[xy];
}

@@ -325,24 +532,28 @@ }

// Calculate knob position
let newPositionX = originalKnobOffsetX + ((e as MouseEvent).pageX - originalPointerX);
const startLimit = newPositionX <= startX;
const endLimit = newPositionX >= endX;
newPositionX = startLimit ? startX : endLimit ? endX : newPositionX;
const pageXY = this.getPointerXY(e);
if (pageXY && originalPointerXY) {
let newPositionXY = originalKnobOffsetXY + (pageXY - originalPointerXY);
let startLimit = vertical ? newPositionXY >= start : newPositionXY <= start;
let endLimit = vertical ? newPositionXY <= end : newPositionXY >= end;
newPositionXY = startLimit ? start : endLimit ? end : newPositionXY;
// Calculate new value
const { min, max, step, values } = this;
const length = max - min;
const pct = (newPositionX + knobBounds.width / 2) / lineBounds!.width;
const value = Math.round(pct * length + min);
// Calculate new value
const { min, max, step, values } = this;
const length = max - min;
const pct = (newPositionXY + knobSize / 2) / lineSize;
let value = Math.round(pct * length + min);
if (vertical) value = max - value;
// Step
if (value === min || (value > min && Math.abs(value) % step === 0)) {
// Set new value & knob position
if (values[i] !== value) {
values[i] = value;
this.value = this.knobs === 1 ? value : [...values];
this.setKnobPostion(i);
// Step
if (value === min || (value > min && Math.abs(value) % step === 0)) {
// Set new value & knob position
if (values[i] !== value) {
values[i] = value;
this.value = this.knobs === 1 ? value : [...values];
this.setKnobPostion(i);
}
// Change line colors
this.setLineColors();
}
// Change line colors
this.setLineColors();
}

@@ -352,12 +563,4 @@ }

private endDrag = () => {
window.removeEventListener('mouseup', this.endDrag);
window.removeEventListener('touchend', this.endDrag);
window.removeEventListener('mousemove', this.drag);
window.removeEventListener('touchmove', this.drag);
};
private get label() {
const i = this.knob ? this.getKnobIndex(this.knob) : 0;
return this.labelElement(i) as HTMLElement;
return this.labelElement(this.knobIndex) as HTMLElement;
}

@@ -378,4 +581,4 @@

private setKnobElements() {
const { knobs, knobIndexes, knobsContainer } = this;
if (knobs && knobsContainer) {
const { knobIndexes, knobsContainer } = this;
if (knobsContainer) {
knobsContainer.innerHTML = '';

@@ -389,17 +592,28 @@ knobIndexes.map(i => {

private createKnobElement(knobIndex: number): HTMLDivElement {
const knobElement = document.createElement('div');
knobElement.tabIndex = knobIndex + 1;
knobElement.setAttribute('role', 'slider');
knobElement.setAttribute('part', `knob knob-${knobIndex}`);
knobElement.addEventListener('mousedown', this.startDrag);
if (knobIndex % 2) knobElement.classList.add('alternate');
knobElement.addEventListener('keydown', event => this.handleKnobKeyDownEvent(event, knobIndex));
return knobElement;
private createKnobElement(knobIndex: number) {
const isAltKnob = knobIndex % 2 ? 'alt-knob' : '';
const handleEvent = this as { handleEvent: EventListener };
return this.createElement(
html`
<div
role="slider"
part="knob ${isAltKnob} knob-${knobIndex}"
tabindex="${knobIndex + 1}"
@mousedown="${handleEvent}"
@touchstart="${handleEvent}"
@keydown="${handleEvent}"
></div>
`
);
}
private createKnobLabelElement(knobIndex: number): HTMLDivElement {
const labelElement = document.createElement('div');
labelElement.setAttribute('part', `label label-${knobIndex}`);
return labelElement;
private createKnobLabelElement(knobIndex: number) {
return this.createElement(
html`
<div part="label label-${knobIndex}">
<span part="label-value label-value-${knobIndex}"></span>
<div part="label-triangle label-triangle-${knobIndex}"></div>
</div>
`
);
}

@@ -413,3 +627,3 @@

if (neighborPrecisionOffset) neighboringValue += step - neighborPrecisionOffset;
return neighboringValue ? neighboringValue : min;
return neighboringValue || min;
}

@@ -423,3 +637,3 @@

if (neighborPrecisionOffset) neighboringValue -= neighborPrecisionOffset;
return neighboringValue ? neighboringValue : max;
return neighboringValue || max;
}

@@ -456,3 +670,3 @@

// Use the smallest number between max value, requested value, and the neighboring value.
other: Math.max(min, values[knobIndex] - step, this.getPrevNeighborValue(knobIndex))
other: Math.max(min, values[knobIndex] - step, this.getPrevNeighborValue(knobIndex)),
});

@@ -468,3 +682,3 @@ }

// Use the biggest number between min value and the neighboring value.
other: Math.max(min, this.getPrevNeighborValue(knobIndex))
other: Math.max(min, this.getPrevNeighborValue(knobIndex)),
});

@@ -482,3 +696,3 @@ }

// Use the smallest number between max value, requested value, and the neighboring value.
other: Math.min(max, values[knobIndex] + step, this.getNextNeighborValue(knobIndex))
other: Math.min(max, values[knobIndex] + step, this.getNextNeighborValue(knobIndex)),
});

@@ -494,9 +708,9 @@ }

// Use the smallest number between max value and the neighboring value.
other: Math.min(this.max, this.getNextNeighborValue(knobIndex))
other: Math.min(this.max, this.getNextNeighborValue(knobIndex)),
});
}
private handleKnobKeyDownEvent(event: KeyboardEvent, knobIndex: number) {
private keyMove(event: Event, knobIndex: number) {
let flag = false;
const key = event.key || event.keyCode;
const key = (event as KeyboardEvent).key || (event as KeyboardEvent).keyCode;
switch (key) {

@@ -510,3 +724,2 @@ case 'ArrowLeft':

break;
case 'ArrowRight':

@@ -519,3 +732,2 @@ case 39:

break;
case 'Home':

@@ -526,3 +738,2 @@ case 36:

break;
case 'End':

@@ -533,3 +744,2 @@ case 35:

break;
default:

@@ -549,4 +759,16 @@ break;

private get lineBounds() {
return this.getBounds(this.line!);
return this.getBounds(this.line as HTMLElement);
}
private createElement(template: TemplateResult) {
return (render(template, document.createElement('x')).parentNode as HTMLElement).firstElementChild as HTMLElement;
}
private sort() {
this.value = this.values.sort((a, b) => a - b);
}
private isSorted(values = this.values) {
return values.every((_, i, a) => !a[i - 1] || a[i] >= a[i - 1]);
}
}

@@ -556,3 +778,3 @@

interface HTMLElementTagNameMap {
'vcf-slider': VcfSlider;
'vcf-slider': Slider;
}

@@ -559,0 +781,0 @@ }

@@ -1,1 +0,3 @@

export { VcfSlider } from './src/vcf-slider.js';
import './src/vcf-slider.js';
export { Slider as VcfSlider } from './src/vcf-slider.js';
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc