Rollup build

This commit is contained in:
Jake Archibald
2020-11-19 11:00:23 +00:00
parent dfee848a39
commit 56e10b3aa2
340 changed files with 37866 additions and 19153 deletions

View File

@@ -0,0 +1,167 @@
import PointerTracker from 'pointer-tracker';
import * as style from './style.css';
import 'add-css:./style.css';
const RETARGETED_EVENTS = ['focus', 'blur'];
const UPDATE_EVENTS = ['input', 'change'];
const REFLECTED_PROPERTIES = [
'name',
'min',
'max',
'step',
'value',
'disabled',
];
const REFLECTED_ATTRIBUTES = [
'name',
'min',
'max',
'step',
'value',
'disabled',
];
function getPrescision(value: string): number {
const afterDecimal = value.split('.')[1];
return afterDecimal ? afterDecimal.length : 0;
}
class RangeInputElement extends HTMLElement {
private _input: HTMLInputElement;
private _valueDisplay?: HTMLDivElement;
private _ignoreChange = false;
static get observedAttributes() {
return REFLECTED_ATTRIBUTES;
}
constructor() {
super();
this._input = document.createElement('input');
this._input.type = 'range';
this._input.className = style.input;
const tracker = new PointerTracker(this._input, {
start: (): boolean => {
if (tracker.currentPointers.length !== 0) return false;
this._input.classList.add(style.touchActive);
return true;
},
end: () => {
this._input.classList.remove(style.touchActive);
},
});
for (const event of RETARGETED_EVENTS) {
this._input.addEventListener(event, this._retargetEvent, true);
}
for (const event of UPDATE_EVENTS) {
this._input.addEventListener(event, this._update, true);
}
}
connectedCallback() {
if (this.contains(this._input)) return;
this.innerHTML =
`<div class="${style.thumbWrapper}">` +
`<div class="${style.thumb}"></div>` +
`<div class="${style.valueDisplay}"></div>` +
'</div>';
this.insertBefore(this._input, this.firstChild);
this._valueDisplay = this.querySelector(
'.' + style.valueDisplay,
) as HTMLDivElement;
// Set inline styles (this is useful when used with frameworks which might clear inline styles)
this._update();
}
get labelPrecision(): string {
return this.getAttribute('label-precision') || '';
}
set labelPrecision(precision: string) {
this.setAttribute('label-precision', precision);
}
attributeChangedCallback(
name: string,
oldValue: string,
newValue: string | null,
) {
if (this._ignoreChange) return;
if (newValue === null) {
this._input.removeAttribute(name);
} else {
this._input.setAttribute(name, newValue);
}
this._reflectAttributes();
this._update();
}
private _retargetEvent = (event: Event) => {
event.stopImmediatePropagation();
const retargetted = new Event(event.type, event);
this.dispatchEvent(retargetted);
};
private _update = () => {
// Not connected?
if (!this._valueDisplay) return;
const value = Number(this.value) || 0;
const min = Number(this.min) || 0;
const max = Number(this.max) || 100;
const labelPrecision =
Number(this.labelPrecision) || getPrescision(this.step) || 0;
const percent = (100 * (value - min)) / (max - min);
const displayValue = labelPrecision
? value.toFixed(labelPrecision)
: Math.round(value).toString();
this._valueDisplay!.textContent = displayValue;
this.style.setProperty('--value-percent', percent + '%');
this.style.setProperty('--value-width', '' + displayValue.length);
};
private _reflectAttributes() {
this._ignoreChange = true;
for (const attributeName of REFLECTED_ATTRIBUTES) {
if (this._input.hasAttribute(attributeName)) {
this.setAttribute(
attributeName,
this._input.getAttribute(attributeName)!,
);
} else {
this.removeAttribute(attributeName);
}
}
this._ignoreChange = false;
}
}
interface RangeInputElement {
name: string;
min: string;
max: string;
step: string;
value: string;
disabled: boolean;
}
for (const prop of REFLECTED_PROPERTIES) {
Object.defineProperty(RangeInputElement.prototype, prop, {
get() {
return this._input[prop];
},
set(val) {
this._input[prop] = val;
this._reflectAttributes();
this._update();
},
});
}
export default RangeInputElement;
customElements.define('range-input', RangeInputElement);

View File

@@ -0,0 +1,9 @@
declare module 'preact' {
namespace createElement.JSX {
interface IntrinsicElements {
'range-input': HTMLAttributes;
}
}
}
export {};

View File

