create-qwik 0.0.7 → 0.0.11
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 +19 -0
- package/create-qwik +106 -0
- package/index.js +2 -101
- package/package.json +5 -4
- package/starters/apps/starter/package.json +3 -3
- package/starters/apps/starter/public/favicon.ico +0 -0
- package/starters/apps/starter/rollup.config.js +16 -6
- package/starters/apps/starter/src/{index.server.qwik.tsx → index.server.tsx} +3 -4
- package/starters/apps/starter/src/my-app.tsx +59 -0
- package/starters/apps/starter/tsconfig.json +3 -3
- package/starters/apps/starter-builder/package.json +5 -5
- package/starters/apps/starter-builder/public/favicon.ico +0 -0
- package/starters/apps/starter-builder/rollup.config.js +16 -6
- package/starters/apps/starter-builder/src/{index.server.qwik.tsx → index.server.tsx} +3 -4
- package/starters/apps/starter-builder/src/my-app.tsx +29 -0
- package/starters/apps/starter-builder/tsconfig.json +3 -3
- package/starters/apps/starter-partytown/package.json +2 -2
- package/starters/apps/starter-partytown/rollup.config.js +16 -6
- package/starters/apps/starter-partytown/src/{index.server.qwik.tsx → index.server.tsx} +6 -7
- package/starters/apps/starter-partytown/src/{my-app.qwik.tsx → my-app.tsx} +10 -19
- package/starters/apps/starter-partytown/tsconfig.json +3 -3
- package/starters/apps/todo/package.json +5 -4
- package/starters/apps/todo/public/favicon.ico +0 -0
- package/starters/apps/todo/rollup.config.js +18 -6
- package/starters/apps/todo/src/components.tsx +227 -0
- package/starters/apps/todo/src/{index.server.qwik.tsx → index.server.tsx} +6 -7
- package/starters/apps/todo/src/{state.qwik.ts → state.ts} +5 -7
- package/starters/apps/todo/tsconfig.json +3 -3
- package/starters/servers/cloudflare/package.json +19 -0
- package/starters/servers/cloudflare/rollup.config.server.js +29 -0
- package/starters/servers/cloudflare/src/index.cloudflare.tsx +89 -0
- package/starters/servers/cloudflare/workers-site/package.json +4 -0
- package/starters/servers/cloudflare/wrangler.toml +18 -0
- package/starters/servers/express/package.json +1 -1
- package/starters/servers/express/server/index.js +8 -15
- package/starters/apps/starter/src/my-app.qwik.tsx +0 -63
- package/starters/apps/starter-builder/src/my-app.qwik.tsx +0 -34
- package/starters/apps/todo/src/components.qwik.tsx +0 -225
|
@@ -6,22 +6,21 @@
|
|
|
6
6
|
* found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { h } from '@builder.io/qwik';
|
|
10
9
|
import { renderToString, RenderToStringOptions, QwikLoader } from '@builder.io/qwik/server';
|
|
11
|
-
import { Footer, Header } from './my-app
|
|
10
|
+
import { Footer, Header } from './my-app';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Entry point for server-side pre-rendering.
|
|
15
14
|
*
|
|
16
15
|
* @returns a promise when all of the rendering is completed.
|
|
17
16
|
*/
|
|
18
|
-
export
|
|
17
|
+
export function renderApp(opts: RenderToStringOptions) {
|
|
19
18
|
return renderToString(
|
|
20
19
|
<html>
|
|
21
20
|
<head>
|
|
22
21
|
<title>Qwik Blank App</title>
|
|
23
22
|
</head>
|
|
24
|
-
<body>
|
|
23
|
+
<body q:base="/build/">
|
|
25
24
|
<Header />
|
|
26
25
|
<div id="my-content"></div>
|
|
27
26
|
<Footer />
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { component$, onRender$ } from '@builder.io/qwik';
|
|
2
|
+
|
|
3
|
+
export const Header = component$(() => {
|
|
4
|
+
return onRender$(() => (
|
|
5
|
+
<p style={{ 'text-align': 'center' }}>
|
|
6
|
+
<a href="https://github.com/builderio/qwik">
|
|
7
|
+
<img
|
|
8
|
+
alt="Qwik Logo"
|
|
9
|
+
width={400}
|
|
10
|
+
src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F667ab6c2283d4c4d878fb9083aacc10f"
|
|
11
|
+
/>
|
|
12
|
+
</a>
|
|
13
|
+
</p>
|
|
14
|
+
));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const Footer = component$(() => {
|
|
18
|
+
return onRender$(() => (
|
|
19
|
+
<>
|
|
20
|
+
<hr />
|
|
21
|
+
<p style={{ 'text-align': 'center' }}>
|
|
22
|
+
Made with ❤️ by{' '}
|
|
23
|
+
<a target="_blank" href="https://www.builder.io/">
|
|
24
|
+
Builder.io
|
|
25
|
+
</a>
|
|
26
|
+
</p>
|
|
27
|
+
</>
|
|
28
|
+
));
|
|
29
|
+
});
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"target": "ES2017",
|
|
4
4
|
"module": "ES2020",
|
|
5
5
|
"lib": ["es2020", "DOM"],
|
|
6
|
-
"jsx": "react",
|
|
7
|
-
"
|
|
8
|
-
"jsxFragmentFactory": "Fragment",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"jsxImportSource": "@builder.io/qwik",
|
|
9
8
|
"strict": true,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
10
|
"moduleResolution": "node",
|
|
11
11
|
"esModuleInterop": true,
|
|
12
12
|
"skipLibCheck": true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "qwik-
|
|
2
|
+
"name": "qwik-project-name",
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"description": "Blank starter app with Partytown",
|
|
5
5
|
"scripts": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"serve": "echo \"server not setup\""
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@builder.io/qwik": "
|
|
12
|
+
"@builder.io/qwik": "0.0.16-6",
|
|
13
13
|
"@rollup/plugin-node-resolve": "^13.0.6",
|
|
14
14
|
"@rollup/plugin-typescript": "^8.3.0",
|
|
15
15
|
"concurrently": "^6.4.0",
|
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
2
|
-
import { qwikRollup } from '@builder.io/qwik/optimizer';
|
|
3
2
|
import typescript from '@rollup/plugin-typescript';
|
|
3
|
+
import { qwikRollup } from '@builder.io/qwik/optimizer';
|
|
4
|
+
import { writeFileSync, mkdirSync } from 'fs';
|
|
5
|
+
import { dirname } from 'path';
|
|
4
6
|
|
|
5
7
|
export default async function () {
|
|
6
8
|
return {
|
|
7
9
|
input: [
|
|
8
|
-
'src/index.server.
|
|
9
|
-
'src/my-app.
|
|
10
|
+
'src/index.server.tsx',
|
|
11
|
+
'src/my-app.tsx'
|
|
10
12
|
],
|
|
11
13
|
plugins: [
|
|
12
14
|
nodeResolve(),
|
|
13
15
|
qwikRollup({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
entryStrategy: {type: 'hook' },
|
|
17
|
+
symbolsOutput: (data) => {
|
|
18
|
+
outputJSON('./server/build/q-symbols.json', data);
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
16
21
|
typescript(),
|
|
17
22
|
],
|
|
18
23
|
output: [
|
|
19
24
|
{
|
|
20
25
|
chunkFileNames: 'q-[hash].js',
|
|
21
26
|
dir: 'public/build',
|
|
22
|
-
format: 'es',
|
|
27
|
+
format: 'es',
|
|
23
28
|
},
|
|
24
29
|
{
|
|
25
30
|
dir: 'server/build',
|
|
@@ -28,3 +33,8 @@ export default async function () {
|
|
|
28
33
|
],
|
|
29
34
|
};
|
|
30
35
|
}
|
|
36
|
+
|
|
37
|
+
function outputJSON(path, data) {
|
|
38
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
39
|
+
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
40
|
+
}
|
|
@@ -6,28 +6,27 @@
|
|
|
6
6
|
* found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { h } from '@builder.io/qwik';
|
|
10
9
|
import { renderToString, RenderToStringOptions, QwikLoader } from '@builder.io/qwik/server';
|
|
11
|
-
import { MyApp } from './my-app
|
|
10
|
+
import { MyApp } from './my-app';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Entry point for server-side pre-rendering.
|
|
15
14
|
*
|
|
16
15
|
* @returns a promise when all of the rendering is completed.
|
|
17
16
|
*/
|
|
18
|
-
export
|
|
17
|
+
export function renderApp(opts: RenderToStringOptions) {
|
|
19
18
|
return renderToString(
|
|
20
19
|
<html>
|
|
21
20
|
<head>
|
|
22
21
|
<title>Qwik + Partytown Blank App</title>
|
|
23
|
-
<script defer async src="
|
|
22
|
+
<script defer async src="/~partytown/debug/partytown.js"></script>
|
|
24
23
|
</head>
|
|
25
|
-
<body>
|
|
24
|
+
<body q:base="/build/">
|
|
26
25
|
<MyApp />
|
|
27
26
|
<script type="text/partytown">
|
|
28
27
|
({partyTownExampleWhichBlocksMainThreadForOneSecond.toString()})()
|
|
29
28
|
</script>
|
|
30
|
-
<QwikLoader debug={opts.debug} events={['click', 'keyup', '
|
|
29
|
+
<QwikLoader debug={opts.debug} events={['click', 'keyup', 'expensiveComputationDone']} />
|
|
31
30
|
</body>
|
|
32
31
|
</html>,
|
|
33
32
|
opts
|
|
@@ -45,5 +44,5 @@ function partyTownExampleWhichBlocksMainThreadForOneSecond() {
|
|
|
45
44
|
}
|
|
46
45
|
// eslint-disable-next-line no-console
|
|
47
46
|
console.log('Expensive computation ended at:', end);
|
|
48
|
-
document.dispatchEvent(new Event('
|
|
47
|
+
document.dispatchEvent(new Event('expensiveComputationDone', { bubbles: true }));
|
|
49
48
|
}
|
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { qComponent, qHook, useEvent } from '@builder.io/qwik';
|
|
1
|
+
import { component$, onRender$, useEvent, useStore } from '@builder.io/qwik';
|
|
3
2
|
|
|
4
|
-
export const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
tagName: 'my-app', // optional
|
|
8
|
-
onMount: qHook(() => ({ name: 'World', running: true })),
|
|
9
|
-
onRender: qHook((props, state) => {
|
|
3
|
+
export const MyApp = component$(() => {
|
|
4
|
+
const state = useStore({ name: 'World', running: true });
|
|
5
|
+
return onRender$(() => {
|
|
10
6
|
// eslint-disable-next-line no-console
|
|
11
7
|
console.log('Qwik: MyApp component is rendering...');
|
|
12
8
|
return (
|
|
13
|
-
<div
|
|
14
|
-
id="my-app"
|
|
15
|
-
{...expensiveComputationDone(
|
|
16
|
-
qHook<typeof MyApp>((props, state) => (state.running = false))
|
|
17
|
-
)}
|
|
18
|
-
>
|
|
9
|
+
<div id="my-app" on$:expensiveComputationDone={() => (state.running = false)}>
|
|
19
10
|
<p style={{ 'text-align': 'center' }}>
|
|
20
11
|
<a href="https://github.com/builderio/qwik">
|
|
21
12
|
<img
|
|
@@ -25,7 +16,7 @@ export const MyApp = qComponent<{}, { name: string; running: boolean }>({
|
|
|
25
16
|
/>
|
|
26
17
|
</a>
|
|
27
18
|
</p>
|
|
28
|
-
<p>
|
|
19
|
+
<p class="congrats">
|
|
29
20
|
Congratulations <a href="https://github.com/builderio/qwik">Qwik</a> with{' '}
|
|
30
21
|
<a href="https://github.com/BuilderIO/partytown">Partytown</a> is working!
|
|
31
22
|
</p>
|
|
@@ -60,7 +51,7 @@ export const MyApp = qComponent<{}, { name: string; running: boolean }>({
|
|
|
60
51
|
</li>
|
|
61
52
|
<li>
|
|
62
53
|
Once the expensive operation is finished it dispatches custom event (
|
|
63
|
-
<code>
|
|
54
|
+
<code>expensiveComputationDone</code>) that this component listens on. It is only at
|
|
64
55
|
that time that Qwik lazy-loads the component render function and updates the UI. (See
|
|
65
56
|
network tab.)
|
|
66
57
|
</li>
|
|
@@ -68,11 +59,11 @@ export const MyApp = qComponent<{}, { name: string; running: boolean }>({
|
|
|
68
59
|
Try interacting with this component by changing{' '}
|
|
69
60
|
<input
|
|
70
61
|
value={state.name}
|
|
71
|
-
on
|
|
62
|
+
on$:keyup={() => {
|
|
72
63
|
const event = useEvent<KeyboardEvent>();
|
|
73
64
|
const input = event.target as HTMLInputElement;
|
|
74
65
|
state.name = input.value;
|
|
75
|
-
}
|
|
66
|
+
}}
|
|
76
67
|
></input>
|
|
77
68
|
.
|
|
78
69
|
</li>
|
|
@@ -99,5 +90,5 @@ export const MyApp = qComponent<{}, { name: string; running: boolean }>({
|
|
|
99
90
|
</p>
|
|
100
91
|
</div>
|
|
101
92
|
);
|
|
102
|
-
})
|
|
93
|
+
});
|
|
103
94
|
});
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"target": "ES2017",
|
|
4
4
|
"module": "ES2020",
|
|
5
5
|
"lib": ["es2020", "DOM"],
|
|
6
|
-
"jsx": "react",
|
|
7
|
-
"
|
|
8
|
-
"jsxFragmentFactory": "Fragment",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"jsxImportSource": "@builder.io/qwik",
|
|
9
8
|
"strict": true,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
10
|
"moduleResolution": "node",
|
|
11
11
|
"esModuleInterop": true,
|
|
12
12
|
"skipLibCheck": true,
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "qwik-
|
|
2
|
+
"name": "qwik-project-name",
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"description": "Classic Todo MVC app",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npm run clean && rollup -c",
|
|
7
7
|
"clean": "rimraf */build/",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"serve": "echo 'missing server'",
|
|
9
|
+
"start": "npm run clean && concurrently -c blue,green \"rollup -c --configDev --watch\" \"wait-on public/build && npm run serve\""
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@builder.io/qwik": "
|
|
12
|
+
"@builder.io/qwik": "0.0.16-6",
|
|
13
13
|
"@rollup/plugin-node-resolve": "^13.0.6",
|
|
14
14
|
"@rollup/plugin-typescript": "^8.3.0",
|
|
15
15
|
"concurrently": "^6.4.0",
|
|
16
16
|
"rimraf": "^3.0.2",
|
|
17
17
|
"rollup": "^2.59.0",
|
|
18
|
+
"rollup-plugin-terser": "^7.0.2",
|
|
18
19
|
"typescript": "^4.5.2",
|
|
19
20
|
"wait-on": "^6.0.0"
|
|
20
21
|
},
|
|
Binary file
|
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
2
|
-
import { qwikRollup } from '@builder.io/qwik/optimizer';
|
|
3
2
|
import typescript from '@rollup/plugin-typescript';
|
|
3
|
+
import { qwikRollup } from '@builder.io/qwik/optimizer';
|
|
4
|
+
import { terser } from 'rollup-plugin-terser';
|
|
5
|
+
import { writeFileSync, mkdirSync } from 'fs';
|
|
6
|
+
import { dirname } from 'path';
|
|
4
7
|
|
|
5
8
|
export default async function () {
|
|
6
9
|
return {
|
|
7
10
|
input: [
|
|
8
|
-
'src/index.server.
|
|
9
|
-
'src/components.
|
|
11
|
+
'src/index.server.tsx',
|
|
12
|
+
'src/components.tsx'
|
|
10
13
|
],
|
|
11
14
|
plugins: [
|
|
12
15
|
nodeResolve(),
|
|
13
16
|
qwikRollup({
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
entryStrategy: {type: 'hook' },
|
|
18
|
+
symbolsOutput: (data) => {
|
|
19
|
+
outputJSON('./server/build/q-symbols.json', data);
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
16
22
|
typescript(),
|
|
23
|
+
terser(),
|
|
17
24
|
],
|
|
18
25
|
output: [
|
|
19
26
|
{
|
|
20
27
|
chunkFileNames: 'q-[hash].js',
|
|
21
28
|
dir: 'public/build',
|
|
22
|
-
format: 'es',
|
|
29
|
+
format: 'es',
|
|
23
30
|
},
|
|
24
31
|
{
|
|
25
32
|
dir: 'server/build',
|
|
@@ -28,3 +35,8 @@ export default async function () {
|
|
|
28
35
|
],
|
|
29
36
|
};
|
|
30
37
|
}
|
|
38
|
+
|
|
39
|
+
function outputJSON(path, data) {
|
|
40
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
41
|
+
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
42
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Host,
|
|
3
|
+
component,
|
|
4
|
+
onRender$,
|
|
5
|
+
useStore,
|
|
6
|
+
useHostElement,
|
|
7
|
+
useEvent,
|
|
8
|
+
notifyRender,
|
|
9
|
+
$,
|
|
10
|
+
} from '@builder.io/qwik';
|
|
11
|
+
import {
|
|
12
|
+
addItem,
|
|
13
|
+
clearCompleted,
|
|
14
|
+
FilterStates,
|
|
15
|
+
getFilteredCount,
|
|
16
|
+
getFilteredItems,
|
|
17
|
+
removeItem,
|
|
18
|
+
TodoItem,
|
|
19
|
+
Todos,
|
|
20
|
+
toggleItem,
|
|
21
|
+
updateFilter,
|
|
22
|
+
} from './state';
|
|
23
|
+
/* eslint no-console: ["off"] */
|
|
24
|
+
|
|
25
|
+
// TODO(misko): APIs for better debugger experience: getProps
|
|
26
|
+
// TODO(misko): APIs for better debugger experience: dehydrate
|
|
27
|
+
// TODO(misko): APIs to have a global way of notifying which events are being fired, so we can console out render events in the demo applications
|
|
28
|
+
// TODO(misko): Place breakpoint in DOM modification and notice that too many writes are happening.
|
|
29
|
+
// TODO(misko): <item> renders twice on toggle. 1) Due to state change, 2) due to <main> somehow triggering render.
|
|
30
|
+
|
|
31
|
+
////////////////////////////////////////////////////////////////////////////////////////
|
|
32
|
+
////////////////////////////////////////////////////////////////////////////////////////
|
|
33
|
+
////////////////////////////////////////////////////////////////////////////////////////
|
|
34
|
+
////////////////////////////////////////////////////////////////////////////////////////
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Overall application component.
|
|
38
|
+
*
|
|
39
|
+
* This component is static (meaning it will never change). Because of this
|
|
40
|
+
* Qwik knows that it should never need to be rerendered, and its code will never
|
|
41
|
+
* download to the client.
|
|
42
|
+
*/
|
|
43
|
+
export const ToDoApp = component(
|
|
44
|
+
'todo',
|
|
45
|
+
$((props: { todos: Todos }) => {
|
|
46
|
+
return onRender$(() => {
|
|
47
|
+
console.log('on:qRender => <ToDoApp/>');
|
|
48
|
+
return (
|
|
49
|
+
<section class="todoapp">
|
|
50
|
+
<Header todos={props.todos} />
|
|
51
|
+
<Main todos={props.todos} />
|
|
52
|
+
<Footer todos={props.todos} />
|
|
53
|
+
</section>
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Header component which is responsible for providing UI to ender new todo item.
|
|
61
|
+
*
|
|
62
|
+
* This component only rerenders if the user interacts with it through the input.
|
|
63
|
+
*/
|
|
64
|
+
export const Header = component(
|
|
65
|
+
'header',
|
|
66
|
+
$((props: { todos: Todos }) => {
|
|
67
|
+
const state = useStore({ text: '' });
|
|
68
|
+
return onRender$(() => {
|
|
69
|
+
console.log('on:qRender => <Header/>');
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
<h1>todos</h1>
|
|
73
|
+
<input
|
|
74
|
+
class="new-todo"
|
|
75
|
+
placeholder="What needs to be done?"
|
|
76
|
+
autoFocus
|
|
77
|
+
value={state.text}
|
|
78
|
+
on$:keyup={() => {
|
|
79
|
+
const event = useEvent<KeyboardEvent>();
|
|
80
|
+
const inputValue = (event.target as HTMLInputElement).value;
|
|
81
|
+
state.text = inputValue;
|
|
82
|
+
if (event.key === 'Enter' && inputValue) {
|
|
83
|
+
addItem(props.todos, state.text);
|
|
84
|
+
state.text = '';
|
|
85
|
+
}
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
</>
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Main body of the application which contains the list of todo items.
|
|
96
|
+
*
|
|
97
|
+
* This component only rerenders/hydrates/downloads if the list of todos changes.
|
|
98
|
+
*/
|
|
99
|
+
export const Main = component(
|
|
100
|
+
'main',
|
|
101
|
+
$((props: { todos: Todos }) => {
|
|
102
|
+
return onRender$(() => {
|
|
103
|
+
console.log('on:qRender => <Main/>');
|
|
104
|
+
return (
|
|
105
|
+
<Host class="main">
|
|
106
|
+
<ul class="todo-list">
|
|
107
|
+
{getFilteredItems(props.todos).map((key) => (
|
|
108
|
+
<Item item={key} todos={props.todos} />
|
|
109
|
+
))}
|
|
110
|
+
</ul>
|
|
111
|
+
</Host>
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Individual items of the component.
|
|
119
|
+
*
|
|
120
|
+
* It only rerenders if the user infarcts with it or if the item itself changes.
|
|
121
|
+
*/
|
|
122
|
+
export const Item = component(
|
|
123
|
+
'li',
|
|
124
|
+
$((props: { item: TodoItem; todos: Todos }) => {
|
|
125
|
+
const state = useStore({ editing: false });
|
|
126
|
+
return onRender$(() => {
|
|
127
|
+
console.log(
|
|
128
|
+
'on:qRender => <Item item="' +
|
|
129
|
+
JSON.stringify(props.item, (key, value) => (key.startsWith('__') ? undefined : value)) +
|
|
130
|
+
'"/>'
|
|
131
|
+
);
|
|
132
|
+
return (
|
|
133
|
+
<Host class={{ completed: props.item.completed, editing: state.editing }}>
|
|
134
|
+
<div class="view">
|
|
135
|
+
<input
|
|
136
|
+
class="toggle"
|
|
137
|
+
type="checkbox"
|
|
138
|
+
checked={props.item.completed}
|
|
139
|
+
on$:click={() => toggleItem(props.todos, props.item)}
|
|
140
|
+
/>
|
|
141
|
+
<label
|
|
142
|
+
on$:dblclick={async () => {
|
|
143
|
+
state.editing = true;
|
|
144
|
+
const hostElement = useHostElement()!;
|
|
145
|
+
await notifyRender(hostElement);
|
|
146
|
+
const inputEl = hostElement.querySelector('input.edit') as HTMLInputElement;
|
|
147
|
+
inputEl.focus();
|
|
148
|
+
inputEl.selectionStart = inputEl.selectionEnd = inputEl.value.length;
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{props.item.title}
|
|
152
|
+
</label>
|
|
153
|
+
<button class="destroy" on$:click={() => removeItem(props.todos, props.item)}></button>
|
|
154
|
+
</div>
|
|
155
|
+
{state.editing ? (
|
|
156
|
+
<input
|
|
157
|
+
class="edit"
|
|
158
|
+
value={props.item.title}
|
|
159
|
+
on$:blur={() => (state.editing = false)}
|
|
160
|
+
on$:keyup={() => {
|
|
161
|
+
const event = useEvent<KeyboardEvent>();
|
|
162
|
+
const inputValue = (event.target as HTMLInputElement).value;
|
|
163
|
+
props.item.title = inputValue;
|
|
164
|
+
if (event.key === 'Enter') {
|
|
165
|
+
state.editing = false;
|
|
166
|
+
}
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
) : null}
|
|
170
|
+
</Host>
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Footer showing items remaining and filtering options
|
|
178
|
+
*
|
|
179
|
+
* It only rerenders if the todos count changes or filters are reset.
|
|
180
|
+
*/
|
|
181
|
+
export const Footer = component(
|
|
182
|
+
'footer',
|
|
183
|
+
$((props: { todos: Todos }) => {
|
|
184
|
+
return onRender$(() => {
|
|
185
|
+
console.log('on:qRender => <Footer/>');
|
|
186
|
+
/**
|
|
187
|
+
* Example of lite-component (it will always be included with the parent component)
|
|
188
|
+
*/
|
|
189
|
+
function Filter({ filter }: { filter: FilterStates }) {
|
|
190
|
+
const lMode = filter.toLowerCase();
|
|
191
|
+
return (
|
|
192
|
+
<li>
|
|
193
|
+
<a
|
|
194
|
+
class={{ selected: props.todos.filter == lMode }}
|
|
195
|
+
on$:click={() => updateFilter(props.todos, filter)}
|
|
196
|
+
>
|
|
197
|
+
{filter[0].toUpperCase() + filter.substr(1)}
|
|
198
|
+
</a>
|
|
199
|
+
</li>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
const remaining = getFilteredCount(props.todos);
|
|
203
|
+
return (
|
|
204
|
+
<Host class="footer">
|
|
205
|
+
{props.todos.items.length > 0 ? (
|
|
206
|
+
<>
|
|
207
|
+
<span class="todo-count">
|
|
208
|
+
<strong>{remaining}</strong>
|
|
209
|
+
{remaining == 1 ? ' item' : ' items'} left
|
|
210
|
+
</span>
|
|
211
|
+
<ul class="filters">
|
|
212
|
+
{FilterStates.map((f) => (
|
|
213
|
+
<Filter filter={f} />
|
|
214
|
+
))}
|
|
215
|
+
</ul>
|
|
216
|
+
{remaining > 0 ? (
|
|
217
|
+
<button class="clear-completed" on$:click={() => clearCompleted(props.todos)}>
|
|
218
|
+
Clear completed
|
|
219
|
+
</button>
|
|
220
|
+
) : null}
|
|
221
|
+
</>
|
|
222
|
+
) : null}
|
|
223
|
+
</Host>
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
})
|
|
227
|
+
);
|
|
@@ -6,17 +6,16 @@
|
|
|
6
6
|
* found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { h } from '@builder.io/qwik';
|
|
10
9
|
import { renderToString, RenderToStringOptions, QwikLoader } from '@builder.io/qwik/server';
|
|
11
|
-
import { ToDoApp } from './components
|
|
12
|
-
import type { Todos } from './state
|
|
10
|
+
import { ToDoApp } from './components';
|
|
11
|
+
import type { Todos } from './state';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Entry point for server-side pre-rendering.
|
|
16
15
|
*
|
|
17
16
|
* @returns a promise when all of the rendering is completed.
|
|
18
17
|
*/
|
|
19
|
-
export
|
|
18
|
+
export function renderApp(opts: RenderToStringOptions) {
|
|
20
19
|
const todos: Todos = {
|
|
21
20
|
filter: 'all',
|
|
22
21
|
items: [
|
|
@@ -30,10 +29,10 @@ export default function serverRender(opts: RenderToStringOptions) {
|
|
|
30
29
|
<html>
|
|
31
30
|
<head>
|
|
32
31
|
<title>Qwik Demo: ToDo</title>
|
|
33
|
-
<link rel="stylesheet" href="base.css" />
|
|
34
|
-
<link rel="stylesheet" href="index.css" />
|
|
32
|
+
<link rel="stylesheet" href="/base.css" />
|
|
33
|
+
<link rel="stylesheet" href="/index.css" />
|
|
35
34
|
</head>
|
|
36
|
-
<body>
|
|
35
|
+
<body q:base="/build/">
|
|
37
36
|
<ToDoApp todos={todos} />
|
|
38
37
|
<QwikLoader debug={opts.debug} />
|
|
39
38
|
</body>
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { qObject } from '@builder.io/qwik';
|
|
2
|
-
|
|
3
1
|
////////////////////////////////////////////////////////////////////////
|
|
4
2
|
// Todo Application State Interfaces
|
|
5
3
|
////////////////////////////////////////////////////////////////////////
|
|
@@ -19,16 +17,16 @@ export interface Todos {
|
|
|
19
17
|
////////////////////////////////////////////////////////////////////////
|
|
20
18
|
|
|
21
19
|
export function addItem(todos: Todos, text: string) {
|
|
22
|
-
todos.items.push(
|
|
20
|
+
todos.items.push({ completed: false, title: text });
|
|
23
21
|
updateFilter(todos);
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
export function removeItem(todos: Todos,
|
|
27
|
-
todos.items = todos.items.filter((i) => i !=
|
|
24
|
+
export function removeItem(todos: Todos, todoItem: TodoItem) {
|
|
25
|
+
todos.items = todos.items.filter((i) => i != todoItem);
|
|
28
26
|
updateFilter(todos);
|
|
29
27
|
}
|
|
30
|
-
export function toggleItem(todos: Todos,
|
|
31
|
-
|
|
28
|
+
export function toggleItem(todos: Todos, todoItem: TodoItem) {
|
|
29
|
+
todoItem.completed = !todoItem.completed;
|
|
32
30
|
updateFilter(todos);
|
|
33
31
|
}
|
|
34
32
|
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"target": "ES2017",
|
|
4
4
|
"module": "ES2020",
|
|
5
5
|
"lib": ["es2020", "DOM"],
|
|
6
|
-
"jsx": "react",
|
|
7
|
-
"
|
|
8
|
-
"jsxFragmentFactory": "Fragment",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"jsxImportSource": "@builder.io/qwik",
|
|
9
8
|
"strict": true,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
10
|
"moduleResolution": "node",
|
|
11
11
|
"esModuleInterop": true,
|
|
12
12
|
"skipLibCheck": true,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qwik-project-name",
|
|
3
|
+
"description": "Cloudflare Workers (serverless)",
|
|
4
|
+
"priority": -1,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "npm run clean && npm run build.client && npm run build.server",
|
|
7
|
+
"build.client": "rollup -c",
|
|
8
|
+
"build.server": "rollup -c rollup.config.server.js",
|
|
9
|
+
"deploy": "wrangler publish",
|
|
10
|
+
"start": "wrangler dev"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@cloudflare/kv-asset-handler": "^0.2.0",
|
|
14
|
+
"@cloudflare/workers-types": "^3.2.0",
|
|
15
|
+
"@types/service-worker-mock": "^2.0.1",
|
|
16
|
+
"@rollup/plugin-commonjs": "^21.0.1",
|
|
17
|
+
"@rollup/plugin-json": "^4.1.0"
|
|
18
|
+
}
|
|
19
|
+
}
|