Skip to content

Commit 9d7d56c

Browse files
committed
feat: add lit-3 app demo
1 parent 91bceee commit 9d7d56c

File tree

5 files changed

+208
-0
lines changed

5 files changed

+208
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ steps.
66
* [Angular 17](./angular-17/)
77
* [Angular 21](./angular-21/)
88
* [Hono 4](./hono-4/)
9+
* [Lit 3](./lit-3/)
910
* [Preact 10](./preact-10/)
1011
* [Solid 1](./solid-1/)
1112
* [Svelte 5](./svelte-5/)

deno.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"workspace": [
33
"./angular-*",
44
"./hono-*",
5+
"./lit-*",
56
"./preact-*",
67
"./solid-*",
78
"./svelte-*",

lit-3/deno.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"imports": {
3+
"@lit/reactive-element": "https://esm.sh/*@lit/reactive-element@2.1.1?dev",
4+
"@lit/reactive-element/": "https://esm.sh/*@lit/reactive-element@2.1.1&dev/",
5+
"lit": "https://esm.sh/*lit@3.3.1?dev",
6+
"lit-element/": "https://esm.sh/*lit-element@4.2.1&dev/",
7+
"lit-html": "https://esm.sh/*lit-html@3.3.1?dev",
8+
"lit-html/": "https://esm.sh/*lit-html@3.3.1&dev/",
9+
"lit/": "https://esm.sh/*lit@3.3.1&dev/"
10+
}
11+
}

lit-3/index.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Buildless Lit 3 app</title>
7+
<script type="importmap">
8+
{
9+
"imports": {
10+
"@lit/reactive-element": "https://esm.sh/*@lit/reactive-element@2.1.1?dev",
11+
"@lit/reactive-element/": "https://esm.sh/*@lit/reactive-element@2.1.1&dev/",
12+
"lit": "https://esm.sh/*lit@3.3.1?dev",
13+
"lit-element/": "https://esm.sh/*lit-element@4.2.1&dev/",
14+
"lit-html": "https://esm.sh/*lit-html@3.3.1?dev",
15+
"lit-html/": "https://esm.sh/*lit-html@3.3.1&dev/",
16+
"lit/": "https://esm.sh/*lit@3.3.1&dev/"
17+
}
18+
}
19+
</script>
20+
<script type="module">
21+
import {
22+
availablePlugins,
23+
availablePresets,
24+
registerPreset,
25+
} from "https://esm.sh/@babel/standalone@7.28.1";
26+
27+
registerPreset("lit", {
28+
presets: [[availablePresets["typescript"]]],
29+
plugins: [
30+
[availablePlugins["proposal-decorators"], { version: "2023-05" }],
31+
[availablePlugins["transform-class-properties"]],
32+
[availablePlugins["transform-class-static-block"]],
33+
[availablePlugins["transform-private-methods"]],
34+
],
35+
});
36+
</script>
37+
<!-- `data-plugins=""` attribute is required for @babel/standalone@7.28.1 -->
38+
<script
39+
type="text/babel"
40+
data-presets="lit"
41+
data-plugins=""
42+
data-type="module"
43+
src="./main.ts"
44+
></script>
45+
</head>
46+
<body>
47+
<app-root></app-root>
48+
</body>
49+
</html>

lit-3/main.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { html, LitElement } from "lit";
2+
import { customElement, state } from "lit/decorators.js";
3+
import type { User } from "jsonplaceholder-types/types/user";
4+
import type { Post } from "jsonplaceholder-types/types/post";
5+
6+
@customElement("app-root")
7+
export class AppElement extends LitElement {
8+
static readonly #urlBase = "https://jsonplaceholder.typicode.com";
9+
10+
@state()
11+
private accessor _users: User[] | undefined;
12+
@state()
13+
private accessor _loadingUsers = false;
14+
15+
@state()
16+
private accessor _selectedUserId: number | undefined;
17+
18+
@state()
19+
private accessor _posts: Post[] | undefined;
20+
@state()
21+
private accessor _loadingPosts = false;
22+
23+
#usersController?: AbortController;
24+
#postsController?: AbortController;
25+
26+
override connectedCallback() {
27+
super.connectedCallback();
28+
this.#fetchUsers();
29+
}
30+
31+
override disconnectedCallback() {
32+
super.disconnectedCallback();
33+
this.#usersController?.abort();
34+
this.#postsController?.abort();
35+
}
36+
37+
override updated(changed: Map<string, unknown>) {
38+
if (changed.has("_selectedUserId")) {
39+
this.#fetchPosts();
40+
}
41+
}
42+
43+
async #fetchUsers() {
44+
this._loadingUsers = true;
45+
this.#usersController?.abort();
46+
this.#usersController = new AbortController();
47+
try {
48+
const res = await fetch(
49+
`${AppElement.#urlBase}/users`,
50+
{ signal: this.#usersController.signal },
51+
);
52+
this._users = await res.json();
53+
} catch (err) {
54+
if (!(err instanceof DOMException && err.name === "AbortError")) {
55+
throw err;
56+
}
57+
} finally {
58+
this._loadingUsers = false;
59+
}
60+
}
61+
62+
async #fetchPosts() {
63+
if (this._selectedUserId === undefined) {
64+
this._posts = undefined;
65+
return;
66+
}
67+
this._loadingPosts = true;
68+
this.#postsController?.abort();
69+
this.#postsController = new AbortController();
70+
try {
71+
const res = await fetch(
72+
`${AppElement.#urlBase}/posts?userId=${this._selectedUserId}`,
73+
{ signal: this.#postsController.signal },
74+
);
75+
this._posts = await res.json();
76+
} catch (err) {
77+
if (!(err instanceof DOMException && err.name === "AbortError")) {
78+
throw err;
79+
}
80+
} finally {
81+
this._loadingPosts = false;
82+
}
83+
}
84+
85+
#onUserChange(e: Event) {
86+
const value = (e.target as HTMLSelectElement).value;
87+
this._selectedUserId = value ? Number(value) : undefined;
88+
}
89+
90+
override render() {
91+
return html`
92+
<h1>Buildless Lit 3 app</h1>
93+
${this._users
94+
? html`
95+
<label>
96+
Select User:
97+
<select @change="${this.#onUserChange}">
98+
<option ?selected="${this._selectedUserId ===
99+
undefined}" hidden></option>
100+
${this._users.map(
101+
(user) =>
102+
html`
103+
<option
104+
value="${user.id}"
105+
?selected="${user.id === this._selectedUserId}"
106+
>
107+
@${user.username}: ${user.name}
108+
</option>
109+
`,
110+
)}
111+
</select>
112+
</label>
113+
`
114+
: this._loadingUsers
115+
? html`
116+
<p>Loading Users...</p>
117+
`
118+
: null} ${this._posts
119+
? html`
120+
<ul>
121+
${this._posts.map(
122+
(post) =>
123+
html`
124+
<li>${post.title}</li>
125+
`,
126+
)}
127+
</ul>
128+
`
129+
: this._loadingPosts
130+
? html`
131+
<p>Loading Posts...</p>
132+
`
133+
: this._users
134+
? html`
135+
<p>Select User to view posts</p>
136+
`
137+
: null}
138+
<p>
139+
Data Source:
140+
<a href="https://jsonplaceholder.typicode.com/" target="_blank">
141+
JSONPlaceholder
142+
</a>
143+
</p>
144+
`;
145+
}
146+
}

0 commit comments

Comments
 (0)