v0.0.11: Added unit-tests, solved the key={...} problem, updated the build/watch configuration of CSX to be able to build minified and non-minified bundle outputs, as well as a CJS version of lib/ (for consuming in Node-environment, like Jest). The previous tests were renamed to examples, and should still need to be updated.

This commit is contained in:
2020-04-14 11:54:30 +02:00
parent 05f0e66a42
commit b95e5506d2
121 changed files with 3406 additions and 4929 deletions

23
examples/.babelrc Normal file
View File

@@ -0,0 +1,23 @@
{
"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" ],
[ "../packages/babel-plugin-transform-csx/dist", {
//"pragma": "render",
//"pragmaFrag": "render",
"throwIfNamespace": false
}]
]
}

10
examples/basic/index.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cerxes - CustomElements</title>
</head>
<body>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>

32
examples/basic/index.jsx Normal file
View File

@@ -0,0 +1,32 @@
import {render} from "../../packages/csx";
import style from "./index.scss";
import {ExamplePage} from "./page";
document.body.appendChild(render(<style>{style}</style>));
document.body.appendChild(render(<div class="center-me" iCanDoUpperCaseAttrs={ "yes" }>
<h1>I am a title!</h1>
</div>));
//document.body.appendChild(render(<example-page />));
document.body.appendChild(render(<ExamplePage pageWidth={200} />));
/**
* Continuation suggestionss:
* - style-attribute untested
* - Want a way to toggle classes: <Host class={{'bq-checkbox': true, 'checked': this.isChecked}}> could do
* - Supporting fragments <>...</>?
*/
// Private vars are visible, because of loose mode, not desirable...
class PrivTest{
#privatevar = 1;
get someVar(){
console.log(Object.getOwnPropertyDescriptors(this));
return this.#privatevar;
}
}
let a = new PrivTest();
console.log(a);
console.log(a.someVar);
let preRenderedText = render("I can prerender text-nodes");
document.body.appendChild(render(preRenderedText));

19
examples/basic/index.scss Normal file
View File

@@ -0,0 +1,19 @@
html{
width: 100%;
height: 100%;
}
body{
display: flex;
flex-direction: column;
overflow: auto;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.center-me{
align-self: center;
}

32
examples/basic/page.jsx Normal file
View File

@@ -0,0 +1,32 @@
import {defineElement, render, CustomElement, prop, state} from "../../packages/csx";
@defineElement('example-page')
export class ExamplePage extends CustomElement{
@prop({reflect: true, attr: 'page-width'})
set pageWidth(value){
if(value!==this.#pageWidth){
this.#pageWidth = value;
}
}
get pageWidth(){
return this.#pageWidth;
}
#pageWidth = undefined;
render(){
return <div className="page" style={{width: `${this.pageWidth}px`}}>
<h1>This page is an example!</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean elit enim, lacinia in pellentesque ut, fermentum quis risus. Vivamus condimentum turpis ac mattis blandit. Ut scelerisque nulla suscipit, rhoncus justo eget, pulvinar ligula. Donec vel orci dolor. Etiam non neque non lorem venenatis tincidunt. Cras tristique mauris ut leo imperdiet, in bibendum elit ultricies. Morbi et felis sed arcu efficitur aliquam.<br/>
Nam ornare scelerisque vestibulum. In porta non risus a tristique. Donec tincidunt diam sed dictum iaculis. In faucibus placerat justo, ut pulvinar tortor hendrerit viverra. Aliquam erat volutpat. Quisque euismod leo at eros fringilla varius. Nam et vulputate nunc. Nunc mattis purus eu massa lacinia posuere.<br/>
In fermentum hendrerit purus non mollis. Duis vel leo nibh. Praesent at odio suscipit, consequat lorem quis, posuere lorem. Donec molestie sodales feugiat. Ut in nisi vel est luctus tristique non eget elit. Donec accumsan quam id scelerisque egestas. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eu lacus convallis elit eleifend tristique id et sapien. Mauris cursus a enim ut scelerisque. Morbi auctor nisl sem, id dignissim lectus condimentum non. Ut tellus lacus, volutpat eget porttitor et, vulputate pharetra nunc. Donec nec ligula est. Donec eget odio tincidunt, laoreet mi sit amet, euismod odio. Donec vitae dignissim leo, nec ultricies quam.<br/>
Proin efficitur arcu sit amet congue commodo. Suspendisse non tristique libero. Maecenas tempor arcu ac felis posuere, eu gravida metus euismod. Vivamus hendrerit nisl id venenatis sodales. Phasellus pharetra arcu a arcu aliquam faucibus. Etiam sodales ligula nisl, sit amet posuere turpis ultricies id. Donec euismod nunc eros, at euismod neque tempor a. Nullam felis eros, imperdiet ac arcu id, lobortis lobortis tellus. Duis mollis sodales leo, et tincidunt enim pretium luctus.<br/>ulputate lorem.
</p>
</div>
}
connectedCallback() {
super.connectedCallback();
}
}

19
examples/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" ]
]
}

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("examples");
/** @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 examples
* @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();
}

32
examples/index.html Normal file
View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSX - CE Tests</title>
<style type="text/css">
html{ width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-items:center; }
body{ flex: 1 1 auto;overflow:auto;display:flex;flex-direction:column;align-items:center;justify-items:center; }
ul { list-style-type: none;font-size: 1.5em; }
li{ padding: .5em; }
</style>
</head>
<body>
<ul>
<li>
<a href="./basic/">Basic testing</a>
</li>
<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>
<li>
<a href="./table/">Tables (arrow functions)</a>
</li>
</ul>
</body>
</html>

1
examples/index.jsx Normal file
View File

@@ -0,0 +1 @@
/** I don't do nothing! (but it might later...) **/

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.

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
examples/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
examples/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
examples/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
examples/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;
}