@@ -0,0 +1,98 @@
range-input {
position: relative;
display: flex;
height: 18px;
width: 130px;
margin: 2px;
font: inherit;
line-height: 16px;
overflow: visible;
}
/* Disabled inputs are greyed out */
range-input[disabled] {
filter: grayscale(1);
}
range-input::before {
content: '';
display: block;
position: absolute;
top: 8px;
left: 0;
width: 100%;
height: 2px;
border-radius: 1px;
box-shadow: 0 -0.5px 0 rgba(0, 0, 0, 0.3),
inset 0 0.5px 0 rgba(255, 255, 255, 0.2), 0 0.5px 0 rgba(255, 255, 255, 0.3);
background: linear-gradient(#34b9eb, #218ab1) 0 / var(--value-percent, 0%)
100% no-repeat #eee;
}
.input {
position: relative;
width: 100%;
padding: 0;
margin: 0;
opacity: 0;
}
.thumb {
pointer-events: none;
position: absolute;
bottom: 3px;
left: var(--value-percent, 0%);
margin-left: -6px;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><circle cx="5" cy="5" r="1" fill="%235D509E" /></svg>')
center no-repeat #34b9eb;
border-radius: 50%;
width: 12px;
height: 12px;
box-shadow: 0 0.5px 2px rgba(0, 0, 0, 0.3);
}
.thumb-wrapper {
position: absolute;
left: 6px;
right: 6px;
bottom: 0;
height: 0;
overflow: visible;
}
.value-display {
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="62" fill="none"><path fill="%2334B9EB" d="M27.3 27.3C25 29.6 17 35.8 17 43v3c0 3 2.5 5 3.2 5.8a6 6 0 1 1-8.5 0C12.6 51 15 49 15 46v-3c0-7.2-8-13.4-10.3-15.7A16 16 0 0 1 16 0a16 16 0 0 1 11.3 27.3z"/><circle cx="16" cy="56" r="1" fill="%235D509E"/></svg>')
top center no-repeat;
position: absolute;
box-sizing: border-box;
left: var(--value-percent, 0%);
bottom: 3px;
width: 32px;
height: 62px;
text-align: center;
padding: 8px 3px 0;
margin: 0 0 0 -16px;
filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3));
transform-origin: 50% 90%;
opacity: 0.0001;
transform: scale(0.2);
color: #fff;
font: inherit;
font-size: calc(100% - var(--value-width, 3) / 5 * 0.2em);
text-overflow: clip;
text-shadow: 0 -0.5px 0 rgba(0, 0, 0, 0.4);
transition: all 200ms ease;
transition-property: opacity, transform;
will-change: transform;
pointer-events: none;
overflow: hidden;
}
.touch-active + .thumb-wrapper .value-display {
opacity: 1;
transform: scale(1);
}
.touch-active + .thumb-wrapper .thumb {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

View File

@@ -0,0 +1,55 @@
import { h, Component } from 'preact';
import * as style from './style.css';
import 'add-css:./style.css';
import RangeInputElement from './custom-els/RangeInput';
import './custom-els/RangeInput';
import { linkRef } from 'shared/initial-app/util';
interface Props extends preact.JSX.HTMLAttributes {}
interface State {}
export default class Range extends Component<Props, State> {
rangeWc?: RangeInputElement;
private onTextInput = (event: Event) => {
const input = event.target as HTMLInputElement;
const value = input.value.trim();
if (!value) return;
this.rangeWc!.value = input.value;
this.rangeWc!.dispatchEvent(
new InputEvent('input', {
bubbles: event.bubbles,
}),
);
};
render(props: Props) {
const { children, ...otherProps } = props;
const { value, min, max, step } = props;
return (
<label class={style.range}>
<span class={style.labelText}>{children}</span>
{/* On interaction, Safari gives focus to the first element in the label, so the
<range-input> is deliberately first. */}
<div class={style.rangeWcContainer}>
<range-input
ref={linkRef(this, 'rangeWc')}
class={style.rangeWc}
{...otherProps}
/>
</div>
<input
type="number"
class={style.textInput}
value={value}
min={min}
max={max}
step={step}
onInput={this.onTextInput}
/>
</label>
);
}
}

View File

@@ -0,0 +1,55 @@
.range {
position: relative;
z-index: 0;
display: grid;
grid-template-columns: 1fr auto;
}
.label-text {
color: #fff; /* TEMP */
}
.range-wc-container {
position: relative;
z-index: 1;
grid-row: 2 / 3;
grid-column: 1 / 3;
}
.range-wc {
width: 100%;
}
.text-input {
grid-row: 1 / 2;
grid-column: 2 / 3;
text-align: right;
background: transparent;
color: inherit;
font: inherit;
border: none;
padding: 2px 5px;
box-sizing: border-box;
text-decoration: underline;
text-decoration-style: dotted;
text-underline-position: under;
width: 48px;
position: relative;
left: 5px;
&:focus {
background: #fff;
color: #000;
}
/* Remove the number controls */
-moz-appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-moz-appearance: none;
-webkit-appearance: none;
margin: 0;
}
}