FIX: Minor cleanups

This commit is contained in:
Nicolás Hatcher
2025-11-17 22:46:42 +01:00
committed by Nicolás Hatcher Andrés
parent 1391f196b5
commit 3bb49d1e8f
5 changed files with 243 additions and 201 deletions

View File

@@ -197,4 +197,40 @@ function getFormulaHTML(
return { html, activeRanges }; return { html, activeRanges };
} }
// Given a formula (without the equals sign) returns (sheetIndex, rowStart, columnStart, rowEnd, columnEnd)
// if it represent a reference or range like `Sheet1!A1` or `Sheet3!D3:D10` in an existing sheet
// If it is not a reference or range it returns null
export function parseRangeInSheet(
model: Model,
formula: string,
): [number, number, number, number, number] | null {
// HACK: We are checking here the series of tokens in the range formula.
// This is enough for our purposes but probably a more specific ranges in formula method would be better.
const worksheets = model.getWorksheetsProperties();
const tokens = getTokens(formula);
const { token } = tokens[0];
if (tokenIsRangeType(token)) {
const {
sheet: refSheet,
left: { row: rowStart, column: columnStart },
right: { row: rowEnd, column: columnEnd },
} = token.Range;
if (refSheet !== null) {
const sheetIndex = worksheets.findIndex((s) => s.name === refSheet);
if (sheetIndex >= 0) {
return [sheetIndex, rowStart, columnStart, rowEnd, columnEnd];
}
}
} else if (tokenIsReferenceType(token)) {
const { sheet: refSheet, row, column } = token.Reference;
if (refSheet !== null) {
const sheetIndex = worksheets.findIndex((s) => s.name === refSheet);
if (sheetIndex >= 0) {
return [sheetIndex, row, column, row, column];
}
}
}
return null;
}
export default getFormulaHTML; export default getFormulaHTML;

View File

