FIX: color picker and border issues

This commit is contained in:
Nicolás Hatcher
2024-11-01 22:22:28 +01:00
committed by Nicolás Hatcher Andrés
parent 47acd0d600
commit d08fe32f97
4 changed files with 216 additions and 108 deletions

View File

@@ -1219,6 +1219,42 @@ impl UserModel {
let new_style = self.model.get_style_for_cell(sheet, source_row, column)?; let new_style = self.model.get_style_for_cell(sheet, source_row, column)?;
self.model.set_cell_style(sheet, row, column, &new_style)?; self.model.set_cell_style(sheet, row, column, &new_style)?;
if column == column1 && column > 1 {
// Fix the right border of the cell to the left
let old_left_style = self.model.get_style_for_cell(sheet, row, column - 1)?;
if old_left_style.border.right != new_style.border.left {
let mut new_left_style = old_left_style.clone();
new_left_style.border.right = new_style.border.left.clone();
self.model
.set_cell_style(sheet, row, column - 1, &new_left_style)?;
// Add the diff
diff_list.push(Diff::SetCellStyle {
sheet,
row,
column: column - 1,
old_value: Box::new(old_left_style),
new_value: Box::new(new_left_style),
});
}
} else if column == column1 + width1 - 1 && column < LAST_COLUMN {
// Fix the left border of the cell to the right
let old_right_style = self.model.get_style_for_cell(sheet, row, column + 1)?;
if old_right_style.border.left != new_style.border.right {
let mut new_right_style = old_right_style.clone();
new_right_style.border.left = new_style.border.right.clone();
self.model
.set_cell_style(sheet, row, column + 1, &new_right_style)?;
// Add the diff
diff_list.push(Diff::SetCellStyle {
sheet,
row,
column: column + 1,
old_value: Box::new(old_right_style),
new_value: Box::new(new_right_style),
});
}
}
// Add the diffs // Add the diffs
diff_list.push(Diff::SetCellStyle { diff_list.push(Diff::SetCellStyle {
sheet, sheet,
@@ -1248,27 +1284,27 @@ impl UserModel {
pub fn auto_fill_columns(&mut self, source_area: &Area, to_column: i32) -> Result<(), String> { pub fn auto_fill_columns(&mut self, source_area: &Area, to_column: i32) -> Result<(), String> {
let mut diff_list = Vec::new(); let mut diff_list = Vec::new();
let sheet = source_area.sheet; let sheet = source_area.sheet;
let row1 = source_area.row; let first_row = source_area.row;
let column1 = source_area.column; let first_column = source_area.column;
let width1 = source_area.width; let last_column = first_column + source_area.width - 1;
let height1 = source_area.height; let last_row = first_row + source_area.height - 1;
// Check first all parameters are valid // Check first all parameters are valid
if self.model.workbook.worksheet(sheet).is_err() { if self.model.workbook.worksheet(sheet).is_err() {
return Err(format!("Invalid worksheet index: '{sheet}'")); return Err(format!("Invalid worksheet index: '{sheet}'"));
} }
if !is_valid_column_number(column1) { if !is_valid_column_number(first_column) {
return Err(format!("Invalid column: '{column1}'")); return Err(format!("Invalid column: '{first_column}'"));
} }
if !is_valid_row(row1) { if !is_valid_row(first_row) {
return Err(format!("Invalid row: '{row1}'")); return Err(format!("Invalid row: '{first_row}'"));
} }
if !is_valid_column_number(column1 + width1 - 1) { if !is_valid_column_number(last_column) {
return Err(format!("Invalid column: '{}'", column1 + width1 - 1)); return Err(format!("Invalid column: '{}'", last_column));
} }
if !is_valid_row(row1 + height1 - 1) { if !is_valid_row(last_row) {
return Err(format!("Invalid row: '{}'", row1 + height1 - 1)); return Err(format!("Invalid row: '{}'", last_row));
} }
if !is_valid_row(to_column) { if !is_valid_row(to_column) {
@@ -1281,21 +1317,21 @@ impl UserModel {
// this is the range of columns we are going to fill // this is the range of columns we are going to fill
let column_range: Vec<i32>; let column_range: Vec<i32>;
if to_column >= column1 + width1 { if to_column > last_column {
// we go right, we start from `1 + width` to `to_column`, // we go right, we start from `1 + width` to `to_column`,
anchor_column = column1; anchor_column = first_column;
sign = 1; sign = 1;
column_range = (column1 + width1..to_column + 1).collect(); column_range = (last_column + 1..to_column + 1).collect();
} else if to_column < column1 { } else if to_column < first_column {
// we go left, starting from `column1 - `` all the way to `to_column` // we go left, starting from `column1 - `` all the way to `to_column`
anchor_column = column1 + width1 - 1; anchor_column = last_column;
sign = -1; sign = -1;
column_range = (to_column..column1).rev().collect(); column_range = (to_column..first_column).rev().collect();
} else { } else {
return Err("Invalid parameters for autofill".to_string()); return Err("Invalid parameters for autofill".to_string());
} }
for row in row1..row1 + height1 { for row in first_row..=last_row {
let mut index = 0; let mut index = 0;
for column_ref in &column_range { for column_ref in &column_range {
let column = *column_ref; let column = *column_ref;
@@ -1316,8 +1352,46 @@ impl UserModel {
self.model self.model
.set_user_input(sheet, row, column, target_value.to_string())?; .set_user_input(sheet, row, column, target_value.to_string())?;
// Compute the new style and set it
let new_style = self.model.get_style_for_cell(sheet, row, source_column)?; let new_style = self.model.get_style_for_cell(sheet, row, source_column)?;
if row == first_row && row > 1 {
// Fix the lower border of the upper cell
let old_upper_style = self.model.get_style_for_cell(sheet, row - 1, column)?;
if old_upper_style.border.bottom != new_style.border.top {
let mut new_upper_style = old_upper_style.clone();
new_upper_style.border.bottom = new_style.border.top.clone();
self.model
.set_cell_style(sheet, row - 1, column, &new_upper_style)?;
// Add the diffs
diff_list.push(Diff::SetCellStyle {
sheet,
row: row - 1,
column,
old_value: Box::new(old_upper_style),
new_value: Box::new(new_upper_style),
});
}
} else if row == last_row && row < LAST_ROW {
// Fix the upper border of the lower cell
let old_lower_style = self.model.get_style_for_cell(sheet, row + 1, column)?;
if old_lower_style.border.top != new_style.border.bottom {
let mut new_lower_style = old_lower_style.clone();
new_lower_style.border.top = new_style.border.bottom.clone();
self.model
.set_cell_style(sheet, row + 1, column, &new_lower_style)?;
// Add the diffs
diff_list.push(Diff::SetCellStyle {
sheet,
row: row + 1,
column,
old_value: Box::new(old_lower_style),
new_value: Box::new(new_lower_style),
});
}
}
// Compute the new style and set it
self.model.set_cell_style(sheet, row, column, &new_style)?; self.model.set_cell_style(sheet, row, column, &new_style)?;
// Add the diffs // Add the diffs
@@ -1336,7 +1410,7 @@ impl UserModel {
old_value: Box::new(old_value), old_value: Box::new(old_value),
}); });
index = (index + sign) % width1; index = (index + sign) % source_area.width;
} }
} }
self.push_diff_list(diff_list); self.push_diff_list(diff_list);
@@ -1478,6 +1552,76 @@ impl UserModel {
.model .model
.get_style_for_cell(sheet, target_row, target_column)?; .get_style_for_cell(sheet, target_row, target_column)?;
let new_style = value.style.clone();
// Fix borders
if target_row == source_first_row && target_row > 1 {
// Bottom border of the top cell
let old_top_style =
self.model
.get_style_for_cell(sheet, target_row - 1, target_column)?;
if new_style.border.top != old_top_style.border.bottom {
let mut new_top_style = old_top_style.clone();
new_top_style.border.bottom = new_style.border.top.clone();
diff_list.push(Diff::SetCellStyle {
sheet,
row: target_row - 1,
column: target_column,
old_value: Box::new(old_top_style),
new_value: Box::new(new_top_style),
});
}
} else if target_row == source_last_row && target_row < LAST_ROW {
// Top border of the lower cell
let old_bottom_style =
self.model
.get_style_for_cell(sheet, target_row + 1, target_column)?;
if new_style.border.bottom != old_bottom_style.border.top {
let mut new_top_style = old_bottom_style.clone();
new_top_style.border.top = new_style.border.bottom.clone();
diff_list.push(Diff::SetCellStyle {
sheet,
row: target_row + 1,
column: target_column,
old_value: Box::new(old_bottom_style),
new_value: Box::new(new_top_style),
});
}
}
if target_column == source_first_column && target_column > 1 {
// Right border of the cell to the left
let old_left_style =
self.model
.get_style_for_cell(sheet, target_row, target_column - 1)?;
if new_style.border.left != old_left_style.border.bottom {
let mut new_left_style = old_left_style.clone();
new_left_style.border.right = new_style.border.left.clone();
diff_list.push(Diff::SetCellStyle {
sheet,
row: target_row,
column: target_column - 1,
old_value: Box::new(old_left_style),
new_value: Box::new(new_left_style),
});
}
} else if target_column == source_last_column && target_column < LAST_COLUMN {
// Left border of the cell to the right
let old_right_style =
self.model
.get_style_for_cell(sheet, target_row, target_column + 1)?;
if new_style.border.right != old_right_style.border.left {
let mut new_right_style = old_right_style.clone();
new_right_style.border.left = new_style.border.right.clone();
diff_list.push(Diff::SetCellStyle {
sheet,
row: target_row,
column: target_column + 1,
old_value: Box::new(old_right_style),
new_value: Box::new(new_right_style),
});
}
}
self.model self.model
.set_user_input(sheet, target_row, target_column, new_value.clone())?; .set_user_input(sheet, target_row, target_column, new_value.clone())?;
self.model self.model
@@ -1509,6 +1653,7 @@ impl UserModel {
.worksheet(sheet)? .worksheet(sheet)?
.cell(row, column) .cell(row, column)
.cloned(); .cloned();
// TODO: also clear the styles and adjacent borders
diff_list.push(Diff::CellClearContents { diff_list.push(Diff::CellClearContents {
sheet, sheet,
row, row,

View File

@@ -143,10 +143,12 @@ export enum BorderStyle {
Thin = "thin", Thin = "thin",
Medium = "medium", Medium = "medium",
Thick = "thick", Thick = "thick",
Dashed = "dashed",
Dotted = "dotted",
Double = "double", Double = "double",
None = "none", Dotted = "dotted",
SlantDashDot = "slantdashdot",
MediumDashed = "mediumdashed",
MediumDashDotDot = "mediumdashdotdot",
MediumDashDot = "mediumdashdot",
} }
interface BorderItem { interface BorderItem {

View File

@@ -43,7 +43,8 @@ const BorderPicker = (properties: BorderPickerProps) => {
const [colorPickerOpen, setColorPickerOpen] = useState(false); const [colorPickerOpen, setColorPickerOpen] = useState(false);
const [stylePickerOpen, setStylePickerOpen] = useState(false); const [stylePickerOpen, setStylePickerOpen] = useState(false);
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation> // FIXME
// biome-ignore lint/correctness/useExhaustiveDependencies: We don't want updating the function every time the properties.onChange
useEffect(() => { useEffect(() => {
if (!borderSelected) { if (!borderSelected) {
return; return;
@@ -57,21 +58,29 @@ const BorderPicker = (properties: BorderPickerProps) => {
const onClose = properties.onClose; const onClose = properties.onClose;
// The reason is that the border picker doesn't start with the properties of the selected area
// biome-ignore lint/correctness/useExhaustiveDependencies: We reset the styles, every time we open (or close) the widget
useEffect(() => {
setBorderSelected(null);
setBorderColor("#000000");
setBorderStyle(BorderStyle.Thin);
}, [properties.open]);
const borderColorButton = useRef(null); const borderColorButton = useRef(null);
const borderStyleButton = useRef(null); const borderStyleButton = useRef(null);
return ( return (
<> <StyledPopover
<StyledPopover open={properties.open}
open={properties.open} onClose={onClose}
onClose={onClose} anchorEl={properties.anchorEl.current}
anchorEl={properties.anchorEl.current} anchorOrigin={
anchorOrigin={ properties.anchorOrigin || { vertical: "bottom", horizontal: "left" }
properties.anchorOrigin || { vertical: "bottom", horizontal: "left" } }
} transformOrigin={
transformOrigin={ properties.transformOrigin || { vertical: "top", horizontal: "left" }
properties.transformOrigin || { vertical: "top", horizontal: "left" } }
} >
> <div>
<BorderPickerDialog> <BorderPickerDialog>
<Borders> <Borders>
<Line> <Line>
@@ -283,16 +292,6 @@ const BorderPicker = (properties: BorderPickerProps) => {
transformOrigin={{ vertical: 38, horizontal: -6 }} transformOrigin={{ vertical: 38, horizontal: -6 }}
> >
<BorderStyleDialog> <BorderStyleDialog>
<LineWrapper
onClick={() => {
setBorderStyle(BorderStyle.Dashed);
setStylePickerOpen(false);
}}
$checked={borderStyle === BorderStyle.None}
>
<BorderDescription>None</BorderDescription>
<NoneLine />
</LineWrapper>
<LineWrapper <LineWrapper
onClick={() => { onClick={() => {
setBorderStyle(BorderStyle.Thin); setBorderStyle(BorderStyle.Thin);
@@ -323,40 +322,10 @@ const BorderPicker = (properties: BorderPickerProps) => {
<BorderDescription>Thick</BorderDescription> <BorderDescription>Thick</BorderDescription>
<ThickLine /> <ThickLine />
</LineWrapper> </LineWrapper>
<LineWrapper
onClick={() => {
setBorderStyle(BorderStyle.Dotted);
setStylePickerOpen(false);
}}
$checked={borderStyle === BorderStyle.Dotted}
>
<BorderDescription>Dotted</BorderDescription>
<DottedLine />
</LineWrapper>
<LineWrapper
onClick={() => {
setBorderStyle(BorderStyle.Dashed);
setStylePickerOpen(false);
}}
$checked={borderStyle === BorderStyle.Dashed}
>
<BorderDescription>Dashed</BorderDescription>
<DashedLine />
</LineWrapper>
<LineWrapper
onClick={() => {
setBorderStyle(BorderStyle.Dashed);
setStylePickerOpen(false);
}}
$checked={borderStyle === BorderStyle.Double}
>
<BorderDescription>Double</BorderDescription>
<DoubleLine />
</LineWrapper>
</BorderStyleDialog> </BorderStyleDialog>
</StyledPopover> </StyledPopover>
</StyledPopover> </div>
</> </StyledPopover>
); );
}; };
@@ -380,10 +349,6 @@ const LineWrapper = styled("div")<LineWrapperProperties>`
border: 1px solid white; border: 1px solid white;
`; `;
const NoneLine = styled("div")`
width: 68px;
border-top: 1px solid #e0e0e0;
`;
const SolidLine = styled("div")` const SolidLine = styled("div")`
width: 68px; width: 68px;
border-top: 1px solid #333333; border-top: 1px solid #333333;
@@ -396,18 +361,6 @@ const ThickLine = styled("div")`
width: 68px; width: 68px;
border-top: 3px solid #333333; border-top: 3px solid #333333;
`; `;
const DashedLine = styled("div")`
width: 68px;
border-top: 1px dashed #333333;
`;
const DottedLine = styled("div")`
width: 68px;
border-top: 1px dotted #333333;
`;
const DoubleLine = styled("div")`
width: 68px;
border-top: 3px double #333333;
`;
const Divider = styled("div")` const Divider = styled("div")`
display: inline-flex; display: inline-flex;

View File

@@ -103,25 +103,33 @@ const ColorPicker = (properties: ColorPickerProps) => {
/> />
))} ))}
</ColorList> </ColorList>
<HorizontalDivider />
<RecentLabel>{"Recent"}</RecentLabel> {recentColors.current.length > 0 ? (
<ColorList> <>
{recentColors.current.map((recentColor) => ( <HorizontalDivider />
<Button <RecentLabel>{"Recent"}</RecentLabel>
key={recentColor} <ColorList>
$color={recentColor} {recentColors.current.map((recentColor) => (
onClick={(): void => { <Button
closePicker(recentColor); key={recentColor}
}} $color={recentColor}
/> onClick={(): void => {
))} closePicker(recentColor);
</ColorList> }}
/>
))}
</ColorList>
</>
) : (
<div />
)}
</ColorPickerDialog> </ColorPickerDialog>
</Popover> </Popover>
); );
}; };
const RecentLabel = styled.div` const RecentLabel = styled.div`
font-family: "Inter";
font-size: 12px; font-size: 12px;
color: ${theme.palette.text.secondary}; color: ${theme.palette.text.secondary};
`; `;