API Reference
Policy System

Policy System

The Policy system controls how cells render values, parse user input, serialize data, and restrict operations. It is GridSheet's primary extension point for custom cell behavior.

Overview

A Policy instance is:

  • Composable — built from one or more PolicyMixinType objects
  • Priority-based — the priority number determines which policy wins when multiple apply
  • Per-cell — each cell references a policy by its string key (set via cell.policy)

Registering Policies

Policies are registered through useBook and referenced by key in cell definitions:

import { GridSheet, Policy, useBook, ThousandSeparatorPolicyMixin } from '@gridsheet/react-core';
 
function MySheet() {
  const book = useBook({
    policies: {
      currency: new Policy({ mixins: [ThousandSeparatorPolicyMixin] }),
    },
  });
 
  return (
    <GridSheet
      book={book}
      initialCells={{
        B: { policy: 'currency' },
        'B1': { value: 1234567.89 },
      }}
    />
  );
}

Use useSpellbook from @gridsheet/react-core/spellbook to create a book with all extended formula functions pre-loaded:

import { GridSheet, Policy, ThousandSeparatorPolicyMixin } from '@gridsheet/react-core';
import { useSpellbook } from '@gridsheet/react-core/spellbook';
 
function MySheet() {
  const book = useSpellbook({
    policies: {
      currency: new Policy({ mixins: [ThousandSeparatorPolicyMixin] }),
    },
  });
 
  return (
    <GridSheet
      book={book}
      initialCells={{
        B: { policy: 'currency' },
        'B1': { value: 1234567.89 },
      }}
    />
  );
}

Creating a Custom Policy

Using Mixins (recommended)

Define a PolicyMixinType object and pass it to the Policy constructor:

import { Policy, PolicyMixinType, RenderProps, SelectProps, AutocompleteOption } from '@gridsheet/react-core';
 
const StatusPolicyMixin: PolicyMixinType = {
  getSelectOptions(): AutocompleteOption[] {
    return [
      { value: 'active',   label: '🟢 Active' },
      { value: 'inactive', label: '🔴 Inactive' },
      { value: 'pending',  label: '🟡 Pending' },
    ];
  },
  select({ next }: SelectProps) {
    const allowed = ['active', 'inactive', 'pending'];
    if (!allowed.includes(next?.value)) {
      return { ...next, value: 'pending' };
    }
    return next;
  },
  renderString({ value }: RenderProps<string>) {
    const map: Record<string, string> = {
      active: '🟢 Active',
      inactive: '🔴 Inactive',
      pending: '🟡 Pending',
    };
    return map[value] ?? value;
  },
};
 
const statusPolicy = new Policy({ mixins: [StatusPolicyMixin] });

Combining Multiple Mixins

import { Policy, ThousandSeparatorPolicyMixin } from '@gridsheet/react-core';
 
const currencyPolicy = new Policy({
  mixins: [
    ThousandSeparatorPolicyMixin,
    {
      renderColHeaderLabel: () => 'Amount ($)',
    },
  ],
});

Subclassing

import { Policy, RenderProps } from '@gridsheet/react-core';
 
class UpperCasePolicy extends Policy {
  override renderString({ value }: RenderProps<string>) {
    return value?.toUpperCase() ?? '';
  }
}
 
const upperCasePolicy = new UpperCasePolicy();

Policy Constructor

new Policy(props?: PolicyProps)
 
type PolicyProps = {
  mixins?: PolicyMixinType[];
  priority?: number;   // default: 1
};
PropertyTypeDefaultDescription
mixinsPolicyMixinType[]undefinedMixin objects whose methods are merged into the policy
prioritynumber1Resolution priority when multiple policies apply

PolicyMixinType Reference

Format Settings

PropertyTypeDefaultDescription
datetimeFormatstring'YYYY-MM-DD HH:mm:ss'Format for datetime values
dateFormatstring'YYYY-MM-DD'Format for date-only values
timeFormatstring'HH:mm:ss'Format for time values
decimalPrecisionnumber15Significant digits for number rendering

