Integrated Quill

This commit is contained in:
metacryst
2025-10-18 16:15:21 -05:00
parent d483e5af37
commit 80015f2435
10 changed files with 831 additions and 155 deletions

758
ui/_/code/quill.js Normal file
View File

@@ -0,0 +1,758 @@
/* $ SELECTOR */
HTMLElement.prototype.$ = function(selector) {
return window.$(selector, this)
}
DocumentFragment.prototype.$ = function(selector) {
return window.$(selector, this)
}
window.$ = function(selector, el = document) {
return el.querySelector(selector)
}
window.$$ = function(selector, el = document) {
return Array.from(el.querySelectorAll(selector))
}
/* CONSOLE */
console.red = function(message) {
this.log(`%c${message}`, "color: rgb(254, 79, 42);");
};
console.green = function(message) {
this.log(`%c${message}`, "color: rgb(79, 254, 42);");
}
/* GET CSS VARIABLES FOR DARK OR LIGHT MODE */
window.darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.classList.add(darkMode ? 'dark' : 'light');
window.getColor = function(name) {
const rootStyles = getComputedStyle(document.documentElement);
const color = rootStyles.getPropertyValue(`--${name}`).trim();
if(!color) {
throw new Error("Color not found")
}
return color
}
/* MOBILE */
window.isMobile = function isMobile() {
return /Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(navigator.userAgent);
}
window.css = function css(cssString) {
let container = document.querySelector("style#pageStyle");
if(!container) {
container = document.createElement('style');
container.id = "pageStyle";
document.head.appendChild(container);
}
let primarySelector = cssString.substring(0, cssString.indexOf("{")).trim();
primarySelector = primarySelector.replace(/\*/g, "all");
primarySelector = primarySelector.replace(/#/g, "id-");
primarySelector = primarySelector.replace(/,/g, "");
let stylesheet = container.querySelector(`:scope > style[id='${primarySelector}']`)
if(!stylesheet) {
stylesheet = document.createElement('style');
stylesheet.id = primarySelector;
stylesheet.appendChild(document.createTextNode(cssString));
container.appendChild(stylesheet);
} else {
stylesheet.innerText = cssString
}
}
window.html = function html(elementString) {
let parser = new DOMParser();
let doc = parser.parseFromString(elementString, 'text/html');
return doc.body.firstChild;
}
window.util = {}
window.util.observeClassChange = (el, callback) => {
if (!el || !(el instanceof Element)) {
throw new Error("observeClassChange requires a valid DOM element.");
}
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === "attributes" && mutation.attributeName === "class") {
callback(el.classList);
}
}
});
observer.observe(el, {
attributes: true,
attributeFilter: ["class"]
});
return observer; // Optional: return it so you can disconnect later
}
/* PAGE SETUP */
Object.defineProperty(Array.prototype, 'last', {
get() {
return this[this.length - 1];
},
enumerable: false,
});
/* QUILL */
window.quill = {
rendering: [],
render: (el) => {
if(el instanceof Shadow) {
let parent = quill.rendering[quill.rendering.length-1]
if(!parent) {
document.currentScript.replaceWith(el)
} else {
parent.appendChild(el)
}
} else {
if(!el.render) {el.render = () => {}}
let parent = quill.rendering[quill.rendering.length-1]
if(!parent) throw new Error("Quill: no parent for element")
parent.appendChild(el)
}
quill.rendering.push(el)
el.render()
quill.rendering.pop(el)
},
rerender: (el) => {
Array.from(el.attributes).forEach(attr => el.removeAttribute(attr.name));
el.innerHTML = ""
el.removeAllListeners()
quill.rendering.push(el)
el.render()
quill.rendering.pop()
},
loadPage: () => {
let URL = window.location.pathname
if(!window.routes[URL]) {
throw new Error("No URL for this route: ", URL)
}
let pageClass = window[routes[URL]]
document.title = pageClass.title ?? document.title
document.body.innerHTML = ""
let page = new pageClass()
quill.render(page)
},
isStack: (el) => {
return el.classList.contains("HStack") || el.classList.contains("ZStack") || el.classList.contains("VStack")
},
}
window.Shadow = class extends HTMLElement {
constructor() {
super()
}
}
window.registerShadow = (el, tagname) => {
if (typeof el.prototype.render !== 'function') {
throw new Error("Element must have a render: " + el.prototype.constructor.name)
}
if(!tagname) {
tagname = el.prototype.constructor.name.toLowerCase() + "-"
}
customElements.define(tagname, el)
if(el.css) {
css(el.css)
}
window[el.prototype.constructor.name] = function (...params) {
let instance = new el(...params)
quill.render(instance)
return instance
}
}
HTMLElement.prototype.rerender = function() {
quill.rerender(this)
}
/* Styling */
window.vh = "vh"
window.vw = "vw"
window.px = "px"
window.em = "em"
window.rem = "rem"
window.inches = "in"
HTMLElement.prototype.addStyle = function(func) {
return func(this)
}
window.css = function css(cssString) {
let container = document.querySelector("style#pageStyle");
if(!container) {
container = document.createElement('style');
container.id = "pageStyle";
document.head.appendChild(container);
}
let primarySelector = cssString.substring(0, cssString.indexOf("{")).trim();
primarySelector = primarySelector.replace(/\*/g, "all");
primarySelector = primarySelector.replace(/#/g, "id-");
primarySelector = primarySelector.replace(/,/g, "");
let stylesheet = container.querySelector(`:scope > style[id='${primarySelector}']`)
if(!stylesheet) {
stylesheet = document.createElement('style');
stylesheet.id = primarySelector;
stylesheet.appendChild(document.createTextNode(cssString));
container.appendChild(stylesheet);
} else {
stylesheet.innerText = cssString
}
}
function extendHTMLElementWithStyleSetters() {
let allStyleProps = Object.keys(document.createElement("div").style)
allStyleProps.forEach(prop => {
if(prop === "translate") return
HTMLElement.prototype[prop] = function(value) {
this.style[prop] = value;
return this;
};
});
}
extendHTMLElementWithStyleSetters();
HTMLElement.prototype.padding = function(one, two, three = "px") {
const setPadding = (side, val) => {
const directionName = `padding${side.charAt(0).toUpperCase()}${side.slice(1)}`;
this.style[directionName] = (typeof val === 'number') ? `${val}${three}` : val;
};
if(one === "horizontal" || one === "vertical") { // is one a direction
if (one === "horizontal") {
setPadding("left", two);
setPadding("right", two);
} else if (one === "vertical") {
setPadding("top", two);
setPadding("bottom", two);
}
} else { // is two a value
if(typeof one !== 'number' || isNaN(one)) {
this.style.padding = one
} else {
this.style.padding = one + two
}
}
return this;
};
HTMLElement.prototype.paddingTop = function(value, unit = "px") {
this.style.paddingTop = value + unit
return this
}
HTMLElement.prototype.paddingLeft = function(value, unit = "px") {
this.style.paddingLeft = value + unit
return this
}
HTMLElement.prototype.paddingBottom = function(value, unit = "px") {
this.style.paddingBottom = value + unit
return this
}
HTMLElement.prototype.paddingRight = function(value, unit = "px") {
this.style.paddingRight = value + unit
return this
}
HTMLElement.prototype.margin = function(direction, value, unit = "px") {
if (!value) {
this.style.margin = direction;
return this;
}
const setMargin = (side, val) => {
const directionName = `margin${side.charAt(0).toUpperCase()}${side.slice(1)}`;
this.style[directionName] = (typeof val === 'number') ? `${val}${unit}` : val;
};
if (direction === "horizontal") {
setMargin("left", value);
setMargin("right", value);
} else if (direction === "vertical") {
setMargin("top", value);
setMargin("bottom", value);
} else {
setMargin(direction, value);
}
return this;
};
HTMLElement.prototype.marginTop = function(value, unit = "px") {
this.style.marginTop = value + unit
return this
}
HTMLElement.prototype.marginLeft = function(value, unit = "px") {
this.style.marginLeft = value + unit
return this
}
HTMLElement.prototype.marginBottom = function(value, unit = "px") {
this.style.marginBottom = value + unit
return this
}
HTMLElement.prototype.marginRight = function(value, unit = "px") {
this.style.marginRight = value + unit
return this
}
HTMLElement.prototype.width = function(value, unit = "px") {
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
this.style.width = value + unit
return this
}
HTMLElement.prototype.minWidth = function(value, unit = "px") {
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
this.style.minWidth = value + unit
return this
}
HTMLElement.prototype.height = function(value, unit = "px") {
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
this.style.height = value + unit
return this
}
HTMLElement.prototype.minHeight = function(value, unit = "px") {
if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
this.style.minHeight = value + unit
return this
}
HTMLElement.prototype.fontSize = function(value, unit = "px") {
switch(value) {
case "6xl":
value = "3.75"; unit = "rem"
break;
case "5xl":
value = "3"; unit = "rem"
break;
case "4xl":
value = "2.25"; unit = "rem"
break;
case "3xl":
value = "1.875"; unit = "rem"
break;
case "2xl":
value = "1.5"; unit = "rem"
break;
case "xl":
value = "1.25"; unit = "rem"
break;
case "l":
value = "1.125"; unit = "rem"
break;
case "s":
value = "0.875"; unit = "rem"
break;
case "xs":
value = "0.75"; unit = "rem"
break;
default:
break;
}
this.style.fontSize = value + unit
return this
}
function checkPositionType(el) {
let computed = window.getComputedStyle(el).position
if(!(computed === "absolute" || computed === "fixed")) {
el.style.position = "absolute"
}
}
HTMLElement.prototype.x = function(value, unit = "px") {
if (typeof value !== 'number' || isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
checkPositionType(this)
this.style.left = value + unit
return this
}
HTMLElement.prototype.y = function(value, unit = "px") {
if (typeof value !== 'number' || isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
checkPositionType(this)
this.style.top = value + unit
return this
}
HTMLElement.prototype.xRight = function(value, unit = "px") {
if (typeof value !== 'number' || isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
checkPositionType(this)
this.style.right = value + unit
return this
}
HTMLElement.prototype.yBottom = function(value, unit = "px") {
if (typeof value !== 'number' || isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
checkPositionType(this)
this.style.bottom = value + unit
return this
}
HTMLElement.prototype.borderRadius = function(value, unit = "px") {
if (typeof value !== 'number' || isNaN(value))
throw new Error(`Invalid value: ${value}. Expected a number.`);
this.style.borderRadius = value + unit
return this
}
HTMLElement.prototype.positionType = function (value) {
if(!(value === "absolute" || value === "relative" || value === "static" || value === "fixed" || value === "sticky")) {
console.error("HTMLElement.overlflow: must have valid overflow value!")
return;
}
this.style.position = value
return this
}
/* Elements */
quill.setChildren = function(el, innerContent) {
if(typeof innerContent === "string") {
el.innerText = innerContent
} else if(typeof innerContent === "function") {
el.render = innerContent
} else {
throw new Error("Children of unknown type")
}
}
window.a = function a( href, inner=href ) {
if(!href) throw new Error("quill a: missing href argument. Function: a( href, inner=href )")
let link = document.createElement("a")
link.setAttribute('href', href);
quill.setChildren(link, inner)
quill.render(link)
return link
}
window.img = function img(src, width="", height="") {
let image = document.createElement("img")
if(!src || !(typeof src==="string")) {
throw new Error("img: missing first argument: src | String")
} else {
image.src = src
}
if(width && typeof width === "string") {
image.style.width = width
} else if(width) {
image.style.width = width + "px"
}
if(height && typeof height === "string") {
image.style.height = height
} else if(height) {
image.style.height = height + "px"
}
quill.render(image)
return image
}
HTMLImageElement.prototype.backgroundColor = function(value) {
if (this.src.endsWith('.svg') || this.src.startsWith('data:image/svg+xml')) {
fetch(this.src).then(response => response.text())
.then(svgText => {
const modifiedSvg = svgText.replace(/\bfill="[^"]*"/g, `fill="${value}"`);
const blob = new Blob([modifiedSvg], { type: 'image/svg+xml' });
this.src = URL.createObjectURL(blob);
}).catch(error => {
console.error('Error updating SVG fill:', error);
});
} else {
this.style.backgroundColor = value;
}
return this; // Always returns the element itself
};
window.p = function p(innerText) {
let el = document.createElement("p")
if(typeof innerText === "function") {
el.render = innerText
} else {
el.innerText = innerText
}
el.style.margin = "0";
quill.render(el)
return el
}
window.h1 = function h1(innerText) {
let el = document.createElement("h1")
el.innerText = innerText
quill.render(el)
return el
}
window.h2 = function h2(innerText) {
let el = document.createElement("h2")
el.innerText = innerText
quill.render(el)
return el
}
window.h3 = function h3(innerText) {
let el = document.createElement("h3")
el.innerText = innerText
quill.render(el)
return el
}
window.div = function (innerText) {
let el = document.createElement("div")
el.innerText = innerText ?? ""
quill.render(el)
return el
}
window.span = function (innerText) {
let el = document.createElement("span")
el.innerText = innerText
quill.render(el)
return el
}
window.button = function (children) {
let el = document.createElement("button")
quill.setChildren(el, children)
quill.render(el)
return el
}
window.form = function(cb) {
let el = document.createElement("form")
el.render = cb
quill.render(el)
return el
}
window.input = function(placeholder, dimensions) {
let el = document.createElement("input")
el.placeholder = placeholder
if(dimensions.width) el.style.width = dimensions.width
if(dimensions.height) el.style.height = dimensions.height
quill.render(el)
return el
}
window.textarea = function(placeholder) {
let el = document.createElement("textarea")
el.placeholder = placeholder
quill.render(el)
return el
}
/* STACKS */
window.VStack = function (cb = () => {}) {
let styles = `
display: flex;
flex-direction: column;
`
let nowRendering = quill.rendering[quill.rendering.length-1]
if (nowRendering.innerHTML.trim() === "" && !quill.isStack(nowRendering)) {
nowRendering.style.cssText += styles
nowRendering.classList.add("VStack")
cb()
return nowRendering
}
let div = document.createElement("div")
div.classList.add("VStack")
div.style.cssText += styles
div.render = cb
quill.render(div)
return div
}
window.HStack = function (cb = () => {}) {
let styles = `
display: flex;
flex-direction: row;
`;
let nowRendering = quill.rendering[quill.rendering.length - 1];
if (nowRendering.innerHTML.trim() === "" && !quill.isStack(nowRendering)) {
nowRendering.style.cssText += styles;
nowRendering.classList.add("HStack")
cb();
return nowRendering;
}
let div = document.createElement("div");
div.classList.add("HStack");
div.style.cssText += styles;
div.render = cb;
quill.render(div);
return div;
};
window.ZStack = function (cb = () => {}) {
let nowRendering = quill.rendering[quill.rendering.length - 1];
if (nowRendering.innerHTML.trim() === "" && !quill.isStack(nowRendering)) {
nowRendering.classList.add("ZStack")
cb();
return nowRendering;
}
let div = document.createElement("div");
div.classList.add("ZStack");
div.render = cb;
quill.render(div);
return div;
};
/* EVENTS */
HTMLElement.prototype.onAppear = function(func) {
func(this);
return this;
};
HTMLElement.prototype.onClick = function(func) {
this._storeListener("click", func);
return this;
};
HTMLElement.prototype.onMouseDown = function(func) {
this._storeListener("mousedown", func);
return this;
};
HTMLElement.prototype.onMouseUp = function(func) {
this._storeListener("mouseup", func);
return this;
};
HTMLElement.prototype.onRightClick = function(func) {
this._storeListener("contextmenu", func);
return this;
};
HTMLElement.prototype.onHover = function(cb) {
const onEnter = () => cb.call(this, true);
const onLeave = () => cb.call(this, false);
this._storeListener("mouseover", onEnter);
this._storeListener("mouseleave", onLeave);
return this;
};
HTMLElement.prototype.onFocus = function(cb) {
if (!this.matches('input, textarea, select, button')) {
throw new Error("Can't put focus event on non-form element!");
}
this._storeListener("focus", cb);
return this;
};
HTMLElement.prototype.onBlur = function(cb) {
if (!this.matches('input, textarea, select, button')) {
throw new Error("Can't put blur event on non-form element!");
}
this._storeListener("blur", cb);
return this;
};
HTMLElement.prototype.onKeyDown = function(cb) {
this._storeListener("keydown", cb);
return this;
};
HTMLElement.prototype.onEvent = function(name, cb) {
window._storeListener(window, name, cb);
return this;
};
HTMLElement.prototype._storeListener = function(type, handler, options) {
window._storeListener(this, type, handler, options)
}
window.__listeners = []
function _storeListener(target, type, handler, options) {
if (!target.__listeners) target.__listeners = [];
const optionsString = JSON.stringify(options);
const index = target.__listeners.findIndex(listener =>
listener.type === type &&
listener.handler.toString() === handler.toString() &&
JSON.stringify(listener.options) === optionsString
);
if (index === -1) {
target.addEventListener(type, handler, options);
target.__listeners.push({ type, handler, options });
} else {
const old = target.__listeners[index];
target.removeEventListener(old.type, old.handler, old.options);
// Replace with the new one
target.addEventListener(type, handler, options);
target.__listeners[index] = { type, handler, options };
}
}
HTMLElement.prototype.removeAllListeners = function() {
if (!this.__listeners) return;
for (const { type, handler, options } of this.__listeners) {
this.removeEventListener(type, handler, options);
}
this.__listeners = [];
return this;
};
/* ATTRIBUTES */
HTMLElement.prototype.attr = function(attributes) {
for (const [key, value] of Object.entries(attributes)) {
this.setAttribute(key, value);
}
return this;
};

View File

@@ -1,98 +0,0 @@
/* $ SELECTOR */
HTMLElement.prototype.$ = function(selector) {
return window.$(selector, this)
}
DocumentFragment.prototype.$ = function(selector) {
return window.$(selector, this)
}
window.$ = function(selector, el = document) {
if(selector[0] === "#" || selector.includes("[name")) {
return el.querySelector(selector)
} else {
let result = el.querySelectorAll(selector);
return result.length === 1 ? result[0] : result
}
}
/* CONSOLE */
console.red = function(message) {
this.log(`%c${message}`, "color: rgb(254, 79, 42);");
};
console.green = function(message) {
this.log(`%c${message}`, "color: rgb(79, 254, 42);");
}
/* GET CSS VARIABLES FOR DARK OR LIGHT MODE */
window.darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.classList.add(darkMode ? 'dark' : 'light');
window.getColor = function(name) {
const rootStyles = getComputedStyle(document.documentElement);
const color = rootStyles.getPropertyValue(`--${name}`).trim();
if(!color) {
throw new Error("Color not found")
}
return color
}
/* MOBILE */
window.isMobile = function isMobile() {
return /Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(navigator.userAgent);
}
window.css = function css(cssString) {
let container = document.querySelector("style#pageStyle");
if(!container) {
container = document.createElement('style');
container.id = "pageStyle";
document.head.appendChild(container);
}
let primarySelector = cssString.substring(0, cssString.indexOf("{")).trim();
primarySelector = primarySelector.replace(/\*/g, "all");
primarySelector = primarySelector.replace(/#/g, "id-");
primarySelector = primarySelector.replace(/,/g, "");
let stylesheet = container.querySelector(`:scope > style[id='${primarySelector}']`)
if(!stylesheet) {
stylesheet = document.createElement('style');
stylesheet.id = primarySelector;
stylesheet.appendChild(document.createTextNode(cssString));
container.appendChild(stylesheet);
} else {
stylesheet.innerText = cssString
}
}
window.html = function html(elementString) {
let parser = new DOMParser();
let doc = parser.parseFromString(elementString, 'text/html');
return doc.body.firstChild;
}
window.util = {}
window.util.observeClassChange = (el, callback) => {
if (!el || !(el instanceof Element)) {
throw new Error("observeClassChange requires a valid DOM element.");
}
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === "attributes" && mutation.attributeName === "class") {
callback(el.classList);
}
}
});
observer.observe(el, {
attributes: true,
attributeFilter: ["class"]
});
return observer; // Optional: return it so you can disconnect later
}

View File

@@ -28,7 +28,7 @@
max-width: 195vw; height: 80vh; left: 0vw;
}}
</style>
<script src="_/code/util.js"></script>
<script src="_/code/quill.js"></script>
<script type="module">
import PageFooter from "./components/Footer.js"
import NavBar from "./components/NavBar.js"
@@ -66,8 +66,8 @@
<h1 style="font-family: Canterbury; font-size: 3.5rem; ">A Classical Christian Society</h1>
<div style="font-size: 1.3em; margin-top: 1em; max-width: 42vw; text-align: justify">
<p>Hyperia is a private club. We exist to promote European and Christian tradition.</p>
<p>Inspired by the Classical Christian schooling movement that began in the 1990s, Hyperia is a similar space for adults.</p>
<p>Hyperia is a private club, focused on promoting the Classical Christian movement in America.</p>
<p>Inspired by Classical Christian schools, Hyperia is a similar space for adults.</p>
</div>
<div style="height: 3vh;"></div>

