Compare commits
2 Commits
main
...
dani/widge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f9b9083ec | ||
|
|
c88076257d |
@@ -1,5 +1,4 @@
|
|||||||
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;
|
||||||
@@ -40,7 +39,6 @@ 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,6 +20,7 @@ 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!");
|
||||||
@@ -28,7 +29,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"), *"10");
|
assert_eq!(model._get_text("A6"), *"#ERROR!");
|
||||||
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,7 +1,5 @@
|
|||||||
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";
|
||||||
@@ -11,7 +9,6 @@ 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;
|
||||||
@@ -20,8 +17,6 @@ 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) {
|
||||||
@@ -33,27 +28,10 @@ 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 $active={isMenuOpen}>
|
<AddressContainer>
|
||||||
<FormulaBarMenu
|
<CellBarAddress>{cellAddress}</CellBarAddress>
|
||||||
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>
|
||||||
@@ -123,7 +101,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 0px 8px;
|
margin: 0px 16px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FormulaContainer = styled("div")`
|
const FormulaContainer = styled("div")`
|
||||||
@@ -143,46 +121,22 @@ 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")<{ $active?: boolean }>`
|
const AddressContainer = styled("div")`
|
||||||
|
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;
|
||||||
align-items: center;
|
flex-grow: row;
|
||||||
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%;
|
||||||
box-sizing: border-box;
|
text-align: "center";
|
||||||
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")`
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
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,9 +398,7 @@ 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]}`,
|
||||||
@@ -440,8 +438,6 @@ 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,6 +33,7 @@ import {
|
|||||||
Redo2,
|
Redo2,
|
||||||
RemoveFormatting,
|
RemoveFormatting,
|
||||||
Strikethrough,
|
Strikethrough,
|
||||||
|
Tags,
|
||||||
Type,
|
Type,
|
||||||
Underline,
|
Underline,
|
||||||
Undo2,
|
Undo2,
|
||||||
@@ -86,6 +87,7 @@ 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) {
|
||||||
@@ -512,6 +514,18 @@ 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,6 +665,9 @@ 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
|
||||||
@@ -679,10 +682,6 @@ 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}
|
||||||
@@ -763,9 +762,9 @@ type WorksheetAreaLeftProps = { $drawerWidth: number };
|
|||||||
const WorksheetAreaLeft = styled("div")<WorksheetAreaLeftProps>(
|
const WorksheetAreaLeft = styled("div")<WorksheetAreaLeftProps>(
|
||||||
({ $drawerWidth }) => ({
|
({ $drawerWidth }) => ({
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: `${TOOLBAR_HEIGHT}px`,
|
top: `${TOOLBAR_HEIGHT + 1}px`,
|
||||||
width: `calc(100% - ${$drawerWidth}px)`,
|
width: `calc(100% - ${$drawerWidth}px)`,
|
||||||
height: `calc(100% - ${TOOLBAR_HEIGHT}px)`,
|
height: `calc(100% - ${TOOLBAR_HEIGHT + 1}px)`,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ 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,6 +3,7 @@ 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,
|
||||||
@@ -34,6 +35,10 @@ 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 => {
|
||||||
@@ -43,10 +48,17 @@ const usePointer = (options: PointerSettings): PointerEvents => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(isSelecting.current || isInsertingRef.current)) {
|
if (
|
||||||
|
!(
|
||||||
|
isSelecting.current ||
|
||||||
|
isInsertingRef.current ||
|
||||||
|
isSelectingRows.current ||
|
||||||
|
isSelectingColumns.current
|
||||||
|
)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { canvasElement, model, worksheetCanvas } = options;
|
const { canvasElement, model, worksheetCanvas, refresh } = options;
|
||||||
const canvas = canvasElement.current;
|
const canvas = canvasElement.current;
|
||||||
const worksheet = worksheetCanvas.current;
|
const worksheet = worksheetCanvas.current;
|
||||||
// Silence the linter
|
// Silence the linter
|
||||||
@@ -57,6 +69,66 @@ 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;
|
||||||
@@ -65,7 +137,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 { refresh, workbookState } = options;
|
const { workbookState } = options;
|
||||||
const editingCell = workbookState.getEditingCell();
|
const editingCell = workbookState.getEditingCell();
|
||||||
if (!editingCell || !editingCell.referencedRange) {
|
if (!editingCell || !editingCell.referencedRange) {
|
||||||
return;
|
return;
|
||||||
@@ -99,6 +171,16 @@ 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],
|
||||||
@@ -157,7 +239,17 @@ 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) {
|
||||||
onRowSelected(cell.row, event.shiftKey);
|
if (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 &&
|
||||||
@@ -168,7 +260,17 @@ 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) {
|
||||||
onColumnSelected(cell.column, event.shiftKey);
|
if (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,9 +92,6 @@
|
|||||||
"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.
BIN
xlsx/tests/calc_tests/COMBIN_COMBINA_detailed.xlsx
Normal file
BIN
xlsx/tests/calc_tests/COMBIN_COMBINA_detailed.xlsx
Normal file
Binary file not shown.
BIN
xlsx/tests/calc_tests/DMIN_DMAX_DAVERAGE_DSUM_DCOUNT_DGET 1.xlsx
Normal file
BIN
xlsx/tests/calc_tests/DMIN_DMAX_DAVERAGE_DSUM_DCOUNT_DGET 1.xlsx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
xlsx/tests/calc_tests/MROUND_edgecases.xlsx
Normal file
BIN
xlsx/tests/calc_tests/MROUND_edgecases.xlsx
Normal file
Binary file not shown.
BIN
xlsx/tests/calc_tests/RADIANS_DEGREES_and_mathematical.xlsx
Normal file
BIN
xlsx/tests/calc_tests/RADIANS_DEGREES_and_mathematical.xlsx
Normal file
Binary file not shown.
BIN
xlsx/tests/calc_tests/TRUNC_INT_edgecases.xlsx
Normal file
BIN
xlsx/tests/calc_tests/TRUNC_INT_edgecases.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user