Initial commit

This commit is contained in:
LZH-YS1998
2026-07-01 17:56:31 +08:00
commit d78931979d
731 changed files with 311088 additions and 0 deletions
@@ -0,0 +1,73 @@
import { memo } from 'react'
import { Handle, Position } from '@xyflow/react'
import { resolveRoleIcon } from './roleIcons'
export interface StructureCanvasNodeData {
[key: string]: unknown
roleId: string
name: string
responsibility: string
icon: string | null
employeeCount: number
/** Names of the actual recruited (non-placeholder) people staffed on this role. */
employeeNames: string[]
isOwner: boolean
isSelected: boolean
isDropTarget: boolean
}
/**
* Canvas node — single-accent refined card.
* Outer <div> always carries .oc-canvas-node (plus optional state
* modifiers) — this is the E2E anchor. Icon is rendered via CSS
* `mask-image` so its tint can track the active theme's --accent;
* the card reads consistently under Paper, OpenOPC, etc.
*/
export const StructureCanvasNode = memo(function StructureCanvasNode({ data }: { data: StructureCanvasNodeData }) {
const stateClass = [
'oc-canvas-node',
data.isOwner && 'is-owner',
data.isSelected && 'is-selected',
data.isDropTarget && 'is-drop-target',
].filter(Boolean).join(' ')
const iconSrc = resolveRoleIcon(data.icon)
return (
<div className={stateClass}>
<Handle type="target" position={Position.Top} className="oc-canvas-handle" />
<div className="oc-canvas-node-row">
<div className="oc-canvas-node-chip">
<span
className="oc-canvas-node-chip-icon"
style={{
WebkitMaskImage: `url("${iconSrc}")`,
maskImage: `url("${iconSrc}")`,
}}
aria-hidden
/>
</div>
<div className="oc-canvas-node-text">
<span className="oc-canvas-node-name">{data.name}</span>
<div className="oc-canvas-node-meta">
{data.employeeNames.length > 0 ? (
<span
className="oc-canvas-node-person"
title={data.employeeNames.join(', ')}
>
{data.employeeNames[0]}
{data.employeeNames.length > 1 ? ` +${data.employeeNames.length - 1}` : ''}
</span>
) : (
<span className="oc-canvas-node-id">{data.roleId}</span>
)}
{data.employeeCount > 0 && (
<span className="oc-canvas-node-badge">{data.employeeCount}</span>
)}
</div>
</div>
</div>
<Handle type="source" position={Position.Bottom} className="oc-canvas-handle" />
</div>
)
})