View File

@@ -24,7 +24,7 @@
}
}
</style>
<script src="_/code/util.js"></script>
<script src="_/code/quill.js"></script>
<script type="module">
import PageFooter from "./components/Footer.js"
import NavBar from "./components/NavBar.js"

View File

@@ -56,7 +56,7 @@
}
}
</style>
<script src="_/code/util.js"></script>
<script src="_/code/quill.js"></script>
<script type="module">
import PageFooter from "./components/Footer.js"
import NavBar from "./components/NavBar.js"

View File

@@ -56,7 +56,7 @@
}
}
</style>
<script src="_/code/util.js"></script>
<script src="_/code/quill.js"></script>
<script type="module">
import PageFooter from "./components/Footer.js"
import NavBar from "./components/NavBar.js"

View File

@@ -0,0 +1,62 @@
css(`
app-menu {
color: var(--tan);
transform: translateX(-50%);
transition: transform .3s;
display: flex; gap: 2em; position: fixed; left: 50vw; bottom: 2.3em;
}
app-menu.minimized {
color: var(--accent);
transform: translate(-50%, 65%);
border: 1px solid var(--accent);
padding-left: 2em; padding-right: 2em;
border-radius: 12px;
}
`)
registerShadow(
class AppMenu extends Shadow {
render() {
p("Messages")
p("Market")
p("Security")
p("Jobs")
}
connectedCallback() {
Array.from(this.querySelectorAll("p")).forEach((el) => {
el.addEventListener("mousedown", (e) => {
el.classList.add("touched")
})
})
window.addEventListener("mouseup", (e) => {
let target = e.target
if(!target.matches("app-menu p")) {
return
}
$$("app-menu p").forEach((el) => {
if(el.innerText !== target.innerText) {
el.classList.remove("selected")
}
})
target.classList.remove("touched")
if(target.classList.contains("selected")) {
target.classList.remove("selected")
$("app-manu").classList.remove("minimized")
$("#divider").classList.remove("minimized")
$("app-window").close()
} else {
target.classList.add("selected")
$("app-manu").classList.add("minimized")
$("#divider").classList.add("minimized")
$("app-window").open(target.innerText)
}
})
}
}
, "app-menu")