Rendering Methods

These methods control what is displayed inside a cell. Return a React node or a primitive value.

MethodSignatureDescription
render(props: RenderProps) => anyOverride the entire render dispatch
renderCallback(rendered: any, props: RenderProps) => anyPost-process the rendered value
renderString(props: RenderProps<string>) => anyRender string values
renderNumber(props: RenderProps<number>) => anyRender number values
renderBool(props: RenderProps<boolean>) => anyRender boolean values
renderDate(props: RenderProps<Date>) => anyRender Date values
renderTime(props: RenderProps<Time>) => anyRender Time values
renderArray(props: RenderProps<any[]>) => anyRender array values
renderObject(props: RenderProps<any>) => anyRender object values
renderNull(props: RenderProps<null | undefined>) => anyRender null/undefined values
renderSheet(props: RenderProps<UserSheet>) => anyRender Sheet values
renderRowHeaderLabel(n: number) => string | nullCustom row header label
renderColHeaderLabel(n: number) => string | nullCustom column header label

RenderProps

type RenderProps<T = any> = {
  value?: T;
  cell?: CellType<T>;
  sheet: Sheet;
  point: PointType;
  sync?: (sheet: UserSheet) => void;
};

Serialization Methods

These methods convert cell values to strings for clipboard and export.

MethodSignatureDescription
serialize(props: SerializeProps) => stringOverride entire serialization dispatch
serializeString(props: SerializeProps<string>) => stringSerialize string values
serializeNumber(props: SerializeProps<number>) => stringSerialize number values
serializeBool(props: SerializeProps<boolean>) => stringSerialize boolean values
serializeDate(props: SerializeProps<Date>) => stringSerialize Date values
serializeTime(props: SerializeProps<Time>) => stringSerialize Time values
serializeArray(props: SerializeProps<any[]>) => stringSerialize array values
serializeNull(props: SerializeProps<null | undefined>) => stringSerialize null/undefined values
serializeFormulaError(props: SerializeProps<FormulaError>) => stringSerialize formula errors
serializeError(props: SerializeProps<Error>) => stringSerialize generic errors
serializeForClipboard(props: SerializeForClipboardProps) => stringCustom clipboard serialization

SerializeProps

type SerializeProps<T = any> = {
  value: T;
  cell?: CellType<T>;
  sheet: Sheet;
  point: PointType;
};

Deserialization Methods

These methods parse user input (strings) into typed cell values.

MethodSignatureDescription
deserialize(value: string, cell?: CellType) => CellTypeOverride entire deserialize dispatch
deserializeRaw(value: any, cell?: CellType) => CellTypeParse raw (non-string) input
deserializeCallback(parsed: any, cell?: CellType) => CellTypePost-process any deserialized value
deserializeFirst(value: string, cell?: CellType) => CellPatchType<any>Runs before built-in deserializers (highest priority)
deserializeNumber(value: string, cell?: CellType) => CellPatchType<number | undefined>Parse as number
deserializeBool(value: string, cell?: CellType) => CellPatchType<boolean | undefined>Parse as boolean
deserializeDate(value: string, cell?: CellType) => CellPatchType<Date | undefined>Parse as Date
deserializeTime(value: string, cell?: CellType) => CellPatchType<Time | undefined>Parse as Time
deserializeAny(value: string, cell?: CellType) => CellPatchType<string | undefined>Fallback — treat as string

Deserialization order (built-in):

  1. deserializeFirst (if defined — runs before all others)
  2. deserializeNumber
  3. deserializeTime
  4. deserializeDate
  5. deserializeBool
  6. deserializeAny

toScalar Methods

These methods convert cell values to scalars used by the formula engine.

