style(ui): improve menu aesthetics and standardize $class syntax

This commit is contained in:
AI Engineer 2026-05-25 14:45:12 +08:00
parent b76ae9fbf5
commit ded8dfd72a

View File

@ -293,6 +293,12 @@ Component.register('DataTable', container => {
state._MODE_ICONS = MODE_ICONS; state._MODE_ICONS = MODE_ICONS;
}, Util.makeDom(/*html*/` }, Util.makeDom(/*html*/`
<!--
NOTE: For $class and $style directives, ALWAYS use the template literal syntax:
$class="base-class \${condition ? 'active' : ''}"
DO NOT use string concatenation like $class="'base-class ' + (condition ? 'active' : '')".
Since the HTML is wrapped in backticks (``), remember to escape the dollar sign: \${ }
-->
<div class="dt-root d-flex flex-column h-100 border bg-body text-body overflow-hidden" style="position:relative; user-select:none; outline: none; min-height: 0" tabindex="0"> <div class="dt-root d-flex flex-column h-100 border bg-body text-body overflow-hidden" style="position:relative; user-select:none; outline: none; min-height: 0" tabindex="0">
<div class="dt-main flex-grow-1 overflow-auto" $onscroll="this.onScroll()" <div class="dt-main flex-grow-1 overflow-auto" $onscroll="this.onScroll()"
$onmousedown="this.onMainMouseDown(event)" $onmouseover="this.onMainMouseOver(event)" $ondblclick="this.onMainDblClick(event)" $onmousedown="this.onMainMouseDown(event)" $onmouseover="this.onMainMouseOver(event)" $ondblclick="this.onMainDblClick(event)"
@ -300,10 +306,10 @@ Component.register('DataTable', container => {
<div class="dt-header border-bottom bg-light sticky-top" style="z-index:20"> <div class="dt-header border-bottom bg-light sticky-top" style="z-index:20">
<div class="dt-header-row fw-bold text-muted small"> <div class="dt-header-row fw-bold text-muted small">
<template $each="this.state?.fields || []"> <template $each="this.state?.fields || []">
<div $data-id="item.id" $class="'dt-cell dt-col border-end d-flex align-items-center header-cell' + (item.pinned ? ' pinned-' + item.pinned : '')" $style="(item.pinned ? 'position: sticky; z-index: 11; background-color: inherit; ' : 'position:relative; ') + 'padding: 0; ' + (item.pinned === 'left' ? 'left: var(--l-' + item.id + '); border-right: 1px solid var(--bs-border-color); box-shadow: 2px 0 5px -2px rgba(0,0,0,0.1);' : (item.pinned === 'right' ? 'right: var(--r-' + item.id + '); border-left: 1px solid var(--bs-border-color); box-shadow: -2px 0 5px -2px rgba(0,0,0,0.1);' : ''))"> <div $data-id="item.id" $class="dt-cell dt-col border-end d-flex align-items-center header-cell \${item.pinned ? 'pinned-' + item.pinned : ''}" $style="(item.pinned ? 'position: sticky; z-index: 11; background-color: inherit; ' : 'position:relative; ') + 'padding: 0; ' + (item.pinned === 'left' ? 'left: var(--l-' + item.id + '); border-right: 1px solid var(--bs-border-color); box-shadow: 2px 0 5px -2px rgba(0,0,0,0.1);' : (item.pinned === 'right' ? 'right: var(--r-' + item.id + '); border-left: 1px solid var(--bs-border-color); box-shadow: -2px 0 5px -2px rgba(0,0,0,0.1);' : ''))">
<div class="d-flex align-items-center overflow-hidden flex-grow-1 h-100 px-2 cursor-pointer" $onclick="this.showColumnMenu(item, event)"> <div class="d-flex align-items-center overflow-hidden flex-grow-1 h-100 px-2 cursor-pointer" $onclick="this.showColumnMenu(item, event)">
<i $if="this.state?.filterConfig?.[item.id] && (this.state.filterConfig[item.id].value || this.state.filterConfig[item.id].selectedValues?.length)" class="bi bi-filter me-1 text-primary"></i> <i $if="this.state?.filterConfig?.[item.id] && (this.state.filterConfig[item.id].value || this.state.filterConfig[item.id].selectedValues?.length)" class="bi bi-filter me-1 text-primary"></i>
<i $if="this.state?.sortConfig?.fieldId === item.id && this.state.sortConfig.direction" $class="'bi bi-sort-' + (this.state.sortConfig.direction === 'asc' ? 'down' : 'up-alt') + ' me-1 text-primary'"></i> <i $if="this.state?.sortConfig?.fieldId === item.id && this.state.sortConfig.direction" $class="bi bi-sort-\${this.state.sortConfig.direction === 'asc' ? 'down' : 'up-alt'} me-1 text-primary"></i>
<span $text="item.name" class="text-truncate flex-grow-1"></span> <span $text="item.name" class="text-truncate flex-grow-1"></span>
</div> </div>
<button class="btn btn-xs btn-link text-muted p-0 border-0 me-1 header-menu-btn" $onclick="this.showColumnMenu(item, event)"><i class="bi bi-chevron-down"></i></button> <button class="btn btn-xs btn-link text-muted p-0 border-0 me-1 header-menu-btn" $onclick="this.showColumnMenu(item, event)"><i class="bi bi-chevron-down"></i></button>
@ -316,7 +322,7 @@ Component.register('DataTable', container => {
<div class="dt-spacer-prev flex-shrink-0" style="display:none"></div> <div class="dt-spacer-prev flex-shrink-0" style="display:none"></div>
<template $each="this.state?._renderedList || []" key="id" index="rIdx"> <template $each="this.state?._renderedList || []" key="id" index="rIdx">
<div class="dt-row dt-body-row border-bottom bg-white" $.="this._initRow(thisNode)"> <div class="dt-row dt-body-row border-bottom bg-white" $.="this._initRow(thisNode)">
<template as="f"><div $class="'dt-cell border-end px-2 d-flex align-items-center' + (f.pinned ? ' pinned-' + f.pinned : '')" $style="(f.pinned ? 'position: sticky; z-index: 1; background-color: inherit; ' : '') + (f.pinned === 'left' ? 'left: var(--l-' + f.id + '); border-right: 1px solid var(--bs-border-color); box-shadow: 2px 0 5px -2px rgba(0,0,0,0.1);' : (f.pinned === 'right' ? 'right: var(--r-' + f.id + '); border-left: 1px solid var(--bs-border-color); box-shadow: -2px 0 5px -2px rgba(0,0,0,0.1);' : ''))"><span $text="this.format(item[f.id], f)" class="text-truncate"></span></div></template> <template as="f"><div $class="dt-cell border-end px-2 d-flex align-items-center \${f.pinned ? 'pinned-' + f.pinned : ''}" $style="(f.pinned ? 'position: sticky; z-index: 1; background-color: inherit; ' : '') + (f.pinned === 'left' ? 'left: var(--l-' + f.id + '); border-right: 1px solid var(--bs-border-color); box-shadow: 2px 0 5px -2px rgba(0,0,0,0.1);' : (f.pinned === 'right' ? 'right: var(--r-' + f.id + '); border-left: 1px solid var(--bs-border-color); box-shadow: -2px 0 5px -2px rgba(0,0,0,0.1);' : ''))"><span $text="this.format(item[f.id], f)" class="text-truncate"></span></div></template>
</div> </div>
</template> </template>
<div class="dt-spacer-post flex-shrink-0" style="display:none"></div> <div class="dt-spacer-post flex-shrink-0" style="display:none"></div>
@ -338,10 +344,12 @@ Component.register('DataTable', container => {
</template> </template>
</div> </div>
<div $if="this.state?.activeModes?.length" class="py-2 border-bottom" style="min-height: 48px"> <template $if="this.state?.activeModes?.length">
<div class="py-2 border-bottom" style="min-height: 48px">
<input type="text" class="form-control form-control-sm mb-1" $placeholder="(this.state?.filterConfig?.[this.state?.activeFieldId]?.mode || 'Search').toUpperCase() + '...'" $bind="this.state?.filterConfig?.[this.state?.activeFieldId].value" $onkeydown="if(event.key==='Enter'){this.applySortFilter();this.hideColumnMenu();}"> <input type="text" class="form-control form-control-sm mb-1" $placeholder="(this.state?.filterConfig?.[this.state?.activeFieldId]?.mode || 'Search').toUpperCase() + '...'" $bind="this.state?.filterConfig?.[this.state?.activeFieldId].value" $onkeydown="if(event.key==='Enter'){this.applySortFilter();this.hideColumnMenu();}">
<input $if="this.state?.filterConfig?.[this.state?.activeFieldId]?.mode === 'between'" type="text" class="form-control form-control-sm" placeholder="And..." $bind="this.state?.filterConfig?.[this.state?.activeFieldId].value2" $onkeydown="if(event.key==='Enter'){this.applySortFilter();this.hideColumnMenu();}"> <input $if="this.state?.filterConfig?.[this.state?.activeFieldId]?.mode === 'between'" type="text" class="form-control form-control-sm" placeholder="And..." $bind="this.state?.filterConfig?.[this.state?.activeFieldId].value2" $onkeydown="if(event.key==='Enter'){this.applySortFilter();this.hideColumnMenu();}">
</div> </div>
</template>
<div class="mt-3" style="max-height: 200px; overflow-y: auto;"> <div class="mt-3" style="max-height: 200px; overflow-y: auto;">
<div class="text-muted fw-bold mb-2" style="font-size: 10px; letter-spacing: 0.5px">TOP FREQUENT VALUES</div> <div class="text-muted fw-bold mb-2" style="font-size: 10px; letter-spacing: 0.5px">TOP FREQUENT VALUES</div>
@ -374,7 +382,7 @@ Component.register('DataTable', container => {
.dt-body-row:hover { background-color: var(--bs-secondary-bg) !important; } .dt-body-row:hover { background-color: var(--bs-secondary-bg) !important; }
.header-cell .header-menu-btn { opacity: 0; transition: opacity 0.2s; } .header-cell .header-menu-btn { opacity: 0; transition: opacity 0.2s; }
.header-cell:hover .header-menu-btn { opacity: 1; } .header-cell:hover .header-menu-btn { opacity: 1; }
.dt-column-menu { background-color: var(--bs-body-bg); border: 1px solid var(--bs-border-color); box-shadow: 0 10px 40px rgba(0,0,0,0.2) !important; z-index: 2100 !important; } .dt-column-menu { background-color: var(--bs-body-bg); border: 1px solid var(--bs-primary); box-shadow: 0 10px 40px rgba(0,0,0,0.2) !important; z-index: 2100 !important; }
.btn-xs { padding: 1px 5px; line-height: 1.5; } .btn-xs { padding: 1px 5px; line-height: 1.5; }
.cursor-pointer { cursor: pointer; } .cursor-pointer { cursor: pointer; }
.dt-filter-tabs i { font-size: 1.1rem; } .dt-filter-tabs i { font-size: 1.1rem; }