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

23
tests/.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
tests/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>

18
tests/basic/index.jsx Normal file
View File

@@ -0,0 +1,18 @@
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 />));
/**
* Continuation suggestionss:
* - style-attribute untested
* - Want a way to toggle classes: <Host class={{'bq-checkbox': true, 'checked': this.isChecked}}> could do
* - Supporting fragments <>...</>?
*/

19
tests/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;
}

12
tests/basic/page.jsx Normal file
View File

@@ -0,0 +1,12 @@
import {defineElement, render, CustomElement} from "../../packages/csx";
@defineElement('example-page')
export class ExamplePage extends CustomElement{
connectedCallback() {
this.appendChild(render(
<div class="page">
<h1>This page is an example!</h1>
</div>
));
}
}

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();
}

29
tests/index.html Normal file
View File

@@ -0,0 +1,29 @@
<!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>
</ul>
</body>
</html>

1
tests/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.

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,
})
]
};

10
tests/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
tests/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
tests/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;
}

57
tests/svg/svg-loader.jsx Normal file
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>
);
}
}

42
tests/svg/svg-tester.jsx Normal file
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>
);
}
}

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() 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>

10
tests/todos-mvc/index.jsx Normal file
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;
}