feat(collections): add book picker modal and fix icon picker
- Add book picker modal with Alpine.js state management for selecting books - Add toggleBookPickerBook, isBookPickerBookSelected, getBookPickerSelectedCount methods - Add clearBookPickerFilters function to reset filter form - Fix icon picker: add showAllIcons function to reset icon search - Fix setupHTMXModalInit to properly initialize Alpine tree after HTMX swap - Update collections template with book picker modal structure
This commit is contained in:
+80
-6
@@ -7,14 +7,17 @@ let collectionId: string | null = null;
|
||||
function initializeCollectionWebSocket(): void {
|
||||
const dataEl = document.getElementById("collection-data");
|
||||
if (!dataEl) return;
|
||||
|
||||
|
||||
collectionId = dataEl.dataset.id || null;
|
||||
|
||||
|
||||
if (!collectionId) return;
|
||||
|
||||
createWebSocket({
|
||||
onMessage: (message) => {
|
||||
if (message.type === "collection_updated" && message.data.collection_id === collectionId) {
|
||||
if (
|
||||
message.type === "collection_updated" &&
|
||||
message.data.collection_id === collectionId
|
||||
) {
|
||||
const actionText =
|
||||
message.data.action === "books_added"
|
||||
? `Added ${message.data.count || 0} book(s)`
|
||||
@@ -329,14 +332,37 @@ function initColorSelection(): void {
|
||||
|
||||
// Initialize icon grid when modal is loaded via HTMX
|
||||
function setupHTMXModalInit(): void {
|
||||
document.body.addEventListener("htmx:afterSwap", function (evt: Event) {
|
||||
const target = (evt as any).detail.target;
|
||||
document.body.addEventListener("htmx:afterSwap", function (evt: CustomEvent) {
|
||||
const target = evt.detail.target;
|
||||
if (target && target.id === "modal-container") {
|
||||
initIconSelection();
|
||||
populateIconGrid();
|
||||
Alpine.initTree(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showAllIcons(): void {
|
||||
// Populate grid if empty
|
||||
populateIconGrid();
|
||||
|
||||
// Clear search filter
|
||||
const searchInput = document.getElementById(
|
||||
"icon-search",
|
||||
) as HTMLInputElement;
|
||||
if (searchInput) {
|
||||
searchInput.value = "";
|
||||
}
|
||||
|
||||
// Show all icons
|
||||
const iconGrid = document.getElementById("icon-grid");
|
||||
if (!iconGrid) return;
|
||||
|
||||
const buttons = iconGrid.querySelectorAll(".icon-btn");
|
||||
buttons.forEach((btn) => {
|
||||
(btn as HTMLElement).style.display = "";
|
||||
});
|
||||
}
|
||||
|
||||
// Icon data with keywords (single source of truth)
|
||||
const iconData: Record<string, string[]> = {
|
||||
// Books & Reading
|
||||
@@ -463,10 +489,56 @@ export {
|
||||
selectColor,
|
||||
selectIcon,
|
||||
setupHTMXAuth,
|
||||
setupHTMXModalInit,
|
||||
showAllIcons,
|
||||
testRule,
|
||||
};
|
||||
|
||||
Alpine.data("collections", () => ({
|
||||
// Book Picker State
|
||||
bookPickerOpen: false,
|
||||
bookPickerSelected: [] as string[],
|
||||
// Book Picker Methods
|
||||
openBookPicker(): void {
|
||||
this.bookPickerOpen = true;
|
||||
this.bookPickerSelected = [];
|
||||
},
|
||||
closeBookPicker(): void {
|
||||
this.bookPickerOpen = false;
|
||||
this.bookPickerSelected = [];
|
||||
},
|
||||
toggleBookPickerBook(bookId: string): void {
|
||||
const index = this.bookPickerSelected.indexOf(bookId);
|
||||
if (index > -1) {
|
||||
this.bookPickerSelected.splice(index, 1);
|
||||
} else {
|
||||
this.bookPickerSelected.push(bookId);
|
||||
}
|
||||
},
|
||||
isBookPickerBookSelected(bookId: string): boolean {
|
||||
return this.bookPickerSelected.includes(bookId);
|
||||
},
|
||||
getBookPickerSelectedCount(): number {
|
||||
return this.bookPickerSelected.length;
|
||||
},
|
||||
clearBookPickerFilters(): void {
|
||||
const filterForm = document.getElementById(
|
||||
"book-picker-filters",
|
||||
) as HTMLFormElement;
|
||||
if (!filterForm) return;
|
||||
|
||||
const inputs = filterForm.querySelectorAll("input:not([type='checkbox'])");
|
||||
inputs.forEach((input) => {
|
||||
(input as HTMLInputElement).value = "";
|
||||
});
|
||||
|
||||
const grid = document.getElementById("book-picker-grid");
|
||||
if (grid) {
|
||||
htmx.trigger(grid, "refresh");
|
||||
}
|
||||
},
|
||||
|
||||
// Collection Methods
|
||||
closeCollectionModal,
|
||||
createRule,
|
||||
deleteRule,
|
||||
@@ -479,5 +551,7 @@ Alpine.data("collections", () => ({
|
||||
populateIconGrid,
|
||||
selectColor,
|
||||
selectIcon,
|
||||
setupHTMXModalInit,
|
||||
showAllIcons,
|
||||
testRule,
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user