Saving the new documentation for Svelte and Dexie made by Gemini. Saving changes made by Gemini to fix various things.
This commit is contained in:
@@ -20,6 +20,10 @@ Additional Aether modules
|
||||
|
||||
## Documentation
|
||||
* TODO.md
|
||||
* Svelte - Introducing runes - https://svelte.dev/blog/runes
|
||||
* Svelte - Breaking changes in runes mode - https://svelte.dev/docs/svelte/v5-migration-guide#Breaking-changes-in-runes-mode
|
||||
* Dexie.js - Getting Started - https://dexie.org/docs/Tutorial/Getting-started
|
||||
* Dexie.js - API Quick Reference - https://dexie.org/docs/API-Reference#quick-reference
|
||||
|
||||
## Ignored Directories
|
||||
|
||||
|
||||
110
SVELTE_DEXIE_GUIDE.md
Normal file
110
SVELTE_DEXIE_GUIDE.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Svelte and Dexie.js Integration Guide
|
||||
|
||||
This document provides a guide to integrating Svelte (with a focus on Runes) and Dexie.js for building reactive web applications. It covers key concepts and best practices for managing reactivity between Svelte components and the Dexie.js database.
|
||||
|
||||
## Svelte 5 Migration Guide
|
||||
|
||||
Svelte 5 introduces "runes" as a new way to manage reactivity. This is a major change from previous versions of Svelte, and it's important to understand the breaking changes before migrating.
|
||||
|
||||
### Key Breaking Changes
|
||||
|
||||
* **`let` is no longer reactive:** In Svelte 4, any `let` variable declared in the top-level scope of a component was automatically reactive. In Svelte 5, you must explicitly declare reactive state using the `$state` rune.
|
||||
* **`$:` is replaced by `$derived` and `$effect`:** The `$` label is no longer used for reactive statements. Instead, you should use the `$derived` rune for computed values and the `$effect` rune for side effects.
|
||||
* **`export let` is replaced by `$props`:** Component props are now declared using the `$props` rune, which provides a more flexible and explicit way to define component APIs.
|
||||
* **Event handling:** The `on:` directive is replaced by event attributes (e.g., `onclick`). Component events are now handled using callback props instead of `createEventDispatcher`.
|
||||
* **Slots are replaced by snippets:** The `<slot>` element is replaced by the `{#snippet ...}` block, which provides a more powerful and flexible way to pass content to components.
|
||||
|
||||
For a complete list of breaking changes, refer to the [Svelte 5 migration guide](https://svelte.dev/docs/svelte/v5-migration-guide).
|
||||
|
||||
## Dexie.js Quick Reference
|
||||
|
||||
Dexie.js is a lightweight, minimalistic wrapper for IndexedDB that makes it easier to work with client-side databases.
|
||||
|
||||
### Key Classes and Methods
|
||||
|
||||
* **`Dexie`:** The main class for creating and managing IndexedDB databases.
|
||||
* `new Dexie(databaseName)`: Creates a new database instance.
|
||||
* `version(versionNumber).stores({ ... })`: Defines the database schema.
|
||||
* **`Table`:** Represents an object store (table) in the database.
|
||||
* `add(item)`: Adds a new item to the table.
|
||||
* `put(item)`: Adds or updates an item in the table.
|
||||
* `update(key, changes)`: Updates an existing item.
|
||||
* `delete(key)`: Deletes an item by its primary key.
|
||||
* `get(key)`: Retrieves an item by its primary key.
|
||||
* `where(index)`: Starts a query using an index.
|
||||
* `toArray()`: Retrieves all items from the table as an array.
|
||||
* **`Collection`:** Represents a collection of items resulting from a query.
|
||||
* `toArray()`: Retrieves all items in the collection as an array.
|
||||
* `first()`: Retrieves the first item in the collection.
|
||||
* `last()`: Retrieves the last item in the collection.
|
||||
* `each(callback)`: Iterates over each item in the collection.
|
||||
* `modify(changes)`: Updates all items in the collection.
|
||||
* `delete()`: Deletes all items in the collection.
|
||||
|
||||
For a complete list of API methods, refer to the [Dexie.js API Reference](https://dexie.org/docs/API-Reference).
|
||||
|
||||
## Integrating Svelte Runes and Dexie.js
|
||||
|
||||
The combination of Svelte Runes and Dexie.js allows for the creation of highly reactive and efficient web applications.
|
||||
|
||||
### The `liveQuery` Function
|
||||
|
||||
Dexie.js provides a `liveQuery` function that returns an observable of the query result. This observable can be used to automatically update the UI whenever the data in the database changes.
|
||||
|
||||
### Using `liveQuery` with Svelte Runes
|
||||
|
||||
To use `liveQuery` with Svelte Runes, you can create a custom readable store that wraps the `liveQuery` observable. This store can then be used in your Svelte components to display and interact with the data.
|
||||
|
||||
**1. Create a `liveQuery` store:**
|
||||
|
||||
```typescript
|
||||
import { liveQuery } from 'dexie';
|
||||
import { readable } from 'svelte/store';
|
||||
import { db } from './db'; // Your Dexie database instance
|
||||
|
||||
export function createLiveQueryStore<T>(query: () => T | Promise<T>) {
|
||||
return readable<T | undefined>(undefined, (set) => {
|
||||
const subscription = liveQuery(query).subscribe({
|
||||
next: (result) => set(result),
|
||||
error: (error) => console.error(error),
|
||||
});
|
||||
return () => subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**2. Use the `createLiveQueryStore` in your component:**
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { createLiveQueryStore } from './stores';
|
||||
import { db } from './db';
|
||||
|
||||
const friends = createLiveQueryStore(() => db.friends.toArray());
|
||||
</script>
|
||||
|
||||
<ul>
|
||||
{#if $friends}
|
||||
{#each $friends as friend}
|
||||
<li>{friend.name}</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
```
|
||||
|
||||
The `createLiveQueryStore` function creates a readable store that automatically updates whenever the data in the `friends` table changes. The `$friends` variable in the component will always contain the latest data from the database.
|
||||
|
||||
## Current Data Flow in `ae_journals` Module
|
||||
|
||||
The `ae_journals` module currently uses a manual, API-first caching strategy. It does not use Dexie.js's `liveQuery` function for reactivity.
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. **Fetch from API:** The `load_ae_obj_id__journal` and `load_ae_obj_li__journal` functions are used to fetch data from the API.
|
||||
2. **Process Data:** The `process_ae_obj__journal_props` and `process_ae_obj__journal_entry_props` functions are used to process the data before it's saved to the database. This includes parsing Markdown, creating temporary sorting fields, and combining passcodes.
|
||||
3. **Save to IndexedDB:** The `db_save_ae_obj_li__ae_obj` function is used to save the processed data to the `journal` and `journal_entry` tables in the `ae_journals_db` IndexedDB database.
|
||||
4. **Manual Store Updates:** The Svelte stores in `src/lib/ae_journals/ae_journals_stores.ts` are manually updated with the fetched data.
|
||||
|
||||
### Future Improvements
|
||||
|
||||
The current implementation could be improved by refactoring it to use Dexie.js's `liveQuery` function. This would simplify the code, reduce boilerplate, and improve the reactivity of the application. By using `liveQuery`, the UI would automatically update whenever the data in the IndexedDB database changes, without the need for manual store updates.
|
||||
@@ -14,7 +14,7 @@ import { db_events } from "$lib/ae_events/db_events";
|
||||
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
|
||||
import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
|
||||
|
||||
import Element_qr_scanner from '$lib/elements/element_qr_scanner.svelte';
|
||||
import Element_qr_scanner from '$lib/element_qr_scanner_v2.svelte';
|
||||
import Element_ae_crud from '$lib/elements/element_ae_crud.svelte';
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
@@ -21,7 +21,7 @@ import { db_events } from "$lib/ae_events/db_events";
|
||||
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
|
||||
import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
|
||||
|
||||
import Element_qr_scanner from '$lib/elements/element_qr_scanner.svelte';
|
||||
import Element_qr_scanner from '$lib/element_qr_scanner_v2.svelte';
|
||||
import Element_ae_crud from '$lib/elements/element_ae_crud.svelte';
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
console.log(`ae_events_leads exhibit [slug] leads_add_scan.svelte`, $events_slct);
|
||||
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { liveQuery } from "dexie";
|
||||
import { db_events } from "$lib/ae_events/db_events";
|
||||
@@ -9,9 +10,8 @@ import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stor
|
||||
import { events_loc, events_sess, events_slct, events_trigger } from '$lib/stores/ae_events_stores';
|
||||
import { events_func } from '$lib/ae_events_functions';
|
||||
|
||||
import Element_qr_scanner from '$lib/elements/element_qr_scanner.svelte';
|
||||
import Element_qr_scanner_v2 from '$lib/elements/element_qr_scanner_v2.svelte';
|
||||
import Element_qr_scanner_dev from '$lib/elements/element_qr_scanner_dev.svelte';
|
||||
import Element_qr_scanner from '$lib/element_qr_scanner_v2.svelte';
|
||||
|
||||
|
||||
|
||||
// TEMPORARY: For testing and development???
|
||||
@@ -84,7 +84,7 @@ async function handle_submit_form_search(event) {
|
||||
|
||||
search_submit_results = await events_func.search__event_badge({
|
||||
api_cfg: $ae_api,
|
||||
event_id: $event_exhibit_obj.event_id_random,
|
||||
event_id: $event_exhibit_obj?.event_id_random,
|
||||
fulltext_search_qry_str: search_str,
|
||||
external_event_id: $events_loc.leads.default__external_registration_id
|
||||
});
|
||||
@@ -295,7 +295,7 @@ function handle_qr_camera(event: CustomEvent<any>) {
|
||||
</span>
|
||||
|
||||
{#if $events_loc.leads.show_content__scan_alert}
|
||||
<div class="border border-slate-500/10 p-2 preset-tonal-warning border border-warning-500 p-1">
|
||||
<div class="border-slate-500/10 p-2 preset-tonal-warning">
|
||||
<span class="fas fa-exclamation-triangle mx-1"></span>
|
||||
<span>
|
||||
<strong>Alert</strong>
|
||||
@@ -306,10 +306,9 @@ function handle_qr_camera(event: CustomEvent<any>) {
|
||||
>
|
||||
<option value={'one'}>v1</option>
|
||||
<option value={'two'}>v2</option>
|
||||
<option value={"dev"}>Development</option>
|
||||
</select>
|
||||
|
||||
<p>A bug was found late Monday that affects some mobile devices (cell phones and tablets). Incremental fixes have been added, but this issue has been difficult to address accoss all devices. We recommend most users stick with v1 (default). If you are not having any trouble and have no idea what this message is about, then this is what you want.</p>
|
||||
<p>A bug was found late Monday that affects some mobile devices (cell phones and tablets). Incremental fixes have been added, but this issue has been difficult to address across all devices. We recommend most users stick with v1 (default). If you are not having any trouble and have no idea what this message is about, then this is what you want.</p>
|
||||
<p>We recommend you try v2 if you are having trouble with the camera not starting or turning on. This version allows you to manually attempt to Allow Camera Access and to Start Scanning if it does not start on its own. This fix is not perfect. A permanent solution is actively being worked on in the development version.</p>
|
||||
<p>A fix is in progress to address all devices. -2024-04-10 2:25 AM (Prague time)</p>
|
||||
</span>
|
||||
@@ -740,23 +739,14 @@ function handle_qr_camera(event: CustomEvent<any>) {
|
||||
on:qr_camera={handle_qr_camera}
|
||||
/>
|
||||
{:else if $ae_loc.qr_scanner_version == 'two'}
|
||||
<Element_qr_scanner_v2
|
||||
<!-- v2 dev -->
|
||||
<!-- <Element_qr_scanner_v2
|
||||
start_qr_scanner={$events_sess.leads.qr_scan_start}
|
||||
show_qr_scan_result={true}
|
||||
show_qr_manual_badge_id_entry_option={true}
|
||||
on:qr_scan_result={handle_qr_scan_result}
|
||||
on:qr_camera={handle_qr_camera}
|
||||
/>
|
||||
<!-- v2 -->
|
||||
{:else if $ae_loc.qr_scanner_version == 'dev'}
|
||||
<Element_qr_scanner_dev
|
||||
start_qr_scanner={$events_sess.leads.qr_scan_start}
|
||||
show_qr_scan_result={true}
|
||||
show_qr_manual_badge_id_entry_option={true}
|
||||
on:qr_scan_result={handle_qr_scan_result}
|
||||
on:qr_camera={handle_qr_camera}
|
||||
/>
|
||||
vDEV
|
||||
/> -->
|
||||
|
||||
{:else}
|
||||
<p>Please close this and select a QR Scanner version</p>
|
||||
|
||||
@@ -380,16 +380,9 @@ $effect(() => {
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
// if ($journals_sess.show__modal_append__journal_entry_id) {
|
||||
// // $journals_sess.show__modal_append__journal_entry_id = null;
|
||||
// $journals_sess.show__modal_append__journal_entry_id = journals_journal_entry_obj?.id;
|
||||
// tmp_entry_obj = journals_journal_entry_obj;
|
||||
// } else {
|
||||
// $journals_sess.show__modal_append__journal_entry_id = journals_journal_entry_obj?.id;
|
||||
// tmp_entry_obj = journals_journal_entry_obj;
|
||||
// }
|
||||
$journals_sess.show__modal_append__journal_entry_id = journals_journal_entry_obj?.id;
|
||||
tmp_entry_obj = journals_journal_entry_obj;
|
||||
// Create a deep copy of the object for editing to prevent direct mutation of the liveQuery result.
|
||||
tmp_entry_obj = JSON.parse(JSON.stringify(journals_journal_entry_obj));
|
||||
}}
|
||||
class="btn btn-icon btn-sm preset-tonal-surface border border-surface-500 hover:preset-filled-secondary-500 transition"
|
||||
title="{$lq__journal_obj?.cfg_json?.entry_add_text == 'append' ? 'Append to Journal Entry' : 'Prepend to Journal Entry'}"
|
||||
|
||||
4
test-results/.last-run.json
Normal file
4
test-results/.last-run.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"status": "interrupted",
|
||||
"failedTests": []
|
||||
}
|
||||
18
tests/example.test.ts
Normal file
18
tests/example.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('has title', async ({ page }) => {
|
||||
await page.goto('http://localhost:4173/');
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/SvelteKit/);
|
||||
});
|
||||
|
||||
test('get started link', async ({ page }) => {
|
||||
await page.goto('http://localhost:4173/');
|
||||
|
||||
// Click the get started link.
|
||||
await page.getByRole('link', { name: 'Docs' }).click();
|
||||
|
||||
// Expects page to have a heading with the name of Installation.
|
||||
await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user