
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
react-matrix-canvas
Advanced tools
A React component library for creating interactive matrix/grid canvases with draggable markers
A React component library for creating interactive matrix/grid canvases with draggable markers. Perfect for building Eisenhower matrices, BCG matrices, risk matrices, and other quadrant-based UIs.
npm install react-matrix-canvas
import { MatrixCanvas, Marker, type AreaConfig } from 'react-matrix-canvas';
const areas: AreaConfig[] = [
{ id: 'urgent-important', label: 'Do First', backgroundColor: '#fee2e2', position: { row: 0, col: 0 } },
{ id: 'not-urgent-important', label: 'Schedule', backgroundColor: '#dbeafe', position: { row: 0, col: 1 } },
{ id: 'urgent-not-important', label: 'Delegate', backgroundColor: '#fef3c7', position: { row: 1, col: 0 } },
{ id: 'not-urgent-not-important', label: 'Eliminate', backgroundColor: '#f3e8ff', position: { row: 1, col: 1 } },
];
function App() {
const [items, setItems] = useState([
{ id: '1', title: 'Task 1', areaId: 'urgent-important', position: { x: 25, y: 25 } },
]);
return (
<MatrixCanvas
areas={areas}
grid={{ rows: 2, cols: 2 }}
onCanvasClick={(position, areaId) => {
// Add new item at clicked position
}}
style={{ height: '500px' }}
>
{items.map(item => (
<Marker
key={item.id}
id={item.id}
position={item.position}
areaId={item.areaId}
onPositionChange={(newPos) => {
setItems(prev => prev.map(i =>
i.id === item.id ? { ...i, position: newPos } : i
));
}}
onAreaChange={(newArea) => {
setItems(prev => prev.map(i =>
i.id === item.id ? { ...i, areaId: newArea } : i
));
}}
tooltip={<div>{item.title}</div>}
/>
))}
</MatrixCanvas>
);
}
Main container component.
| Prop | Type | Description |
|---|---|---|
areas | AreaConfig[] | Area configurations (optional for free canvas) |
grid | { rows: number; cols: number } | Grid dimensions (auto-calculated from areas if omitted) |
onCanvasClick | (position, areaId) => void | Callback when clicking empty canvas area |
dragEndClickDelay | number | Delay (ms) to ignore clicks after drag ends |
renderArea | (area) => ReactNode | Custom area renderer |
className | string | Custom class name |
style | CSSProperties | Custom styles |
Draggable marker component.
| Prop | Type | Description |
|---|---|---|
id | string | Unique identifier |
position | { x: number; y: number } | Position in percentage (0-100) |
areaId | string | Current area ID |
data | T | Custom data passed to renderTooltip |
onPositionChange | (position) => void | Callback when position changes |
onAreaChange | (newArea, oldArea) => void | Callback when area changes |
onDragEnd | () => void | Callback when drag ends |
onClick | () => void | Callback when clicked |
tooltip | ReactNode | (data) => ReactNode | Tooltip content |
renderTooltip | (props) => ReactNode | Custom tooltip renderer |
size | number | Marker size in pixels (default: 20) |
color | string | Marker color (default: #3b82f6) |
hoverScale | number | Scale on hover (e.g., 1.25) |
hoverClassName | string | Class name applied on hover |
hoverStyle | CSSProperties | Style applied on hover |
disabled | boolean | Disable dragging |
className | string | Custom class name |
style | CSSProperties | Custom styles |
interface AreaConfig {
id: string;
label?: string;
backgroundColor?: string;
labelColor?: string;
position: { row: number; col: number };
}
renderTooltipを使うと、ライブラリが提供する位置情報とマウスイベントを使って、独自のツールチップUIを実装できます。
// ライブラリが提供するprops:
// - data: Markerに渡したカスタムデータ
// - position: ツールチップの表示位置 { top, left }
// - onMouseEnter/onMouseLeave: ホバー状態管理用
<Marker
id="task-1"
position={{ x: 50, y: 50 }}
areaId="design"
data={{ title: 'Task 1', status: 'active' }}
renderTooltip={({ data, position, onMouseEnter, onMouseLeave }) => (
<MyCustomTooltip
title={data.title}
status={data.status}
style={{ top: position.top, left: position.left }}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
/>
)}
/>
実際のアプリケーションでの使用例(アイゼンハワーマトリクス風のタスク管理):
import { MatrixCanvas, Marker, type AreaConfig } from 'react-matrix-canvas';
import { createPortal } from 'react-dom';
// エリア設定
const AREA_CONFIGS: AreaConfig[] = [
{ id: 'design', label: 'Design', backgroundColor: '#dbeafe', position: { row: 0, col: 0 } },
{ id: 'action', label: 'Action', backgroundColor: '#dcfce7', position: { row: 0, col: 1 } },
{ id: 'hold', label: 'Hold', backgroundColor: '#fef3c7', position: { row: 1, col: 0 } },
{ id: 'leave', label: 'Leave', backgroundColor: '#fee2e2', position: { row: 1, col: 1 } },
];
// カスタムツールチップ(Portalで表示)
function TaskTooltip({ task, position, onMouseEnter, onMouseLeave, onEdit, onDelete }) {
return createPortal(
<div
className="fixed z-50 rounded-lg bg-white p-3 shadow-xl"
style={{ top: position.top - 8, left: position.left, transform: 'translate(-50%, -100%)' }}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<h4>{task.title}</h4>
<div className="mt-2 flex gap-2">
<button onClick={onEdit}>編集</button>
<button onClick={onDelete}>削除</button>
</div>
</div>,
document.body
);
}
function TaskMatrix({ tasks, onUpdateTask, onDeleteTask }) {
// カスタムエリアレンダリング
const renderArea = useCallback((area: AreaConfig) => (
<div
className="relative p-4"
style={{ backgroundColor: area.backgroundColor }}
>
<span className="rounded-full bg-white px-3 py-1 shadow">
{area.label}
</span>
</div>
), []);
return (
<MatrixCanvas
areas={AREA_CONFIGS}
grid={{ rows: 2, cols: 2 }}
onCanvasClick={(position, areaId) => {
// クリック位置とエリアIDを使って新規タスク作成
console.log('Clicked at', position, 'in area', areaId);
}}
dragEndClickDelay={150}
renderArea={renderArea}
className="h-[600px] w-full"
>
{tasks.map(task => (
<Marker
key={task.id}
id={task.id}
position={task.position}
areaId={task.areaId}
data={task}
onPositionChange={(pos) => onUpdateTask(task.id, { position: pos })}
onAreaChange={(newAreaId) => onUpdateTask(task.id, { areaId: newAreaId })}
color="#3b82f6"
size={20}
hoverScale={1.25}
renderTooltip={({ data, position, onMouseEnter, onMouseLeave }) => (
<TaskTooltip
task={data}
position={position}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onEdit={() => console.log('Edit', data.id)}
onDelete={() => onDeleteTask(data.id)}
/>
)}
/>
))}
</MatrixCanvas>
);
}
<Marker
id="1"
position={{ x: 50, y: 50 }}
hoverScale={1.25}
hoverClassName="shadow-lg"
hoverStyle={{ boxShadow: '0 4px 12px rgba(0,0,0,0.3)' }}
/>
<MatrixCanvas
areas={areas}
dragEndClickDelay={150} // Ignore clicks for 150ms after drag ends
onCanvasClick={(position, areaId) => {
// This won't fire immediately after dragging
}}
>
{/* markers */}
</MatrixCanvas>
<MatrixCanvas>
<Marker id="1" position={{ x: 50, y: 50 }} />
</MatrixCanvas>
<MatrixCanvas
areas={[
{ id: 'low', label: 'Low', position: { row: 0, col: 0 } },
{ id: 'high', label: 'High', position: { row: 0, col: 1 } },
]}
grid={{ rows: 1, cols: 2 }}
>
{/* markers */}
</MatrixCanvas>
<MatrixCanvas
areas={[
{ id: '0-0', position: { row: 0, col: 0 } },
{ id: '0-1', position: { row: 0, col: 1 } },
{ id: '0-2', position: { row: 0, col: 2 } },
// ... 9 areas total
]}
grid={{ rows: 3, cols: 3 }}
>
{/* markers */}
</MatrixCanvas>
このライブラリは「ライブラリが処理を担当し、アプリがUIを担当する」という設計思想に基づいています。
renderArea)renderTooltip)color, size, classNameなど)Copyright (c) 2025 yuitonn
FAQs
A React component library for creating interactive matrix/grid canvases with draggable markers
The npm package react-matrix-canvas receives a total of 2 weekly downloads. As such, react-matrix-canvas popularity was classified as not popular.
We found that react-matrix-canvas demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.