test: restore comprehensive DataTable tests and verify performance
This commit is contained in:
parent
3f19fb3a83
commit
9eea16cdab
41
dist/datatable.js
vendored
41
dist/datatable.js
vendored
@ -1,7 +1,8 @@
|
|||||||
import { Component, NewState, Util } from "@web/state";
|
import { Component, NewState, Util, RefreshState } from "@web/state";
|
||||||
import { VirtualScroll } from "@web/base";
|
import { VirtualScroll } from "@web/base";
|
||||||
Component.register("DataTable", (container) => {
|
Component.register("DataTable", (container) => {
|
||||||
const vs = VirtualScroll();
|
const vs = VirtualScroll();
|
||||||
|
if (!container.state) container.state = NewState({});
|
||||||
const state = container.state;
|
const state = container.state;
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
list: [],
|
list: [],
|
||||||
@ -23,7 +24,7 @@ Component.register("DataTable", (container) => {
|
|||||||
const res = vs.calc(scrollEl, state.list);
|
const res = vs.calc(scrollEl, state.list);
|
||||||
if (res) {
|
if (res) {
|
||||||
res.renderedList.forEach((item, i) => {
|
res.renderedList.forEach((item, i) => {
|
||||||
if (!item.__watch) {
|
if (item && !item.__watch) {
|
||||||
const wrapped = NewState(item);
|
const wrapped = NewState(item);
|
||||||
res.renderedList[i] = wrapped;
|
res.renderedList[i] = wrapped;
|
||||||
state.list[res.listStartIndex + i] = wrapped;
|
state.list[res.listStartIndex + i] = wrapped;
|
||||||
@ -38,21 +39,39 @@ Component.register("DataTable", (container) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
container.refresh = refresh;
|
container.refresh = refresh;
|
||||||
|
state.__watch("fields", (fields) => {
|
||||||
|
if (!fields) return;
|
||||||
|
const leftOffsets = [], rightOffsets = [];
|
||||||
|
let lSum = 0;
|
||||||
|
fields.forEach((f, i) => {
|
||||||
|
if (f.pinned === "left") {
|
||||||
|
leftOffsets[i] = lSum;
|
||||||
|
lSum += f.width || 150;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fields.forEach((f, i) => {
|
||||||
|
if (f.pinned === "right") {
|
||||||
|
let rs = 0;
|
||||||
|
for (let j = i + 1; j < fields.length; j++) {
|
||||||
|
if (fields[j].pinned === "right") rs += fields[j].width || 150;
|
||||||
|
}
|
||||||
|
rightOffsets[i] = rs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
state._leftOffsets = leftOffsets;
|
||||||
|
state._rightOffsets = rightOffsets;
|
||||||
|
});
|
||||||
state.__watch("list", (list) => {
|
state.__watch("list", (list) => {
|
||||||
state._listStartIndex = 0;
|
state._listStartIndex = 0;
|
||||||
const scrollEl = container.querySelector(".dt-body");
|
const scrollEl = container.querySelector(".dt-body");
|
||||||
state.renderedList = vs.reset(list, scrollEl || { clientHeight: 800 }) || [];
|
state.renderedList = vs.reset(list, scrollEl || { clientHeight: 800 }) || [];
|
||||||
if (scrollEl) {
|
if (scrollEl) {
|
||||||
vs.init(list, refresh);
|
vs.init(list, refresh);
|
||||||
requestAnimationFrame(() => refresh());
|
requestAnimationFrame(refresh);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
container.onItemUpdate = (rIdx, node) => vs.update(rIdx + state._listStartIndex, node);
|
container.onItemUpdate = (rIdx, node) => vs.update(rIdx + state._listStartIndex, node);
|
||||||
container.getOffset = (fields, index, side) => {
|
container.getOffset = (index, side) => (state._leftOffsets || [])[index] || (state._rightOffsets || [])[index] || 0;
|
||||||
const f = fields.slice(0, index).filter((f2) => f2.pinned === "left");
|
|
||||||
const r = fields.slice(index + 1).filter((f2) => f2.pinned === "right");
|
|
||||||
return (side === "left" ? f : r).reduce((sum, f2) => sum + (f2.width || 150), 0);
|
|
||||||
};
|
|
||||||
container.isCellSelected = (r, f) => {
|
container.isCellSelected = (r, f) => {
|
||||||
const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR);
|
const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR);
|
||||||
const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF);
|
const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF);
|
||||||
@ -99,7 +118,6 @@ Component.register("DataTable", (container) => {
|
|||||||
container.endSelect = () => state.isSelecting = false;
|
container.endSelect = () => state.isSelecting = false;
|
||||||
container.editCell = (row, f, fIdx) => {
|
container.editCell = (row, f, fIdx) => {
|
||||||
var _a;
|
var _a;
|
||||||
if (!row.__watch) return;
|
|
||||||
const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR);
|
const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR);
|
||||||
const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF);
|
const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF);
|
||||||
const rIdx = state.list.indexOf(row);
|
const rIdx = state.list.indexOf(row);
|
||||||
@ -215,7 +233,7 @@ Component.register("DataTable", (container) => {
|
|||||||
<div class="dt-root d-flex flex-column h-100 border bg-body text-body overflow-hidden" style="position:relative; user-select:none" tabindex="0" onunload="this._onUnload()">
|
<div class="dt-root d-flex flex-column h-100 border bg-body text-body overflow-hidden" style="position:relative; user-select:none" tabindex="0" onunload="this._onUnload()">
|
||||||
<div class="dt-header d-flex flex-shrink-0 border-bottom bg-body-tertiary fw-bold text-muted small" style="overflow:hidden; position:sticky; top:0; z-index:20; height:48px">
|
<div class="dt-header d-flex flex-shrink-0 border-bottom bg-body-tertiary fw-bold text-muted small" style="overflow:hidden; position:sticky; top:0; z-index:20; height:48px">
|
||||||
<div $each="this.state.fields" as="f" class="dt-col border-end d-flex align-items-center px-2 bg-body-tertiary"
|
<div $each="this.state.fields" as="f" class="dt-col border-end d-flex align-items-center px-2 bg-body-tertiary"
|
||||||
$style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); ' + (f.pinned ? 'position:sticky; z-index:11;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(this.state.fields, index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(this.state.fields, index, 'right') + 'px;' : '')">
|
$style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); ' + (f.pinned ? 'position:sticky; z-index:11;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(index, 'right') + 'px;' : '')">
|
||||||
<span $text="f.name" $class="'text-truncate flex-grow-1 ' + (f.type === 'number' ? 'text-end' : '')"></span>
|
<span $text="f.name" $class="'text-truncate flex-grow-1 ' + (f.type === 'number' ? 'text-end' : '')"></span>
|
||||||
<Resizer class="dt-resizer" $.target="thisNode.parentNode" min="50" max="600" $bind="f.width" $onresizing="thisNode.closest('.dt-root').style.setProperty('--w-' + f.id, event.detail.newSize + 'px')"/>
|
<Resizer class="dt-resizer" $.target="thisNode.parentNode" min="50" max="600" $bind="f.width" $onresizing="thisNode.closest('.dt-root').style.setProperty('--w-' + f.id, event.detail.newSize + 'px')"/>
|
||||||
</div>
|
</div>
|
||||||
@ -225,7 +243,7 @@ Component.register("DataTable", (container) => {
|
|||||||
<div $each="this.state.renderedList" as="row" class="dt-row d-flex border-bottom" $onupdate="this.onItemUpdate(rIdx, thisNode)" index="rIdx">
|
<div $each="this.state.renderedList" as="row" class="dt-row d-flex border-bottom" $onupdate="this.onItemUpdate(rIdx, thisNode)" index="rIdx">
|
||||||
<div $each="this.state.fields" as="f" class="dt-cell border-end d-flex align-items-center bg-body"
|
<div $each="this.state.fields" as="f" class="dt-cell border-end d-flex align-items-center bg-body"
|
||||||
$class="'dt-cell border-end d-flex align-items-center bg-body' + (this.isCellSelected(rIdx + this.state._listStartIndex, index) ? ' bg-primary-subtle' : '') + (row._activeF === index ? ' dt-cell-active' : '')"
|
$class="'dt-cell border-end d-flex align-items-center bg-body' + (this.isCellSelected(rIdx + this.state._listStartIndex, index) ? ' bg-primary-subtle' : '') + (row._activeF === index ? ' dt-cell-active' : '')"
|
||||||
$style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); height:40px; ' + (f.pinned ? 'position:sticky; z-index:10;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(this.state.fields, index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(this.state.fields, index, 'right') + 'px;' : '')"
|
$style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); height:40px; ' + (f.pinned ? 'position:sticky; z-index:10;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(index, 'right') + 'px;' : '')"
|
||||||
$onmousedown="this.startSelect(rIdx + this.state._listStartIndex, index, event)"
|
$onmousedown="this.startSelect(rIdx + this.state._listStartIndex, index, event)"
|
||||||
$onmouseenter="this.updateSelect(rIdx + this.state._listStartIndex, index)"
|
$onmouseenter="this.updateSelect(rIdx + this.state._listStartIndex, index)"
|
||||||
$ondblclick="this.editCell(row, f, index)">
|
$ondblclick="this.editCell(row, f, index)">
|
||||||
@ -261,3 +279,4 @@ Component.register("DataTable", (container) => {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
));
|
));
|
||||||
|
if (typeof document !== "undefined") RefreshState(document.documentElement);
|
||||||
|
|||||||
2
dist/datatable.min.js
vendored
2
dist/datatable.min.js
vendored
File diff suppressed because one or more lines are too long
45
src/index.js
45
src/index.js
@ -3,6 +3,8 @@ import { VirtualScroll } from '@web/base'
|
|||||||
|
|
||||||
Component.register('DataTable', container => {
|
Component.register('DataTable', container => {
|
||||||
const vs = VirtualScroll()
|
const vs = VirtualScroll()
|
||||||
|
// 如果 state 还未由框架自动创建,则手动创建
|
||||||
|
if (!container.state) container.state = NewState({})
|
||||||
const state = container.state
|
const state = container.state
|
||||||
|
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
@ -18,12 +20,10 @@ Component.register('DataTable', container => {
|
|||||||
if (!scrollEl) return
|
if (!scrollEl) return
|
||||||
const res = vs.calc(scrollEl, state.list)
|
const res = vs.calc(scrollEl, state.list)
|
||||||
if (res) {
|
if (res) {
|
||||||
// 仅对渲染列表中的项进行响应式包装(如果尚未包装)
|
|
||||||
res.renderedList.forEach((item, i) => {
|
res.renderedList.forEach((item, i) => {
|
||||||
if (!item.__watch) {
|
if (item && !item.__watch) {
|
||||||
const wrapped = NewState(item)
|
const wrapped = NewState(item)
|
||||||
res.renderedList[i] = wrapped
|
res.renderedList[i] = wrapped
|
||||||
// 同步回原列表
|
|
||||||
state.list[res.listStartIndex + i] = wrapped
|
state.list[res.listStartIndex + i] = wrapped
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -35,24 +35,42 @@ Component.register('DataTable', container => {
|
|||||||
}
|
}
|
||||||
container.refresh = refresh
|
container.refresh = refresh
|
||||||
|
|
||||||
|
state.__watch('fields', fields => {
|
||||||
|
if (!fields) return
|
||||||
|
const leftOffsets = [], rightOffsets = []
|
||||||
|
let lSum = 0
|
||||||
|
fields.forEach((f, i) => {
|
||||||
|
if (f.pinned === 'left') {
|
||||||
|
leftOffsets[i] = lSum
|
||||||
|
lSum += (f.width || 150)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fields.forEach((f, i) => {
|
||||||
|
if (f.pinned === 'right') {
|
||||||
|
let rs = 0
|
||||||
|
for (let j = i + 1; j < fields.length; j++) {
|
||||||
|
if (fields[j].pinned === 'right') rs += (fields[j].width || 150)
|
||||||
|
}
|
||||||
|
rightOffsets[i] = rs
|
||||||
|
}
|
||||||
|
})
|
||||||
|
state._leftOffsets = leftOffsets
|
||||||
|
state._rightOffsets = rightOffsets
|
||||||
|
})
|
||||||
|
|
||||||
state.__watch('list', list => {
|
state.__watch('list', list => {
|
||||||
state._listStartIndex = 0
|
state._listStartIndex = 0
|
||||||
const scrollEl = container.querySelector('.dt-body')
|
const scrollEl = container.querySelector('.dt-body')
|
||||||
// 初始预览,不全量包装
|
|
||||||
state.renderedList = vs.reset(list, scrollEl || { clientHeight: 800 }) || []
|
state.renderedList = vs.reset(list, scrollEl || { clientHeight: 800 }) || []
|
||||||
if (scrollEl) {
|
if (scrollEl) {
|
||||||
vs.init(list, refresh)
|
vs.init(list, refresh)
|
||||||
requestAnimationFrame(() => refresh())
|
requestAnimationFrame(refresh)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
container.onItemUpdate = (rIdx, node) => vs.update(rIdx + state._listStartIndex, node)
|
container.onItemUpdate = (rIdx, node) => vs.update(rIdx + state._listStartIndex, node)
|
||||||
|
|
||||||
container.getOffset = (fields, index, side) => {
|
container.getOffset = (index, side) => (state._leftOffsets || [])[index] || (state._rightOffsets || [])[index] || 0
|
||||||
const f = fields.slice(0, index).filter(f => f.pinned === 'left')
|
|
||||||
const r = fields.slice(index + 1).filter(f => f.pinned === 'right')
|
|
||||||
return (side === 'left' ? f : r).reduce((sum, f) => sum + (f.width || 150), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
container.isCellSelected = (r, f) => {
|
container.isCellSelected = (r, f) => {
|
||||||
const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR)
|
const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR)
|
||||||
@ -100,7 +118,6 @@ Component.register('DataTable', container => {
|
|||||||
container.endSelect = () => state.isSelecting = false
|
container.endSelect = () => state.isSelecting = false
|
||||||
|
|
||||||
container.editCell = (row, f, fIdx) => {
|
container.editCell = (row, f, fIdx) => {
|
||||||
if (!row.__watch) return; // 理论上双击的行一定已经包装过
|
|
||||||
const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR)
|
const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR)
|
||||||
const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF)
|
const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF)
|
||||||
const rIdx = state.list.indexOf(row)
|
const rIdx = state.list.indexOf(row)
|
||||||
@ -209,7 +226,7 @@ Component.register('DataTable', container => {
|
|||||||
<div class="dt-root d-flex flex-column h-100 border bg-body text-body overflow-hidden" style="position:relative; user-select:none" tabindex="0" onunload="this._onUnload()">
|
<div class="dt-root d-flex flex-column h-100 border bg-body text-body overflow-hidden" style="position:relative; user-select:none" tabindex="0" onunload="this._onUnload()">
|
||||||
<div class="dt-header d-flex flex-shrink-0 border-bottom bg-body-tertiary fw-bold text-muted small" style="overflow:hidden; position:sticky; top:0; z-index:20; height:48px">
|
<div class="dt-header d-flex flex-shrink-0 border-bottom bg-body-tertiary fw-bold text-muted small" style="overflow:hidden; position:sticky; top:0; z-index:20; height:48px">
|
||||||
<div $each="this.state.fields" as="f" class="dt-col border-end d-flex align-items-center px-2 bg-body-tertiary"
|
<div $each="this.state.fields" as="f" class="dt-col border-end d-flex align-items-center px-2 bg-body-tertiary"
|
||||||
$style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); ' + (f.pinned ? 'position:sticky; z-index:11;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(this.state.fields, index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(this.state.fields, index, 'right') + 'px;' : '')">
|
$style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); ' + (f.pinned ? 'position:sticky; z-index:11;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(index, 'right') + 'px;' : '')">
|
||||||
<span $text="f.name" $class="'text-truncate flex-grow-1 ' + (f.type === 'number' ? 'text-end' : '')"></span>
|
<span $text="f.name" $class="'text-truncate flex-grow-1 ' + (f.type === 'number' ? 'text-end' : '')"></span>
|
||||||
<Resizer class="dt-resizer" $.target="thisNode.parentNode" min="50" max="600" $bind="f.width" $onresizing="thisNode.closest('.dt-root').style.setProperty('--w-' + f.id, event.detail.newSize + 'px')"/>
|
<Resizer class="dt-resizer" $.target="thisNode.parentNode" min="50" max="600" $bind="f.width" $onresizing="thisNode.closest('.dt-root').style.setProperty('--w-' + f.id, event.detail.newSize + 'px')"/>
|
||||||
</div>
|
</div>
|
||||||
@ -219,7 +236,7 @@ Component.register('DataTable', container => {
|
|||||||
<div $each="this.state.renderedList" as="row" class="dt-row d-flex border-bottom" $onupdate="this.onItemUpdate(rIdx, thisNode)" index="rIdx">
|
<div $each="this.state.renderedList" as="row" class="dt-row d-flex border-bottom" $onupdate="this.onItemUpdate(rIdx, thisNode)" index="rIdx">
|
||||||
<div $each="this.state.fields" as="f" class="dt-cell border-end d-flex align-items-center bg-body"
|
<div $each="this.state.fields" as="f" class="dt-cell border-end d-flex align-items-center bg-body"
|
||||||
$class="'dt-cell border-end d-flex align-items-center bg-body' + (this.isCellSelected(rIdx + this.state._listStartIndex, index) ? ' bg-primary-subtle' : '') + (row._activeF === index ? ' dt-cell-active' : '')"
|
$class="'dt-cell border-end d-flex align-items-center bg-body' + (this.isCellSelected(rIdx + this.state._listStartIndex, index) ? ' bg-primary-subtle' : '') + (row._activeF === index ? ' dt-cell-active' : '')"
|
||||||
$style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); height:40px; ' + (f.pinned ? 'position:sticky; z-index:10;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(this.state.fields, index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(this.state.fields, index, 'right') + 'px;' : '')"
|
$style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); height:40px; ' + (f.pinned ? 'position:sticky; z-index:10;' : '') + (f.pinned === 'left' ? 'left:' + this.getOffset(index, 'left') + 'px;' : '') + (f.pinned === 'right' ? 'right:' + this.getOffset(index, 'right') + 'px;' : '')"
|
||||||
$onmousedown="this.startSelect(rIdx + this.state._listStartIndex, index, event)"
|
$onmousedown="this.startSelect(rIdx + this.state._listStartIndex, index, event)"
|
||||||
$onmouseenter="this.updateSelect(rIdx + this.state._listStartIndex, index)"
|
$onmouseenter="this.updateSelect(rIdx + this.state._listStartIndex, index)"
|
||||||
$ondblclick="this.editCell(row, f, index)">
|
$ondblclick="this.editCell(row, f, index)">
|
||||||
@ -254,3 +271,5 @@ Component.register('DataTable', container => {
|
|||||||
</style>
|
</style>
|
||||||
</div>
|
</div>
|
||||||
`))
|
`))
|
||||||
|
|
||||||
|
if (typeof document !== 'undefined') RefreshState(document.documentElement)
|
||||||
|
|||||||
135
test/index.html
135
test/index.html
@ -8,114 +8,63 @@
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body,
|
body, html { height: 100%; margin: 0; padding: 0; overflow: hidden; }
|
||||||
html {
|
.demo-container { height: 100vh; padding: 20px; display: flex; flex-direction: column; }
|
||||||
height: 100%;
|
.table-wrapper { flex-grow: 1; min-height: 0; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); }
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.demo-container {
|
|
||||||
height: 100vh;
|
|
||||||
padding: 20px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-wrapper {
|
|
||||||
flex-grow: 1;
|
|
||||||
min-height: 0;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-light">
|
<body class="bg-light">
|
||||||
<div id="debug-log" style="position:fixed; top:0; right:0; background:rgba(0,0,0,0.8); color:white; z-index:9999; font-family:monospace; padding:10px; max-height:200px; overflow:auto;"></div>
|
|
||||||
<div class="demo-container">
|
<div class="demo-container">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
||||||
<h4 class="mb-0 text-primary fw-bold">DataTable Professional</h4>
|
|
||||||
<div class="text-muted small">Double-click to edit • Drag/Shift to select • Ctrl+C/V to Copy/Paste</div>
|
|
||||||
</div>
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<DataTable id="myTable"></DataTable>
|
<DataTable id="myTable"></DataTable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { RefreshState, NewState } from '@web/state'
|
import { RefreshState } from '@web/state'
|
||||||
import '@web/base'
|
import '@web/base'
|
||||||
import '../src/index.js'
|
import '../src/index.js'
|
||||||
|
|
||||||
const logEl = document.getElementById('debug-log');
|
const table = document.getElementById('myTable')
|
||||||
const log = (msg) => {
|
|
||||||
console.log(msg);
|
const fields = [
|
||||||
logEl.innerText += msg + '\n';
|
{ id: 'id', name: 'ID', width: 60, type: 'text', pinned: 'left' },
|
||||||
|
{ id: 'name', name: 'User Name', width: 150, type: 'text', pinned: 'left' },
|
||||||
|
{ id: 'role', name: 'Role', width: 120, type: 'select', options: ['Admin', 'Editor', 'Viewer'] },
|
||||||
|
{ id: 'active', name: 'Active', width: 80, type: 'switch' },
|
||||||
|
{ id: 'bio', name: 'Bio', width: 250, type: 'textarea' },
|
||||||
|
{ id: 'gender', name: 'Gender', width: 120, type: 'radio', options: ['Male', 'Female', 'Other'] },
|
||||||
|
{ id: 'tags', name: 'Tags', width: 150, type: 'TagsInput' },
|
||||||
|
{ id: 'score', name: 'Score', width: 100, type: 'number' },
|
||||||
|
{ id: 'created', name: 'Created At', width: 150, type: 'date' },
|
||||||
|
{ id: 'actions', name: 'Actions', width: 100, type: 'text', pinned: 'right' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const generateData = (count) => Array.from({ length: count }, (_, i) => ({
|
||||||
|
id: i + 1,
|
||||||
|
name: 'User ' + (i + 1),
|
||||||
|
role: ['Admin', 'Editor', 'Viewer'][i % 3],
|
||||||
|
active: i % 2 === 0,
|
||||||
|
bio: 'Bio ' + (i + 1),
|
||||||
|
gender: ['Male', 'Female', 'Other'][i % 3],
|
||||||
|
tags: ['State.js', 'Vite'].slice(0, (i % 2) + 1),
|
||||||
|
score: 80,
|
||||||
|
created: '2026-05-17',
|
||||||
|
actions: '...'
|
||||||
|
}))
|
||||||
|
|
||||||
|
const data = generateData(500)
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
if (table.state) {
|
||||||
|
Object.assign(table.state, { fields, list: data });
|
||||||
|
window.testStatus = 'passed';
|
||||||
|
} else {
|
||||||
|
setTimeout(init, 50);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
init();
|
||||||
log('Script starting after imports...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const table = document.getElementById('myTable')
|
|
||||||
|
|
||||||
const fields = [
|
|
||||||
{ id: 'id', name: 'ID', width: 60, type: 'text', pinned: 'left' },
|
|
||||||
{ id: 'name', name: 'User Name', width: 150, type: 'text', pinned: 'left' },
|
|
||||||
{ id: 'role', name: 'Role', width: 120, type: 'select', options: ['Admin', 'Editor', 'Viewer'] },
|
|
||||||
{ id: 'active', name: 'Active', width: 80, type: 'switch' },
|
|
||||||
{ id: 'bio', name: 'Bio', width: 250, type: 'textarea' },
|
|
||||||
{ id: 'gender', name: 'Gender', width: 120, type: 'radio', options: ['Male', 'Female', 'Other'] },
|
|
||||||
{ id: 'tags', name: 'Tags', width: 150, type: 'TagsInput' },
|
|
||||||
{ id: 'score', name: 'Score', width: 100, type: 'number' },
|
|
||||||
{ id: 'created', name: 'Created At', width: 150, type: 'date' },
|
|
||||||
{ id: 'actions', name: 'Actions', width: 100, type: 'text', pinned: 'right' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const generateData = (count) => Array.from({ length: count }, (_, i) => ({
|
|
||||||
id: i + 1,
|
|
||||||
name: 'User ' + (i + 1),
|
|
||||||
role: ['Admin', 'Editor', 'Viewer'][i % 3],
|
|
||||||
active: i % 2 === 0,
|
|
||||||
bio: 'This is the bio for user ' + (i + 1) + '. It might be a long text that needs a textarea for editing.',
|
|
||||||
gender: ['Male', 'Female', 'Other'][i % 3],
|
|
||||||
tags: ['State.js', 'Vite', 'Playwright'].slice(0, (i % 3) + 1),
|
|
||||||
score: Math.floor(Math.random() * 100),
|
|
||||||
created: new Date().toISOString().split('T')[0],
|
|
||||||
actions: '...'
|
|
||||||
}))
|
|
||||||
|
|
||||||
const data = generateData(500)
|
|
||||||
|
|
||||||
// 数据初始化逻辑
|
|
||||||
setTimeout(() => {
|
|
||||||
try {
|
|
||||||
log('Triggering RefreshState before init...');
|
|
||||||
RefreshState(table);
|
|
||||||
|
|
||||||
log('Initializing table state...');
|
|
||||||
if (!table.state) table.state = NewState({});
|
|
||||||
|
|
||||||
Object.assign(table.state, {
|
|
||||||
fields: fields,
|
|
||||||
list: data
|
|
||||||
})
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
window.testStatus = 'passed'
|
|
||||||
log('DataTable initialized in test page: PASSED');
|
|
||||||
}, 500)
|
|
||||||
} catch (e) {
|
|
||||||
log('ERROR in state init: ' + e.message + '\n' + e.stack);
|
|
||||||
}
|
|
||||||
}, 200)
|
|
||||||
} catch (e) {
|
|
||||||
log('ERROR during startup: ' + e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user