Skip to content

Commit 89fda60

Browse files
author
Luis Pardo
committed
Add dixiejs version and differentiate through the route
1 parent 9dd77ac commit 89fda60

26 files changed

Lines changed: 6871 additions & 546 deletions

experimental/javascript-wc-indexeddb/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ It utilizes custom elements and html templates to build reusable components.
77

88
In contrast to other workloads, this application uses an updated set of css rules and an optimized dom structure to ensure the application follows best practices in regards to accessibility.
99

10+
## Storage Options
11+
12+
This application supports two different IndexedDB implementations that can be selected via URL hash:
13+
14+
- **Vanilla IndexedDB** (default): Uses the native IndexedDB API
15+
- Access via: `http://localhost:7005/#indexeddb` or `http://localhost:7005/`
16+
- **Dexie.js**: Uses the Dexie.js wrapper library for IndexedDB
17+
- Access via: `http://localhost:7005/#dexie`
18+
19+
Simply click on the storage API links in the header to switch between implementations. The database will be reset when switching between storage types.
20+
1021
## Built steps
1122

1223
A simple build script copies all necessary files to a `dist` folder.

experimental/javascript-wc-indexeddb/dist/components/todo-app/todo-app.component.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,9 @@ class TodoApp extends HTMLElement {
134134
}
135135

136136
routeChange(route) {
137-
const routeName = route.split("/")[1] || "all";
138-
this.list.updateRoute(routeName);
139-
this.bottombar.updateRoute(routeName);
140-
this.topbar.updateRoute(routeName);
137+
this.list.updateRoute(route);
138+
this.bottombar.updateRoute(route);
139+
this.topbar.updateRoute(route);
141140
}
142141

