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_actions;
|
||||||
|
mod test_arabic_roman;
|
||||||
mod test_binary_search;
|
mod test_binary_search;
|
||||||
mod test_cell;
|
mod test_cell;
|
||||||
mod test_cell_clear_contents;
|
mod test_cell_clear_contents;
|
||||||
@@ -39,6 +40,7 @@ mod test_metadata;
|
|||||||
mod test_model_cell_clear_all;
|
mod test_model_cell_clear_all;
|
||||||
mod test_model_is_empty_cell;
|
mod test_model_is_empty_cell;
|
||||||
mod test_move_formula;
|
mod test_move_formula;
|
||||||
|
mod test_mround_trunc_int;
|
||||||
mod test_quote_prefix;
|
mod test_quote_prefix;
|
||||||
mod test_row_column_styles;
|
mod test_row_column_styles;
|
||||||
mod test_set_user_input;
|
mod test_set_user_input;
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use crate::test::util::new_empty_model;
|
|||||||
fn arguments() {
|
fn arguments() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model._set("A1", "=ARABIC()");
|
model._set("A1", "=ARABIC()");
|
||||||
model._set("A2", "=ARABIC(V)");
|
model._set("A2", "=ARABIC(\"V\")");
|
||||||
model._set("A3", "=ARABIC(V, 2)");
|
model._set("A3", "=ARABIC(\"V\", 2)");
|
||||||
|
|
||||||
model._set("A4", "=ROMAN()");
|
model._set("A4", "=ROMAN()");
|
||||||
model._set("A5", "=ROMAN(5)");
|
model._set("A5", "=ROMAN(5)");
|
||||||
@@ -20,7 +20,6 @@ fn arguments() {
|
|||||||
model._set("A11", "=INT(10.22, 1)");
|
model._set("A11", "=INT(10.22, 1)");
|
||||||
model._set("A12", "=INT(10.22, 1, 2)");
|
model._set("A12", "=INT(10.22, 1, 2)");
|
||||||
|
|
||||||
|
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
|
|
||||||
assert_eq!(model._get_text("A1"), *"#ERROR!");
|
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("A4"), *"#ERROR!");
|
||||||
|
|
||||||
assert_eq!(model._get_text("A5"), *"#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("A7"), *"10.2");
|
||||||
assert_eq!(model._get_text("A8"), *"#ERROR!");
|
assert_eq!(model._get_text("A8"), *"#ERROR!");
|
||||||
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { Model } from "@ironcalc/wasm";
|
import type { Model } from "@ironcalc/wasm";
|
||||||
import { styled } from "@mui/material";
|
import { styled } from "@mui/material";
|
||||||
|
import { ChevronDown } from "lucide-react";
|
||||||
|
import { useState } from "react";
|
||||||
import { Fx } from "../../icons";
|
import { Fx } from "../../icons";
|
||||||
import { theme } from "../../theme";
|
import { theme } from "../../theme";
|
||||||
import { FORMULA_BAR_HEIGHT } from "../constants";
|
import { FORMULA_BAR_HEIGHT } from "../constants";
|
||||||
@@ -9,6 +11,7 @@ import {
|
|||||||
ROW_HEIGH_SCALE,
|
ROW_HEIGH_SCALE,
|
||||||
} from "../WorksheetCanvas/constants";
|
} from "../WorksheetCanvas/constants";
|
||||||
import type { WorkbookState } from "../workbookState";
|
import type { WorkbookState } from "../workbookState";
|
||||||
|
import FormulaBarMenu from "./FormulaBarMenu";
|
||||||
|
|
||||||
type FormulaBarProps = {
|
type FormulaBarProps = {
|
||||||
cellAddress: string;
|
cellAddress: string;
|
||||||
@@ -17,6 +20,8 @@ type FormulaBarProps = {
|
|||||||
workbookState: WorkbookState;
|
workbookState: WorkbookState;
|
||||||
onChange: () => void;
|
onChange: () => void;
|
||||||
onTextUpdated: () => void;
|
onTextUpdated: () => void;
|
||||||
|
openDrawer: () => void;
|
||||||
|
canEdit: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function FormulaBar(properties: FormulaBarProps) {
|
function FormulaBar(properties: FormulaBarProps) {
|
||||||
@@ -28,10 +33,27 @@ function FormulaBar(properties: FormulaBarProps) {
|
|||||||
onTextUpdated,
|
onTextUpdated,
|
||||||
workbookState,
|
workbookState,
|
||||||
} = properties;
|
} = properties;
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleMenuOpenChange = (isOpen: boolean): void => {
|
||||||
|
setIsMenuOpen(isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<AddressContainer>
|
<AddressContainer $active={isMenuOpen}>
|
||||||
<CellBarAddress>{cellAddress}</CellBarAddress>
|
<FormulaBarMenu
|
||||||
|
onMenuOpenChange={handleMenuOpenChange}
|
||||||
|
openDrawer={properties.openDrawer}
|
||||||
|
canEdit={properties.canEdit}
|
||||||
|
model={model}
|
||||||
|
onUpdate={onChange}
|
||||||
|
>
|
||||||
|
<CellBarAddress>{cellAddress}</CellBarAddress>
|
||||||
|
<StyledIcon>
|
||||||
|
<ChevronDown size={16} />
|
||||||
|
</StyledIcon>
|
||||||
|
</FormulaBarMenu>
|
||||||
</AddressContainer>
|
</AddressContainer>
|
||||||
<Divider />
|
<Divider />
|
||||||
<FormulaContainer>
|
<FormulaContainer>
|
||||||
@@ -101,7 +123,7 @@ const Divider = styled("div")`
|
|||||||
background-color: ${theme.palette.grey["300"]};
|
background-color: ${theme.palette.grey["300"]};
|
||||||
min-width: 1px;
|
min-width: 1px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin: 0px 16px;
|
margin: 0px 16px 0px 8px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FormulaContainer = styled("div")`
|
const FormulaContainer = styled("div")`
|
||||||
@@ -121,22 +143,46 @@ const Container = styled("div")`
|
|||||||
background: ${(properties): string =>
|
background: ${(properties): string =>
|
||||||
properties.theme.palette.background.default};
|
properties.theme.palette.background.default};
|
||||||
height: ${FORMULA_BAR_HEIGHT}px;
|
height: ${FORMULA_BAR_HEIGHT}px;
|
||||||
|
border-top: 1px solid ${theme.palette.grey["300"]};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const AddressContainer = styled("div")`
|
const AddressContainer = styled("div")<{ $active?: boolean }>`
|
||||||
padding-left: 16px;
|
|
||||||
color: ${theme.palette.common.black};
|
color: ${theme.palette.common.black};
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: normal;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-weight: 600;
|
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")`
|
const CellBarAddress = styled("div")`
|
||||||
width: 100%;
|
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")`
|
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",
|
display: "flex",
|
||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
gap: "8px",
|
||||||
padding: "8px 12px",
|
padding: "8px 12px",
|
||||||
|
cursor: "pointer",
|
||||||
minHeight: "40px",
|
minHeight: "40px",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
borderBottom: `1px solid ${theme.palette.grey[200]}`,
|
borderBottom: `1px solid ${theme.palette.grey[200]}`,
|
||||||
@@ -438,6 +440,8 @@ const NameText = styled("span")({
|
|||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
color: theme.palette.common.black,
|
color: theme.palette.common.black,
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
|
wordBreak: "break-all",
|
||||||
|
overflowWrap: "break-word",
|
||||||
});
|
});
|
||||||
|
|
||||||
const IconsWrapper = styled("div")({
|
const IconsWrapper = styled("div")({
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import {
|
|||||||
Redo2,
|
Redo2,
|
||||||
RemoveFormatting,
|
RemoveFormatting,
|
||||||
Strikethrough,
|
Strikethrough,
|
||||||
Tags,
|
|
||||||
Type,
|
Type,
|
||||||
Underline,
|
Underline,
|
||||||
Undo2,
|
Undo2,
|
||||||
@@ -87,7 +86,6 @@ type ToolbarProperties = {
|
|||||||
numFmt: string;
|
numFmt: string;
|
||||||
showGridLines: boolean;
|
showGridLines: boolean;
|
||||||
onToggleShowGridLines: (show: boolean) => void;
|
onToggleShowGridLines: (show: boolean) => void;
|
||||||
openDrawer: () => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function Toolbar(properties: ToolbarProperties) {
|
function Toolbar(properties: ToolbarProperties) {
|
||||||
@@ -514,18 +512,6 @@ function Toolbar(properties: ToolbarProperties) {
|
|||||||
{properties.showGridLines ? <Grid2x2Check /> : <Grid2x2X />}
|
{properties.showGridLines ? <Grid2x2Check /> : <Grid2x2X />}
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</Tooltip>
|
</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")}>
|
<Tooltip title={t("toolbar.selected_png")}>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -665,9 +665,6 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => {
|
|||||||
model.setShowGridLines(sheet, show);
|
model.setShowGridLines(sheet, show);
|
||||||
setRedrawId((id) => id + 1);
|
setRedrawId((id) => id + 1);
|
||||||
}}
|
}}
|
||||||
openDrawer={() => {
|
|
||||||
setDrawerOpen(true);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<WorksheetAreaLeft $drawerWidth={isDrawerOpen ? drawerWidth : 0}>
|
<WorksheetAreaLeft $drawerWidth={isDrawerOpen ? drawerWidth : 0}>
|
||||||
<FormulaBar
|
<FormulaBar
|
||||||
@@ -682,6 +679,10 @@ const Workbook = (props: { model: Model; workbookState: WorkbookState }) => {
|
|||||||
}}
|
}}
|
||||||
model={model}
|
model={model}
|
||||||
workbookState={workbookState}
|
workbookState={workbookState}
|
||||||
|
openDrawer={() => {
|
||||||
|
setDrawerOpen(true);
|
||||||
|
}}
|
||||||
|
canEdit={true}
|
||||||
/>
|
/>
|
||||||
<Worksheet
|
<Worksheet
|
||||||
model={model}
|
model={model}
|
||||||
@@ -762,9 +763,9 @@ type WorksheetAreaLeftProps = { $drawerWidth: number };
|
|||||||
const WorksheetAreaLeft = styled("div")<WorksheetAreaLeftProps>(
|
const WorksheetAreaLeft = styled("div")<WorksheetAreaLeftProps>(
|
||||||
({ $drawerWidth }) => ({
|
({ $drawerWidth }) => ({
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: `${TOOLBAR_HEIGHT + 1}px`,
|
top: `${TOOLBAR_HEIGHT}px`,
|
||||||
width: `calc(100% - ${$drawerWidth}px)`,
|
width: `calc(100% - ${$drawerWidth}px)`,
|
||||||
height: `calc(100% - ${TOOLBAR_HEIGHT + 1}px)`,
|
height: `calc(100% - ${TOOLBAR_HEIGHT}px)`,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -214,7 +214,6 @@ const Worksheet = forwardRef(
|
|||||||
onAllSheetSelected: () => {
|
onAllSheetSelected: () => {
|
||||||
model.setSelectedCell(1, 1);
|
model.setSelectedCell(1, 1);
|
||||||
model.setSelectedRange(1, 1, LAST_ROW, LAST_COLUMN);
|
model.setSelectedRange(1, 1, LAST_ROW, LAST_COLUMN);
|
||||||
refresh();
|
|
||||||
},
|
},
|
||||||
onCellSelected: (cell: Cell, event: React.MouseEvent) => {
|
onCellSelected: (cell: Cell, event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { type PointerEvent, type RefObject, useCallback, useRef } from "react";
|
|||||||
import { isInReferenceMode } from "../Editor/util";
|
import { isInReferenceMode } from "../Editor/util";
|
||||||
import type { Cell } from "../types";
|
import type { Cell } from "../types";
|
||||||
import { rangeToStr } from "../util";
|
import { rangeToStr } from "../util";
|
||||||
import { LAST_COLUMN, LAST_ROW } from "../WorksheetCanvas/constants";
|
|
||||||
import type WorksheetCanvas from "../WorksheetCanvas/worksheetCanvas";
|
import type WorksheetCanvas from "../WorksheetCanvas/worksheetCanvas";
|
||||||
import {
|
import {
|
||||||
headerColumnWidth,
|
headerColumnWidth,
|
||||||
@@ -35,10 +34,6 @@ interface PointerEvents {
|
|||||||
const usePointer = (options: PointerSettings): PointerEvents => {
|
const usePointer = (options: PointerSettings): PointerEvents => {
|
||||||
const isSelecting = useRef(false);
|
const isSelecting = useRef(false);
|
||||||
const isInsertingRef = useRef(false);
|
const isInsertingRef = useRef(false);
|
||||||
const isSelectingRows = useRef(false);
|
|
||||||
const initialRowRef = useRef<number | null>(null);
|
|
||||||
const isSelectingColumns = useRef(false);
|
|
||||||
const initialColumnRef = useRef<number | null>(null);
|
|
||||||
|
|
||||||
const onPointerMove = useCallback(
|
const onPointerMove = useCallback(
|
||||||
(event: PointerEvent): void => {
|
(event: PointerEvent): void => {
|
||||||
@@ -48,17 +43,10 @@ const usePointer = (options: PointerSettings): PointerEvents => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!(isSelecting.current || isInsertingRef.current)) {
|
||||||
!(
|
|
||||||
isSelecting.current ||
|
|
||||||
isInsertingRef.current ||
|
|
||||||
isSelectingRows.current ||
|
|
||||||
isSelectingColumns.current
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { canvasElement, model, worksheetCanvas, refresh } = options;
|
const { canvasElement, model, worksheetCanvas } = options;
|
||||||
const canvas = canvasElement.current;
|
const canvas = canvasElement.current;
|
||||||
const worksheet = worksheetCanvas.current;
|
const worksheet = worksheetCanvas.current;
|
||||||
// Silence the linter
|
// Silence the linter
|
||||||
@@ -69,66 +57,6 @@ const usePointer = (options: PointerSettings): PointerEvents => {
|
|||||||
const x = event.clientX - canvasRect.x;
|
const x = event.clientX - canvasRect.x;
|
||||||
const y = event.clientY - canvasRect.y;
|
const y = event.clientY - canvasRect.y;
|
||||||
|
|
||||||
if (isSelectingRows.current) {
|
|
||||||
// Prevent text selection during row dragging
|
|
||||||
event.preventDefault();
|
|
||||||
// Handle row selection dragging
|
|
||||||
if (initialRowRef.current === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let targetRow: number | null = null;
|
|
||||||
if (x >= 0 && x < headerColumnWidth && y >= headerRowHeight) {
|
|
||||||
const cell = worksheet.getCellByCoordinates(headerColumnWidth, y);
|
|
||||||
if (cell) targetRow = cell.row;
|
|
||||||
} else if (x >= headerColumnWidth && y >= headerRowHeight) {
|
|
||||||
const cell = worksheet.getCellByCoordinates(x, y);
|
|
||||||
if (cell) targetRow = cell.row;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetRow !== null) {
|
|
||||||
const initialRow = initialRowRef.current;
|
|
||||||
model.setSelectedCell(Math.min(initialRow, targetRow), 1);
|
|
||||||
model.setSelectedRange(
|
|
||||||
Math.min(initialRow, targetRow),
|
|
||||||
1,
|
|
||||||
Math.max(initialRow, targetRow),
|
|
||||||
LAST_COLUMN,
|
|
||||||
);
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSelectingColumns.current) {
|
|
||||||
// Prevent text selection during column dragging
|
|
||||||
event.preventDefault();
|
|
||||||
// Handle column selection dragging
|
|
||||||
if (initialColumnRef.current === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let targetColumn: number | null = null;
|
|
||||||
if (x >= headerColumnWidth && y >= 0 && y < headerRowHeight) {
|
|
||||||
const cell = worksheet.getCellByCoordinates(x, headerRowHeight);
|
|
||||||
if (cell) targetColumn = cell.column;
|
|
||||||
} else if (x >= headerColumnWidth && y >= headerRowHeight) {
|
|
||||||
const cell = worksheet.getCellByCoordinates(x, y);
|
|
||||||
if (cell) targetColumn = cell.column;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetColumn !== null) {
|
|
||||||
const initialColumn = initialColumnRef.current;
|
|
||||||
model.setSelectedCell(1, Math.min(initialColumn, targetColumn));
|
|
||||||
model.setSelectedRange(
|
|
||||||
1,
|
|
||||||
Math.min(initialColumn, targetColumn),
|
|
||||||
LAST_ROW,
|
|
||||||
Math.max(initialColumn, targetColumn),
|
|
||||||
);
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cell = worksheet.getCellByCoordinates(x, y);
|
const cell = worksheet.getCellByCoordinates(x, y);
|
||||||
if (!cell) {
|
if (!cell) {
|
||||||
return;
|
return;
|
||||||
@@ -137,7 +65,7 @@ const usePointer = (options: PointerSettings): PointerEvents => {
|
|||||||
if (isSelecting.current) {
|
if (isSelecting.current) {
|
||||||
options.onAreaSelecting(cell);
|
options.onAreaSelecting(cell);
|
||||||
} else if (isInsertingRef.current) {
|
} else if (isInsertingRef.current) {
|
||||||
const { workbookState } = options;
|
const { refresh, workbookState } = options;
|
||||||
const editingCell = workbookState.getEditingCell();
|
const editingCell = workbookState.getEditingCell();
|
||||||
if (!editingCell || !editingCell.referencedRange) {
|
if (!editingCell || !editingCell.referencedRange) {
|
||||||
return;
|
return;
|
||||||
@@ -171,16 +99,6 @@ const usePointer = (options: PointerSettings): PointerEvents => {
|
|||||||
const { worksheetElement } = options;
|
const { worksheetElement } = options;
|
||||||
isInsertingRef.current = false;
|
isInsertingRef.current = false;
|
||||||
worksheetElement.current?.releasePointerCapture(event.pointerId);
|
worksheetElement.current?.releasePointerCapture(event.pointerId);
|
||||||
} else if (isSelectingRows.current) {
|
|
||||||
const { worksheetElement } = options;
|
|
||||||
isSelectingRows.current = false;
|
|
||||||
initialRowRef.current = null;
|
|
||||||
worksheetElement.current?.releasePointerCapture(event.pointerId);
|
|
||||||
} else if (isSelectingColumns.current) {
|
|
||||||
const { worksheetElement } = options;
|
|
||||||
isSelectingColumns.current = false;
|
|
||||||
initialColumnRef.current = null;
|
|
||||||
worksheetElement.current?.releasePointerCapture(event.pointerId);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[options],
|
[options],
|
||||||
@@ -239,17 +157,7 @@ const usePointer = (options: PointerSettings): PointerEvents => {
|
|||||||
// Click on a row number
|
// Click on a row number
|
||||||
const cell = worksheet.getCellByCoordinates(headerColumnWidth, y);
|
const cell = worksheet.getCellByCoordinates(headerColumnWidth, y);
|
||||||
if (cell) {
|
if (cell) {
|
||||||
if (event.shiftKey) {
|
onRowSelected(cell.row, event.shiftKey);
|
||||||
// Shift+click: extend selection
|
|
||||||
onRowSelected(cell.row, true);
|
|
||||||
} else {
|
|
||||||
// Regular click: start drag selection
|
|
||||||
event.preventDefault();
|
|
||||||
initialRowRef.current = cell.row;
|
|
||||||
isSelectingRows.current = true;
|
|
||||||
worksheetWrapper.setPointerCapture(event.pointerId);
|
|
||||||
onRowSelected(cell.row, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
x > headerColumnWidth &&
|
x > headerColumnWidth &&
|
||||||
@@ -260,17 +168,7 @@ const usePointer = (options: PointerSettings): PointerEvents => {
|
|||||||
// Click on a column letter
|
// Click on a column letter
|
||||||
const cell = worksheet.getCellByCoordinates(x, headerRowHeight);
|
const cell = worksheet.getCellByCoordinates(x, headerRowHeight);
|
||||||
if (cell) {
|
if (cell) {
|
||||||
if (event.shiftKey) {
|
onColumnSelected(cell.column, event.shiftKey);
|
||||||
// Shift+click: extend selection
|
|
||||||
onColumnSelected(cell.column, true);
|
|
||||||
} else {
|
|
||||||
// Regular click: start drag selection
|
|
||||||
event.preventDefault();
|
|
||||||
initialColumnRef.current = cell.column;
|
|
||||||
isSelectingColumns.current = true;
|
|
||||||
worksheetWrapper.setPointerCapture(event.pointerId);
|
|
||||||
onColumnSelected(cell.column, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -92,6 +92,9 @@
|
|||||||
"label": "Formula",
|
"label": "Formula",
|
||||||
"title": "Update formula"
|
"title": "Update formula"
|
||||||
},
|
},
|
||||||
|
"formula_bar": {
|
||||||
|
"manage_named_ranges": "Manage Named Ranges"
|
||||||
|
},
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"add_sheet": "Add sheet",
|
"add_sheet": "Add sheet",
|
||||||
"sheet_list": "Sheet list",
|
"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