✨ feat(vscode): 代码阅读 React 组件卡片按钩子分区:状态 / 引用 / 读取的数据 / 派生值 / 副作用 / 事件处理;副作用、处理函数成子卡片放进卡片那一行下面(库规则新增把子块放进属性表某一行的机制,事件处理表点名称展开)
This commit is contained in:
1 parent
15f3a6600d
commit
ee807ec7a4
19 files changed
+1057
-56
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import type { CSSProperties, ReactElement, ReactNode } from 'react'
|
||||
import type { DiffTree, FoldedRuns, PlacedRemoved } from './diffView'
|
||||
import type { GridSlots } from './PropertyGrid'
|
||||
import type { QaState } from './qa'
|
||||
import type { PendingImage } from './qaImages'
|
||||
import type { BlockMarks, BlockView } from './types'
|
||||
@@ -34,6 +35,8 @@ export interface BlockTreeState {
|
||||
onFocused: () => void
|
||||
onToggleEdit: (id: string) => void
|
||||
onToggleFold: (id: string) => void
|
||||
/** 一次记下几块、几行折叠还是展开(点子行展开一块、同一行里别的收起) */
|
||||
onSetFolds: (folds: Record<string, boolean>) => void
|
||||
/** 问 AI;文件不在工作区文件夹里(问答没地方存)时没有 */
|
||||
qa?: QaTree
|
||||
/** 代码差异:新增、修改的块和删掉的块;侧栏开着「未提交的更改」或审查工单、基准里有这个文件时才有 */
|
||||
@@ -56,9 +59,12 @@ export interface QaTree {
|
||||
onSelectProfile: (id: string) => void
|
||||
}
|
||||
|
||||
/** 块下面有没有画出来的子块(删掉的子块也算):子块都被筛掉、被盖住时不留空的一层、不给折叠箭头。 */
|
||||
/**
|
||||
* 块下面有没有画出来的子块(删掉的子块也算):子块都被筛掉、被盖住时不留空的一层、不给折叠箭头。
|
||||
* 放进卡片属性表里的子块(BlockSlot)画在卡片里,不算。
|
||||
*/
|
||||
function hasShownChildren(block: BlockView, tree: BlockTreeState): boolean {
|
||||
return block.children.some(child => shown(child, tree)) || (!tree.covered.has(block.id) && tree.diff?.removed.has(block.id) === true)
|
||||
return block.children.some(child => !child.slot && shown(child, tree)) || (!tree.covered.has(block.id) && tree.diff?.removed.has(block.id) === true)
|
||||
}
|
||||
|
||||
const CHANGE_NAMES = { add: '新增', mod: '修改', del: '删除' } as const
|
||||
@@ -110,13 +116,20 @@ function kindProps(block: BlockView | undefined): { 'data-kind'?: string, 'data-
|
||||
* 一层代码块:左边一条竖线标出这一层,每块的子块紧跟在它下面、再往右缩进一层。被筛掉、被盖住的块不画,不留空位,竖线跟着变短。
|
||||
* owner 是这一层所属的块(顶层没有),悬停它时竖线换成它的种类色。
|
||||
* 标差异时每张卡片左边外侧一条改动色条,删掉的块收成一行放回原来的位置(见 RemovedCard);「只看改动」时连着没变的块折成一条(UnchangedStrip)。
|
||||
* slotted:这一层是画在卡片属性表某一行下面的子块(BlockSlot),blocks 只有那几块。
|
||||
*/
|
||||
export function BlockLevel({ blocks, owner, tree }: { blocks: BlockView[], owner?: BlockView, tree: BlockTreeState }): ReactElement {
|
||||
const removed = tree.diff?.removed.get(owner?.id ?? '') ?? []
|
||||
// 删掉的块排在它原来前面那块后面;那块被筛掉了也照排,不跟着消失
|
||||
export function BlockLevel({ blocks, owner, tree, slotted = false }: { blocks: BlockView[], owner?: BlockView, tree: BlockTreeState, slotted?: boolean }): ReactElement {
|
||||
// 放进卡片属性表里的子块画在卡片里(slotted 的那一层),这一层不画;删掉的块都放回卡片后面这一层
|
||||
const listed = slotted ? blocks : blocks.filter(block => !block.slot)
|
||||
const removed = slotted ? [] : tree.diff?.removed.get(owner?.id ?? '') ?? []
|
||||
const listedIds = new Set(listed.map(block => block.id))
|
||||
// 删掉的块排在它原来前面那块后面;那块被筛掉了也照排,不跟着消失;那块画在卡片里时排到这一层最后
|
||||
const removedAfter = (id: string | undefined): ReactElement[] => removed
|
||||
.filter(item => item.after === id)
|
||||
.map(item => <RemovedCard key={item.id} placed={item} tree={tree} />)
|
||||
const removedElsewhere = removed
|
||||
.filter(item => item.after !== undefined && !listedIds.has(item.after))
|
||||
.map(item => <RemovedCard key={item.id} placed={item} tree={tree} />)
|
||||
return (
|
||||
<div className="cr-level flex gap-2" {...kindProps(owner)}>
|
||||
<div className="flex w-3 shrink-0 justify-center">
|
||||
@@ -124,7 +137,7 @@ export function BlockLevel({ blocks, owner, tree }: { blocks: BlockView[], owner
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-2">
|
||||
{removedAfter(undefined)}
|
||||
{blocks.map(block => (
|
||||
{listed.map(block => (
|
||||
<Fragment key={block.id}>
|
||||
{tree.folded?.runs.has(block.id) && <UnchangedStrip count={tree.folded.runs.get(block.id) ?? 0} tree={tree} onOpen={() => tree.onOpenRun(block.id)} />}
|
||||
{shown(block, tree) && !tree.folded?.folded.has(block.id) && (
|
||||
@@ -141,6 +154,7 @@ export function BlockLevel({ blocks, owner, tree }: { blocks: BlockView[], owner
|
||||
{removedAfter(block.id)}
|
||||
</Fragment>
|
||||
))}
|
||||
{removedElsewhere}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -169,7 +183,7 @@ function BlockQa({ anchor, qa }: { anchor: string, qa: QaTree }): ReactElement {
|
||||
*/
|
||||
function BlockPreview({ block, tree, marks }: { block: BlockView, tree: BlockTreeState, marks?: BlockMarks }): ReactElement {
|
||||
if ('properties' in block)
|
||||
return <PropertyGrid blockId={block.id} rows={block.properties} collapsed={tree.collapsed} onToggleFold={tree.onToggleFold} marks={marks?.rows} />
|
||||
return <PropertyGrid blockId={block.id} rows={block.properties} collapsed={tree.collapsed} onToggleFold={tree.onToggleFold} marks={marks?.rows} slots={cardSlots(block, tree)} />
|
||||
return (
|
||||
<>
|
||||
{marks?.translation ? <DiffText diff={marks.translation} /> : <TextPreview text={block.translation} />}
|
||||
@@ -183,12 +197,36 @@ function BlockPreview({ block, tree, marks }: { block: BlockView, tree: BlockTre
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片属性表里放的子块(BlockSlot)怎么画:一行下面的几块画成一层块(同块树);点子行才展开的,点那个子行的第一格展开它、
|
||||
* 同一行里别的收起,展开着的再点收起。卡片没有放进属性表的子块时没有。
|
||||
*/
|
||||
function cardSlots(block: BlockView, tree: BlockTreeState): GridSlots | undefined {
|
||||
const blocks = block.children.filter(child => child.slot)
|
||||
if (blocks.length === 0)
|
||||
return undefined
|
||||
return {
|
||||
blocks,
|
||||
render: list => <BlockLevel blocks={list} owner={block} tree={tree} slotted />,
|
||||
onToggle: (child) => {
|
||||
if (!tree.collapsed.has(child.id)) {
|
||||
tree.onSetFolds({ [child.id]: true })
|
||||
return
|
||||
}
|
||||
const others = blocks.filter(other => other !== child && other.slot?.row === child.slot?.row && other.slot?.item !== undefined)
|
||||
tree.onSetFolds({ ...Object.fromEntries(others.map(other => [other.id, true])), [child.id]: false })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/** 一块:折叠箭头、种类图标、预览内容或源码、右上角的一排图标按钮(BlockTools)。 */
|
||||
function BlockCard({ block, tree }: { block: BlockView, tree: BlockTreeState }): ReactElement {
|
||||
const editing = tree.editing.has(block.id)
|
||||
const collapsed = tree.collapsed.has(block.id)
|
||||
const hasChildren = hasShownChildren(block, tree)
|
||||
const Chevron = collapsed ? ChevronRight : ChevronDown
|
||||
// 点父卡片子行才展开的子块:画出来就是展开着的,箭头收起整张
|
||||
const toggled = block.slot?.item !== undefined
|
||||
const hasChildren = toggled || hasShownChildren(block, tree)
|
||||
const Chevron = collapsed && !toggled ? ChevronRight : ChevronDown
|
||||
const change = tree.diff?.changes.get(block.id)
|
||||
return (
|
||||
<div
|
||||
@@ -205,8 +243,8 @@ function BlockCard({ block, tree }: { block: BlockView, tree: BlockTreeState }):
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-5 w-3.5 shrink-0 items-center justify-center rounded-sm text-[var(--vscode-descriptionForeground)] hover:text-[var(--vscode-foreground)]"
|
||||
aria-label={collapsed ? '展开子块' : '折叠子块'}
|
||||
aria-expanded={!collapsed}
|
||||
aria-label={toggled ? '收起' : collapsed ? '展开子块' : '折叠子块'}
|
||||
aria-expanded={toggled || !collapsed}
|
||||
onClick={() => tree.onToggleFold(block.id)}
|
||||
>
|
||||
<Chevron size={14} strokeWidth={1.8} />
|
||||
|
||||
Reference in new issue
Block a user