View File

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

10
examples/svg/index.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cerxes - CustomElements - SVG</title>
</head>
<body>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>

47
examples/svg/index.jsx Normal file
View File

@@ -0,0 +1,47 @@
import {render} from "../../packages/csx";
import style from "./index.scss";
import {SvgLoader} from "./svg-loader";
import {SvgTester} from "./svg-tester";
import {SvgTesterTwo} from "./svg-tester-two";
let loader = render(<SvgLoader inverted="yes"/>);
document.body.appendChild(render(<style>{style}</style>));
document.body.appendChild(render(
<div class="center-me">
<h3>SVG Loader</h3>
{loader}
<h3>SVG Tester</h3>
<SvgTester/>
<h3>SVG Tester Two</h3>
<SvgTesterTwo/>
</div>
));
setTimeout(()=>{
console.log("Uninverting");
loader.removeAttribute("inverted");
setTimeout(()=>{
console.log("Inverting");
loader.setAttribute("inverted", "ja");
setTimeout(()=>{
console.log("Stays inverted");
loader.setAttribute("inverted", "");
setTimeout(()=>{
console.log("Inverted color");
loader.setAttribute("inverted-color", "#0F0");
}, 1000);
}, 1000);
}, 1000);
}, 1000);

19
examples/svg/index.scss Normal file
View File

