Compare commits
19 Commits
dani/widge
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
203d640dbd | ||
|
|
ba75ffcf4f | ||
|
|
b5c977d3aa | ||
|
|
4029441cea | ||
|
|
cd47c609a0 | ||
|
|
ae6acdcdd5 | ||
|
|
c196db2115 | ||
|
|
a3c201e4e4 | ||
|
|
126e62957a | ||
|
|
294a651ae5 | ||
|
|
6f8a1e0da6 | ||
|
|
205ba6ee2d | ||
|
|
547b331773 | ||
|
|
db552047c8 | ||
|
|
bcbacdb0a3 | ||
|
|
d0f37854d9 | ||
|
|
99b03f70c3 | ||
|
|
3e1605a494 | ||
|
|
d6aad08e73 |
@@ -1,4 +1,5 @@
|
||||
mod test_actions;
|
||||
mod test_arabic_roman;
|
||||
mod test_binary_search;
|
||||
mod test_cell;
|
||||
mod test_cell_clear_contents;
|
||||
@@ -39,6 +40,7 @@ mod test_metadata;
|
||||
mod test_model_cell_clear_all;
|
||||
mod test_model_is_empty_cell;
|
||||
mod test_move_formula;
|
||||
mod test_mround_trunc_int;
|
||||
mod test_quote_prefix;
|
||||
mod test_row_column_styles;
|
||||
mod test_set_user_input;
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::test::util::new_empty_model;
|
||||
fn arguments() {
|
||||
let mut model = new_empty_model();
|
||||
model._set("A1", "=ARABIC()");
|
||||
model._set("A2", "=ARABIC(V)");
|
||||
model._set("A3", "=ARABIC(V, 2)");
|
||||
model._set("A2", "=ARABIC(\"V\")");
|
||||
model._set("A3", "=ARABIC(\"V\", 2)");
|
||||
|
||||
model._set("A4", "=ROMAN()");
|
||||
model._set("A5", "=ROMAN(5)");
|
||||
@@ -20,7 +20,6 @@ fn arguments() {
|
||||
model._set("A11", "=INT(10.22, 1)");
|
||||
model._set("A12", "=INT(10.22, 1, 2)");
|
||||
|
||||
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_text("A1"), *"#ERROR!");
|
||||
@@ -29,7 +28,7 @@ fn arguments() {
|
||||
assert_eq!(model._get_text("A4"), *"#ERROR!");
|
||||
|
||||
assert_eq!(model._get_text("A5"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("A6"), *"#ERROR!");
|
||||
assert_eq!(model._get_text("A6"), *"10");
|
||||
assert_eq!(model._get_text("A7"), *"10.2");
|
||||
assert_eq!(model._get_text("A8"), *"#ERROR!");
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { Model } from "@ironcalc/wasm";
|
||||
import { styled } from "@mui/material";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { Fx } from "../../icons";
|
||||
import { theme } from "../../theme";
|
||||
import { FORMULA_BAR_HEIGHT } from "../constants";
|
||||
@@ -9,6 +11,7 @@ import {
|
||||
ROW_HEIGH_SCALE,
|
||||
} from "../WorksheetCanvas/constants";
|
||||
import type { WorkbookState } from "../workbookState";
|
||||
import FormulaBarMenu from "./FormulaBarMenu";
|
||||
|
||||
type FormulaBarProps = {
|
||||
cellAddress: string;
|
||||
@@ -17,6 +20,8 @@ type FormulaBarProps = {
|
||||
workbookState: WorkbookState;
|
||||
onChange: () => void;
|
||||
onTextUpdated: () => void;
|
||||
openDrawer: () => void;
|
||||
canEdit: boolean;
|
||||
};
|
||||
|
||||
function FormulaBar(properties: FormulaBarProps) {
|
||||
@@ -28,10 +33,27 @@ function FormulaBar(properties: FormulaBarProps) {
|
||||
onTextUpdated,
|
||||
workbookState,
|
||||
} = properties;
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const handleMenuOpenChange = (isOpen: boolean): void => {
|
||||
setIsMenuOpen(isOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<AddressContainer>
|
||||
<AddressContainer $active={isMenuOpen}>
|
||||
<FormulaBarMenu
|
||||
onMenuOpenChange={handleMenuOpenChange}
|
||||
openDrawer={properties.openDrawer}
|
||||
canEdit={properties.canEdit}
|
||||
model={model}
|
||||
onUpdate={onChange}
|
||||
>
|
||||
<CellBarAddress>{cellAddress}</CellBarAddress>
|
||||
<StyledIcon>
|
||||
<ChevronDown size={16} />
|
||||
</StyledIcon>
|
||||
</FormulaBarMenu>
|
||||
</AddressContainer>
|
||||
<Divider />
|
||||
<FormulaContainer>
|
||||
@@ -101,7 +123,7 @@ const Divider = styled("div")`
|
||||
background-color: ${theme.palette.grey["300"]};
|
||||
min-width: 1px;
|
||||
height: 16px;
|
||||
margin: 0px 16px;
|
||||
margin: 0px 16px 0px 8px;
|
||||
`;
|
||||
|
||||
const FormulaContainer = styled("div")`
|
||||
@@ -121,22 +143,46 @@ const Container = styled("div")`
|
||||
background: ${(properties): string =>
|
||||
properties.theme.palette.background.default};
|
||||
height: ${FORMULA_BAR_HEIGHT}px;
|
||||
border-top: 1px solid ${theme.palette.grey["300"]};
|
||||
`;
|
||||
|
||||
const AddressContainer = styled("div")`
|
||||
padding-left: 16px;
|
||||
const AddressContainer = styled("div")<{ $active?: boolean }>`
|
||||
color: ${theme.palette.common.black};
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
font-weight: 600;
|
||||
flex-grow: row;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
background-color: ${(props) =>
|
||||
props.$active ? theme.palette.action.selected : "transparent"};
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.$active ? theme.palette.action.selected : theme.palette.grey["100"]};
|
||||
}
|
||||
`;
|
||||
|
||||
const CellBarAddress = styled("div")`
|
||||
width: 100%;
|
||||
text-align: "center";
|
||||
box-sizing: border-box;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding-left: 8px;
|
||||
background-color: transparent;
|
||||
`;
|
||||
|
||||
const StyledIcon = styled("div")`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px 2px;
|
||||
background-color: transparent;
|
||||
`;
|
||||
|
||||
const EditorWrapper = styled("div")`
|
||||
|
||||
170
webapp/IronCalc/src/components/FormulaBar/FormulaBarMenu.tsx
Normal file
170
webapp/IronCalc/src/components/FormulaBar/FormulaBarMenu.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
import type { Model } from "@ironcalc/wasm";
|
||||
import { Menu, MenuItem, styled } from "@mui/material";
|
||||
import { Tag } from "lucide-react";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { theme } from "../../theme";
|
||||
import { parseRangeInSheet } from "../Editor/util";
|
||||
|
||||
type FormulaBarMenuProps = {
|
||||
children: React.ReactNode;
|
||||
onMenuOpenChange: (isOpen: boolean) => void;
|
||||
openDrawer: () => void;
|
||||
canEdit: boolean;
|
||||
model: Model;
|
||||
onUpdate: () => void;
|
||||
};
|
||||
|
||||
const FormulaBarMenu = (properties: FormulaBarMenuProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [isMenuOpen, setMenuOpen] = useState(false);
|
||||
const anchorElement = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleMenuOpen = useCallback((): void => {
|
||||
setMenuOpen(true);
|
||||
properties.onMenuOpenChange(true);
|
||||
}, [properties.onMenuOpenChange]);
|
||||
|
||||
const handleMenuClose = useCallback((): void => {
|
||||
setMenuOpen(false);
|
||||
properties.onMenuOpenChange(false);
|
||||
}, [properties.onMenuOpenChange]);
|
||||
|
||||
const definedNameList = properties.model.getDefinedNameList();
|
||||
|
||||
return (
|
||||
<>
|
||||
<ChildrenWrapper onClick={handleMenuOpen} ref={anchorElement}>
|
||||
{properties.children}
|
||||
</ChildrenWrapper>
|
||||
<StyledMenu
|
||||
open={isMenuOpen}
|
||||
onClose={handleMenuClose}
|
||||
anchorEl={anchorElement.current}
|
||||
marginThreshold={0}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
>
|
||||
{definedNameList.length > 0 ? (
|
||||
<>
|
||||
{definedNameList.map((definedName) => {
|
||||
return (
|
||||
<MenuItemWrapper
|
||||
key={`${definedName.name}-${definedName.scope}`}
|
||||
disableRipple
|
||||
onClick={() => {
|
||||
// select the area corresponding to the defined name
|
||||
const formula = definedName.formula;
|
||||
const range = parseRangeInSheet(properties.model, formula);
|
||||
if (range) {
|
||||
const [
|
||||
sheetIndex,
|
||||
rowStart,
|
||||
columnStart,
|
||||
rowEnd,
|
||||
columnEnd,
|
||||
] = range;
|
||||
properties.model.setSelectedSheet(sheetIndex);
|
||||
properties.model.setSelectedCell(rowStart, columnStart);
|
||||
properties.model.setSelectedRange(
|
||||
rowStart,
|
||||
columnStart,
|
||||
rowEnd,
|
||||
columnEnd,
|
||||
);
|
||||
}
|
||||
properties.onUpdate();
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
<Tag />
|
||||
<MenuItemText>{definedName.name}</MenuItemText>
|
||||
<MenuItemExample>{definedName.formula}</MenuItemExample>
|
||||
</MenuItemWrapper>
|
||||
);
|
||||
})}
|
||||
<MenuDivider />
|
||||
</>
|
||||
) : null}
|
||||
<MenuItemWrapper
|
||||
onClick={() => {
|
||||
properties.openDrawer();
|
||||
handleMenuClose();
|
||||
}}
|
||||
disabled={!properties.canEdit}
|
||||
disableRipple
|
||||
>
|
||||
<MenuItemText>{t("formula_bar.manage_named_ranges")}</MenuItemText>
|
||||
</MenuItemWrapper>
|
||||
</StyledMenu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const StyledMenu = styled(Menu)`
|
||||
top: 4px;
|
||||
min-width: 260px;
|
||||
max-width: 460px;
|
||||
& .MuiPaper-root {
|
||||
border-radius: 8px;
|
||||
padding: 4px 0px;
|
||||
margin-left: -4px;
|
||||
}
|
||||
& .MuiList-root {
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const MenuItemWrapper = styled(MenuItem)`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 12px;
|
||||
gap: 8px;
|
||||
width: calc(100% - 8px);
|
||||
min-width: 172px;
|
||||
margin: 0px 4px;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
height: 32px;
|
||||
& svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
flex-shrink: 0;
|
||||
color: ${theme.palette.grey[600]};
|
||||
}
|
||||
`;
|
||||
|
||||
const ChildrenWrapper = styled("div")`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const MenuDivider = styled("div")`
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
border-top: 1px solid ${theme.palette.grey[200]};
|
||||
`;
|
||||
|
||||
const MenuItemText = styled("div")`
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: ${theme.palette.common.black};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const MenuItemExample = styled("div")`
|
||||
color: ${theme.palette.grey[400]};
|
||||
margin-left: 12px;
|
||||
`;
|
||||
|
||||
export default FormulaBarMenu;
|
||||
@@ -398,7 +398,9 @@ const ListItem = styled("div")<{ $isSelected: boolean }>(({ $isSelected }) => ({
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "space-between",
|
||||
gap: "8px",
|
||||
padding: "8px 12px",
|
||||
cursor: "pointer",
|
||||
minHeight: "40px",
|
||||
boxSizing: "border-box",
|
||||
borderBottom: `1px solid ${theme.palette.grey[200]}`,
|
||||
@@ -438,6 +440,8 @@ const NameText = styled("span")({
|
||||
fontSize: "12px",
|
||||
color: theme.palette.common.black,
|
||||
fontWeight: 600,
|
||||
wordBreak: "break-all",
|
||||
overflowWrap: "break-word",
|
||||
});
|
||||
|
||||
const IconsWrapper = styled("div")({
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
Redo2,
|
||||
RemoveFormatting,
|
||||
Strikethrough,
|
||||
Tags,
|
||||
Type,
|
||||
Underline,
|
||||
Undo2,
|
||||
@@ -87,7 +86,6 @@ type ToolbarProperties = {
|
||||
numFmt: string;
|
||||
showGridLines: boolean;
|
||||
onToggleShowGridLines: (show: boolean) => void;
|
||||
openDrawer: () => void;
|
||||
};
|
||||
|
||||
function Toolbar(properties: ToolbarProperties) {
|
||||
@@ -514,18 +512,6 @@ function Toolbar(properties: ToolbarProperties) {
|
||||
{properties.showGridLines ? <Grid2x2Check /> : <Grid2x2X />}
|
||||
</StyledButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("toolbar.named_ranges")}>
|
||||
<StyledButton
|
||||
type="button"
|
||||
$pressed={false}
|
||||
onClick={() => {
|
||||
properties.openDrawer();
|
||||
}}
|
||||
disabled={!canEdit}
|
||||
>
|
||||
<Tags />
|
||||
</StyledButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("toolbar.selected_png")}>
|
||||
<StyledButton
|
||||
type="button"
|
||||
|
||||
@@ -665,9 +665,6 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => {
|
||||
model.setShowGridLines(sheet, show);
|
||||
setRedrawId((id) => id + 1);
|
||||
}}
|
||||
openDrawer={() => {
|
||||
setDrawerOpen(true);
|
||||
}}
|
||||
/>
|
||||
<WorksheetAreaLeft $drawerWidth={isDrawerOpen ? drawerWidth : 0}>
|
||||
<FormulaBar
|
||||
@@ -682,6 +679,10 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => {
|
||||
}}
|
||||
model={model}
|
||||
workbookState={workbookState}
|
||||
openDrawer={() => {
|
||||
setDrawerOpen(true);
|
||||
}}
|
||||
canEdit={true}
|
||||
/>
|
||||
<Worksheet
|
||||
model={model}
|
||||
@@ -762,9 +763,9 @@ type WorksheetAreaLeftProps = { $drawerWidth: number };
|
||||
const WorksheetAreaLeft = styled("div")<WorksheetAreaLeftProps>(
|
||||
({ $drawerWidth }) => ({
|
||||
position: "absolute",
|
||||
top: `${TOOLBAR_HEIGHT + 1}px`,
|
||||
top: `${TOOLBAR_HEIGHT}px`,
|
||||
width: `calc(100% - ${$drawerWidth}px)`,
|
||||
height: `calc(100% - ${TOOLBAR_HEIGHT + 1}px)`,
|
||||
height: `calc(100% - ${TOOLBAR_HEIGHT}px)`,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -92,6 +92,9 @@
|
||||
"label": "Formula",
|
||||
"title": "Update formula"
|
||||
},
|
||||
"formula_bar": {
|
||||
"manage_named_ranges": "Manage Named Ranges"
|
||||
},
|
||||
"navigation": {
|
||||
"add_sheet": "Add sheet",
|
||||
"sheet_list": "Sheet list",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user