Untitled

mail@pastecode.io avatar
unknown
javascript
20 days ago
20 kB
7
Indexable
Never
import React, { lazy, useMemo, useState } from 'react'
import {
  SpreadsheetProvider,
  CanvasGrid,
  Sheet,
  CellData,
  Toolbar,
  ButtonRedo,
  ButtonUndo,
  ScaleSelector,
  ToolbarSeparator,
  BackgroundColorSelector,
  BorderSelector,
  ButtonBold,
  ButtonDecreaseDecimal,
  ButtonFormatCurrency,
  ButtonFormatPercent,
  ButtonIncreaseDecimal,
  ButtonInsertImage,
  ButtonItalic,
  ButtonStrikethrough,
  ButtonSwitchColorMode,
  ButtonUnderline,
  DEFAULT_FONT_SIZE_PT,
  FontFamilySelector,
  FontSizeSelector,
  MergeCellsSelector,
  TextColorSelector,
  TextFormatSelector,
  TextHorizontalAlignSelector,
  TextVerticalAlignSelector,
  TextWrapSelector,
  ThemeSelector,
  SpreadsheetTheme,
  defaultSpreadsheetTheme,
  ColorMode,
  EmbeddedChart,
  EmbeddedObject,
  NamedRange,
  TableView,
  BottomBar,
  NewSheetButton,
  SheetStatus,
  SheetSwitcher,
  SheetTabs,
  FormulaBar,
  FormulaBarInput,
  FormulaBarLabel,
  RangeSelector,
  SheetSearch,
  TableStyleSelector,
  ConditionalFormatRule,
  useSpreadsheetApi,
  ProtectedRange,
} from '@rowsncolumns/spreadsheet'

import {
  DeleteSheetConfirmation,
  NamedRangeEditor,
  pattern_currency_decimal,
  pattern_percent_decimal,
  SheetData,
  TableEditor,
  useSearch,
  useSpreadsheetState,
} from '@rowsncolumns/spreadsheet-state'
import { IconButton, Separator } from '@rowsncolumns/ui'
import { functionDescriptions, functions } from '@rowsncolumns/functions'
import { MagnifyingGlassIcon } from '@rowsncolumns/icons'
import { useCharts } from '@rowsncolumns/charts'

