We'll figure it out later.
This commit is contained in:
commit
c7c9c4809d
20
README.md
20
README.md
|
@ -1,20 +0,0 @@
|
||||||
This repository contains the boilerplate package which I use to start most of my smaller Typescript
|
|
||||||
projects. It is "primitive" in that it doesn't have hot module reloading or VSCode integration;
|
|
||||||
instead, it runs the build process using a watcher that enables fast iteration via the command line.
|
|
||||||
This boilerplate is focused on web components, primarily those build with [Lit](https://lit.dev).
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
Aside from the NodeJS dependencies, this script uses
|
|
||||||
[codespell](https://github.com/codespell-project/codespell), which is a Python-based spell checker
|
|
||||||
for comments and documentation.
|
|
||||||
|
|
||||||
## Decisions:
|
|
||||||
|
|
||||||
- TSC is still the best analyzer of Typescript's types.
|
|
||||||
- Scripting ESBuild gives you enormous power.
|
|
||||||
- Many of the commands have the `${NODE_RUNNER}` prefix. If you set `export NODE_RUNNER=bun`, you
|
|
||||||
can get a huge speedup in building and linting.
|
|
||||||
- Knip makes sure you're not importing anything you're not using.
|
|
||||||
- As this is a Lit-focused project, both Lit-Analyzer and WC-Analyzer are included
|
|
||||||
- xo
|
|
BIN
alphamod.png
BIN
alphamod.png
Binary file not shown.
Before Width: | Height: | Size: 198 B |
|
@ -2,21 +2,10 @@ import { LitElement, html, css } from "lit";
|
||||||
import { customElement } from "lit/decorators/custom-element.js";
|
import { customElement } from "lit/decorators/custom-element.js";
|
||||||
import { property } from "lit/decorators/property.js";
|
import { property } from "lit/decorators/property.js";
|
||||||
import { styleMap } from "lit/directives/style-map.js";
|
import { styleMap } from "lit/directives/style-map.js";
|
||||||
import { LitDragEvent, LitDraggable } from "./lit-draggable.js";
|
import { ref, createRef, Ref } from "lit/directives/ref.js";
|
||||||
|
import { LitDraggable } from "./lit-draggable.js";
|
||||||
export function bound(_target: unknown, key: string, descriptor: PropertyDescriptor): PropertyDescriptor {
|
import { LitDragEvent } from "./types.js";
|
||||||
if (typeof descriptor?.value !== "function") {
|
import { LitDragStart, LitDragEnd } from "./lit-events.js";
|
||||||
throw new Error("Only methods can be @bound.");
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
configurable: true,
|
|
||||||
get() {
|
|
||||||
const method = descriptor.value.bind(this);
|
|
||||||
Object.defineProperty(this, key, { value: method, configurable: true, writable: true });
|
|
||||||
return method;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@customElement("fridge-tile")
|
@customElement("fridge-tile")
|
||||||
export class FridgeTile extends LitElement {
|
export class FridgeTile extends LitElement {
|
||||||
|
@ -25,6 +14,13 @@ export class FridgeTile extends LitElement {
|
||||||
|
|
||||||
dragHandle: LitDraggable;
|
dragHandle: LitDraggable;
|
||||||
|
|
||||||
|
transform = {
|
||||||
|
rotate: Math.random() * 30 - 15,
|
||||||
|
scale: 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
handle: Ref<HTMLDivElement> = createRef();
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
|
@ -52,6 +48,9 @@ export class FridgeTile extends LitElement {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: white;
|
background: white;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
transition-property: transform;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
transition-timing-function: ease-in-out; //other options are ease
|
||||||
}
|
}
|
||||||
|
|
||||||
.word.dragging {
|
.word.dragging {
|
||||||
|
@ -64,17 +63,35 @@ export class FridgeTile extends LitElement {
|
||||||
super();
|
super();
|
||||||
this.dragHandle = new LitDraggable(this);
|
this.dragHandle = new LitDraggable(this);
|
||||||
this.onDragEnd = this.onDragEnd.bind(this);
|
this.onDragEnd = this.onDragEnd.bind(this);
|
||||||
this.addEventListener("lit-drag-end", this.onDragEnd);
|
this.onDragStart = this.onDragStart.bind(this);
|
||||||
|
this.addEventListener(LitDragEnd.eventName, this.onDragEnd);
|
||||||
|
this.addEventListener(LitDragStart.eventName, this.onDragStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDragEnd(ev: LitDragEvent) {
|
onDragEnd(ev: LitDragEvent) {
|
||||||
|
this.transform = { scale: 1.0, rotate: Math.random() * 30 - 15 };
|
||||||
|
this.handle.value!.style.setProperty(
|
||||||
|
"transform",
|
||||||
|
`scale(${+this.transform.scale}) rotate(${+this.transform.rotate}deg)`
|
||||||
|
);
|
||||||
this.style.setProperty("top", `${+ev.offsetY}px`);
|
this.style.setProperty("top", `${+ev.offsetY}px`);
|
||||||
this.style.setProperty("left", `${+ev.offsetX}px`);
|
this.style.setProperty("left", `${+ev.offsetX}px`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDragStart(_ev: LitDragEvent) {
|
||||||
|
this.transform = { scale: 1.3, rotate: Math.random() * 30 - 15 };
|
||||||
|
this.handle.value!.style.setProperty(
|
||||||
|
"transform",
|
||||||
|
`scale(${+this.transform.scale}) rotate(${+this.transform.rotate}deg)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const styles = styleMap({ width: `${this.word.length}ch` });
|
const styles = {
|
||||||
return html`<div part="word" style="${styles}" class="word">${this.word}</div>`;
|
width: `${this.word.length}ch`,
|
||||||
|
transform: `rotate(${this.transform.rotate}deg)`,
|
||||||
|
};
|
||||||
|
return html`<div part="word ${ref(this.handle)} " style="${styleMap(styles)}" class="word">${this.word}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { ReactiveControllerHost, ReactiveController } from "lit";
|
import { ReactiveControllerHost, ReactiveController } from "lit";
|
||||||
|
import { LitDragStart, LitDragging, LitDragEnd } from "./lit-events.js";
|
||||||
|
|
||||||
export type DragBoundaries = {
|
export type DragBoundaries = {
|
||||||
top: number;
|
top: number;
|
||||||
|
@ -15,37 +16,6 @@ export type DragAxes = "x" | "y" | "both" | "none";
|
||||||
|
|
||||||
export type DragBounds = HTMLElement | Partial<DragBoundaries> | "parent" | "body" | (string & Record<never, never>);
|
export type DragBounds = HTMLElement | Partial<DragBoundaries> | "parent" | "body" | (string & Record<never, never>);
|
||||||
|
|
||||||
export interface LitDragEvent extends Event {
|
|
||||||
offsetX: number;
|
|
||||||
offsetY: number;
|
|
||||||
node: HTMLElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeLitDragEvent(name: string): LitDragEvent {
|
|
||||||
class _LitDragEvent extends Event implements LitDragEvent {
|
|
||||||
static readonly eventName = name;
|
|
||||||
offsetX: number = 0;
|
|
||||||
offsetY: number = 0;
|
|
||||||
node: HTMLElement;
|
|
||||||
// container: HTMLElement;
|
|
||||||
constructor(source: LitDraggable) {
|
|
||||||
super(_LitDragEvent.eventName, { bubbles: true, composed: true });
|
|
||||||
this.offsetX = source.translateX;
|
|
||||||
this.offsetY = source.translateY;
|
|
||||||
this.node = source.host;
|
|
||||||
// this.container = source.container;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _LitDragEvent as unknown as LitDragEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LitDragStart = makeLitDragEvent("lit-drag-start");
|
|
||||||
|
|
||||||
export const LitDragging = makeLitDragEvent("lit-dragging");
|
|
||||||
|
|
||||||
export const LitDragEnd: LitDragEvent = makeLitDragEvent("lit-drag-end");
|
|
||||||
|
|
||||||
const defaultOptions: DragOptions = {
|
const defaultOptions: DragOptions = {
|
||||||
preventSelect: true,
|
preventSelect: true,
|
||||||
};
|
};
|
||||||
|
@ -61,6 +31,9 @@ export class LitDraggable implements ReactiveController {
|
||||||
initialX = 0;
|
initialX = 0;
|
||||||
initialY = 0;
|
initialY = 0;
|
||||||
|
|
||||||
|
handleOffsetX = 0;
|
||||||
|
handleOffsetY = 0;
|
||||||
|
|
||||||
dragging = false;
|
dragging = false;
|
||||||
|
|
||||||
currentSelect?: string;
|
currentSelect?: string;
|
||||||
|
@ -107,8 +80,12 @@ export class LitDraggable implements ReactiveController {
|
||||||
document.body.style.userSelect = "none";
|
document.body.style.userSelect = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hostRect = this.host.getBoundingClientRect();
|
||||||
|
|
||||||
this.initialX = ev.clientX;
|
this.initialX = ev.clientX;
|
||||||
this.initialY = ev.clientY;
|
this.initialY = ev.clientY;
|
||||||
|
this.handleOffsetX = this.initialX - hostRect.left;
|
||||||
|
this.handleOffsetY = this.initialY - hostRect.top;
|
||||||
this.host.dataset.litDrag = "true";
|
this.host.dataset.litDrag = "true";
|
||||||
this.host.dispatchEvent(new LitDragStart(this));
|
this.host.dispatchEvent(new LitDragStart(this));
|
||||||
}
|
}
|
||||||
|
@ -142,8 +119,9 @@ export class LitDraggable implements ReactiveController {
|
||||||
document.body.style.userSelect = this.currentSelect;
|
document.body.style.userSelect = this.currentSelect;
|
||||||
this.currentSelect = undefined;
|
this.currentSelect = undefined;
|
||||||
}
|
}
|
||||||
this.initialX = this.translateX;
|
|
||||||
this.initialY = this.translateY;
|
this.initialX = ev.clientX - this.handleOffsetX;
|
||||||
|
this.initialY = ev.clientY - this.handleOffsetY;
|
||||||
this.host.dispatchEvent(new LitDragEnd(this));
|
this.host.dispatchEvent(new LitDragEnd(this));
|
||||||
this.host.style.removeProperty("transform");
|
this.host.style.removeProperty("transform");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { LitDraggable, LitDragEvent } from "./types";
|
||||||
|
|
||||||
|
export class LitDragStart extends Event implements LitDragEvent {
|
||||||
|
static readonly eventName = "lit-drag-start";
|
||||||
|
offsetX: number = 0;
|
||||||
|
offsetY: number = 0;
|
||||||
|
node: HTMLElement;
|
||||||
|
// container: HTMLElement;
|
||||||
|
constructor(source: LitDraggable) {
|
||||||
|
super(LitDragStart.eventName, { bubbles: true, composed: true });
|
||||||
|
this.offsetX = source.translateX;
|
||||||
|
this.offsetY = source.translateY;
|
||||||
|
this.node = source.host;
|
||||||
|
// this.container = source.container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LitDragging extends Event implements LitDragEvent {
|
||||||
|
static readonly eventName = "lit-dragging";
|
||||||
|
offsetX: number = 0;
|
||||||
|
offsetY: number = 0;
|
||||||
|
node: HTMLElement;
|
||||||
|
// container: HTMLElement;
|
||||||
|
constructor(source: LitDraggable) {
|
||||||
|
super(LitDragging.eventName, { bubbles: true, composed: true });
|
||||||
|
this.offsetX = source.translateX;
|
||||||
|
this.offsetY = source.translateY;
|
||||||
|
this.node = source.host;
|
||||||
|
// this.container = source.container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LitDragEnd extends Event implements LitDragEvent {
|
||||||
|
static readonly eventName = "lit-drag-end";
|
||||||
|
offsetX: number = 0;
|
||||||
|
offsetY: number = 0;
|
||||||
|
node: HTMLElement;
|
||||||
|
// container: HTMLElement;
|
||||||
|
constructor(source: LitDraggable) {
|
||||||
|
super(LitDragEnd.eventName, { bubbles: true, composed: true });
|
||||||
|
this.offsetX = source.initialX;
|
||||||
|
this.offsetY = source.initialY;
|
||||||
|
this.node = source.host;
|
||||||
|
// this.container = source.container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface GlobalEventHandlersEventMap {
|
||||||
|
[LitDragStart.eventName]: LitDragStart;
|
||||||
|
[LitDragging.eventName]: LitDragging;
|
||||||
|
[LitDragEnd.eventName]: LitDragEnd;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
// Copyright (c) 2012 Elf M. Sternberg
|
||||||
|
//
|
||||||
|
// Much of the code here I would never have understood if it hadn't
|
||||||
|
// been for the patient work of Caleb Helbling
|
||||||
|
// (http://www.propulsionjs.com/), as well as the Wikipedia pages for
|
||||||
|
// the Separating Axis Theorem. It took me a week to wrap my head
|
||||||
|
// around these ideas.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
class Vector {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
add: (v2: Vector) {
|
||||||
|
return new Vector(this.x + v2.x, this.y + v2.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
add: function (v1, v2) {
|
||||||
|
return {
|
||||||
|
x: v1.x + v2.x,
|
||||||
|
y: v1.y + v2.y,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
scalar: function (v, s) {
|
||||||
|
return {
|
||||||
|
x: v.x * s,
|
||||||
|
y: v.y * s,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
dot: function (v1, v2) {
|
||||||
|
return v1.x * v2.x + v1.y * v2.y;
|
||||||
|
},
|
||||||
|
magnitude2: function (v) {
|
||||||
|
var x, y;
|
||||||
|
x = v.x;
|
||||||
|
y = v.y;
|
||||||
|
return x * x + y * y;
|
||||||
|
},
|
||||||
|
magnitude: function (v) {
|
||||||
|
return Math.sqrt(Math.vector.magnitude2(v));
|
||||||
|
},
|
||||||
|
normalize: function (v) {
|
||||||
|
var mag;
|
||||||
|
mag = Math.vector.magnitude(v);
|
||||||
|
return {
|
||||||
|
x: v.x / mag,
|
||||||
|
y: v.y / mag,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
leftNormal: function (v) {
|
||||||
|
return {
|
||||||
|
x: -v.y,
|
||||||
|
y: v.x,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.colliding = function (shape1, shape2) {
|
||||||
|
var axes, axes1, axes2, axis, genAxes, genProjection, j, len, proj1, proj2;
|
||||||
|
genAxes = function (shape) {
|
||||||
|
var axis, i, j, ref, results;
|
||||||
|
if (shape.length < 3) {
|
||||||
|
throw "Cannot handle non-polygons";
|
||||||
|
}
|
||||||
|
axis = function (shape, pi) {
|
||||||
|
var edge, p1, p2;
|
||||||
|
p1 = shape[pi];
|
||||||
|
p2 = shape[pi === shape.length - 1 ? 0 : pi + 1];
|
||||||
|
edge = {
|
||||||
|
x: p1.x - p2.x,
|
||||||
|
y: p1.y - p2.y,
|
||||||
|
};
|
||||||
|
return Math.vector.normalize(Math.vector.leftNormal(edge));
|
||||||
|
};
|
||||||
|
results = [];
|
||||||
|
for (i = j = 0, ref = shape.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
|
||||||
|
results.push(axis(shape, i));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
genProjection = function (shape, axis) {
|
||||||
|
var i, j, max, min, p, ref;
|
||||||
|
min = Math.vector.dot(axis, shape[0]);
|
||||||
|
max = min;
|
||||||
|
for (i = j = 1, ref = shape.length; 1 <= ref ? j < ref : j > ref; i = 1 <= ref ? ++j : --j) {
|
||||||
|
p = Math.vector.dot(axis, shape[i]);
|
||||||
|
if (p < min) {
|
||||||
|
min = p;
|
||||||
|
}
|
||||||
|
if (p > max) {
|
||||||
|
max = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
axes1 = genAxes(shape1);
|
||||||
|
axes2 = genAxes(shape2);
|
||||||
|
axes = axes1.concat(axes2);
|
||||||
|
for (j = 0, len = axes.length; j < len; j++) {
|
||||||
|
axis = axes[j];
|
||||||
|
proj1 = genProjection(shape1, axis);
|
||||||
|
proj2 = genProjection(shape2, axis);
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
(proj1.min >= proj2.min && proj1.min <= proj2.max) ||
|
||||||
|
(proj1.max >= proj2.min && proj1.max <= proj2.max) ||
|
||||||
|
(proj2.min >= proj1.min && proj2.min <= proj1.max) ||
|
||||||
|
(proj2.max >= proj1.min && proj2.max <= proj1.max)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}).call(this);
|
|
@ -0,0 +1,13 @@
|
||||||
|
export interface LitDraggable {
|
||||||
|
translateX: number;
|
||||||
|
translateY: number;
|
||||||
|
initialX: number;
|
||||||
|
initialY: number;
|
||||||
|
host: HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LitDragEvent extends Event {
|
||||||
|
offsetX: number;
|
||||||
|
offsetY: number;
|
||||||
|
node: HTMLElement;
|
||||||
|
}
|
Loading…
Reference in New Issue