Fixed a bug relating to custom-elements's first vnode where render(){ return <div class="example" /> } would set the example-class on the custom-element itself.
Added support for className and style similar to react Cleaned up some comments Reworked how tests are built in order to add a new test "pdf" which was a small side-project where previous mentioned bug showed up, it's an example using HTML to create a PDF for printing
This commit is contained in:
parent
93fb6927ca
commit
4ca54727f1
24
.idea/codeStyles/Project.xml
generated
24
.idea/codeStyles/Project.xml
generated
@ -24,29 +24,5 @@
|
||||
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||
</formatting-settings>
|
||||
</DBN-SQL>
|
||||
<DBN-PSQL>
|
||||
<case-options enabled="true">
|
||||
<option name="KEYWORD_CASE" value="lower" />
|
||||
<option name="FUNCTION_CASE" value="lower" />
|
||||
<option name="PARAMETER_CASE" value="lower" />
|
||||
<option name="DATATYPE_CASE" value="lower" />
|
||||
<option name="OBJECT_CASE" value="preserve" />
|
||||
</case-options>
|
||||
<formatting-settings enabled="false" />
|
||||
</DBN-PSQL>
|
||||
<DBN-SQL>
|
||||
<case-options enabled="true">
|
||||
<option name="KEYWORD_CASE" value="lower" />
|
||||
<option name="FUNCTION_CASE" value="lower" />
|
||||
<option name="PARAMETER_CASE" value="lower" />
|
||||
<option name="DATATYPE_CASE" value="lower" />
|
||||
<option name="OBJECT_CASE" value="preserve" />
|
||||
</case-options>
|
||||
<formatting-settings enabled="false">
|
||||
<option name="STATEMENT_SPACING" value="one_line" />
|
||||
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||
</formatting-settings>
|
||||
</DBN-SQL>
|
||||
</code_scheme>
|
||||
</component>
|
||||
1
.idea/webResources.xml
generated
1
.idea/webResources.xml
generated
@ -15,6 +15,7 @@
|
||||
<path value="file://$PROJECT_DIR$/packages/csx" />
|
||||
<path value="file://$PROJECT_DIR$/packages/babel-plugin-transform-csx" />
|
||||
<path value="file://$PROJECT_DIR$/packages/csx/src" />
|
||||
<path value="file://$PROJECT_DIR$/tests" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
|
||||
21
package.json
21
package.json
@ -3,6 +3,7 @@
|
||||
"version": "0.0.1",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/register": "latest",
|
||||
"@babel/cli": "csx",
|
||||
"@babel/core": "csx",
|
||||
"@babel/plugin-proposal-class-properties": "csx",
|
||||
@ -20,19 +21,23 @@
|
||||
"rollup-plugin-babel": "csx",
|
||||
"rollup-plugin-node-resolve": "latest",
|
||||
"rollup-plugin-commonjs": "latest",
|
||||
"rollup-plugin-json": "latest",
|
||||
"rollup-plugin-postcss": "latest",
|
||||
"rollup-plugin-terser": "latest",
|
||||
"rollup-plugin-copy": "latest",
|
||||
"rollup-plugin-sass": "latest",
|
||||
"@cerxes/host": "latest",
|
||||
"postcss": "latest",
|
||||
"postcss-preset-env": "latest",
|
||||
"postcss-import": "latest",
|
||||
"serve": "latest",
|
||||
"npm-run-all": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "npm-run-all -p watch:babel-transform-csx watch:csx watch:test start:test",
|
||||
"build": "npm-run-all -s build:babel-transform-csx build:csx build:test",
|
||||
"watch": "npm-run-all -p watch:babel-transform-csx watch:csx watch:test",
|
||||
"start:test": "serve public",
|
||||
"build:test": "rollup -c",
|
||||
"watch:test": "rollup -c -w",
|
||||
"dev": "npm-run-all -p watch:babel-transform-csx watch:csx watch:tests serve:tests",
|
||||
"build": "npm-run-all -s build:babel-transform-csx build:csx build:tests",
|
||||
"watch": "npm-run-all -p watch:babel-transform-csx watch:csx watch:tests",
|
||||
"build:tests": "node -r @babel/register ./tests/cfg/rollup-build.js build",
|
||||
"watch:tests": "node -r @babel/register ./tests/cfg/rollup-build.js watch",
|
||||
"serve:tests": "serve public",
|
||||
"build:babel-transform-csx": "cd packages/babel-plugin-transform-csx && npm run build",
|
||||
"build:csx": "cd packages/csx && npm run build",
|
||||
"watch:babel-transform-csx": "cd packages/babel-plugin-transform-csx && npm run watch",
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
"rollup-plugin-commonjs": "latest",
|
||||
"rollup-plugin-terser": "latest",
|
||||
"rollup-plugin-json": "latest",
|
||||
"rollup-plugin-postcss": "latest",
|
||||
"npm-run-all": "latest"
|
||||
},
|
||||
"dependencies":{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {render} from "../vdom";
|
||||
import {render, Host} from "../vdom";
|
||||
|
||||
// TODO the custom-element class could be removed, and its functionality implemented through the @defineElement decorator
|
||||
// Currently there are issues: if a custom-element reimplements the connectedCallback, they need to magically know that
|
||||
@ -23,6 +23,9 @@ export class CustomElement extends HTMLElement {
|
||||
update(){
|
||||
if (this.render) {
|
||||
let newVNode = this.render();
|
||||
if(newVNode.type !== Host){
|
||||
newVNode = {type: Host, children: newVNode instanceof Array? newVNode : [newVNode]};
|
||||
}
|
||||
render(newVNode, {
|
||||
host: this,
|
||||
old: this.#renderedVNode,
|
||||
|
||||
@ -6,25 +6,23 @@ import {
|
||||
NodeTreeRenderer, NativeRenderer
|
||||
} from "./renderers";
|
||||
|
||||
// TODO Element renderer (for things that are already DOM-elements)
|
||||
export function getNodeMeta(vnode){
|
||||
if(vnode===undefined||vnode===null) return undefined; // Indicate it shouldn't render
|
||||
if(vnode instanceof Element) return {renderer: NativeRenderer, normedType: Element};
|
||||
export function getNodeMeta(vnode) {
|
||||
if (vnode === undefined || vnode === null) return undefined; // Indicate it shouldn't render
|
||||
if (vnode instanceof Element) return { renderer: NativeRenderer, normedType: Element };
|
||||
let type = vnode?.type;
|
||||
if(!type) return {renderer: PrimitiveRenderer, normedType: Primitive};
|
||||
else if(type===Host) return {renderer: HostNodeRenderer, normedType: Host};
|
||||
else if(type===ShadowDOM) return {renderer: ShadowNodeRenderer, normedType: ShadowDOM};
|
||||
else return {renderer: NodeTreeRenderer, normedType: window.customElements?.get(type)??type};
|
||||
if (!type) return { renderer: PrimitiveRenderer, normedType: Primitive };
|
||||
else if (type === Host) return { renderer: HostNodeRenderer, normedType: Host };
|
||||
else if (type === ShadowDOM) return { renderer: ShadowNodeRenderer, normedType: ShadowDOM };
|
||||
else return { renderer: NodeTreeRenderer, normedType: window.customElements?.get(type) ?? type };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} RenderOptions
|
||||
* @category VDOM
|
||||
* @property {Element} [host] - A host element to update to the specified VDOM
|
||||
* @property {Element} [host] - The element to update to the specified VDOM
|
||||
* @property {VNode} [old] - Old VNode representation of rendered host
|
||||
* @property {Document} [document] - The document we're rendering to
|
||||
* @property {Element} [parent] - The parent element (TODO not sure what this will do when specified; Insert it as child element of the parent where?)
|
||||
* @property {Element} [parent] - The parent element (TODO not sure what this will do when specified; Insert it as child element of the parent where?)
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -35,9 +33,12 @@ export function getNodeMeta(vnode){
|
||||
* @return {Element}
|
||||
*/
|
||||
export function render(vnode, opts = {}) {
|
||||
// TODO innerHTML, innerText and other tags/props that are trickyer then just mapping value to attribute (see NodeTreeRenderer)
|
||||
// TODO ref-prop (should it only return once all child els are created and appended to the child?!)
|
||||
|
||||
// TODO this code could use restructuring when opts.host and vnode.type are incompatible (non updatable type), the host element should be replaced
|
||||
// with a newly created element, like it does with all child-elements..
|
||||
// General flow of this code is to process the hierarchy using a queue (so no recursion is used)
|
||||
// on each node of the hierarchy a renderer is determined which is compared to the renderer of the previous version of this vnode-hierarchy
|
||||
// to determine if these nodes can be updated (e.g updating a div, or textnode) and if they behave as a child-node (e.g. shows up in childNodes)
|
||||
// or are some other special type of node (like Host or ShadowDOM)
|
||||
/**
|
||||
*
|
||||
* @type {VRenderState}
|
||||
@ -48,7 +49,7 @@ export function render(vnode, opts = {}) {
|
||||
queue: [{
|
||||
// Start item
|
||||
item: {
|
||||
document: opts.document||document,
|
||||
document: opts.document || document,
|
||||
host: opts.host,
|
||||
parent: opts.parent,
|
||||
old: opts.old,
|
||||
@ -59,14 +60,14 @@ export function render(vnode, opts = {}) {
|
||||
};
|
||||
|
||||
let newRoot = undefined;
|
||||
while(state.queue.length>0){
|
||||
let {item, meta, previous} = state.queue.splice(0,1)[0];
|
||||
while (state.queue.length > 0) {
|
||||
let { item, meta, previous } = state.queue.splice(0, 1)[ 0 ];
|
||||
let renderer = meta.renderer;
|
||||
if(!renderer) throw new Error("No renderer for vnode", item.vnode);
|
||||
if (!renderer) throw new Error("No renderer for vnode", item.vnode);
|
||||
|
||||
// SVG handling..
|
||||
if(!item.inSvg && item.vnode?.type === 'svg') item.inSvg = true;
|
||||
else if(item.inSvg && item.vnode?.type === 'foreignObject') item.inSvg = false;
|
||||
if (!item.inSvg && item.vnode?.type === 'svg') item.inSvg = true;
|
||||
else if (item.inSvg && item.vnode?.type === 'foreignObject') item.inSvg = false;
|
||||
|
||||
// Create the element if no matching existing element was set
|
||||
let newlyCreated = false;
|
||||
@ -74,8 +75,8 @@ export function render(vnode, opts = {}) {
|
||||
item.host = renderer.create(item, meta);
|
||||
newlyCreated = true;
|
||||
|
||||
if(item.vnode?.props?.ref){// If props specify a ref-function, queue it to be called at the end of the render
|
||||
state.refs.push([item.vnode.props.ref,item.host]);
|
||||
if (item.vnode?.props?.ref) {// If props specify a ref-function, queue it to be called at the end of the render
|
||||
state.refs.push([item.vnode.props.ref, item.host]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +84,7 @@ export function render(vnode, opts = {}) {
|
||||
renderer.update(item, meta);
|
||||
|
||||
// Update children
|
||||
if(meta.normedType!==Element && (item.vnode?.children || item.old?.children)) {
|
||||
if (meta.normedType !== Element && (item.vnode?.children || item.old?.children)) {
|
||||
let childTypes = new Set();
|
||||
|
||||
// Flatten and organize new vNode-children (this could be a separate function, or implemented using a helper function (because mucht of the code is similar between old/new vnodes)
|
||||
@ -91,19 +92,19 @@ export function render(vnode, opts = {}) {
|
||||
* @type { Object.<VNodeType, Array.<VRenderQueueItem>> }
|
||||
*/
|
||||
let vChildren = {};
|
||||
let queue = (item.vnode?.children||[]).slice();
|
||||
while(queue.length>0){
|
||||
let next = queue.splice(0,1)[0];
|
||||
if(next instanceof Array) queue.splice(0,0,...next);
|
||||
else{
|
||||
let queue = (item.vnode?.children || []).slice();
|
||||
while (queue.length > 0) {
|
||||
let next = queue.splice(0, 1)[ 0 ];
|
||||
if (next instanceof Array) queue.splice(0, 0, ...next);
|
||||
else {
|
||||
let meta = getNodeMeta(next);
|
||||
if(meta && meta.renderer) {
|
||||
if (meta && meta.renderer) {
|
||||
// Only items with a renderer are tracked (any other are undefined or null and shoulnd't be rendered at all)
|
||||
let childType = meta.normedType;
|
||||
if(!meta.renderer.remove) childType = 'node'; // Treat anything that doesnt have a special remove-function as ChildNode-type (e.g. it shows up in Element.childNodes)
|
||||
if (!meta.renderer.remove) childType = 'node'; // Treat anything that doesnt have a special remove-function as ChildNode-type (e.g. it shows up in Element.childNodes)
|
||||
childTypes.add(childType);// Track that children of this type exist and should be iterated later
|
||||
vChildren[childType] = vChildren[childType] || []; // Make sure the array exists
|
||||
vChildren[childType].push({
|
||||
vChildren[ childType ] = vChildren[ childType ] || []; // Make sure the array exists
|
||||
vChildren[ childType ].push({
|
||||
item: {
|
||||
...item,
|
||||
old: undefined,
|
||||
@ -121,64 +122,64 @@ export function render(vnode, opts = {}) {
|
||||
/**
|
||||
* @type { Object.<VNodeType, Array.<VOldQueueItem>> }
|
||||
*/
|
||||
let oldVChildren = { };
|
||||
let oldVChildren = {};
|
||||
let curElement = item.host.firstChild;
|
||||
queue = (item.old?.children || []).slice();
|
||||
while(queue.length>0){
|
||||
let next = queue.splice(0,1)[0];
|
||||
if(next instanceof Array) queue.splice(0,0,...next);
|
||||
else{
|
||||
while (queue.length > 0) {
|
||||
let next = queue.splice(0, 1)[ 0 ];
|
||||
if (next instanceof Array) queue.splice(0, 0, ...next);
|
||||
else {
|
||||
let meta = getNodeMeta(next);
|
||||
if(meta && meta.renderer) {
|
||||
if (meta && meta.renderer) {
|
||||
// Only items with a renderer are tracked (any other are undefined or null and shoulnd't be rendered at all)
|
||||
let childType = meta.normedType;
|
||||
let childElement;
|
||||
if(!meta.renderer.remove){
|
||||
if (!meta.renderer.remove) {
|
||||
childType = 'node';// Treat anything that doesnt have a special remove-function as ChildNode-type (e.g. it shows up in Element.childNodes)
|
||||
if(curElement){
|
||||
if (curElement) {
|
||||
childElement = curElement;
|
||||
curElement = curElement.nextSibling;
|
||||
}
|
||||
}
|
||||
childTypes.add(childType);// Track that children of this type exist and should be iterated later
|
||||
oldVChildren[childType] = oldVChildren[childType] || []; // Make sure the array exists
|
||||
oldVChildren[ childType ] = oldVChildren[ childType ] || []; // Make sure the array exists
|
||||
let oldItem = {
|
||||
vnode: next,
|
||||
element: childElement,
|
||||
meta: meta
|
||||
};
|
||||
oldVChildren[childType].push(oldItem);
|
||||
if(next.props?.key){
|
||||
state.keyedElements.set(next.key,oldItem);
|
||||
oldVChildren[ childType ].push(oldItem);
|
||||
if (next.props?.key) {
|
||||
state.keyedElements.set(next.key, oldItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let sortedChildTypes = Array.from(childTypes).sort((a,b)=>a==='node'?1:-1); // Always do ChildNode-types last
|
||||
let sortedChildTypes = Array.from(childTypes).sort((a, b) => a === 'node' ? 1 : -1); // Always do ChildNode-types last
|
||||
let queuedItems = [];
|
||||
/**@type {VRenderQueueItem}*/ let previous = null;
|
||||
for(let childType of sortedChildTypes){
|
||||
let newChildren = vChildren[childType];
|
||||
let oldChildren = oldVChildren[childType];
|
||||
for (let childType of sortedChildTypes) {
|
||||
let newChildren = vChildren[ childType ];
|
||||
let oldChildren = oldVChildren[ childType ];
|
||||
|
||||
while(newChildren && newChildren.length){
|
||||
let child = newChildren.splice(0,1)[0];
|
||||
while (newChildren && newChildren.length) {
|
||||
let child = newChildren.splice(0, 1)[ 0 ];
|
||||
|
||||
// Key handling
|
||||
let childKey = child.item.vnode.props?.key;
|
||||
/**@type {VOldQueueItem}*/ let oldChild;
|
||||
if(childKey){
|
||||
if (childKey) {
|
||||
oldChild = state.keyedElements.get(childKey);
|
||||
if(oldChild) {
|
||||
if (oldChild) {
|
||||
if (oldChildren && oldChildren[ 0 ] === oldChild) {
|
||||
// Old keyed child already in the right place (just clear it from the queue);
|
||||
oldChildren.splice(0, 1);
|
||||
} else {
|
||||
// Old keyed child not already in the right place
|
||||
let indexOfKeyed = oldChildren.indexOf(oldChild);
|
||||
if(indexOfKeyed) {
|
||||
if (indexOfKeyed) {
|
||||
oldChildren.splice(indexOfKeyed, 1);
|
||||
item.host.removeChild(oldChild.element);
|
||||
}
|
||||
@ -190,32 +191,33 @@ export function render(vnode, opts = {}) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!oldChild) oldChild = oldChildren && oldChildren.splice(0,1)[0];
|
||||
if (!oldChild) oldChild = oldChildren && oldChildren.splice(0, 1)[ 0 ];
|
||||
|
||||
child.previous = previous;
|
||||
if(oldChild && child.meta.normedType === oldChild.meta.normedType && childKey===oldChild.vnode.props?.key){
|
||||
if (oldChild && child.meta.normedType === oldChild.meta.normedType && childKey === oldChild.vnode.props?.key
|
||||
&& (child.meta.normedType !== Element || child.item.vnode === oldChild.vnode)) {
|
||||
// Update old-child
|
||||
child.item.host = oldChild.element;
|
||||
child.item.old = oldChild.vnode;
|
||||
queuedItems.push(child);
|
||||
}else{
|
||||
} else {
|
||||
// New child
|
||||
if(oldChild){
|
||||
if(oldChild.meta.renderer.remove)
|
||||
if (oldChild) {
|
||||
if (oldChild.meta.renderer.remove)
|
||||
oldChild.meta.renderer.remove({ ...item, parent: item.host, host: oldChild.element });
|
||||
else
|
||||
item.host.removeChild(oldChild.element);
|
||||
}
|
||||
queuedItems.push(child);
|
||||
}
|
||||
if(!child.meta.renderer.remove){
|
||||
if (!child.meta.renderer.remove) {
|
||||
// If child is a node-type item track it as the previous (so we can insert next node-type items after it as intended)
|
||||
previous = child.item;
|
||||
}
|
||||
}
|
||||
while(oldChildren && oldChildren.length){
|
||||
let oldChild = oldChildren.splice(0,1)[0];
|
||||
if(oldChild.meta.renderer.remove)
|
||||
while (oldChildren && oldChildren.length) {
|
||||
let oldChild = oldChildren.splice(0, 1)[ 0 ];
|
||||
if (oldChild.meta.renderer.remove)
|
||||
oldChild.meta.renderer.remove({ ...item, parent: item.host, host: oldChild.element });
|
||||
else
|
||||
item.host.removeChild(oldChild.element);
|
||||
@ -225,23 +227,23 @@ export function render(vnode, opts = {}) {
|
||||
state.queue.splice(0, 0, ...queuedItems);
|
||||
}
|
||||
|
||||
if(newlyCreated){
|
||||
if(!meta.renderer.remove){
|
||||
if(item.parent){
|
||||
if(!previous){
|
||||
if (newlyCreated) {
|
||||
if (!meta.renderer.remove) {
|
||||
if (item.parent) {
|
||||
if (!previous) {
|
||||
// First child
|
||||
item.parent.prepend(item.host);
|
||||
}else{
|
||||
} else {
|
||||
// Subsequent child
|
||||
previous.host.after(item.host);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!item.parent) newRoot = item.host;
|
||||
if (!item.parent) newRoot = item.host;
|
||||
}
|
||||
}
|
||||
|
||||
for(let [refCb, refItem] of state.refs){
|
||||
for (let [refCb, refItem] of state.refs) {
|
||||
refCb(refItem);
|
||||
}
|
||||
return newRoot;
|
||||
|
||||
@ -1,26 +1,9 @@
|
||||
import '../types';
|
||||
|
||||
// Keys of a Element to be set directly rather than using setAttribute
|
||||
const VNODEPROP_DIRECT = {
|
||||
//['checked']: true NOT NEEDED!
|
||||
};
|
||||
const VNODEPROP_EXCLUDE_DIRECT = {
|
||||
['style']: true,
|
||||
['class']: true,
|
||||
};
|
||||
const VNODEPROP_IGNORE = {
|
||||
['key']: true,
|
||||
['ref']: true
|
||||
};
|
||||
const VNODE_SPECIAL_PROPS = {
|
||||
['key']: false,
|
||||
['ref']: false,
|
||||
// TODO: className, (style), event/events, (see react-docs)
|
||||
}
|
||||
|
||||
let namespace = {
|
||||
svg: "http://www.w3.org/2000/svg"
|
||||
}
|
||||
// Plain HTML doens't need a namespace, if it did it would've shown up here..
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes care of rendering a typical VNode (like div, span or any custom-element)
|
||||
@ -90,11 +73,14 @@ export const NodeTreeRenderer = {
|
||||
// Now apply each
|
||||
for(let [key, newVal, oldVal] of propDiffs){
|
||||
let special = VNODE_SPECIAL_PROPS[key];
|
||||
if(special === false){
|
||||
if(special === false) {
|
||||
// Ignore the prop
|
||||
}else if(vtype.props?.has && vtype.props.has(key)){
|
||||
// Registered prop of the Custom-Element
|
||||
host[key] = newVal;
|
||||
}else if(special instanceof Function){
|
||||
// Special prop
|
||||
special({host, newVal, oldVal, key});
|
||||
}else if(key.slice(0,2)==='on' && key[2]>='A' && key[2]<='Z'){
|
||||
// Event-prop
|
||||
// Convert event name from camelCase to dash-case (this means that this on<EvenName> syntax might not be able to cover all custom-events)
|
||||
@ -117,3 +103,58 @@ export const NodeTreeRenderer = {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Keys of a Element that need special handling rather than simply using setAttribute
|
||||
const VNODE_SPECIAL_PROPS = {
|
||||
['key']: false,
|
||||
['ref']: false,
|
||||
['className']: ({host, newVal, oldVal, key})=>{
|
||||
let oldClasses = new Set();
|
||||
let newClasses = new Set();
|
||||
|
||||
if(typeof(oldVal) === 'string'){
|
||||
oldVal = oldVal.split(' ').map(x=>x.trim()).filter(x=>x);
|
||||
for(let className of oldVal){ oldClasses.add(className); }
|
||||
}else if(typeof(oldVal)==='object'){
|
||||
for(let key in oldVal){
|
||||
if(oldVal[key]) oldClasses.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
if(typeof(newVal) === 'string'){
|
||||
newVal = newVal.split(' ').map(x=>x.trim()).filter(x=>x);
|
||||
for(let className of newVal){ newClasses.add(className); }
|
||||
}else if(typeof(newVal)==='object'){
|
||||
for(let key in newVal){
|
||||
if(newVal[key]) newClasses.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
host.classList.remove(...Array.from(oldClasses));
|
||||
host.classList.add(...Array.from(newClasses));
|
||||
},
|
||||
['style']: ({host, newVal, oldVal, key})=>{
|
||||
if(typeof(newVal) === 'string'){
|
||||
host.setAttribute('style', newVal);
|
||||
}else if(typeof(newVal) ==='object'){
|
||||
if(oldVal){
|
||||
for(let key in oldVal){
|
||||
if(!newVal[key]){
|
||||
host.style[key]=null;
|
||||
}
|
||||
}
|
||||
for(let key in newVal){
|
||||
host.style[key] = newVal[key];
|
||||
}
|
||||
}
|
||||
}else if(!newVal){
|
||||
host.removeAttribute('style');
|
||||
}
|
||||
},
|
||||
['events']: ({host, newVal, oldVal, key})=>{
|
||||
//TODO!!
|
||||
throw new Error("We're still planning to implement this but it hasn't been done yet!");
|
||||
},
|
||||
};
|
||||
19
tests/cfg/.babelrc
Normal file
19
tests/cfg/.babelrc
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"sourceMaps": "both",
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
[ "@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||
[ "@babel/plugin-proposal-class-properties", { "loose": true } ],
|
||||
[ "@babel/plugin-proposal-private-methods", {"loose": true } ],
|
||||
[ "@babel/plugin-proposal-optional-chaining" ],
|
||||
[ "@babel/plugin-proposal-nullish-coalescing-operator" ],
|
||||
[ "@babel/plugin-proposal-export-namespace-from" ],
|
||||
[ "@babel/plugin-proposal-export-default-from" ]
|
||||
]
|
||||
}
|
||||
166
tests/cfg/rollup-build.js
Normal file
166
tests/cfg/rollup-build.js
Normal file
@ -0,0 +1,166 @@
|
||||
// @cerxes
|
||||
import {host, Host} from "@cerxes/host";
|
||||
|
||||
// Observables
|
||||
import {Observable} from "zen-observable/lib/Observable";
|
||||
import {merge, combineLatest, zip} from "zen-observable/lib/extras";
|
||||
|
||||
// Rollup and plugins
|
||||
import * as rollup from 'rollup';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import json from "rollup-plugin-json";
|
||||
import postcss from "rollup-plugin-postcss";
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
/** @type {Host} */
|
||||
const srcDir = host.from("tests");
|
||||
/** @type {Host} */
|
||||
const distDir = host.from("public");
|
||||
const assetsGlob = `**/*.@(${[
|
||||
"html",
|
||||
"otf",
|
||||
'svg',
|
||||
'ico',
|
||||
'png',
|
||||
'jpg',
|
||||
'jpeg'
|
||||
].join('|')})`;
|
||||
const sourcesGlob = `**/index.@(js|jsx)`;
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const entryConfig = (entry, output)=>({
|
||||
input: srcDir.resolve(entry),
|
||||
output: {
|
||||
file: distDir.resolve(output),
|
||||
format: 'esm',
|
||||
sourcemap: true
|
||||
},
|
||||
plugins: [
|
||||
// json
|
||||
json(),
|
||||
// PostCSS-style
|
||||
postcss({
|
||||
plugins: [],
|
||||
inject: false,
|
||||
minimize: !!args.includes('build')
|
||||
}),
|
||||
// babel
|
||||
babel(),
|
||||
// node_modules
|
||||
resolve({
|
||||
extensions: ['.mjs', '.js', '.jsx', '.json'],
|
||||
}),
|
||||
// CJS-modules
|
||||
commonjs({
|
||||
namedExports: {
|
||||
// If there were any...
|
||||
}
|
||||
}),
|
||||
// minify, but only in production
|
||||
args.includes('build') && terser(),
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
function logBundle(output, duration){
|
||||
let relativeOut = distDir.relative(output);
|
||||
console.log(relativeOut + " built in " + (duration/1000)+"s");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the tests
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function build(){
|
||||
// Clean host
|
||||
await host.remove(distDir.workingDirectory, {recursive: true});
|
||||
console.log("Dist cleaned!");
|
||||
|
||||
let assetsJob = srcDir.glob(assetsGlob).then(
|
||||
matched=>Promise.all(
|
||||
matched.map(asset=>host.copy(srcDir.resolve(asset), distDir.resolve(asset)).then(()=>asset))
|
||||
)
|
||||
).then((assets)=>{
|
||||
console.log("Assets copied");
|
||||
return assets;
|
||||
});
|
||||
|
||||
let sourceJobs = srcDir.glob(sourcesGlob).then(
|
||||
matched=>Promise.all(
|
||||
matched.map(async (source)=>{
|
||||
let sourceStart = new Date();
|
||||
let entry = source;
|
||||
let output = distDir.resolve(source,'..','index.js');
|
||||
let rollupCfg = entryConfig(entry,output);
|
||||
return await rollup.rollup(rollupCfg).then(async (bundle)=>{
|
||||
let written = await bundle.write(rollupCfg.output);
|
||||
logBundle(output, (new Date()).getTime()-sourceStart.getTime());
|
||||
return {bundle, written};
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Wait for both to be done
|
||||
let [app, assets] = await Promise.all([sourceJobs, assetsJob]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch the app (watch asset files and automatically rebuild, this triggers development mode, as it should never be used on the server!)
|
||||
*/
|
||||
async function watch(){
|
||||
let lastUpdate = new Date();
|
||||
// Clean host
|
||||
await host.remove(distDir.workingDirectory, { recursive: true });
|
||||
console.log("Dist cleaned!");
|
||||
|
||||
// Configure a WebPackage (containing build-info)
|
||||
|
||||
// Watch sources and map them to an observable
|
||||
let sourceJobs = await srcDir.glob(sourcesGlob).then(
|
||||
matched=>matched.map((source)=>{
|
||||
let entry = source;
|
||||
let output = distDir.resolve(source,'..','index.js');
|
||||
let rollupCfg = entryConfig(entry,output);
|
||||
let sourceWatcher = rollup.watch(rollupCfg);
|
||||
return new Observable(observer => {
|
||||
sourceWatcher.on('event', event => {
|
||||
if(event.code==='BUNDLE_END'){
|
||||
logBundle(output, event.duration);
|
||||
observer.next(event);
|
||||
}else if(event.code==='FATAL'){
|
||||
observer.complete();
|
||||
}else if(event.code==='ERROR'){
|
||||
console.error(event.error.toString());
|
||||
}
|
||||
});
|
||||
|
||||
return () =>{
|
||||
// On unsubscription, do what?
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
let assetsObservable = srcDir.watch().glob(assetsGlob).sync(distDir.workingDirectory).map(event=>{
|
||||
console.log("Assets synced!");
|
||||
// console.log(event.files.join('\n'));
|
||||
return event.files;
|
||||
});
|
||||
|
||||
combineLatest(...sourceJobs, assetsObservable).subscribe(async (built)=>{
|
||||
let newUpdate = new Date();
|
||||
console.log(`Updated in ${(newUpdate.getTime()- lastUpdate.getTime())/1000}s`);
|
||||
lastUpdate = newUpdate;
|
||||
});
|
||||
}
|
||||
|
||||
// Run!
|
||||
if(args.includes('watch')){
|
||||
watch();
|
||||
}else{
|
||||
build();
|
||||
}
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
<li>
|
||||
<a href="./svg/">SVG Rendering</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./pdf/">PDF Rendering</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./todos-mvc/">Todos MVC</a>
|
||||
</li>
|
||||
BIN
tests/pdf/assets/fonts/DINPro-Black.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-Black.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-BlackItalic.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-BlackItalic.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-Bold.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-Bold.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-BoldItalic.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-BoldItalic.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-Cond.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-Cond.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondBlack.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondBlack.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondBlackIta.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondBlackIta.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondBold.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondBold.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondBoldIta.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondBoldIta.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondIta.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondIta.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondLight.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondLight.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondLightIta.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondLightIta.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondMediIta.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondMediIta.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-CondMedium.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-CondMedium.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-Italic.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-Italic.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro-Light.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro-Light.otf
Normal file
Binary file not shown.
BIN
tests/pdf/assets/fonts/DINPro.otf
Normal file
BIN
tests/pdf/assets/fonts/DINPro.otf
Normal file
Binary file not shown.
73
tests/pdf/assets/logo.svg
Normal file
73
tests/pdf/assets/logo.svg
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1133.3333 392"
|
||||
height="392"
|
||||
width="1133.3333"
|
||||
xml:space="preserve"
|
||||
id="svg4485"
|
||||
version="1.1"><metadata
|
||||
id="metadata4491"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs4489"><clipPath
|
||||
id="clipPath4501"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path4499"
|
||||
d="M 0.06,293.854 H 849.394 V 0.06 H 0.06 Z" /></clipPath><clipPath
|
||||
id="clipPath4515"
|
||||
clipPathUnits="userSpaceOnUse"><path
|
||||
id="path4513"
|
||||
d="M 0.06,293.854 H 849.394 V 0.06 H 0.06 Z" /></clipPath></defs><g
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,0,392)"
|
||||
id="g4493"><g
|
||||
id="g4495"><g
|
||||
clip-path="url(#clipPath4501)"
|
||||
id="g4497"><path
|
||||
id="path4503"
|
||||
style="fill:#43a998;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 849.394,160.098 833.52,42.32 285.572,0 263.592,160.098 h 585.802" /></g></g><g
|
||||
id="text4507"
|
||||
style="font-variant:normal;font-weight:900;font-size:91.8742981px;font-family:Simplo;-inkscape-font-specification:Simplo-Black;writing-mode:lr-tb;display:inline;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
transform="scale(1,-1)"
|
||||
aria-label="LUMMEN"><path
|
||||
id="path4534"
|
||||
d="M 414.79861,-66.139618 H 366.61843 V -132.9369 h 17.22644 v 53.877459 h 30.95374 z" /><path
|
||||
id="path4536"
|
||||
d="m 483.61461,-90.319426 q 0,12.381497 -7.53656,18.97599 -7.53657,6.594493 -22.20595,6.594493 -14.66938,0 -22.20594,-6.594493 -7.49171,-6.594493 -7.49171,-18.931129 V -132.9369 h 17.31615 v 41.675404 q 0,6.953377 2.91594,10.362775 2.91593,3.409397 9.46556,3.409397 6.45991,0 9.42071,-3.274816 3.00565,-3.274816 3.00565,-10.497356 V -132.9369 h 17.31615 z" /><path
|
||||
id="path4538"
|
||||
d="m 569.79162,-66.139618 h -17.13671 v -44.725912 l -12.3815,29.024738 h -11.88803 l -12.3815,-29.024738 v 44.725912 h -16.2395 V -132.9369 h 20.00779 l 15.02826,33.510793 14.98341,-33.510793 h 20.00778 z" /><path
|
||||
id="path4540"
|
||||
d="m 656.82099,-66.139618 h -17.13671 v -44.725912 l -12.38149,29.024738 h -11.88803 l -12.3815,-29.024738 v 44.725912 h -16.2395 V -132.9369 h 20.00778 l 15.02827,33.510793 14.9834,-33.510793 h 20.00778 z" /><path
|
||||
id="path4542"
|
||||
d="M 722.13787,-66.139618 H 673.82312 V -132.9369 h 48.31475 v 12.91983 h -31.17804 v 11.52914 h 28.93502 v 12.919826 h -28.93502 v 16.508663 h 31.17804 z" /><path
|
||||
id="path4544"
|
||||
d="M 797.36892,-66.139618 H 780.77053 L 752.4187,-111.98705 v 45.847432 H 736.6278 V -132.9369 h 20.59097 l 24.35925,38.266006 V -132.9369 h 15.7909 z" /></g><g
|
||||
id="g4509"><g
|
||||
clip-path="url(#clipPath4515)"
|
||||
id="g4511"><path
|
||||
id="path4517"
|
||||
style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 368.363,140.416 285.572,0 264.044,152.94 368.363,140.416" /><path
|
||||
id="path4519"
|
||||
style="fill:#00897e;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,293.854 15.874,-111.118 352.489,-42.32 21.98,153.438 H 0" /></g></g><path
|
||||
id="path4521"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 326.403,265.506 V 241.099 L 292,266.583 v -63.156 h 19.345 v 24.404 l 34.401,-25.482 v 63.157 h -19.343" /><path
|
||||
id="path4523"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 261.052,227.149 h 25.056 v 15.564 h -25.056 v 5.812 h 25.499 v 16.999 h -44.817 v -62.085 h 44.913 v 17.535 h -25.595 v 6.175" /><path
|
||||
id="path4525"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 212.916,224.493 c -2.776,-2.685 -5.994,-4.025 -9.846,-4.025 -3.936,0 -7.154,1.34 -9.929,4.025 -2.685,2.772 -4.028,6.086 -4.028,9.932 0,3.846 1.343,7.154 4.028,9.836 2.775,2.778 5.993,4.12 9.929,4.12 3.852,0 7.07,-1.342 9.846,-4.12 2.863,-2.682 4.2,-5.99 4.2,-9.836 0,-3.846 -1.337,-7.16 -4.2,-9.932 z m -9.846,43.302 c -9.211,0 -16.999,-3.313 -23.526,-9.842 -6.442,-6.44 -9.752,-14.317 -9.752,-23.528 0,-9.128 3.31,-17 9.752,-23.531 6.527,-6.443 14.315,-9.751 23.526,-9.751 9.217,0 17.006,3.308 23.537,9.751 6.529,6.531 9.834,14.403 9.834,23.531 0,9.211 -3.305,17.088 -9.834,23.528 -6.531,6.529 -14.32,9.842 -23.537,9.842" /><path
|
||||
id="path4527"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 126.495,239.23 v 7.243 h 11.361 c 4.07,0 7.255,-3.245 7.321,-7.214 -0.066,-3.965 -3.251,-7.207 -7.321,-7.207 h -11.361 z m 30.322,-18.812 c 5.151,5.153 7.827,11.542 7.866,18.812 h 0.006 c 0,0.009 0,0.018 0,0.029 0,0.013 0,0.025 0,0.033 h -0.006 c -0.039,7.268 -2.715,13.663 -7.866,18.81 -5.186,5.188 -11.629,7.422 -18.961,7.422 h -30.778 v -62.082 h 19.417 v 9.554 h 9.974 l 20.613,-27.512 15.481,11.629 -16.764,22.382 c 0.336,0.303 0.691,0.592 1.018,0.923" /><path
|
||||
id="path4529"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 101.633,234.478 c 0,0.274 0,0.539 0,0.811 0,0.892 0,3.771 0,3.771 H 69.73 v -12.308 h 10.78 c -0.538,-0.812 -1.164,-1.533 -1.884,-2.25 -2.697,-2.688 -6.023,-4.04 -9.884,-4.04 -3.862,0 -7.187,1.352 -9.968,4.04 -2.697,2.79 -4.046,6.115 -4.046,9.976 0,3.871 1.349,7.191 4.046,9.882 2.781,2.786 6.106,4.138 9.968,4.138 3.861,0 6.561,-0.807 9.256,-3.507 l 14.376,13.12 c -6.555,6.558 -14.376,9.881 -23.632,9.881 -9.164,0 -17.071,-3.323 -23.633,-9.881 -6.552,-6.467 -9.788,-14.38 -9.788,-23.633 0,-9.163 3.236,-17.07 9.788,-23.629 6.562,-6.47 14.469,-9.792 23.633,-9.792 9.256,0 17.077,3.322 23.632,9.792 6.559,6.559 9.259,14.466 9.259,23.629" /></g></svg>
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
106
tests/pdf/fonts.pcss
Normal file
106
tests/pdf/fonts.pcss
Normal file
@ -0,0 +1,106 @@
|
||||
/* DINPro */
|
||||
@font-face {
|
||||
font-family: 'DINPro';
|
||||
src: url('assets/fonts/DINPro.otf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DINPro';
|
||||
src: url('assets/fonts/DINPro-Light.otf');
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro';
|
||||
src: url('assets/fonts/DINPro-Italic.otf');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DINPro';
|
||||
src: url('assets/fonts/DINPro-Bold.otf');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro';
|
||||
src: url('assets/fonts/DINPro-BoldItalic.otf');
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro';
|
||||
src: url('assets/fonts/DINPro-Black.otf');
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro';
|
||||
src: url('assets/fonts/DINPro-BlackItalic.otf');
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
/* DINPro Condensed */
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro.otf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro-CondLight.otf');
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro-CondIta.otf');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro-CondMedium.otf');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro-CondMediIta.otf');
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro-CondBold.otf');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro-CondBoldIta.otf');
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro-CondBlack.otf');
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'DINPro Condensed';
|
||||
src: url('assets/fonts/DINPro-CondBlackIta.otf');
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
}
|
||||
10
tests/pdf/index.html
Normal file
10
tests/pdf/index.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Pastabuffet Groen Lummen</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
88
tests/pdf/index.jsx
Normal file
88
tests/pdf/index.jsx
Normal file
@ -0,0 +1,88 @@
|
||||
import kaartStyle from "./index.pcss";
|
||||
import {render, defineElement, CustomElement, Host, State, Prop, ShadowDOM} from "../../packages/csx";
|
||||
|
||||
// Style
|
||||
document.head.appendChild(render(<style>{kaartStyle}</style>));
|
||||
|
||||
// Kaart
|
||||
@defineElement("pasta-buffet-kaart")
|
||||
class PastaBuffetKaart extends CustomElement{
|
||||
|
||||
@Prop() eigenExemplaar;
|
||||
|
||||
render(){
|
||||
return <div className="kaart">
|
||||
<div className="header">
|
||||
<img src="assets/logo.svg" alt={"logo"} className="logo"/>
|
||||
<div className="header-info">
|
||||
<div className="title">Pastabuffet</div>
|
||||
<div className="sub-title">Zondag 02/02/2020 vanaf 17u</div>
|
||||
<div className="sub-title">O.C De Link, Linkhoutstraat 194</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="content">
|
||||
<h3>Bestelling</h3>
|
||||
<table className="order">
|
||||
<tr>
|
||||
<td className="product-name">Volwassenen</td>
|
||||
<td className="product-amount"><input type="text"/></td>
|
||||
<td className="product-multiplier">x</td>
|
||||
<td className="product-price">14,00€ =</td>
|
||||
<td className="product-total"><input type="text"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="product-name">Kinderen</td>
|
||||
<td className="product-amount"><input type="text"/></td>
|
||||
<td className="product-multiplier">x</td>
|
||||
<td className="product-price">8,00€ =</td>
|
||||
<td className="product-total"><input type="text"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={3}/>
|
||||
<td className="total-label">Totaal =</td>
|
||||
<td className="order-total"><input type="text"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table className="account">
|
||||
<tr>
|
||||
<td className="product-name">Naam</td>
|
||||
<td className="product-input"><input type="text"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div className="payment-options">
|
||||
<div className="option">
|
||||
Cash betaald
|
||||
<input type="checkbox"/>
|
||||
</div>
|
||||
<div className="option">
|
||||
Overschrijving
|
||||
<input type="checkbox"/>
|
||||
BE50 9731 5197 8018
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer">
|
||||
{this.eigenExemplaar ? ([
|
||||
<div className="form-for">Eigen exemplaar</div>,
|
||||
<div className="contact">Contactpersoon: Peter Aerts 0499 26 54 99</div>
|
||||
]) : ([
|
||||
<div className="form-for">Exemplaar voor Groen Lummen</div>
|
||||
])}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
// Kaarten
|
||||
document.body.appendChild(render(<div className="kaarten-print">
|
||||
<div className="kaarten-pair">
|
||||
<PastaBuffetKaart eigenExemplaar={true}/>
|
||||
<PastaBuffetKaart/>
|
||||
</div>
|
||||
<div className="kaarten-pair">
|
||||
<PastaBuffetKaart eigenExemplaar={true}/>
|
||||
<PastaBuffetKaart/>
|
||||
</div>
|
||||
</div>));
|
||||
158
tests/pdf/index.pcss
Normal file
158
tests/pdf/index.pcss
Normal file
@ -0,0 +1,158 @@
|
||||
@import "./fonts.pcss";
|
||||
|
||||
@page {
|
||||
size: A4 landscape;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media print {
|
||||
html {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 297mm;
|
||||
height: 210mm;
|
||||
size: landscape A4;
|
||||
border: none !important;
|
||||
background: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
html{
|
||||
background: #aeaeae;
|
||||
font-family: 'DINPro';
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
body{
|
||||
background: white;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
width: 289mm;
|
||||
height: 202mm;
|
||||
border: 1px solid black;
|
||||
padding: 4mm;
|
||||
}
|
||||
|
||||
.kaarten-print{
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.kaarten-pair{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.kaart{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 148mm;
|
||||
padding: 1em;
|
||||
|
||||
& .header{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
& .logo{
|
||||
flex: 0 1 35%;
|
||||
max-width: 35%;
|
||||
}
|
||||
|
||||
& .header-info{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1 1 auto;
|
||||
|
||||
& .title{
|
||||
font-size: 200%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
& .sub-title{
|
||||
font-weight: 700;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .content{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& table{
|
||||
margin-bottom: .75em;
|
||||
}
|
||||
|
||||
& .order >tr >td{
|
||||
padding-bottom: .25em;
|
||||
}
|
||||
|
||||
& .product-name{
|
||||
font-weight: 600;
|
||||
width: 6em;
|
||||
}
|
||||
& .product-multiplier{
|
||||
font-weight: 600;
|
||||
width: 1em;
|
||||
}
|
||||
& .product-price, & .total-label{
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
& .payment-options{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
|
||||
& .option{
|
||||
padding: 0 0 1em;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
& .footer{
|
||||
align-self: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
& .form-for{
|
||||
font-weight: bold;
|
||||
}
|
||||
& .contact{
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
input[type=text]{
|
||||
border: none;
|
||||
border-bottom: 1px dashed darkgray;
|
||||
|
||||
width: calc(100% - 1em);
|
||||
}
|
||||
|
||||
input[type=checkbox]{
|
||||
-webkit-appearance: none;
|
||||
border: 1px solid #999;
|
||||
padding: 9px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
margin: 3px 6px;
|
||||
}
|
||||
9
tests/postcss.config.js
Normal file
9
tests/postcss.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
//require('autoprefixer'),
|
||||
require('postcss-import'),
|
||||
require('postcss-preset-env')({
|
||||
stage: 1,
|
||||
})
|
||||
]
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user