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:
Miel Truyen 2019-12-21 21:48:38 +01:00
parent 93fb6927ca
commit 4ca54727f1
56 changed files with 2397 additions and 381 deletions

View File

@ -24,29 +24,5 @@
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" /> <option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
</formatting-settings> </formatting-settings>
</DBN-SQL> </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> </code_scheme>
</component> </component>

View File

@ -15,6 +15,7 @@
<path value="file://$PROJECT_DIR$/packages/csx" /> <path value="file://$PROJECT_DIR$/packages/csx" />
<path value="file://$PROJECT_DIR$/packages/babel-plugin-transform-csx" /> <path value="file://$PROJECT_DIR$/packages/babel-plugin-transform-csx" />
<path value="file://$PROJECT_DIR$/packages/csx/src" /> <path value="file://$PROJECT_DIR$/packages/csx/src" />
<path value="file://$PROJECT_DIR$/tests" />
</resourceRoots> </resourceRoots>
</entryData> </entryData>
</entry> </entry>

View File

@ -3,6 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"@babel/register": "latest",
"@babel/cli": "csx", "@babel/cli": "csx",
"@babel/core": "csx", "@babel/core": "csx",
"@babel/plugin-proposal-class-properties": "csx", "@babel/plugin-proposal-class-properties": "csx",
@ -20,19 +21,23 @@
"rollup-plugin-babel": "csx", "rollup-plugin-babel": "csx",
"rollup-plugin-node-resolve": "latest", "rollup-plugin-node-resolve": "latest",
"rollup-plugin-commonjs": "latest", "rollup-plugin-commonjs": "latest",
"rollup-plugin-json": "latest",
"rollup-plugin-postcss": "latest",
"rollup-plugin-terser": "latest", "rollup-plugin-terser": "latest",
"rollup-plugin-copy": "latest", "@cerxes/host": "latest",
"rollup-plugin-sass": "latest", "postcss": "latest",
"postcss-preset-env": "latest",
"postcss-import": "latest",
"serve": "latest", "serve": "latest",
"npm-run-all": "latest" "npm-run-all": "latest"
}, },
"scripts": { "scripts": {
"dev": "npm-run-all -p watch:babel-transform-csx watch:csx watch:test start:test", "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:test", "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:test", "watch": "npm-run-all -p watch:babel-transform-csx watch:csx watch:tests",
"start:test": "serve public", "build:tests": "node -r @babel/register ./tests/cfg/rollup-build.js build",
"build:test": "rollup -c", "watch:tests": "node -r @babel/register ./tests/cfg/rollup-build.js watch",
"watch:test": "rollup -c -w", "serve:tests": "serve public",
"build:babel-transform-csx": "cd packages/babel-plugin-transform-csx && npm run build", "build:babel-transform-csx": "cd packages/babel-plugin-transform-csx && npm run build",
"build:csx": "cd packages/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", "watch:babel-transform-csx": "cd packages/babel-plugin-transform-csx && npm run watch",

View File

@ -28,6 +28,7 @@
"rollup-plugin-commonjs": "latest", "rollup-plugin-commonjs": "latest",
"rollup-plugin-terser": "latest", "rollup-plugin-terser": "latest",
"rollup-plugin-json": "latest", "rollup-plugin-json": "latest",
"rollup-plugin-postcss": "latest",
"npm-run-all": "latest" "npm-run-all": "latest"
}, },
"dependencies":{ "dependencies":{

View File

@ -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 // 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 // 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(){ update(){
if (this.render) { if (this.render) {
let newVNode = this.render(); let newVNode = this.render();
if(newVNode.type !== Host){
newVNode = {type: Host, children: newVNode instanceof Array? newVNode : [newVNode]};
}
render(newVNode, { render(newVNode, {
host: this, host: this,
old: this.#renderedVNode, old: this.#renderedVNode,

View File

@ -6,7 +6,6 @@ import {
NodeTreeRenderer, NativeRenderer NodeTreeRenderer, NativeRenderer
} from "./renderers"; } from "./renderers";
// TODO Element renderer (for things that are already DOM-elements)
export function getNodeMeta(vnode) { export function getNodeMeta(vnode) {
if (vnode === undefined || vnode === null) return undefined; // Indicate it shouldn't render if (vnode === undefined || vnode === null) return undefined; // Indicate it shouldn't render
if (vnode instanceof Element) return { renderer: NativeRenderer, normedType: Element }; if (vnode instanceof Element) return { renderer: NativeRenderer, normedType: Element };
@ -17,11 +16,10 @@ export function getNodeMeta(vnode){
else return { renderer: NodeTreeRenderer, normedType: window.customElements?.get(type) ?? type }; else return { renderer: NodeTreeRenderer, normedType: window.customElements?.get(type) ?? type };
} }
/** /**
* @typedef {Object} RenderOptions * @typedef {Object} RenderOptions
* @category VDOM * @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 {VNode} [old] - Old VNode representation of rendered host
* @property {Document} [document] - The document we're rendering to * @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} * @return {Element}
*/ */
export function render(vnode, opts = {}) { export function render(vnode, opts = {}) {
// TODO innerHTML, innerText and other tags/props that are trickyer then just mapping value to attribute (see NodeTreeRenderer) // TODO this code could use restructuring when opts.host and vnode.type are incompatible (non updatable type), the host element should be replaced
// TODO ref-prop (should it only return once all child els are created and appended to the child?!) // 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} * @type {VRenderState}
@ -193,7 +194,8 @@ 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; 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 // Update old-child
child.item.host = oldChild.element; child.item.host = oldChild.element;
child.item.old = oldChild.vnode; child.item.old = oldChild.vnode;

View File

@ -1,26 +1,9 @@
import '../types'; 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 = { let namespace = {
svg: "http://www.w3.org/2000/svg" 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) * Takes care of rendering a typical VNode (like div, span or any custom-element)
@ -95,6 +78,9 @@ export const NodeTreeRenderer = {
}else if(vtype.props?.has && vtype.props.has(key)){ }else if(vtype.props?.has && vtype.props.has(key)){
// Registered prop of the Custom-Element // Registered prop of the Custom-Element
host[key] = newVal; 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'){ }else if(key.slice(0,2)==='on' && key[2]>='A' && key[2]<='Z'){
// Event-prop // 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) // 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
View 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
View 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();
}

View File

@ -18,6 +18,9 @@
<li> <li>
<a href="./svg/">SVG Rendering</a> <a href="./svg/">SVG Rendering</a>
</li> </li>
<li>
<a href="./pdf/">PDF Rendering</a>
</li>
<li> <li>
<a href="./todos-mvc/">Todos MVC</a> <a href="./todos-mvc/">Todos MVC</a>
</li> </li>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

73
tests/pdf/assets/logo.svg Normal file
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,9 @@
module.exports = {
plugins: [
//require('autoprefixer'),
require('postcss-import'),
require('postcss-preset-env')({
stage: 1,
})
]
};

1869
yarn.lock

File diff suppressed because it is too large Load Diff