const SpreadsheetWithState = () => {
  const [sheets, onChangeSheets] = useState<Sheet[]>(initialSheets)
  const [sheetData, onChangeSheetData] = useState<SheetData<CellData>>({})
  const [scale, onChangeScale] = useState(1)
  const [theme, onChangeTheme] = useState<SpreadsheetTheme>(
    defaultSpreadsheetTheme
  )
  const [conditionalFormats, onChangeConditionalFormats] = useState<
    ConditionalFormatRule[]
  >([])
  const [protectedRanges, onChangeProtectedRanges] = useState<ProtectedRange[]>(
    []
  )
  const [colorMode, onChangeColorMode] = useState<ColorMode>()
  const [charts, onChangeCharts] = useState<EmbeddedChart[]>([])
  const [embeds, onChangeEmbeds] = useState<EmbeddedObject[]>([])
  const [tables, onChangeTables] = useState<TableView[]>([])
  const [namedRanges, onChangeNamedRanges] = useState<NamedRange[]>([])
  const locale = 'en-GB'
  const currency = 'USD'

  const {
    activeCell,
    activeSheetId,
    selections,
    rowCount,
    getDataRowCount,
    columnCount,
    frozenColumnCount,
    frozenRowCount,
    rowMetadata,
    columnMetadata,
    merges,
    bandedRanges,
    spreadsheetColors,
    canRedo,
    canUndo,
    onUndo,
    onRedo,
    getCellData,
    getSheetName,
    getEffectiveFormat,
    onRequestCalculate,
    onChangeActiveCell,
    onChangeActiveSheet,
    onSelectNextSheet,
    onSelectPreviousSheet,
    onChangeSelections,
    onChange,
    onDelete,
    onChangeFormatting,
    onRepeatFormatting,
    onClearFormatting,
    onUnMergeCells,
    onMergeCells,
    onResize,
    onChangeBorder,
    onChangeDecimals,
    onChangeSheetTabColor,
    onRenameSheet,
    onRequestDeleteSheet,
    onDeleteSheet,
    onShowSheet,
    onHideSheet,
    onProtectSheet,
    onUnProtectSheet,
    onMoveSheet,
    onCreateNewSheet,
    onDuplicateSheet,
    onHideColumn,
    onShowColumn,
    onHideRow,
    onShowRow,
    onFill,
    onFillRange,
    onMoveEmbed,
    onResizeEmbed,
    onDeleteEmbed,
    onDeleteRow,
    onDeleteColumn,
    onDeleteCellsShiftUp,
    onDeleteCellsShiftLeft,
    onInsertCellsShiftRight,
    onInsertCellsShiftDown,
    onInsertRow,
    onInsertColumn,
    onMoveColumns,
    onMoveRows,
    onMoveSelection,
    onSortColumn,
    onSortTable,
    onFilterTable,
    onResizeTable,
    onCopy,
    onPaste,
    onCreateTable,
    onRequestEditTable,
    onUpdateTable,
    onDragOver,
    onDrop,
    onInsertFile,
    onFreezeColumn,
    onFreezeRow,
    onChangeSpreadsheetTheme,
    onUpdateNote,
    onSortRange,
    onProtectRange,
    onUnProtectRange,
    onRequestDefineNamedRange,
    onRequestUpdateNamedRange,
    onCreateNamedRange,
    onUpdateNamedRange,
    onDeleteNamedRange,

    // For charts
    getSeriesValuesFromRange,
    getDomainValuesFromRange,

    // Create a history stack
    createHistory,

    // Enqueue any calculation manually
    enqueueCalculation,
    getNonEmptyColumnCount,
    getNonEmptyRowCount,
  } = useSpreadsheetState({
    sheets,
    sheetData,
    tables,
    functions,
    namedRanges,
    theme,
    colorMode,
    locale,
    onChangeSheets,
    onChangeSheetData,
    onChangeEmbeds,
    onChangeCharts,
    onChangeTables,
    onChangeNamedRanges,
    onChangeTheme,
    onChangeConditionalFormats,
    onChangeProtectedRanges,
  })

  // Charts module
  const { onDeleteChart, onMoveChart, onResizeChart } = useCharts({
    onChangeCharts,
    createHistory,
  })

  const {
    onSearch,
    onResetSearch,
    onFocusNextResult,
    onFocusPreviousResult,
    hasNextResult,
    hasPreviousResult,
    borderStyles,
    isSearchActive,
    onRequestSearch,
    currentResult,
    totalResults,
    searchQuery,
  } = useSearch({
    sheets,
    getCellData,
    sheetId: activeSheetId,
    getNonEmptyColumnCount,
    getNonEmptyRowCount,
  })

  const currentCellFormat = useMemo(
    () =>
      getEffectiveFormat(
        activeSheetId,
        activeCell.rowIndex,
        activeCell.columnIndex
      ),
    [activeSheetId, activeCell, getEffectiveFormat]
  )

  const api = useSpreadsheetApi()

  return (
    <div className="flex flex-1 flex-col h-full">
      <button
        onClick={() => {
          console.log('api', api.getActiveSheetId())
          api.getSheet(1).getRange({
            startRowIndex: 1,
            endRowIndex: 2,
            startColumnIndex: 2,
            endColumnIndex: 3,
          }).setValues([
            ['foo', 'bar'],
            ['hello', 'world']
          ])
        }}
      >
        hello world
      </button>
      <Toolbar>
        <ButtonUndo onClick={onUndo} disabled={!canUndo} />
        <ButtonRedo onClick={onRedo} disabled={!canRedo} />
        <ToolbarSeparator />
        <ScaleSelector value={scale} onChange={onChangeScale} />
        <ToolbarSeparator />

        <ButtonFormatCurrency
          onClick={() => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'numberFormat',
              {
                type: 'CURRENCY',
                pattern: pattern_currency_decimal,
              }
            )
          }}
        />
        <ButtonFormatPercent
          onClick={() => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'numberFormat',
              {
                type: 'PERCENT',
                pattern: pattern_percent_decimal,
              }
            )
          }}
        />
        <ButtonDecreaseDecimal
          onClick={() =>
            onChangeDecimals(activeSheetId, activeCell, selections, 'decrement')
          }
        />
        <ButtonIncreaseDecimal
          onClick={() =>
            onChangeDecimals(activeSheetId, activeCell, selections, 'increment')
          }
        />
        <TextFormatSelector
          locale={locale}
          currency={currency}
          onChangeFormatting={(type, value) =>
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              type,
              value
            )
          }
        />
        <ToolbarSeparator />
        <FontFamilySelector
          value={currentCellFormat?.textFormat?.fontFamily}
          theme={theme}
          onChange={value => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'textFormat',
              {
                fontFamily: value,
              }
            )
          }}
        />
        <ToolbarSeparator />
        <FontSizeSelector
          value={
            currentCellFormat?.textFormat?.fontSize ?? DEFAULT_FONT_SIZE_PT
          }
          onChange={value => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'textFormat',
              {
                fontSize: Number(value),
              }
            )
          }}
        />
        <ToolbarSeparator />
        <ButtonBold
          isActive={currentCellFormat?.textFormat?.bold}
          onClick={() => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'textFormat',
              {
                bold: !currentCellFormat?.textFormat?.bold,
              }
            )
          }}
        />
        <ButtonItalic
          isActive={currentCellFormat?.textFormat?.italic}
          onClick={() => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'textFormat',
              {
                italic: !currentCellFormat?.textFormat?.italic,
              }
            )
          }}
        />
        <ButtonUnderline
          isActive={currentCellFormat?.textFormat?.underline}
          onClick={() => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'textFormat',
              {
                underline: !currentCellFormat?.textFormat?.underline,
              }
            )
          }}
        />
        <ButtonStrikethrough
          isActive={currentCellFormat?.textFormat?.strikethrough}
          onClick={() => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'textFormat',
              {
                strikethrough: !currentCellFormat?.textFormat?.strikethrough,
              }
            )
          }}
        />
        <TextColorSelector
          color={currentCellFormat?.textFormat?.color}
          theme={theme}
          onChange={color => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'textFormat',
              {
                color,
              }
            )
          }}
        />
        <ToolbarSeparator />
        <BackgroundColorSelector
          color={currentCellFormat?.backgroundColor}
          theme={theme}
          onChange={color => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'backgroundColor',
              color
            )
          }}
        />

        <BorderSelector
          borders={currentCellFormat?.borders}
          onChange={(location, color, style) =>
            onChangeBorder(
              activeSheetId,
              activeCell,
              selections,
              location,
              color,
              style
            )
          }
          theme={theme}
        />
        <MergeCellsSelector
          activeCell={activeCell}
          selections={selections}
          sheetId={activeSheetId}
          merges={merges}
          onUnMerge={onUnMergeCells}
          onMerge={onMergeCells}
        />
        <ToolbarSeparator />
        <TextHorizontalAlignSelector
          value={currentCellFormat?.horizontalAlignment}
          onChange={value => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'horizontalAlignment',
              value
            )
          }}
        />
        <TextVerticalAlignSelector
          value={currentCellFormat?.verticalAlignment}
          onChange={value => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'verticalAlignment',
              value
            )
          }}
        />
        <TextWrapSelector
          value={currentCellFormat?.wrapStrategy}
          onChange={value => {
            onChangeFormatting(
              activeSheetId,
              activeCell,
              selections,
              'wrapStrategy',
              value
            )
          }}
        />
        <ToolbarSeparator />

        <ButtonInsertImage onInsertFile={onInsertFile} />

        <TableStyleSelector
          theme={theme}
          tables={tables}
          activeCell={activeCell}
          selections={selections}
          sheetId={activeSheetId}
          onCreateTable={onCreateTable}
          onUpdateTable={onUpdateTable}
        />
        <ToolbarSeparator />
        <ThemeSelector theme={theme} onChangeTheme={onChangeSpreadsheetTheme} />
        <ButtonSwitchColorMode
          colorMode={colorMode}
          onClick={() =>
            onChangeColorMode(prev => (prev === 'dark' ? 'light' : 'dark'))
          }
        />

        <IconButton onClick={onRequestSearch}>
          <MagnifyingGlassIcon />
        </IconButton>
      </Toolbar>

      <FormulaBar>
        <RangeSelector
          selections={selections}
          activeCell={activeCell}
          onChangeActiveCell={onChangeActiveCell}
          onChangeSelections={onChangeSelections}
          sheets={sheets}
          rowCount={rowCount}
          columnCount={columnCount}
          onChangeActiveSheet={onChangeActiveSheet}
          onRequestDefineNamedRange={onRequestDefineNamedRange}
          onRequestUpdateNamedRange={onRequestUpdateNamedRange}
          onDeleteNamedRange={onDeleteNamedRange}
          namedRanges={namedRanges}
          tables={tables}
          sheetId={activeSheetId}
          merges={merges}
        />
        <Separator orientation="vertical" />
        <FormulaBarLabel>fx</FormulaBarLabel>
        <FormulaBarInput
          sheetId={activeSheetId}
          activeCell={activeCell}
          functionDescriptions={functionDescriptions}
        />
      </FormulaBar>
      <CanvasGrid
        {...spreadsheetColors}
        borderStyles={borderStyles}
        scale={scale}
        conditionalFormats={conditionalFormats}
        sheetId={activeSheetId}
        rowCount={rowCount}
        getDataRowCount={getDataRowCount}
        columnCount={columnCount}
        frozenColumnCount={frozenColumnCount}
        frozenRowCount={frozenRowCount}
        rowMetadata={rowMetadata}
        columnMetadata={columnMetadata}
        activeCell={activeCell}
        selections={selections}
        theme={theme}
        merges={merges}
        charts={charts}
        embeds={embeds}
        tables={tables}
        protectedRanges={protectedRanges}
        bandedRanges={bandedRanges}
        functionDescriptions={functionDescriptions}
        getSheetName={getSheetName}
        getCellData={getCellData}
        onChangeActiveCell={onChangeActiveCell}
        onChangeSelections={onChangeSelections}
        onChangeActiveSheet={onChangeActiveSheet}
        onRequestCalculate={onRequestCalculate}
        onSelectNextSheet={onSelectNextSheet}
        onSelectPreviousSheet={onSelectPreviousSheet}
        onChangeFormatting={onChangeFormatting}
        onRepeatFormatting={onRepeatFormatting}
        onHideColumn={onHideColumn}
        onShowColumn={onShowColumn}
        onHideRow={onHideRow}
        onShowRow={onShowRow}
        onDelete={onDelete}
        onClearContents={onDelete}
        onFill={onFill}
        onFillRange={onFillRange}
        onResize={onResize}
        onMoveChart={onMoveChart}
        onMoveEmbed={onMoveEmbed}
        onResizeChart={onResizeChart}
        onDeleteChart={onDeleteChart}
        onResizeEmbed={onResizeEmbed}
        onDeleteEmbed={onDeleteEmbed}
        onDeleteRow={onDeleteRow}
        onDeleteColumn={onDeleteColumn}
        onDeleteCellsShiftUp={onDeleteCellsShiftUp}
        onDeleteCellsShiftLeft={onDeleteCellsShiftLeft}
        onInsertCellsShiftRight={onInsertCellsShiftRight}
        onInsertCellsShiftDown={onInsertCellsShiftDown}
        onInsertRow={onInsertRow}
        onInsertColumn={onInsertColumn}
        onMoveColumns={onMoveColumns}
        onMoveRows={onMoveRows}
        onMoveSelection={onMoveSelection}
        onCreateNewSheet={onCreateNewSheet}
        onChange={onChange}
        onUndo={onUndo}
        onRedo={onRedo}
        onSortColumn={onSortColumn}
        onSortTable={onSortTable}
        onFilterTable={onFilterTable}
        onResizeTable={onResizeTable}
        onClearFormatting={onClearFormatting}
        onCopy={onCopy}
        onPaste={onPaste}
        onDragOver={onDragOver}
        onDrop={onDrop}
        onCreateTable={onCreateTable}
        onRequestEditTable={onRequestEditTable}
        onRequestDefineNamedRange={onRequestDefineNamedRange}
        onFreezeColumn={onFreezeColumn}
        onFreezeRow={onFreezeRow}
        onUpdateNote={onUpdateNote}
        onSortRange={onSortRange}
        onProtectRange={onProtectRange}
        onUnProtectRange={onUnProtectRange}
        namedRanges={namedRanges}
        licenseKey="joseph-solo-d2bd-1a54-c786-9a09-9d73-9097-2c09-ee30-6b25-e356-56a4-a80b-1412-630b-ecfb-5ed8"
        onRequestSearch={onRequestSearch}
      />

      <BottomBar>
        <NewSheetButton onClick={onCreateNewSheet} />

        <SheetSwitcher
          sheets={sheets}
          activeSheetId={activeSheetId}
          onChangeActiveSheet={onChangeActiveSheet}
          onShowSheet={onShowSheet}
        />

        <SheetTabs
          sheets={sheets}
          protectedRanges={protectedRanges}
          activeSheetId={activeSheetId}
          theme={theme}
          onChangeActiveSheet={onChangeActiveSheet}
          onRenameSheet={onRenameSheet}
          onChangeSheetTabColor={onChangeSheetTabColor}
          onDeleteSheet={onRequestDeleteSheet}
          onHideSheet={onHideSheet}
          onMoveSheet={onMoveSheet}
          onProtectSheet={onProtectSheet}
          onUnProtectSheet={onUnProtectSheet}
          onDuplicateSheet={onDuplicateSheet}
        />

        <SheetStatus
          sheetId={activeSheetId}
          activeCell={activeCell}
          selections={selections}
          onRequestCalculate={onRequestCalculate}
          rowCount={rowCount}
          columnCount={columnCount}
          merges={merges}
        />
      </BottomBar>

      <TableEditor
        sheetId={activeSheetId}
        rowCount={rowCount}
        columnCount={columnCount}
        onSubmit={onUpdateTable}
        theme={theme}
        merges={merges}
      />
      <DeleteSheetConfirmation
        sheetId={activeSheetId}
        onDeleteSheet={onDeleteSheet}
      />
      <NamedRangeEditor
        sheetId={activeSheetId}
        rowCount={rowCount}
        columnCount={columnCount}
        onCreateNamedRange={onCreateNamedRange}
        onUpdateNamedRange={onUpdateNamedRange}
        merges={merges}
      />
      <SheetSearch
        isActive={isSearchActive}
        onSubmit={onSearch}
        onReset={onResetSearch}
        onNext={onFocusNextResult}
        onPrevious={onFocusPreviousResult}
        disableNext={!hasNextResult}
        disablePrevious={!hasPreviousResult}
        currentResult={currentResult}
        totalResults={totalResults}
        searchQuery={searchQuery}
      />
    </div>
  )
}

// Always wrap the component with SpreadsheetProvider
export const SpreadsheetWithStateProvider = ({
  windowId,
}: {
  windowId: string
}) => (
  <SpreadsheetProvider>
    <SpreadsheetWithState />
  </SpreadsheetProvider>
)
Leave a Comment