chore: bump version to 0.3.11 — 状态栏扩展点架构
This commit is contained in:
@@ -1,81 +1,39 @@
|
||||
import React from 'react'
|
||||
import { useTabStore } from '../../stores/tabStore'
|
||||
import { useEditorStore } from '../../stores/editorStore'
|
||||
import { useDocStats } from '../../hooks/useDocStats'
|
||||
import { getFileName } from '../../lib/fileUtils'
|
||||
import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useStatusBarStore, type StatusBarItem } from '../../stores/statusBarStore'
|
||||
|
||||
interface StatusBarProps {
|
||||
isAutoSaving?: boolean
|
||||
autoSaveEnabled?: boolean
|
||||
onToggleAutoSave?: () => void
|
||||
}
|
||||
/**
|
||||
* StatusBar — 纯渲染层
|
||||
*
|
||||
* 不包含任何业务逻辑。所有状态栏项由各功能模块通过
|
||||
* useStatusBarStore.register() 注入,本组件按 alignment + priority 排序渲染。
|
||||
* 每个 item.Component 是独立 React 组件,可自由使用 hooks。
|
||||
*/
|
||||
|
||||
export const StatusBar = React.memo(function StatusBar({
|
||||
isAutoSaving = false,
|
||||
autoSaveEnabled = true,
|
||||
onToggleAutoSave
|
||||
}: StatusBarProps) {
|
||||
const activeTab = useTabStore(s => s.getActiveTab())
|
||||
const loadingStates = useEditorStore(s => s.loadingStates)
|
||||
const stats = useDocStats(activeTab?.content)
|
||||
export const StatusBar = React.memo(function StatusBar() {
|
||||
const items = useStatusBarStore(s => s.items)
|
||||
|
||||
// 判断是否有任何加载状态激活
|
||||
const hasLoading = Object.values(loadingStates).some(Boolean)
|
||||
const loadingLabel = loadingStates['file-open']
|
||||
? '正在打开文件...'
|
||||
: loadingStates['dir-load']
|
||||
? '正在加载目录...'
|
||||
: loadingStates['markdown-render']
|
||||
? '正在渲染...'
|
||||
: ''
|
||||
const sorted = useMemo(() => {
|
||||
const list = Object.values(items)
|
||||
if (list.length === 0) return { left: [] as StatusBarItem[], right: [] as StatusBarItem[] }
|
||||
const byAlignment = (a: 'left' | 'right') =>
|
||||
list.filter(i => i.alignment === a).sort((x, y) => x.priority - y.priority)
|
||||
return { left: byAlignment('left'), right: byAlignment('right') }
|
||||
}, [items])
|
||||
|
||||
return (
|
||||
<div id="statusbar" role="status" aria-live="polite">
|
||||
<div className="status-left">
|
||||
{hasLoading && (
|
||||
<span className="status-loading" aria-busy="true">
|
||||
<LoadingSpinner size="small" />
|
||||
<span>{loadingLabel}</span>
|
||||
</span>
|
||||
)}
|
||||
<span id="status-text">
|
||||
{activeTab ? (activeTab.filePath ? getFileName(activeTab.filePath) : '未命名') : '就绪'}
|
||||
</span>
|
||||
{sorted.left.map(({ id, Component }) => (
|
||||
<Component key={id} />
|
||||
))}
|
||||
</div>
|
||||
<div className="status-right">
|
||||
{activeTab && (
|
||||
<>
|
||||
<span className="status-stat" title="行数" aria-label={`${stats.lines} 行`}>
|
||||
{stats.lines} 行
|
||||
</span>
|
||||
<span className="status-divider">|</span>
|
||||
<span className="status-stat" title="单词数" aria-label={`${stats.words} 个单词`}>
|
||||
{stats.words} 词
|
||||
</span>
|
||||
<span className="status-divider">|</span>
|
||||
<span className="status-stat" title="字符数(含空格)" aria-label={`${stats.chars} 个字符`}>
|
||||
{stats.chars} 字符
|
||||
</span>
|
||||
<span className="status-divider">|</span>
|
||||
</>
|
||||
)}
|
||||
{activeTab?.filePath && onToggleAutoSave !== undefined && (
|
||||
<>
|
||||
<button
|
||||
className={`status-auto-save${isAutoSaving ? ' saving' : ''}`}
|
||||
title={isAutoSaving ? '正在自动保存...' : (autoSaveEnabled ? '自动保存已开启 — 点击关闭' : '自动保存已关闭 — 点击开启')}
|
||||
aria-label={isAutoSaving ? '正在自动保存' : (autoSaveEnabled ? '关闭自动保存' : '开启自动保存')}
|
||||
onClick={onToggleAutoSave}
|
||||
>
|
||||
{isAutoSaving ? '保存中...' : (autoSaveEnabled ? '自动' : '手动')}
|
||||
</button>
|
||||
<span className="status-divider">|</span>
|
||||
</>
|
||||
)}
|
||||
<span id="status-encoding">UTF-8</span>
|
||||
<span className="status-divider">|</span>
|
||||
<span id="status-lang">Markdown</span>
|
||||
{sorted.right.map(({ id, Component }, i) => (
|
||||
<React.Fragment key={id}>
|
||||
{i > 0 && <span className="status-divider">|</span>}
|
||||
<Component />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user