143142
connectedCallback() {

experimental/javascript-wc-indexeddb/dist/components/todo-bottombar/todo-bottombar.component.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import template from "./todo-bottombar.template.js";
2+
import { getStorageType } from "../../storage/storage-factory.js";
23

34
import globalStyles from "../../styles/global.constructable.js";
45
import bottombarStyles from "../../styles/bottombar.constructable.js";
@@ -57,13 +58,32 @@ class TodoBottombar extends HTMLElement {
5758
this.clearCompletedItems = this.clearCompletedItems.bind(this);
5859
this.MoveToNextPage = this.MoveToNextPage.bind(this);
5960
this.MoveToPreviousPage = this.MoveToPreviousPage.bind(this);
61+
62+
// Set up filter links with storage type
63+
this.setupFilterLinks();
64+
}
65+
66+
setupFilterLinks() {
67+
const storagePrefix = getStorageType();
68+
69+
this.filterLinks.forEach((link) => {
70+
const route = link.dataset.route;
71+
if (route === "all")
72+
link.href = `#/${storagePrefix}`;
73+
else
74+
link.href = `#/${storagePrefix}/${route}`;
75+
});
6076
}
6177

6278
updateDisplay() {
6379
this.todoStatus.textContent = `${this["active-items"]} ${this["active-items"] === "1" ? "item" : "items"} left!`;
6480
}
6581

6682
updateRoute(route) {
83+
// Update filter links to preserve current storage type
84+
this.setupFilterLinks();
85+
86+
// Update selected state
6787
this.filterLinks.forEach((link) => {
6888
if (link.dataset.route === route)
6989
link.classList.add("selected");

experimental/javascript-wc-indexeddb/dist/components/todo-bottombar/todo-bottombar.template.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ template.innerHTML = `
66
<div class="todo-status"><span class="todo-count">0</span> item left</div>
77
<ul class="filter-list">
88
<li class="filter-item">
9-
<a id="filter-link-all" class="filter-link selected" href="#/" data-route="all">All</a>
9+
<a id="filter-link-all" class="filter-link selected" href="#" data-route="all">All</a>
1010
</li>
1111
<li class="filter-item">
12-
<a id="filter-link-active" class="filter-link" href="#/active" data-route="active">Active</a>
12+
<a id="filter-link-active" class="filter-link" href="#" data-route="active">Active</a>
1313
</li>
1414
<li class="filter-item">
15-
<a id="filter-link-completed" class="filter-link" href="#/completed" data-route="completed">Completed</a>
15+
<a id="filter-link-completed" class="filter-link" href="#" data-route="completed">Completed</a>
1616
</li>
1717
</ul>
1818
<button id="clear-completed" class="clear-completed-button">Clear completed</button>

experimental/javascript-wc-indexeddb/dist/components/todo-list/todo-list.component.js

Lines changed: 3 additions & 247 deletions
Original file line numberDiff line numberDiff line change
@@ -1,258 +1,14 @@
11
import template from "./todo-list.template.js";
22
import TodoItem from "../todo-item/todo-item.component.js";
3+
import { createStorageManager } from "../../storage/storage-factory.js";
34

45
import globalStyles from "../../styles/global.constructable.js";
56
import listStyles from "../../styles/todo-list.constructable.js";
67

7-
class IndexedDBManager {
8-
constructor() {
9-
this.dbName = "todoDB";
10-
this.dbVersion = 1;
11-
this.storeName = "todos";
12-
this.db = null;
13-
this.pendingAdditions = 0;
14-
this.totalItemsToggled = 0;
15-
this.totalItemsDeleted = 0;
16-
this.initDB().then(() => {
17-
const newDiv = document.createElement("div");
18-
newDiv.classList.add("indexeddb-ready");
19-
newDiv.style.display = "none";
20-
document.body.append(newDiv);
21-
});
22-
}
23-
24-
initDB() {
25-
return new Promise((resolve, reject) => {
26-
// Delete the existing database first for clean state
27-
const deleteRequest = indexedDB.deleteDatabase(this.dbName);
28-
29-
deleteRequest.onerror = (event) => {
30-
// Continue despite error in deletion
31-
this.openDatabase(resolve, reject);
32-
};
33-
34-
deleteRequest.onsuccess = () => {
35-
this.openDatabase(resolve, reject);
36-
};
37-
38-
deleteRequest.onblocked = () => {
39-
// Try opening anyway
40-
this.openDatabase(resolve, reject);
41-
};
42-
});
43-
}
44-
45-
openDatabase(resolve, reject) {
46-
const request = indexedDB.open(this.dbName, this.dbVersion);
47-
48-
request.onerror = (event) => {
49-
reject(event.target.error);
50-
};
51-
52-
request.onsuccess = (event) => {
53-
this.db = event.target.result;
54-
resolve(this.db);
55-
};
56-
57-
request.onupgradeneeded = (event) => {
58-
const db = event.target.result;
59-
60-
// Create object store (since we're always creating a fresh DB now)
61-
const store = db.createObjectStore(this.storeName, { keyPath: "itemNumber" });
62-
store.createIndex("id", "id", { unique: true });
63-
store.createIndex("title", "title", { unique: false });
64-
store.createIndex("completed", "completed", { unique: false });
65-
store.createIndex("priority", "priority", { unique: false });
66-
};
67-
}
68-
69-
addTodo(todo) {
70-
return new Promise((resolve, reject) => {
71-
// Ensure the database connection is established
72-
if (!this.db) {
73-
this.initDB()
74-
.then(() => this.addTodo(todo))
75-
.then(resolve)
76-
.catch(reject);
77-
return;
78-
}
79-
80-
// Add todo item to IndexedDB
81-
const transaction = this.db.transaction(this.storeName, "readwrite");
82-
const store = transaction.objectStore(this.storeName);
83-
84-
const request = store.add(todo);
85-
this.pendingAdditions++;
86-
87-
request.onsuccess = () => {
88-
if (--this.pendingAdditions === 0)
89-
window.dispatchEvent(new CustomEvent("indexeddb-add-completed", {}));
90-
91-
resolve(todo);
92-
};
93-
94-
request.onerror = (event) => {
95-
reject(event.target.error);
96-
};
97-
});
98-
}
99-
100-
getTodos(upperItemNumber, count) {
101-
return new Promise((resolve, reject) => {
102-
// Ensure the database connection is established
103-
if (!this.db) {
104-
this.initDB()
105-
.then(() => this.getTodos(upperItemNumber, count))
106-
.then(resolve)
107-
.catch(reject);
108-
return;
109-
}
110-
111-
const transaction = this.db.transaction(this.storeName, "readonly");
112-
const store = transaction.objectStore(this.storeName);
113-
114-
// Use IDBKeyRange to get items with itemNumber less than upperItemNumber
115-
const range = IDBKeyRange.upperBound(upperItemNumber, true); // true = exclusive bound
116-
117-
// Open a cursor to iterate through records in descending order
118-
const request = store.openCursor(range, "prev");
119-
120-
const items = [];
121-
let itemsProcessed = 0;
122-
123-
request.onsuccess = (event) => {
124-
const cursor = event.target.result;
125-
126-
// Check if we have a valid cursor and haven't reached our count limit
127-
if (cursor && itemsProcessed < count) {
128-
items.push(cursor.value);
129-
itemsProcessed++;
130-
cursor.continue(); // Move to next item
131-
} else {
132-
// We're done - sort items by itemNumber in descending order
133-
// for proper display order (newest to oldest)
134-
items.sort((a, b) => a.itemNumber - b.itemNumber);
135-
136-
resolve(items);
137-
}
138-
};
139-
140-
request.onerror = (event) => {
141-
reject(event.target.error);
142-
};
143-
144-
// Also handle transaction errors
145-
transaction.onerror = (event) => {
146-
reject(event.target.error);
147-
};
148-
});
149-
}
150-
151-
toggleTodo(itemNumber, completed) {
152-
return new Promise((resolve, reject) => {
153-
// Ensure the database connection is established
154-
if (!this.db) {
155-
this.initDB()
156-
.then(() => this.toggleTodo(itemNumber, completed))
157-
.then(resolve)
158-
.catch(reject);
159-
return;
160-
}
161-
162-
// Access the todo item directly by its itemNumber (keyPath)
163-
const transaction = this.db.transaction(this.storeName, "readwrite");
164-
const store = transaction.objectStore(this.storeName);
165-
166-
// Get the todo item directly using its primary key (itemNumber)
167-
const getRequest = store.get(itemNumber);
168-
169-
getRequest.onsuccess = (event) => {
170-
const todoItem = getRequest.result;
171-
172-
if (!todoItem) {
173-
reject(new Error(`Todo item with itemNumber '${itemNumber}' not found`));
174-
return;
175-
}
176-
177-
// Update the completed status
178-
todoItem.completed = completed;
179-
180-
// Save the updated item back to the database
181-
const updateRequest = store.put(todoItem);
182-
183-
updateRequest.onsuccess = () => {
184-
if (window.numberOfItemsToAdd && ++this.totalItemsToggled === window.numberOfItemsToAdd)
185-
window.dispatchEvent(new CustomEvent("indexeddb-toggle-completed", {}));
186-
187-
resolve(todoItem);
188-
};
189-
190-
updateRequest.onerror = (event) => {
191-
reject(event.target.error);
192-
};
193-
};
194-
195-
getRequest.onerror = (event) => {
196-
reject(event.target.error);
197-
};
198-
199-
// Handle potential errors in finding the item
200-
transaction.onerror = (event) => {
201-
reject(event.target.error);
202-
};
203-
204-
// Handle transaction errors
205-
transaction.onerror = (event) => {
206-
reject(event.target.error);
207-
};
208-
});
209-
}
210-
211-
removeTodo(itemNumber) {
212-
return new Promise((resolve, reject) => {
213-
// Ensure the database connection is established
214-
if (!this.db) {
215-
this.initDB()
216-
.then(() => this.removeTodo(itemNumber))
217-
.then(resolve)
218-
.catch(reject);
219-
return;
220-
}
221-
222-
// Access the todo item directly by its itemNumber (keyPath)
223-
const transaction = this.db.transaction(this.storeName, "readwrite");
224-
const store = transaction.objectStore(this.storeName);
225-
226-
// Delete the todo item directly using its primary key (itemNumber)
227-
const deleteRequest = store.delete(itemNumber);
228-
229-
deleteRequest.onsuccess = () => {
230-
if (window.numberOfItemsToAdd && ++this.totalItemsDeleted === window.numberOfItemsToAdd)
231-
window.dispatchEvent(new CustomEvent("indexeddb-remove-completed", {}));
232-
233-
resolve(itemNumber);
234-
};
235-
236-
deleteRequest.onerror = (event) => {
237-
reject(event.target.error);
238-
};
239-
240-
// Handle transaction errors
241-
transaction.onerror = (event) => {
242-
reject(event.target.error);
243-
};
244-
});
245-
}
246-
}
247-
2488
const MAX_ON_SCREEN_ITEMS = 10;
2499

25010
const customListStyles = new CSSStyleSheet();
25111
customListStyles.replaceSync(`
252-
.todo-list > todo-item {
253-
display: block;
254-
}
255-
25612
.todo-list[route="completed"] > [itemcompleted="false"] {
25713
display: none;
25814
}
@@ -286,7 +42,7 @@ class TodoList extends HTMLElement {
28642
this.shadow.adoptedStyleSheets = [globalStyles, listStyles, customListStyles];
28743
this.shadow.append(node);
28844
this.classList.add("show-priority");
289-
this.storageManager = new IndexedDBManager();
45+
this.storageManager = createStorageManager();
29046

29147
if (window.extraTodoListCssToAdopt) {
29248
let extraAdoptedStyleSheet = new CSSStyleSheet();
@@ -354,7 +110,7 @@ class TodoList extends HTMLElement {
354110
this.listNode.setAttribute("route", "completed");
355111
break;
356112
case "active":
357-
this.listNode.setAttribute("rout", "active");
113+
this.listNode.setAttribute("route", "active");
358114
break;
359115
default:
360116
this.listNode.setAttribute("route", "all");

experimental/javascript-wc-indexeddb/dist/components/todo-topbar/todo-topbar.component.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ import topbarStyles from "../../styles/topbar.constructable.js";
77

88
const customListStyles = new CSSStyleSheet();
99
customListStyles.replaceSync(`
10-
.toggle-all-container {
11-
display: block;
12-
}
1310
:host([total-items="0"]) .toggle-all-container {
1411
display: none;
1512
}

0 commit comments

Comments
 (0)