@@ -0,0 +1,19 @@
html{
width: 100%;
height: 100%;
}
body{
display: flex;
flex-direction: column;
overflow: auto;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.center-me{
align-self: center;
}

View File

@@ -0,0 +1,57 @@
import {CustomElement, defineElement, Host, ShadowDOM, state, prop} from "../../packages/csx";
import loaderComponentShadowStyle from './svg-loader.shadow.scss';
// TODO configurability, like inverted and not with props...
@defineElement('svg-loader')
export class SvgLoader extends CustomElement{
// Constructor
constructor(){
super();
}
// Private properties
// Properties
@prop({reflect: true}) inverted;
@prop({reflect: true, attr: "inverted-color"}) invertedColor = "#000";
// Handlers
// CustomElement
render(){
return (
<Host>
<ShadowDOM>
<style>
{ loaderComponentShadowStyle }
</style>
<div class="loader-content">
<div class="spinner">
<svg width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke={(this.inverted??false)!==false? this.invertedColor : "#F00"}>
<g fill="none" fill-rule="evenodd">
<g transform="translate(1 1)" stroke-width="2">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</g>
</svg>
</div>
<slot>
</slot>
</div>
</ShadowDOM>
</Host>
)
}
}

View File

@@ -0,0 +1,10 @@
:host{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.spinner{
padding: .5rem;
}
}

View File

@@ -0,0 +1,43 @@
import {CustomElement, defineElement, Host, ShadowDOM, state, prop} from "../../packages/csx";
import {SvgLoader} from "./svg-loader";
@defineElement('svg-tester-two')
export class SvgTesterTwo extends CustomElement{
// Constructor
constructor(){
super();
}
// Private properties
states = [
{ inverted: true, invertedColor: "#F00"},
{ inverted: true, invertedColor: "#FF0"},
{ inverted: true, invertedColor: "#0FF"},
{ inverted: false},
{ inverted: true, invertedColor: "#0F0"},
];
// Properties
@state() state = this.states[0];
// Handlers
// CustomElement
connectedCallback() {
setInterval(()=>{
// Moving state
let curIndex = this.states.indexOf(this.state);
this.state = this.states[(curIndex+1)>=this.states.length?0:curIndex+1];
}, 1000);
super.connectedCallback();
}
render(){
// invertedColor instead of inverted-color is the only difference!
return (
<Host>
<SvgLoader inverted={this.state.inverted} invertedColor={this.state.invertedColor} />
</Host>
);
}
}

View File

@@ -0,0 +1,42 @@
import {CustomElement, defineElement, Host, ShadowDOM, state, prop} from "../../packages/csx";
import {SvgLoader} from "./svg-loader";
@defineElement('svg-tester')
export class SvgTester extends CustomElement{
// Constructor
constructor(){
super();
}
// Private properties
states = [
{ inverted: true, invertedColor: "#F00"},
{ inverted: true, invertedColor: "#FF0"},
{ inverted: true, invertedColor: "#0FF"},
{ inverted: false},
{ inverted: true, invertedColor: "#0F0"},
];
// Properties
@state() state = this.states[0];
// Handlers
// CustomElement
connectedCallback() {
setInterval(()=>{
// Moving state
let curIndex = this.states.indexOf(this.state);
this.state = this.states[(curIndex+1)>=this.states.length?0:curIndex+1];
}, 1000);
super.connectedCallback();
}
render(){
return (
<Host>
<SvgLoader inverted={this.state.inverted} inverted-color={this.state.invertedColor} />
</Host>
);
}
}

10
examples/table/index.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cerxes - CustomElements - SVG</title>
</head>
<body>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>

108
examples/table/index.jsx Normal file
View File

@@ -0,0 +1,108 @@
import {render, CustomElement, defineElement, Host, prop, state} from "../../packages/csx";
import style from "./index.scss";
import {TableComponent} from "./table-component";
@defineElement("table-tester")
class TableTester extends CustomElement{
/**
*
* @type {({[headerRender]: (function(): string), render: (function(User): *), [size]: number})[]}
*/
#columnDefinitions = [
{
headerRender: () => "Id",
render: (u) => u.userId,
size: 110
},
{
headerRender: () => "Email",
render: (u) => u.identity?.email,
},
{
headerRender: () => "FirstName",
render: (u) => u.identity?.firstName,
size: 160
},
{
headerRender: () => "LastName",
render: (u) => u.identity?.lastName,
size: 160
},
{
headerRender: () => "...",
render: (u) => {
let d = new Date();
return (<button onClick={(ev) => this.testMe(ev, u, d)}>test me</button>)
},
size: 110
},
];
@state()
users = [];
render(){
return <TableComponent
columns={this.#columnDefinitions}
data={this.users}
/>
}
connectedCallback() {
super.connectedCallback();
setTimeout(()=>this.load(), 0);
setTimeout(()=>this.load(), 50);
setTimeout(()=>this.load(), 100);
setTimeout(()=>this.load(), 150);
setTimeout(()=>this.load(), 200);
setTimeout(()=>this.load(), 250);
this.interval = setInterval(()=>this.load(), 3000);
}
interval;
disconnectedCallback() {
super.disconnectedCallback();
if(this.interval) {
clearInterval(this.interval);
}
}
load(){
let users = [];
let rndFirstNames = ['Loes', 'Johnny', 'Maria', 'Jezus', 'Philippe', 'Filip', 'Annie'];
let rndLastNames = ['Peeters', 'Wachters', 'Jannsens', 'De Schaetzen', 'Becks', 'Konings', 'De Clerk'];
for(let i = 0; i < 5; ++i){
let first = rndFirstNames[Math.floor(rndFirstNames.length*Math.random())];
let last = rndLastNames[Math.floor(rndLastNames.length*Math.random())];
users.push({
userId: (Math.random()*99999).toString(36).slice(-6).toUpperCase(),
identity: {
firstName: first,
lastName: last,
email: `${first}.${last}@example.com`.toLocaleLowerCase()
}
})
}
this.users = users;
}
lastClear = new Date();
testMe = (ev, u, d)=>{
if((new Date()).getTime() - this.lastClear.getTime() > 20){
console.log("\n\n");
this.lastClear = new Date();
}
console.log("I should only show up once per click:", d.getTime());
}
}
document.body.appendChild(render(<style>{style}</style>));
document.body.appendChild(render(<TableTester/>));

19
examples/table/index.scss Normal file
View File

@@ -0,0 +1,19 @@
html{
width: 100%;
height: 100%;
}
body{
display: flex;
flex-direction: column;
overflow: auto;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.center-me{
align-self: center;
}

View File

@@ -0,0 +1,61 @@
import {CustomElement, defineElement, Host, prop, state} from "../../packages/csx";
import TableComponentStyle from "./table-component.scss";
let tableId = 0;
@defineElement("tripto-table")
export class TableComponent extends CustomElement {
#columnDefinitions;
@prop()
set columns(value) {
this.#columnDefinitions = value;
}
@state() data;
@prop()
set data(value) {
this.data = value;
}
#tableId = tableId++;
render() {
console.log(`Table render at for ${this.data?.length??0} rows: ${Date.now()}`);
return (
<Host>
<style>{TableComponentStyle}</style>
<style>
{this.#columnDefinitions?.map((col, idx) => (
`#table_${this.#tableId} .cell.cell_${idx} {` +
` flex: ${(col.size ? (`0 0 ${col.size}px`) : `1`)};` +
`}`
))}
</style>
<section className="table" id={`table_${this.#tableId}`}>
<header>
<div className="row">
{this.#columnDefinitions.map((col, idx) => (
<div className={`cell cell_${idx}`}>
{col.headerRender()}
</div>
))}
</div>
</header>
<main>
{this.data?.map(dataRow => (
<div className="row">
{this.#columnDefinitions.map((col, idx) => (
<div className={`cell cell_${idx}`}>
{col.render(dataRow)}
</div>
))}
</div>
))}
</main>
</section>
</Host>
);
}
}

