jops-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +297 -0
- package/bin/init.js +223 -0
- package/lib/jops-core.min.js +5 -0
- package/package.json +34 -0
- package/templates/empty/src/App.js +11 -0
- package/templates/empty/src/layout/mainlayout.js +2 -0
- package/templates/hello-world/assets/jops-hierarchy.svg +57 -0
- package/templates/hello-world/assets/jops-lifecycle.svg +51 -0
- package/templates/hello-world/assets/jops-navigation.svg +72 -0
- package/templates/hello-world/src/App.js +21 -0
- package/templates/hello-world/src/Contact.js +20 -0
- package/templates/hello-world/src/DataSection.js +99 -0
- package/templates/hello-world/src/Features.js +68 -0
- package/templates/hello-world/src/Form.js +21 -0
- package/templates/hello-world/src/Media.js +93 -0
- package/templates/hello-world/src/css/styles.css +488 -0
- package/templates/hello-world/src/layout/layout_data.js +25 -0
- package/templates/hello-world/src/layout/layout_footer.js +8 -0
- package/templates/hello-world/src/layout/layout_form.js +44 -0
- package/templates/hello-world/src/layout/layout_header.js +17 -0
- package/templates/hello-world/src/layout/layout_hero.js +9 -0
- package/templates/hello-world/src/layout/mainlayout.js +39 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<svg viewBox="0 0 400 340" xmlns="http://www.w3.org/2000/svg" font-family="monospace">
|
|
2
|
+
<rect width="400" height="340" fill="#0f1117"/>
|
|
3
|
+
|
|
4
|
+
<text x="200" y="30" text-anchor="middle" fill="#7c8cf8" font-size="13" font-weight="bold" letter-spacing="2">VIEW LIFECYCLE</text>
|
|
5
|
+
|
|
6
|
+
<!-- Flow boxes -->
|
|
7
|
+
<!-- loadLayout -->
|
|
8
|
+
<rect x="130" y="50" width="140" height="38" rx="6" fill="#1e2235" stroke="#7c8cf8" stroke-width="1.5"/>
|
|
9
|
+
<text x="200" y="68" text-anchor="middle" fill="#7c8cf8" font-size="11">loadLayout()</text>
|
|
10
|
+
<text x="200" y="81" text-anchor="middle" fill="#3d4466" font-size="9">fetch layout module</text>
|
|
11
|
+
|
|
12
|
+
<line x1="200" y1="88" x2="200" y2="106" stroke="#3d4466" stroke-width="1.2" marker-end="url(#arrow)"/>
|
|
13
|
+
|
|
14
|
+
<!-- inflate -->
|
|
15
|
+
<rect x="130" y="106" width="140" height="38" rx="6" fill="#1e2235" stroke="#56cfb2" stroke-width="1.5"/>
|
|
16
|
+
<text x="200" y="124" text-anchor="middle" fill="#56cfb2" font-size="11">inflate()</text>
|
|
17
|
+
<text x="200" y="137" text-anchor="middle" fill="#3d4466" font-size="9">layout string → domNode</text>
|
|
18
|
+
|
|
19
|
+
<line x1="200" y1="144" x2="200" y2="162" stroke="#3d4466" stroke-width="1.2" marker-end="url(#arrow)"/>
|
|
20
|
+
|
|
21
|
+
<!-- onLayout -->
|
|
22
|
+
<rect x="130" y="162" width="140" height="38" rx="6" fill="#1e2235" stroke="#56cfb2" stroke-width="1.5"/>
|
|
23
|
+
<text x="200" y="180" text-anchor="middle" fill="#56cfb2" font-size="11">onLayout()</text>
|
|
24
|
+
<text x="200" y="193" text-anchor="middle" fill="#3d4466" font-size="9">DOM ready, add subviews</text>
|
|
25
|
+
|
|
26
|
+
<line x1="200" y1="200" x2="200" y2="218" stroke="#3d4466" stroke-width="1.2" marker-end="url(#arrow)"/>
|
|
27
|
+
|
|
28
|
+
<!-- onResume -->
|
|
29
|
+
<rect x="130" y="218" width="140" height="38" rx="6" fill="#1e2235" stroke="#f0a040" stroke-width="1.5"/>
|
|
30
|
+
<text x="200" y="236" text-anchor="middle" fill="#f0a040" font-size="11">onResume()</text>
|
|
31
|
+
<text x="200" y="249" text-anchor="middle" fill="#3d4466" font-size="9">view visible, read Store</text>
|
|
32
|
+
|
|
33
|
+
<line x1="200" y1="256" x2="200" y2="274" stroke="#3d4466" stroke-width="1.2" marker-end="url(#arrow)"/>
|
|
34
|
+
|
|
35
|
+
<!-- bindEvents -->
|
|
36
|
+
<rect x="130" y="274" width="140" height="38" rx="6" fill="#1e2235" stroke="#f0a040" stroke-width="1.5"/>
|
|
37
|
+
<text x="200" y="292" text-anchor="middle" fill="#f0a040" font-size="11">bindEvents()</text>
|
|
38
|
+
<text x="200" y="305" text-anchor="middle" fill="#3d4466" font-size="9">wire on* handlers</text>
|
|
39
|
+
|
|
40
|
+
<!-- onResume loop arrow -->
|
|
41
|
+
<path d="M 270 237 Q 340 237 340 275 Q 340 311 270 311" stroke="#f06292" stroke-width="1.2" fill="none" stroke-dasharray="4 3"/>
|
|
42
|
+
<text x="345" y="278" fill="#f06292" font-size="9" text-anchor="middle">back()</text>
|
|
43
|
+
<text x="345" y="290" fill="#f06292" font-size="9" text-anchor="middle">/ resume</text>
|
|
44
|
+
|
|
45
|
+
<!-- Arrow marker -->
|
|
46
|
+
<defs>
|
|
47
|
+
<marker id="arrow" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto">
|
|
48
|
+
<path d="M0,0 L6,3 L0,6 Z" fill="#3d4466"/>
|
|
49
|
+
</marker>
|
|
50
|
+
</defs>
|
|
51
|
+
</svg>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<svg viewBox="0 0 400 320" xmlns="http://www.w3.org/2000/svg" font-family="monospace">
|
|
2
|
+
<rect width="400" height="320" fill="#0f1117"/>
|
|
3
|
+
|
|
4
|
+
<text x="200" y="30" text-anchor="middle" fill="#7c8cf8" font-size="13" font-weight="bold" letter-spacing="2">NAVIGATION MODEL</text>
|
|
5
|
+
|
|
6
|
+
<!-- Flat tab row -->
|
|
7
|
+
<text x="20" y="60" fill="#5a6080" font-size="10">FLAT (hashchange)</text>
|
|
8
|
+
|
|
9
|
+
<rect x="20" y="68" width="70" height="34" rx="5" fill="#1a2a1a" stroke="#56cfb2" stroke-width="2"/>
|
|
10
|
+
<text x="55" y="90" text-anchor="middle" fill="#56cfb2" font-size="11">Home</text>
|
|
11
|
+
|
|
12
|
+
<rect x="100" y="68" width="70" height="34" rx="5" fill="#1e2235" stroke="#3d4466" stroke-width="1"/>
|
|
13
|
+
<text x="135" y="90" text-anchor="middle" fill="#3d4466" font-size="11">Media</text>
|
|
14
|
+
|
|
15
|
+
<rect x="180" y="68" width="70" height="34" rx="5" fill="#1e2235" stroke="#3d4466" stroke-width="1"/>
|
|
16
|
+
<text x="215" y="90" text-anchor="middle" fill="#3d4466" font-size="11">Form</text>
|
|
17
|
+
|
|
18
|
+
<rect x="260" y="68" width="70" height="34" rx="5" fill="#1e2235" stroke="#3d4466" stroke-width="1"/>
|
|
19
|
+
<text x="295" y="90" text-anchor="middle" fill="#3d4466" font-size="11">Contact</text>
|
|
20
|
+
|
|
21
|
+
<!-- Tab switch arrows -->
|
|
22
|
+
<line x1="90" y1="85" x2="100" y2="85" stroke="#56cfb2" stroke-width="1.2"/>
|
|
23
|
+
<line x1="170" y1="85" x2="180" y2="85" stroke="#3d4466" stroke-width="1" stroke-dasharray="3 2"/>
|
|
24
|
+
<line x1="250" y1="85" x2="260" y2="85" stroke="#3d4466" stroke-width="1" stroke-dasharray="3 2"/>
|
|
25
|
+
|
|
26
|
+
<line x1="20" y1="116" x2="380" y2="116" stroke="#1e2235" stroke-width="1"/>
|
|
27
|
+
|
|
28
|
+
<!-- Stack navigation -->
|
|
29
|
+
<text x="20" y="136" fill="#5a6080" font-size="10">HIERARCHICAL (navigate / back)</text>
|
|
30
|
+
|
|
31
|
+
<!-- Stack frames -->
|
|
32
|
+
<rect x="20" y="148" width="110" height="50" rx="5" fill="#1a2a1a" stroke="#56cfb2" stroke-width="1.5"/>
|
|
33
|
+
<text x="75" y="168" text-anchor="middle" fill="#56cfb2" font-size="10">Home ← _baseView</text>
|
|
34
|
+
<text x="75" y="183" text-anchor="middle" fill="#3d4466" font-size="9">flat route instance</text>
|
|
35
|
+
|
|
36
|
+
<!-- navigate arrow -->
|
|
37
|
+
<line x1="130" y1="173" x2="158" y2="173" stroke="#7c8cf8" stroke-width="1.5" marker-end="url(#arrow2)"/>
|
|
38
|
+
<text x="144" y="167" text-anchor="middle" fill="#7c8cf8" font-size="9">navigate()</text>
|
|
39
|
+
|
|
40
|
+
<rect x="160" y="148" width="110" height="50" rx="5" fill="#1e2235" stroke="#f0a040" stroke-width="1.5"/>
|
|
41
|
+
<text x="215" y="168" text-anchor="middle" fill="#f0a040" font-size="10">DetailView</text>
|
|
42
|
+
<text x="215" y="183" text-anchor="middle" fill="#3d4466" font-size="9">new instance</text>
|
|
43
|
+
|
|
44
|
+
<!-- navigate arrow 2 -->
|
|
45
|
+
<line x1="270" y1="173" x2="298" y2="173" stroke="#7c8cf8" stroke-width="1.5" marker-end="url(#arrow2)"/>
|
|
46
|
+
<text x="284" y="167" text-anchor="middle" fill="#7c8cf8" font-size="9">navigate()</text>
|
|
47
|
+
|
|
48
|
+
<rect x="300" y="148" width="80" height="50" rx="5" fill="#1e2235" stroke="#f06292" stroke-width="2"/>
|
|
49
|
+
<text x="340" y="168" text-anchor="middle" fill="#f06292" font-size="10">FormView</text>
|
|
50
|
+
<text x="340" y="183" text-anchor="middle" fill="#3d4466" font-size="9">← current</text>
|
|
51
|
+
|
|
52
|
+
<!-- Stack label -->
|
|
53
|
+
<text x="200" y="220" text-anchor="middle" fill="#3d4466" font-size="9">_navStack [ DetailView, FormView ]</text>
|
|
54
|
+
|
|
55
|
+
<!-- back arrow -->
|
|
56
|
+
<path d="M 340 198 Q 340 230 215 230 Q 90 230 90 198" stroke="#f06292" stroke-width="1.5" fill="none" marker-end="url(#arrow3)" stroke-dasharray="5 3"/>
|
|
57
|
+
<text x="200" y="246" text-anchor="middle" fill="#f06292" font-size="10">back() → destroy() popped view</text>
|
|
58
|
+
|
|
59
|
+
<!-- replace -->
|
|
60
|
+
<rect x="60" y="268" width="280" height="34" rx="5" fill="#1e2235" stroke="#7c8cf8" stroke-width="1" stroke-dasharray="5 3"/>
|
|
61
|
+
<text x="200" y="282" text-anchor="middle" fill="#7c8cf8" font-size="10">replace() → clears stack, switches route</text>
|
|
62
|
+
<text x="200" y="295" text-anchor="middle" fill="#3d4466" font-size="9">FLAG_CLEAR_TOP equivalent</text>
|
|
63
|
+
|
|
64
|
+
<defs>
|
|
65
|
+
<marker id="arrow2" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
|
|
66
|
+
<path d="M0,0 L6,3 L0,6 Z" fill="#7c8cf8"/>
|
|
67
|
+
</marker>
|
|
68
|
+
<marker id="arrow3" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
|
|
69
|
+
<path d="M0,0 L6,3 L0,6 Z" fill="#f06292"/>
|
|
70
|
+
</marker>
|
|
71
|
+
</defs>
|
|
72
|
+
</svg>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { View } from "jops-core";
|
|
2
|
+
import mainLayout from "./layout/mainlayout.js";
|
|
3
|
+
|
|
4
|
+
export default class App {
|
|
5
|
+
constructor() {
|
|
6
|
+
console.log("JOPS App mounted");
|
|
7
|
+
|
|
8
|
+
const mainContainer = new View();
|
|
9
|
+
mainContainer.setStyle("./src/css/styles.css");
|
|
10
|
+
|
|
11
|
+
/*(async () => {
|
|
12
|
+
await mainContainer.loadLayout("/src/layout/mainlayout.js");
|
|
13
|
+
mainContainer.render();
|
|
14
|
+
})();*/
|
|
15
|
+
|
|
16
|
+
// "/src/layout/mainlayout.js"
|
|
17
|
+
mainContainer.loadLayout(mainLayout).then(() => {
|
|
18
|
+
mainContainer.render();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { View } from "jops-core";
|
|
2
|
+
|
|
3
|
+
export default class Contact extends View {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async loadLayout() {
|
|
9
|
+
await super.loadLayout("/src/layout/layout_contact.js");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
onSubmit(event) {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
const data = Object.fromEntries(new FormData(event.target));
|
|
15
|
+
const message = Object.entries(data)
|
|
16
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
17
|
+
.join("\n");
|
|
18
|
+
alert(`Message Sent:\n\n${message}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { View, layout } from "jops-core";
|
|
2
|
+
|
|
3
|
+
export default class DataSection extends View {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async loadLayout() {
|
|
9
|
+
await super.loadLayout("/src/layout/layout_data.js");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async onLayout() {
|
|
13
|
+
for (const row of rowsData) {
|
|
14
|
+
const { col1, col2, col3, col4 } = row;
|
|
15
|
+
await this.addSubView(
|
|
16
|
+
"tableBody",
|
|
17
|
+
new DataTableItem(col1, col2, col3, col4),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const item of listData) {
|
|
22
|
+
await this.addSubView("list", new ListItem(item));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class DataTableItem extends View {
|
|
28
|
+
constructor(col1, col2, col3, col4) {
|
|
29
|
+
super();
|
|
30
|
+
this.col1 = col1;
|
|
31
|
+
this.col2 = col2;
|
|
32
|
+
this.col3 = col3;
|
|
33
|
+
this.col4 = col4;
|
|
34
|
+
this.layout = layout`
|
|
35
|
+
<tr>
|
|
36
|
+
<td id="col1"></td>
|
|
37
|
+
<td id="col2"></td>
|
|
38
|
+
<td id="col3"></td>
|
|
39
|
+
<td id="col4"></td>
|
|
40
|
+
</tr>
|
|
41
|
+
`;
|
|
42
|
+
this.inflate();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
onLayout() {
|
|
46
|
+
this.get("col1").set(this.col1);
|
|
47
|
+
this.get("col2").set(this.col2);
|
|
48
|
+
this.get("col3").set(this.col3);
|
|
49
|
+
this.get("col4").set(this.col4);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class ListItem extends View {
|
|
54
|
+
constructor(text) {
|
|
55
|
+
super();
|
|
56
|
+
this.layout = layout`<li id="text" class="list__item"></li>`;
|
|
57
|
+
this.text = text;
|
|
58
|
+
this.inflate();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async onLayout() {
|
|
62
|
+
this.get("text").set(this.text);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const listData = [
|
|
67
|
+
"Native ES6 module system",
|
|
68
|
+
"Android-style view lifecycle",
|
|
69
|
+
"Proxy-based reactive store",
|
|
70
|
+
"Two-way data binding",
|
|
71
|
+
"Hash-based SPA routing",
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const rowsData = [
|
|
75
|
+
{
|
|
76
|
+
col1: "001",
|
|
77
|
+
col2: "Alice Johnson",
|
|
78
|
+
col3: "Frontend Developer",
|
|
79
|
+
col4: `<span class="badge badge--active">Active</span>`,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
col1: "002",
|
|
83
|
+
col2: "Bob Smith",
|
|
84
|
+
col3: "Backend Developer",
|
|
85
|
+
col4: `<span class="badge badge--active">Active</span>`,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
col1: "003",
|
|
89
|
+
col2: "Carol White",
|
|
90
|
+
col3: "Mobile Developer",
|
|
91
|
+
col4: `<span class="badge badge--inactive">Inactive</span>`,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
col1: "004",
|
|
95
|
+
col2: "David Lee",
|
|
96
|
+
col3: "Full Stack Developer",
|
|
97
|
+
col4: `<span class="badge badge--active">Active</span>`,
|
|
98
|
+
},
|
|
99
|
+
];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { View, layout } from "jops-core";
|
|
2
|
+
// import Card from "./Card.js";
|
|
3
|
+
|
|
4
|
+
export default class Features extends View {
|
|
5
|
+
constructor() {
|
|
6
|
+
super();
|
|
7
|
+
this.layout = layout`
|
|
8
|
+
<section id="features" class="features">
|
|
9
|
+
<h2 class="section__title">Features</h2>
|
|
10
|
+
<div id="grid" class="cards">
|
|
11
|
+
</div>
|
|
12
|
+
</section>
|
|
13
|
+
`;
|
|
14
|
+
this.inflate();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//async loadLayout() {
|
|
18
|
+
// await super.loadLayout("/src/layout/layout_features.js");
|
|
19
|
+
//}
|
|
20
|
+
|
|
21
|
+
async onLayout() {
|
|
22
|
+
for (const item of data) {
|
|
23
|
+
await this.addSubView("grid", new Card(item.title, item.content));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class Card extends View {
|
|
29
|
+
constructor(title, content) {
|
|
30
|
+
super();
|
|
31
|
+
this.title = title;
|
|
32
|
+
this.content = content;
|
|
33
|
+
this.layout = layout`
|
|
34
|
+
<article class="card">
|
|
35
|
+
<h3 id="title" class="card__title"></h3>
|
|
36
|
+
<p id="content" class="card__body"></p>
|
|
37
|
+
</article>
|
|
38
|
+
`;
|
|
39
|
+
this.inflate();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// async loadLayout() {
|
|
43
|
+
// await super.loadLayout("/src/layout/card.js");
|
|
44
|
+
// }
|
|
45
|
+
|
|
46
|
+
onLayout() {
|
|
47
|
+
this.get("title").set(this.title);
|
|
48
|
+
this.get("content").set(this.content);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const data = [
|
|
53
|
+
{
|
|
54
|
+
title: "No Bundler",
|
|
55
|
+
content:
|
|
56
|
+
"Native ES6 modules loaded directly in the browser. No Webpack, no Babel, no build step.",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
title: "OOP First",
|
|
60
|
+
content:
|
|
61
|
+
"Real class inheritance with extends and super(). Write JavaScript the way it was meant to be written.",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
title: "View Composition",
|
|
65
|
+
content:
|
|
66
|
+
"Android-style declarative nested view composition. Break layouts into reusable sub-modules.",
|
|
67
|
+
},
|
|
68
|
+
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { View, Store } from "jops-core";
|
|
2
|
+
|
|
3
|
+
export default class Form extends View {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async loadLayout() {
|
|
9
|
+
await super.loadLayout("/src/layout/layout_form.js");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
onSubmit(event) {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
const data = Object.fromEntries(new FormData(event.target));
|
|
15
|
+
Store.singleton().set("form.submission", data);
|
|
16
|
+
const message = Object.entries(data)
|
|
17
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
18
|
+
.join("\n");
|
|
19
|
+
alert(`Form Submitted:\n\n${message}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { View, layout, raw, Router } from "jops-core";
|
|
2
|
+
|
|
3
|
+
export default class Media extends View {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.layout = layout`
|
|
7
|
+
<section id="media" class="media-section">
|
|
8
|
+
<h2 class="section__title">Media</h2>
|
|
9
|
+
<div id="grid" class="media-grid"></div>
|
|
10
|
+
</section>
|
|
11
|
+
`;
|
|
12
|
+
this.inflate();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async onLayout() {
|
|
16
|
+
for (const item of data) {
|
|
17
|
+
await this.addSubView("grid", new MediaCard(item.imgSrc, item.caption));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class MediaCard extends View {
|
|
23
|
+
constructor(imgSrc, caption) {
|
|
24
|
+
super();
|
|
25
|
+
this.imgSrc = imgSrc;
|
|
26
|
+
this.caption = caption;
|
|
27
|
+
this.layout = layout`
|
|
28
|
+
<div class="media-card" type="jops-event-bind" onclick="showMedia">
|
|
29
|
+
<div id="imgSrc" class="media-card__img-placeholder"></div>
|
|
30
|
+
<p id="caption" class="media-card__caption"></p>
|
|
31
|
+
</div>
|
|
32
|
+
`;
|
|
33
|
+
this.inflate();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onLayout() {
|
|
37
|
+
this.get("imgSrc").set(
|
|
38
|
+
`<img src="${this.imgSrc}" style="width:100%;border-radius:6px">`,
|
|
39
|
+
);
|
|
40
|
+
this.get("caption").set(this.caption);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
showMedia(event) {
|
|
44
|
+
Router.singleton().navigate(
|
|
45
|
+
`/mediadialog?imgSrc=${encodeURIComponent(this.imgSrc)}&caption=${encodeURIComponent(this.caption)}`,
|
|
46
|
+
MediaDialog,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class MediaDialog extends View {
|
|
52
|
+
constructor() {
|
|
53
|
+
super();
|
|
54
|
+
this.layout = layout`
|
|
55
|
+
<div class="media-dialog">
|
|
56
|
+
<button class="media-dialog__back btn btn--secondary" type="jops-event-bind" onclick="onBack">← Back</button>
|
|
57
|
+
<div id="dialogImage" class="media-dialog__image"></div>
|
|
58
|
+
<p id="dialogCaption" class="media-dialog__caption"></p>
|
|
59
|
+
</div>
|
|
60
|
+
`;
|
|
61
|
+
this.inflate();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
onLayout() {
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
const { imgSrc, caption } = Router.singleton().getParams();
|
|
67
|
+
if (!imgSrc) return;
|
|
68
|
+
this.get("dialogImage").set(
|
|
69
|
+
`<img src="${imgSrc}" style="width:100%;height:100%;object-fit:contain">`,
|
|
70
|
+
);
|
|
71
|
+
this.get("dialogCaption").set(caption ?? "");
|
|
72
|
+
}, 0);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onBack() {
|
|
76
|
+
Router.singleton().back();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const data = [
|
|
81
|
+
{
|
|
82
|
+
imgSrc: "/assets/jops-hierarchy.svg",
|
|
83
|
+
caption: "View Hierarchy — composable tree of Views",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
imgSrc: "/assets/jops-lifecycle.svg",
|
|
87
|
+
caption: "View Lifecycle — loadLayout → inflate → onLayout → onResume",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
imgSrc: "/assets/jops-navigation.svg",
|
|
91
|
+
caption: "Navigation Model — flat tabs + hierarchical stack",
|
|
92
|
+
},
|
|
93
|
+
];
|