refactored HTML injection system
- using a <div> container instead of an <iframe> (simplifies the build system, avoids the quirks iframes) - Bulma CSS is encapsulated in the #simulator ID to avoid polluting the rest of the page, as well as increasing the chances to override the page CSS
This commit is contained in:
parent
5f63dc7c7d
commit
3121342337
1508
simulator/package-lock.json
generated
1508
simulator/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -8,10 +8,11 @@
|
||||
"directory": "simulator"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node tools/build.js"
|
||||
"build": "node tools/build.js"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"node-sass": "^6.0.1",
|
||||
"purify-css": "^1.2.5",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
|
@ -1,50 +0,0 @@
|
||||
input[type=number] {
|
||||
-moz-appearance:textfield;
|
||||
}
|
||||
|
||||
.wide-label .field-label {
|
||||
flex-grow: 2.5;
|
||||
}
|
||||
|
||||
.dropdown.is-fullwidth {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dropdown.is-fullwidth .dropdown-trigger,
|
||||
.dropdown.is-fullwidth .dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-trigger.with-dropdown-icon::after {
|
||||
border: 3px solid black;
|
||||
border-radius: 2px;
|
||||
border-right: 0;
|
||||
border-top: 0;
|
||||
content: " ";
|
||||
display: block;
|
||||
height: 0.625em;
|
||||
margin-top: -0.4375em;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: center;
|
||||
width: 0.625em;
|
||||
}
|
||||
|
||||
.climate-zone {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg g {
|
||||
filter: drop-shadow( 4px 4px 3px rgba(0, 0, 0, .7));
|
||||
}
|
||||
|
||||
.climate-zone:hover {
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
svg text {
|
||||
pointer-events: none;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
function closest (el: Element, predicate: (e: Element) => boolean) {
|
||||
do if (predicate(el)) return el;
|
||||
while (el = el && <Element>el.parentNode);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Load CSS
|
||||
document.getElementsByTagName('head')[0].innerHTML += (<any>window)['app.css'];
|
||||
|
||||
// In order to be able to style SVG elements with CSS, and register events with javascript, we must use inline SVG (we can't use an img tag)
|
||||
// For this purpose, the SVG file contents are embedded in a javascript file
|
||||
document.getElementById('zones-map').innerHTML = (<any>window)['climate-zones-map.svg'];
|
||||
|
||||
document.querySelectorAll("[data-activate-modal]").forEach(elt => {
|
||||
elt.addEventListener('click', e => {
|
||||
document.getElementById(elt.getAttribute('data-activate-modal')).classList.toggle('is-active', true);
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.modal-close, .modal-card-head .delete').forEach(elt => {
|
||||
elt.addEventListener('click', e => {
|
||||
closest(elt, e => e.classList.contains('modal')).classList.toggle('is-active', false);
|
||||
});
|
||||
});
|
||||
|
||||
let zoneSelector = <HTMLSelectElement>document.getElementById('zone-selector');
|
||||
document.querySelectorAll('.climate-zone').forEach(elt => {
|
||||
elt.addEventListener('click', e => {
|
||||
let zoneName = elt.getAttribute('id');
|
||||
zoneSelector.value = zoneName;
|
||||
closest(elt, e => e.classList.contains('modal')).classList.toggle('is-active', false);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "system",
|
||||
"noImplicitAny": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"outFile": "../../.intermediate/app.js",
|
||||
"sourceMap": false
|
||||
},
|
||||
"include": ["./**/*", "../../.intermediate/app/**/*"]
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
.modal-background {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-card-head {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.modal-card {
|
||||
box-shadow: 4px 3px 10px 3px rgba(0,0,0,0.7);
|
||||
}
|
77
simulator/src/simulator.scss
Normal file
77
simulator/src/simulator.scss
Normal file
@ -0,0 +1,77 @@
|
||||
#simulator {
|
||||
@import "3rdparty/bulma/bulma.sass";
|
||||
|
||||
// properties that Bulma would set on the <html> tag
|
||||
font-size: $body-size;
|
||||
text-rendering: $body-rendering;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: $body-background-color;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-width: $body-min-width;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
text-size-adjust: 100%;
|
||||
|
||||
// properties that Bulma would set on the <body> tag
|
||||
font-weight: $body-weight;
|
||||
line-height: $body-line-height;
|
||||
font-family: $body-family;
|
||||
color: $body-color;
|
||||
& > section, & > footer {
|
||||
font-size: $body-font-size; // we must set this on each direct child of the #simulator div, because if it's specified in em, it's multiplied by the parent font-size
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
-moz-appearance:textfield;
|
||||
}
|
||||
|
||||
.wide-label .field-label {
|
||||
flex-grow: 2.5;
|
||||
}
|
||||
|
||||
.dropdown.is-fullwidth {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dropdown.is-fullwidth .dropdown-trigger,
|
||||
.dropdown.is-fullwidth .dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-trigger.with-dropdown-icon::after {
|
||||
border: 3px solid black;
|
||||
border-radius: 2px;
|
||||
border-right: 0;
|
||||
border-top: 0;
|
||||
content: " ";
|
||||
display: block;
|
||||
height: 0.625em;
|
||||
margin-top: -0.4375em;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: center;
|
||||
width: 0.625em;
|
||||
}
|
||||
|
||||
.climate-zone {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg g {
|
||||
filter: drop-shadow( 4px 4px 3px rgba(0, 0, 0, .7));
|
||||
}
|
||||
|
||||
.climate-zone:hover {
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
svg text {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
@ -1,26 +1,36 @@
|
||||
function closest (el: Element, predicate: (e: Element) => boolean) {
|
||||
do if (predicate(el)) return el;
|
||||
while (el = el && <Element>el.parentNode);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let frame = <HTMLIFrameElement>document.querySelector('iframe#simulator');
|
||||
let doc = frame.contentWindow.document;
|
||||
let container = document.getElementById('simulator');
|
||||
|
||||
frame.style.width = '100%';
|
||||
frame.style.border = 'none';
|
||||
frame.setAttribute('scrolling', 'no');
|
||||
// Insert HTML code in the container
|
||||
container.innerHTML += (<any>window)['simulator.html'];
|
||||
|
||||
// Insert HTML code in the iframe
|
||||
doc.open();
|
||||
doc.write((<any>window)['simulator.html']);
|
||||
doc.close();
|
||||
// In order to be able to style SVG elements with CSS, and register events with javascript, we must use inline SVG (we can't use an img tag)
|
||||
// For this purpose, the SVG file contents are embedded in a javascript file
|
||||
document.getElementById('zones-map').innerHTML = (<any>window)['climate-zones-map.svg'];
|
||||
|
||||
// Load iframe specific CSS
|
||||
doc.head.innerHTML += (<any>window)['simulator-in-iframe.css'];
|
||||
document.querySelectorAll("[data-activate-modal]").forEach(elt => {
|
||||
elt.addEventListener('click', e => {
|
||||
document.getElementById(elt.getAttribute('data-activate-modal')).classList.toggle('is-active', true);
|
||||
});
|
||||
});
|
||||
|
||||
// Add script inside frame
|
||||
let script = doc.createElement('script');
|
||||
script.type = "text/javascript";
|
||||
script.innerText = (<any>window)['app.js'];
|
||||
doc.body.appendChild(script);
|
||||
document.querySelectorAll('.modal-close, .modal-card-head .delete').forEach(elt => {
|
||||
elt.addEventListener('click', e => {
|
||||
closest(elt, e => e.classList.contains('modal')).classList.toggle('is-active', false);
|
||||
});
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
frame.height = Math.max(doc.body.scrollHeight, 700) + 'px';
|
||||
}, 100);
|
||||
let zoneSelector = <HTMLSelectElement>document.getElementById('zone-selector');
|
||||
document.querySelectorAll('.climate-zone').forEach(elt => {
|
||||
elt.addEventListener('click', e => {
|
||||
let zoneName = elt.getAttribute('id');
|
||||
zoneSelector.value = zoneName;
|
||||
closest(elt, e => e.classList.contains('modal')).classList.toggle('is-active', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -7,5 +7,5 @@
|
||||
"outFile": "../www/simulator.js",
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["./*", "../.intermediate/simulator.html.ts", "../.intermediate/simulator-in-iframe.css.ts", "../.intermediate/app.js.ts"]
|
||||
"include": ["./**/*", "../.intermediate/**/*"]
|
||||
}
|
||||
|
@ -15,10 +15,9 @@ if (!fs.existsSync(intermediateDir+'/app')){
|
||||
}
|
||||
|
||||
let commands = [
|
||||
() => child_process.fork(nodeModulesDir + '/node-sass/bin/node-sass', ['simulator.scss', '../.intermediate/simulator.css'], {'cwd': srcDir}),
|
||||
() => child_process.fork(toolsDir + '/purify.js', {'cwd': srcDir}),
|
||||
() => child_process.fork(toolsDir + '/embed.js'),
|
||||
() => child_process.fork(nodeModulesDir + '/typescript/lib/tsc.js', {'cwd': srcDir+'/app'}),
|
||||
() => child_process.fork(toolsDir + '/embed-app.js'),
|
||||
() => child_process.fork(nodeModulesDir + '/typescript/lib/tsc.js', {'cwd': srcDir})
|
||||
];
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
let fs = require('fs')
|
||||
|
||||
function embedJs(src, dst) {
|
||||
fs.readFile(src, 'utf8', function(err, data) {
|
||||
if(err) throw err;
|
||||
|
||||
data = data.replace(/\\"/g, '\\\\"');
|
||||
data = "(<any>window)['"+src.replace(/^.*[\\\/]/, '')+"'] = `" + data + "`;";
|
||||
|
||||
fs.writeFile(dst, data, function(err) {
|
||||
if(err) throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let toolsDir = __dirname;
|
||||
let dataDir = toolsDir + "/../data";
|
||||
let srcDir = toolsDir + "/../src";
|
||||
let intermediateDir = toolsDir + "/../.intermediate";
|
||||
|
||||
embedJs(intermediateDir+'/app.js', intermediateDir+'/app.js.ts');
|
@ -82,7 +82,5 @@ let srcDir = toolsDir + "/../src";
|
||||
let intermediateDir = toolsDir + "/../.intermediate";
|
||||
|
||||
embedHtml(srcDir+'/simulator.html', intermediateDir+'/simulator.html.ts');
|
||||
embedCss(intermediateDir+'/app.css', intermediateDir+'/app/app.css.ts');
|
||||
embedCss(srcDir+'/simulator-in-iframe.css', intermediateDir+'/simulator-in-iframe.css.ts');
|
||||
embedSvg(dataDir+'/climate-zones-map.svg', intermediateDir+'/app/climate-zones-map.svg.ts');
|
||||
embedCsv(dataDir+'/climate-zones-data.csv', intermediateDir+'/app/climate-zones-data.ts');
|
||||
embedSvg(dataDir+'/climate-zones-map.svg', intermediateDir+'/climate-zones-map.svg.ts');
|
||||
embedCsv(dataDir+'/climate-zones-data.csv', intermediateDir+'/climate-zones-data.ts');
|
||||
|
@ -4,12 +4,12 @@ const purify = require("purify-css")
|
||||
let content = ['./*.html'];
|
||||
|
||||
// Reference of all CSS files from root directory
|
||||
let css = ['3rdparty/bulma/css/bulma.css', './app/app.css'];
|
||||
let css = ['../.intermediate/simulator.css'];
|
||||
|
||||
let options = {
|
||||
output: '../.intermediate/app.css',
|
||||
whitelist: ['is-multiple', 'is-loading', 'is-narrow', 'is-active', 'climate-zone'],
|
||||
minify: true,
|
||||
output: '../www/simulator.css',
|
||||
whitelist: ['is-multiple', 'is-loading', 'is-narrow', 'is-active', 'climate-zone', 'is-max-desktop', 'is-max-widescreen'],
|
||||
minify: false,
|
||||
info: false
|
||||
};
|
||||
|
||||
|
@ -3,16 +3,20 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="simulator.css">
|
||||
|
||||
<style>
|
||||
body { margin: 0; }
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Simulateur de vhélio</h1>
|
||||
<p>Estimez votre autonomie et votre consommation d'énergie</p>
|
||||
|
||||
<iframe id="simulator"></iframe>
|
||||
<div id="simulator"></div>
|
||||
|
||||
<script type="text/javascript" src="simulator.js"></script>
|
||||
</body>
|
||||
|
Loading…
Reference in New Issue
Block a user