Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 0 additions & 2 deletions client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ node_modules

# Source files
src/lib
src/framework

# Public
public/js
public/css
public/static/config.js
public/static/service-worker-assets.js
public/audio
1 change: 0 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"update": "npm update && run-p install:*",
"install:pkg": "node ./build/bundle.js",
"install:brixi": "brixi && mv ./brixi/brixi.css ./src/styles/brixi.css && rmdir ./brixi",
"install:ui": "install-ui --framework=./src/framework --audio=./public/audio",
"build": "run-p build:*",
"build:js": "twist --src=./src --outDir=./public/js --type=esbuild",
"build:css": "cssmonster",
Expand Down
Binary file added client/public/audio/activate.wav
Binary file not shown.
Binary file added client/public/audio/camera.wav
Binary file not shown.
Binary file added client/public/audio/deactivate.wav
Binary file not shown.
Binary file added client/public/audio/error-alert.wav
Binary file not shown.
Binary file added client/public/audio/error.wav
Binary file not shown.
Binary file added client/public/audio/mouseclick.wav
Binary file not shown.
Binary file added client/public/audio/mouseover.wav
Binary file not shown.
Binary file added client/public/audio/notification.wav
Binary file not shown.
Binary file added client/public/audio/snackbar.wav
Binary file not shown.
Binary file added client/public/audio/success.wav
Binary file not shown.
Binary file added client/public/audio/warning.wav
Binary file not shown.
4 changes: 4 additions & 0 deletions client/src/components/window/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ export default class Window extends SuperComponent<IWindow>{
let diffY = bounds.y - y - this.localY;
this.x -= diffX;
this.y -= diffY;
if (this.y < 28) this.y = 28;
if (this.y > window.innerHeight - 28) this.y = window.innerHeight - 28;
if (this.x < 0) this.x = 0;
if (this.x > window.innerWidth - bounds.width) this.x = window.innerWidth - bounds.width;
this.style.transform = `translate(${this.x}px, ${this.y}px)`;
}
}
Expand Down
7 changes: 6 additions & 1 deletion client/src/components/window/windows/dice-box/dice-box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,18 @@ export default class DiceBox extends SuperComponent<IDiceBox>{
}

private handleKeypress:EventListener = (e:Event) => {
e.stopImmediatePropagation()
if (e instanceof KeyboardEvent){
if (e.key.toLowerCase() === "enter"){
this.doRoll();
}
}
}

private noopEvent:EventListener = (e:Event) => {
e.stopImmediatePropagation();
}