View File

@@ -13,6 +13,7 @@ css(`
export default class AppWindow extends HTMLElement {
open(app) {
console.log("opening", app)
this.style.display = "block"
}

View File

@@ -21,17 +21,6 @@
background-repeat: no-repeat;
}
#menu-bar {
color: var(--tan);
transform: translateX(-50%);
transition: transform .3s;
}
#menu-bar.minimized {
color: var(--accent);
transform: translate(-50%, 55%);
border: 1px solid var(--accent);
padding-left: 2em; padding-right: 2em;
}
#divider.minimized {
display: none;
}
@@ -56,7 +45,7 @@
transform: translateY(-10%)
}
</style>
<script src="_/code/util.js"></script>
<script src="_/code/quill.js"></script>
<script type="module">
import "./components/ProfileButton.js"
import "./components/InputBox.js"
@@ -66,6 +55,7 @@
import ConnectionHandler from "./ws/ConnectionHandler.js"
window.ConnectionHandler = new ConnectionHandler()
</script>
<script src="./components/AppMenu.js"></script>
<script>
const img = new Image();
img.src = "_/images/the_return.webp";
@@ -87,45 +77,8 @@
<app-window></app-window>
<profile-button style="z-index: 1; cursor: default; position: fixed; top: 5.5vh; right: 4.5vw"></profile-button>
<img src="_/icons/logo.svg" style="width: 3.5em; position: fixed; left: 3em; top: 2em;"/>
<div id="menu-bar" style="display: flex; gap: 2em; position: fixed; left: 50vw; bottom: 2.3em;">
<p class="app">Forum</p>
<p class="app">Messages</p>
<p class="app">Market</p>
<p class="app">Security</p>
<p class="app">Jobs</p>
</div>
<script>AppMenu()</script>
<img id="divider" src="_/images/divider.svg" style="width: 40vw; position: fixed; bottom: 2em; left: 50vw; transform: translateX(-50%)"/>
<script>
Array.from(document.querySelectorAll(".app")).forEach((el) => {
el.addEventListener("mousedown", (e) => {
el.classList.add("touched")
})
})
window.addEventListener("mouseup", (e) => {
let target = e.target
if(!target.matches("p.app")) {
return
}
$("p.app").forEach((el) => {
if(el.innerText !== target.innerText) {
el.classList.remove("selected")
}
})
target.classList.remove("touched")
if(target.classList.contains("selected")) {
target.classList.remove("selected")
$("#menu-bar").classList.remove("minimized")
$("#divider").classList.remove("minimized")
$("app-window").close()
} else {
target.classList.add("selected")
$("#menu-bar").classList.add("minimized")
$("#divider").classList.add("minimized")
$("app-window").open(target.innerText)
}
})
</script>
</div>
</body>
</html>

View File

@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="_/icons/logo.svg">
<link rel="stylesheet" href="_/code/shared.css">
<script src="_/code/util.js"></script>
<script src="_/code/quill.js"></script>
<script type="module">
import "./components/ProfileButton.js"
import "./components/InputBox.js"