View File

@@ -0,0 +1,32 @@
.table {
--box-color: #a0a0a0;
--primary-color: #5f74ff;
--table-background: #e4e4f0;
--box-border: 1px solid #7d7d7d;
border: var(--box-border);
display: block;
header {
color: var(--primary-color);
}
header > .row,
main > .row {
background: var(--table-background);
display: flex;
width: 100%;
line-height: 3em;
border-bottom: var(--box-border);
.cell {
padding: 0 15px;
}
}
main > .row {
&:last-child {
border-bottom: 0;
}
}
}

View File

@@ -0,0 +1,58 @@
import {defineElement, render, CustomElement, Host, state} from "../../../packages/csx";
import style from './my-todo.scss';
import {TodoInput} from './todo-input';
import {TodoItem} from './todo-item';
@defineElement('my-todo')
export class MyTodo extends CustomElement{
uid = 1;
@state() todos = [
{id: this.uid++, text: "my initial todo", checked: false },
{id: this.uid++, text: "Learn about Web Components", checked: false },
];
render(){
return (
<Host>
<style>{ style }</style>
<h1>CSX Todo</h1>
<section>
<TodoInput onSubmit={this.handleSubmit}/>
<ul id="list-container"
onCheck={this.handleCheck}
onRemove={this.handleRemove}
>
{this.todos.map(item =>
<TodoItem
key={item.id}
model={ item.id }
checked={ item.checked }
>
{ item.text }
</TodoItem>
)}
</ul>
</section>
</Host>
);
}
handleSubmit = ({ detail: text }) => {
if(text) {
console.log("Submit rcvd: " + text);
this.todos = [...this.todos, { id: this.uid++, text, checked: false }];
}
};
handleCheck = ({detail: {checked,id}}) => {
let indexOf = this.todos.findIndex(t=>t.id===id);
if(indexOf>=0) {
let updated = { ...this.todos[ indexOf ], checked };
this.todos = [...this.todos.slice(0, indexOf), updated, ...this.todos.slice(indexOf + 1)];
}
};
handleRemove = ({detail: {id}})=>{
let indexOf = this.todos.findIndex(t=>t.id===id);
this.todos = [...this.todos.slice(0,indexOf), ...this.todos.slice(indexOf+1)];
}
}

