275 lines
6.3 KiB
Vue
275 lines
6.3 KiB
Vue
<script setup lang="ts">
|
|
import type { CSSProperties } from 'vue';
|
|
|
|
import { computed, useSlots } from 'vue';
|
|
|
|
interface Props {
|
|
contentPadding?: number | string;
|
|
dense?: boolean;
|
|
stickyToolbar?: boolean;
|
|
surface?: 'panel' | 'plain' | 'subtle';
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
contentPadding: 0,
|
|
dense: false,
|
|
stickyToolbar: false,
|
|
surface: 'panel',
|
|
});
|
|
|
|
const slots = useSlots();
|
|
|
|
const hasToolbar = computed(() => Boolean(slots.filters || slots.actions));
|
|
|
|
const contentStyle = computed((): CSSProperties => {
|
|
const padding =
|
|
typeof props.contentPadding === 'number'
|
|
? `${props.contentPadding}px`
|
|
: props.contentPadding;
|
|
|
|
return padding ? { '--list-page-shell-content-padding': padding } : {};
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<section
|
|
class="list-page-shell"
|
|
:class="[
|
|
`is-${surface}`,
|
|
{
|
|
'is-dense': dense,
|
|
},
|
|
]"
|
|
>
|
|
<div
|
|
v-if="hasToolbar"
|
|
class="list-page-shell__toolbar"
|
|
:class="{
|
|
'is-sticky': stickyToolbar,
|
|
}"
|
|
>
|
|
<div v-if="$slots.filters" class="list-page-shell__filters">
|
|
<slot name="filters"></slot>
|
|
</div>
|
|
<div v-if="$slots.actions" class="list-page-shell__actions">
|
|
<slot name="actions"></slot>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="list-page-shell__content" :style="contentStyle">
|
|
<slot></slot>
|
|
<slot name="empty"></slot>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.list-page-shell {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
min-height: 0;
|
|
}
|
|
|
|
.list-page-shell.is-dense {
|
|
gap: 12px;
|
|
}
|
|
|
|
.list-page-shell__toolbar {
|
|
position: relative;
|
|
display: flex;
|
|
gap: 16px;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
padding: 8px 6px 2px;
|
|
background: transparent;
|
|
border: none;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.list-page-shell__toolbar.is-sticky {
|
|
position: sticky;
|
|
top: 12px;
|
|
z-index: 5;
|
|
padding: 10px 10px 6px;
|
|
background: hsl(var(--glass-tint) / 70%);
|
|
border: 1px solid hsl(var(--glass-border) / 44%);
|
|
border-radius: 20px;
|
|
box-shadow: var(--shadow-toolbar);
|
|
backdrop-filter: blur(var(--glass-blur)) saturate(155%);
|
|
}
|
|
|
|
.list-page-shell__filters,
|
|
.list-page-shell__actions {
|
|
min-width: 0;
|
|
}
|
|
|
|
.list-page-shell__filters {
|
|
flex: 1;
|
|
}
|
|
|
|
.list-page-shell__content {
|
|
--list-page-shell-content-padding: 0px;
|
|
|
|
position: relative;
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
padding: var(--list-page-shell-content-padding);
|
|
overflow: hidden;
|
|
background: linear-gradient(
|
|
180deg,
|
|
hsl(var(--glass-border) / 84%) 0%,
|
|
hsl(var(--surface-panel) / 94%) 28%,
|
|
hsl(var(--surface-panel) / 97%) 100%
|
|
);
|
|
border: 1px solid hsl(var(--glass-border) / 58%);
|
|
border-radius: 24px;
|
|
box-shadow:
|
|
inset 0 1px 0 hsl(var(--glass-border) / 72%),
|
|
0 24px 54px -42px hsl(var(--primary) / 22%),
|
|
0 18px 42px -34px hsl(var(--foreground) / 10%);
|
|
backdrop-filter: var(--persistent-surface-backdrop-filter);
|
|
}
|
|
|
|
.list-page-shell.is-subtle .list-page-shell__content {
|
|
background: linear-gradient(
|
|
180deg,
|
|
hsl(var(--glass-tint) / 84%) 0%,
|
|
hsl(var(--surface-contrast-soft) / 94%) 100%
|
|
);
|
|
box-shadow: none;
|
|
}
|
|
|
|
.list-page-shell.is-plain .list-page-shell__content {
|
|
background: hsl(var(--surface-panel) / 96%);
|
|
border: 1px solid hsl(var(--divider-faint) / 58%);
|
|
border-radius: 20px;
|
|
box-shadow: none;
|
|
backdrop-filter: none;
|
|
}
|
|
|
|
.list-page-shell.is-plain .list-page-shell__content::before {
|
|
display: none;
|
|
}
|
|
|
|
.list-page-shell__content::before {
|
|
position: absolute;
|
|
inset: 0 0 auto;
|
|
height: 220px;
|
|
pointer-events: none;
|
|
content: '';
|
|
background:
|
|
radial-gradient(
|
|
ellipse 84% 72% at 10% 0%,
|
|
hsl(var(--nav-ambient) / 18%) 0%,
|
|
transparent 68%
|
|
),
|
|
linear-gradient(180deg, hsl(var(--nav-flow-trace) / 8%) 0%, transparent 78%);
|
|
}
|
|
|
|
.list-page-shell__content > * {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.page-data-container) {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-empty) {
|
|
margin: auto;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-table) {
|
|
--el-table-bg-color: transparent;
|
|
--el-table-tr-bg-color: transparent;
|
|
--el-table-header-bg-color: hsl(var(--table-header-bg));
|
|
--el-table-border-color: hsl(var(--table-row-border));
|
|
--el-table-current-row-bg-color: hsl(var(--table-row-hover));
|
|
--el-fill-color-blank: transparent;
|
|
|
|
background: transparent;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-table--border),
|
|
.list-page-shell__content :deep(.el-table__inner-wrapper),
|
|
.list-page-shell__content :deep(.el-table__header-wrapper),
|
|
.list-page-shell__content :deep(.el-table__body-wrapper) {
|
|
border: none;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-table__inner-wrapper::before),
|
|
.list-page-shell__content :deep(.el-table--border::before),
|
|
.list-page-shell__content :deep(.el-table--border::after) {
|
|
display: none;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-table th.el-table__cell) {
|
|
padding: 12px 0;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: hsl(var(--text-strong));
|
|
background: hsl(var(--table-header-bg) / 86%);
|
|
border-bottom: 1px solid hsl(var(--divider-faint) / 38%);
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-table th.el-table__cell:first-child) {
|
|
border-top-left-radius: 12px;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-table th.el-table__cell:last-child) {
|
|
border-top-right-radius: 12px;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-table td.el-table__cell),
|
|
.list-page-shell__content :deep(.el-table--border .el-table__cell) {
|
|
padding: 14px 0;
|
|
border-right: none;
|
|
border-bottom: 1px solid hsl(var(--divider-faint) / 46%);
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-table__row:hover > td.el-table__cell) {
|
|
background: hsl(var(--table-row-hover)) !important;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-tag) {
|
|
border-color: transparent;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-pagination) {
|
|
padding: 0 2px;
|
|
}
|
|
|
|
.list-page-shell__content :deep(.el-pagination button),
|
|
.list-page-shell__content :deep(.el-pager li) {
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.list-page-shell__toolbar :deep(.custom-header) {
|
|
width: 100%;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.list-page-shell {
|
|
gap: 12px;
|
|
}
|
|
|
|
.list-page-shell__toolbar {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
padding: 2px 0 0;
|
|
}
|
|
|
|
.list-page-shell__actions {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|