@@ -1,4 +1,4 @@
import type { DefinedName, WorksheetProperties } from "@ironcalc/wasm"; import type { DefinedName, Model } from "@ironcalc/wasm";
import { import {
Box, Box,
FormControl, FormControl,
@@ -10,43 +10,60 @@ import {
TextField, TextField,
} from "@mui/material"; } from "@mui/material";
import { t } from "i18next"; import { t } from "i18next";
import { Check, Tag } from "lucide-react"; import { Check, MousePointerClick, Tag } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { theme } from "../../../theme"; import { theme } from "../../../theme";
import { getFullRangeToString } from "../../util";
import { Footer, NewButton } from "./NamedRanges"; import { Footer, NewButton } from "./NamedRanges";
export interface SaveError { export interface SaveError {
nameError?: string; nameError: string;
formulaError?: string; formulaError: string;
} }
interface EditNamedRangeProps { interface EditNamedRangeProps {
worksheets: WorksheetProperties[];
name: string; name: string;
scope: string; scope: string;
formula: string; formula: string;
model: Model;
onSave: (name: string, scope: string, formula: string) => SaveError; onSave: (name: string, scope: string, formula: string) => SaveError;
onCancel: () => void; onCancel: () => void;
definedNameList: DefinedName[];
editingDefinedName: DefinedName | null; editingDefinedName: DefinedName | null;
} }
function EditNamedRange({ // HACK: We are using the text structure of the server error
worksheets, // to add an error here. This is wrong for several reasons:
// 1. There is no i18n
// 2. Server error messages could change with no warning
export function formatOnSaveError(error: string): SaveError {
if (error.startsWith("Name: ")) {
return { formulaError: "", nameError: error.slice(6) };
} else if (error.startsWith("Formula: ")) {
return { formulaError: error.slice(9), nameError: "" };
} else if (error.startsWith("Scope: ")) {
return { formulaError: "", nameError: error.slice(7) };
}
// Fallback for other errors
return { formulaError: error, nameError: "" };
}
const EditNamedRange = ({
name: initialName, name: initialName,
scope: initialScope, scope: initialScope,
formula: initialFormula, formula: initialFormula,
onSave, onSave,
onCancel, onCancel,
definedNameList,
editingDefinedName, editingDefinedName,
}: EditNamedRangeProps) { model,
}: EditNamedRangeProps) => {
const getDefaultName = () => { const getDefaultName = () => {
if (initialName) return initialName; if (initialName) return initialName;
let counter = 1; let counter = 1;
let defaultName = `Range${counter}`; let defaultName = `Range${counter}`;
const worksheets = model.getWorksheetsProperties();
const scopeIndex = worksheets.findIndex((s) => s.name === initialScope); const scopeIndex = worksheets.findIndex((s) => s.name === initialScope);
const newScope = scopeIndex >= 0 ? scopeIndex : null; const newScope = scopeIndex >= 0 ? scopeIndex : undefined;
const definedNameList = model.getDefinedNameList();
while ( while (
definedNameList.some( definedNameList.some(
@@ -69,38 +86,27 @@ function EditNamedRange({
// Validate name (format and duplicates) // Validate name (format and duplicates)
useEffect(() => { useEffect(() => {
const trimmed = name.trim(); const worksheets = model.getWorksheetsProperties();
let error = "";
if (!trimmed) {
error = t("name_manager_dialog.errors.range_name_required");
} else if (trimmed.includes(" ")) {
error = t("name_manager_dialog.errors.name_cannot_contain_spaces");
} else if (/^\d/.test(trimmed)) {
error = t("name_manager_dialog.errors.name_cannot_start_with_number");
} else if (!/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(trimmed)) {
error = t("name_manager_dialog.errors.name_invalid_characters");
} else {
// Check for duplicates only if format is valid
const scopeIndex = worksheets.findIndex((s) => s.name === scope); const scopeIndex = worksheets.findIndex((s) => s.name === scope);
const newScope = scopeIndex >= 0 ? scopeIndex : undefined; const newScope = scopeIndex >= 0 ? scopeIndex : null;
const existing = definedNameList.find( try {
(dn) => model.isValidDefinedName(name, newScope, formula);
dn.name === trimmed && } catch (e) {
dn.scope === newScope && const message = (e as Error).message;
!( if (editingDefinedName && message.includes("already exists")) {
editingDefinedName?.name === dn.name && // Allow the same name if it's the one being edited
editingDefinedName?.scope === dn.scope setNameError("");
),
);
if (existing) {
error = t("name_manager_dialog.errors.name_already_exists");
}
}
setNameError(error);
setFormulaError(""); setFormulaError("");
}, [name, scope, definedNameList, editingDefinedName, worksheets]); return;
}
const { nameError, formulaError } = formatOnSaveError(message);
setNameError(nameError);
setFormulaError(formulaError);
return;
}
setNameError("");
setFormulaError("");
}, [name, scope, formula, model, editingDefinedName]);
const hasAnyError = nameError !== "" || formulaError !== ""; const hasAnyError = nameError !== "" || formulaError !== "";
@@ -154,7 +160,9 @@ function EditNamedRange({
return stringValue === "[Global]" ? ( return stringValue === "[Global]" ? (
<> <>
<MenuSpan>{t("name_manager_dialog.workbook")}</MenuSpan> <MenuSpan>{t("name_manager_dialog.workbook")}</MenuSpan>
<MenuSpanGrey>{` ${t("name_manager_dialog.global")}`}</MenuSpanGrey> <MenuSpanGrey>{` ${t(
"name_manager_dialog.global",
)}`}</MenuSpanGrey>
</> </>
) : ( ) : (
stringValue stringValue
@@ -180,9 +188,11 @@ function EditNamedRange({
<MenuSpan $selected={isSelected("[Global]")}> <MenuSpan $selected={isSelected("[Global]")}>
{t("name_manager_dialog.workbook")} {t("name_manager_dialog.workbook")}
</MenuSpan> </MenuSpan>
<MenuSpanGrey>{` ${t("name_manager_dialog.global")}`}</MenuSpanGrey> <MenuSpanGrey>{` ${t(
"name_manager_dialog.global",
)}`}</MenuSpanGrey>
</StyledMenuItem> </StyledMenuItem>
{worksheets.map((option) => ( {model.getWorksheetsProperties().map((option) => (
<StyledMenuItem key={option.name} value={option.name}> <StyledMenuItem key={option.name} value={option.name}>
{isSelected(option.name) ? ( {isSelected(option.name) ? (
<CheckIcon /> <CheckIcon />
@@ -201,9 +211,25 @@ function EditNamedRange({
</FormControl> </FormControl>
</FieldWrapper> </FieldWrapper>
<FieldWrapper> <FieldWrapper>
<LineWrapper>
<StyledLabel htmlFor="formula"> <StyledLabel htmlFor="formula">
{t("name_manager_dialog.refers_to")} {t("name_manager_dialog.refers_to")}
</StyledLabel> </StyledLabel>
<MousePointerClick
size={16}
onClick={() => {
const worksheetNames = model
.getWorksheetsProperties()
.map((s) => s.name);
const selectedView = model.getSelectedView();
const formula = getFullRangeToString(
selectedView,
worksheetNames,
);
setFormula(formula);
}}
/>
</LineWrapper>
<FormControl fullWidth size="small" error={!!formulaError}> <FormControl fullWidth size="small" error={!!formulaError}>
<StyledTextField <StyledTextField
id="formula" id="formula"
@@ -259,7 +285,13 @@ function EditNamedRange({
</Footer> </Footer>
</Container> </Container>
); );
} };
const LineWrapper = styled("div")({
display: "flex",
alignItems: "center",
gap: "8px",
});
const Container = styled("div")({ const Container = styled("div")({
height: "100%", height: "100%",
@@ -308,14 +340,14 @@ const HeaderBox = styled(Box)`
align-items: center; align-items: center;
text-align: center; text-align: center;
border-bottom: 1px solid ${theme.palette.grey["200"]}; border-bottom: 1px solid ${theme.palette.grey["200"]};
`; `;
const HeaderBoxText = styled("span")` const HeaderBoxText = styled("span")`
max-width: 100%; max-width: 100%;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
`; `;
const HeaderIcon = styled(Box)` const HeaderIcon = styled(Box)`
width: 28px; width: 28px;

View File

@@ -1,4 +1,4 @@
import type { DefinedName, WorksheetProperties } from "@ironcalc/wasm"; import type { DefinedName, Model } from "@ironcalc/wasm";
import { Button, styled, Tooltip } from "@mui/material"; import { Button, styled, Tooltip } from "@mui/material";
import { t } from "i18next"; import { t } from "i18next";
import { import {
@@ -12,41 +12,29 @@ import {
} from "lucide-react"; } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { theme } from "../../../theme"; import { theme } from "../../../theme";
import EditNamedRange, { type SaveError } from "./EditNamedRange"; import { parseRangeInSheet } from "../../Editor/util";
import EditNamedRange, {
formatOnSaveError,
type SaveError,
} from "./EditNamedRange";
const normalizeRangeString = (range: string): string => { const normalizeRangeString = (range: string): string => {
return range.trim().replace(/['"]/g, ""); return range.trim().replace(/['"]/g, "");
}; };
interface NamedRangesProps { interface NamedRangesProps {
title: string;
onClose: () => void; onClose: () => void;
definedNameList: DefinedName[]; model: Model;
worksheets: WorksheetProperties[];
updateDefinedName: (
name: string,
scope: number | null,
newName: string,
newScope: number | null,
newFormula: string,
) => void;
newDefinedName: (name: string, scope: number | null, formula: string) => void;
deleteDefinedName: (name: string, scope: number | null) => void;
getSelectedArea: () => string; getSelectedArea: () => string;
onNameSelected: (name: string) => void; onUpdate: () => void;
} }
function NamedRanges({ const NamedRanges = ({
title,
onClose, onClose,
definedNameList,
worksheets,
updateDefinedName,
newDefinedName,
deleteDefinedName,
getSelectedArea, getSelectedArea,
onNameSelected, model,
}: NamedRangesProps) { onUpdate,
}: NamedRangesProps) => {
const [editingDefinedName, setEditingDefinedName] = const [editingDefinedName, setEditingDefinedName] =
useState<DefinedName | null>(null); useState<DefinedName | null>(null);
const [isCreatingNew, setIsCreatingNew] = useState(false); const [isCreatingNew, setIsCreatingNew] = useState(false);
@@ -71,26 +59,35 @@ function NamedRanges({
scope: string, scope: string,
formula: string, formula: string,
): SaveError => { ): SaveError => {
const worksheets = model.getWorksheetsProperties();
if (isCreatingNew) { if (isCreatingNew) {
if (!newDefinedName) return {};
const scope_index = worksheets.findIndex((s) => s.name === scope); const scope_index = worksheets.findIndex((s) => s.name === scope);
const newScope = scope_index >= 0 ? scope_index : null; const newScope = scope_index >= 0 ? scope_index : null;
try { try {
newDefinedName(name, newScope, formula); model.newDefinedName(name, newScope, formula);
setIsCreatingNew(false); setIsCreatingNew(false);
return {}; onUpdate();
return {
formulaError: "",
nameError: "",
};
} catch (e) { } catch (e) {
// Since name validation is done client-side, errors from model are formula errors if (e instanceof Error) {
return { formulaError: `${e}` }; return formatOnSaveError(e.message);
}
return { formulaError: "", nameError: `${e}` };
} }
} else { } else {
if (!editingDefinedName) return {}; if (!editingDefinedName)
return {
formulaError: "",
nameError: "",
};
const scope_index = worksheets.findIndex((s) => s.name === scope); const scope_index = worksheets.findIndex((s) => s.name === scope);
const newScope = scope_index >= 0 ? scope_index : null; const newScope = scope_index >= 0 ? scope_index : null;
try { try {
updateDefinedName( model.updateDefinedName(
editingDefinedName.name, editingDefinedName.name,
editingDefinedName.scope ?? null, editingDefinedName.scope ?? null,
name, name,
@@ -98,10 +95,13 @@ function NamedRanges({
formula, formula,
); );
setEditingDefinedName(null); setEditingDefinedName(null);
return {}; onUpdate();
return { formulaError: "", nameError: "" };
} catch (e) { } catch (e) {
// Since name validation is done client-side, errors from model are formula errors if (e instanceof Error) {
return { formulaError: `${e}` }; return formatOnSaveError(e.message);
}
return { formulaError: "", nameError: `${e}` };
} }
} }
}; };
@@ -114,12 +114,13 @@ function NamedRanges({
if (editingDefinedName) { if (editingDefinedName) {
name = editingDefinedName.name; name = editingDefinedName.name;
const worksheets = model.getWorksheetsProperties();
scopeName = scopeName =
editingDefinedName.scope != null editingDefinedName.scope != null
? worksheets[editingDefinedName.scope]?.name || "[unknown]" ? worksheets[editingDefinedName.scope]?.name || "[unknown]"
: "[Global]"; : "[Global]";
formula = editingDefinedName.formula; formula = editingDefinedName.formula;
} else if (isCreatingNew && getSelectedArea) { } else if (isCreatingNew) {
formula = getSelectedArea(); formula = getSelectedArea();
} }
@@ -177,14 +178,13 @@ function NamedRanges({
</EditHeader> </EditHeader>
<Content> <Content>
<EditNamedRange <EditNamedRange
worksheets={worksheets}
name={name} name={name}
scope={scopeName} scope={scopeName}
formula={formula} formula={formula}
onSave={handleSave} onSave={handleSave}
onCancel={handleCancel} onCancel={handleCancel}
definedNameList={definedNameList}
editingDefinedName={editingDefinedName} editingDefinedName={editingDefinedName}
model={model}
/> />
</Content> </Content>
</Container> </Container>
@@ -192,11 +192,22 @@ function NamedRanges({
} }
const currentSelectedArea = getSelectedArea(); const currentSelectedArea = getSelectedArea();
const definedNameList = model.getDefinedNameList();
const onNameSelected = (formula: string) => {
const range = parseRangeInSheet(model, formula);
if (range) {
const [sheetIndex, rowStart, columnStart, rowEnd, columnEnd] = range;
model.setSelectedSheet(sheetIndex);
model.setSelectedCell(rowStart, columnStart);
model.setSelectedRange(rowStart, columnStart, rowEnd, columnEnd);
}
onUpdate();
};
return ( return (
<Container> <Container>
<Header> <Header>
<HeaderTitle>{title}</HeaderTitle> <HeaderTitle>{t("name_manager_dialog.title")}</HeaderTitle>
<Tooltip <Tooltip
title={t("right_drawer.close")} title={t("right_drawer.close")}
slotProps={{ slotProps={{
@@ -239,6 +250,7 @@ function NamedRanges({
) : ( ) : (
<ListContainer> <ListContainer>
{definedNameList.map((definedName) => { {definedNameList.map((definedName) => {
const worksheets = model.getWorksheetsProperties();
const scopeName = const scopeName =
definedName.scope != null definedName.scope != null
? worksheets[definedName.scope]?.name || "[Unknown]" ? worksheets[definedName.scope]?.name || "[Unknown]"
@@ -252,7 +264,29 @@ function NamedRanges({
key={`${definedName.name}-${definedName.scope}`} key={`${definedName.name}-${definedName.scope}`}
tabIndex={0} tabIndex={0}
$isSelected={isSelected} $isSelected={isSelected}
onClick={() => onNameSelected(definedName.formula)} onClick={() => {
// select the area corresponding to the defined name
const formula = definedName.formula;
const range = parseRangeInSheet(model, formula);
if (range) {
const [
sheetIndex,
rowStart,
columnStart,
rowEnd,
columnEnd,
] = range;
model.setSelectedSheet(sheetIndex);
model.setSelectedCell(rowStart, columnStart);
model.setSelectedRange(
rowStart,
columnStart,
rowEnd,
columnEnd,
);
}
onUpdate();
}}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") { if (e.key === "Enter" || e.key === " ") {
e.preventDefault(); e.preventDefault();
@@ -290,23 +324,21 @@ function NamedRanges({
<IconButton <IconButton
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
if (deleteDefinedName) { model.deleteDefinedName(
deleteDefinedName(
definedName.name, definedName.name,
definedName.scope ?? null, definedName.scope ?? null,
); );
} onUpdate();
}} }}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") { if (e.key === "Enter" || e.key === " ") {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (deleteDefinedName) { model.deleteDefinedName(
deleteDefinedName(
definedName.name, definedName.name,
definedName.scope ?? null, definedName.scope ?? null,
); );
} onUpdate();
} }
}} }}
aria-label={t("name_manager_dialog.delete")} aria-label={t("name_manager_dialog.delete")}
@@ -342,7 +374,7 @@ function NamedRanges({
</Footer> </Footer>
</Container> </Container>
); );
} };
const Container = styled("div")({ const Container = styled("div")({
height: "100%", height: "100%",
@@ -362,8 +394,7 @@ const ListContainer = styled("div")({
flexDirection: "column", flexDirection: "column",
}); });
const ListItem = styled("div")<{ $isSelected?: boolean }>( const ListItem = styled("div")<{ $isSelected: boolean }>(({ $isSelected }) => ({
({ $isSelected }) => ({
display: "flex", display: "flex",
alignItems: "flex-start", alignItems: "flex-start",
justifyContent: "space-between", justifyContent: "space-between",
@@ -380,8 +411,7 @@ const ListItem = styled("div")<{ $isSelected?: boolean }>(
backgroundColor: theme.palette.grey[50], backgroundColor: theme.palette.grey[50],
paddingLeft: "20px", paddingLeft: "20px",
}, },
}), }));
);
const ListItemText = styled("div")({ const ListItemText = styled("div")({
fontSize: "12px", fontSize: "12px",

View File

@@ -1,4 +1,4 @@
import type { DefinedName, WorksheetProperties } from "@ironcalc/wasm"; import type { Model } from "@ironcalc/wasm";
import { styled } from "@mui/material/styles"; import { styled } from "@mui/material/styles";
import type { MouseEvent as ReactMouseEvent } from "react"; import type { MouseEvent as ReactMouseEvent } from "react";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
@@ -16,20 +16,9 @@ interface RightDrawerProps {
onClose: () => void; onClose: () => void;
width: number; width: number;
onWidthChange: (width: number) => void; onWidthChange: (width: number) => void;
title: string; model: Model;
definedNameList: DefinedName[]; onUpdate: () => void;
worksheets: WorksheetProperties[];
updateDefinedName: (
name: string,
scope: number | null,
newName: string,
newScope: number | null,
newFormula: string,
) => void;
newDefinedName: (name: string, scope: number | null, formula: string) => void;
deleteDefinedName: (name: string, scope: number | null) => void;
getSelectedArea: () => string; getSelectedArea: () => string;
onNameSelected: (name: string) => void;
} }
const RightDrawer = ({ const RightDrawer = ({
@@ -37,14 +26,9 @@ const RightDrawer = ({
onClose, onClose,
width, width,
onWidthChange, onWidthChange,
title,
definedNameList,
worksheets,
updateDefinedName,
newDefinedName,
deleteDefinedName,
getSelectedArea, getSelectedArea,
onNameSelected, model,
onUpdate,
}: RightDrawerProps) => { }: RightDrawerProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [drawerWidth, setDrawerWidth] = useState(width); const [drawerWidth, setDrawerWidth] = useState(width);
@@ -57,7 +41,9 @@ const RightDrawer = ({
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!isResizing) return; if (!isResizing) {
return;
}
// Prevent text selection during resize // Prevent text selection during resize
document.body.style.userSelect = "none"; document.body.style.userSelect = "none";
@@ -90,7 +76,9 @@ const RightDrawer = ({
}; };
}, [isResizing, onWidthChange]); }, [isResizing, onWidthChange]);
if (!isOpen) return null; if (!isOpen) {
return null;
}
return ( return (
<DrawerContainer $drawerWidth={drawerWidth}> <DrawerContainer $drawerWidth={drawerWidth}>
@@ -103,15 +91,10 @@ const RightDrawer = ({
<Divider /> <Divider />
<DrawerContent> <DrawerContent>
<NamedRanges <NamedRanges
title={title}
onClose={onClose} onClose={onClose}
definedNameList={definedNameList} model={model}
worksheets={worksheets} onUpdate={onUpdate}
updateDefinedName={updateDefinedName}
newDefinedName={newDefinedName}
deleteDefinedName={deleteDefinedName}
getSelectedArea={getSelectedArea} getSelectedArea={getSelectedArea}
onNameSelected={onNameSelected}
/> />
</DrawerContent> </DrawerContent>
</DrawerContainer> </DrawerContainer>

View File

@@ -1,19 +1,16 @@
import { import type {
type BorderOptions, BorderOptions,
type ClipboardCell, ClipboardCell,
getTokens, Model,
type Model, WorksheetProperties,
type WorksheetProperties,
} from "@ironcalc/wasm"; } from "@ironcalc/wasm";
import { styled } from "@mui/material/styles"; import { styled } from "@mui/material/styles";
import { t } from "i18next";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { import {
CLIPBOARD_ID_SESSION_STORAGE_KEY, CLIPBOARD_ID_SESSION_STORAGE_KEY,
getNewClipboardId, getNewClipboardId,
} from "../clipboard"; } from "../clipboard";
import { TOOLBAR_HEIGHT } from "../constants"; import { TOOLBAR_HEIGHT } from "../constants";
import { tokenIsRangeType } from "../Editor/util";
import FormulaBar from "../FormulaBar/FormulaBar"; import FormulaBar from "../FormulaBar/FormulaBar";
import RightDrawer, { DEFAULT_DRAWER_WIDTH } from "../RightDrawer/RightDrawer"; import RightDrawer, { DEFAULT_DRAWER_WIDTH } from "../RightDrawer/RightDrawer";
import SheetTabBar from "../SheetTabBar"; import SheetTabBar from "../SheetTabBar";
@@ -745,53 +742,17 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => {
onClose={() => setDrawerOpen(false)} onClose={() => setDrawerOpen(false)}
width={drawerWidth} width={drawerWidth}
onWidthChange={setDrawerWidth} onWidthChange={setDrawerWidth}
title={t("name_manager_dialog.title")} model={model}
definedNameList={model.getDefinedNameList()} onUpdate={() => {
worksheets={worksheets}
updateDefinedName={(
name: string,
scope: number | null,
newName: string,
newScope: number | null,
newFormula: string,
) => {
model.updateDefinedName(name, scope, newName, newScope, newFormula);
setRedrawId((id) => id + 1);
}}
newDefinedName={(
name: string,
scope: number | null,
formula: string,
) => {
model.newDefinedName(name, scope, formula);
setRedrawId((id) => id + 1);
}}
deleteDefinedName={(name: string, scope: number | null) => {
model.deleteDefinedName(name, scope);
setRedrawId((id) => id + 1); setRedrawId((id) => id + 1);
}} }}
getSelectedArea={() => { getSelectedArea={() => {
const worksheetNames = worksheets.map((s) => s.name); const worksheetNames = model
.getWorksheetsProperties()
.map((s) => s.name);
const selectedView = model.getSelectedView(); const selectedView = model.getSelectedView();
return getFullRangeToString(selectedView, worksheetNames); return getFullRangeToString(selectedView, worksheetNames);
}} }}
onNameSelected={(formula) => {
const tokens = getTokens(formula);
const { token } = tokens[0];
if (tokenIsRangeType(token)) {
const sheetName = worksheets[model.getSelectedSheet()].name;
const {
sheet: refSheet,
left: { row: rowStart, column: columnStart },
right: { row: rowEnd, column: columnEnd },
} = token.Range;
if (refSheet !== null && refSheet === sheetName) {
model.setSelectedCell(rowStart, columnStart);
model.setSelectedRange(rowStart, columnStart, rowEnd, columnEnd);
}
}
setRedrawId((id) => id + 1);
}}
/> />
</Container> </Container>
); );