Skip to content

Commit 82f0b51

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

19 files changed

+1926
-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/AutoFillHandle.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as React from "react";
2+
import * as Actions from "./actions";
3+
import useDispatch from "./use-dispatch";
4+
5+
const AutoFillHandle: React.FC = () => {
6+
const dispatch = useDispatch();
7+
8+
const autoFillStart = React.useCallback(() => {
9+
dispatch(Actions.autoFillStart());
10+
}, [dispatch]);
11+
12+
const autoFillEnd = React.useCallback(() => {
13+
dispatch(Actions.autoFillEnd());
14+
}, [dispatch]);
15+
16+
const handleMouseDown = React.useCallback(
17+
(event: React.MouseEvent) => {
18+
event.stopPropagation();
19+
event.preventDefault();
20+
21+
autoFillStart();
22+
23+
const handleMouseUp = () => {
24+
autoFillEnd();
25+
window.removeEventListener("mouseup", handleMouseUp);
26+
};
27+
28+
window.addEventListener("mouseup", handleMouseUp);
29+
},
30+
[autoFillStart, autoFillEnd]
31+
);
32+
33+
return (
34+
<div
35+
className="Spreadsheet__auto-fill-handle"
36+
onMouseDown={handleMouseDown}
37+
/>
38+
);
39+
};
40+
41+
export default AutoFillHandle;
42+

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: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ import * as React from "react";
22
import { getSelectedDimensions } from "./util";
33
import FloatingRect from "./FloatingRect";
44
import useSelector from "./use-selector";
5+
import classNames from "classnames";
6+
import AutoFillHandle from "./AutoFillHandle";
57

68
const Selected: React.FC = () => {
79
const selected = useSelector((state) => state.selected);
10+
const selectedSize = useSelector((state) =>
11+
state.selected.size(state.model.data)
12+
);
813
const dimensions = useSelector(
914
(state) =>
1015
selected &&
@@ -16,16 +21,22 @@ const Selected: React.FC = () => {
1621
)
1722
);
1823
const dragging = useSelector((state) => state.dragging);
19-
const hidden = useSelector(
20-
(state) => state.selected.size(state.model.data) < 2
21-
);
24+
const autoFilling = useSelector((state) => state.autoFilling);
25+
const hidden = selectedSize === 0;
26+
2227
return (
2328
<FloatingRect
2429
variant="selected"
2530
dimensions={dimensions}
2631
dragging={dragging}
2732
hidden={hidden}
28-
/>
33+
className={classNames({
34+
"Spreadsheet__selected-single": selectedSize === 1,
35+
})}
36+
autoFilling={autoFilling}
37+
>
38+
{!hidden && <AutoFillHandle />}
39+
</FloatingRect>
2940
);
3041
};
3142

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;

0 commit comments

Comments
 (0)