Skip to content

Commit 64806b2

Browse files
committed
AutoFill Handle
Add auto-fill handle to autofill values
1 parent bb80b60 commit 64806b2

19 files changed

+1928
-22
lines changed

src/ActiveCell.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,25 @@ const ActiveCell: React.FC<Props> = (props) => {
1616
const rootRef = React.useRef<HTMLDivElement>(null);
1717

1818
const dispatch = useDispatch();
19+
1920
const setCellData = React.useCallback(
2021
(active: Point.Point, data: Types.CellBase) =>
2122
dispatch(Actions.setCellData(active, data)),
2223
[dispatch]
2324
);
25+
2426
const edit = React.useCallback(() => dispatch(Actions.edit()), [dispatch]);
27+
2528
const commit = React.useCallback(
2629
(changes: Types.CommitChanges<Types.CellBase>) =>
2730
dispatch(Actions.commit(changes)),
2831
[dispatch]
2932
);
33+
3034
const view = React.useCallback(() => {
3135
dispatch(Actions.view());
3236
}, [dispatch]);
37+
3338
const active = useSelector((state) => state.active);
3439
const mode = useSelector((state) => state.mode);
3540
const cell = useSelector((state) =>

src/FloatingRect.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,37 @@ export type Props = {
77
dimensions?: Types.Dimensions | null | undefined;
88
hidden?: boolean;
99
dragging?: boolean;
10+
autoFilling?: boolean;
11+
className: string;
12+
children?: React.ReactNode;
1013
};
1114

1215
const FloatingRect: React.FC<Props> = ({
1316
dimensions,
1417
dragging,
18+
autoFilling,
1519
hidden,
1620
variant,
21+
className,
22+
children,
1723
}) => {
1824
const { width, height, top, left } = dimensions || {};
1925
return (
2026
<div
21-
className={classnames("Spreadsheet__floating-rect", {
22-
[`Spreadsheet__floating-rect--${variant}`]: variant,
23-
"Spreadsheet__floating-rect--dragging": dragging,
24-
"Spreadsheet__floating-rect--hidden": hidden,
25-
})}
27+
className={classnames(
28+
"Spreadsheet__floating-rect",
29+
{
30+
[`Spreadsheet__floating-rect--${variant}`]: variant,
31+
"Spreadsheet__floating-rect--dragging": dragging,
32+
"Spreadsheet__floating-rect--auto-filling": autoFilling,
33+
"Spreadsheet__floating-rect--hidden": hidden,
34+
},
35+
className
36+
)}
2637
style={{ width, height, top, left }}
27-
/>
38+
>
39+
{children}
40+
</div>
2841
);
2942
};
3043

src/Selected.tsx

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import * as React from "react";
2+
import * as Actions from "./actions";
23
import { getSelectedDimensions } from "./util";
34
import FloatingRect from "./FloatingRect";
45
import useSelector from "./use-selector";
6+
import useDispatch from "./use-dispatch";
7+
import classNames from "classnames";
58

69
const Selected: React.FC = () => {
710
const selected = useSelector((state) => state.selected);
11+
const selectedSize = useSelector((state) =>
12+
state.selected.size(state.model.data)
13+
);
814
const dimensions = useSelector(
915
(state) =>
1016
selected &&
@@ -16,17 +22,59 @@ const Selected: React.FC = () => {
1622
)
1723
);
1824
const dragging = useSelector((state) => state.dragging);
19-
const hidden = useSelector(
20-
(state) => state.selected.size(state.model.data) < 2
21-
);
25+
const autoFilling = useSelector((state) => state.autoFilling);
26+
const hidden = selectedSize === 0;
27+
2228
return (
2329
<FloatingRect
2430
variant="selected"
2531
dimensions={dimensions}
2632
dragging={dragging}
2733
hidden={hidden}
28-
/>
34+
className={classNames({
35+
"Spreadsheet__selected-single": selectedSize === 1,
36+
})}
37+
autoFilling={autoFilling}
38+
>
39+
{!hidden && <AutoFillHandle />}
40+
</FloatingRect>
2941
);
3042
};
3143

3244
export default Selected;
45+
46+
const AutoFillHandle: React.FC = () => {
47+
const dispatch = useDispatch();
48+
49+
const autoFillStart = React.useCallback(() => {
50+
dispatch(Actions.autoFillStart());
51+
}, [dispatch]);
52+
53+
const autoFillEnd = React.useCallback(() => {
54+
dispatch(Actions.autoFillEnd());
55+
}, [dispatch]);
56+
57+
const handleMouseDown = React.useCallback(
58+
(event: React.MouseEvent) => {
59+
event.stopPropagation();
60+
event.preventDefault();
61+
62+
autoFillStart();
63+
64+
const handleMouseUp = () => {
65+
autoFillEnd();
66+
window.removeEventListener("mouseup", handleMouseUp);
67+
};
68+
69+
window.addEventListener("mouseup", handleMouseUp);
70+
},
71+
[autoFillStart, autoFillEnd]
72+
);
73+
74+
return (
75+
<div
76+
className="Spreadsheet__auto-fill-handle"
77+
onMouseDown={handleMouseDown}
78+
/>
79+
);
80+
};

src/Spreadsheet.css

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,31 @@
124124
border: 2px var(--outline-color) solid;
125125
}
126126

127-
.Spreadsheet__floating-rect--dragging {
127+
.Spreadsheet__floating-rect--selected.Spreadsheet__selected-single {
128+
background: none;
128129
border: none;
129130
}
130131

132+
.Spreadsheet__floating-rect--selected.Spreadsheet__floating-rect--auto-filling {
133+
background: none;
134+
border: 2px var(--readonly-text-color) dashed;
135+
}
136+
131137
.Spreadsheet__floating-rect--copied {
132138
border: 2px var(--outline-color) dashed;
133139
}
140+
141+
.Spreadsheet__auto-fill-handle {
142+
position: absolute;
143+
bottom: 0;
144+
right: 0;
145+
transform: translate(50%, 50%);
146+
width: 8px;
147+
height: 8px;
148+
background: var(--outline-color);
149+
border-radius: 50%;
150+
box-shadow: var(--elevation);
151+
cursor: pointer;
152+
z-index: 10;
153+
pointer-events: auto;
154+
}

src/actions.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export const KEY_DOWN = "KEY_DOWN";
3030
export const DRAG_START = "DRAG_START";
3131
export const DRAG_END = "DRAG_END";
3232
export const COMMIT = "COMMIT";
33+
export const AUTO_FILL_START = "AUTO_FILL_START";
34+
export const AUTO_FILL_END = "AUTO_FILL_END";
3335

3436
export type BaseAction<T extends string> = {
3537
type: T;
@@ -276,6 +278,18 @@ export function blur(): BlurAction {
276278
return { type: BLUR };
277279
}
278280

281+
export type AutoFillStartAction = BaseAction<typeof AUTO_FILL_START>;
282+
283+
export function autoFillStart(): AutoFillStartAction {
284+
return { type: AUTO_FILL_START };
285+
}
286+
287+
export type AutoFillEndAction = BaseAction<typeof AUTO_FILL_END>;
288+
289+
export function autoFillEnd(): AutoFillEndAction {
290+
return { type: AUTO_FILL_END };
291+
}
292+
279293
export type Action =
280294
| SetDataAction
281295
| SetCreateFormulaParserAction
@@ -298,4 +312,6 @@ export type Action =
298312
| EditAction
299313
| ViewAction
300314
| ClearAction
301-
| BlurAction;
315+
| BlurAction
316+
| AutoFillStartAction
317+
| AutoFillEndAction;

src/auto-fill.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Re-export from the new auto-fill module structure
2+
export { autoFillRange } from "./auto-fill/auto-fill-range";

0 commit comments

Comments
 (0)