View File

@@ -0,0 +1,24 @@
:host {
display: block;
}
h1 {
font-size: 60px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
}
section {
background: #fff;
margin: 30px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
#list-container {
margin: 0;
padding: 0;
list-style: none;
border-top: 1px solid #e6e6e6;
}

View File

@@ -0,0 +1,35 @@
import {defineElement, render, CustomElement, Host, state} from "../../../packages/csx";
import style from './todo-input.scss';
@defineElement('todo-input')
export class TodoInput extends CustomElement{
@state() value = "";
render(){
return (
<Host>
<style>{ style }</style>
<form onSubmit={ this.handleSubmit }>
<input
value={this.value}
type="text"
placeholder="What needs to be done?"
onInput={this.handleInput}
/>
</form>
</Host>
)
}
handleSubmit = (e)=>{
e.preventDefault();
if (!this.value) return;
this.dispatchEvent(new CustomEvent('submit', {
detail: this.value
}));
this.value = "";
};
handleInput = ({target: {value}})=>{
this.value = value;
};
}

View File

@@ -0,0 +1,29 @@
:host {
display: block;
}
form {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
input {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #CCC;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
}

View File

@@ -0,0 +1,41 @@
import {defineElement, render, CustomElement, Host, ShadowDOM, state, prop} from "../../../packages/csx";
import style from './todo-item.scss';
@defineElement('todo-item')
export class TodoItem extends CustomElement{
@prop({reflect: true}) checked = false;
@prop() model;
render(){
return (
<Host>
<ShadowDOM>
<style>{ style }</style>
<li class={( this.checked ? 'completed' : '' )}>
<input
type="checkbox" checked={ this.checked }
onChange={this.handleChange}
/>
<label>
<slot />
</label>
<button onClick={this.handleClick}>x</button>
</li>
</ShadowDOM>
</Host>
);
}
handleChange = ()=>{
this.dispatchEvent(new CustomEvent('check', {
detail: {checked: (this.checked=!this.checked), id: this.model},
bubbles: true
}));
};
handleClick = ()=>{
this.dispatchEvent(new CustomEvent('remove', {
detail: {id: this.model},
bubbles: true
}));
};
}

View File

@@ -0,0 +1,88 @@
:host {
display: block;
}
li {
font-size: 24px;
display: block;
position: relative;
border-bottom: 1px solid #ededed;
}
li input {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 9px;
bottom: 0;
margin: auto 0;
border: none;
/* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
li input:after {
content: url('data:image/svg+xml;utf8,<svg%20xmlns%3D"http%3A//www.w3.org/2000/svg"%20width%3D"40"%20height%3D"40"%20viewBox%3D"-10%20-18%20100%20135"><circle%20cx%3D"50"%20cy%3D"50"%20r%3D"50"%20fill%3D"none"%20stroke%3D"%23ededed"%20stroke-width%3D"3"/></svg>');
}
li input:checked:after {
content: url('data:image/svg+xml;utf8,<svg%20xmlns%3D"http%3A//www.w3.org/2000/svg"%20width%3D"40"%20height%3D"40"%20viewBox%3D"-10%20-18%20100%20135"><circle%20cx%3D"50"%20cy%3D"50"%20r%3D"50"%20fill%3D"none"%20stroke%3D"%23bddad5"%20stroke-width%3D"3"/><path%20fill%3D"%235dc2af"%20d%3D"M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z"/></svg>');
}
li label {
white-space: pre;
word-break: break-word;
padding: 15px 60px 15px 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
transition: color 0.4s;
}
li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
li button,
li input[type="checkbox"] {
outline: none;
}
li button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
}
li button {
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
}
li button:hover {
color: #af5b5e;
}

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Todos MVC</title>
</head>
<body>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>

View File

@@ -0,0 +1,10 @@
import {render} from "../../packages/csx";
import style from "./index.scss";
import {MyTodo} from "./components/my-todo";
// Replace this with an example implementation of the Todos-MVC app
// look for inspiration here: https://github.com/shprink/web-components-todo
document.body.appendChild(render(<style>{style}</style>));
document.body.appendChild(render(<div class="center-me">
<MyTodo />
</div>));

View File

@@ -0,0 +1,19 @@
html{
width: 100%;
height: 100%;
}
body{
display: flex;
flex-direction: column;
overflow: auto;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.center-me{
align-self: center;
}