Former-commit-id: a7f582489dec8308c131d88464a80720c26b3692
This commit is contained in:
Ben
2022-03-25 15:35:45 +00:00
parent fe7ac49c0b
commit 93686c10e1
13 changed files with 151 additions and 31 deletions

View File

@@ -9,8 +9,17 @@ class CompactProductListing extends Component {
Render() {
return {
template: SideLoad('./components/templates/compact-listing.html'),
style: SideLoad('./components/css/compact-listing.css'),
template: `
{this.state.name}
{this.state.desc}
£{this.state.price}
<img src="{this.state.image}"></img>
`,
style: `
compact-listing-component {
display: flex;
}
`,
};
}

View File

@@ -2,8 +2,6 @@
// neccesary is fetched from the server
const preLoadCache = [];
export function SideLoad(path) {
console.log(preLoadCache);
return new Promise((resolve) => {
if (preLoadCache[path]) {
resolve(preLoadCache[path]);
@@ -34,9 +32,9 @@ export class Component extends HTMLElement {
}
// Override these
Render() { this.__WARN('Render'); }
OnceRendered() { this.__WARN('Render'); }
static __IDENTIFY() { this.__WARN('identify'); }
Render() { Component.__WARN('Render'); }
OnceRendered() { Component.__WARN('Render'); }
static __IDENTIFY() { Component.__WARN('identify'); }
connectedCallback() {
// set up to watch all attributes for changes
@@ -45,9 +43,11 @@ export class Component extends HTMLElement {
// if there are any attributes related to the element
// be sure to include them in the state to be sure that
// they can be resolved
let stateUpdateQueue = { ...this.state };
for (const attribute of this.attributes) {
this.SetState({ ...this.state, [attribute.name]: attribute.value });
stateUpdateQueue = { ...stateUpdateQueue, [attribute.name]: attribute.value };
}
this.setState(stateUpdateQueue);
if (this.attributes.length === 0) {
this.__INVOKE_RENDER();
@@ -72,27 +72,36 @@ export class Component extends HTMLElement {
attributeChangedCallback(name, newValue) {
console.log(`attribute changed: ${name} ${newValue}`);
this.SetState({ ...this.state, [name]: newValue });
this.setState({ ...this.state, [name]: newValue });
this.__INVOKE_RENDER();
}
get GetState() {
get getState() {
return this.state;
}
SetState(newState) {
setState(newState) {
this.state = newState;
this.__INVOKE_RENDER(Object.bind(this));
}
async __INVOKE_RENDER() {
const res = this.Render(Object.bind(this));
let res = this.Render(Object.bind(this));
if (!res.template || !res.style) {
this.__ERR('no template or style');
if (res instanceof Promise) {
res = await res;
}
if (res.template === undefined || res.style === undefined) {
Component.__ERR('no template or style');
return;
}
// way to formally update state WITHOUT triggering a re-render
if (res.state) {
this.state = res.state;
}
// if res.template is a promise, we need to wait to resolve it
if (res.template instanceof Promise) {
res.template = await res.template;
@@ -113,6 +122,7 @@ export class Component extends HTMLElement {
if (m[1].startsWith('this.state')) {
const stateKey = m[1].substring(11);
const stateValue = this.state[stateKey];
console.log('attempting to replace', m[0], 'with', stateValue);
if (stateValue === undefined) {
continue;
}

View File

@@ -0,0 +1,48 @@
import { RegisterComponent, Component } from './components.mjs';
class ProductList extends Component {
static __IDENTIFY() { return 'product-list'; }
constructor() {
super(ProductList);
}
async Render() {
const route = this.state.getroute;
const products = await fetch(route).then(response => response.json());
return {
template: `
<h1>{this.state.title}</h1>
<div class="product-list">
${products.data.map(product => {
return `<compact-listing-component name="${product.name}"
desc="${product.description}"
image="${product.image}"
price="${product.price}"></compact-listing-component>`;
})}
</div>
`,
style: `
.product-list {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
max-width: 800px;
}
`,
state: {
...this.getState,
products,
},
};
}
OnceRendered() {
}
}
RegisterComponent(ProductList);

View File

@@ -1,4 +1,4 @@
import { RegisterComponent, Component, SideLoad } from './components.mjs';
import { RegisterComponent, Component } from './components.mjs';
class StoreFront extends Component {
static __IDENTIFY() { return 'storefront'; }
@@ -9,18 +9,22 @@ class StoreFront extends Component {
Render() {
return {
template: SideLoad('./components/templates/storefront.html'),
style: SideLoad('./components/css/storefront.css'),
template: `
<product-list-component id="featured"
title="Featured Lego Sets"
getroute="/api/sets/featured">
</product-list-component>
`,
style: `
product-list-component {
display: block;
margin: 0 auto;
}`,
};
}
OnceRendered() {
const items = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7', 'item8', 'item9', 'item10'];
for (const item of items) {
const itemElement = document.createElement('compact-listing-component');
itemElement.setAttribute('item', item);
this.root.appendChild(itemElement);
}
}
}

View File

@@ -1 +0,0 @@
{this.state.item}

View File

@@ -1,3 +0,0 @@
<div class="product-list">
</div>

View File

@@ -13,6 +13,7 @@
<script type="module" src="components/navbar.mjs"></script>
<script type="module" src="components/notificationbar.mjs"></script>
<script type="module" src="components/storefront.mjs"></script>
<script type="module" src="components/product-list.mjs"></script>
<script type="module" src="components/compact-listing.mjs"></script>
<script type="module" src="index.mjs"></script>
@@ -24,8 +25,7 @@
<navbar-component></navbar-component>
<storefront-component></storefront-component>
<!-- <canvas id="webglviewer" width="300" height="300"></canvas> -->
<!-- <canvas id="webglviewer" width="300" height="300"></canvas>
<canvas id="webglviewer" width="300" height="300"></canvas>

View File

@@ -13,6 +13,7 @@ automatically every request
| GET | /api/search/ | query, page | no | |
| GET | /api/bricks/ | query, page | no | |
| GET | /api/sets/ | query, page | no | |
| GET | /api/sets/featured | page | no | |
| GET | /api/brick/:id/ | | no | |
| GET | /api/set/:id/ | | no | |
| GET | /api/cdn/:id/ | | no | |
@@ -27,6 +28,8 @@ automatically every request
## Query structure
### /api/search/
### /api/bricks/

View File

@@ -25,7 +25,7 @@ class MyComponent extends Component {
That is the simplest form a component can be, it won't render but it
will register it's self in the DOM and be accessable with the
`<mycomponent-component></mycomponent-component>` tag within the DOM.
`<mycomponent></mycomponent>` tag within the DOM.
In order to get some stuff rendering in there, it is important to
override the `Render` method, returning an object with a template
@@ -76,7 +76,7 @@ a given component has a state which can be user-defined `this.state.x=`
or an attribute to the component tag in the HTML, or both. When the
state changes, the component is re-renderered.
State is updated with `SetState()`.
State is updated with `setState()`.
Within the HTML, any instance of `{this.state.}` will be replaced with
the internal state of the component.

View File

@@ -10,6 +10,7 @@ function Init() {
Server.App.get('/api/search/', []);
Server.App.get('/api/bricks/', Bricks.Query);
Server.App.get('/api/sets/');
Server.App.get('/api/sets/featured/', Sets.Featured);
Server.App.get('/api/brick/:id/');
Server.App.get('/api/set/:id/');

View File

@@ -0,0 +1,49 @@
const Controller = require('../controllers/set-controller.js');
function Featured(req, res, next) {
const query = req.query;
res.send(JSON.stringify({
data: [
{
id: '1',
name: 'Brick 1',
description: 'Brick 1 description',
price: '1.00',
image: 'https://via.placeholder.com/300x300',
},
{
id: '2',
name: 'Brick 2',
description: 'Brick 2 description',
price: '2.00',
image: 'https://via.placeholder.com/300x300',
},
{
id: '3',
name: 'Brick 3',
description: 'Brick 3 description',
price: '3.00',
image: 'https://via.placeholder.com/300x300',
},
],
}));
// Validation
// const validation = Controller.ValidateQuery(query);
// if (!validation.isValid) {
// return res.status(400).json({
// error: {
// short: validation.error,
// long: validation.longError,
// },
// });
// }
// next();
}
module.exports = {
Featured,
};