create-tinybase 0.2.5 → 0.3.1
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 +62 -18
- package/cli.js +2 -2
- package/package.json +7 -4
- package/screenshots/chat.png +0 -0
- package/screenshots/drawing.png +0 -0
- package/screenshots/todos.png +0 -0
- package/templates/README.md.hbs +130 -12
- package/templates/client/eslint.config.js.hbs +7 -0
- package/templates/client/package.json.hbs +28 -8
- package/templates/client/src/chat/App.tsx.hbs +28 -22
- package/templates/client/src/chat/ChatStore.tsx.hbs +108 -27
- package/templates/client/src/chat/Message.tsx.hbs +8 -9
- package/templates/client/src/chat/MessageInput.tsx.hbs +7 -7
- package/templates/client/src/chat/Messages.tsx.hbs +5 -15
- package/templates/client/src/chat/SettingsStore.tsx.hbs +86 -9
- package/templates/client/src/chat/UsernameInput.tsx.hbs +6 -11
- package/templates/client/src/chat/app.ts.hbs +18 -18
- package/templates/client/src/chat/chatStore.ts.hbs +97 -35
- package/templates/client/src/chat/message.ts.hbs +4 -3
- package/templates/client/src/chat/messageInput.css.hbs +1 -1
- package/templates/client/src/chat/messageInput.ts.hbs +7 -8
- package/templates/client/src/chat/messages.ts.hbs +7 -12
- package/templates/client/src/chat/settingsStore.ts.hbs +65 -6
- package/templates/client/src/chat/usernameInput.css.hbs +1 -1
- package/templates/client/src/chat/usernameInput.ts.hbs +6 -6
- package/templates/client/src/chat/utils.ts.hbs +26 -0
- package/templates/client/src/drawing/App.tsx.hbs +26 -20
- package/templates/client/src/drawing/BrushSize.tsx.hbs +8 -11
- package/templates/client/src/drawing/Canvas.tsx.hbs +65 -73
- package/templates/client/src/drawing/CanvasStore.tsx.hbs +104 -18
- package/templates/client/src/drawing/ColorPicker.tsx.hbs +4 -11
- package/templates/client/src/drawing/DrawingControls.tsx.hbs +7 -7
- package/templates/client/src/drawing/SettingsStore.tsx.hbs +81 -8
- package/templates/client/src/drawing/app.ts.hbs +18 -8
- package/templates/client/src/drawing/brushSize.ts.hbs +12 -5
- package/templates/client/src/drawing/canvas.ts.hbs +84 -86
- package/templates/client/src/drawing/canvasStore.ts.hbs +93 -26
- package/templates/client/src/drawing/colorPicker.ts.hbs +3 -3
- package/templates/client/src/drawing/drawingControls.ts.hbs +7 -7
- package/templates/client/src/drawing/settingsStore.ts.hbs +63 -8
- package/templates/client/src/game/App.tsx.hbs +20 -16
- package/templates/client/src/game/Board.tsx.hbs +8 -8
- package/templates/client/src/game/Game.tsx.hbs +14 -21
- package/templates/client/src/game/GameStatus.tsx.hbs +5 -5
- package/templates/client/src/game/Square.tsx.hbs +6 -11
- package/templates/client/src/game/Store.tsx.hbs +106 -16
- package/templates/client/src/game/app.ts.hbs +17 -6
- package/templates/client/src/game/board.ts.hbs +7 -7
- package/templates/client/src/game/game.ts.hbs +12 -18
- package/templates/client/src/game/gameStatus.ts.hbs +3 -3
- package/templates/client/src/game/square.ts.hbs +4 -4
- package/templates/client/src/game/store.ts.hbs +95 -23
- package/templates/client/src/index.tsx.hbs +5 -7
- package/templates/client/src/shared/Button.tsx.hbs +3 -3
- package/templates/client/src/shared/Input.tsx.hbs +2 -2
- package/templates/client/src/shared/Loading.tsx.hbs +5 -0
- package/templates/client/src/shared/button.ts.hbs +2 -2
- package/templates/client/src/shared/config.ts.hbs +4 -6
- package/templates/client/src/shared/input.ts.hbs +2 -2
- package/templates/client/src/shared/loading.css.hbs +21 -0
- package/templates/client/src/shared/loading.ts.hbs +13 -0
- package/templates/client/src/shared/pglite.ts.hbs +10 -0
- package/templates/client/src/shared/sqlite.ts.hbs +17 -0
- package/templates/client/src/todos/App.tsx.hbs +22 -22
- package/templates/client/src/todos/Store.tsx.hbs +106 -23
- package/templates/client/src/todos/TodoInput.tsx.hbs +6 -8
- package/templates/client/src/todos/TodoItem.tsx.hbs +5 -6
- package/templates/client/src/todos/TodoList.tsx.hbs +5 -6
- package/templates/client/src/todos/app.ts.hbs +16 -10
- package/templates/client/src/todos/store.ts.hbs +94 -30
- package/templates/client/src/todos/todoInput.ts.hbs +5 -8
- package/templates/client/src/todos/todoItem.ts.hbs +3 -4
- package/templates/client/src/todos/todoList.ts.hbs +6 -8
- package/templates/client/vite-env.d.ts.hbs +4 -0
- package/templates/client/vite.config.js.hbs +53 -3
- package/templates/server/index.ts.hbs +43 -0
- package/templates/server/package.json.hbs +6 -7
- package/templates/server/wrangler.toml.hbs +1 -1
- package/templates/server/index-do.ts.hbs +0 -22
- package/templates/server/index-node.ts.hbs +0 -8
- /package/templates/client/{.prettierrc.hbs → .prettierrc.json.hbs} +0 -0
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{{#if schemas}}
|
|
2
|
-
import {createMergeableStore} from 'tinybase/with-schemas';
|
|
2
|
+
{{addImport "import {createMergeableStore} from 'tinybase/with-schemas';"}}
|
|
3
3
|
{{else}}
|
|
4
|
-
import {createMergeableStore} from 'tinybase';
|
|
4
|
+
{{addImport "import {createMergeableStore} from 'tinybase';"}}
|
|
5
5
|
{{/if}}
|
|
6
6
|
|
|
7
|
-
const STORE_ID = 'game';
|
|
8
|
-
|
|
9
7
|
{{#if schemas}}
|
|
8
|
+
const TABLES_SCHEMA = {
|
|
9
|
+
board: {
|
|
10
|
+
value: {type: 'string', default: ''},
|
|
11
|
+
},
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
10
14
|
const VALUES_SCHEMA = {
|
|
11
15
|
currentPlayer: {type: 'string'},
|
|
12
16
|
gameStatus: {type: 'string'},
|
|
@@ -15,7 +19,8 @@ const STORE_ID = 'game';
|
|
|
15
19
|
} as const;
|
|
16
20
|
|
|
17
21
|
{{/if}}
|
|
18
|
-
export const store = createMergeableStore(
|
|
22
|
+
export const store = createMergeableStore(){{#if schemas}}
|
|
23
|
+
.setTablesSchema(TABLES_SCHEMA)
|
|
19
24
|
.setValuesSchema(VALUES_SCHEMA){{/if}}
|
|
20
25
|
.setDefaultContent([
|
|
21
26
|
{},
|
|
@@ -25,23 +30,90 @@ gameStatus: 'playing',
|
|
|
25
30
|
},
|
|
26
31
|
]);
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
|
|
32
|
-
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
|
|
33
|
-
|
|
34
|
-
const serverPathId = location.pathname;
|
|
35
|
-
createWsSynchronizer(
|
|
36
|
-
store,
|
|
37
|
-
new ReconnectingWebSocket(SERVER + serverPathId),
|
|
38
|
-
).then(async (synchronizer) => {
|
|
39
|
-
await synchronizer.startSync();
|
|
40
|
-
|
|
41
|
-
synchronizer.getWebSocket().addEventListener('open', () => {
|
|
42
|
-
synchronizer.load().then(() => synchronizer.save());
|
|
43
|
-
});
|
|
33
|
+
let resolveReady: () => void;
|
|
34
|
+
export const storeReady = new Promise<void>((resolve) => {
|
|
35
|
+
resolveReady = resolve;
|
|
44
36
|
});
|
|
45
|
-
{{/if}}
|
|
46
37
|
|
|
47
|
-
|
|
38
|
+
{{#if persist}}
|
|
39
|
+
{{#if persistLocalStorage}}
|
|
40
|
+
{{#if schemas}}
|
|
41
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
|
|
42
|
+
{{else}}
|
|
43
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
|
|
44
|
+
{{/if}}
|
|
45
|
+
|
|
46
|
+
const persister = createLocalPersister(store, 'game');
|
|
47
|
+
persister.load().then(() => {
|
|
48
|
+
persister.startAutoSave();
|
|
49
|
+
resolveReady();
|
|
50
|
+
});
|
|
51
|
+
{{/if}}
|
|
52
|
+
{{#if persistSqlite}}
|
|
53
|
+
{{#if schemas}}
|
|
54
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
|
|
55
|
+
{{else}}
|
|
56
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
|
|
57
|
+
{{/if}}
|
|
58
|
+
{{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.ts"}}
|
|
59
|
+
{{addImport "import {getDb} from './sqlite';"}}
|
|
60
|
+
|
|
61
|
+
getDb().then(({sqlite3, db}) => {
|
|
62
|
+
const persister = createSqliteWasmPersister(store, sqlite3, db, 'game');
|
|
63
|
+
persister.load().then(() => {
|
|
64
|
+
persister.startAutoSave();
|
|
65
|
+
resolveReady();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
{{/if}}
|
|
69
|
+
{{#if persistPglite}}
|
|
70
|
+
{{#if schemas}}
|
|
71
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
|
|
72
|
+
{{else}}
|
|
73
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
|
|
74
|
+
{{/if}}
|
|
75
|
+
{{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
|
|
76
|
+
{{addImport "import {getPgLite} from './pglite';"}}
|
|
77
|
+
|
|
78
|
+
getPgLite().then((pgLite) => {
|
|
79
|
+
createPglitePersister(store, pgLite, 'game').then((persister) => {
|
|
80
|
+
persister.load().then(() => {
|
|
81
|
+
persister.startAutoSave();
|
|
82
|
+
resolveReady();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
{{/if}}
|
|
87
|
+
{{/if}}
|
|
88
|
+
|
|
89
|
+
{{#if sync}}
|
|
90
|
+
{{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
|
|
91
|
+
{{addImport "import {SERVER} from './config';"}}
|
|
92
|
+
{{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
|
|
93
|
+
{{#if schemas}}
|
|
94
|
+
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client/with-schemas';"}}
|
|
95
|
+
{{else}}
|
|
96
|
+
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
|
|
97
|
+
{{/if}}
|
|
98
|
+
|
|
99
|
+
const serverPathId = location.pathname;
|
|
100
|
+
createWsSynchronizer(
|
|
101
|
+
store,
|
|
102
|
+
new ReconnectingWebSocket(SERVER + serverPathId),
|
|
103
|
+
).then(async (synchronizer) => {
|
|
104
|
+
await synchronizer.startSync();
|
|
105
|
+
|
|
106
|
+
synchronizer.getWebSocket().addEventListener('open', () => {
|
|
107
|
+
synchronizer.load().then(() => {
|
|
108
|
+
synchronizer.save();
|
|
109
|
+
{{#unless persist}}resolveReady();{{/unless}}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
{{/if}}
|
|
114
|
+
|
|
115
|
+
{{#unless persist}}{{#unless sync}}
|
|
116
|
+
resolveReady();
|
|
117
|
+
{{/unless}}{{/unless}}
|
|
118
|
+
|
|
119
|
+
export type GameStore = typeof store;
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
{{#if sync}}
|
|
2
|
-
import {getUniqueId} from 'tinybase';
|
|
3
|
-
|
|
2
|
+
{{addImport "import {getUniqueId} from 'tinybase';"}}
|
|
4
3
|
if (location.pathname === '/') {
|
|
5
4
|
location.assign('/' + getUniqueId());
|
|
6
5
|
}
|
|
7
6
|
{{/if}}
|
|
7
|
+
|
|
8
8
|
{{#if react}}
|
|
9
|
-
import ReactDOM from 'react-dom/client';
|
|
10
|
-
import {App} from './App';
|
|
9
|
+
{{addImport "import ReactDOM from 'react-dom/client';"}}
|
|
10
|
+
{{addImport "import {App} from './App';"}}
|
|
11
11
|
{{includeFile template="client/src/{{appType}}/App.tsx.hbs" output="client/src/App.{{ext}}"}}
|
|
12
|
-
|
|
13
12
|
addEventListener('load', () => {
|
|
14
13
|
ReactDOM.createRoot(document.getElementById('app')!).render(
|
|
15
14
|
<App />);
|
|
16
15
|
});
|
|
17
16
|
{{else}}
|
|
18
|
-
import {app} from './app';
|
|
17
|
+
{{addImport "import {app} from './app';"}}
|
|
19
18
|
{{includeFile template="client/src/{{appType}}/app.ts.hbs" output="client/src/app.{{ext}}"}}
|
|
20
|
-
|
|
21
19
|
addEventListener('load', () => {
|
|
22
20
|
app();
|
|
23
21
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{{includeFile template="client/src/shared/button.css.hbs" output="client/src/button.css"}}
|
|
2
|
-
import './button.css';
|
|
3
|
-
import {ReactNode} from 'react';
|
|
2
|
+
{{addImport "import './button.css';"}}
|
|
3
|
+
{{addImport "import {ReactNode} from 'react';"}}
|
|
4
4
|
|
|
5
5
|
interface ButtonProps {
|
|
6
6
|
children: ReactNode;
|
|
@@ -13,4 +13,4 @@ export const Button = ({children, onClick, variant = 'default', type = 'button'}
|
|
|
13
13
|
<button onClick={onClick} type={type} className={variant==='primary' ? 'primary' : undefined}>
|
|
14
14
|
{children}
|
|
15
15
|
</button>
|
|
16
|
-
);
|
|
16
|
+
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{{includeFile template="client/src/shared/input.css.hbs" output="client/src/input.css"}}
|
|
2
|
-
import './input.css';
|
|
2
|
+
{{addImport "import './input.css';"}}
|
|
3
3
|
|
|
4
4
|
interface InputProps {
|
|
5
5
|
value: string;
|
|
@@ -13,4 +13,4 @@ export const Input = ({value, onChange, placeholder = '', autoFocus = false}: In
|
|
|
13
13
|
placeholder={placeholder}
|
|
14
14
|
autoFocus={autoFocus}
|
|
15
15
|
/>
|
|
16
|
-
);
|
|
16
|
+
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{{includeFile template="client/src/shared/button.css.hbs" output="client/src/button.css"}}
|
|
2
|
-
import './button.css';
|
|
2
|
+
{{addImport "import './button.css';"}}
|
|
3
3
|
|
|
4
4
|
export const createButton = (
|
|
5
5
|
text: string,
|
|
@@ -13,4 +13,4 @@ button.className = 'primary';
|
|
|
13
13
|
}
|
|
14
14
|
button.addEventListener('click', onClick);
|
|
15
15
|
return button;
|
|
16
|
-
};
|
|
16
|
+
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
{{#if
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export const SERVER = 'ws://localhost:8787';
|
|
6
|
-
{{/if}}
|
|
1
|
+
{{#if (eq syncType "node")}}
|
|
2
|
+
export const SERVER = 'ws://localhost:8043';
|
|
3
|
+
{{else if (eq syncType "durable-objects")}}
|
|
4
|
+
export const SERVER = 'ws://localhost:8787';
|
|
7
5
|
{{else}}
|
|
8
6
|
export const SERVER = 'wss://vite.tinybase.org';
|
|
9
7
|
{{/if}}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{{includeFile template="client/src/shared/input.css.hbs" output="client/src/input.css"}}
|
|
2
|
-
import './input.css';
|
|
2
|
+
{{addImport "import './input.css';"}}
|
|
3
3
|
|
|
4
4
|
export const createInput = (
|
|
5
5
|
placeholder: string = '',
|
|
@@ -14,4 +14,4 @@ if (onInput) {
|
|
|
14
14
|
input.addEventListener('input', () => onInput(input.value));
|
|
15
15
|
}
|
|
16
16
|
return input;
|
|
17
|
-
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#loading {
|
|
2
|
+
animation: spin 1s infinite linear;
|
|
3
|
+
height: 2rem;
|
|
4
|
+
margin: 40vh auto;
|
|
5
|
+
width: 2rem;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
#loading::before {
|
|
9
|
+
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="2rem" viewBox="0 0 100 100">
|
|
10
|
+
<path d="M50 10A40 40 0 1 1 10 50" stroke="%23d81b60" fill="none" stroke-width="4" />
|
|
11
|
+
</svg>');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@keyframes spin {
|
|
15
|
+
from {
|
|
16
|
+
transform: rotate(0);
|
|
17
|
+
}
|
|
18
|
+
to {
|
|
19
|
+
transform: rotate(360deg);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{{includeFile template="client/src/shared/loading.css.hbs" output="client/src/loading.css"}}
|
|
2
|
+
{{addImport "import './loading.css';"}}
|
|
3
|
+
|
|
4
|
+
export const showLoading = (container: HTMLElement): HTMLElement => {
|
|
5
|
+
const loadingDiv = document.createElement('div');
|
|
6
|
+
loadingDiv.id = 'loading';
|
|
7
|
+
container.appendChild(loadingDiv);
|
|
8
|
+
return loadingDiv;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const hideLoading = (loadingDiv: HTMLElement): void => {
|
|
12
|
+
loadingDiv.remove();
|
|
13
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{{addImport "import {PGlite} from '@electric-sql/pglite';"}}
|
|
2
|
+
|
|
3
|
+
let pgLitePromise: Promise<PGlite> | null = null;
|
|
4
|
+
|
|
5
|
+
export const getPgLite = async (): Promise<PGlite> => {
|
|
6
|
+
if (!pgLitePromise) {
|
|
7
|
+
pgLitePromise = PGlite.create('idb://local');
|
|
8
|
+
}
|
|
9
|
+
return await pgLitePromise;
|
|
10
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{{addImport "import sqlite3InitModule, {type Database, type Sqlite3Static} from '@sqlite.org/sqlite-wasm';"}}
|
|
2
|
+
|
|
3
|
+
let sqlite3Promise: Promise<Sqlite3Static> | null = null;
|
|
4
|
+
let db: Database | null = null;
|
|
5
|
+
|
|
6
|
+
export const getDb = async () => {
|
|
7
|
+
if (!sqlite3Promise) {
|
|
8
|
+
sqlite3Promise = sqlite3InitModule({
|
|
9
|
+
locateFile: () => '/sqlite3.wasm',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
if (!db) {
|
|
13
|
+
const sqlite3 = await sqlite3Promise;
|
|
14
|
+
db = new sqlite3.oo1.DB('file:local?vfs=kvvfs', 'c');
|
|
15
|
+
}
|
|
16
|
+
return {sqlite3: await sqlite3Promise, db};
|
|
17
|
+
};
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import {StrictMode} from 'react';
|
|
2
|
-
|
|
3
|
-
{{
|
|
4
|
-
|
|
5
|
-
{{
|
|
6
|
-
import {Provider} from 'tinybase/ui-react';
|
|
7
|
-
{{/if}}
|
|
8
|
-
import {Inspector} from 'tinybase/ui-react-inspector';
|
|
9
|
-
|
|
1
|
+
{{addImport "import {StrictMode, useState} from 'react';"}}
|
|
2
|
+
{{includeFile template="client/src/shared/Loading.tsx.hbs" output="client/src/Loading.{{ext}}"}}
|
|
3
|
+
{{addImport "import {Loading} from './Loading';"}}
|
|
4
|
+
{{addImport "import {Provider} from 'tinybase/ui-react';"}}
|
|
5
|
+
{{addImport "import {Inspector} from 'tinybase/ui-react-inspector';"}}
|
|
10
6
|
{{includeFile template="client/src/todos/Store.tsx.hbs" output="client/src/Store.{{ext}}"}}
|
|
11
|
-
import {Store} from './Store';
|
|
12
|
-
|
|
7
|
+
{{addImport "import {Store} from './Store';"}}
|
|
13
8
|
{{includeFile template="client/src/todos/TodoInput.tsx.hbs" output="client/src/TodoInput.{{ext}}"}}
|
|
14
|
-
import {TodoInput} from './TodoInput';
|
|
15
|
-
|
|
9
|
+
{{addImport "import {TodoInput} from './TodoInput';"}}
|
|
16
10
|
{{includeFile template="client/src/todos/TodoList.tsx.hbs" output="client/src/TodoList.{{ext}}"}}
|
|
17
|
-
import {TodoList} from './TodoList';
|
|
11
|
+
{{addImport "import {TodoList} from './TodoList';"}}
|
|
12
|
+
|
|
13
|
+
export const App = () => {
|
|
14
|
+
const [loading, setLoading] = useState(true);
|
|
18
15
|
|
|
19
|
-
const App = () => {
|
|
20
16
|
return (
|
|
21
17
|
<StrictMode>
|
|
22
18
|
<Provider>
|
|
23
|
-
<Store />
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
<Store onReady={()=> setLoading(false)} />
|
|
20
|
+
{loading ? (
|
|
21
|
+
<Loading />
|
|
22
|
+
) : (
|
|
23
|
+
<>
|
|
24
|
+
<TodoInput />
|
|
25
|
+
<TodoList />
|
|
26
|
+
<Inspector />
|
|
27
|
+
</>
|
|
28
|
+
)}
|
|
27
29
|
</Provider>
|
|
28
30
|
</StrictMode>
|
|
29
31
|
);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export {App};
|
|
32
|
+
};
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
{{#if schemas}}
|
|
2
|
-
import {createMergeableStore} from 'tinybase/with-schemas';
|
|
3
|
-
import * as UiReact from 'tinybase/ui-react/with-schemas';
|
|
4
|
-
import {type NoValuesSchema} from 'tinybase/with-schemas';
|
|
5
|
-
{{else}}
|
|
6
|
-
import {createMergeableStore} from 'tinybase';
|
|
7
|
-
import {useCreateStore, useProvideStore, useAddRowCallback, useDelRowCallback, useRow, useRowIds, useSetPartialRowCallback} from 'tinybase/ui-react';
|
|
8
|
-
{{/if}}
|
|
9
|
-
|
|
10
|
-
export const STORE_ID = 'todos';
|
|
2
|
+
{{addImport "import {createMergeableStore, type Row} from 'tinybase/with-schemas';"}}
|
|
3
|
+
{{addImport "import * as UiReact from 'tinybase/ui-react/with-schemas';"}}
|
|
4
|
+
{{addImport "import {type NoValuesSchema} from 'tinybase/with-schemas';"}}
|
|
11
5
|
|
|
12
|
-
{{#if schemas}}
|
|
13
6
|
const TODOS_SCHEMA = {
|
|
14
7
|
todos: {
|
|
15
|
-
text: {type: 'string'},
|
|
16
|
-
completed: {type: 'boolean'},
|
|
8
|
+
text: {type: 'string', default: ''},
|
|
9
|
+
completed: {type: 'boolean', default: false},
|
|
17
10
|
},
|
|
18
11
|
} as const;
|
|
19
12
|
|
|
20
13
|
type Schemas = [typeof TODOS_SCHEMA, NoValuesSchema];
|
|
21
14
|
|
|
22
|
-
const {
|
|
15
|
+
const {useAddRowCallback, useCreateMergeableStore, {{#if persist}}useCreatePersister, {{/if}}useDelRowCallback, useProvideStore, useRow, useSetPartialRowCallback, useSortedRowIds} = UiReact as UiReact.WithSchemas<Schemas>;
|
|
16
|
+
{{else}}
|
|
17
|
+
{{addImport "import {createMergeableStore} from 'tinybase';"}}
|
|
18
|
+
{{addImport "import {useCreateMergeableStore, useProvideStore, useAddRowCallback, useDelRowCallback, useRow, useSortedRowIds, useSetPartialRowCallback} from 'tinybase/ui-react';"}}
|
|
23
19
|
{{/if}}
|
|
24
20
|
|
|
25
|
-
export {
|
|
21
|
+
export type TodoRow = {{#if schemas}}Row<typeof TODOS_SCHEMA, 'todos'>{{else}}{text: string; completed: boolean}{{/if}};
|
|
22
|
+
|
|
23
|
+
export {useAddRowCallback, useDelRowCallback, useRow, useSortedRowIds, useSetPartialRowCallback};
|
|
24
|
+
|
|
25
|
+
export const STORE_ID = 'todos';
|
|
26
26
|
|
|
27
|
-
export const Store = () => {
|
|
28
|
-
const store =
|
|
29
|
-
createMergeableStore(
|
|
27
|
+
export const Store = ({onReady}: {onReady?: () => void}) => {
|
|
28
|
+
const store = useCreateMergeableStore(() =>
|
|
29
|
+
createMergeableStore(){{#if schemas}}
|
|
30
30
|
.setTablesSchema(TODOS_SCHEMA){{/if}}
|
|
31
31
|
.setDefaultContent([
|
|
32
32
|
{
|
|
@@ -41,15 +41,92 @@ todos: {
|
|
|
41
41
|
|
|
42
42
|
useProvideStore(STORE_ID, store);
|
|
43
43
|
|
|
44
|
+
{{#if persist}}
|
|
45
|
+
{{#if persistLocalStorage}}
|
|
46
|
+
{{#if schemas}}
|
|
47
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
|
|
48
|
+
{{else}}
|
|
49
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
|
|
50
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
51
|
+
{{/if}}
|
|
52
|
+
|
|
53
|
+
useCreatePersister(
|
|
54
|
+
store,
|
|
55
|
+
(store) => createLocalPersister(store, STORE_ID),
|
|
56
|
+
[],
|
|
57
|
+
async (persister) => {
|
|
58
|
+
await persister.load();
|
|
59
|
+
await persister.startAutoSave();
|
|
60
|
+
onReady?.();
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
{{/if}}
|
|
64
|
+
{{#if persistSqlite}}
|
|
65
|
+
{{#if schemas}}
|
|
66
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
|
|
67
|
+
{{else}}
|
|
68
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
|
|
69
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
70
|
+
{{/if}}
|
|
71
|
+
{{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.{{ext}}"}}
|
|
72
|
+
{{addImport "import {getDb} from './sqlite';"}}
|
|
73
|
+
|
|
74
|
+
useCreatePersister(
|
|
75
|
+
store,
|
|
76
|
+
async (store) => {
|
|
77
|
+
const {sqlite3, db} = await getDb();
|
|
78
|
+
return createSqliteWasmPersister(store, sqlite3, db, STORE_ID);
|
|
79
|
+
},
|
|
80
|
+
[],
|
|
81
|
+
async (persister) => {
|
|
82
|
+
await persister.load();
|
|
83
|
+
await persister.startAutoSave();
|
|
84
|
+
onReady?.();
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
{{/if}}
|
|
88
|
+
{{#if persistPglite}}
|
|
89
|
+
{{#if schemas}}
|
|
90
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
|
|
91
|
+
{{else}}
|
|
92
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
|
|
93
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
94
|
+
{{/if}}
|
|
95
|
+
{{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
|
|
96
|
+
{{addImport "import {getPgLite} from './pglite';"}}
|
|
97
|
+
|
|
98
|
+
useCreatePersister(
|
|
99
|
+
store,
|
|
100
|
+
async (store) => {
|
|
101
|
+
const pgLite = await getPgLite();
|
|
102
|
+
return await createPglitePersister(store, pgLite, STORE_ID);
|
|
103
|
+
},
|
|
104
|
+
[],
|
|
105
|
+
async (persister) => {
|
|
106
|
+
await persister.load();
|
|
107
|
+
await persister.startAutoSave();
|
|
108
|
+
onReady?.();
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
{{/if}}
|
|
112
|
+
{{/if}}
|
|
113
|
+
|
|
44
114
|
{{#if sync}}
|
|
45
115
|
{{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
|
|
46
116
|
{{addImport "import {SERVER} from './config';"}}
|
|
47
117
|
{{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
|
|
48
|
-
{{
|
|
49
|
-
|
|
50
|
-
{{
|
|
118
|
+
{{#if schemas}}
|
|
119
|
+
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client/with-schemas';"}}
|
|
120
|
+
{{else}}
|
|
121
|
+
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
|
|
122
|
+
{{/if}}
|
|
123
|
+
{{#if schemas}}
|
|
124
|
+
{{addImport "import {useCreateSynchronizer} from 'tinybase/ui-react/with-schemas';"}}
|
|
125
|
+
{{else}}
|
|
126
|
+
{{addImport "import {useCreateSynchronizer} from 'tinybase/ui-react';"}}
|
|
127
|
+
{{/if}}
|
|
51
128
|
|
|
52
|
-
useCreateSynchronizer(store, async (store
|
|
129
|
+
useCreateSynchronizer(store, async (store) => {
|
|
53
130
|
const serverPathId = location.pathname;
|
|
54
131
|
const synchronizer = await createWsSynchronizer(
|
|
55
132
|
store,
|
|
@@ -57,14 +134,20 @@ useProvideStore(STORE_ID, store);
|
|
|
57
134
|
);
|
|
58
135
|
await synchronizer.startSync();
|
|
59
136
|
|
|
60
|
-
// If the websocket reconnects in the future, do another explicit sync.
|
|
61
137
|
synchronizer.getWebSocket().addEventListener('open', () => {
|
|
62
|
-
synchronizer.load().then(() =>
|
|
138
|
+
synchronizer.load().then(() => {
|
|
139
|
+
synchronizer.save();
|
|
140
|
+
{{#unless persist}}onReady?.();{{/unless}}
|
|
141
|
+
});
|
|
63
142
|
});
|
|
64
143
|
|
|
65
144
|
return synchronizer;
|
|
66
145
|
});
|
|
67
146
|
{{/if}}
|
|
68
147
|
|
|
148
|
+
{{#unless persist}}{{#unless sync}}
|
|
149
|
+
onReady?.();
|
|
150
|
+
{{/unless}}{{/unless}}
|
|
151
|
+
|
|
69
152
|
return null;
|
|
70
153
|
};
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
{{includeFile template="client/src/todos/todoInput.css.hbs" output="client/src/todoInput.css"}}
|
|
2
|
-
import './todoInput.css';
|
|
3
|
-
import {useState} from 'react';
|
|
4
|
-
import {useAddRowCallback, STORE_ID} from './Store';
|
|
5
|
-
|
|
2
|
+
{{addImport "import './todoInput.css';"}}
|
|
3
|
+
{{addImport "import {useState} from 'react';"}}
|
|
4
|
+
{{addImport "import {useAddRowCallback, STORE_ID} from './Store';"}}
|
|
6
5
|
{{includeFile template="client/src/shared/Button.tsx.hbs" output="client/src/Button.{{ext}}"}}
|
|
7
|
-
import {Button} from './Button';
|
|
8
|
-
|
|
6
|
+
{{addImport "import {Button} from './Button';"}}
|
|
9
7
|
{{includeFile template="client/src/shared/Input.tsx.hbs" output="client/src/Input.{{ext}}"}}
|
|
10
|
-
import {Input} from './Input';
|
|
8
|
+
{{addImport "import {Input} from './Input';"}}
|
|
11
9
|
|
|
12
10
|
export const TodoInput = () => {
|
|
13
11
|
const [text, setText] = useState('');
|
|
@@ -27,4 +25,4 @@ return (
|
|
|
27
25
|
<Button type="submit" variant="primary">Add</Button>
|
|
28
26
|
</form>
|
|
29
27
|
);
|
|
30
|
-
};
|
|
28
|
+
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{{includeFile template="client/src/todos/todoItem.css.hbs" output="client/src/todoItem.css"}}
|
|
2
|
-
import './todoItem.css';
|
|
3
|
-
import {useDelRowCallback, useRow, useSetPartialRowCallback, STORE_ID} from './Store';
|
|
4
|
-
|
|
2
|
+
{{addImport "import './todoItem.css';"}}
|
|
3
|
+
{{addImport "import {useDelRowCallback, useRow, useSetPartialRowCallback, type TodoRow, STORE_ID} from './Store';"}}
|
|
5
4
|
{{includeFile template="client/src/shared/Button.tsx.hbs" output="client/src/Button.{{ext}}"}}
|
|
6
|
-
import {Button} from './Button';
|
|
5
|
+
{{addImport "import {Button} from './Button';"}}
|
|
7
6
|
|
|
8
7
|
export const TodoItem = ({rowId}: {rowId: string}) => {
|
|
9
|
-
const todo = useRow('todos', rowId, STORE_ID);
|
|
8
|
+
const todo = useRow('todos', rowId, STORE_ID) as TodoRow;
|
|
10
9
|
const setPartialRow = useSetPartialRowCallback('todos', rowId, (e: React.ChangeEvent<HTMLInputElement>) => ({completed: e.target.checked}), [], STORE_ID);
|
|
11
10
|
const delRow = useDelRowCallback('todos', rowId, STORE_ID);
|
|
12
11
|
|
|
@@ -17,4 +16,4 @@ const setPartialRow = useSetPartialRowCallback('todos', rowId, (e: React.ChangeE
|
|
|
17
16
|
<Button onClick={delRow}>Delete</Button>
|
|
18
17
|
</div>
|
|
19
18
|
);
|
|
20
|
-
};
|
|
19
|
+
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{{includeFile template="client/src/todos/todoList.css.hbs" output="client/src/todoList.css"}}
|
|
2
|
-
import './todoList.css';
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
{{addImport "import './todoList.css';"}}
|
|
3
|
+
{{addImport "import {useSortedRowIds, STORE_ID} from './Store';"}}
|
|
5
4
|
{{includeFile template="client/src/todos/TodoItem.tsx.hbs" output="client/src/TodoItem.{{ext}}"}}
|
|
6
|
-
import {TodoItem} from './TodoItem';
|
|
5
|
+
{{addImport "import {TodoItem} from './TodoItem';"}}
|
|
7
6
|
|
|
8
7
|
export const TodoList = () => {
|
|
9
|
-
const todoIds =
|
|
8
|
+
const todoIds = useSortedRowIds('todos', undefined, false, 0, undefined, STORE_ID);
|
|
10
9
|
|
|
11
10
|
return (
|
|
12
11
|
<div id="todoList">
|
|
@@ -15,4 +14,4 @@ return (
|
|
|
15
14
|
))}
|
|
16
15
|
</div>
|
|
17
16
|
);
|
|
18
|
-
};
|
|
17
|
+
};
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
{{includeFile template="client/src/todos/store.ts.hbs" output="client/src/store.ts"}}
|
|
2
|
-
import {store} from './store';
|
|
3
|
-
|
|
2
|
+
{{addImport "import {store, storeReady} from './store';"}}
|
|
3
|
+
{{includeFile template="client/src/shared/loading.ts.hbs" output="client/src/loading.{{ext}}"}}
|
|
4
|
+
{{addImport "import {showLoading, hideLoading} from './loading';"}}
|
|
4
5
|
{{includeFile template="client/src/todos/todoInput.ts.hbs" output="client/src/todoInput.{{ext}}"}}
|
|
5
|
-
import {createTodoInput} from './todoInput';
|
|
6
|
-
|
|
6
|
+
{{addImport "import {createTodoInput} from './todoInput';"}}
|
|
7
7
|
{{includeFile template="client/src/todos/todoList.ts.hbs" output="client/src/todoList.{{ext}}"}}
|
|
8
|
-
import {createTodoList} from './todoList';
|
|
8
|
+
{{addImport "import {createTodoList} from './todoList';"}}
|
|
9
9
|
|
|
10
|
-
const app = () => {
|
|
10
|
+
export const app = async () => {
|
|
11
11
|
const appContainer = document.getElementById('app')!;
|
|
12
12
|
|
|
13
|
+
// Show loading spinner
|
|
14
|
+
const loadingDiv = showLoading(appContainer);
|
|
15
|
+
|
|
16
|
+
// Wait for store to be ready
|
|
17
|
+
await storeReady;
|
|
18
|
+
|
|
19
|
+
// Remove loading spinner
|
|
20
|
+
hideLoading(loadingDiv);
|
|
21
|
+
|
|
13
22
|
const todoInputContainer = createTodoInput(store);
|
|
14
23
|
appContainer.appendChild(todoInputContainer);
|
|
15
24
|
|
|
16
|
-
// Focus the input after it's in the DOM
|
|
17
25
|
const input = todoInputContainer.querySelector('input')!;
|
|
18
26
|
input.focus();
|
|
19
27
|
|
|
20
28
|
appContainer.appendChild(createTodoList(store));
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export {app};
|
|
29
|
+
};
|