Add web example served under docs website

This commit is contained in:
Simon Binder 2023-06-16 16:41:13 +02:00
parent 1e74aae972
commit b976e6f7ef
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
10 changed files with 282 additions and 0 deletions

74
docs/lib/sample/app.zap Normal file
View File

@ -0,0 +1,74 @@
<script>
import 'database.dart';
import 'entry.zap';
import 'toolbar.zap';
var filter = TodoListFilter.all;
var uncompleted = 0;
final connFuture = connect();
final dbFuture = connFuture.then((r) => Database(r.resolvedExecutor));
</script>
<style>
.ok {
color: green;
}
.bad {
color: red;
}
small {
display: block;
}
</style>
<hgroup>
<h1>Todo list</h1>
<h2>This offline todo-list app is implemented with drift on the web.</h2>
</hgroup>
<article>
{#await database from dbFuture}
{#if database.hasData}
<header>
<toolbar database={database.data} />
</header>
{#await each snapshot from database.data.items}
{#if snapshot.hasData}
{#for entry, i in snapshot.data}
<entry entry={entry} database={database.data} />
{#if i != snapshot.data.length - 1}
<hr>
{/if}
{/for}
{/if}
{/await}
{:else if database.hasError}
Sorry, we could not open the database on this browser!
{:else}
<progress></progress>
{/if}
{/await}
<footer>
{#await connection from connFuture}
{#if connection.hasData}
Using implementation <span class={ connection.data.chosenImplementation.fullySupported ? 'ok' : 'bad' }>{ connection.data.chosenImplementation.name }</span>.
{#if connection.data.chosenImplementation.fullySupported}
Updates are synchronized across tabs thanks to drift.
<small>Want to try it out? Go ahead and open this website in <a href="#" target="_blank">a new tab</a>.</small>
{:else}
This implementation has known caveats and shouldn't be selected on recent browsers.
More information is in the console and in <a href="https://drift.simonbinder.eu/web">the documentation</a>.
<small>Please consider <a href="https://github.com/simolus3/drift/issues/new/choose">filing an issue</a>.</small>
{/if}
{/if}
{/await}
</footer>
</article>
<footer>
<small><a href="https://github.com/simolus3/drift/tree/develop/docs/lib/sample">Source code</a></small>
</footer>

View File

@ -0,0 +1,105 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:drift/wasm.dart';
import 'package:rxdart/rxdart.dart';
part 'database.g.dart';
enum TodoListFilter { all, active, completed }
class TodoItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get description => text()();
BoolColumn get completed => boolean().withDefault(const Constant(false))();
}
@DriftDatabase(tables: [TodoItems])
class Database extends _$Database {
final BehaviorSubject<TodoListFilter> _filterChanges =
BehaviorSubject.seeded(TodoListFilter.all);
Database(super.e);
@override
int get schemaVersion => 1;
@override
MigrationStrategy get migration {
return MigrationStrategy(
onCreate: (m) async {
await m.createAll();
// Create some entries by default
await batch((b) {
b.insertAll(todoItems, [
TodoItemsCompanion.insert(description: 'Migrate to drift'),
TodoItemsCompanion.insert(description: 'Support all platforms'),
TodoItemsCompanion.insert(
description: 'Solve local persistence issues'),
]);
});
},
);
}
TodoListFilter get currentFilter => _filterChanges.value;
set currentFilter(TodoListFilter value) {
_filterChanges.add(value);
}
Stream<List<TodoItem>> get items => _filterChanges.stream.switchMap(_items);
Stream<int> get uncompletedItems {
final all = countAll();
final query = selectOnly(todoItems)
..addColumns([all])
..where(todoItems.completed.not());
return query.map((row) => row.read(all)!).watchSingle();
}
Stream<List<TodoItem>> _items(TodoListFilter filter) {
final query = todoItems.select();
switch (filter) {
case TodoListFilter.completed:
query.where((row) => row.completed);
case TodoListFilter.active:
query.where((row) => row.completed.not());
case TodoListFilter.all:
break;
}
return query.watch();
}
Future<void> toggleCompleted(TodoItem item) async {
final statement = update(todoItems)..whereSamePrimaryKey(item);
await statement.write(TodoItemsCompanion.custom(
completed: todoItems.completed.not(),
));
}
}
Future<WasmDatabaseResult> connect() async {
final result = await WasmDatabase.open(
databaseName: 'todo_example',
sqlite3Uri: Uri.parse('/sqlite3.wasm'),
driftWorkerUri: Uri.parse('/drift_worker.dart.js'),
);
if (!result.chosenImplementation.fullySupported) {
print('Using ${result.chosenImplementation} due to unsupported browser '
'features: ${result.missingFeatures}');
}
return result;
}
extension CompatibilityUI on WasmStorageImplementation {
bool get fullySupported =>
this != WasmStorageImplementation.inMemory &&
this != WasmStorageImplementation.unsafeIndexedDb;
}

18
docs/lib/sample/entry.zap Normal file
View File

@ -0,0 +1,18 @@
<script>
import 'database.dart';
@prop
Database database;
@prop
TodoItem entry;
void toggle() {
database.toggleCompleted(entry);
}
</script>
<label for="entry-{entry.id}">
<input type="checkbox" id="entry-{entry.id}" checked={entry.completed} on:change={toggle}>
{entry.description}
</label>

View File

@ -0,0 +1,41 @@
<script>
import 'database.dart';
@prop
Database database;
TextInputElement? text;
var currentFilter = database.currentFilter;
void select(TodoListFilter filter) {
currentFilter = filter;
database.currentFilter = filter;
}
</script>
<style>
div {
float: right;
}
a {
margin: 0px 2px;
}
label {
margin-top: 20px;
}
</style>
{#await each entry from database.uncompletedItems}
{#if entry.hasData}
<strong>{entry.data} {entry.data == 1 ? 'item' : 'items'} left</strong>
{/if}
{/await}
<div>
<a class={currentFilter == TodoListFilter.all ? '' : 'secondary'} on:click={() => select(TodoListFilter.all)}>All</a>
<a class={currentFilter == TodoListFilter.active ? '' : 'secondary'} on:click={() => select(TodoListFilter.active)}>Active</a>
<a class={currentFilter == TodoListFilter.completed ? '' : 'secondary'} on:click={() => select(TodoListFilter.completed)}>Completed</a>
</div>

View File

@ -25,6 +25,10 @@ dependencies:
rxdart: ^0.27.3
yaml: ^3.1.1
drift_dev: any
zap: ^0.2.0
picocss:
hosted: https://simonbinder.eu
version: ^1.5.10
dev_dependencies:
lints: ^2.0.0
@ -42,6 +46,7 @@ dev_dependencies:
source_span: ^1.9.1
test: ^1.18.0
sqlparser:
zap_dev: ^0.2.2
dependency_overrides:

View File

@ -1,8 +1,13 @@
import 'dart:html';
import 'package:drift/wasm.dart';
import 'package:drift_docs/site.dart' as i0;
import 'package:docsy/main.dart' as i1;
void main() async {
i0.built_site_main();
i1.built_site_main();
final btn = querySelector('#drift-compat-btn')!;
final results = querySelector('#drift-compat-results')!;

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Drift web example</title>
<link rel="stylesheet" href="style.css">
<script defer src="main.dart.js"></script>
</head>
<body>
<main class="container">
</main>
</body>
</html>

View File

@ -0,0 +1,9 @@
import 'dart:html';
import 'package:drift_docs/sample/app.zap.dart';
void main() {
final main = document.querySelector('main.container')!;
App().create(main);
}

View File

@ -0,0 +1,2 @@
@use "package:picocss/pico";
@use "package:drift_docs/sample/app.zap";

View File

@ -21,6 +21,12 @@
status = 301
force = true
[[headers]]
for = "/*"
[headers.values]
Cross-Origin-Opener-Policy = "same-origin"
Cross-Origin-Embedder-Policy = "require-corp"
[context.production]
environment = { BUILD_RELEASE="release" }