MethodSignatureDescription
toScalar(props: ScalarProps) => ScalarOverride entire scalar dispatch
toScalarString(props: ScalarProps<string>) => ScalarConvert string to scalar
toScalarNumber(props: ScalarProps<number>) => ScalarConvert number to scalar
toScalarBool(props: ScalarProps<boolean>) => ScalarConvert boolean to scalar
toScalarDate(props: ScalarProps<Date>) => ScalarConvert Date to scalar
toScalarTime(props: ScalarProps<Time>) => ScalarConvert Time to scalar
toScalarArray(props: ScalarProps<any[]>) => ScalarConvert array to scalar
toScalarNull(props: ScalarProps<null | undefined>) => ScalarConvert null/undefined to scalar
type ScalarProps<T = any> = {
  value?: T;
  cell?: CellType<T>;
  sheet: Sheet;
  point: PointType;
};
 
type Scalar = string | number | boolean | null | undefined;

Selection / Autocomplete Methods

These methods control dropdown menus and value validation when users select from a list.

MethodSignatureDescription
getSelectOptions() => AutocompleteOption[]Return available dropdown options
select(props: SelectProps) => CellType | undefinedValidate/transform the selected value
getSelectFallback(props: SelectFallbackProps) => CellType | undefinedReturn a default cell when validation fails

AutocompleteOption

type AutocompleteOption = {
  value: any;
  label?: any;                                       // React node displayed in dropdown
  keywords?: string[];                               // Extra search terms
  tooltip?: ReactNode | FC<{ value?: any }>;         // Tooltip shown on hover
};

SelectProps

type SelectProps = {
  sheet: UserSheet;
  point: PointType;
  current?: CellType;   // Cell value before the change
  next?: CellType;      // Cell value after the change
  operation: OperationType;
};

Example: Dropdown with Validation

import { Policy, PolicyMixinType, AutocompleteOption, SelectProps } from '@gridsheet/react-core';
 
const ColorPolicyMixin: PolicyMixinType = {
  getSelectOptions(): AutocompleteOption[] {
    return [
      { value: 'red',   label: <span style={{ color: 'red' }}>Red</span> },
      { value: 'green', label: <span style={{ color: 'green' }}>Green</span> },
      { value: 'blue',  label: <span style={{ color: 'blue' }}>Blue</span> },
    ];
  },
  select({ next, current }: SelectProps) {
    const allowed = ['red', 'green', 'blue'];
    if (allowed.includes(next?.value)) {
      return { value: next!.value, style: { backgroundColor: next!.value } };
    }
    // Preserve current value if input is invalid
    return current ?? { value: null };
  },
};
 
const colorPolicy = new Policy({ mixins: [ColorPolicyMixin], priority: 2 });

Built-in Mixins

GridSheet ships with several ready-to-use mixins:

ThousandSeparatorPolicyMixin

Formats numbers with comma separators.

import { Policy, ThousandSeparatorPolicyMixin } from '@gridsheet/react-core';
 
const policy = new Policy({ mixins: [ThousandSeparatorPolicyMixin] });
// 1234567.89 → "1,234,567.89"

ThousandSpaceSeparatorPolicyMixin

Same as above but uses a space as the thousands separator.

import { Policy, ThousandSpaceSeparatorPolicyMixin } from '@gridsheet/react-core';
 
const policy = new Policy({ mixins: [ThousandSpaceSeparatorPolicyMixin] });
// 1234567.89 → "1 234 567.89"

CheckboxPolicyMixin

Renders boolean cells as interactive <input type="checkbox"> elements.

import { Policy, CheckboxPolicyMixin } from '@gridsheet/react-core';
 
const policy = new Policy({ mixins: [CheckboxPolicyMixin] });
// Registration and cell assignment
const book = useBook({
  policies: {
    checkbox: new Policy({ mixins: [CheckboxPolicyMixin] }),
  },
});
 
const initialCells = {
  A: { policy: 'checkbox' },
  'A1': { value: true },
  'A2': { value: false },
};

nonePolicy

A minimal policy with priority: 0 that applies no customization. Used as the baseline default.

import { nonePolicy } from '@gridsheet/react-core';