@odoo/o-spreadsheet
Version:
A spreadsheet component
1,460 lines (1,415 loc) • 527 kB
TypeScript
import * as chart_js from 'chart.js';
import { ChartConfiguration, CoreChartOptions, Scriptable, Color as Color$1, ScriptableContext, FontSpec, ChartDataset, Point, ChartType as ChartType$1, ChartMeta } from 'chart.js';
import * as ChartGeo from 'chartjs-chart-geo';
import * as GeoJSON$1 from 'geojson';
import * as _odoo_owl from '@odoo/owl';
import { ComponentConstructor, Component } from '@odoo/owl';
import * as chart_js_dist_types_utils from 'chart.js/dist/types/utils';
import * as chart_js_dist_types_geometric from 'chart.js/dist/types/geometric';
import * as chart_js_dist_types_basic from 'chart.js/dist/types/basic';
import * as chart_js_auto from 'chart.js/auto';
import { Chart, ChartConfiguration as ChartConfiguration$1 } from 'chart.js/auto';
interface FigureInfo {
id: UID;
width: Pixel;
height: Pixel;
tag: string;
}
interface Figure extends FigureInfo, AnchorOffset {
}
interface FigureUI extends DOMCoordinates, Figure {
}
interface AnchorOffset {
col: HeaderIndex;
row: HeaderIndex;
offset: PixelPosition;
}
interface FigureSize {
width: Pixel;
height: Pixel;
}
interface ExcelFigureSize {
cx: number;
cy: number;
}
type ResizeDirection = -1 | 0 | 1;
interface Carousel {
readonly items: CarouselItem[];
readonly title?: TitleDesign;
}
type CarouselItem = {
type: "chart";
chartId: UID;
title?: string;
} | {
type: "carouselDataView";
title?: string;
};
type ImageSVG = {
name: string;
width: number;
height: number;
paths: {
path: string;
fillColor: Color;
}[];
};
interface Image$1 {
path: string;
size: FigureSize;
mimetype?: string;
}
declare const AllowedImageMimeTypes: readonly ["image/avif", "image/bmp", "image/gif", "image/vnd.microsoft.icon", "image/jpeg", "image/png", "image/tiff", "image/webp"];
type XLSXExportFile = XLSXExportImageFile | XLSXExportXMLFile;
interface XLSXExportXMLFile {
path: string;
content: string;
contentType?: string;
}
interface XLSXExportImageFile {
path: string;
imageSrc: string;
}
interface XLSXExport {
name: string;
files: XLSXExportFile[];
}
/**
* Standardized XLSX hexadecimal color (with or without alpha channel).
* Note that the alpha channel goes first! AARRGGBB
* e.g. "1E5010" or "331E5010"
*/
type XlsxHexColor = string & Alias;
type VerticalAxisPosition = "left" | "right";
type LegendPosition = "top" | "bottom" | "left" | "right" | "none";
interface CommonChartDefinition {
readonly dataSets: CustomizedDataSet[];
readonly dataSetsHaveTitle: boolean;
readonly labelRange?: string;
readonly title: TitleDesign;
readonly background?: Color;
readonly legendPosition: LegendPosition;
readonly aggregated?: boolean;
readonly axesDesign?: AxesDesign;
readonly showValues?: boolean;
readonly humanize?: boolean;
}
interface TreeMapChartDefinition {
readonly type: "treemap";
readonly dataSets: CustomizedDataSet[];
readonly dataSetsHaveTitle: boolean;
readonly labelRange?: string;
readonly title: TitleDesign;
readonly background?: Color;
readonly legendPosition: LegendPosition;
readonly showHeaders?: boolean;
readonly headerDesign?: TitleDesign;
readonly showValues?: boolean;
readonly showLabels?: boolean;
readonly valuesDesign?: TitleDesign;
readonly coloringOptions?: TreeMapColoringOptions;
readonly humanize?: boolean;
}
type TreeMapCategoryColorOptions = {
type: "categoryColor";
colors: (Color | undefined | null)[];
useValueBasedGradient: boolean;
};
type TreeMapColorScaleOptions = {
type: "colorScale";
minColor: Color;
midColor?: Color;
maxColor: Color;
};
interface TreeMapGroupColor {
label: string;
color: Color;
}
type TreeMapColoringOptions = TreeMapCategoryColorOptions | TreeMapColorScaleOptions;
type TreeMapChartRuntime = {
chartJsConfig: ChartConfiguration;
background: Color;
};
/*!
* chartjs-chart-treemap v3.1.0
* https://chartjs-chart-treemap.pages.dev/
* (c) 2024 Jukka Kurkela
* Released under the MIT license
*/
type AnyObject = Record<string, unknown>;
type TreemapScriptableContext = ScriptableContext<"treemap"> & {
raw: TreemapDataPoint;
};
type TreemapControllerDatasetCaptionsOptions = {
align?: Scriptable<LabelAlign, TreemapScriptableContext>;
color?: Scriptable<Color$1, TreemapScriptableContext>;
display?: boolean;
formatter?: Scriptable<string, TreemapScriptableContext>;
font?: FontSpec;
hoverColor?: Scriptable<Color$1, TreemapScriptableContext>;
hoverFont?: FontSpec;
padding?: number;
};
type TreemapControllerDatasetLabelsOptions = {
align?: Scriptable<LabelAlign, TreemapScriptableContext>;
color?: Scriptable<Color$1 | Color$1[], TreemapScriptableContext>;
display?: boolean;
formatter?: Scriptable<string | Array<string>, TreemapScriptableContext>;
font?: Scriptable<FontSpec | FontSpec[], TreemapScriptableContext>;
hoverColor?: Scriptable<Color$1 | Color$1[], TreemapScriptableContext>;
hoverFont?: Scriptable<FontSpec | FontSpec[], TreemapScriptableContext>;
overflow?: Scriptable<LabelOverflow, TreemapScriptableContext>;
padding?: number;
position?: Scriptable<LabelPosition, TreemapScriptableContext>;
};
type LabelPosition = "top" | "middle" | "bottom";
type LabelAlign = "left" | "center" | "right";
type LabelOverflow = "cut" | "hidden" | "fit";
type TreemapControllerDatasetDividersOptions = {
display?: boolean;
lineCapStyle?: string;
lineColor?: string;
lineDash?: number[];
lineDashOffset?: number;
lineWidth?: number;
};
interface TreemapControllerDatasetOptions<DType> {
spacing?: number;
rtl?: boolean;
backgroundColor?: Scriptable<Color$1, TreemapScriptableContext>;
borderColor?: Scriptable<Color$1, TreemapScriptableContext>;
borderWidth?: number;
hoverBackgroundColor?: Scriptable<Color$1, TreemapScriptableContext>;
hoverBorderColor?: Scriptable<Color$1, TreemapScriptableContext>;
hoverBorderWidth?: number;
captions?: TreemapControllerDatasetCaptionsOptions;
dividers?: TreemapControllerDatasetDividersOptions;
labels?: TreemapControllerDatasetLabelsOptions;
label?: string;
data: TreemapDataPoint[];
groups?: Array<keyof DType>;
sumKeys?: Array<keyof DType>;
tree: number[] | DType[] | AnyObject;
treeLeafKey?: keyof DType;
key?: keyof DType;
hidden?: boolean;
displayMode?: "containerBoxes" | "headerBoxes";
groupColors?: TreeMapGroupColor[];
}
interface TreemapDataPoint {
x: number;
y: number;
w: number;
h: number;
/**
* Value
*/
v: number;
/**
* Sum
*/
s: number;
/**
* Depth, only available if grouping
*/
l?: number;
/**
* Group name, only available if grouping
*/
g?: string;
/**
* Group Sum, only available if grouping
*/
gs?: number;
/**
* additonal keys sums, only available if grouping
*/
vs?: AnyObject;
isLeaf?: boolean;
}
declare module "chart.js" {
interface ChartTypeRegistry {
treemap: {
chartOptions: CoreChartOptions<"treemap">;
datasetOptions: TreemapControllerDatasetOptions<Record<string, unknown>>;
defaultDataPoint: TreemapDataPoint;
metaExtensions: AnyObject;
parsedDataType: unknown;
scales: never;
};
}
}
interface FunnelChartDefinition {
readonly type: "funnel";
readonly dataSets: CustomizedDataSet[];
readonly dataSetsHaveTitle: boolean;
readonly labelRange?: string;
readonly title: TitleDesign;
readonly background?: Color;
readonly legendPosition: LegendPosition;
readonly horizontal?: boolean;
readonly axesDesign?: AxesDesign;
readonly aggregated?: boolean;
readonly showValues?: boolean;
readonly funnelColors?: FunnelChartColors;
readonly cumulative?: boolean;
readonly humanize?: boolean;
}
type FunnelChartRuntime = {
chartJsConfig: ChartConfiguration;
background: Color;
};
type FunnelChartColors = (Color | undefined)[];
interface GaugeChartDefinition {
readonly type: "gauge";
readonly title: TitleDesign;
readonly dataRange?: string;
readonly sectionRule: SectionRule;
readonly background?: Color;
readonly humanize?: boolean;
}
interface SectionRule {
readonly colors: ColorSet;
readonly rangeMin: string;
readonly rangeMax: string;
readonly lowerInflectionPoint: SectionThreshold;
readonly upperInflectionPoint: SectionThreshold;
}
interface ColorSet {
readonly lowerColor: Color;
readonly middleColor: Color;
readonly upperColor: Color;
}
interface SectionThreshold {
readonly type: "number" | "percentage";
readonly value: string;
readonly operator: "<" | "<=";
}
interface GaugeValue {
value: number;
label: string;
}
interface GaugeInflectionValue extends GaugeValue {
operator: "<" | "<=";
}
interface GaugeChartRuntime {
background: Color;
title: TitleDesign;
minValue: GaugeValue;
maxValue: GaugeValue;
gaugeValue?: GaugeValue;
inflectionValues: GaugeInflectionValue[];
colors: Color[];
}
interface LineChartDefinition extends CommonChartDefinition {
readonly type: "line";
readonly labelsAsText: boolean;
readonly stacked: boolean;
readonly aggregated?: boolean;
readonly cumulative: boolean;
readonly fillArea?: boolean;
readonly hideDataMarkers?: boolean;
readonly zoomable?: boolean;
}
type LineChartRuntime = {
chartJsConfig: ChartConfiguration;
masterChartConfig?: ChartConfiguration;
background: Color;
};
interface PieChartDefinition extends CommonChartDefinition {
readonly type: "pie";
readonly aggregated?: boolean;
readonly isDoughnut?: boolean;
readonly showValues?: boolean;
readonly pieHolePercentage?: number;
}
type PieChartRuntime = {
chartJsConfig: ChartConfiguration<"pie" | "doughnut">;
background: Color;
};
interface PyramidChartDefinition extends Omit<BarChartDefinition, "type"> {
readonly type: "pyramid";
}
type PyramidChartRuntime = {
chartJsConfig: ChartConfiguration;
background: Color;
};
interface ScatterChartDefinition extends Omit<LineChartDefinition, "type" | "stacked" | "cumulative"> {
readonly type: "scatter";
}
type ScatterChartRuntime = LineChartRuntime;
interface ScorecardChartDefinition {
readonly type: "scorecard";
readonly title: TitleDesign;
readonly keyValue?: string;
readonly keyDescr?: TitleDesign;
readonly baseline?: string;
readonly baselineMode: BaselineMode;
readonly baselineDescr?: TitleDesign;
readonly background?: Color;
readonly baselineColorUp: Color;
readonly baselineColorDown: Color;
readonly humanize?: boolean;
}
type BaselineMode = "text" | "difference" | "percentage" | "progress";
type BaselineArrowDirection = "neutral" | "up" | "down";
interface ProgressBar {
readonly value: number;
readonly color: Color;
}
interface ScorecardChartRuntime {
readonly title: TitleDesign;
readonly keyValue: string;
readonly keyDescr: string;
readonly baselineDisplay: string;
readonly baselineColor?: string;
readonly baselineArrow: BaselineArrowDirection;
readonly baselineDescr?: string;
readonly background: Color;
readonly fontColor: Color;
readonly keyValueStyle?: Style;
readonly keyValueDescrStyle?: Style;
readonly baselineStyle?: Style;
readonly baselineDescrStyle?: Style;
readonly progressBar?: ProgressBar;
}
interface SunburstChartDefinition {
readonly type: "sunburst";
readonly dataSets: CustomizedDataSet[];
readonly dataSetsHaveTitle: boolean;
readonly labelRange?: string;
readonly title: TitleDesign;
readonly background?: Color;
readonly legendPosition: LegendPosition;
readonly showValues?: boolean;
readonly showLabels?: boolean;
readonly valuesDesign?: ChartStyle;
readonly groupColors?: (Color | undefined | null)[];
readonly pieHolePercentage?: number;
readonly humanize?: boolean;
}
type SunburstChartRuntime = {
chartJsConfig: ChartConfiguration<"doughnut">;
background: Color;
};
interface SunburstChartJSDataset extends ChartDataset<"doughnut"> {
groupColors: {
label: string;
color: Color;
}[];
}
interface WaterfallChartDefinition extends CommonChartDefinition {
readonly type: "waterfall";
readonly verticalAxisPosition: VerticalAxisPosition;
readonly aggregated?: boolean;
readonly showSubTotals: boolean;
readonly showConnectorLines: boolean;
readonly firstValueAsSubtotal?: boolean;
readonly positiveValuesColor?: Color;
readonly negativeValuesColor?: Color;
readonly subTotalValuesColor?: Color;
readonly zoomable?: boolean;
}
type WaterfallChartRuntime = {
chartJsConfig: ChartConfiguration;
background: Color;
};
interface BarChartDefinition extends CommonChartDefinition {
readonly type: "bar";
readonly stacked: boolean;
readonly horizontal?: boolean;
readonly zoomable?: boolean;
}
type BarChartRuntime = {
chartJsConfig: ChartConfiguration;
masterChartConfig?: ChartConfiguration;
background: Color;
};
interface ComboChartDefinition extends CommonChartDefinition {
readonly dataSets: ComboChartDataSet[];
readonly type: "combo";
readonly hideDataMarkers?: boolean;
readonly zoomable?: boolean;
}
type ComboChartDataSet = CustomizedDataSet & {
type?: "bar" | "line";
};
type ComboChartRuntime = {
chartJsConfig: ChartConfiguration;
masterChartConfig?: ChartConfiguration;
background: Color;
};
interface GeoChartDefinition {
readonly type: "geo";
readonly dataSets: CustomizedDataSet[];
readonly dataSetsHaveTitle: boolean;
readonly labelRange?: string;
readonly title: TitleDesign;
readonly background?: Color;
readonly legendPosition: LegendPosition;
readonly colorScale?: GeoChartColorScale;
readonly missingValueColor?: Color;
readonly region?: string;
readonly humanize?: boolean;
}
type GeoChartRuntime = {
chartJsConfig: ChartConfiguration;
background: Color;
};
interface GeoChartCustomColorScale {
minColor: Color;
midColor?: Color;
maxColor: Color;
}
type GeoChartColorScale = GeoChartCustomColorScale | "blues" | "cividis" | "greens" | "greys" | "oranges" | "purples" | "rainbow" | "reds" | "viridis";
type GeoChartProjection = "azimuthalEqualArea" | "azimuthalEquidistant" | "gnomonic" | "orthographic" | "stereographic" | "equalEarth" | "albers" | "albersUsa" | "conicConformal" | "conicEqualArea" | "conicEquidistant" | "equirectangular" | "mercator" | "transverseMercator" | "naturalEarth1";
interface GeoChartRegion {
id: string;
label: string;
defaultProjection: GeoChartProjection;
}
interface GeoChartRuntimeGenerationArgs extends ChartRuntimeGenerationArgs {
availableRegions: GeoChartRegion[];
getGeoJsonFeatures: (region: string) => GeoJSON.Feature[] | undefined;
geoFeatureNameToId: (region: string, featureName: string) => string | undefined;
}
interface RadarChartDefinition extends CommonChartDefinition {
readonly type: "radar";
readonly aggregated?: boolean;
readonly stacked: boolean;
readonly fillArea?: boolean;
readonly hideDataMarkers?: boolean;
readonly humanize?: boolean;
}
type RadarChartRuntime = {
chartJsConfig: ChartConfiguration;
background: Color;
};
declare const CHART_TYPES: readonly ["line", "bar", "pie", "scorecard", "gauge", "scatter", "combo", "waterfall", "pyramid", "radar", "geo", "funnel", "sunburst", "treemap"];
type ChartType = (typeof CHART_TYPES)[number];
type ChartDefinition = LineChartDefinition | PieChartDefinition | BarChartDefinition | ScorecardChartDefinition | GaugeChartDefinition | ScatterChartDefinition | ComboChartDefinition | WaterfallChartDefinition | PyramidChartDefinition | RadarChartDefinition | GeoChartDefinition | FunnelChartDefinition | SunburstChartDefinition | TreeMapChartDefinition;
type ChartWithDataSetDefinition = Extract<ChartDefinition, {
dataSets: CustomizedDataSet[];
labelRange?: string;
humanize?: boolean;
}>;
type ChartWithAxisDefinition = Extract<ChartWithDataSetDefinition, {
axesDesign?: AxesDesign;
}>;
type ZoomableChartDefinition = Extract<ChartWithAxisDefinition, {
zoomable?: boolean;
}>;
type ChartJSRuntime = LineChartRuntime | PieChartRuntime | BarChartRuntime | ComboChartRuntime | ScatterChartRuntime | WaterfallChartRuntime | PyramidChartRuntime | RadarChartRuntime | GeoChartRuntime | FunnelChartRuntime | SunburstChartRuntime | TreeMapChartRuntime;
type ChartRuntime = ChartJSRuntime | ScorecardChartRuntime | GaugeChartRuntime;
interface LabelValues {
readonly values: string[];
readonly formattedValues: string[];
}
interface DatasetValues {
readonly label?: string;
readonly data: any[];
readonly hidden?: boolean;
}
interface DatasetDesign {
readonly backgroundColor?: string;
readonly yAxisId?: string;
readonly label?: string;
}
interface AxisDesign {
readonly title?: TitleDesign;
}
interface AxesDesign {
readonly x?: AxisDesign;
readonly y?: AxisDesign;
readonly y1?: AxisDesign;
}
interface ChartStyle {
readonly bold?: boolean;
readonly italic?: boolean;
readonly align?: Align;
readonly verticalAlign?: VerticalAlign;
readonly color?: Color;
readonly fontSize?: number;
readonly fillColor?: Color;
}
interface TitleDesign extends ChartStyle {
readonly text?: string;
}
type TrendType = "polynomial" | "exponential" | "logarithmic" | "trailingMovingAverage";
interface TrendConfiguration {
type?: TrendType;
order?: number;
color?: Color;
display?: boolean;
window?: number;
}
type CustomizedDataSet = {
readonly dataRange: string;
readonly trend?: TrendConfiguration;
} & DatasetDesign;
type AxisType = "category" | "linear" | "time";
type ChartDatasetOrientation = "rows" | "columns";
interface DataSet {
readonly labelCell?: Range;
readonly dataRange: Range;
readonly rightYAxis?: boolean;
readonly backgroundColor?: Color;
readonly customLabel?: string;
readonly trend?: TrendConfiguration;
}
interface ExcelChartDataset {
readonly label?: {
text?: string;
} | {
reference?: string;
};
readonly range: string;
readonly backgroundColor?: Color;
readonly rightYAxis?: boolean;
readonly trend?: ExcelChartTrendConfiguration;
}
interface ExcelChartTrendConfiguration {
readonly type?: ExcelTrendlineType;
readonly order?: number;
readonly color?: Color;
readonly window?: number;
}
type ExcelTrendlineType = "poly" | "exp" | "log" | "movingAvg" | "linear";
type ExcelChartType = "line" | "bar" | "pie" | "combo" | "scatter" | "radar" | "pyramid";
interface ExcelChartDefinition {
readonly title?: TitleDesign;
readonly type: ExcelChartType;
readonly dataSets: ExcelChartDataset[];
readonly labelRange?: string;
readonly backgroundColor: XlsxHexColor;
readonly fontColor: XlsxHexColor;
readonly legendPosition: LegendPosition;
readonly stacked?: boolean;
readonly cumulative?: boolean;
readonly verticalAxis?: {
useLeftAxis?: boolean;
useRightAxis?: boolean;
};
readonly axesDesign?: AxesDesign;
readonly horizontal?: boolean;
readonly isDoughnut?: boolean;
readonly pieHolePercentage?: number;
readonly maxValue?: number;
}
interface ChartCreationContext {
readonly range?: CustomizedDataSet[];
readonly hierarchicalRanges?: CustomizedDataSet[];
readonly title?: TitleDesign;
readonly background?: Color;
readonly auxiliaryRange?: string;
readonly aggregated?: boolean;
readonly stacked?: boolean;
readonly cumulative?: boolean;
readonly dataSetsHaveTitle?: boolean;
readonly labelsAsText?: boolean;
readonly showSubTotals?: boolean;
readonly showConnectorLines?: boolean;
readonly firstValueAsSubtotal?: boolean;
readonly legendPosition?: LegendPosition;
readonly axesDesign?: AxesDesign;
readonly fillArea?: boolean;
readonly showValues?: boolean;
readonly funnelColors?: FunnelChartColors;
readonly showLabels?: boolean;
readonly hideDataMarkers?: boolean;
readonly valuesDesign?: ChartStyle;
readonly groupColors?: (Color | undefined | null)[];
readonly horizontal?: boolean;
readonly isDoughnut?: boolean;
readonly pieHolePercentage?: number;
readonly showHeaders?: boolean;
readonly headerDesign?: TitleDesign;
readonly treemapColoringOptions?: TreeMapColoringOptions;
readonly zoomable?: boolean;
readonly humanize?: boolean;
}
type ChartAxisFormats = {
[axisId: string]: Format | undefined;
} | undefined;
interface ChartRuntimeGenerationArgs {
dataSetsValues: DatasetValues[];
axisFormats: ChartAxisFormats;
labels: string[];
locale: Locale;
trendDataSetsValues?: (Point[] | undefined)[];
axisType?: AxisType;
topPadding?: number;
}
/** Generic definition of chart to create a runtime: omit the chart type and the dataRange of the dataSets*/
type GenericDefinition<T extends ChartWithDataSetDefinition> = Partial<Omit<T, "dataSets" | "type">> & {
dataSets?: Omit<T["dataSets"][number], "dataRange">[];
};
/**
* This is a generic event bus based on the Owl event bus.
* This bus however ensures type safety across events and subscription callbacks.
*/
declare class EventBus<Event extends {
type: string;
}> {
subscriptions: {
[eventType: string]: Subscription[];
};
/**
* Add a listener for the 'eventType' events.
*
* Note that the 'owner' of this event can be anything, but will more likely
* be a component or a class. The idea is that the callback will be called with
* the proper owner bound.
*
* Also, the owner should be kind of unique. This will be used to remove the
* listener.
*/
on<T extends Event["type"], E extends Extract<Event, {
type: T;
}>>(type: T, owner: any, callback: (r: Omit<E, "type">) => void): void;
/**
* Emit an event of type 'eventType'. Any extra arguments will be passed to
* the listeners callback.
*/
trigger<T extends Event["type"], E extends Extract<Event, {
type: T;
}>>(type: T, payload?: Omit<E, "type">): void;
/**
* Remove a listener
*/
off<T extends Event["type"]>(eventType: T, owner: any): void;
/**
* Remove all subscriptions.
*/
clear(): void;
}
type Callback = (...args: any[]) => void;
interface Subscription {
owner: any;
callback: Callback;
}
declare class SelectiveHistory<T = unknown> {
private HEAD_BRANCH;
private HEAD_OPERATION;
private tree;
private readonly applyOperation;
private readonly revertOperation;
private readonly buildEmpty;
private readonly buildTransformation;
/**
* The selective history is a data structure used to register changes/updates of a state.
* Each change/update is called an "operation".
* The data structure allows to easily cancel (and redo) any operation individually.
* An operation can be represented by any data structure. It can be a "command", a "diff", etc.
* However it must have the following properties:
* - it can be applied to modify the state
* - it can be reverted on the state such that it was never executed.
* - it can be transformed given other operation (Operational Transformation)
*
* Since this data structure doesn't know anything about the state nor the structure of
* operations, the actual work must be performed by external functions given as parameters.
* @param initialOperationId
* @param applyOperation a function which can apply an operation to the state
* @param revertOperation a function which can revert an operation from the state
* @param buildEmpty a function returning an "empty" operation.
* i.e an operation that leaves the state unmodified once applied or reverted
* (used for internal implementation)
* @param buildTransformation Factory used to build transformations
*/
constructor(args: {
initialOperationId: UID;
applyOperation: (data: T) => void;
revertOperation: (data: T) => void;
buildEmpty: (id: UID) => T;
buildTransformation: TransformationFactory<T>;
});
/**
* Return the operation identified by its id.
*/
get(operationId: UID): T;
/**
* Append a new operation as the last one
*/
append(operationId: UID, data: T): void;
/**
* Insert a new operation after a specific operation (may not be the last operation).
* Following operations will be transformed according
* to the new operation.
*/
insert(operationId: UID, data: T, insertAfter: UID): void;
/**
* @param operationId operation to undo
* @param undoId the id of the "undo operation"
* @param insertAfter the id of the operation after which to insert the undo
*/
undo(operationId: UID, undoId: UID, insertAfter: UID): void;
/**
* @param operationId operation to redo
* @param redoId the if of the "redo operation"
* @param insertAfter the id of the operation after which to insert the redo
*/
redo(operationId: UID, redoId: UID, insertAfter: UID): void;
rebase(operationId: UID): void;
/**
* Revert the state as it was *before* the given operation was executed.
*/
private revertBefore;
/**
* Revert the state as it was *after* the given operation was executed.
*/
private revertTo;
/**
* Revert an execution
*/
private revert;
/**
* Replay the operations between the current HEAD_BRANCH and the end of the tree
*/
private fastForward;
}
type ClientId = string;
interface Client {
id: ClientId;
name: string;
position?: ClientPosition;
color?: Color;
}
interface ClientWithPosition extends Client {
position: ClientPosition;
}
interface ClientWithColor extends Client {
color: Color;
}
interface ClientPosition {
sheetId: UID;
col: HeaderIndex;
row: HeaderIndex;
}
interface RemoteRevisionReceivedEvent {
type: "remote-revision-received";
commands: readonly CoreCommand[];
}
interface RevisionAcknowledgedEvent {
type: "revision-acknowledged";
revisionId: UID;
}
interface RevisionUndone {
type: "revision-undone";
revisionId: UID;
commands: readonly CoreCommand[];
}
interface RevisionRedone {
type: "revision-redone";
revisionId: UID;
commands: readonly CoreCommand[];
}
interface CollaborativeEventReceived {
type: "collaborative-event-received";
}
interface UnexpectedRevisionIdEvent {
type: "unexpected-revision-id";
}
interface NewLocalStateUpdateEvent {
type: "new-local-state-update";
id: UID;
}
interface SnapshotEvent {
type: "snapshot";
}
type CollaborativeEvent = NewLocalStateUpdateEvent | UnexpectedRevisionIdEvent | RemoteRevisionReceivedEvent | RevisionAcknowledgedEvent | RevisionUndone | RevisionRedone | SnapshotEvent | CollaborativeEventReceived;
type CollaborativeEventTypes = CollaborativeEvent["type"];
declare class PositionMap<T> {
private map;
constructor(entries?: Iterable<readonly [CellPosition, T]>);
set({ sheetId, col, row }: CellPosition, value: T): void;
setMany(values: Iterable<[CellPosition, T]>): void;
get({ sheetId, col, row }: CellPosition): T | undefined;
getSheet(sheetId: UID): Record<number, Record<number, T>> | undefined;
clearSheet(sheetId: UID): void;
has({ sheetId, col, row }: CellPosition): boolean;
delete({ sheetId, col, row }: CellPosition): void;
keys(): CellPosition[];
keysForSheet(sheetId: UID): CellPosition[];
entries(): IterableIterator<[CellPosition, T]>;
}
/**
* RGBA to HEX representation (#RRGGBBAA).
*
* https://css-tricks.com/converting-color-spaces-in-javascript/
*/
declare function rgbaToHex(rgba: RGBA): Color;
/**
* Color string to RGBA representation
*/
declare function colorToRGBA(color: Color): RGBA;
declare class ColorGenerator {
private preferredColors;
private currentColorIndex;
protected palette: Color[];
constructor(paletteSize: number, preferredColors?: (Color | undefined | null)[]);
next(): string;
}
interface RangePart {
readonly colFixed: boolean;
readonly rowFixed: boolean;
}
interface Range {
readonly zone: Readonly<Zone>;
readonly unboundedZone: Readonly<UnboundedZone>;
readonly parts: readonly RangePart[];
readonly invalidXc?: string;
/** true if the user provided the range with the sheet name */
readonly prefixSheet: boolean;
/** the name of any sheet that is invalid */
readonly invalidSheetName?: string;
/** the sheet on which the range is defined */
readonly sheetId: UID;
}
interface RangeStringOptions {
useBoundedReference?: boolean;
useFixedReference?: boolean;
}
interface RangeData {
_zone: Zone | UnboundedZone;
_sheetId: UID;
}
interface SearchOptions {
matchCase: boolean;
exactMatch: boolean;
searchFormulas: boolean;
searchScope: "allSheets" | "activeSheet" | "specificRange";
specificRange?: Range;
}
/**
* Deep copy arrays, plain objects and primitive values.
* Throws an error for other types such as class instances.
* Sparse arrays remain sparse.
*/
declare function deepCopy<T>(obj: T): T;
declare function unquote(string: string, quoteChar?: "'" | '"'): string;
/** Replace the excel-excluded characters of a sheetName */
declare function sanitizeSheetName(sheetName: string, replacementChar?: string): string;
declare function isMarkdownLink(str: string): boolean;
/**
* Build a markdown link from a label and an url
*/
declare function markdownLink(label: string, url: string): string;
declare function parseMarkdownLink(str: string): {
url: string;
label: string;
};
/**
* This helper function can be used as a type guard when filtering arrays.
* const foo: number[] = [1, 2, undefined, 4].filter(isDefined)
*/
declare function isDefined<T>(argument: T | undefined): argument is T;
/**
* Lazy value computed by the provided function.
*/
declare function lazy<T>(fn: (() => T) | T): Lazy<T>;
/**
* Compares n objects.
*/
declare function deepEquals(...o: any[]): boolean;
declare function getUniqueText(text: string, texts: string[], options?: {
compute?: (text: string, increment: number) => string;
start?: number;
computeFirstOne?: boolean;
}): string;
/**
* Convert a (col) number to the corresponding letter.
*
* Examples:
* 0 => 'A'
* 25 => 'Z'
* 26 => 'AA'
* 27 => 'AB'
*/
declare function numberToLetters(n: number): string;
declare function lettersToNumber(letters: string): number;
/**
* Convert a "XC" coordinate to cartesian coordinates.
*
* Examples:
* A1 => [0,0]
* B3 => [1,2]
*
* Note: it also accepts lowercase coordinates, but not fixed references
*/
declare function toCartesian(xc: string): Position$1;
/**
* Convert from cartesian coordinate to the "XC" coordinate system.
*
* Examples:
* - 0,0 => A1
* - 1,2 => B3
* - 0,0, {colFixed: false, rowFixed: true} => A$1
* - 1,2, {colFixed: true, rowFixed: false} => $B3
*/
declare function toXC(col: HeaderIndex, row: HeaderIndex, rangePart?: RangePart): string;
/**
* A DateTime object that can be used to manipulate spreadsheet dates.
* Conceptually, a spreadsheet date is simply a number with a date format,
* and it is timezone-agnostic.
* This DateTime object consistently uses UTC time to represent a naive date and time.
*/
declare class DateTime {
private jsDate;
constructor(year: number, month: number, day: number, hours?: number, minutes?: number, seconds?: number);
static fromTimestamp(timestamp: number): DateTime;
static now(): DateTime;
toString(): string;
toLocaleDateString(): string;
getTime(): number;
getFullYear(): number;
getMonth(): number;
getQuarter(): number;
getDate(): number;
getDay(): number;
getHours(): number;
getMinutes(): number;
getSeconds(): number;
getIsoWeek(): number;
setFullYear(year: number): number;
setMonth(month: number): number;
setDate(date: number): number;
setHours(hours: number): number;
setMinutes(minutes: number): number;
setSeconds(seconds: number): number;
}
declare function isDateTime(str: string, locale: Locale): boolean;
interface FormatWidth {
availableWidth: number;
measureText: (text: string) => number;
}
/**
* Formats a cell value with its format.
*/
declare function formatValue(value: CellValue, { format, locale, formatWidth }: LocaleFormat & {
formatWidth?: FormatWidth;
}): FormattedValue;
declare function createCurrencyFormat(currency: Partial<Currency>): Format;
/**
* Return true if the argument is a "number string".
*
* Note that "" (empty string) does not count as a number string
*/
declare function isNumber(value: string | undefined, locale: Locale): boolean;
/**
* Registry
*
* The Registry class is basically just a mapping from a string key to an object.
* It is really not much more than an object. It is however useful for the
* following reasons:
*
* 1. it let us react and execute code when someone add something to the registry
* (for example, the FunctionRegistry subclass this for this purpose)
* 2. it throws an error when the get operation fails
* 3. it provides a chained API to add items to the registry.
*/
declare class Registry<T> {
content: {
[key: string]: T;
};
/**
* Add an item to the registry, you can only add if there is no item
* already present in the registery with the given key
*
* Note that this also returns the registry, so another add method call can
* be chained
*/
add(key: string, value: T): this;
/**
* Replace (or add) an item to the registry
*
* Note that this also returns the registry, so another add method call can
* be chained
*/
replace(key: string, value: T): this;
/**
* Get an item from the registry
*/
get(key: string): T;
/**
* Check if the key is already in the registry
*/
contains(key: string): boolean;
/**
* Get a list of all elements in the registry
*/
getAll(): T[];
/**
* Get a list of all keys in the registry
*/
getKeys(): string[];
/**
* Remove an item from the registry
*/
remove(key: string): void;
}
declare function splitReference(ref: string): {
sheetName?: string;
xc: string;
};
declare function computeTextWidth(context: CanvasRenderingContext2D, text: string, style: Style, fontUnit?: "px" | "pt"): number;
declare class UuidGenerator {
/**
* Generates a custom UUID using a simple 36^12 method (8-character alphanumeric string with lowercase letters)
* This has a higher chance of collision than a UUIDv4, but not only faster to generate than an UUIDV4,
* it also has a smaller size, which is preferable to alleviate the overall data size.
*
* This method is preferable when generating uuids for the core data (sheetId, figureId, etc)
* as they will appear several times in the revisions and local history.
*
*/
smallUuid(): string;
/**
* Generates an UUIDV4, has astronomically low chance of collision, but is larger in size than the smallUuid.
* This method should be used when you need to avoid collisions at all costs, like the id of a revision.
*/
uuidv4(): string;
}
/**
* Convert from a cartesian reference to a (possibly unbounded) Zone
*
* Examples:
* "A1" ==> Top 0, Bottom 0, Left: 0, Right: 0
* "B1:B3" ==> Top 0, Bottom 3, Left: 1, Right: 1
* "B:B" ==> Top 0, Bottom undefined, Left: 1, Right: 1
* "B2:B" ==> Top 1, Bottom undefined, Left: 1, Right: 1, hasHeader: 1
* "Sheet1!A1" ==> Top 0, Bottom 0, Left: 0, Right: 0
* "Sheet1!B1:B3" ==> Top 0, Bottom 3, Left: 1, Right: 1
*
* @param xc the string reference to convert
*
*/
declare function toUnboundedZone(xc: string): UnboundedZone;
/**
* Convert from a cartesian reference to a Zone.
* Will return throw an error if given a unbounded zone (eg : A:A).
*
* Examples:
* "A1" ==> Top 0, Bottom 0, Left: 0, Right: 0
* "B1:B3" ==> Top 0, Bottom 2, Left: 1, Right: 1
* "Sheet1!A1" ==> Top 0, Bottom 0, Left: 0, Right: 0
* "Sheet1!B1:B3" ==> Top 0, Bottom 2, Left: 1, Right: 1
*
* @param xc the string reference to convert
*
*/
declare function toZone(xc: string): Zone;
/**
* Expand a zone after inserting columns or rows.
*
* Don't resize the zone if a col/row was added right before/after the row but only move the zone.
*/
declare function expandZoneOnInsertion<Z extends UnboundedZone | Zone>(zone: Z, start: "left" | "top", base: number, position: "after" | "before", quantity: number): Z;
/**
* Reduce a zone after deletion of elements
*/
declare function reduceZoneOnDeletion<Z extends UnboundedZone | Zone>(zone: Z, start: "left" | "top", elements: number[]): Z | undefined;
/**
* Compute the union of multiple zones.
*/
declare function union(...zones: Zone[]): Zone;
/**
* Return true if two zones overlap, false otherwise.
*/
declare function overlap(z1: UnboundedZone, z2: UnboundedZone): boolean;
declare function isInside(col: number, row: number, zone: Zone): boolean;
/**
* This function will compare the modifications of selection to determine
* a cell that is part of the new zone and not the previous one.
*/
declare function findCellInNewZone(oldZone: Zone, currentZone: Zone): Position$1;
declare function positionToZone(position: Position$1): Zone;
/**
* Merge contiguous and overlapping zones that are in the array into bigger zones
*/
declare function mergeContiguousZones(zones: Zone[]): Zone[];
type SelectionEventOptions = {
scrollIntoView?: boolean;
unbounded?: boolean;
};
interface SelectionEvent {
anchor: AnchorZone;
previousAnchor: AnchorZone;
mode: "newAnchor" | "overrideSelection" | "updateAnchor";
options: SelectionEventOptions;
}
interface StreamCallbacks<Event> {
handleEvent: (event: Event) => void;
/** this callback will only be called when another consumer captures the stream,
* not when the current consumer decides to release itself */
release?: () => void;
}
type StatefulStream<Event, State> = {
capture(owner: unknown, state: State, callbacks: StreamCallbacks<Event>): void;
registerAsDefault: (owner: unknown, state: State, callbacks: StreamCallbacks<Event>) => void;
resetDefaultAnchor: (owner: unknown, state: State) => void;
resetAnchor: (owner: unknown, state: State) => void;
observe: (owner: unknown, callbacks: StreamCallbacks<Event>) => void;
release: (owner: unknown) => void;
getBackToDefault(): void;
};
/**
* Allows to select cells in the grid and update the selection
*/
interface SelectionProcessor {
selectZone(anchor: AnchorZone, options?: SelectionEventOptions): DispatchResult;
selectCell(col: number, row: number): DispatchResult;
moveAnchorCell(direction: Direction$1, step: SelectionStep): DispatchResult;
setAnchorCorner(col: number, row: number): DispatchResult;
addCellToSelection(col: number, row: number): DispatchResult;
resizeAnchorZone(direction: Direction$1, step: SelectionStep): DispatchResult;
selectColumn(index: number, mode: SelectionEvent["mode"]): DispatchResult;
selectRow(index: number, mode: SelectionEvent["mode"]): DispatchResult;
selectAll(): DispatchResult;
loopSelection(): DispatchResult;
selectTableAroundSelection(): DispatchResult;
isListening(owner: unknown): boolean;
}
type SelectionStreamProcessor = SelectionProcessor & StatefulStream<SelectionEvent, AnchorZone>;
type FilePath = string;
/**
* FileStore manage the transfer of file with the server.
*/
interface FileStore {
/**
* Upload a file to a server and returns its path.
*/
upload(file: File): Promise<FilePath>;
/**
* Delete a file from the server
*/
delete(filePath: FilePath): Promise<void>;
/**
* get File from the server
*/
getFile(filePath: FilePath): Promise<File | Blob>;
}
/**
* ImageProvider can request the user to input an image file before sending it to a server.
*/
interface ImageProviderInterface {
/**
* RequestImage ask the user to input an image file. Then send it to a server trough an FileStore. Finally it return the path and the size of the image in the server.
*/
requestImage(): Promise<Image$1>;
uploadFile(file: File | Blob): Promise<Image$1>;
getImageOriginalSize(path: string): Promise<FigureSize>;
}
/**
* Model
*
* The Model class is the owner of the state of the Spreadsheet. However, it
* has more a coordination role: it defers the actual state manipulation work to
* plugins.
*
* At creation, the Model instantiates all necessary plugins. They each have
* a private state (for example, the Selection plugin has the current selection).
*
* State changes are then performed through commands. Commands are dispatched
* to the model, which will then relay them to each plugins (and the history
* handler). Then, the model will trigger an 'update' event to notify whoever
* is concerned that the command was applied (if it was not cancelled).
*
* Also, the model has an unconventional responsibility: it actually renders the
* visible viewport on a canvas. This is because each plugins actually manage a
* specific concern about the content of the spreadsheet, and it is more natural
* if they are able to read data from their internal state to represent it on the
* screen.
*
* Note that the Model can be used in a standalone way to manipulate
* programmatically a spreadsheet.
*/
type Mode = "normal" | "readonly" | "dashboard";
interface ModelConfig {
readonly mode: Mode;
/**
* Any external custom dependencies your custom plugins or functions might need.
* They are available in plugins config and functions
* evaluation context.
*/
readonly custom: Readonly<{
[key: string]: any;
}>;
readonly defaultCurrency?: Partial<Currency>;
/**
* External dependencies required to enable some features
* such as uploading images.
*/
readonly external: Readonly<ModelExternalConfig>;
readonly moveClient: (position: ClientPosition) => void;
readonly transportService: TransportService;
readonly client: Client;
readonly snapshotRequested: boolean;
readonly notifyUI: (payload: InformationNotification) => void;
readonly raiseBlockingErrorUI: (text: string) => void;
readonly customColors: Color[];
}
interface ModelExternalConfig {
readonly fileStore?: FileStore;
readonly loadCurrencies?: () => Promise<Currency[]>;
readonly loadLocales?: () => Promise<Locale[]>;
readonly geoJsonService?: {
getAvailableRegions: () => GeoChartRegion[];
getTopoJson: (region: string) => Promise<any>;
/** Convert the name of a geographical feature (eg. France) to the id of the corresponding feature in the TopoJSON */
geoFeatureNameToId: (region: string, territory: string) => string | undefined;
};
}
declare class Model extends EventBus<any> implements CommandDispatcher {
private corePlugins;
private statefulUIPlugins;
private range;
private session;
/**
* In a collaborative context, some commands can be replayed, we have to ensure
* that these commands are not replayed on the UI plugins.
*/
private isReplayingCommand;
/**
* A plugin can draw some contents on the canvas. But even better: it can do
* so multiple times. The order of the render calls will determine a list of
* "layers" (i.e., earlier calls will be obviously drawn below later calls).
* This list simply keeps the renderers+layer information so the drawing code
* can just iterate on it
*/
private renderers;
/**
* Internal status of the model. Important for command handling coordination
*/
private status;
/**
* The config object contains some configuration flag and callbacks
*/
readonly config: ModelConfig;
private corePluginConfig;
private coreViewPluginConfig;
private uiPluginConfig;
private state;
readonly selection: SelectionStreamProcessor;
/**
* Getters are the main way the rest of the UI read data from the model. Also,
* it is shared between all plugins, so they can also communicate with each
* other.
*/
getters: Getters;
/**
* Getters that are accessible from the core plugins. It's a subset of `getters`,
* without the UI getters
*/
private coreGetters;
uuidGenerator: UuidGenerator;
private readonly handlers;
private readonly uiHandlers;
private readonly coreHandlers;
constructor(data?: any, config?: Partial<ModelConfig>, stateUpdateMessages?: StateUpdateMessage[], uuidGenerator?: UuidGenerator, verboseImport?: boolean);
joinSession(): void;
leaveSession(): Promise<void>;
private setupUiPlugin;
private setupCoreViewPlugin;
/**
* Initialize and properly configure a plugin.
*
* This method is private for now, but if the need arise, there is no deep
* reason why the model could not add dynamically a plugin while it is running.
*/
private setupCorePlugin;
private onRemoteRevisionReceived;
private setupSession;
private setupSessionEvents;
private setupConfig;
private setupExternalConfig;
private setupCorePluginConfig;
private setupCoreViewPluginConfig;
private setupUiPluginConfig;
/**
* Check if the given command is allowed by all the plugins and the history.
*/
private checkDispatchAllowed;
private checkDispatchAllowedCoreCommand;
private checkDispatchAllowedLocalCommand;
private finalize;
/**
* Check if a command can be dispatched, and returns a DispatchResult object with the possible
* reasons the dispatch failed.
*/
canDispatch: CommandDispatcher["dispatch"];
/**
* The dispatch method is the only entry point to manipulate data in the model.
* This is through this method that commands are dispatched most of the time
* recursively until no plugin want to react anymore.
*
* CoreCommands dispatched from this function are saved in the history.
*
* Small technical detail: it is defined as an arrow function. There are two
* reasons for this:
* 1. this means that the dispatch method can be "detached" from the model,
* which is done when it is put in the environment (see the Spreadsheet
* component)
* 2. This allows us to define its type by using the interface CommandDispatcher
*/
dispatch: CommandDispatcher["dispatch"];
/**
* Dispatch a command from a Core Plugin (or the History).
* A command dispatched from this function is not added to the history.
*/
private dispatchFromCorePlugin;
/**
* Dispatch the given command to the given handlers.
* It will call `beforeHandle` and `handle`
*/
private dispatchToHandlers;
/**
* When the Grid component is ready (= mounted), it has a reference to its
* canvas and need to draw the grid on it. This is then done by calling this
* method, which will dispatch the call to all registered plugins.
*
* Note that nothing prevent multiple grid components from calling this method
* each, or one grid component calling it multiple times with a different
* context. This is probably the way we should do if we want to be able to
* freeze a part of the grid (so, we would need to render different zones)
*/
drawLayer(context: GridRenderingContext, layer: LayerName): void;
/**
* As the name of this method strongly implies, it is useful when we need to
* export date out of the model.
*/
exportData(): WorkbookData;
updateMode(mode: Mode): void;
/**
* Exports the current model data into a list of serialized XML files
* to be zipped together as an *.xlsx file.
*
* We need to trigger a cell revaluation on every sheet and ensure that even
* async functions are evaluated.
* This prove to be necessary if the client did not trigger that evaluation in the first place
* (e.g. open a document with several sheet and click on download before visiting each sheet)
*/
exportXLSX(): XLSXExport;
garbageCollectExternalResources(): void;
}
type HistoryPath = [any, ...(number | string)[]];
declare class StateObserver {
private changes;
private commands;
/**
* Record the changes which could happen in the given callback, save them in a
* new revision with the given id and userId.
*/
recordChanges(callback: () => void): {
changes: HistoryChange[];
commands: CoreCommand[];
};
addCommand(command: CoreCommand): void;
addChange(...args: [...HistoryPath, any]): void;
}
interface CarouselState {
readonly carousels: Record<UID, Record<UID, Carousel | undefined> | undefined>;
}
declare class CarouselPlugin extends CorePlugin<CarouselState> implements CarouselState {
static getters: readonly ["getCarousel", "doesCarouselExist"];
readonly carousels: Record<UID, Record<UID, Carousel | undefined> | undefined>;
allowDispatch(cmd: CoreCommand): CommandResult.Success | CommandResult.InvalidFigureId | CommandResult.DuplicatedFigureId;