A:添加动态选择动效
This commit is contained in:
1 parent
2b182b6d79
commit
5c2b7d03f7
2 files changed
+114
-36
No files matched your search
@@ -23,15 +23,19 @@
|
||||
ref="appListRef"
|
||||
tabindex="0"
|
||||
@keydown="handleKeyDown"
|
||||
@blur="handleListBlur"
|
||||
>
|
||||
<div
|
||||
v-for="(app, index) in appInfos"
|
||||
:key="app.Id"
|
||||
:ref="el => setAppItemRef(el, index)"
|
||||
class="app-item"
|
||||
:class="{ 'app-item--selected': selectedApp?.Id === app.Id }"
|
||||
:class="{
|
||||
'app-item--selected': selectedApp?.Id === app.Id,
|
||||
'app-item--focused': focusedIndex === index
|
||||
}"
|
||||
@click="selectApp(app)"
|
||||
@mouseenter="handleMouseEnter"
|
||||
@mouseenter="handleMouseEnter(index)"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<!-- 书角折叠效果 -->
|
||||
@@ -111,6 +115,7 @@ const { appInfos, selectedApp } = storeToRefs(applicationStore)
|
||||
// Refs
|
||||
const appListRef = ref<HTMLDivElement>()
|
||||
const appItemRefs = ref<(HTMLDivElement | null)[]>([])
|
||||
const focusedIndex = ref<number>(-1) // 添加聚焦索引
|
||||
|
||||
// 设置应用项目的 ref
|
||||
const setAppItemRef = (el: unknown, index: number) => {
|
||||
@@ -149,6 +154,9 @@ onMounted(() => {
|
||||
watch(
|
||||
appInfos,
|
||||
async (newAppInfos) => {
|
||||
// 重置聚焦索引
|
||||
// focusedIndex.value = -1
|
||||
|
||||
// 如果有应用且当前没有选中的应用
|
||||
if (newAppInfos.length > 0 && !selectedApp.value) {
|
||||
await nextTick()
|
||||
@@ -194,56 +202,75 @@ onUnmounted(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (appInfos.value.length === 0) return
|
||||
|
||||
const currentIndex = selectedApp.value
|
||||
? appInfos.value.findIndex(app => app.Id === selectedApp.value?.Id)
|
||||
: -1
|
||||
|
||||
let newIndex = currentIndex
|
||||
let newFocusedIndex = focusedIndex.value
|
||||
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
event.preventDefault()
|
||||
// 如果当前没有选中或者是第一个,则选中最后一个
|
||||
if (currentIndex <= 0) {
|
||||
newIndex = appInfos.value.length - 1
|
||||
// 如果当前没有聚焦,从选中项开始
|
||||
if (focusedIndex.value === -1 && selectedApp.value) {
|
||||
const selectedIndex = appInfos.value.findIndex(app => app.Id === selectedApp.value?.Id)
|
||||
newFocusedIndex = selectedIndex > 0 ? selectedIndex - 1 : appInfos.value.length - 1
|
||||
}
|
||||
// 如果没有聚焦也没有选中,从最后一个开始
|
||||
else if (focusedIndex.value === -1) {
|
||||
newFocusedIndex = appInfos.value.length - 1
|
||||
}
|
||||
// 正常向上移动
|
||||
else if (focusedIndex.value === 0) {
|
||||
newFocusedIndex = appInfos.value.length - 1
|
||||
} else {
|
||||
newIndex = currentIndex - 1
|
||||
newFocusedIndex = focusedIndex.value - 1
|
||||
}
|
||||
break
|
||||
|
||||
case 'ArrowDown':
|
||||
event.preventDefault()
|
||||
// 如果当前没有选中或者是最后一个,则选中第一个
|
||||
if (currentIndex === -1 || currentIndex >= appInfos.value.length - 1) {
|
||||
newIndex = 0
|
||||
// 如果当前没有聚焦,从选中项开始
|
||||
if (focusedIndex.value === -1 && selectedApp.value) {
|
||||
const selectedIndex = appInfos.value.findIndex(app => app.Id === selectedApp.value?.Id)
|
||||
newFocusedIndex = selectedIndex < appInfos.value.length - 1 ? selectedIndex + 1 : 0
|
||||
}
|
||||
// 如果没有聚焦也没有选中,从第一个开始
|
||||
else if (focusedIndex.value === -1) {
|
||||
newFocusedIndex = 0
|
||||
}
|
||||
// 正常向下移动
|
||||
else if (focusedIndex.value >= appInfos.value.length - 1) {
|
||||
newFocusedIndex = 0
|
||||
} else {
|
||||
newIndex = currentIndex + 1
|
||||
newFocusedIndex = focusedIndex.value + 1
|
||||
}
|
||||
break
|
||||
|
||||
case 'Enter':
|
||||
event.preventDefault()
|
||||
// 如果有选中的应用,可以在这里触发某些操作
|
||||
if (selectedApp.value) {
|
||||
console.log('Enter pressed on:', selectedApp.value.ProductName)
|
||||
// 可以触发一个事件或执行某个操作
|
||||
// 如果有聚焦的应用,选中它
|
||||
if (focusedIndex.value >= 0 && focusedIndex.value < appInfos.value.length) {
|
||||
selectApp(appInfos.value[focusedIndex.value])
|
||||
}
|
||||
break
|
||||
|
||||
case 'Escape':
|
||||
event.preventDefault()
|
||||
// ESC 键清除聚焦
|
||||
focusedIndex.value = -1
|
||||
break
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// 选择新的应用
|
||||
if (newIndex !== currentIndex && newIndex >= 0 && newIndex < appInfos.value.length) {
|
||||
selectApp(appInfos.value[newIndex])
|
||||
// 更新聚焦索引
|
||||
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
||||
focusedIndex.value = newFocusedIndex
|
||||
// 滚动到可见区域
|
||||
scrollToSelectedItem(newIndex)
|
||||
scrollToFocusedItem(newFocusedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动到选中的项目
|
||||
const scrollToSelectedItem = (index: number) => {
|
||||
// 滚动到聚焦的项目
|
||||
const scrollToFocusedItem = (index: number) => {
|
||||
nextTick(() => {
|
||||
const itemEl = appItemRefs.value[index]
|
||||
if (itemEl && appListRef.value) {
|
||||
@@ -261,6 +288,14 @@ const scrollToSelectedItem = (index: number) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 列表失去焦点时的处理
|
||||
const handleListBlur = () => {
|
||||
// 延迟清除聚焦,避免点击列表项时立即清除
|
||||
// setTimeout(() => {
|
||||
// focusedIndex.value = -1
|
||||
// }, 200)
|
||||
}
|
||||
|
||||
// 格式化内存显示
|
||||
const formatMemory = (memoryInBytes: number): string => {
|
||||
const result = convertFileSize(memoryInBytes, FILE_SIZE_UNIT_ENUM.B)
|
||||
@@ -278,15 +313,26 @@ const getFirstChar = (name: string): string => {
|
||||
// 选择应用
|
||||
const selectApp = (app: ApplicationType) => {
|
||||
applicationStore.setSelectedApp(app)
|
||||
// 选中后,如果是通过键盘选中的,保持聚焦在当前项
|
||||
// 如果是通过鼠标点击的,会在 mouseLeave 时自动清除聚焦
|
||||
const selectedIndex = appInfos.value.findIndex(a => a.Id === app.Id)
|
||||
if (focusedIndex.value === selectedIndex) {
|
||||
// 通过键盘选中的,保持聚焦
|
||||
} else {
|
||||
// 通过鼠标选中的,清除聚焦
|
||||
focusedIndex.value = -1
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标悬停事件(预留扩展)
|
||||
const handleMouseEnter = () => {
|
||||
// 可以在这里添加额外的悬停逻辑
|
||||
// 鼠标悬停事件
|
||||
const handleMouseEnter = (index: number) => {
|
||||
// 鼠标悬停时更新聚焦索引
|
||||
focusedIndex.value = index
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
// 可以在这里添加额外的离开逻辑
|
||||
// 鼠标离开时清除聚焦
|
||||
focusedIndex.value = -1
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -369,12 +415,6 @@ const handleMouseLeave = () => {
|
||||
outline: none; /* 移除聚焦时的默认轮廓 */
|
||||
}
|
||||
|
||||
/* 聚焦时的样式 */
|
||||
.app-item:focus {
|
||||
box-shadow: inset 0 0 0 1px var(--accent-primary);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* 自定义滚动条样式 */
|
||||
.app-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
@@ -435,6 +475,27 @@ const handleMouseLeave = () => {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
/* 键盘导航聚焦状态 */
|
||||
.app-item--focused {
|
||||
border-color: var(--accent-primary);
|
||||
background: var(--bg-hover, var(--bg-card));
|
||||
box-shadow:
|
||||
inset 0 0 0 2px var(--accent-primary),
|
||||
0 4px 12px rgba(59, 130, 246, 0.2);
|
||||
transform: translateY(-1px);
|
||||
animation: focusPulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 同时聚焦和选中的状态 */
|
||||
.app-item--focused.app-item--selected {
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow:
|
||||
inset 0 0 0 2px var(--accent-primary),
|
||||
0 0 0 4px var(--monitor-accent-primary-alpha),
|
||||
0 8px 25px -5px rgba(59, 130, 246, 0.3);
|
||||
animation: none;
|
||||
}
|
||||
|
||||
/* 选中元素的左侧强调边框 */
|
||||
.app-item--selected::before {
|
||||
content: '';
|
||||
@@ -672,6 +733,24 @@ const handleMouseLeave = () => {
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes focusPulse {
|
||||
0% {
|
||||
box-shadow:
|
||||
inset 0 0 0 2px var(--accent-primary),
|
||||
0 4px 12px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
50% {
|
||||
box-shadow:
|
||||
inset 0 0 0 2px var(--accent-primary),
|
||||
0 4px 16px rgba(59, 130, 246, 0.35);
|
||||
}
|
||||
100% {
|
||||
box-shadow:
|
||||
inset 0 0 0 2px var(--accent-primary),
|
||||
0 4px 12px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar-content {
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
<!-- 右侧检查器面板 -->
|
||||
<MonitorInspector
|
||||
v-show="false"
|
||||
:width="inspectorWidth"
|
||||
:selectedConnection="selectedConnection"
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user