perf: 重构并美化整体UI
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type {CSSProperties} from 'vue';
|
||||||
|
import {computed, useSlots} from 'vue';
|
||||||
import { computed, useSlots } from 'vue';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
contentPadding?: number | string;
|
contentPadding?: number | string;
|
||||||
@@ -33,8 +32,8 @@ const contentStyle = computed((): CSSProperties => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section
|
<section
|
||||||
|
class="list-page-shell"
|
||||||
:class="[
|
:class="[
|
||||||
'list-page-shell',
|
|
||||||
`is-${surface}`,
|
`is-${surface}`,
|
||||||
{
|
{
|
||||||
'is-dense': dense,
|
'is-dense': dense,
|
||||||
@@ -43,12 +42,10 @@ const contentStyle = computed((): CSSProperties => {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="hasToolbar"
|
v-if="hasToolbar"
|
||||||
:class="[
|
class="list-page-shell__toolbar"
|
||||||
'list-page-shell__toolbar',
|
:class="{
|
||||||
{
|
'is-sticky': stickyToolbar,
|
||||||
'is-sticky': stickyToolbar,
|
}"
|
||||||
},
|
|
||||||
]"
|
|
||||||
>
|
>
|
||||||
<div v-if="$slots.filters" class="list-page-shell__filters">
|
<div v-if="$slots.filters" class="list-page-shell__filters">
|
||||||
<slot name="filters"></slot>
|
<slot name="filters"></slot>
|
||||||
@@ -117,18 +114,57 @@ const contentStyle = computed((): CSSProperties => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
padding: var(--list-page-shell-content-padding);
|
padding: var(--list-page-shell-content-padding);
|
||||||
background: hsl(var(--surface-panel));
|
background: linear-gradient(
|
||||||
border: none;
|
180deg,
|
||||||
|
hsl(var(--glass-border) / 0.84) 0%,
|
||||||
|
hsl(var(--surface-panel) / 0.94) 28%,
|
||||||
|
hsl(var(--surface-panel) / 0.97) 100%
|
||||||
|
);
|
||||||
|
border: 1px solid hsl(var(--glass-border) / 0.58);
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
box-shadow: var(--shadow-subtle);
|
box-shadow:
|
||||||
|
inset 0 1px 0 hsl(var(--glass-border) / 0.72),
|
||||||
|
0 24px 54px -42px hsl(var(--primary) / 0.22),
|
||||||
|
0 18px 42px -34px hsl(var(--foreground) / 0.1);
|
||||||
|
backdrop-filter: blur(calc(var(--glass-blur) * 0.7)) saturate(145%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-page-shell.is-subtle .list-page-shell__content {
|
.list-page-shell.is-subtle .list-page-shell__content {
|
||||||
background: hsl(var(--surface-contrast-soft));
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
hsl(var(--glass-tint) / 0.84) 0%,
|
||||||
|
hsl(var(--surface-contrast-soft) / 0.94) 100%
|
||||||
|
);
|
||||||
box-shadow: none;
|
box-shadow: 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) / 0.18) 0%,
|
||||||
|
transparent 68%
|
||||||
|
),
|
||||||
|
linear-gradient(
|
||||||
|
180deg,
|
||||||
|
hsl(var(--nav-flow-trace) / 0.08) 0%,
|
||||||
|
transparent 78%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-shell__content > * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.list-page-shell__content :deep(.page-data-container) {
|
.list-page-shell__content :deep(.page-data-container) {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormInstance } from 'element-plus';
|
import type {FormInstance} from 'element-plus';
|
||||||
|
|
||||||
import { markRaw, onMounted, ref } from 'vue';
|
|
||||||
|
|
||||||
import { Delete, MoreFilled, Plus } from '@element-plus/icons-vue';
|
|
||||||
import {
|
import {
|
||||||
ElButton,
|
ElButton,
|
||||||
ElDropdown,
|
ElDropdown,
|
||||||
@@ -15,13 +11,17 @@ import {
|
|||||||
ElTableColumn,
|
ElTableColumn,
|
||||||
} from 'element-plus';
|
} from 'element-plus';
|
||||||
|
|
||||||
import { api } from '#/api/request';
|
import {markRaw, onMounted, ref} from 'vue';
|
||||||
|
|
||||||
|
import {Delete, MoreFilled, Plus} from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import {api} from '#/api/request';
|
||||||
import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
|
import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
|
||||||
import ListPageShell from '#/components/page/ListPageShell.vue';
|
import ListPageShell from '#/components/page/ListPageShell.vue';
|
||||||
import PageData from '#/components/page/PageData.vue';
|
import PageData from '#/components/page/PageData.vue';
|
||||||
import { $t } from '#/locales';
|
import {$t} from '#/locales';
|
||||||
import { router } from '#/router';
|
import {router} from '#/router';
|
||||||
import { useDictStore } from '#/store';
|
import {useDictStore} from '#/store';
|
||||||
|
|
||||||
import DatacenterTableModal from './DatacenterTableModal.vue';
|
import DatacenterTableModal from './DatacenterTableModal.vue';
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ function toDetailPage(row: any) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-full flex-col gap-6 p-6">
|
<div class="datacenter-table-page flex h-full flex-col gap-6 p-6">
|
||||||
<DatacenterTableModal ref="saveDialog" @reload="reset" />
|
<DatacenterTableModal ref="saveDialog" @reload="reset" />
|
||||||
<ListPageShell>
|
<ListPageShell>
|
||||||
<template #filters>
|
<template #filters>
|
||||||
@@ -174,4 +174,31 @@ function toDetailPage(row: any) {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.datacenter-table-page {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datacenter-table-page::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
content: '';
|
||||||
|
background:
|
||||||
|
radial-gradient(
|
||||||
|
ellipse 76% 34% at 2% 0%,
|
||||||
|
hsl(var(--nav-ambient) / 0.16) 0%,
|
||||||
|
transparent 64%
|
||||||
|
),
|
||||||
|
radial-gradient(
|
||||||
|
ellipse 56% 22% at 24% 12%,
|
||||||
|
hsl(var(--nav-flow-core) / 0.08) 0%,
|
||||||
|
transparent 68%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.datacenter-table-page > * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -154,6 +154,10 @@
|
|||||||
--modal-preview-glow: 211 100% 62%;
|
--modal-preview-glow: 211 100% 62%;
|
||||||
--nav-ambient: 214 100% 44%;
|
--nav-ambient: 214 100% 44%;
|
||||||
--nav-ambient-secondary: 194 82% 40%;
|
--nav-ambient-secondary: 194 82% 40%;
|
||||||
|
--nav-flow-core: 211 92% 56%;
|
||||||
|
--nav-flow-secondary: 194 78% 48%;
|
||||||
|
--nav-flow-accent: 214 88% 70%;
|
||||||
|
--nav-flow-trace: 189 62% 56%;
|
||||||
--nav-sheen: 210 100% 96%;
|
--nav-sheen: 210 100% 96%;
|
||||||
--shadow-subtle: 0 18px 42px -30px hsl(212 100% 5% / 0.48);
|
--shadow-subtle: 0 18px 42px -30px hsl(212 100% 5% / 0.48);
|
||||||
--shadow-toolbar: 0 28px 54px -36px hsl(212 100% 4% / 0.58);
|
--shadow-toolbar: 0 28px 54px -36px hsl(212 100% 4% / 0.58);
|
||||||
|
|||||||
@@ -154,6 +154,10 @@
|
|||||||
--modal-preview-glow: 211 100% 56%;
|
--modal-preview-glow: 211 100% 56%;
|
||||||
--nav-ambient: 212 100% 84%;
|
--nav-ambient: 212 100% 84%;
|
||||||
--nav-ambient-secondary: 194 93% 82%;
|
--nav-ambient-secondary: 194 93% 82%;
|
||||||
|
--nav-flow-core: 209 94% 74%;
|
||||||
|
--nav-flow-secondary: 195 90% 76%;
|
||||||
|
--nav-flow-accent: 214 100% 88%;
|
||||||
|
--nav-flow-trace: 191 78% 84%;
|
||||||
--nav-sheen: 0 0% 100%;
|
--nav-sheen: 0 0% 100%;
|
||||||
--shadow-subtle: 0 18px 42px -30px hsl(211 78% 48% / 0.16);
|
--shadow-subtle: 0 18px 42px -30px hsl(211 78% 48% / 0.16);
|
||||||
--shadow-toolbar: 0 28px 54px -34px hsl(211 78% 48% / 0.2);
|
--shadow-toolbar: 0 28px 54px -34px hsl(211 78% 48% / 0.2);
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type {CSSProperties} from 'vue';
|
||||||
|
import {computed} from 'vue';
|
||||||
|
|
||||||
import type { ContentCompactType } from '@easyflow-core/typings';
|
import type {ContentCompactType} from '@easyflow-core/typings';
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import {useLayoutContentStyle} from '@easyflow-core/composables';
|
||||||
|
import {Slot} from '@easyflow-core/shadcn-ui';
|
||||||
import { useLayoutContentStyle } from '@easyflow-core/composables';
|
|
||||||
import { Slot } from '@easyflow-core/shadcn-ui';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
@@ -27,6 +26,10 @@ interface Props {
|
|||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {});
|
||||||
|
|
||||||
const { contentElement, overlayStyle } = useLayoutContentStyle();
|
const { contentElement, overlayStyle } = useLayoutContentStyle();
|
||||||
|
const flowContinuationStyle: CSSProperties = {
|
||||||
|
backgroundImage:
|
||||||
|
'radial-gradient(ellipse 96% 34% at -4% -6%, hsl(var(--nav-flow-core) / 0.18) 0%, hsl(var(--nav-flow-core) / 0.08) 30%, transparent 62%), radial-gradient(ellipse 74% 24% at 20% 8%, hsl(var(--nav-ambient) / 0.2) 0%, transparent 64%), linear-gradient(128deg, hsl(var(--nav-flow-trace) / 0.1) 0%, hsl(var(--nav-flow-secondary) / 0.08) 12%, transparent 24%, transparent 48%, hsl(var(--nav-flow-core) / 0.05) 66%, transparent 88%), radial-gradient(ellipse 52% 18% at 50% 26%, hsl(var(--nav-flow-accent) / 0.05) 0%, transparent 72%)',
|
||||||
|
};
|
||||||
|
|
||||||
const style = computed((): CSSProperties => {
|
const style = computed((): CSSProperties => {
|
||||||
const {
|
const {
|
||||||
@@ -44,6 +47,9 @@ const style = computed((): CSSProperties => {
|
|||||||
: {};
|
: {};
|
||||||
return {
|
return {
|
||||||
...compactStyle,
|
...compactStyle,
|
||||||
|
backgroundColor: 'hsl(var(--surface-canvas) / 0.72)',
|
||||||
|
backgroundImage:
|
||||||
|
'linear-gradient(180deg, hsl(var(--nav-surface) / 0.72) 0%, hsl(var(--surface-canvas) / 0.92) 16%, hsl(var(--background) / 0.98) 100%)',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: `${padding}px`,
|
padding: `${padding}px`,
|
||||||
paddingBottom: `${paddingBottom}px`,
|
paddingBottom: `${paddingBottom}px`,
|
||||||
@@ -55,14 +61,16 @@ const style = computed((): CSSProperties => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main
|
<main ref="contentElement" :style="style" class="relative overflow-hidden">
|
||||||
ref="contentElement"
|
<div
|
||||||
:style="style"
|
:style="flowContinuationStyle"
|
||||||
class="relative overflow-hidden bg-[radial-gradient(circle_at_top,hsl(var(--glass-tint))/0.34,transparent_40%),linear-gradient(180deg,hsl(var(--surface-canvas)),hsl(var(--background-deep)))]"
|
class="pointer-events-none absolute inset-0"
|
||||||
>
|
></div>
|
||||||
<Slot :style="overlayStyle">
|
<Slot :style="overlayStyle">
|
||||||
<slot name="overlay"></slot>
|
<slot name="overlay"></slot>
|
||||||
</Slot>
|
</Slot>
|
||||||
<slot></slot>
|
<div class="relative z-[1] h-full">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type {CSSProperties} from 'vue';
|
||||||
|
import {computed, useSlots} from 'vue';
|
||||||
import { computed, useSlots } from 'vue';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
@@ -47,11 +46,10 @@ const style = computed((): CSSProperties => {
|
|||||||
const right = !show || !fullWidth ? undefined : 0;
|
const right = !show || !fullWidth ? undefined : 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
backgroundColor: 'hsl(var(--nav-surface) / 0.86)',
|
backgroundColor: 'transparent',
|
||||||
backgroundImage:
|
backgroundImage: 'none',
|
||||||
'linear-gradient(180deg, hsl(var(--nav-surface) / 0.96), hsl(var(--glass-tint) / 0.82))',
|
backdropFilter: 'none',
|
||||||
backdropFilter: 'blur(var(--glass-blur)) saturate(160%)',
|
boxShadow: 'none',
|
||||||
boxShadow: 'var(--shadow-subtle)',
|
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
marginTop: show ? 0 : `-${height}px`,
|
marginTop: show ? 0 : `-${height}px`,
|
||||||
right,
|
right,
|
||||||
@@ -63,10 +61,6 @@ const logoStyle = computed((): CSSProperties => {
|
|||||||
minWidth: `${props.isMobile ? 40 : props.sidebarWidth}px`,
|
minWidth: `${props.isMobile ? 40 : props.sidebarWidth}px`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const ambientStyle: CSSProperties = {
|
|
||||||
backgroundImage:
|
|
||||||
'radial-gradient(circle at left top, hsl(var(--nav-ambient) / 0.42) 0%, hsl(var(--nav-ambient) / 0.18) 22%, transparent 56%), radial-gradient(circle at 20% 0%, hsl(var(--nav-ambient-secondary) / 0.22) 0%, transparent 40%), linear-gradient(180deg, hsl(var(--nav-sheen) / 0.22), transparent 48%)',
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -75,8 +69,6 @@ const ambientStyle: CSSProperties = {
|
|||||||
:style="style"
|
:style="style"
|
||||||
class="relative top-0 flex w-full flex-[0_0_auto] items-center gap-1 overflow-x-hidden overflow-y-visible px-3 transition-[margin-top,background-color,box-shadow] duration-200"
|
class="relative top-0 flex w-full flex-[0_0_auto] items-center gap-1 overflow-x-hidden overflow-y-visible px-3 transition-[margin-top,background-color,box-shadow] duration-200"
|
||||||
>
|
>
|
||||||
<div :style="ambientStyle" class="pointer-events-none absolute inset-0"></div>
|
|
||||||
|
|
||||||
<div v-if="slots.logo" :style="logoStyle" class="relative z-10">
|
<div v-if="slots.logo" :style="logoStyle" class="relative z-10">
|
||||||
<slot name="logo"></slot>
|
<slot name="logo"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type {CSSProperties} from 'vue';
|
||||||
|
import {computed, shallowRef, useSlots, watchEffect} from 'vue';
|
||||||
|
|
||||||
import { computed, shallowRef, useSlots, watchEffect } from 'vue';
|
import {EasyFlowScrollbar} from '@easyflow-core/shadcn-ui';
|
||||||
|
|
||||||
import { EasyFlowScrollbar } from '@easyflow-core/shadcn-ui';
|
import {useScrollLock} from '@vueuse/core';
|
||||||
|
|
||||||
import { useScrollLock } from '@vueuse/core';
|
import {SidebarCollapseButton, SidebarFixedButton} from './widgets';
|
||||||
|
|
||||||
import { SidebarCollapseButton, SidebarFixedButton } from './widgets';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
@@ -123,12 +122,12 @@ const style = computed((): CSSProperties => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'--scroll-shadow': 'var(--surface-elevated)',
|
'--scroll-shadow': 'var(--surface-elevated)',
|
||||||
backgroundColor: 'hsl(var(--nav-surface-subtle) / 0.44)',
|
backgroundColor: 'hsl(var(--nav-surface-subtle) / 0.16)',
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
'linear-gradient(180deg, hsl(var(--nav-surface) / 0.66) 0%, hsl(var(--nav-surface-subtle) / 0.5) 28%, hsl(var(--glass-tint) / 0.36) 100%)',
|
'linear-gradient(180deg, hsl(var(--nav-surface) / 0.3) 0%, hsl(var(--nav-surface-subtle) / 0.2) 28%, hsl(var(--glass-tint) / 0.12) 100%)',
|
||||||
backdropFilter: 'blur(calc(var(--glass-blur) * 1.5)) saturate(188%)',
|
backdropFilter: 'blur(calc(var(--glass-blur) * 1.5)) saturate(188%)',
|
||||||
boxShadow:
|
boxShadow:
|
||||||
'0 26px 64px -42px hsl(var(--primary) / 0.28), inset 0 1px 0 hsl(var(--nav-sheen) / 0.42), inset -1px 0 0 hsl(var(--nav-sheen) / 0.14)',
|
'inset 0 1px 0 hsl(var(--nav-sheen) / 0.14), inset -1px 0 0 hsl(var(--nav-border) / 0.08)',
|
||||||
...calcMenuWidthStyle(false),
|
...calcMenuWidthStyle(false),
|
||||||
height: `calc(100% - ${marginTop}px)`,
|
height: `calc(100% - ${marginTop}px)`,
|
||||||
marginTop: `${marginTop}px`,
|
marginTop: `${marginTop}px`,
|
||||||
@@ -142,26 +141,17 @@ const extraStyle = computed((): CSSProperties => {
|
|||||||
const { extraWidth, show, width, zIndex } = props;
|
const { extraWidth, show, width, zIndex } = props;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
backgroundColor: 'hsl(var(--nav-surface-subtle) / 0.4)',
|
backgroundColor: 'hsl(var(--nav-surface-subtle) / 0.12)',
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
'linear-gradient(180deg, hsl(var(--nav-surface-subtle) / 0.62) 0%, hsl(var(--glass-tint) / 0.34) 100%)',
|
'linear-gradient(180deg, hsl(var(--nav-surface-subtle) / 0.24) 0%, hsl(var(--glass-tint) / 0.12) 100%)',
|
||||||
backdropFilter: 'blur(calc(var(--glass-blur) * 1.34)) saturate(182%)',
|
backdropFilter: 'blur(calc(var(--glass-blur) * 1.34)) saturate(182%)',
|
||||||
boxShadow:
|
boxShadow:
|
||||||
'0 26px 64px -42px hsl(var(--primary) / 0.24), inset 0 1px 0 hsl(var(--nav-sheen) / 0.34), inset -1px 0 0 hsl(var(--nav-sheen) / 0.1)',
|
'inset 0 1px 0 hsl(var(--nav-sheen) / 0.12), inset -1px 0 0 hsl(var(--nav-border) / 0.08)',
|
||||||
left: `${width}px`,
|
left: `${width}px`,
|
||||||
width: extraVisible.value && show ? `${extraWidth}px` : 0,
|
width: extraVisible.value && show ? `${extraWidth}px` : 0,
|
||||||
zIndex,
|
zIndex,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const shellAmbientStyle: CSSProperties = {
|
|
||||||
backgroundImage:
|
|
||||||
'radial-gradient(circle at left top, hsl(var(--nav-ambient) / 0.58) 0%, hsl(var(--nav-ambient) / 0.22) 20%, transparent 56%), radial-gradient(circle at 14% 0%, hsl(var(--nav-ambient-secondary) / 0.24) 0%, transparent 34%), linear-gradient(180deg, hsl(var(--nav-sheen) / 0.34), transparent 16%)',
|
|
||||||
};
|
|
||||||
const extraAmbientStyle: CSSProperties = {
|
|
||||||
backgroundImage:
|
|
||||||
'radial-gradient(circle at left top, hsl(var(--nav-ambient) / 0.42) 0%, hsl(var(--nav-ambient) / 0.16) 18%, transparent 44%), linear-gradient(180deg, hsl(var(--nav-sheen) / 0.28), transparent 16%)',
|
|
||||||
};
|
|
||||||
|
|
||||||
const extraTitleStyle = computed((): CSSProperties => {
|
const extraTitleStyle = computed((): CSSProperties => {
|
||||||
const { headerHeight } = props;
|
const { headerHeight } = props;
|
||||||
|
|
||||||
@@ -211,6 +201,10 @@ const toolStyle = computed((): CSSProperties => {
|
|||||||
top: `${Math.max(10, (props.headerHeight - 36) / 2)}px`,
|
top: `${Math.max(10, (props.headerHeight - 36) / 2)}px`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
const pinToolStyle: CSSProperties = {
|
||||||
|
bottom: '16px',
|
||||||
|
left: '12px',
|
||||||
|
};
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
extraVisible.value = props.fixedExtra ? true : extraVisible.value;
|
extraVisible.value = props.fixedExtra ? true : extraVisible.value;
|
||||||
@@ -240,11 +234,7 @@ function calcMenuWidthStyle(isHiddenDom: boolean): CSSProperties {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMouseenter(e: MouseEvent) {
|
function handleMouseenter() {
|
||||||
if (e?.offsetX < 10) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 未开启和未折叠状态不生效
|
// 未开启和未折叠状态不生效
|
||||||
if (expandOnHover.value) {
|
if (expandOnHover.value) {
|
||||||
return;
|
return;
|
||||||
@@ -271,6 +261,22 @@ function handleMouseleave() {
|
|||||||
collapse.value = true;
|
collapse.value = true;
|
||||||
extraVisible.value = false;
|
extraVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleFixedToggle(nextValue: boolean) {
|
||||||
|
if (nextValue) {
|
||||||
|
collapse.value = false;
|
||||||
|
extraVisible.value = true;
|
||||||
|
expandOnHovering.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse.value = true;
|
||||||
|
extraVisible.value = false;
|
||||||
|
expandOnHovering.value = false;
|
||||||
|
if (props.isSidebarMixed) {
|
||||||
|
isLocked.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -289,11 +295,10 @@ function handleMouseleave() {
|
|||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
:style="style"
|
:style="style"
|
||||||
class="fixed left-0 top-0 h-full overflow-hidden transition-all duration-150"
|
class="fixed left-0 top-0 h-full overflow-hidden transition-all duration-150"
|
||||||
@mouseenter="handleMouseenter"
|
@mouseenter="handleMouseenter"
|
||||||
@mouseleave="handleMouseleave"
|
@mouseleave="handleMouseleave"
|
||||||
>
|
>
|
||||||
<div :style="shellAmbientStyle" class="pointer-events-none absolute inset-0"></div>
|
|
||||||
<div
|
<div
|
||||||
v-if="slots.logo"
|
v-if="slots.logo"
|
||||||
:style="headerStyle"
|
:style="headerStyle"
|
||||||
@@ -306,12 +311,13 @@ function handleMouseleave() {
|
|||||||
</EasyFlowScrollbar>
|
</EasyFlowScrollbar>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="!isSidebarMixed && !collapse && showFixedButton"
|
v-if="!isSidebarMixed && showFixedButton"
|
||||||
:style="toolStyle"
|
:style="pinToolStyle"
|
||||||
class="absolute right-3 z-20 flex"
|
class="absolute z-20 flex"
|
||||||
>
|
>
|
||||||
<SidebarFixedButton
|
<SidebarFixedButton
|
||||||
v-model:expand-on-hover="expandOnHover"
|
v-model:expand-on-hover="expandOnHover"
|
||||||
|
@toggle="handleFixedToggle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -320,7 +326,6 @@ function handleMouseleave() {
|
|||||||
:style="extraStyle"
|
:style="extraStyle"
|
||||||
class="fixed top-0 h-full overflow-hidden transition-all duration-200"
|
class="fixed top-0 h-full overflow-hidden transition-all duration-200"
|
||||||
>
|
>
|
||||||
<div :style="extraAmbientStyle" class="pointer-events-none absolute inset-0"></div>
|
|
||||||
<div
|
<div
|
||||||
v-if="!extraCollapse"
|
v-if="!extraCollapse"
|
||||||
:style="extraTitleStyle"
|
:style="extraTitleStyle"
|
||||||
@@ -346,6 +351,7 @@ function handleMouseleave() {
|
|||||||
<SidebarFixedButton
|
<SidebarFixedButton
|
||||||
v-if="!extraCollapse"
|
v-if="!extraCollapse"
|
||||||
v-model:expand-on-hover="expandOnHover"
|
v-model:expand-on-hover="expandOnHover"
|
||||||
|
@toggle="handleFixedToggle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type {CSSProperties} from 'vue';
|
||||||
|
import {computed} from 'vue';
|
||||||
import { computed } from 'vue';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
@@ -15,18 +14,13 @@ const props = withDefaults(defineProps<Props>(), {});
|
|||||||
const style = computed((): CSSProperties => {
|
const style = computed((): CSSProperties => {
|
||||||
const { height } = props;
|
const { height } = props;
|
||||||
return {
|
return {
|
||||||
backgroundColor: 'hsl(var(--glass-tint) / 0.68)',
|
backgroundColor: 'transparent',
|
||||||
backgroundImage:
|
backgroundImage: 'none',
|
||||||
'linear-gradient(180deg, hsl(var(--nav-surface-subtle) / 0.92), hsl(var(--glass-tint) / 0.74))',
|
backdropFilter: 'none',
|
||||||
backdropFilter: 'blur(calc(var(--glass-blur) * 0.9)) saturate(155%)',
|
boxShadow: 'none',
|
||||||
boxShadow: '0 18px 36px -30px hsl(var(--primary) / 0.12)',
|
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const ambientStyle: CSSProperties = {
|
|
||||||
backgroundImage:
|
|
||||||
'radial-gradient(circle at left top, hsl(var(--nav-ambient) / 0.24) 0%, transparent 38%), linear-gradient(180deg, hsl(var(--nav-sheen) / 0.14), transparent 62%)',
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -34,7 +28,6 @@ const ambientStyle: CSSProperties = {
|
|||||||
:style="style"
|
:style="style"
|
||||||
class="relative flex w-full overflow-hidden transition-all"
|
class="relative flex w-full overflow-hidden transition-all"
|
||||||
>
|
>
|
||||||
<div :style="ambientStyle" class="pointer-events-none absolute inset-0"></div>
|
|
||||||
<div class="relative z-10 flex w-full">
|
<div class="relative z-10 flex w-full">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type {CSSProperties} from 'vue';
|
||||||
|
|
||||||
import { Pin, PinOff } from '@easyflow-core/icons';
|
import {Pin, PinOff} from '@easyflow-core/icons';
|
||||||
|
|
||||||
const expandOnHover = defineModel<boolean>('expandOnHover');
|
const expandOnHover = defineModel<boolean>('expandOnHover');
|
||||||
|
const emit = defineEmits<{
|
||||||
|
toggle: [value: boolean];
|
||||||
|
}>();
|
||||||
const buttonStyle: CSSProperties = {
|
const buttonStyle: CSSProperties = {
|
||||||
backgroundColor: 'hsl(var(--nav-tool-bg) / 0.92)',
|
backgroundColor: 'hsl(var(--nav-tool-bg) / 0.92)',
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
@@ -13,7 +16,9 @@ const buttonStyle: CSSProperties = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function toggleFixed() {
|
function toggleFixed() {
|
||||||
expandOnHover.value = !expandOnHover.value;
|
const nextValue = !expandOnHover.value;
|
||||||
|
expandOnHover.value = nextValue;
|
||||||
|
emit('toggle', nextValue);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type {CSSProperties} from 'vue';
|
||||||
|
import {computed, ref, watch} from 'vue';
|
||||||
|
|
||||||
import type { EasyFlowLayoutProps } from './easyflow-layout';
|
import type {EasyFlowLayoutProps} from './easyflow-layout';
|
||||||
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SCROLL_FIXED_CLASS,
|
SCROLL_FIXED_CLASS,
|
||||||
useLayoutFooterStyle,
|
useLayoutFooterStyle,
|
||||||
useLayoutHeaderStyle,
|
useLayoutHeaderStyle,
|
||||||
} from '@easyflow-core/composables';
|
} from '@easyflow-core/composables';
|
||||||
import { IconifyIcon } from '@easyflow-core/icons';
|
import {IconifyIcon} from '@easyflow-core/icons';
|
||||||
import { EasyFlowIconButton } from '@easyflow-core/shadcn-ui';
|
import {EasyFlowIconButton} from '@easyflow-core/shadcn-ui';
|
||||||
import { ELEMENT_ID_MAIN_CONTENT } from '@easyflow-core/shared/constants';
|
import {ELEMENT_ID_MAIN_CONTENT} from '@easyflow-core/shared/constants';
|
||||||
|
|
||||||
import { useMouse, useScroll, useThrottleFn } from '@vueuse/core';
|
import {useMouse, useScroll, useThrottleFn} from '@vueuse/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LayoutContent,
|
LayoutContent,
|
||||||
@@ -23,7 +22,7 @@ import {
|
|||||||
LayoutSidebar,
|
LayoutSidebar,
|
||||||
LayoutTabbar,
|
LayoutTabbar,
|
||||||
} from './components';
|
} from './components';
|
||||||
import { useLayout } from './hooks/use-layout';
|
import {useLayout} from './hooks/use-layout';
|
||||||
|
|
||||||
interface Props extends EasyFlowLayoutProps {}
|
interface Props extends EasyFlowLayoutProps {}
|
||||||
|
|
||||||
@@ -40,13 +39,13 @@ const headerToggleButtonStyle: CSSProperties = {
|
|||||||
border: '1px solid hsl(var(--glass-border) / 0.2)',
|
border: '1px solid hsl(var(--glass-border) / 0.2)',
|
||||||
boxShadow: '0 18px 36px -28px hsl(var(--primary) / 0.24)',
|
boxShadow: '0 18px 36px -28px hsl(var(--primary) / 0.24)',
|
||||||
};
|
};
|
||||||
const layoutAmbientLeftStyle: CSSProperties = {
|
const layoutFlowFieldStyle: CSSProperties = {
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
'radial-gradient(circle at left top, hsl(var(--nav-ambient) / 0.34) 0%, hsl(var(--nav-ambient) / 0.14) 28%, transparent 72%)',
|
'radial-gradient(ellipse 14% 18% at 5% 3%, hsl(var(--nav-flow-core) / 0.34) 0%, hsl(var(--nav-flow-core) / 0.14) 34%, transparent 74%), radial-gradient(ellipse 48% 18% at 16% 8%, hsl(var(--nav-flow-core) / 0.12) 0%, transparent 68%), radial-gradient(ellipse 68% 34% at 28% 18%, hsl(var(--nav-flow-secondary) / 0.1) 0%, transparent 70%), linear-gradient(122deg, hsl(var(--nav-flow-trace) / 0.13) 0%, hsl(var(--nav-flow-trace) / 0.1) 8%, hsl(var(--nav-flow-core) / 0.08) 14%, transparent 24%, transparent 42%, hsl(var(--nav-flow-secondary) / 0.06) 58%, hsl(var(--nav-flow-trace) / 0.04) 70%, transparent 88%), radial-gradient(ellipse 84% 34% at 34% 26%, hsl(var(--nav-flow-core) / 0.08) 0%, transparent 72%), radial-gradient(ellipse 74% 30% at 48% 38%, hsl(var(--nav-flow-secondary) / 0.06) 0%, transparent 74%), radial-gradient(ellipse 62% 26% at 60% 48%, hsl(var(--nav-flow-accent) / 0.04) 0%, transparent 76%)',
|
||||||
};
|
};
|
||||||
const layoutAmbientRightStyle: CSSProperties = {
|
const layoutFlowGlowStyle: CSSProperties = {
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
'radial-gradient(circle at left top, hsl(var(--nav-ambient-secondary) / 0.14) 0%, transparent 72%)',
|
'radial-gradient(ellipse 18% 20% at 6% 4%, hsl(var(--nav-flow-core) / 0.18) 0%, transparent 74%), radial-gradient(ellipse 72% 28% at 26% 16%, hsl(var(--nav-flow-trace) / 0.08) 0%, transparent 70%), radial-gradient(ellipse 68% 26% at 44% 28%, hsl(var(--nav-flow-secondary) / 0.05) 0%, transparent 72%), radial-gradient(ellipse 58% 22% at 62% 42%, hsl(var(--nav-flow-trace) / 0.04) 0%, transparent 74%)',
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@@ -335,6 +334,11 @@ const headerZIndex = computed(() => {
|
|||||||
const headerWrapperStyle = computed((): CSSProperties => {
|
const headerWrapperStyle = computed((): CSSProperties => {
|
||||||
const fixed = headerFixed.value;
|
const fixed = headerFixed.value;
|
||||||
return {
|
return {
|
||||||
|
backdropFilter: 'blur(calc(var(--glass-blur) * 1.1)) saturate(158%)',
|
||||||
|
backgroundColor: 'hsl(var(--glass-tint) / 0.18)',
|
||||||
|
backgroundImage:
|
||||||
|
'linear-gradient(180deg, hsl(var(--nav-surface) / 0.22), hsl(var(--glass-tint) / 0.08))',
|
||||||
|
boxShadow: 'inset 0 -1px 0 hsl(var(--nav-border) / 0.05)',
|
||||||
height: isFullContent.value ? '0' : `${headerWrapperHeight.value}px`,
|
height: isFullContent.value ? '0' : `${headerWrapperHeight.value}px`,
|
||||||
left: isMixedNav.value ? 0 : mainStyle.value.sidebarAndExtraWidth,
|
left: isMixedNav.value ? 0 : mainStyle.value.sidebarAndExtraWidth,
|
||||||
position: fixed ? 'fixed' : 'static',
|
position: fixed ? 'fixed' : 'static',
|
||||||
@@ -501,8 +505,8 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative flex min-h-full w-full">
|
<div class="relative flex min-h-full w-full">
|
||||||
<div class="pointer-events-none absolute inset-0 overflow-hidden">
|
<div class="pointer-events-none absolute inset-0 overflow-hidden">
|
||||||
<div :style="layoutAmbientLeftStyle" class="absolute -left-16 top-0 h-[320px] w-[320px] rounded-full blur-3xl"></div>
|
<div :style="layoutFlowFieldStyle" class="absolute inset-0"></div>
|
||||||
<div :style="layoutAmbientRightStyle" class="absolute right-[-120px] top-[-40px] h-[260px] w-[320px] rounded-full blur-3xl"></div>
|
<div :style="layoutFlowGlowStyle" class="absolute inset-0 blur-3xl"></div>
|
||||||
</div>
|
</div>
|
||||||
<LayoutSidebar
|
<LayoutSidebar
|
||||||
v-if="sidebarEnableState"
|
v-if="sidebarEnableState"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { BreadcrumbProps } from './types';
|
import type {BreadcrumbProps} from './types';
|
||||||
|
|
||||||
import { ChevronDown } from '@easyflow-core/icons';
|
import {ChevronDown} from '@easyflow-core/icons';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '../../ui';
|
} from '../../ui';
|
||||||
import { EasyFlowIcon } from '../icon';
|
import {EasyFlowIcon} from '../icon';
|
||||||
|
|
||||||
interface Props extends BreadcrumbProps {}
|
interface Props extends BreadcrumbProps {}
|
||||||
|
|
||||||
@@ -45,7 +45,11 @@ function handleClick(path?: string) {
|
|||||||
<div v-if="item.items?.length ?? 0 > 0">
|
<div v-if="item.items?.length ?? 0 > 0">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger class="easyflow-breadcrumb__link">
|
<DropdownMenuTrigger class="easyflow-breadcrumb__link">
|
||||||
<EasyFlowIcon v-if="showIcon" :icon="item.icon" class="size-4" />
|
<EasyFlowIcon
|
||||||
|
v-if="showIcon"
|
||||||
|
:icon="item.icon"
|
||||||
|
class="size-4"
|
||||||
|
/>
|
||||||
<span class="max-w-[180px] truncate">{{ item.title }}</span>
|
<span class="max-w-[180px] truncate">{{ item.title }}</span>
|
||||||
<ChevronDown class="size-3.5" />
|
<ChevronDown class="size-3.5" />
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
@@ -77,10 +81,7 @@ function handleClick(path?: string) {
|
|||||||
<span class="max-w-[180px] truncate">{{ item.title }}</span>
|
<span class="max-w-[180px] truncate">{{ item.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
</BreadcrumbLink>
|
</BreadcrumbLink>
|
||||||
<BreadcrumbPage
|
<BreadcrumbPage v-else class="easyflow-breadcrumb__current">
|
||||||
v-else
|
|
||||||
class="easyflow-breadcrumb__current"
|
|
||||||
>
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<EasyFlowIcon
|
<EasyFlowIcon
|
||||||
v-if="showIcon"
|
v-if="showIcon"
|
||||||
@@ -130,23 +131,55 @@ function handleClick(path?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.easyflow-breadcrumb__current {
|
.easyflow-breadcrumb__current {
|
||||||
|
position: relative;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 4px 10px;
|
padding: 4px 8px 6px;
|
||||||
color: hsl(var(--breadcrumb-current));
|
color: hsl(var(--breadcrumb-current));
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.01em;
|
letter-spacing: 0.01em;
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easyflow-breadcrumb__current::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: -8px -10px -2px;
|
||||||
|
z-index: -1;
|
||||||
|
content: '';
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
135deg,
|
180deg,
|
||||||
hsl(var(--nav-item-active) / 0.88),
|
hsl(var(--nav-item-active) / 0.36) 0%,
|
||||||
hsl(var(--glass-tint) / 0.92)
|
hsl(var(--nav-item-active) / 0.18) 42%,
|
||||||
|
hsl(var(--glass-tint) / 0.08) 82%,
|
||||||
|
transparent 100%
|
||||||
);
|
);
|
||||||
border-radius: 999px;
|
border-radius: 14px 14px 10px 10px;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
inset 0 1px 0 hsl(var(--nav-sheen) / 0.42),
|
inset 1px 0 0 hsl(var(--nav-border) / 0.54),
|
||||||
0 10px 22px -18px hsl(var(--primary) / 0.22);
|
inset -1px 0 0 hsl(var(--nav-border) / 0.54),
|
||||||
|
inset 0 1px 0 hsl(var(--nav-sheen) / 0.68),
|
||||||
|
0 10px 22px -22px hsl(var(--primary) / 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.easyflow-breadcrumb__current::after {
|
||||||
|
position: absolute;
|
||||||
|
right: -6px;
|
||||||
|
bottom: 0;
|
||||||
|
left: -6px;
|
||||||
|
height: 1px;
|
||||||
|
z-index: -1;
|
||||||
|
content: '';
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent,
|
||||||
|
hsl(var(--nav-item-active) / 0.2) 22%,
|
||||||
|
hsl(var(--nav-item-active) / 0.26) 50%,
|
||||||
|
hsl(var(--nav-item-active) / 0.2) 78%,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
opacity: 0.72;
|
||||||
}
|
}
|
||||||
|
|
||||||
.easyflow-breadcrumb__separator {
|
.easyflow-breadcrumb__separator {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { TabDefinition } from '@easyflow-core/typings';
|
import type {TabDefinition} from '@easyflow-core/typings';
|
||||||
|
|
||||||
import type { TabConfig, TabsProps } from '../../types';
|
import type {TabConfig, TabsProps} from '../../types';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
import {computed, ref} from 'vue';
|
||||||
|
|
||||||
import { Pin, X } from '@easyflow-core/icons';
|
import {Pin, X} from '@easyflow-core/icons';
|
||||||
import { EasyFlowContextMenu, EasyFlowIcon } from '@easyflow-core/shadcn-ui';
|
import {EasyFlowContextMenu, EasyFlowIcon} from '@easyflow-core/shadcn-ui';
|
||||||
|
|
||||||
interface Props extends TabsProps {}
|
interface Props extends TabsProps {}
|
||||||
|
|
||||||
@@ -110,7 +110,8 @@ function onMouseDown(e: MouseEvent, tab: TabConfig) {
|
|||||||
class="tabs-chrome__background absolute inset-0 z-[-1] px-0 py-0 transition-opacity duration-150"
|
class="tabs-chrome__background absolute inset-0 z-[-1] px-0 py-0 transition-opacity duration-150"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="tabs-chrome__background-content h-[calc(100%-8px)] rounded-2xl bg-transparent transition-all duration-150 group-[.is-active]:bg-[hsl(var(--glass-tint))/0.74] group-[.is-active]:shadow-[0_16px_28px_-24px_hsl(var(--foreground)/0.34)]"
|
:class="{ 'is-active': tab.key === active }"
|
||||||
|
class="tabs-chrome__background-content h-[calc(100%-8px)] rounded-2xl bg-transparent transition-all duration-150"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -141,7 +142,9 @@ function onMouseDown(e: MouseEvent, tab: TabConfig) {
|
|||||||
class="mr-2 flex size-[15px] items-center overflow-hidden"
|
class="mr-2 flex size-[15px] items-center overflow-hidden"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span class="flex-1 overflow-hidden whitespace-nowrap text-[13px]">
|
<span
|
||||||
|
class="flex-1 overflow-hidden whitespace-nowrap text-[13px]"
|
||||||
|
>
|
||||||
{{ tab.title }}
|
{{ tab.title }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,6 +157,26 @@ function onMouseDown(e: MouseEvent, tab: TabConfig) {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.tabs-chrome {
|
.tabs-chrome {
|
||||||
|
&__background-content {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__background-content.is-active::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: 1px -5px -1px;
|
||||||
|
content: '';
|
||||||
|
background: hsl(var(--background));
|
||||||
|
border-radius: 15px 15px 0 0;
|
||||||
|
border-top: 2px solid hsl(var(--primary) / 0.38);
|
||||||
|
border-right: 1px solid hsl(var(--primary) / 0.16);
|
||||||
|
border-left: 1px solid hsl(var(--primary) / 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__background-content.is-active::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
&__item:not(.dragging) {
|
&__item:not(.dragging) {
|
||||||
@apply cursor-pointer;
|
@apply cursor-pointer;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { TabsEmits, TabsProps } from './types';
|
import type {TabsEmits, TabsProps} from './types';
|
||||||
|
|
||||||
import { useForwardPropsEmits } from '@easyflow-core/composables';
|
import {useForwardPropsEmits} from '@easyflow-core/composables';
|
||||||
import { ChevronLeft, ChevronRight } from '@easyflow-core/icons';
|
import {ChevronLeft, ChevronRight} from '@easyflow-core/icons';
|
||||||
import { EasyFlowScrollbar } from '@easyflow-core/shadcn-ui';
|
import {EasyFlowScrollbar} from '@easyflow-core/shadcn-ui';
|
||||||
|
|
||||||
import { Tabs, TabsChrome } from './components';
|
import {Tabs, TabsChrome} from './components';
|
||||||
import { useTabsDrag } from './use-tabs-drag';
|
import {useTabsDrag} from './use-tabs-drag';
|
||||||
import { useTabsViewScroll } from './use-tabs-view-scroll';
|
import {useTabsViewScroll} from './use-tabs-view-scroll';
|
||||||
|
|
||||||
interface Props extends TabsProps {}
|
interface Props extends TabsProps {}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ useTabsDrag(props, emit);
|
|||||||
'cursor-pointer text-[hsl(var(--nav-item-muted-foreground))] hover:bg-[hsl(var(--surface-contrast-soft))] hover:text-foreground': !scrollIsAtLeft,
|
'cursor-pointer text-[hsl(var(--nav-item-muted-foreground))] hover:bg-[hsl(var(--surface-contrast-soft))] hover:text-foreground': !scrollIsAtLeft,
|
||||||
'pointer-events-none opacity-30': scrollIsAtLeft,
|
'pointer-events-none opacity-30': scrollIsAtLeft,
|
||||||
}"
|
}"
|
||||||
class="mx-1 my-1 flex items-center rounded-2xl border border-transparent bg-[hsl(var(--glass-tint))/0.44] px-2 shadow-[0_10px_24px_-24px_hsl(var(--foreground)/0.3)] backdrop-blur-xl transition-[background-color,color,transform]"
|
class="mx-1 my-1 flex items-center rounded-2xl border border-transparent bg-[hsl(var(--glass-tint))/0.18] px-2 shadow-none backdrop-blur-xl transition-[background-color,color,transform]"
|
||||||
@click="scrollDirection('left')"
|
@click="scrollDirection('left')"
|
||||||
>
|
>
|
||||||
<ChevronLeft class="size-4 h-full" />
|
<ChevronLeft class="size-4 h-full" />
|
||||||
@@ -97,7 +97,7 @@ useTabsDrag(props, emit);
|
|||||||
'cursor-pointer text-[hsl(var(--nav-item-muted-foreground))] hover:bg-[hsl(var(--surface-contrast-soft))] hover:text-foreground': !scrollIsAtRight,
|
'cursor-pointer text-[hsl(var(--nav-item-muted-foreground))] hover:bg-[hsl(var(--surface-contrast-soft))] hover:text-foreground': !scrollIsAtRight,
|
||||||
'pointer-events-none opacity-30': scrollIsAtRight,
|
'pointer-events-none opacity-30': scrollIsAtRight,
|
||||||
}"
|
}"
|
||||||
class="mx-1 my-1 flex items-center rounded-2xl border border-transparent bg-[hsl(var(--glass-tint))/0.44] px-2 shadow-[0_10px_24px_-24px_hsl(var(--foreground)/0.3)] backdrop-blur-xl transition-[background-color,color,transform]"
|
class="mx-1 my-1 flex items-center rounded-2xl border border-transparent bg-[hsl(var(--glass-tint))/0.18] px-2 shadow-none backdrop-blur-xl transition-[background-color,color,transform]"
|
||||||
@click="scrollDirection('right')"
|
@click="scrollDirection('right')"
|
||||||
>
|
>
|
||||||
<ChevronRight class="size-4 h-full" />
|
<ChevronRight class="size-4 h-full" />
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { CSSProperties } from 'vue';
|
import type {CSSProperties} from 'vue';
|
||||||
|
import {computed, useSlots} from 'vue';
|
||||||
|
|
||||||
import { computed, useSlots } from 'vue';
|
import {useRefresh} from '@easyflow/hooks';
|
||||||
|
import {RotateCw} from '@easyflow/icons';
|
||||||
|
import {preferences, usePreferences} from '@easyflow/preferences';
|
||||||
|
import {useAccessStore} from '@easyflow/stores';
|
||||||
|
|
||||||
import { useRefresh } from '@easyflow/hooks';
|
import {EasyFlowFullScreen, EasyFlowIconButton} from '@easyflow-core/shadcn-ui';
|
||||||
import { RotateCw } from '@easyflow/icons';
|
|
||||||
import { preferences, usePreferences } from '@easyflow/preferences';
|
|
||||||
import { useAccessStore } from '@easyflow/stores';
|
|
||||||
|
|
||||||
import { EasyFlowFullScreen, EasyFlowIconButton } from '@easyflow-core/shadcn-ui';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GlobalSearch,
|
GlobalSearch,
|
||||||
@@ -45,17 +44,6 @@ const toolbarButtonStyle: CSSProperties = {
|
|||||||
border: '1px solid transparent',
|
border: '1px solid transparent',
|
||||||
boxShadow: '0 18px 36px -28px hsl(var(--primary) / 0.24)',
|
boxShadow: '0 18px 36px -28px hsl(var(--primary) / 0.24)',
|
||||||
};
|
};
|
||||||
const breadcrumbShellStyle: CSSProperties = {
|
|
||||||
backgroundColor: 'hsl(var(--glass-tint) / 0.66)',
|
|
||||||
backgroundImage:
|
|
||||||
'linear-gradient(180deg, hsl(var(--glass-tint) / 0.8), hsl(var(--nav-surface-subtle) / 0.58))',
|
|
||||||
border: '1px solid transparent',
|
|
||||||
boxShadow:
|
|
||||||
'inset 0 1px 0 hsl(var(--nav-sheen) / 0.42), 0 18px 36px -30px hsl(var(--primary) / 0.14)',
|
|
||||||
};
|
|
||||||
const rightShellStyle: CSSProperties = {
|
|
||||||
backgroundColor: 'hsl(var(--glass-tint) / 0.28)',
|
|
||||||
};
|
|
||||||
|
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
const { globalSearchShortcutKey, preferencesButtonPosition } = usePreferences();
|
const { globalSearchShortcutKey, preferencesButtonPosition } = usePreferences();
|
||||||
@@ -144,8 +132,7 @@ function clearPreferencesAndLogout() {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="$slots.breadcrumb"
|
v-if="$slots.breadcrumb"
|
||||||
:style="breadcrumbShellStyle"
|
class="hidden min-w-0 max-w-[min(520px,46vw)] items-center px-2 py-1 lg:flex"
|
||||||
class="hidden min-w-0 max-w-[min(520px,46vw)] items-center rounded-full px-3 py-1.5 lg:flex"
|
|
||||||
>
|
>
|
||||||
<slot name="breadcrumb"></slot>
|
<slot name="breadcrumb"></slot>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,11 +151,11 @@ function clearPreferencesAndLogout() {
|
|||||||
</template>
|
</template>
|
||||||
<div
|
<div
|
||||||
:class="`menu-align-${preferences.header.menuAlign}`"
|
:class="`menu-align-${preferences.header.menuAlign}`"
|
||||||
class="flex h-full min-w-0 flex-1 items-center px-3"
|
class="flex h-full min-w-0 flex-1 items-center px-2"
|
||||||
>
|
>
|
||||||
<slot name="menu"></slot>
|
<slot name="menu"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div :style="rightShellStyle" class="flex h-full min-w-0 flex-shrink-0 items-center gap-1 rounded-full px-2 py-1 pl-2">
|
<div class="flex h-full min-w-0 flex-shrink-0 items-center gap-1 px-1 py-1">
|
||||||
<template v-for="slot in rightSlots" :key="slot.name">
|
<template v-for="slot in rightSlots" :key="slot.name">
|
||||||
<slot :name="slot.name">
|
<slot :name="slot.name">
|
||||||
<template v-if="slot.name === 'global-search'">
|
<template v-if="slot.name === 'global-search'">
|
||||||
|
|||||||
Reference in New Issue
Block a user