diff --git a/qrCodes/convertToJSON.js b/qrCodes/convertToJSON.js new file mode 100644 index 0000000..fc3869f --- /dev/null +++ b/qrCodes/convertToJSON.js @@ -0,0 +1,27 @@ +import fs from 'fs' +import {parse} from 'csv-parse' + +const csvFilePath = './tokens.csv'; +const jsonFilePath = './tokens.json'; + +fs.readFile(csvFilePath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading file:', err); + return; + } + + parse(data, { columns: true, skip_empty_lines: true }, (err, output) => { + if (err) { + console.error('Error parsing CSV:', err); + return; + } + + fs.writeFile(jsonFilePath, JSON.stringify(output, null, 2), (err) => { + if (err) { + console.error('Error writing JSON file:', err); + } else { + console.log(`JSON successfully written to ${jsonFilePath}`); + } + }); + }); +}); \ No newline at end of file diff --git a/qrCodes/package.json b/qrCodes/package.json index d01bf7a..15566b1 100644 --- a/qrCodes/package.json +++ b/qrCodes/package.json @@ -1,6 +1,7 @@ { "type": "module", "dependencies": { + "csv-parse": "^6.1.0", "csv-writer": "^1.6.0", "qrcode": "^1.5.4", "uuid": "^13.0.0" diff --git a/server/db/db.js b/server/db/db.js index 495b76b..d433a0f 100644 --- a/server/db/db.js +++ b/server/db/db.js @@ -1,6 +1,20 @@ import QuillDB from "../_/quilldb.js" +import fs from 'fs/promises' +import path from 'path' export default class Database extends QuillDB { + tokens; + + constructor() { + super() + this.loadTokens() + } + + async loadTokens() { + const tokenData = await fs.readFile(path.join(process.cwd(), 'db/tokens.json'), 'utf8'); + let tokenJSON = JSON.parse(tokenData); + this.tokens = tokenJSON + } get = { user: (id) => { @@ -15,6 +29,9 @@ export default class Database extends QuillDB { } return null; }, + token: (id) => { + return this.tokens[id] + } } generateUserID() { diff --git a/server/index.js b/server/index.js index ed81526..4378381 100644 --- a/server/index.js +++ b/server/index.js @@ -27,11 +27,31 @@ class Server { // router.post('/api/location', handlers.updateLocation) router.post('/login', this.auth.login) router.get('/signout', this.auth.logout) + router.get('/signup', this.verifyToken, this.get) + router.post('/signup', this.verifyToken, this.newUserSubmission) router.get('/db/images/*', this.getUserImage) router.get('/*', this.get) return router } + verifyToken = (req, res, next) => { + const { token } = req.query; + if (!token) { + return res.status(400).json({ error: 'Token is required' }); + } + let fromDB = this.db.get.token(token) + if (!fromDB) { + return res.status(403).json({ error: 'Invalid or expired token' }); + } else if(fromDB.used) { + return res.status(403).json({ error: 'Invalid or expired token' }); + } + next() + } + + newUserSubmission = (req, res) => { + return res.status(400).json({ error: 'Haven\t finished this bruh' }); + } + authMiddleware = (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader) { @@ -67,37 +87,32 @@ class Server { } get = async (req, res) => { - if(!this.auth.isLoggedInUser(req, res)) { - console.log("Not logged in") - let url = req.url - if(!url.includes(".")) { // Page request - if(url === "/") { - url = "/index.html" - } else { - url = path.join("/pages", url) + ".html" - } + let url = req.url - let filePath = path.join(this.UIPath, "public", url); - res.sendFile(filePath, (err) => { - if (err) { - console.log("File not found, sending fallback:", filePath); - res.redirect("/"); - } - }); - } else { // File Request - let filePath; - if(url.startsWith("/_")) { - filePath = path.join(this.UIPath, url); - } else { - filePath = path.join(this.UIPath, "public", url); + let publicPage = () => { + url = "/index.html" + let filePath = path.join(this.UIPath, "public", url); + res.sendFile(filePath, (err) => { + if (err) { + console.log("File not found, sending fallback:", filePath); + res.redirect("/"); } - - res.sendFile(filePath); + }); + } + + let publicFile = () => { + let filePath; + if(url.startsWith("/_")) { + filePath = path.join(this.UIPath, url); + } else { + filePath = path.join(this.UIPath, "public", url); } - } else { - let url = req.url + res.sendFile(filePath); + } + + let privateSite = () => { let filePath; if(url.startsWith("/_")) { filePath = path.join(this.UIPath, url); @@ -109,6 +124,16 @@ class Server { res.sendFile(filePath); } + + if(!this.auth.isLoggedInUser(req, res)) { + if(!url.includes(".")) { + publicPage() + } else { + publicFile() + } + } else { + privateSite() + } } logRequest(req, res, next) { @@ -158,7 +183,7 @@ class Server { const PORT = 3003; server.listen(PORT, () => { console.log("\n") - console.log(chalk.yellow("**************America****************")) + console.log(chalk.yellow("*************** Hyperia ***************")) console.log(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`)); console.log(chalk.yellow("***************************************")) console.log("\n") diff --git a/ui/_/code/quill.js b/ui/_/code/quill.js index ef9148a..6232ebd 100644 --- a/ui/_/code/quill.js +++ b/ui/_/code/quill.js @@ -1,6 +1,11 @@ /* Sam Russell Captured Sun + 11.17.25.3 - Adding styles() and fixing dynamic function from earlier + 11.17.25.2 - Fixing onNavigate() and onAppear() + 11.17.25 - Added dynamic function to have units in style func parameters. + 11.14.25 - Added onTouch, onTap. Changed style setters to work with Safari. Added center() funcs. + 11.13.25 - changed onFocus() to be a boolean event, added onInput() 11.9.25 - changed p(innerText) to p(innerHTML), adjusted onNavigate to work for multiple elements and with correct "this" scope 11.7.25 - changed registerShadow() to register(), changed onClick() to be like onHover() 11.6.25 - adding default value for "button()" "children" parameter @@ -248,17 +253,81 @@ window.css = function css(cssString) { } function extendHTMLElementWithStyleSetters() { - let allStyleProps = Object.keys(document.createElement("div").style) + + function cssValueType(prop) { + const div = document.createElement("div"); + const style = div.style; + if (!(prop in style)) return "invalid"; + + switch(prop) { + + case "gap": + case "borderRadius": + case "width": + case "height": + case "maxWidth": + case "maxHeight": + + case "left": + case "top": + case "bottom": + case "right": + + case "paddingLeft": + case "paddingTop": + case "paddingBottom": + case "paddingRight": + + case "marginLeft": + case "marginTop": + case "marginBottom": + case "marginRight": + + case "textUnderlineOffset": + + return "unit-number" + + default: + + return "string" + } + + } + + let allStyleProps = ["accentColor", "additiveSymbols", "alignContent", "alignItems", "alignSelf", "alignmentBaseline", "all", "anchorName", "anchorScope", "animation", "animationComposition", "animationDelay", "animationDirection", "animationDuration", "animationFillMode", "animationIterationCount", "animationName", "animationPlayState", "animationRange", "animationRangeEnd", "animationRangeStart", "animationTimeline", "animationTimingFunction", "appRegion", "appearance", "ascentOverride", "aspectRatio", "backdropFilter", "backfaceVisibility", "background", "backgroundAttachment", "backgroundBlendMode", "backgroundClip", "backgroundColor", "backgroundImage", "backgroundOrigin", "backgroundPosition", "backgroundPositionX", "backgroundPositionY", "backgroundRepeat", "backgroundSize", "basePalette", "baselineShift", "baselineSource", "blockSize", "border", "borderBlock", "borderBlockColor", "borderBlockEnd", "borderBlockEndColor", "borderBlockEndStyle", "borderBlockEndWidth", "borderBlockStart", "borderBlockStartColor", "borderBlockStartStyle", "borderBlockStartWidth", "borderBlockStyle", "borderBlockWidth", "borderBottom", "borderBottomColor", "borderBottomLeftRadius", "borderBottomRightRadius", "borderBottomStyle", "borderBottomWidth", "borderCollapse", "borderColor", "borderEndEndRadius", "borderEndStartRadius", "borderImage", "borderImageOutset", "borderImageRepeat", "borderImageSlice", "borderImageSource", "borderImageWidth", "borderInline", "borderInlineColor", "borderInlineEnd", "borderInlineEndColor", "borderInlineEndStyle", "borderInlineEndWidth", "borderInlineStart", "borderInlineStartColor", "borderInlineStartStyle", "borderInlineStartWidth", "borderInlineStyle", "borderInlineWidth", "borderLeft", "borderLeftColor", "borderLeftStyle", "borderLeftWidth", "borderRadius", "borderRight", "borderRightColor", "borderRightStyle", "borderRightWidth", "borderSpacing", "borderStartEndRadius", "borderStartStartRadius", "borderStyle", "borderTop", "borderTopColor", "borderTopLeftRadius", "borderTopRightRadius", "borderTopStyle", "borderTopWidth", "borderWidth", "bottom", "boxDecorationBreak", "boxShadow", "boxSizing", "breakAfter", "breakBefore", "breakInside", "bufferedRendering", "captionSide", "caretAnimation", "caretColor", "clear", "clip", "clipPath", "clipRule", "color", "colorInterpolation", "colorInterpolationFilters", "colorRendering", "colorScheme", "columnCount", "columnFill", "columnGap", "columnRule", "columnRuleColor", "columnRuleStyle", "columnRuleWidth", "columnSpan", "columnWidth", "columns", "contain", "containIntrinsicBlockSize", "containIntrinsicHeight", "containIntrinsicInlineSize", "containIntrinsicSize", "containIntrinsicWidth", "container", "containerName", "containerType", "content", "contentVisibility", "cornerBlockEndShape", "cornerBlockStartShape", "cornerBottomLeftShape", "cornerBottomRightShape", "cornerBottomShape", "cornerEndEndShape", "cornerEndStartShape", "cornerInlineEndShape", "cornerInlineStartShape", "cornerLeftShape", "cornerRightShape", "cornerShape", "cornerStartEndShape", "cornerStartStartShape", "cornerTopLeftShape", "cornerTopRightShape", "cornerTopShape", "counterIncrement", "counterReset", "counterSet", "cursor", "cx", "cy", "d", "descentOverride", "direction", "display", "dominantBaseline", "dynamicRangeLimit", "emptyCells", "fallback", "fieldSizing", "fill", "fillOpacity", "fillRule", "filter", "flex", "flexBasis", "flexDirection", "flexFlow", "flexGrow", "flexShrink", "flexWrap", "float", "floodColor", "floodOpacity", "font", "fontDisplay", "fontFamily", "fontFeatureSettings", "fontKerning", "fontOpticalSizing", "fontPalette", "fontSize", "fontSizeAdjust", "fontStretch", "fontStyle", "fontSynthesis", "fontSynthesisSmallCaps", "fontSynthesisStyle", "fontSynthesisWeight", "fontVariant", "fontVariantAlternates", "fontVariantCaps", "fontVariantEastAsian", "fontVariantEmoji", "fontVariantLigatures", "fontVariantNumeric", "fontVariantPosition", "fontVariationSettings", "fontWeight", "forcedColorAdjust", "gap", "grid", "gridArea", "gridAutoColumns", "gridAutoFlow", "gridAutoRows", "gridColumn", "gridColumnEnd", "gridColumnGap", "gridColumnStart", "gridGap", "gridRow", "gridRowEnd", "gridRowGap", "gridRowStart", "gridTemplate", "gridTemplateAreas", "gridTemplateColumns", "gridTemplateRows", "height", "hyphenateCharacter", "hyphenateLimitChars", "hyphens", "imageOrientation", "imageRendering", "inherits", "initialLetter", "initialValue", "inlineSize", "inset", "insetBlock", "insetBlockEnd", "insetBlockStart", "insetInline", "insetInlineEnd", "insetInlineStart", "interactivity", "interpolateSize", "isolation", "justifyContent", "justifyItems", "justifySelf", "left", "letterSpacing", "lightingColor", "lineBreak", "lineGapOverride", "lineHeight", "listStyle", "listStyleImage", "listStylePosition", "listStyleType", "margin", "marginBlock", "marginBlockEnd", "marginBlockStart", "marginBottom", "marginInline", "marginInlineEnd", "marginInlineStart", "marginLeft", "marginRight", "marginTop", "marker", "markerEnd", "markerMid", "markerStart", "mask", "maskClip", "maskComposite", "maskImage", "maskMode", "maskOrigin", "maskPosition", "maskRepeat", "maskSize", "maskType", "mathDepth", "mathShift", "mathStyle", "maxBlockSize", "maxHeight", "maxInlineSize", "maxWidth", "minBlockSize", "minHeight", "minInlineSize", "minWidth", "mixBlendMode", "navigation", "negative", "objectFit", "objectPosition", "objectViewBox", "offset", "offsetAnchor", "offsetDistance", "offsetPath", "offsetPosition", "offsetRotate", "opacity", "order", "orphans", "outline", "outlineColor", "outlineOffset", "outlineStyle", "outlineWidth", "overflow", "overflowAnchor", "overflowBlock", "overflowClipMargin", "overflowInline", "overflowWrap", "overflowX", "overflowY", "overlay", "overrideColors", "overscrollBehavior", "overscrollBehaviorBlock", "overscrollBehaviorInline", "overscrollBehaviorX", "overscrollBehaviorY", "pad", "padding", "paddingBlock", "paddingBlockEnd", "paddingBlockStart", "paddingBottom", "paddingInline", "paddingInlineEnd", "paddingInlineStart", "paddingLeft", "paddingRight", "paddingTop", "page", "pageBreakAfter", "pageBreakBefore", "pageBreakInside", "pageOrientation", "paintOrder", "perspective", "perspectiveOrigin", "placeContent", "placeItems", "placeSelf", "pointerEvents", "position", "positionAnchor", "positionArea", "positionTry", "positionTryFallbacks", "positionTryOrder", "positionVisibility", "prefix", "printColorAdjust", "quotes", "r", "range", "readingFlow", "readingOrder", "resize", "result", "right", "rotate", "rowGap", "rubyAlign", "rubyPosition", "rx", "ry", "scale", "scrollBehavior", "scrollInitialTarget", "scrollMargin", "scrollMarginBlock", "scrollMarginBlockEnd", "scrollMarginBlockStart", "scrollMarginBottom", "scrollMarginInline", "scrollMarginInlineEnd", "scrollMarginInlineStart", "scrollMarginLeft", "scrollMarginRight", "scrollMarginTop", "scrollMarkerGroup", "scrollPadding", "scrollPaddingBlock", "scrollPaddingBlockEnd", "scrollPaddingBlockStart", "scrollPaddingBottom", "scrollPaddingInline", "scrollPaddingInlineEnd", "scrollPaddingInlineStart", "scrollPaddingLeft", "scrollPaddingRight", "scrollPaddingTop", "scrollSnapAlign", "scrollSnapStop", "scrollSnapType", "scrollTargetGroup", "scrollTimeline", "scrollTimelineAxis", "scrollTimelineName", "scrollbarColor", "scrollbarGutter", "scrollbarWidth", "shapeImageThreshold", "shapeMargin", "shapeOutside", "shapeRendering", "size", "sizeAdjust", "speak", "speakAs", "src", "stopColor", "stopOpacity", "stroke", "strokeDasharray", "strokeDashoffset", "strokeLinecap", "strokeLinejoin", "strokeMiterlimit", "strokeOpacity", "strokeWidth", "suffix", "symbols", "syntax", "system", "tabSize", "tableLayout", "textAlign", "textAlignLast", "textAnchor", "textAutospace", "textBox", "textBoxEdge", "textBoxTrim", "textCombineUpright", "textDecoration", "textDecorationColor", "textDecorationLine", "textDecorationSkipInk", "textDecorationStyle", "textDecorationThickness", "textEmphasis", "textEmphasisColor", "textEmphasisPosition", "textEmphasisStyle", "textIndent", "textOrientation", "textOverflow", "textRendering", "textShadow", "textSizeAdjust", "textSpacingTrim", "textTransform", "textUnderlineOffset", "textUnderlinePosition", "textWrap", "textWrapMode", "textWrapStyle", "timelineScope", "top", "touchAction", "transform", "transformBox", "transformOrigin", "transformStyle", "transition", "transitionBehavior", "transitionDelay", "transitionDuration", "transitionProperty", "transitionTimingFunction", "translate", "types", "unicodeBidi", "unicodeRange", "userSelect", "vectorEffect", "verticalAlign", "viewTimeline", "viewTimelineAxis", "viewTimelineInset", "viewTimelineName", "viewTransitionClass", "viewTransitionGroup", "viewTransitionName", "visibility", "webkitAlignContent", "webkitAlignItems", "webkitAlignSelf", "webkitAnimation", "webkitAnimationDelay", "webkitAnimationDirection", "webkitAnimationDuration", "webkitAnimationFillMode", "webkitAnimationIterationCount", "webkitAnimationName", "webkitAnimationPlayState", "webkitAnimationTimingFunction", "webkitAppRegion", "webkitAppearance", "webkitBackfaceVisibility", "webkitBackgroundClip", "webkitBackgroundOrigin", "webkitBackgroundSize", "webkitBorderAfter", "webkitBorderAfterColor", "webkitBorderAfterStyle", "webkitBorderAfterWidth", "webkitBorderBefore", "webkitBorderBeforeColor", "webkitBorderBeforeStyle", "webkitBorderBeforeWidth", "webkitBorderBottomLeftRadius", "webkitBorderBottomRightRadius", "webkitBorderEnd", "webkitBorderEndColor", "webkitBorderEndStyle", "webkitBorderEndWidth", "webkitBorderHorizontalSpacing", "webkitBorderImage", "webkitBorderRadius", "webkitBorderStart", "webkitBorderStartColor", "webkitBorderStartStyle", "webkitBorderStartWidth", "webkitBorderTopLeftRadius", "webkitBorderTopRightRadius", "webkitBorderVerticalSpacing", "webkitBoxAlign", "webkitBoxDecorationBreak", "webkitBoxDirection", "webkitBoxFlex", "webkitBoxOrdinalGroup", "webkitBoxOrient", "webkitBoxPack", "webkitBoxReflect", "webkitBoxShadow", "webkitBoxSizing", "webkitClipPath", "webkitColumnBreakAfter", "webkitColumnBreakBefore", "webkitColumnBreakInside", "webkitColumnCount", "webkitColumnGap", "webkitColumnRule", "webkitColumnRuleColor", "webkitColumnRuleStyle", "webkitColumnRuleWidth", "webkitColumnSpan", "webkitColumnWidth", "webkitColumns", "webkitFilter", "webkitFlex", "webkitFlexBasis", "webkitFlexDirection", "webkitFlexFlow", "webkitFlexGrow", "webkitFlexShrink", "webkitFlexWrap", "webkitFontFeatureSettings", "webkitFontSmoothing", "webkitHyphenateCharacter", "webkitJustifyContent", "webkitLineBreak", "webkitLineClamp", "webkitLocale", "webkitLogicalHeight", "webkitLogicalWidth", "webkitMarginAfter", "webkitMarginBefore", "webkitMarginEnd", "webkitMarginStart", "webkitMask", "webkitMaskBoxImage", "webkitMaskBoxImageOutset", "webkitMaskBoxImageRepeat", "webkitMaskBoxImageSlice", "webkitMaskBoxImageSource", "webkitMaskBoxImageWidth", "webkitMaskClip", "webkitMaskComposite", "webkitMaskImage", "webkitMaskOrigin", "webkitMaskPosition", "webkitMaskPositionX", "webkitMaskPositionY", "webkitMaskRepeat", "webkitMaskSize", "webkitMaxLogicalHeight", "webkitMaxLogicalWidth", "webkitMinLogicalHeight", "webkitMinLogicalWidth", "webkitOpacity", "webkitOrder", "webkitPaddingAfter", "webkitPaddingBefore", "webkitPaddingEnd", "webkitPaddingStart", "webkitPerspective", "webkitPerspectiveOrigin", "webkitPerspectiveOriginX", "webkitPerspectiveOriginY", "webkitPrintColorAdjust", "webkitRtlOrdering", "webkitRubyPosition", "webkitShapeImageThreshold", "webkitShapeMargin", "webkitShapeOutside", "webkitTapHighlightColor", "webkitTextCombine", "webkitTextDecorationsInEffect", "webkitTextEmphasis", "webkitTextEmphasisColor", "webkitTextEmphasisPosition", "webkitTextEmphasisStyle", "webkitTextFillColor", "webkitTextOrientation", "webkitTextSecurity", "webkitTextSizeAdjust", "webkitTextStroke", "webkitTextStrokeColor", "webkitTextStrokeWidth", "webkitTransform", "webkitTransformOrigin", "webkitTransformOriginX", "webkitTransformOriginY", "webkitTransformOriginZ", "webkitTransformStyle", "webkitTransition", "webkitTransitionDelay", "webkitTransitionDuration", "webkitTransitionProperty", "webkitTransitionTimingFunction", "webkitUserDrag", "webkitUserModify", "webkitUserSelect", "webkitWritingMode", "whiteSpace", "whiteSpaceCollapse", "widows", "width", "willChange", "wordBreak", "wordSpacing", "wordWrap", "writingMode", "x", "y", "zIndex", "zoom"] + allStyleProps.forEach(prop => { - if(prop === "translate") return - HTMLElement.prototype[prop] = function(value) { - this.style[prop] = value; - return this; - }; + if (prop === "translate") return; + + const type = cssValueType(prop); + + switch (type) { + case "unit-number": + HTMLElement.prototype[prop] = function(value, unit = "px") { + if (typeof value !== "number" || isNaN(value)) { + throw new Error(`Invalid value for ${prop}: ${value}. Expected a number.`); + } + this.style[prop] = value + unit; + return this; + }; + break; + + case "string": + HTMLElement.prototype[prop] = function(value) { + this.style[prop] = value; + return this; + }; + break; + } }); } + extendHTMLElementWithStyleSetters(); +HTMLElement.prototype.styles = function(cb) { + cb.call(this, this) +} + HTMLElement.prototype.padding = function(one, two, three = "px") { const setPadding = (side, val) => { @@ -285,31 +354,19 @@ HTMLElement.prototype.padding = function(one, two, three = "px") { return this; }; - HTMLElement.prototype.paddingTop = function(value, unit = "px") { + HTMLElement.prototype.paddingVertical = function(value, unit = "px") { if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) throw new Error(`Invalid value: ${value}. Expected a number.`); this.style.paddingTop = value + unit - return this - } - - HTMLElement.prototype.paddingLeft = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.paddingLeft = value + unit - return this - } - - HTMLElement.prototype.paddingBottom = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); this.style.paddingBottom = value + unit return this } - HTMLElement.prototype.paddingRight = function(value, unit = "px") { + HTMLElement.prototype.paddingHorizontal = function(value, unit = "px") { if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) throw new Error(`Invalid value: ${value}. Expected a number.`); this.style.paddingRight = value + unit + this.style.paddingLeft = value + unit return this } @@ -337,62 +394,6 @@ HTMLElement.prototype.margin = function(direction, value, unit = "px") { return this; }; - HTMLElement.prototype.marginTop = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.marginTop = value + unit - return this - } - - HTMLElement.prototype.marginLeft = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.marginLeft = value + unit - return this - } - - HTMLElement.prototype.marginBottom = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.marginBottom = value + unit - return this - } - - HTMLElement.prototype.marginRight = function(value, unit = "px") { - if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - 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") { if ((typeof value !== 'number' && value !== "auto") || Number.isNaN(value)) throw new Error(`Invalid value: ${value}. Expected a number.`); @@ -480,28 +481,34 @@ HTMLElement.prototype.yBottom = function(value, unit = "px") { 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.backgroundImage = function (...values) { + const formatted = values + .map(v => { + if(v.includes("/") && !v.includes("gradient")) { + v = "url(" + v + ")" + } + return String(v).trim(); + }) + .join(", "); -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 -} + this.style.backgroundImage = formatted; + return this; +}; -HTMLElement.prototype.gap = function(value, unit = "px") { - if (typeof value !== 'number' || Number.isNaN(value)) - throw new Error(`Invalid value: ${value}. Expected a number.`); - this.style.gap = value + unit - return this -} +HTMLElement.prototype.center = function () { + this.style.transform = "translate(-50%, -50%)" + return this; +}; + +HTMLElement.prototype.centerX = function () { + this.style.transform = "translateX(-50%)" + return this; +}; + +HTMLElement.prototype.centerY = function () { + this.style.transform = "translateY(-50%)" + return this; +}; /* Elements */ @@ -707,10 +714,103 @@ window.ZStack = function (cb = () => {}) { return div; }; +/* SHAPES */ + +window.svgMethods = function(svg) { + svg.pulse = function (duration = 600) { + this.style.transition = `transform ${duration}ms ease-in-out` + this.style.transform = "scale(1.2)" + setTimeout(() => { + this.style.transform = "scale(1)" + }, duration / 2) + return this + } + + // Rotate (e.g. loading spinner) + svg.rotate = function (degrees = 360, duration = 1000) { + this.style.transition = `transform ${duration}ms linear` + this.style.transform = `rotate(${degrees}deg)` + return this + } + + // Change color + svg.fill = function (color) { + this.setAttribute("fill", color) + return this + } + + svg.height = function (height) { + this.setAttribute("height", height) + return this + } + + svg.width = function (width) { + this.setAttribute("width", width) + return this + } + + svg.stroke = function (width, color) { + this.setAttribute("stroke", color) + this.setAttribute("stroke-width", width) + return this + } + + // Toggle visibility + svg.toggle = function () { + this.style.display = this.style.display === "none" ? "" : "none" + return this + } +} + +window.Rectangle = function (width = "40px", height = "40px") { + const svgNS = "http://www.w3.org/2000/svg"; + const svgEl = document.createElementNS(svgNS, "svg"); + const rectEl = document.createElementNS(svgNS, "rect"); + + // SVG size + svgEl.setAttribute("width", width); + svgEl.setAttribute("height", height); + svgEl.setAttribute("viewBox", "0 0 100 100"); + svgEl.setAttribute("preserveAspectRatio", "xMidYMid meet"); + + // Rectangle: full size, slightly rounded corners + rectEl.setAttribute("x", "15"); // 15% margin from edges + rectEl.setAttribute("y", "15"); + rectEl.setAttribute("width", "70"); // 70% of viewBox + rectEl.setAttribute("height", "70"); + // rectEl.setAttribute("rx", "8"); // rounded corners (optional) + // rectEl.setAttribute("ry", "8"); + + svgEl.appendChild(rectEl); + svgMethods(svgEl); // assuming you have this + quill.render(svgEl); + return svgEl; +} + +window.Triangle = function (width = "40px", height = "40px") { + const svgNS = "http://www.w3.org/2000/svg" + const svgEl = document.createElementNS(svgNS, "svg") + const pathEl = document.createElementNS(svgNS, "path") + + // SVG size + svgEl.setAttribute("width", width) + svgEl.setAttribute("height", height) + svgEl.setAttribute("viewBox", "0 0 100 100") + svgEl.setAttribute("preserveAspectRatio", "xMidYMid meet") + // Right-pointing triangle (Play icon) + pathEl.setAttribute("d", "M 25 15 L 90 50 L 25 85 Z") // ◄ adjust points if needed + + svgEl.appendChild(pathEl) + svgMethods(svgEl) + quill.render(svgEl) + return svgEl +} + + /* EVENTS */ HTMLElement.prototype.onAppear = function(func) { - func(this); + func.call(this); return this; }; @@ -749,15 +849,10 @@ 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); + const onFocus = () => cb.call(this, true); + const onBlur = () => cb.call(this, false); + this._storeListener("focus", onFocus); + this._storeListener("blur", onBlur); return this; }; @@ -766,18 +861,58 @@ HTMLElement.prototype.onKeyDown = function(cb) { return this; }; -/* WHY THIS LISTENER IS THE WAY IT IS: -- If we dispatch the "navigate" event on the window (as one would expect for a "navigate" event), a listener placed on the element will not pick it up. -- However, if we add the event as a window event, it won't have the "this" scope that a callback normally would. -- Then, if we try to add that scope using bind(), it makes the function.toString() unreadable, which means we will get false positives for duplicate listeners. -- Therefore, we just have to attach the navigate event to the element, and manually trigger that when the window listener fires. -*/ -HTMLElement.prototype.onNavigate = function(cb) { - this._storeListener("navigate", cb); - window.addEventListener("navigate", () => this.dispatchEvent(new CustomEvent("navigate"))) +HTMLElement.prototype.onInput = function(cb) { + if(!this.matches('input, textarea, [contenteditable=""], [contenteditable="true"]')) + throw new Error("Can't put input event on non-input element!") + this._storeListener("input", cb); return this; }; +HTMLElement.prototype.onTouch = function(cb) { + const onStart = () => cb.call(this, true); + const onEnd = () => cb.call(this, false); + const onCancel = () => cb.call(this, null); + this._storeListener("touchstart", onStart); + this._storeListener("touchend", onEnd); + this._storeListener("touchcancel", onCancel); + return this; +}; + +HTMLElement.prototype.onTap = function(cb) { + this._storeListener("touchend", cb); + return this; +}; + +/* WHY THIS LISTENER IS THE WAY IT IS: +- If we dispatch the "navigate" event on the window (as one would expect for a "navigate" event), a listener placed on the element will not pick it up. +- However, if we add the listener on the window, it won't have the "this" scope that a callback normally would. Which makes it much less useful. +- Then, if we try to add that scope using bind(), it makes the function.toString() unreadable, which means we cannot detect duplicate listeners. +- Therefore, we just have to attach the navigate event to the element, and manually trigger that when the window listener fires. +*/ +navigateListeners = [] +HTMLElement.prototype.onNavigate = function(cb) { + this._storeListener("navigate", cb); + + let found = false + for(entry of navigateListeners) { + if(entry.cb.toString() === cb.toString() && + this.nodeName === entry.el.nodeName) { + found = true + break; + } + } + if(found === false) { + navigateListeners.push({el: this, cb: cb}) + } + + return this; +}; +window.addEventListener("navigate", () => { + for(entry of navigateListeners) { + entry.el.dispatchEvent(new CustomEvent("navigate")) + } +}) + HTMLElement.prototype.onEvent = function(name, cb) { window._storeListener(window, name, cb); return this; diff --git a/ui/_/code/shared.css b/ui/_/code/shared.css index 62165b6..d807318 100644 --- a/ui/_/code/shared.css +++ b/ui/_/code/shared.css @@ -1,10 +1,11 @@ :root { --main: #FFDFB4; - --accent: black; + --accent: var(--darkbrown); --tan: #FFDFB4; + --gold: #F2B36F; --purple: #251D44; - --green: #0B5538; + --green: #0857265c; --red: #BC1C02; --brown: #c6a476; --darkbrown: #60320c; @@ -17,8 +18,9 @@ @media (prefers-color-scheme: dark) { :root { --main: #251D44; - --accent: #AF7323; - --accent2: var(--periwinkle); + --accent: var(--gold); + --dividerColor: var(--gold); + --accent2: var(--gold); } } @@ -30,21 +32,21 @@ } @font-face { - font-family: 'BonaNova'; + font-family: 'Bona Nova'; src: url('/_/fonts/BonaNova/BonaNova-Regular.woff') format('truetype'); font-weight: normal; font-style: normal; } @font-face { - font-family: 'BonaNova'; + font-family: 'Bona Nova'; src: url('/_/fonts/BonaNova/BonaNova-Bold.woff') format('truetype'); font-weight: bold; font-style: normal; } body { - font-family: 'BonaNova', sans-serif; + font-family: 'Bona Nova', sans-serif; font-size: 16px; background-color: var(--main); color: var(--accent); @@ -79,8 +81,23 @@ a:active { } button { - background-color: var(--green); - color: var(--tan); - padding: 1em; + background-color: transparent; + color: var(--accent); + padding: 0.5em; box-shadow: none; + border: 1px solid var(--accent); + border-radius: 0.3em; +} + +input { + background-color: transparent; + border: 1px solid var(--accent2); + padding-left: 1em; + padding-top: 0.5em; + padding-bottom: 0.5em; + border-radius: 0.3em; +} + +input:focus { + outline: 1px solid var(--red); } diff --git a/ui/_/icons/locationPin.svg b/ui/_/icons/locationPin.svg new file mode 100644 index 0000000..02c7690 --- /dev/null +++ b/ui/_/icons/locationPin.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/ui/_/icons/logo.svg b/ui/_/icons/logo.svg index 317a745..67b933e 100644 --- a/ui/_/icons/logo.svg +++ b/ui/_/icons/logo.svg @@ -1,29 +1,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/_/icons/logo2.svg b/ui/_/icons/logo2.svg new file mode 100644 index 0000000..317a745 --- /dev/null +++ b/ui/_/icons/logo2.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/_/images/fabric.png b/ui/_/images/fabric.png new file mode 100644 index 0000000..c0dfbda Binary files /dev/null and b/ui/_/images/fabric.png differ diff --git a/ui/_/images/knight.png b/ui/_/images/knight.png new file mode 100644 index 0000000..27dcf5e Binary files /dev/null and b/ui/_/images/knight.png differ diff --git a/ui/public/components/JoinForm.js b/ui/public/components/JoinForm.js new file mode 100644 index 0000000..7e30516 --- /dev/null +++ b/ui/public/components/JoinForm.js @@ -0,0 +1,91 @@ +css(` + joinform- input::placeholder { + color: var(--accent) + } +`) + +class JoinForm extends Shadow { + + inputStyles(el) { + return el + .border("1px solid var(--accent)") + } + + render() { + ZStack(() => { + form(() => { + + VStack(() => { + + HStack(() => { + + VStack(() => { + input("First Name") + .attr({name: "firstname", type: "name"}) + .styles(this.inputStyles) + + input("Last Name") + .attr({name: "lastname", type: "name"}) + .styles(this.inputStyles) + + input("Email") + .attr({name: "email", type: "email"}) + .styles(this.inputStyles) + + input("Password") + .attr({name: "password", type: "password"}) + .styles(this.inputStyles) + + input("Confirm Password") + .attr({name: "password", type: "password"}) + .styles(this.inputStyles) + }) + .width(50, "%") + .gap(1, em) + + VStack(() => { + input("Street Address") + .attr({ name: "address1", type: "text", autocomplete: "address-line1" }) + .styles(this.inputStyles) + + input("Apt, Suite, Unit (optional)") + .attr({ name: "address2", type: "text", autocomplete: "address-line2" }) + .styles(this.inputStyles) + + input("City") + .attr({ name: "city", type: "text", autocomplete: "address-level2" }) + .styles(this.inputStyles) + + input("State") + .attr({ name: "state", type: "text", autocomplete: "address-level1" }) + .styles(this.inputStyles) + + input("ZIP Code") + .attr({ name: "zip", type: "text", autocomplete: "postal-code" }) + .styles(this.inputStyles) + + input("Country") + .attr({ name: "country", type: "text", autocomplete: "country-name" }) + .styles(this.inputStyles) + }) + .width(50, "%") + .gap(1, em) + + }) + .gap(2, em) + + button("Submit") + }) + .gap(2, em) + + console.log(window.location.pathname) + }) + .attr({action: window.location.pathname + window.location.search, method: "POST"}) + .x(50, vw).y(53, vh) + .width(60, vw) + .center() + }) + } +} + +register(JoinForm) \ No newline at end of file diff --git a/ui/public/components/NavBar.js b/ui/public/components/NavBar.js index 9e97bb2..d955e16 100644 --- a/ui/public/components/NavBar.js +++ b/ui/public/components/NavBar.js @@ -1,150 +1,55 @@ -css(` - nav-bar { - position: fixed; - top: 0px; - left: 0px; - width: 100vw; - z-index: 1; - height: 100px; - } - - .title-div { - position: absolute; - left: 2em; - top: 2em; - display: flex; - align-items: center; - } - - .links-div { - position: absolute; - right: 3em; - top: 2.75em; - display: flex; - } - - .logo-p { - font-size: 2em; - margin: 0 0 0 15px; - letter-spacing: 1px; - text-align: center; - } - - .nav-hamburger { - margin-right: 8vw; - display: none; - max-height:40px; - } - - .nav-link { - font-size: 1em; - margin: 0 0 0 15px; - letter-spacing: 1px; - } - - @media only screen and (max-width: 820px) { - nav-bar a { - text-decoration: none; - color: var(--green); +class NavBar extends Shadow { + NavButton(text) { + + function normalizeText(text) { + return text.toLowerCase().replace(/[\s?]+/g, ""); } + + function isSelected(el) { + return ("/" + normalizeText(el.innerText)) === window.location.pathname + } + + return p(text) + .cursor("default") + .textUnderlineOffset(0.5, em) + .onAppear(function() { + this.style.textDecoration = isSelected(this) ? "underline" : "" + }) + .onHover(function (hovering) { + if(hovering) { + this.style.textDecoration = "underline" + } else if(!isSelected(this)) { + this.style.textDecoration = "" + } + }) + .onClick(function (done) { + if(done) { + if(!isSelected(this)) { + window.navigateTo(normalizeText(this.innerText)) + } else { + window.navigateTo("/") + } + } + }) - .title-div { - position: static - } - - .logo { - width: 10vw; - } - - .logo-p { - font-size: 8vw; - margin: 0 0 0 1vw; - } - - .nav-hamburger { - display: block; - } - - .links-div { - display:none; - } - - .outer-nav { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 20vw; - background: var(--tan); - display: flex; - z-index: 1; - justify-content: space-between; - align-items: center; - padding: 0 3vw; - max-height:80px; - } } -`) -export default class NavBar extends HTMLElement { - connectedCallback() { - this.innerHTML += /* html */ ` -
-
- -
- -

Hyperia

-
-
- -
- - - - - - -
- - `; - - // Track which anchor was pressed down - let activeLink = null; - - // Get all anchor tags inside this element - const anchors = this.querySelectorAll('a'); - - anchors.forEach(anchor => { - anchor.addEventListener('mousedown', (e) => { - activeLink = anchor; - }); - }); - - // Listen globally for mouseup, to detect release anywhere - document.addEventListener('mouseup', (e) => { - if (activeLink) { - // Navigate to the href of the activeLink - window.location.href = activeLink.href; - activeLink = null; - } - }); - - const hamburger = this.querySelectorAll("img")[1] - const sidebar = document.querySelector("side-bar"); - - hamburger.addEventListener("click", () => { - if (sidebar.style.right === "0vw") { - sidebar.style.right = "-100vw"; - } else { - sidebar.style.right = "0vw"; - } - }); + render() { + HStack(() => { + this.NavButton("WHY?") + this.NavButton("EVENTS") + div().width(2.5, em).height(2.5, em) + this.NavButton("JOIN") + this.NavButton("SIGN IN") + }) + .x(50, vw).y(4, em) + .center() + .fontSize(0.85, em) + .justifyContent("center") + .gap(3, em) + .paddingRight(2, em) + .width(50, vw) } } -customElements.define("nav-bar", NavBar) +register(NavBar) \ No newline at end of file diff --git a/ui/public/index.css b/ui/public/index.css deleted file mode 100644 index e69de29..0000000 diff --git a/ui/public/index.html b/ui/public/index.html index fab5fd2..28a9064 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -9,110 +9,13 @@ body { font-size: 16px; + background-image: url("_/images/fabric.png"); + background-size: 33vw auto; /* width height of each tile */ } - - .main-image { - content:url("_/images/castle.svg"); position: absolute; height: 70vh; bottom: 2vh; left: 55vw; transform: translateX(-50%) - } - @media (max-width: 600px) { - .main-image { - max-width: 90vw; bottom: -7vh; - }} - @media (prefers-color-scheme: dark) { - .main-image { - content:url("_/images/castle-dark3.svg"); - height: 1000px; bottom: -34vmin; left: 26vw; - }} - @media (prefers-color-scheme: dark) and (max-width: 600px) { - .main-image { - max-width: 195vw; height: 80vh; left: 0vw; - }} - - + - hyperia - - - - -
- - -
-

A Classical Christian Society

- -
-

Hyperia is a private club, focused on promoting the Classical Christian movement in America.

-

Inspired by Classical Christian schools, Hyperia is a similar space for adults.

-
- -
-
- - -
- - -
-
- -
- - - - - - \ No newline at end of file diff --git a/ui/public/index.js b/ui/public/index.js new file mode 100644 index 0000000..b6b7e32 --- /dev/null +++ b/ui/public/index.js @@ -0,0 +1,2 @@ +import "./pages/Home.js" +Home() \ No newline at end of file diff --git a/ui/public/pages/Events.js b/ui/public/pages/Events.js new file mode 100644 index 0000000..97d2f58 --- /dev/null +++ b/ui/public/pages/Events.js @@ -0,0 +1,7 @@ +class Events extends Shadow { + render() { + + } +} + +register(Events) \ No newline at end of file diff --git a/ui/public/pages/Home.js b/ui/public/pages/Home.js new file mode 100644 index 0000000..37ec179 --- /dev/null +++ b/ui/public/pages/Home.js @@ -0,0 +1,70 @@ +import "../components/NavBar.js" +import "../components/JoinForm.js" +import "./Why.js" +import "./Events.js" +import "./Join.js" +import "./SignIn.js" + +class Home extends Shadow { + render() { + + ZStack(() => { + + NavBar() + + img("_/icons/logo.svg", "2.5em") + .onClick((done) => { + if(!done) return + window.navigateTo("/") + }) + .position("absolute") + .left(50, vw).top(4, em) + .center() + .transform("translate(-2em, -50%)") + + switch(window.location.pathname) { + case "/": + img("_/images/knight.png", "29vw") + .position("absolute") + .left(50, vw).top(50, vh) + .center() + + p("H   Y   P   E   R   I   A") + .x(50, vw).y(50, vh) + .center() + .color("var(--gold)") + .fontSize(5, vw) + + p("A CLASSICAL CHRISTIAN ASSOCIATION") + .x(50, vw).y(94, vh) + .center() + .letterSpacing(0.3, em) + break; + case "/why": + Why() + break; + case "/events": + Events() + break; + case "/join": + Join() + break; + case "/signin": + SignIn() + break; + + default: + if(window.location.pathname.startsWith("/signup")) { + JoinForm() + } + } + + }) + .onNavigate(() => { + this.rerender() + }) + + } +} + +register(Home) \ No newline at end of file diff --git a/ui/public/pages/Join.js b/ui/public/pages/Join.js new file mode 100644 index 0000000..41abbc1 --- /dev/null +++ b/ui/public/pages/Join.js @@ -0,0 +1,9 @@ +class Join extends Shadow { + render() { + p("Membership is invitation-only. Look out for us in person, or come to an event!") + .x(50, vw).y(50, vh) + .center() + } +} + +register(Join) \ No newline at end of file diff --git a/ui/public/pages/SIgnIn.js b/ui/public/pages/SIgnIn.js new file mode 100644 index 0000000..b7eaf13 --- /dev/null +++ b/ui/public/pages/SIgnIn.js @@ -0,0 +1,33 @@ +css(` + signin- input::placeholder { + color: var(--accent) + } +`) + +class SignIn extends Shadow { + + inputStyles(el) { + console.log(el) + return el + .border("1px solid var(--accent)") + } + + render() { + ZStack(() => { + form(() => { + input("Email") + .attr({name: "email", type: "email"}) + .styles(this.inputStyles) + input("Password") + .attr({name: "password", type: "password"}) + .styles(this.inputStyles) + button("Submit") + }) + .attr({action: "/login", method: "POST"}) + .x(50, vw).y(50, vh) + .center() + }) + } +} + +register(SignIn) \ No newline at end of file diff --git a/ui/public/pages/Why.js b/ui/public/pages/Why.js new file mode 100644 index 0000000..9540886 --- /dev/null +++ b/ui/public/pages/Why.js @@ -0,0 +1,9 @@ +class Why extends Shadow { + render() { + p("The West is Falling. Why Not?") + .x(50, vw).y(50, vh) + .center() + } +} + +register(Why) \ No newline at end of file diff --git a/ui/public/pages/about.html b/ui/public/pages/about.html deleted file mode 100644 index 2287388..0000000 --- a/ui/public/pages/about.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - Hyperia - - - - - - - - - hyperia - - -
- -
- -
-

- -
- - - -
- - \ No newline at end of file diff --git a/ui/public/pages/join.html b/ui/public/pages/join.html deleted file mode 100644 index 74e2da4..0000000 --- a/ui/public/pages/join.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - Hyperia - - - - - - - - - hyperia - - -
- -
- -
-

- -
- - - -
- - \ No newline at end of file diff --git a/ui/public/pages/signin.html b/ui/public/pages/signin.html deleted file mode 100644 index f4802ba..0000000 --- a/ui/public/pages/signin.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - Hyperia - - - - - - - - - hyperia - - -
-
- -
- -
-

-
- -
-
- - \ No newline at end of file diff --git a/ui/site/apps/Forum/Forum.js b/ui/site/apps/Forum/Forum.js index 6c6c3b9..b92e5fd 100644 --- a/ui/site/apps/Forum/Forum.js +++ b/ui/site/apps/Forum/Forum.js @@ -1,9 +1,9 @@ css(` - messages- { + forum- { font-family: 'Bona'; } - messages- input::placeholder { + forum- input::placeholder { font-family: 'Bona Nova'; font-size: 0.9em; color: var(--accent); @@ -22,7 +22,7 @@ css(` } `) -class Messages extends Shadow { +class Forum extends Shadow { friends = [] conversations = [] @@ -49,27 +49,6 @@ class Messages extends Shadow { .paddingTop(2, em) .gap(0, em) .borderRight("1px solid var(--accent2)") - - VStack(() => { - h3("Conversations") - .marginTop(0) - .marginBottom(1, em) - .marginLeft(0.4, em) - - if (this.conversations.length > 1) { - for(let i = 0; i < this.conversations.length; i++) { - p(this.conversations[i].name) - } - } else { - p("No Conversations!") - } - }) - .height(100, vh) - .paddingLeft(2, em) - .paddingRight(2, em) - .paddingTop(2, em) - .gap(0, em) - .borderRight("1px solid var(--accent2)") }) .width(100, "%") .x(0).y(13, vh) @@ -175,4 +154,4 @@ class Messages extends Shadow { } } -register(Messages) \ No newline at end of file +register(Forum) \ No newline at end of file diff --git a/ui/site/apps/Jobs/JobsSidebar.js b/ui/site/apps/Jobs/JobsSidebar.js index 8176c10..0cc61df 100644 --- a/ui/site/apps/Jobs/JobsSidebar.js +++ b/ui/site/apps/Jobs/JobsSidebar.js @@ -3,14 +3,22 @@ class JobsSidebar extends Shadow { VStack(() => { h3("Location") .color("var(--accent2)") + .marginBottom(0, em) - + HStack(() => { + input("Location", "100%") + .paddingLeft(3, em) + .paddingVertical(0.75, em) + .backgroundImage("/_/icons/locationPin.svg") + .backgroundRepeat("no-repeat") + .backgroundSize("18px 18px") + .backgroundPosition("10px center") + }) }) .paddingTop(1, em) .paddingLeft(3, em) .paddingRight(3, em) .gap(1, em) - .borderRight("1px solid var(--accent2)") .borderTop("1px solid var(--accent2)") .minWidth(10, vw) } diff --git a/ui/site/apps/Market/MarketGrid.js b/ui/site/apps/Market/MarketGrid.js index 151bc0d..a81821f 100644 --- a/ui/site/apps/Market/MarketGrid.js +++ b/ui/site/apps/Market/MarketGrid.js @@ -22,10 +22,13 @@ class MarketGrid extends Shadow { .marginTop(0.1, em) .marginBottom(1, em) .marginLeft(0.4, em) - .color("var(--accent2)") + .color("var(--accent)") + .opacity(0.7) if (this.listings.length > 0) { ZStack(() => { + // BuyModal() + for (let i = 0; i < this.listings.length; i++) { const rating = this.listings[i].stars const percent = (rating / 5) @@ -74,6 +77,11 @@ class MarketGrid extends Shadow { .marginBottom(0.5, em) button("Buy Now") + .onClick((finished) => { + if(finished) { + + } + }) }) .padding(1, em) @@ -91,7 +99,6 @@ class MarketGrid extends Shadow { .height(100, vh) .paddingLeft(2, em) .paddingRight(2, em) - .paddingTop(2, em) .gap(0, em) .width(100, "%") } diff --git a/ui/site/apps/Messages.js b/ui/site/apps/Messages/Messages.js similarity index 100% rename from ui/site/apps/Messages.js rename to ui/site/apps/Messages/Messages.js diff --git a/ui/site/apps/Tasks/Tasks.js b/ui/site/apps/Tasks/Tasks.js new file mode 100644 index 0000000..4b0e733 --- /dev/null +++ b/ui/site/apps/Tasks/Tasks.js @@ -0,0 +1,153 @@ +css(` + tasks- { + font-family: 'Bona'; + } + + tasks- input::placeholder { + font-family: 'Bona Nova'; + font-size: 0.9em; + color: var(--accent); + } + + input[type="checkbox"] { + appearance: none; /* remove default style */ + -webkit-appearance: none; + width: 1em; + height: 1em; + border: 1px solid var(--accent); + } + + input[type="checkbox"]:checked { + background-color: var(--red); + } +`) + +class Tasks extends Shadow { + projects = [ + { + "title": "Blockcatcher", + "tasks": {} + } + ] + columns = [ + { + "title": "backlog", + "tasks": {} + } + ] + + render() { + ZStack(() => { + HStack(() => { + VStack(() => { + h3("Projects") + .marginTop(0) + .marginBottom(1, em) + .marginLeft(0.4, em) + + if (this.projects.length >= 1) { + for(let i = 0; i < this.projects.length; i++) { + p(this.projects[i].title) + } + } else { + p("No Projects!") + } + }) + .height(100, vh) + .paddingLeft(2, em) + .paddingRight(2, em) + .paddingTop(2, em) + .gap(0, em) + .borderRight("0.5px solid var(--accent2)") + + HStack(() => { + if (this.columns.length >= 1) { + for(let i = 0; i < this.columns.length; i++) { + p(this.columns[i].name) + } + } else { + p("No Conversations!") + } + }) + .height(100, vh) + .paddingLeft(2, em) + .paddingRight(2, em) + .paddingTop(2, em) + .gap(0, em) + .borderRight("0.5px solid var(--accent2)") + }) + .width(100, "%") + .x(0).y(13, vh) + .borderTop("0.5px solid var(--accent2)") + + p("0 Items") + .position("absolute") + .x(50, vw).y(50, vh) + .transform("translate(-50%, -50%)") + + HStack(() => { + input("Search tasks...", "45vw") + .attr({ + "type": "text" + }) + .fontSize(1.1, em) + .paddingLeft(1.3, em) + .background("transparent") + .border("0.5px solid var(--accent2)") + .outline("none") + .color("var(--accent)") + .borderRadius(10, px) + + button("Search") + .marginLeft(2, em) + .borderRadius(10, px) + .background("transparent") + .border("0.5px solid var(--accent2)") + .color("var(--accent)") + .fontFamily("Bona Nova") + .onHover(function (hovering) { + if(hovering) { + this.style.background = "var(--green)" + + } else { + this.style.background = "transparent" + + } + }) + + button("+ New Task") + .width(9, em) + .marginLeft(1, em) + .borderRadius(10, px) + .background("transparent") + .border("0.5px solid var(--accent2)") + .color("var(--accent)") + .fontFamily("Bona Nova") + .onHover(function (hovering) { + if(hovering) { + this.style.background = "var(--green)" + + } else { + this.style.background = "transparent" + + } + }) + .onClick((clicking) => { + console.log(this, "clicked") + }) + + }) + .x(55, vw).y(4, vh) + .position("absolute") + .transform("translateX(-50%)") + }) + .width(100, "%") + .height(100, "%") + } + + connectedCallback() { + // Optional additional logic + } +} + +register(Tasks) \ No newline at end of file diff --git a/ui/site/components/AppMenu.js b/ui/site/components/AppMenu.js index 0a3974b..4fafbe6 100644 --- a/ui/site/components/AppMenu.js +++ b/ui/site/components/AppMenu.js @@ -16,8 +16,9 @@ css(` padding-bottom: 4em; bottom: 1em; border-radius: 12px; + background-color: var(--green); } - + app-menu p { cursor: default; transition: transform .3s, text-decoration .3s; @@ -56,6 +57,8 @@ class AppMenu extends Shadow { render() { VStack(() => { HStack(() => { + p("Forum") + p("Tasks") p("Messages") p("Market") p("Jobs") diff --git a/ui/site/components/AppWindow.js b/ui/site/components/AppWindow.js index 858475d..ca8f6a7 100644 --- a/ui/site/components/AppWindow.js +++ b/ui/site/components/AppWindow.js @@ -1,6 +1,8 @@ -import "../apps/Jobs/Jobs.js" -import "../apps/Messages.js" +import "../apps/Forum/Forum.js" +import "../apps/Tasks/Tasks.js" +import "../apps/Messages/Messages.js" import "../apps/Market/Market.js" +import "../apps/Jobs/Jobs.js" class AppWindow extends Shadow { app; @@ -13,8 +15,11 @@ class AppWindow extends Shadow { render() { ZStack(() => { switch(this.app) { - case "Jobs": - Jobs() + case "Forum": + Forum() + break; + case "Tasks": + Tasks() break; case "Messages": Messages() @@ -22,12 +27,16 @@ class AppWindow extends Shadow { case "Market": Market() break; + case "Jobs": + Jobs() + break; } }) .display(this.app ? '' : 'none') .width(100, "vw") .height(100, "vh") - .backgroundColor("var(--main)") + .backgroundImage("/_/images/fabric.png") + .backgroundSize("33vw auto") .position("fixed") .top(0) .left(0) diff --git a/ui/site/components/Home.js b/ui/site/components/Home.js index 4005cbc..d81f34d 100644 --- a/ui/site/components/Home.js +++ b/ui/site/components/Home.js @@ -10,8 +10,8 @@ class Home extends Shadow { ZStack(() => { img("/_/icons/logo.svg", "2.5em") .position("fixed") - .left("3em") - .top("3vh") + .left(3, em) + .top(3, vh) .zIndex(3) .onClick(() => { window.navigateTo("/") @@ -21,7 +21,7 @@ class Home extends Shadow { .width(100, vw) .height(100, vh) .margin("0px") - .backgroundImage("url('/_/images/the_return.webp')") + .backgroundImage("/_/images/the_return.webp") .backgroundSize("cover") .backgroundPosition("48% 65%") .backgroundRepeat("no-repeat") @@ -43,19 +43,29 @@ class Home extends Shadow { AppWindow("Market") AppMenu("Market") break; + case "/app/tasks": + AppWindow("Tasks") + AppMenu("Tasks") + break; + case "/app/forum": + AppWindow("Forum") + AppMenu("Forum") + break; + default: + throw new Error("Unknown route!") } ProfileButton() .zIndex(1) .cursor("default") .position("fixed") - .top("5.5vh") - .right("4.5vw") + .top(5.5, vh) + .right(4.5, vw) a("/signout", "Sign Out") .position("fixed") - .top("5vh") - .right("2em") + .top(5, vh) + .right(2, em) .background("transparent") .border(window.location.pathname === "/" ? "1px solid var(--tan)" : "1px solid var(--accent2)") .color(window.location.pathname === "/" ? "var(--tan)" : "var(--accent)")