Skip to content

Commit 70afac7

Browse files
committed
feat: editor tabs for demo code
1 parent fbe9fc4 commit 70afac7

File tree

3 files changed

+180
-82
lines changed

3 files changed

+180
-82
lines changed

live/index.html

+53-64
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33
<head>
44
<meta charset="utf-8" />
55
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6-
<title>kasper-js</title>
6+
<title>Kasper.js</title>
7+
<meta
8+
name="description"
9+
content="Kasper-JS is a work-in-progress of a JavaScript HTML template parser and renderer designed to help create and learn core mechanics of modern JavaScript frameworks."
10+
/>
711
<meta name="viewport" content="width=device-width, initial-scale=1" />
812
<link
913
href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap"
1014
rel="stylesheet"
1115
/>
1216
<script src="../dist/kasper.min.js"></script>
1317
<script src="./js/demo.js"></script>
14-
1518
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.9/ace.js"></script>
1619
<script src="https://cdn.tailwindcss.com"></script>
1720
<script>
@@ -28,27 +31,13 @@
2831
},
2932
};
3033
</script>
31-
<script></script>
3234
<style>
33-
kasper {
34-
color: #374151;
35-
}
36-
kasper h3 {
37-
font-size: 28px;
38-
margin-bottom: 8px;
39-
}
40-
kasper h4 {
41-
font-size: 21px;
42-
margin-bottom: 8px;
43-
margin-top: 8px;
44-
}
45-
4635
.app {
4736
width: 100dvw;
4837
height: 100dvh;
4938
display: grid;
5039
grid-template-rows: 1fr;
51-
grid-template-columns: minmax(724px, 1024px) minmax(120px, 1fr);
40+
grid-template-columns: minmax(724px, 804px) minmax(120px, 1fr);
5241
}
5342

