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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
2
|
+
import { qwikRollup } from '@builder.io/qwik/optimizer';
|
|
3
|
+
import jsonPlugin from '@rollup/plugin-json';
|
|
4
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
5
|
+
|
|
6
|
+
export default async function () {
|
|
7
|
+
return {
|
|
8
|
+
input: {
|
|
9
|
+
index: 'src/index.cloudflare.tsx',
|
|
10
|
+
},
|
|
11
|
+
inlineDynamicImports: true,
|
|
12
|
+
plugins: [
|
|
13
|
+
nodeResolve(),
|
|
14
|
+
jsonPlugin(),
|
|
15
|
+
qwikRollup({
|
|
16
|
+
entryStrategy: {
|
|
17
|
+
type: 'single',
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
commonjs(),
|
|
21
|
+
],
|
|
22
|
+
output: [
|
|
23
|
+
{
|
|
24
|
+
dir: 'workers-site/build',
|
|
25
|
+
format: 'commonjs',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import { renderApp } from './index.server';
|
|
5
|
+
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
import { getAssetFromKV } from '@cloudflare/kv-asset-handler';
|
|
8
|
+
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
import symbols from '../server/build/q-symbols.json';
|
|
11
|
+
|
|
12
|
+
const CACHING = true;
|
|
13
|
+
|
|
14
|
+
addEventListener('fetch', (event: any) => {
|
|
15
|
+
event.respondWith(handleRequest(event));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
async function handleRequest(event: any) {
|
|
19
|
+
const request = event.request;
|
|
20
|
+
const url = new URL(request.url);
|
|
21
|
+
|
|
22
|
+
if (/\.\w+$/.test(url.pathname)) {
|
|
23
|
+
// If path ends with extension, serve static file
|
|
24
|
+
return handleStaticAssets(event, url);
|
|
25
|
+
} else {
|
|
26
|
+
// Otherwise Server side render qwik
|
|
27
|
+
return handleQwik(event, request);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function handleQwik(event: any, request: Request) {
|
|
32
|
+
const cache = await caches.open('custom:qwik');
|
|
33
|
+
if (CACHING) {
|
|
34
|
+
const cachedResponse = await cache.match(request);
|
|
35
|
+
if (cachedResponse) {
|
|
36
|
+
return cachedResponse;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ssrResult = await renderApp({
|
|
41
|
+
url: new URL(request.url),
|
|
42
|
+
symbols,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const response = new Response(ssrResult.html, {
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
48
|
+
'Cache-Control': `max-age=${60}`,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
if (CACHING) {
|
|
52
|
+
event.waitUntil(cache.put(request, response.clone()));
|
|
53
|
+
}
|
|
54
|
+
return response;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function handleStaticAssets(event: any, url: URL) {
|
|
58
|
+
try {
|
|
59
|
+
// Server static file
|
|
60
|
+
const options = CACHING
|
|
61
|
+
? {
|
|
62
|
+
cacheControl(req: Request) {
|
|
63
|
+
const inmmutable = /\/q-\w+\.\w+$/.test(req.url);
|
|
64
|
+
if (inmmutable) {
|
|
65
|
+
return {
|
|
66
|
+
browserTTL: 31536000,
|
|
67
|
+
edgeTTL: 31536000,
|
|
68
|
+
bypassCache: false,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
browserTTL: 60 * 60,
|
|
73
|
+
edgeTTL: 60,
|
|
74
|
+
bypassCache: false,
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
: undefined;
|
|
79
|
+
|
|
80
|
+
const staticResponse = await getAssetFromKV(event, options);
|
|
81
|
+
return staticResponse;
|
|
82
|
+
} catch (e) {
|
|
83
|
+
// Handle 404
|
|
84
|
+
return new Response(`"${url.pathname}" not found`, {
|
|
85
|
+
status: 404,
|
|
86
|
+
statusText: 'not found',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name = "qwik-project-name"
|
|
2
|
+
type = "javascript"
|
|
3
|
+
|
|
4
|
+
account_id = ""
|
|
5
|
+
workers_dev = true
|
|
6
|
+
route = ""
|
|
7
|
+
zone_id = ""
|
|
8
|
+
compatibility_date = "2021-12-13"
|
|
9
|
+
|
|
10
|
+
[build]
|
|
11
|
+
command = "npm run build"
|
|
12
|
+
|
|
13
|
+
[build.upload]
|
|
14
|
+
format = "service-worker"
|
|
15
|
+
|
|
16
|
+
[site]
|
|
17
|
+
bucket = "public" # directory with your static assets
|
|
18
|
+
entry-point = "workers-site" # JS folder serving your assets
|
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const qwik = require('@builder.io/qwik/server');
|
|
4
2
|
const { join } = require('path');
|
|
5
3
|
const { existsSync } = require('fs');
|
|
6
|
-
|
|
4
|
+
const { renderApp } = require('./build/index.server.js');
|
|
5
|
+
const symbols = require('./build/q-symbols.json');
|
|
7
6
|
const PORT = process.env.PORT || 8080;
|
|
8
7
|
|
|
9
8
|
async function startServer() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
async function indexHandler(req, res) {
|
|
17
|
-
const result = await render({
|
|
18
|
-
url: req.url,
|
|
9
|
+
async function handleQwik(req, res) {
|
|
10
|
+
const result = await renderApp({
|
|
11
|
+
symbols,
|
|
12
|
+
url: new URL(`${req.protocol}://${req.hostname}${req.url}`),
|
|
19
13
|
debug: true,
|
|
20
14
|
});
|
|
21
15
|
res.send(result.html);
|
|
@@ -23,9 +17,7 @@ async function startServer() {
|
|
|
23
17
|
|
|
24
18
|
const app = express();
|
|
25
19
|
const publicDir = join(__dirname, '..', 'public');
|
|
26
|
-
const buildDir = join(__dirname, '..', 'public', 'build');
|
|
27
20
|
app.use(express.static(publicDir));
|
|
28
|
-
app.use(express.static(buildDir));
|
|
29
21
|
|
|
30
22
|
// Optionally server Partytown if found.
|
|
31
23
|
const partytownDir = join(__dirname, '..', 'node_modules', '@builder.io', 'partytown', 'lib');
|
|
@@ -33,7 +25,8 @@ async function startServer() {
|
|
|
33
25
|
app.use('/~partytown', express.static(partytownDir));
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
app.get('/',
|
|
28
|
+
app.get('/', handleQwik);
|
|
29
|
+
|
|
37
30
|
app.listen(PORT, () => {
|
|
38
31
|
// eslint-disable-next-line no-console
|
|
39
32
|
console.log(`http://localhost:${PORT}/`);
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { h } from '@builder.io/qwik';
|
|
2
|
-
import { qComponent, qHook, useEvent } from '@builder.io/qwik';
|
|
3
|
-
|
|
4
|
-
export const MyApp = qComponent<{}, { name: string }>({
|
|
5
|
-
tagName: 'my-app', // optional
|
|
6
|
-
onMount: qHook(() => ({ name: 'World' })),
|
|
7
|
-
onRender: qHook((props, state) => {
|
|
8
|
-
return (
|
|
9
|
-
<div>
|
|
10
|
-
<p style={{ 'text-align': 'center' }}>
|
|
11
|
-
<a href="https://github.com/builderio/qwik">
|
|
12
|
-
<img
|
|
13
|
-
alt="Qwik Logo"
|
|
14
|
-
width={400}
|
|
15
|
-
src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F667ab6c2283d4c4d878fb9083aacc10f"
|
|
16
|
-
/>
|
|
17
|
-
</a>
|
|
18
|
-
</p>
|
|
19
|
-
<p>Congratulations Qwik is working!</p>
|
|
20
|
-
|
|
21
|
-
<p>Next steps:</p>
|
|
22
|
-
<ol>
|
|
23
|
-
<li>
|
|
24
|
-
Open dev-tools network tab and notice that no JavaScript was downloaded to render this
|
|
25
|
-
page. (Zero JavaScript no matter the size of your app.)
|
|
26
|
-
</li>
|
|
27
|
-
<li>
|
|
28
|
-
Try interacting with this component by changing{' '}
|
|
29
|
-
<input
|
|
30
|
-
value={state.name}
|
|
31
|
-
on:keyup={qHook<typeof MyApp>((props, state) => {
|
|
32
|
-
const event = useEvent<KeyboardEvent>();
|
|
33
|
-
const input = event.target as HTMLInputElement;
|
|
34
|
-
state.name = input.value;
|
|
35
|
-
})}
|
|
36
|
-
></input>
|
|
37
|
-
.
|
|
38
|
-
</li>
|
|
39
|
-
<li>
|
|
40
|
-
Observe that the binding changes: <code>Hello {state.name}!</code>
|
|
41
|
-
</li>
|
|
42
|
-
<li>
|
|
43
|
-
Notice that Qwik automatically lazily-loaded and hydrated the component upon interaction
|
|
44
|
-
without the developer having to code that behavior. (Lazy hydration is what gives even
|
|
45
|
-
large apps instant on behavior.)
|
|
46
|
-
</li>
|
|
47
|
-
<li>
|
|
48
|
-
Read the docs <a href="https://github.com/builderio/qwik">here</a>.
|
|
49
|
-
</li>
|
|
50
|
-
<li>Replace the content of this component with your code.</li>
|
|
51
|
-
<li>Build amazing web-sites with unbeatable startup performance.</li>
|
|
52
|
-
</ol>
|
|
53
|
-
<hr />
|
|
54
|
-
<p style={{ 'text-align': 'center' }}>
|
|
55
|
-
Made with ❤️ by{' '}
|
|
56
|
-
<a target="_blank" href="https://www.builder.io/">
|
|
57
|
-
Builder.io
|
|
58
|
-
</a>
|
|
59
|
-
</p>
|
|
60
|
-
</div>
|
|
61
|
-
);
|
|
62
|
-
}),
|
|
63
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { h, Fragment } from '@builder.io/qwik';
|
|
2
|
-
import { qComponent, qHook } from '@builder.io/qwik';
|
|
3
|
-
|
|
4
|
-
export const Header = qComponent<{}, { name: string }>({
|
|
5
|
-
onRender: qHook(() => {
|
|
6
|
-
return (
|
|
7
|
-
<p style={{ 'text-align': 'center' }}>
|
|
8
|
-
<a href="https://github.com/builderio/qwik">
|
|
9
|
-
<img
|
|
10
|
-
alt="Qwik Logo"
|
|
11
|
-
width={400}
|
|
12
|
-
src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F667ab6c2283d4c4d878fb9083aacc10f"
|
|
13
|
-
/>
|
|
14
|
-
</a>
|
|
15
|
-
</p>
|
|
16
|
-
);
|
|
17
|
-
}),
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
export const Footer = qComponent<{}, { name: string }>({
|
|
21
|
-
onRender: qHook(() => {
|
|
22
|
-
return (
|
|
23
|
-
<>
|
|
24
|
-
<hr />
|
|
25
|
-
<p style={{ 'text-align': 'center' }}>
|
|
26
|
-
Made with ❤️ by{' '}
|
|
27
|
-
<a target="_blank" href="https://www.builder.io/">
|
|
28
|
-
Builder.io
|
|
29
|
-
</a>
|
|
30
|
-
</p>
|
|
31
|
-
</>
|
|
32
|
-
);
|
|
33
|
-
}),
|
|
34
|
-
});
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Fragment,
|
|
3
|
-
h,
|
|
4
|
-
Host,
|
|
5
|
-
qComponent,
|
|
6
|
-
qHook,
|
|
7
|
-
useHostElement,
|
|
8
|
-
useEvent,
|
|
9
|
-
notifyRender,
|
|
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.qwik';
|
|
23
|
-
/* eslint no-console: ["off"] */
|
|
24
|
-
|
|
25
|
-
// TODO(misko): APIs for better debugger experience: qProps
|
|
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 = qComponent<{ todos: Todos }>({
|
|
44
|
-
tagName: 'todo', // optional
|
|
45
|
-
onRender: qHook(({ todos }) => {
|
|
46
|
-
console.log('on:qRender => <ToDoApp/>');
|
|
47
|
-
return (
|
|
48
|
-
<section class="todoapp">
|
|
49
|
-
<Header todos={todos} />
|
|
50
|
-
<Main todos={todos} />
|
|
51
|
-
<Footer todos={todos} />
|
|
52
|
-
</section>
|
|
53
|
-
);
|
|
54
|
-
}),
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Header component which is responsible for providing UI to ender new todo item.
|
|
59
|
-
*
|
|
60
|
-
* This component only rerenders if the user interacts with it through the input.
|
|
61
|
-
*/
|
|
62
|
-
export const Header = qComponent<{ todos: Todos }, { text: string }>({
|
|
63
|
-
tagName: 'header', // optional
|
|
64
|
-
onMount: qHook(() => ({ text: '' })),
|
|
65
|
-
onRender: qHook((_, { text }) => {
|
|
66
|
-
console.log('on:qRender => <Header/>');
|
|
67
|
-
return (
|
|
68
|
-
<>
|
|
69
|
-
<h1>todos</h1>
|
|
70
|
-
<input
|
|
71
|
-
class="new-todo"
|
|
72
|
-
placeholder="What needs to be done?"
|
|
73
|
-
autoFocus
|
|
74
|
-
value={text}
|
|
75
|
-
on:keyup={qHook<typeof Header>(({ todos }, state) => {
|
|
76
|
-
const event = useEvent<KeyboardEvent>();
|
|
77
|
-
const inputValue = (event.target as HTMLInputElement).value;
|
|
78
|
-
state.text = inputValue;
|
|
79
|
-
if (event.key === 'Enter' && inputValue) {
|
|
80
|
-
addItem(todos, state.text);
|
|
81
|
-
state.text = '';
|
|
82
|
-
}
|
|
83
|
-
})}
|
|
84
|
-
/>
|
|
85
|
-
</>
|
|
86
|
-
);
|
|
87
|
-
}),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Main body of the application which contains the list of todo items.
|
|
92
|
-
*
|
|
93
|
-
* This component only rerenders/hydrates/downloads if the list of todos changes.
|
|
94
|
-
*/
|
|
95
|
-
export const Main = qComponent<{ todos: Todos }>({
|
|
96
|
-
tagName: 'main', // optional
|
|
97
|
-
onRender: qHook(({ todos }) => {
|
|
98
|
-
console.log('on:qRender => <Main/>');
|
|
99
|
-
return (
|
|
100
|
-
<Host class="main">
|
|
101
|
-
<ul class="todo-list">
|
|
102
|
-
{getFilteredItems(todos).map((key) => (
|
|
103
|
-
<Item item={key} todos={todos} />
|
|
104
|
-
))}
|
|
105
|
-
</ul>
|
|
106
|
-
</Host>
|
|
107
|
-
);
|
|
108
|
-
}),
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Individual items of the component.
|
|
113
|
-
*
|
|
114
|
-
* It only rerenders if the user infarcts with it or if the item itself changes.
|
|
115
|
-
*/
|
|
116
|
-
export const Item = qComponent<{ item: TodoItem; todos: Todos }, { editing: boolean }>({
|
|
117
|
-
tagName: 'li', // optional
|
|
118
|
-
onMount: qHook(() => ({ editing: false })),
|
|
119
|
-
onRender: qHook(({ item }, { editing }) => {
|
|
120
|
-
console.log(
|
|
121
|
-
'on:qRender => <Item item="' +
|
|
122
|
-
JSON.stringify(item, (key, value) => (key.startsWith('__') ? undefined : value)) +
|
|
123
|
-
'"/>'
|
|
124
|
-
);
|
|
125
|
-
return (
|
|
126
|
-
<Host class={{ completed: item.completed, editing: editing }}>
|
|
127
|
-
<div class="view">
|
|
128
|
-
<input
|
|
129
|
-
class="toggle"
|
|
130
|
-
type="checkbox"
|
|
131
|
-
checked={item.completed}
|
|
132
|
-
on:click={qHook<typeof Item>(({ item, todos }) => toggleItem(todos, item))}
|
|
133
|
-
/>
|
|
134
|
-
<label
|
|
135
|
-
on:dblclick={qHook<typeof Item>(async (props, state) => {
|
|
136
|
-
state.editing = true;
|
|
137
|
-
const hostElement = useHostElement()!;
|
|
138
|
-
await notifyRender(hostElement);
|
|
139
|
-
const inputEl = hostElement.querySelector('input.edit') as HTMLInputElement;
|
|
140
|
-
inputEl.focus();
|
|
141
|
-
inputEl.selectionStart = inputEl.selectionEnd = inputEl.value.length;
|
|
142
|
-
})}
|
|
143
|
-
>
|
|
144
|
-
{item.title}
|
|
145
|
-
</label>
|
|
146
|
-
<button
|
|
147
|
-
class="destroy"
|
|
148
|
-
on:click={qHook<typeof Item>(({ item, todos }) => removeItem(todos, item))}
|
|
149
|
-
></button>
|
|
150
|
-
</div>
|
|
151
|
-
{editing ? (
|
|
152
|
-
<input
|
|
153
|
-
class="edit"
|
|
154
|
-
value={item.title}
|
|
155
|
-
on:blur={qHook<typeof Item>((_, state) => (state.editing = false))}
|
|
156
|
-
on:keyup={qHook<typeof Item>(({ item }, state) => {
|
|
157
|
-
const event = useEvent<KeyboardEvent>();
|
|
158
|
-
const inputValue = (event.target as HTMLInputElement).value;
|
|
159
|
-
item.title = inputValue;
|
|
160
|
-
if (event.key === 'Enter') {
|
|
161
|
-
state.editing = false;
|
|
162
|
-
}
|
|
163
|
-
})}
|
|
164
|
-
/>
|
|
165
|
-
) : null}
|
|
166
|
-
</Host>
|
|
167
|
-
);
|
|
168
|
-
}),
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Footer showing items remaining and filtering options
|
|
173
|
-
*
|
|
174
|
-
* It only rerenders if the todos count changes or filters are reset.
|
|
175
|
-
*/
|
|
176
|
-
export const Footer = qComponent<{ todos: Todos }>({
|
|
177
|
-
tagName: 'footer', // optional
|
|
178
|
-
onRender: qHook(({ todos }) => {
|
|
179
|
-
console.log('on:qRender => <Footer/>');
|
|
180
|
-
/**
|
|
181
|
-
* Example of lite-component (it will always be included with the parent component)
|
|
182
|
-
*/
|
|
183
|
-
function Filter({ filter }: { filter: FilterStates }) {
|
|
184
|
-
const lMode = filter.toLowerCase();
|
|
185
|
-
return (
|
|
186
|
-
<li>
|
|
187
|
-
<a
|
|
188
|
-
class={{ selected: todos.filter == lMode }}
|
|
189
|
-
on:click={qHook<typeof Footer, { filter: FilterStates }>((props, _, { filter }) =>
|
|
190
|
-
updateFilter(props.todos, filter)
|
|
191
|
-
).with({ filter })}
|
|
192
|
-
>
|
|
193
|
-
{filter[0].toUpperCase() + filter.substr(1)}
|
|
194
|
-
</a>
|
|
195
|
-
</li>
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
const remaining = getFilteredCount(todos);
|
|
199
|
-
return (
|
|
200
|
-
<Host class="footer">
|
|
201
|
-
{todos.items.length > 0 ? (
|
|
202
|
-
<>
|
|
203
|
-
<span class="todo-count">
|
|
204
|
-
<strong>{remaining}</strong>
|
|
205
|
-
{remaining == 1 ? ' item' : ' items'} left
|
|
206
|
-
</span>
|
|
207
|
-
<ul class="filters">
|
|
208
|
-
{FilterStates.map((f) => (
|
|
209
|
-
<Filter filter={f} />
|
|
210
|
-
))}
|
|
211
|
-
</ul>
|
|
212
|
-
{remaining > 0 ? (
|
|
213
|
-
<button
|
|
214
|
-
class="clear-completed"
|
|
215
|
-
on:click={qHook<typeof Footer>(({ todos }) => clearCompleted(todos))}
|
|
216
|
-
>
|
|
217
|
-
Clear completed
|
|
218
|
-
</button>
|
|
219
|
-
) : null}
|
|
220
|
-
</>
|
|
221
|
-
) : null}
|
|
222
|
-
</Host>
|
|
223
|
-
);
|
|
224
|
-
}),
|
|
225
|
-
});
|