fix(database): rewrite table view in vanilla JS (Alpine 3.14.9 compat)
FlowDeck CI / test (push) Failing after 5s
FlowDeck CI / docker (push) Has been skipped

Alpine 3.14.9 evaluates x-data expressions before Alpine.data() registration,
causing 'pages is not defined', 'emptyRows is not defined', etc.
Solution: pure vanilla JS DOM rendering (same pattern as Library fix).
- _database_table.html: static DOM with IDs only, no Alpine directives
- _database_table_scripts.html: vanilla JS state, render, cell editing, modals
- Removes all x-data, x-for, x-show, x-model from database table
This commit is contained in:
2026-07-22 14:40:22 -04:00
parent 24a760c5eb
commit 50273fcb1a
2 changed files with 340 additions and 334 deletions
+24 -136
View File
@@ -1,145 +1,53 @@
<!-- ═══════════ Database Table View (Notion-style) ═══════════ -->
<div x-data="databaseTable()" x-init="init($el)" class="database-table-container">
<!-- ═══════════ Database Table View (Notion-style) — Vanilla JS ═══════════ -->
<div id="db-table-container" class="database-table-container">
<!-- View Selector Bar -->
<div class="db-view-bar">
<div id="db-view-bar" class="db-view-bar">
<div class="db-view-tabs">
<button class="db-view-tab active"
@click="switchView('table')">
{{ fd_icon("table",14) }} Table
<button id="db-view-tab-table" class="db-view-tab active">
{{ fd_icon("grid",14) }} Table
</button>
<button class="db-view-add" @click="addViewMenuOpen=!addViewMenuOpen"
title="Add a view">
<button id="db-view-add-btn" class="db-view-add" title="Add a view">
{{ fd_icon("plus",14) }}
</button>
<!-- View type dropdown -->
<div x-show="addViewMenuOpen" @click.outside="addViewMenuOpen=false"
class="db-dropdown" x-transition>
<div id="db-add-view-dropdown" class="db-dropdown" style="display:none">
<div class="db-dropdown-header">Add a new view</div>
<div class="db-dropdown-item" @click="addViewMenuOpen=false">{{ fd_icon("table",14) }} Table</div>
<div class="db-dropdown-item" @click="addViewMenuOpen=false">{{ fd_icon("columns",14) }} Board</div>
<div class="db-dropdown-item" @click="addViewMenuOpen=false">{{ fd_icon("list",14) }} List</div>
<div class="db-dropdown-item" @click="addViewMenuOpen=false">{{ fd_icon("calendar",14) }} Calendar</div>
<div class="db-dropdown-item">{{ fd_icon("grid",14) }} Table</div>
<div class="db-dropdown-item">{{ fd_icon("grid",14) }} Board</div>
<div class="db-dropdown-item">{{ fd_icon("list",14) }} List</div>
<div class="db-dropdown-item">{{ fd_icon("calendar",14) }} Calendar</div>
</div>
</div>
<div class="db-view-actions">
<button class="db-btn-icon" title="Filter">{{ fd_icon("filter",14) }}</button>
<button class="db-btn-new" @click="addNewPage()">
<button id="db-filter-btn" class="db-btn-icon" title="Filter">{{ fd_icon("search",14) }}</button>
<button id="db-new-btn" class="db-btn-new">
<span class="db-new-icon">{{ fd_icon("plus",12) }}</span> New
</button>
</div>
</div>
<!-- Database Table -->
<div class="db-table-wrapper">
<table class="db-table">
<thead>
<tr class="db-header-row">
<!-- Drag handle column -->
<th class="db-th db-th-handle" style="width:32px"></th>
<!-- Property columns -->
<template x-for="prop in properties" :key="prop.id">
<th class="db-th db-th-property">
<div class="db-th-content">
<span class="db-prop-type-icon" x-text="propTypeIcon(prop.prop_type)"></span>
<span class="db-prop-name" x-text="prop.name"></span>
</div>
</th>
</template>
<!-- Add Property column -->
<th class="db-th db-th-add-prop">
<button class="db-add-prop-btn" @click="addProperty()"
title="Add a property">
{{ fd_icon("plus",14) }} Add property
</button>
</th>
</tr>
</thead>
<tbody>
<!-- New Page row (always first) -->
<tr class="db-row db-row-new">
<td class="db-td db-td-handle">
<span class="db-plus-icon">{{ fd_icon("plus",14) }}</span>
</td>
<td class="db-td db-td-name" colspan="99">
<button class="db-new-page-btn" @click="addNewPage()">
New page
</button>
</td>
</tr>
<!-- Data rows -->
<template x-for="(cpage, idx) in pages" :key="cpage.id">
<tr class="db-row"
@mouseenter="cpage._hover=true"
@mouseleave="cpage._hover=false"
:class="{ 'db-row-selected': cpage._selected }">
<!-- Drag handle / hover actions -->
<td class="db-td db-td-handle">
<span class="db-drag-handle" x-show="cpage._hover"
@mousedown.prevent="startDrag(idx, $event)">
{{ fd_icon("grip-vertical",14) }}
</span>
</td>
<!-- Property cells -->
<template x-for="prop in properties" :key="prop.id">
<td class="db-td db-td-cell">
<div class="db-cell-content"
:class="{'db-cell-editing': editingCell && editingCell.pageId === cpage.id && editingCell.propId === prop.id}"
@click="startEditCell(cpage.id, prop.id, $event)"
@dblclick="openPage(cpage.id)">
<!-- Render property value -->
<span x-show="!(editingCell && editingCell.pageId === cpage.id && editingCell.propId === prop.id)"
x-text="cellValue(cpage, prop) || '—'"
class="db-cell-value"></span>
<!-- Edit mode -->
<input x-show="editingCell && editingCell.pageId === cpage.id && editingCell.propId === prop.id"
x-ref="cellInput"
:value="cellValue(cpage, prop)"
@keydown.enter="commitEditCell(cpage, prop)"
@keydown.escape="cancelEditCell()"
@blur="commitEditCell(cpage, prop)"
class="db-cell-input" />
</div>
</td>
</template>
<!-- Open button on hover -->
<td class="db-td db-td-open" x-show="cpage._hover">
<button class="db-open-btn" @click="openPage(cpage.id)"
title="Open">
{{ fd_icon("external-link",14) }}
</button>
</td>
</tr>
</template>
<!-- Empty rows (fill to 5 total if less than 4 data rows) -->
<template x-for="i in emptyRows()" :key="'e'+i">
<tr class="db-row db-row-empty">
<td class="db-td db-td-handle"></td>
<template x-for="prop in properties" :key="prop.id">
<td class="db-td db-td-cell"><div class="db-cell-content"></div></td>
</template>
<td class="db-td db-td-open"></td>
</tr>
</template>
</tbody>
<div id="db-table-wrapper" class="db-table-wrapper">
<table id="db-table" class="db-table">
<thead id="db-thead"></thead>
<tbody id="db-tbody"></tbody>
</table>
</div>
<!-- Add Property Modal -->
<div x-show="propModalOpen" class="db-modal-overlay" @click.self="propModalOpen=false">
<div class="db-modal" x-transition>
<div id="db-prop-modal" class="db-modal-overlay" style="display:none">
<div class="db-modal">
<div class="db-modal-header">
<h3>Add a property</h3>
<button @click="propModalOpen=false">{{ fd_icon("x",14) }}</button>
<button id="db-prop-modal-close">{{ fd_icon("x",14) }}</button>
</div>
<div class="db-modal-body">
<div class="db-modal-field">
<label>Name</label>
<input x-model="newPropName" type="text" placeholder="Property name"
@keydown.enter="createProperty()" />
<input id="db-prop-name-input" type="text" placeholder="Property name" />
</div>
<div class="db-modal-field">
<label>Type</label>
<select x-model="newPropType">
<select id="db-prop-type-select">
<option value="text">Text</option>
<option value="number">Number</option>
<option value="select">Select</option>
@@ -152,28 +60,8 @@
</select>
</div>
<div class="db-modal-actions">
<button class="db-btn-cancel" @click="propModalOpen=false">Cancel</button>
<button class="db-btn-create" @click="createProperty()">Create</button>
</div>
</div>
</div>
</div>
<!-- Page title rename (inline) -->
<div x-show="renameOpen" class="db-modal-overlay" @click.self="renameOpen=false" style="z-index:600">
<div class="db-modal" x-transition style="max-width:400px">
<div class="db-modal-header">
<h3>Rename page</h3>
<button @click="renameOpen=false">{{ fd_icon("x",14) }}</button>
</div>
<div class="db-modal-body">
<div class="db-modal-field">
<input x-model="renameTitle" type="text" placeholder="Page title"
@keydown.enter="commitRename()" x-ref="renameInput" />
</div>
<div class="db-modal-actions">
<button class="db-btn-cancel" @click="renameOpen=false">Cancel</button>
<button class="db-btn-create" @click="commitRename()">Save</button>
<button id="db-prop-cancel" class="db-btn-cancel">Cancel</button>
<button id="db-prop-create" class="db-btn-create">Create</button>
</div>
</div>
</div>