diff --git a/webapp/src/components/NameManager/NameManagerDialog.tsx b/webapp/src/components/NameManager/NameManagerDialog.tsx deleted file mode 100644 index 265fd15..0000000 --- a/webapp/src/components/NameManager/NameManagerDialog.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import type { DefinedName, Model } from "@ironcalc/wasm"; -import { - Box, - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - IconButton, - Stack, - styled, -} from "@mui/material"; -import { t } from "i18next"; -import { BookOpen, Plus, X } from "lucide-react"; -import { useEffect, useState } from "react"; -import { getFullRangeToString } from "../util"; -import NamedRange from "./NamedRange"; - -interface NameManagerDialogProperties { - onClose: () => void; - open: boolean; - model: Model; -} - -function NameManagerDialog({ - onClose, - open, - model, -}: NameManagerDialogProperties) { - const [definedNamesLocal, setDefinedNamesLocal] = useState(); - const [showNewName, setShowNewName] = useState(false); - const [showOptions, setShowOptions] = useState(true); - - useEffect(() => { - // render definedNames from model - if (open) { - const definedNamesModel = model.getDefinedNameList(); - setDefinedNamesLocal(definedNamesModel); - } - setShowNewName(false); - setShowOptions(true); - }, [open, model]); - - const handleNewName = () => { - setShowNewName(true); - setShowOptions(false); - }; - - const handleDelete = () => { - // re-render modal - setDefinedNamesLocal(model.getDefinedNameList()); - }; - - const formatFormula = (): string => { - const worksheets = model.getWorksheetsProperties(); - const selectedView = model.getSelectedView(); - - return getFullRangeToString(selectedView, worksheets); - }; - - const toggleOptions = () => { - setShowOptions(!showOptions); - }; - - const toggleShowNewName = () => { - setShowNewName(false); - }; - - return ( - - - {t("name_manager_dialog.title")} - onClose()}> - - - - - - {t("name_manager_dialog.name")} - {t("name_manager_dialog.range")} - {t("name_manager_dialog.scope")} - - {definedNamesLocal?.map((definedName) => ( - - ))} - {showNewName && ( - - )} - - - - - - {t("name_manager_dialog.help")} - - - - - - ); -} - -const StyledDialog = styled(Dialog)(() => ({ - "& .MuiPaper-root": { - height: "380px", - minWidth: "620px", - }, -})); - -const StyledDialogTitle = styled(DialogTitle)` -padding: 12px 20px; -font-size: 14px; -font-weight: 600; -display: flex; -align-items: center; -justify-content: space-between; -`; - -const StyledDialogContent = styled(DialogContent)` -display: flex; -flex-direction: column; -gap: 12px; -padding: 20px 12px 20px 20px; -`; - -const StyledRangesHeader = styled(Stack)(({ theme }) => ({ - flexDirection: "row", - gap: "12px", - fontFamily: theme.typography.fontFamily, - fontSize: "12px", - fontWeight: "700", - color: theme.palette.info.main, -})); - -const StyledDialogActions = styled(DialogActions)` -padding: 12px 20px; -height: 40px; -display: flex; -align-items: center; -justify-content: space-between; -font-size: 12px; -color: #757575; -`; - -export default NameManagerDialog; diff --git a/webapp/src/components/NameManagerDialog/NameManagerDialog.tsx b/webapp/src/components/NameManagerDialog/NameManagerDialog.tsx new file mode 100644 index 0000000..f94560e --- /dev/null +++ b/webapp/src/components/NameManagerDialog/NameManagerDialog.tsx @@ -0,0 +1,178 @@ +import type { Model } from "@ironcalc/wasm"; +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + Stack, + styled, +} from "@mui/material"; +import { t } from "i18next"; +import { BookOpen, Plus, X } from "lucide-react"; +import { useState } from "react"; +import { getFullRangeToString } from "../util"; +import NamedRangeActive, { NamedRangeInactive } from "./NamedRange"; + +interface NameManagerDialogProperties { + onClose: () => void; + open: boolean; + model: Model; + onNamesChanged: () => void; +} + +function NameManagerDialog(properties: NameManagerDialogProperties) { + const { onClose, open, model, onNamesChanged } = properties; + + // If editingNameIndex is -1, then we are adding a new name + // If editingNameIndex is -2, then we are not editing any name + // If editingNameIndex is a positive number, then we are editing that index + const [editingNameIndex, setEditingNameIndex] = useState(-2); + + const handleNewName = () => { + setEditingNameIndex(-1); + }; + + const handleDelete = () => { + onNamesChanged(); + }; + + const formatFormula = (): string => { + const worksheets = model.getWorksheetsProperties(); + const selectedView = model.getSelectedView(); + + return getFullRangeToString(selectedView, worksheets); + }; + + const worksheets = model.getWorksheetsProperties(); + const definedNameList = model.getDefinedNameList(); + + return ( + + + {t("name_manager_dialog.title")} + + + + + + + {t("name_manager_dialog.name")} + {t("name_manager_dialog.range")} + {t("name_manager_dialog.scope")} + + + {definedNameList.map((definedName, index) => { + if (index === editingNameIndex) { + return ( + setEditingNameIndex(-2)} + /> + ); + } + return ( + setEditingNameIndex(index)} + onDelete={handleDelete} + /> + ); + })} + + {editingNameIndex === -1 && ( + setEditingNameIndex(-2)} + />) + } + + + + + + {t("name_manager_dialog.help")} + + + + + + ); +} + +const NameLisWrapper = styled(Stack)` + overflow-y: auto; +`; + +const StyledBox = styled("div")` + width: 171px; +`; + +const StyledDialog = styled(Dialog)(() => ({ + "& .MuiPaper-root": { + height: "380px", + minWidth: "620px", + }, +})); + +const StyledDialogTitle = styled(DialogTitle)` + padding: 12px 20px; + font-size: 14px; + font-weight: 600; + display: flex; + align-items: center; + justify-content: space-between; +`; + +const StyledDialogContent = styled(DialogContent)` + display: flex; + flex-direction: column; + gap: 12px; + padding: 20px 12px 20px 20px; +`; + +const StyledRangesHeader = styled(Box)(({ theme }) => ({ + display: "flex", + paddingLeft: "6px", + fontFamily: theme.typography.fontFamily, + fontSize: "12px", + fontWeight: "700", + color: theme.palette.info.main, +})); + +const StyledDialogActions = styled(DialogActions)` + padding: 12px 20px; + height: 40px; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 12px; + color: #757575; +`; + +export default NameManagerDialog; diff --git a/webapp/src/components/NameManager/NamedRange.tsx b/webapp/src/components/NameManagerDialog/NamedRange.tsx similarity index 67% rename from webapp/src/components/NameManager/NamedRange.tsx rename to webapp/src/components/NameManagerDialog/NamedRange.tsx index 76b591b..191b28c 100644 --- a/webapp/src/components/NameManager/NamedRange.tsx +++ b/webapp/src/components/NameManagerDialog/NamedRange.tsx @@ -14,26 +14,44 @@ import { useEffect, useState } from "react"; interface NamedRangeProperties { model: Model; worksheets: WorksheetProperties[]; - name?: string; + name: string; scope?: number; formula: string; - onDelete?: () => void; - toggleShowNewName?: () => void; - toggleOptions: () => void; - showOptions?: boolean; + onSave: () => void; + onCancel: () => void; } -function NamedRange({ - model, - worksheets, - name, - scope, - formula, - onDelete, - toggleShowNewName, - toggleOptions, - showOptions, -}: NamedRangeProperties) { +interface NamedRangeInactiveProperties { + name: string; + scope?: number; + formula: string; + onDelete: () => void; + onEdit: () => void; +} + +export function NamedRangeInactive(properties: NamedRangeInactiveProperties) { + const { name, scope, formula, onDelete, onEdit } = properties; + const showOptions = true; + return ( + + {name} + {scope} + {formula} + + + + + + + + + + ); +} + +function NamedRangeActive(properties: NamedRangeProperties) { + const { model, worksheets, name, scope, formula, onCancel, onSave } = + properties; const [newName, setNewName] = useState(name || ""); const [newScope, setNewScope] = useState(scope); const [newFormula, setNewFormula] = useState(formula); @@ -64,7 +82,7 @@ function NamedRange({ scope, newName, newScope, - newFormula, + newFormula ); } catch (error) { console.log("DefinedName update failed", error); @@ -79,24 +97,18 @@ function NamedRange({ setReadOnly(true); } setShowEditDelete(false); - toggleOptions(); }; const handleCancel = () => { setReadOnly(true); setShowEditDelete(false); - toggleOptions(); setNewName(name || ""); setNewScope(scope); - - // if it's newName remove it from modal - toggleShowNewName?.(); }; const handleEdit = () => { setReadOnly(false); setShowEditDelete(true); - toggleOptions(); }; const handleDelete = () => { @@ -105,7 +117,6 @@ function NamedRange({ } catch (error) { console.log("DefinedName delete failed", error); } - onDelete?.(); // refresh modal }; return ( @@ -117,7 +128,6 @@ function NamedRange({ size="small" margin="none" fullWidth - InputProps={{ readOnly: readOnly }} error={nameError} value={newName} onChange={(event) => setNewName(event.target.value)} @@ -133,7 +143,6 @@ function NamedRange({ size="small" margin="none" fullWidth - InputProps={{ readOnly: readOnly }} value={newScope ?? "global"} onChange={(event) => { event.target.value === "global" @@ -156,7 +165,6 @@ function NamedRange({ size="small" margin="none" fullWidth - InputProps={{ readOnly: readOnly }} error={formulaError} value={newFormula} onChange={(event) => setNewFormula(event.target.value)} @@ -165,45 +173,44 @@ function NamedRange({ }} onClick={(event) => event.stopPropagation()} /> - - {showEditDelete ? ( - // save cancel - <> - - - - - - - - ) : ( - // edit delete - <> - - - - - - - - )} + <> + + + + + + + - ); } const StyledBox = styled(Box)` -display: flex; -gap: 12px; -width: 577px; + display: flex; + width: 577px; `; +const StyledPencilLine = styled(PencilLine)(({ theme }) => ({ + color: theme.palette.common.black, +})); + +const StyledCheck = styled(Check)(({ theme }) => ({ + color: theme.palette.success.main, +})); + const StyledTextField = styled(TextField)(() => ({ + padding: "0px", + width: "163px", + marginRight: "8px", "& .MuiInputBase-root": { height: "28px", margin: 0, }, + "& .MuiInputBase-input": { + padding: "6px", + fontSize: "12px", + }, })); const StyledIconButton = styled(IconButton)(({ theme }) => ({ @@ -214,4 +221,24 @@ const StyledIconButton = styled(IconButton)(({ theme }) => ({ }, })); -export default NamedRange; + +const WrappedLine = styled(Box)({ + display: "flex", + paddingLeft: "6px", + height: "28px", +}); + +const StyledDiv = styled("div")(({ theme }) => ({ + fontFamily: theme.typography.fontFamily, + fontSize: "12px", + fontWeight: "400", + color: theme.palette.common.black, + width: "171px", +})); + +const WrappedIcons = styled(Box)({ + display: "flex", + gap: "0px", +}); + +export default NamedRangeActive; diff --git a/webapp/src/components/NameManagerDialog/index.tsx b/webapp/src/components/NameManagerDialog/index.tsx new file mode 100644 index 0000000..d062e24 --- /dev/null +++ b/webapp/src/components/NameManagerDialog/index.tsx @@ -0,0 +1 @@ +export { default } from "./NameManagerDialog"; \ No newline at end of file diff --git a/webapp/src/components/toolbar.tsx b/webapp/src/components/toolbar.tsx index 30d265c..b4f4d3f 100644 --- a/webapp/src/components/toolbar.tsx +++ b/webapp/src/components/toolbar.tsx @@ -36,7 +36,7 @@ import { DecimalPlacesIncreaseIcon, } from "../icons"; import { theme } from "../theme"; -import NameManagerDialog from "./NameManager/NameManagerDialog"; +import NameManagerDialog from "./NameManagerDialog"; import BorderPicker from "./borderPicker"; import ColorPicker from "./colorPicker"; import { TOOLBAR_HEIGHT } from "./constants"; @@ -63,7 +63,6 @@ type ToolbarProperties = { onFillColorPicked: (hex: string) => void; onNumberFormatPicked: (numberFmt: string) => void; onBorderChanged: (border: BorderOptions) => void; - onNamedRangesUpdate: () => void; fillColor: string; fontColor: string; bold: boolean; @@ -76,6 +75,7 @@ type ToolbarProperties = { numFmt: string; showGridLines: boolean; onToggleShowGridLines: (show: boolean) => void; + onNamesChanged: () => void; model: Model; }; @@ -399,6 +399,7 @@ function Toolbar(properties: ToolbarProperties) { setNameManagerDialogOpen(false); }} model={properties.model} + onNamesChanged={properties.onNamesChanged} /> ); diff --git a/webapp/src/components/util.ts b/webapp/src/components/util.ts index e826081..cbf04b1 100644 --- a/webapp/src/components/util.ts +++ b/webapp/src/components/util.ts @@ -68,7 +68,7 @@ export function rangeToStr( export function getFullRangeToString( selectedView: SelectedView, - worksheets: WorksheetProperties[], // solo pasar names + worksheets: WorksheetProperties[], ): string { // order of values is confusing compared to rangeToStr range type, needs refactoring for consistency const [rowStart, columnStart, rowEnd, columnEnd] = selectedView.range; diff --git a/webapp/src/components/workbook.tsx b/webapp/src/components/workbook.tsx index 484e927..dc321d7 100644 --- a/webapp/src/components/workbook.tsx +++ b/webapp/src/components/workbook.tsx @@ -137,16 +137,17 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => { // FIXME: This is so that the cursor indicates there are styles to be pasted const el = rootRef.current?.getElementsByClassName("sheet-container")[0]; if (el) { - (el as HTMLElement).style.cursor = - `url('data:image/svg+xml;utf8,${encodeURIComponent( - ReactDOMServer.renderToString( - , - ), - )}'), auto`; + ( + el as HTMLElement + ).style.cursor = `url('data:image/svg+xml;utf8,${encodeURIComponent( + ReactDOMServer.renderToString( + + ) + )}'), auto`; } }; @@ -168,12 +169,12 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => { row, column, row + height, - column + width, + column + width ); setRedrawId((id) => id + 1); }, onExpandAreaSelectedKeyboard: ( - key: "ArrowRight" | "ArrowLeft" | "ArrowUp" | "ArrowDown", + key: "ArrowRight" | "ArrowLeft" | "ArrowUp" | "ArrowDown" ): void => { model.onExpandSelectedRange(key); setRedrawId((id) => id + 1); @@ -327,7 +328,7 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => { } = model.getSelectedView(); return getCellAddress( { rowStart, rowEnd, columnStart, columnEnd }, - { row, column }, + { row, column } ); }, [model]); @@ -404,7 +405,7 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => { source.sheet, source.area, data, - source.type === "cut", + source.type === "cut" ); setRedrawId((id) => id + 1); } else if (mimeType === "text/plain") { @@ -435,7 +436,7 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => { // '2024-10-18T14:07:37.599Z' let clipboardId = sessionStorage.getItem( - CLIPBOARD_ID_SESSION_STORAGE_KEY, + CLIPBOARD_ID_SESSION_STORAGE_KEY ); if (!clipboardId) { clipboardId = getNewClipboardId(); @@ -473,7 +474,7 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => { // '2024-10-18T14:07:37.599Z' let clipboardId = sessionStorage.getItem( - CLIPBOARD_ID_SESSION_STORAGE_KEY, + CLIPBOARD_ID_SESSION_STORAGE_KEY ); if (!clipboardId) { clipboardId = getNewClipboardId(); @@ -545,7 +546,7 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => { }; model.setAreaWithBorder( { sheet, row, column, width, height }, - borderArea, + borderArea ); setRedrawId((id) => id + 1); }} @@ -570,6 +571,9 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => { setRedrawId((id) => id + 1); }} model={model} + onNamesChanged={() => { + setRedrawId((id) => id + 1); + }} />