Sheet Data Structure
Relevant source files
The following files were used as context for generating this wiki page:
The Sheet Data Structure is the central data model for the entire application. It defines every element on a character sheet, from its position and size to its content and styling. This structure is managed by a Zustand store, which provides a reactive and persistent state management solution. All data is stored locally in the user's browser via localStorage, making the application "Local-First". The design is intentionally system-agnostic, allowing users to create sheets for any tabletop RPG.
Sources: src/store/useSheetStore.ts:58-61, README.md:9
State Management Architecture
The application's state is centralized in a Zustand store, defined in useSheetStore.ts. This store is created with persist middleware, which automatically saves the entire sheet state to localStorage under the key rpg-sheet-storage. This ensures that a user's work is not lost between sessions.
The main state interface is SheetState, which encapsulates all data related to the character sheet being edited.
Sources: src/store/useSheetStore.ts:4-5, src/store/useSheetStore.ts:60-61
SheetState Interface
The SheetState interface is the root of the data structure. It contains the global styles, the list of all blocks on the canvas, and grid layout settings.
// src/store/useSheetStore.ts:42-56
interface SheetState {
blocks: Block[];
addBlock: (type: Block["type"], config?: Block["config"]) => void;
isCompact: boolean;
removeBlock: (id: string) => void;
toggleCompact: () => void;
updateBlockData: (id: string, data: any) => void;
updateLayout: (layout: any[]) => void;
setGlobalFont: (tag: keyof SheetStyle["fonts"], fontName: string) => void;
setTheme: (theme: ThemeKey) => void;
globalStyle: {
theme: SheetStyle["theme"];
fonts: { h1: string; h2: string; h3: string; body: string };
};
}
The following diagram shows the relationship between the main state components.
This diagram illustrates that the SheetState contains an array of Block objects and a single globalStyle object, which is based on the SheetStyle interface.
Sources: src/store/useSheetStore.ts:42-56
Core Data Interfaces
The two primary interfaces that define the sheet's content and appearance are Block and SheetStyle.
The Block Interface
A Block represents a single, self-contained element on the grid layout. This can be a piece of text, an image, a form input, or a decorative shape. Every item added to the canvas is an instance of a Block.
// src/store/useSheetStore.ts:20-33
export interface Block {
i: string; // UID, required by RGL
x: number;
y: number;
w: number;
h: number;
type: "text" | "image" | "header" | "form-box" | "shape" | "divider" | "list" | "container";
data?: any; // The real content (text, url image, values)
config?: {
level?: 1 | 2 | 3;
shapeType?: "square" | "circle" | "rounded" | "heart";
label?: string;
zIndex?: "back" | "middle" | "front";
// Any other config props
};
}
Block Properties
| Property | Type | Description |
|---|---|---|
i | string | A unique identifier for the block. Required by react-grid-layout. |
x, y | number | The coordinates of the block on the grid (top-left corner). |
w, h | number | The width and height of the block in grid units. |
type | string | The type of the block, which determines how it's rendered. |
data | any | The content of the block (e.g., { text: "..." }, { src: "..." }). |
config | object | Configuration options that modify the block's appearance or behavior (e.g., header level, shape type). |
Sources: src/store/useSheetStore.ts:20-33
Block Types
The type property is crucial as it dictates which component is rendered by BlockRenderer.tsx.
| Type | Purpose | Added From |
|---|---|---|
header | A title or subtitle. Config level determines if it's H1 or H2. | ToolsSidebar.tsx |
text | A multi-line paragraph of text. | ToolsSidebar.tsx |
list | A bulleted list of items. | ToolsSidebar.tsx |
image | An image container with an uploader. | ToolsSidebar.tsx |
form-box | A labeled text area, for character stats or notes. | ToolsSidebar.tsx |
divider | A horizontal line for visual separation. | ToolsSidebar.tsx |
shape | A decorative shape (e.g., square, circle). Config shapeType defines the form. | ToolsSidebar.tsx |
container | A background container to visually group other blocks. | ToolsSidebar.tsx |
Sources: src/store/useSheetStore.ts:26, src/components/layout/ToolsSidebar.tsx:112-144, src/components/sheet/BlockRenderer.tsx
The SheetStyle Interface
This interface defines the global aesthetic of the character sheet, including the active theme and font selections.
// src/store/useSheetStore.ts:9-18
export interface SheetStyle {
theme: ThemeKey;
fonts: {
h1: string; // Ej: 'Cinzel'
h2: string; // Ej: 'Roboto Slab'
h3: string;
body: string; // Normal text and inputs
};
}
The theme property uses ThemeKey, which is a string literal type defined as "simple" | "medieval" | "cyberpunk". Each theme key corresponds to a detailed theme configuration in THEMES.
Sources: src/store/useSheetStore.ts:9-18, src/lib/themes.ts:1
State Actions and Data Flow
The useSheetStore provides a set of actions to manipulate the sheet state. These actions are called from various UI components, primarily the ToolsSidebar and the Canvas.
Key Actions
| Action | Description |
|---|---|
addBlock | Adds a new Block of a specified type to the blocks array. |
removeBlock | Removes a Block from the blocks array using its ID. |
updateBlockData | Updates the data property of a specific block. |
updateLayout | Synchronizes position and size changes from react-grid-layout back to the blocks array. |
setTheme | Changes the global theme in globalStyle. |
toggleCompact | Toggles the grid's compacting behavior (vertical or null). |
Sources: src/store/useSheetStore.ts:43-55
Data Flow: Adding a New Block
The following sequence diagram illustrates the process of a user adding a new block to the sheet.
This flow shows how a user interaction in ToolsSidebar.tsx triggers the addBlock action, which updates the state. Zustand then ensures that the Canvas.tsx component re-renders to display the new block.
Sources: src/components/layout/ToolsSidebar.tsx:114, src/store/useSheetStore.ts:90-104, src/components/sheet/Canvas.tsx:12
Data Flow: Updating Block Layout
When a user drags or resizes a block on the canvas, react-grid-layout manages the interaction and then reports the new layout back to the application via the onLayoutChange callback.
This process, handled by the updateLayout function, ensures that the application's source of truth (useSheetStore) is always synchronized with the visual representation managed by the grid library.
Sources: src/components/sheet/Canvas.tsx:35, src/store/useSheetStore.ts:106-123
Summary
The sheet data structure is a well-defined system built around a central Zustand store. It uses two main interfaces, Block and SheetStyle, to define the content and appearance of a character sheet. Block objects represent individual grid items, while SheetStyle controls the global theme. Actions within the store manage the entire lifecycle of these objects, from creation to update and deletion, providing a robust and persistent foundation for the character sheet builder.