override async render() {
const view = html`
<dice-log>
Expand All @@ -81,7 +86,7 @@ export default class DiceBox extends SuperComponent<IDiceBox>{
})}
</dice-log>
<div class="w-full" flex="items-center row nowrap">
<input autofocus type="text" placeholder="Dice codes" @keypress=${this.handleKeypress}>
<input autofocus type="text" placeholder="Dice codes" @keypress=${this.handleKeypress} @keydown=${this.noopEvent} @keyup=${this.noopEvent}>
</div>
<p class="block font-xs font-grey-600 dark:font-grey-400 px-0.125">Example: 1d20 + 1d6 + 4</p>
`;
Expand Down
29 changes: 29 additions & 0 deletions client/src/components/window/windows/fog-brush/fog-brush.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,32 @@ fog-brush-circle{
pointer-events: none;
cursor: crosshair;
}

rect-preview{
display: inline-block;
position: fixed;
border: 1px solid yellow;
pointer-events: none;
z-index: 1000;
transform-origin: top left;

&.hidden {
opacity: 0;
visibility: hidden;
}
}
.poly-preview{
display: inline-block;
position: fixed;
pointer-events: none;
z-index: 1000;
width: 100vw;
height: 100vh;
top: 0;
left: 0;

&.hidden {
opacity: 0;
visibility: hidden;
}
}
205 changes: 128 additions & 77 deletions client/src/components/window/windows/fog-brush/fog-brush.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,30 @@ import { publish } from "@codewithkyle/pubsub";
import room from "room";
import TabletopPage from "pages/tabletop-page/tabletop-page";

type Point = {
x: number,
y: number,
}
interface IFogBrush { }
export default class FogBrush extends SuperComponent<IFogBrush>{
export default class FogBrush extends SuperComponent<IFogBrush> {
private painting: boolean;
private mode: "eraser" | "fill";
private brushSize: number;
private fogBrushCircle: HTMLElement;
private tabletop: TabletopPage;
private mode: "rect" | "poly";
private points: Array<Point>;
private rectPreviewEl: HTMLElement;
private polyPreviewEl: SVGElement;
private mouse: Point;

constructor() {
super();
this.painting = false;
this.mode = "eraser";
this.brushSize = 2;
this.mode = "rect";
this.points = [];
this.mouse = { x: 0, y: 0 };
this.rectPreviewEl = document.createElement("rect-preview");
this.polyPreviewEl = document.createElementNS('http://www.w3.org/2000/svg', "svg");
this.polyPreviewEl.classList.add("poly-preview");
this.polyPreviewEl.setAttribute("viewBox", `0 0, ${window.innerWidth} ${window.innerHeight}`);
}

override async connected() {
Expand All @@ -27,34 +38,94 @@ export default class FogBrush extends SuperComponent<IFogBrush>{
this.tabletop.addEventListener("mousedown", this.onMouseDown);
this.tabletop.addEventListener("mouseup", this.onMouseUp);
this.tabletop.addEventListener("mousemove", this.onMouseMove);
window.addEventListener("wheel", this.onMouseWheel, { passive: true });
window.addEventListener("keydown", this.onKeyDown);
this.render();
this.fogBrushCircle = document.createElement("fog-brush-circle");
document.body.appendChild(this.fogBrushCircle);
this.scaleBrush();
publish("tabletop", "cursor:draw");
}

disconnected(): void {
if (this.fogBrushCircle) {
this.fogBrushCircle.remove();
}
this.tabletop.removeEventListener("mousedown", this.onMouseDown);
this.tabletop.removeEventListener("mouseup", this.onMouseUp);
this.tabletop.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("wheel", this.onMouseWheel);
window.removeEventListener("keydown", this.onKeyDown);
this.rectPreviewEl.remove();
this.polyPreviewEl.remove();
publish("tabletop", "cursor:move");
}

private scaleBrush() {
if (this.fogBrushCircle) {
if (this.brushSize > 2) {
this.fogBrushCircle.style.width = `${(this.brushSize - 2) * room.gridSize}px`;
this.fogBrushCircle.style.height = `${(this.brushSize - 2) * room.gridSize}px`;
} else {
this.fogBrushCircle.style.width = `${(this.brushSize - 1) * room.gridSize}px`;
this.fogBrushCircle.style.height = `${(this.brushSize - 1) * room.gridSize}px`;
private updateRectPreview(x: number, y: number) {
if (!this.painting) {
this.rectPreviewEl.classList.add("hidden");
}
if (this.mode === "rect") {
if (!this.rectPreviewEl?.isConnected) {
document.body.appendChild(this.rectPreviewEl);
}
this.rectPreviewEl.classList.remove("hidden");
const width = Math.abs(x - this.points[0].x);
const height = Math.abs(y - this.points[0].y);
this.rectPreviewEl.style.top = `${this.points[0].y}px`;
this.rectPreviewEl.style.left = `${this.points[0].x}px`;
this.rectPreviewEl.style.width = `${width}px`;
this.rectPreviewEl.style.height = `${height}px`;
let transform = "";
if (x > this.points[0].x && y < this.points[0].y) {
transform += `scaleY(-1) `;
}
if (x < this.points[0].x) {
transform = `scaleX(-1) `;
if (y < this.points[0].y) {
transform += `scaleY(-1) `;
}
}
this.rectPreviewEl.style.transform = transform;
}
}

private updatePolyPreview() {
if (!this.painting) {
this.polyPreviewEl.classList.add("hidden");
}
if (this.mode === "poly") {
if (!this.polyPreviewEl?.isConnected) {
document.body.appendChild(this.polyPreviewEl);
}
this.polyPreviewEl.innerHTML = "";
this.polyPreviewEl.classList.remove("hidden");
for (let i = 0; i < this.points.length - 1; i++) {
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', this.points[i].x.toString());
line.setAttribute('y1', this.points[i].y.toString());
line.setAttribute('x2', this.points[i + 1].x.toString());
line.setAttribute('y2', this.points[i + 1].y.toString());
line.setAttribute('stroke', 'yellow');
line.setAttribute('stroke-width', "1");
this.polyPreviewEl.appendChild(line);
}
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', this.points[this.points.length-1].x.toString());
line.setAttribute('y1', this.points[this.points.length-1].y.toString());
line.setAttribute('x2', this.mouse.x.toString());
line.setAttribute('y2', this.mouse.y.toString());
line.setAttribute('stroke', 'yellow');
line.setAttribute('stroke-width', "1");
this.polyPreviewEl.appendChild(line);
console.log("line", line);
}
}

private onKeyDown = (e: KeyboardEvent) => {
const key = e.key.toLowerCase();
if (key === "enter" && this.painting && this.mode === "poly"){
this.polyPreviewEl.classList.add("hidden");
if (this.points.length >= 2) {
publish("fog", {
type: "poly",
points: this.points,
});
}
this.points = [];
this.painting = false;
}
}

Expand All @@ -63,80 +134,60 @@ export default class FogBrush extends SuperComponent<IFogBrush>{
this.painting = true;
const x = e.clientX;
const y = e.clientY;
publish("fog", {
type: this.mode,
data: {
x: x,
y: y,
brushSize: this.brushSize,
},
});
this.points.push({ x, y });
if (this.mode === "poly") {
this.updatePolyPreview();
}
}
}

private onMouseUp = (e: MouseEvent) => {
this.painting = false;
}

private onMouseWheel = (e: WheelEvent) => {
if (this.fogBrushCircle) {
let zoom = 1;
if (sessionStorage.getItem("zoom")) {
zoom = parseFloat(sessionStorage.getItem("zoom"));
}
if (this.brushSize > 2) {
this.fogBrushCircle.style.transform = `matrix(${zoom}, 0, 0, ${zoom}, ${e.clientX - (room.gridSize * (this.brushSize - 2) * 0.5)}, ${e.clientY - (room.gridSize * (this.brushSize - 2) * 0.5)})`;
} else {
this.fogBrushCircle.style.transform = `matrix(${zoom}, 0, 0, ${zoom}, ${e.clientX - (room.gridSize * (this.brushSize - 1) * 0.5)}, ${e.clientY - (room.gridSize * (this.brushSize - 1) * 0.5)})`;
const x = e.clientX;
const y = e.clientY;
if (this.painting) {
if (this.mode === "rect") {
this.rectPreviewEl.classList.add("hidden");
this.points.push({ x, y });
if (this.points.length === 2) {
publish("fog", {
type: "rect",
points: this.points,
});
}
this.points = [];
this.painting = false;
}
}
}

private onMouseMove = (e: MouseEvent) => {
if (this.fogBrushCircle) {
let zoom = 1;
if (sessionStorage.getItem("zoom")) {
zoom = parseFloat(sessionStorage.getItem("zoom"));
}
if (this.brushSize > 2) {
this.fogBrushCircle.style.transform = `matrix(${zoom}, 0, 0, ${zoom}, ${e.clientX - (room.gridSize * (this.brushSize - 2) * 0.5)}, ${e.clientY - (room.gridSize * (this.brushSize - 2) * 0.5)})`;
} else {
this.fogBrushCircle.style.transform = `matrix(${zoom}, 0, 0, ${zoom}, ${e.clientX - (room.gridSize * (this.brushSize - 1) * 0.5)}, ${e.clientY - (room.gridSize * (this.brushSize - 1) * 0.5)})`;
}
}
const x = e.clientX;
const y = e.clientY;
this.mouse = { x, y };
if (this.painting) {
const x = e.clientX;
const y = e.clientY;
publish("fog", {
type: this.mode,
data: {
x: x,
y: y,
brushSize: this.brushSize,
},
});
switch(this.mode){
case "rect":
this.updateRectPreview(x, y);
break;
case "poly":
this.updatePolyPreview();
break;
}
}
}

render() {
const view = html`
<group-button-component
class="mb-1"
data-buttons='[{"label":"Eraser","id":"eraser"},{"label":"Fill","id":"fill"}]'
class="mb-0.5"
data-buttons='[{"label":"Rectangle","id":"rect"},{"label":"Polygon","id":"poly"}]'
data-active="${this.mode}"
@change=${(e) => {
this.mode = e.detail.id;
}}
this.mode = e.detail.id;
this.render();
}}
></group-button-component>
<select-component
data-label="Brush Size"
data-options='[{"label":"Small","value":"2"},{"label":"Medium","value":"4"},{"label":"Large","value":"8"}]'
data-value="${this.brushSize}"
@change=${(e) => {
this.brushSize = parseInt(e.detail.value);
this.scaleBrush();
}}
></select-component>
${this.mode === "poly" ? html`<p class="font-xs px-0.5">Press 'enter' to complete path.</p>` : ""}
`;
render(view, this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,23 @@ class MonsterInfoTable extends SuperComponent<IMonsterInfoTable>{
this.set(updatedModal, true);
}

private noopEvent: EventListener = (e:Event) => {
e.stopImmediatePropagation();
}

override render(): void {
const view = html`
<h4 class="block w-full font-medium font-sm font-grey-800 dark:font-grey-300 pl-0.125">${unsafeHTML(this.model.label)}</h4>
${this.model.rows.map((row, index) => {
return html`
<table-row data-index="{{ index }}">
<row-title>
<input name="${this.model.name}-name" required .value="${row.name}" placeholder="Name" @input=${this.updateName} data-index="${index}">
<input @keydown=${this.noopEvent} @keyup=${this.noopEvent} name="${this.model.name}-name" required .value="${row.name}" placeholder="Name" @input=${this.updateName} data-index="${index}">
<button aria-label="Delete ${row.name}" tooltip class="delete" type="button" @click=${this.deleteRow} data-index="${index}">
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M296 432h16a8 8 0 0 0 8-8V152a8 8 0 0 0-8-8h-16a8 8 0 0 0-8 8v272a8 8 0 0 0 8 8zm-160 0h16a8 8 0 0 0 8-8V152a8 8 0 0 0-8-8h-16a8 8 0 0 0-8 8v272a8 8 0 0 0 8 8zM440 64H336l-33.6-44.8A48 48 0 0 0 264 0h-80a48 48 0 0 0-38.4 19.2L112 64H8a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h24v368a48 48 0 0 0 48 48h288a48 48 0 0 0 48-48V96h24a8 8 0 0 0 8-8V72a8 8 0 0 0-8-8zM171.2 38.4A16.1 16.1 0 0 1 184 32h80a16.1 16.1 0 0 1 12.8 6.4L296 64H152zM384 464a16 16 0 0 1-16 16H80a16 16 0 0 1-16-16V96h320zm-168-32h16a8 8 0 0 0 8-8V152a8 8 0 0 0-8-8h-16a8 8 0 0 0-8 8v272a8 8 0 0 0 8 8z"></path></svg>
</button>
</row-title>
<textarea .value="${row.value}" name="${this.model.name}-value" required placeholder="Description" rows="4" @input=${this.updateDesc} data-index="${index}">${row.value}</textarea>
<textarea @keydown=${this.noopEvent} @keyup=${this.noopEvent} .value="${row.value}" name="${this.model.name}-value" required placeholder="Description" rows="4" @input=${this.updateDesc} data-index="${index}">${row.value}</textarea>
</table-row>
`;
})}
Expand Down
2 changes: 1 addition & 1 deletion client/src/controllers/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ let socket:WebSocket;
let connected = false;
let wasReconnection = false;

let SOCKET_URL:string = "ws://ws.tabletopper.local";
let SOCKET_URL:string = "ws://localhost:8080";
if (location.host == "tabletopper.app") {
SOCKET_URL = "wss://ws.tabletopper.app";
}
Expand Down
Loading