5443
::-webkit-scrollbar {
@@ -67,16 +56,48 @@
6756
::-webkit-scrollbar-thumb:window-inactive {
6857
background: rgba(255, 255, 255, 0.4);
6958
}
59+
60+
button[role="tab"] {
61+
background-color: #191a1d;
62+
color: #bbbbbb;
63+
}
64+
65+
button[aria-selected="true"] {
66+
background-color: #26282c;
67+
color: #ffffff;
68+
border-color: #aaaaaa;
69+
}
7070
</style>
7171
</head>
7272
<body>
7373
<div class="app w-dvw h-dvh">
7474
<div class="flex flex-col bg-[#1d1f21]">
75-
<div class="border-b border-gray-500 px-4 pt-4 flex justify-between">
76-
<div
77-
class="text-white px-2 py-1 rounded-t-lg border-l border-r border-t border-gray-500 w-32 text-center"
78-
>
79-
App.kasper
75+
<div class="border-b border-gray-500 px-4 pt-4 flex">
76+
<div class="flex-grow flex gap-2">
77+
<button
78+
role="tab"
79+
aria-selected="true"
80+
aria-controls="htmlSource"
81+
class="text-white px-2 py-1 rounded-t-lg border-l border-r border-t border-gray-500 w-28 text-center"
82+
>
83+
App.html
84+
</button>
85+
<button
86+
role="tab"
87+
aria-selected="false"
88+
aria-controls="scriptSource"
89+
class="text-white px-2 py-1 rounded-t-lg border-l border-r border-t border-gray-500 w-28 text-center"
90+
>
91+
App.js
92+
</button>
93+
<button
94+
role="tab"
95+
aria-selected="false"
96+
aria-controls="stylesSource"
97+
class="text-white px-2 py-1 rounded-t-lg border-l border-r border-t border-gray-500 w-28 text-center"
98+
>
99+
App.css
100+
</button>
80101
</div>
81102
<button
82103
class="px-8 py-1 bg-green-600 rounded text-white font-mono hover:bg-green-500 relative -top-2"
@@ -86,25 +107,23 @@
86107
</button>
87108
</div>
88109
<div id="editor" class="flex-grow w-full"></div>
89-
<div class="border-b border-gray-500 pt-6 pl-2">
90-
<div
91-
class="text-white px-2 py-1 rounded-t-lg border-l border-r border-t border-gray-500 w-32 text-center"
92-
>
93-
Data.kasper
94-
</div>
95-
</div>
96-
<div id="json" class="w-full h-64"></div>
97110
</div>
98-
<div class="bg-gray-100">
99-
<div id="render" class="w-full h-full p-6">
111+
<div class="text-white bg-gray-700 flex flex-col">
112+
<div id="script" class="hidden"></div>
113+
<div id="render" class="flex-grow p-6">
100114
<div
101-
class="w-full h-full flex items-center justify-center p-4 text-gray-500 text-center text-xl"
115+
class="w-full h-full flex items-center justify-center p-4 text-gray-300 text-center text-xl"
102116
>
103-
Hit RENDER to view template output
117+
Press RENDER to render!
104118
</div>
105119
</div>
120+
<div
121+
class="text-sm px-4 py-2 border-t border-gray-500"
122+
id="status"
123+
></div>
106124
</div>
107125
</div>
126+
<script src="./js/playground.js"></script>
108127
<a
109128
href="https://github.com/eugenioenko/kasper-js"
110129
title="View source on GitHub"
@@ -135,35 +154,5 @@
135154
></path>
136155
</svg>
137156
</a>
138-
<script>
139-
function createEditor(id, source, mode) {
140-
let editor = ace.edit(id);
141-
editor.session.setMode(`ace/mode/${mode || "javascript"}`);
142-
editor.getSession().setUseWorker(false);
143-
editor.setTheme("ace/theme/tomorrow_night");
144-
editor.setFontSize(15);
145-
editor.setValue(source);
146-
editor.selection.moveCursorToPosition({ row: 1, column: 0 });
147-
editor.selection.selectLine();
148-
return editor;
149-
}
150-
151-
const ejson = createEditor("json", DemoJson, "json");
152-
const editor = createEditor("editor", DemoSourceCode, "html");
153-
154-
document.getElementById("execute").addEventListener("click", () => {
155-
const source = editor.getValue();
156-
const result = kasper.execute(source);
157-
const entries = JSON.parse(ejson.getValue());
158-
entries.alert = function (value) {
159-
alert(value);
160-
};
161-
const node = kasper.transpile(
162-
source,
163-
entries,
164-
document.getElementById("render")
165-
);
166-
});
167-
</script>
168157
</body>
169158
</html>

live/js/demo.js

+45-18
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,34 @@ More info: https://github.com/eugenioenko/kasper-js
2626
<h4>{{person.profession}}</h4>
2727
2828
<!-- conditional element creation -->
29-
<p @if="person.age > 21">Age is greater than 21</p>
30-
<p @elseif="person.age == 21">Age is equal to 21</p>
29+
<p @if="person.age > 21">21+ years old</p>
30+
<p @elseif="person.age == 21">21 years old</p>
3131
<p @elseif="person.age < 21">Age is less than 21</p>
3232
<p @else>Age is impossible</p>
3333
3434
<!-- iterating over arrays -->
35-
<h4>Hobbies ({{person.hobbies.length}}):</h4>
36-
<ul class="list-disc">
37-
<li @each="hobby with index of person.hobbies" class="text-red">
38-
{{index + 1}}: {{hobby}}
35+
<h4>Hobbies ({{person.hobbies.length}})</h4>
36+
<ul class="hobbies">
37+
<li
38+
@each="hobby of person.hobbies"
39+
@on:click="alert(person.name + ' ' + 'likes ' + hobby)"
40+
>
41+
{{hobby}}
3942
</li>
4043
</ul>
4144
4245
<!-- event binding -->
4346
<div class="my-4">
4447
<button
4548
class="bg-blue-500 rounded px-4 py-2 text-white hover:bg-blue-700"
46-
@on:click="alert('Hello World'); console.log(100 / 2.5 + 15)"
49+
@on:click="onClick()"
4750
>
4851
CLICK ME
4952
</button>
5053
</div>
5154
5255
<!-- evaluating code on element creation -->
53-
<div @let="student = {name: person.name, degree: 'Masters'}; console.log(student.name)">
56+
<div @let="student = {name: person.name, degree: 'Masters'}">
5457
{{student.name}}
5558
</div>
5659
@@ -86,20 +89,44 @@ More info: https://github.com/eugenioenko/kasper-js
8689
{{void console.log("same as previous just less wordy")}}
8790
`;
8891

89-
const DemoJson = `
90-
{
91-
"person": {
92-
"name": "John Doe",
93-
"profession": "Software Developer",
94-
"age": 20,
95-
"hobbies": ["reading", "music", "golf"]
96-
}
92+
const DemoScript = `
93+
var person = {
94+
name: "John Doe",
95+
profession: "Composer and Photographer",
96+
age: 28,
97+
hobbies: ["Reading", "Music", "Golf"]
9798
}
9899
100+
function onClick() {
101+
alert("Hello World");
102+
}
103+
`;
99104

105+
const DemoStyle = `
106+
h3 {
107+
font-size: 72px;
108+
line-height: 1;
109+
}
100110
111+
h4 {
112+
font-size: 21px;
113+
margin-bottom: 8px;
114+
margin-top: 8px;
115+
}
101116
117+
.hobbies {
118+
display: flex;
119+
gap: 10px;
120+
}
102121
103-
104-
122+
.hobbies > li {
123+
flex: auto;
124+
text-align: center;
125+
padding: 5px 10px;
126+
background-color: #ddd;
127+
color: #111;
128+
font-size: 21px;
129+
border-radius: 5px;
130+
cursor: pointer;
131+
}
105132
`;

live/js/playground.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
const state = {
2+
htmlSource: DemoSourceCode,
3+
scriptSource: DemoScript,
4+
stylesSource: DemoStyle,
5+
activeTab: "htmlSource",
6+
renderBtn: document.getElementById("execute"),
7+
editor: createEditor("editor", DemoSourceCode),
8+
render: document.getElementById("render"),
9+
headTag: document.getElementById("script"),
10+
status: document.getElementById("status"),
11+
tabs: [...document.querySelectorAll('button[role="tab"]')],
12+
};
13+
14+
const tabToAceMode = {
15+
htmlSource: "ace/mode/html",
16+
scriptSource: "ace/mode/javascript",
17+
stylesSource: "ace/mode/css",
18+
};
19+
20+
function createEditor(id, source) {
21+
const editor = ace.edit(id);
22+
editor.session.setMode(`ace/mode/html`);
23+
editor.getSession().setUseWorker(false);
24+
editor.setTheme("ace/theme/tomorrow_night");
25+
editor.setFontSize(15);
26+
editor.setValue(source);
27+
editor.selection.moveCursorToPosition({ row: 21, column: 0 });
28+
editor.selection.selectLine();
29+
return editor;
30+
}
31+
32+
function injectScriptAndStyles() {
33+
state.headTag.innerHTML = "";
34+
const script = document.createElement("script");
35+
script.type = "text/javascript";
36+
script.textContent = state.scriptSource;
37+
state.headTag.appendChild(script);
38+
const style = document.createElement("style");
39+
style.textContent = state.stylesSource;
40+
state.headTag.appendChild(style);
41+
}
42+
43+
state.renderBtn.addEventListener("click", () => {
44+
state[state.activeTab] = state.editor.getValue();
45+
const source = state.htmlSource;
46+
const result = kasper.execute(source);
47+
injectScriptAndStyles();
48+
kasper.transpile(source, {}, render);
49+
state.status.textContent = `Rendered at ${new Date().toJSON()}`;
50+
});
51+
52+
function switchTab(nextElement) {
53+
next = nextElement.getAttribute("aria-controls");
54+
current = document.querySelector('button[aria-selected="true"]');
55+
if (current) {
56+
current.setAttribute("aria-selected", "false");
57+
state[state.activeTab] = state.editor.getValue();
58+
}
59+
state.editor.setValue(state[next]);
60+
nextElement.setAttribute("aria-selected", "true");
61+
state.activeTab = next;
62+
const mode = tabToAceMode[next];
63+
if (mode) {
64+
state.editor.session.setMode(mode);
65+
state.editor.selection.moveCursorToPosition({ row: 1, column: 0 });
66+
state.editor.selection.selectLine();
67+
}
68+
}
69+
70+
for (const tab of state.tabs) {
71+
tab.addEventListener("click", function (e) {
72+
switchTab(e.currentTarget);
73+
});
74+
}
75+
76+
window.addEventListener(
77+
"wheel",
78+
function doPreventDefault(e) {
79+
e?.preventDefault?.();
80+
},
81+
{ passive: false }
82+
);

0 commit comments

Comments
 (0)