Skip to content

Commit a3d9ac4

Browse files
authored
feat: Change spacing alt-click behavior to reset only one value (#4540)
## Description - When using alt modifier, on a spacing value, currently you can't reset one value only, you would reset the opposite value too. - When alt-click happens outside of label-button, no reset should happen - when using input in spacing, hodling alt/shift and then pressing enter allows to set opposite and all sides ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 0000) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
1 parent 7f09077 commit a3d9ac4

File tree

7 files changed

+74
-53
lines changed

7 files changed

+74
-53
lines changed

apps/builder/app/builder/features/style-panel/sections/position/inset-control.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import { InputPopover } from "../shared/input-popover";
99
import { InsetLayout, type InsetProperty } from "./inset-layout";
1010
import { InsetTooltip } from "./inset-tooltip";
1111
import { useComputedStyleDecl, useComputedStyles } from "../../shared/model";
12-
import { useModifierKeys } from "../../shared/modifier-keys";
12+
import { useModifierKeys, type Modifiers } from "../../shared/modifier-keys";
1313

1414
const Cell = ({
1515
scrubStatus,
1616
property,
17-
activeProperties,
17+
getActiveProperties,
1818
onHover,
1919
isPopoverOpen,
2020
onPopoverClose,
@@ -23,7 +23,7 @@ const Cell = ({
2323
onPopoverClose: () => void;
2424
scrubStatus: ReturnType<typeof useScrub>;
2525
property: InsetProperty;
26-
activeProperties: InsetProperty[];
26+
getActiveProperties: (modifiers?: Modifiers) => readonly InsetProperty[];
2727
onHover: (target: HoverTarget | undefined) => void;
2828
}) => {
2929
const styleDecl = useComputedStyleDecl(property);
@@ -38,7 +38,7 @@ const Cell = ({
3838
value={finalValue}
3939
isOpen={isPopoverOpen}
4040
property={property}
41-
activeProperties={activeProperties}
41+
getActiveProperties={getActiveProperties}
4242
onClose={onPopoverClose}
4343
/>
4444
<InsetTooltip property={property} preventOpen={scrubStatus.isActive}>
@@ -116,6 +116,11 @@ export const InsetControl = () => {
116116
const activeProperties = [
117117
...(activePopoverProperties ?? scrubStatus.properties),
118118
];
119+
const getActiveProperties = (modifiers?: Modifiers) => {
120+
return modifiers && openProperty
121+
? getInsetModifiersGroup(openProperty, modifiers)
122+
: activeProperties;
123+
};
119124

120125
const handleHover = (target: HoverTarget | undefined) => {
121126
setHoverTarget(target);
@@ -162,12 +167,12 @@ export const InsetControl = () => {
162167
}}
163168
>
164169
<InsetLayout
165-
activeProperties={activeProperties}
170+
getActiveProperties={getActiveProperties}
166171
renderCell={(property) => (
167172
<Cell
168173
scrubStatus={scrubStatus}
169174
property={property}
170-
activeProperties={activeProperties}
175+
getActiveProperties={getActiveProperties}
171176
onHover={handleHover}
172177
isPopoverOpen={openProperty === property}
173178
onPopoverClose={() => {
Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import type { Meta } from "@storybook/react";
2-
import type * as React from "react";
32
import { InsetLayout } from "./inset-layout";
43
import { Grid, theme, Text } from "@webstudio-is/design-system";
54

65
const Cell = () => (
76
<Text variant={"spaceSectionValueText"}>auto{/*1.275&#8203;rem*/}</Text>
87
);
98

10-
export const InsetLayoutComponent = (
11-
args: Omit<React.ComponentProps<typeof InsetLayout>, "renderCell">
12-
) => (
9+
export const InsetLayoutComponent = () => (
1310
<Grid css={{ width: theme.spacing[22], height: theme.spacing[18] }}>
1411
<InsetLayout
1512
renderCell={() => <Cell />}
16-
activeProperties={args.activeProperties}
13+
getActiveProperties={() => []}
1714
onHover={(hoverProps) => {
1815
console.info(hoverProps);
1916
}}
@@ -24,11 +21,4 @@ export const InsetLayoutComponent = (
2421
export default {
2522
title: "Style Panel/Inset",
2623
component: InsetLayoutComponent,
27-
28-
argTypes: {
29-
activeProperties: {
30-
options: ["left", "right", "top", "bottom"],
31-
control: { type: "check" },
32-
},
33-
},
3424
} as Meta<typeof InsetLayoutComponent>;

apps/builder/app/builder/features/style-panel/sections/position/inset-layout.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Grid, Box, theme, styled } from "@webstudio-is/design-system";
22
import type { MouseEvent } from "react";
3+
import type { Modifiers } from "../../shared/modifier-keys";
34

45
const RECT_HEIGHT = 6;
56
const RECT_WIDTH = 42;
@@ -41,7 +42,7 @@ type InsetLayoutProps = {
4142
onHover: (
4243
args: { element: HTMLElement; property: InsetProperty } | undefined
4344
) => void;
44-
activeProperties: readonly InsetProperty[];
45+
getActiveProperties: (modifiers?: Modifiers) => readonly InsetProperty[];
4546
};
4647
/**
4748
* Grid schema for graphical layout
@@ -59,7 +60,7 @@ type InsetLayoutProps = {
5960
export const InsetLayout = ({
6061
renderCell,
6162
onHover,
62-
activeProperties,
63+
getActiveProperties,
6364
}: InsetLayoutProps) => {
6465
const createHandleHover = (property: InsetProperty) => ({
6566
onMouseEnter: (e: MouseEvent<HTMLDivElement>) => {
@@ -72,6 +73,7 @@ export const InsetLayout = ({
7273
onHover(undefined);
7374
},
7475
});
76+
const activeProperties = getActiveProperties();
7577

7678
return (
7779
<Grid

apps/builder/app/builder/features/style-panel/sections/shared/input-popover.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { createBatchUpdate } from "../../shared/use-style-data";
1515
import { theme } from "@webstudio-is/design-system";
1616
import type { StyleValueSourceColor } from "~/shared/style-object-model";
1717
import { $availableUnitVariables } from "../../shared/model";
18+
import type { Modifiers } from "../../shared/modifier-keys";
1819

1920
const slideUpAndFade = keyframes({
2021
"0%": { opacity: 0, transform: "scale(0.8)" },
@@ -25,12 +26,12 @@ const Input = ({
2526
styleSource,
2627
value,
2728
property,
28-
activeProperties,
29+
getActiveProperties,
2930
onClosePopover,
3031
}: {
3132
styleSource: StyleValueSourceColor;
3233
property: StyleProperty;
33-
activeProperties: StyleProperty[];
34+
getActiveProperties: (modifiers?: Modifiers) => readonly StyleProperty[];
3435
value: StyleValue;
3536
onClosePopover: () => void;
3637
}) => {
@@ -47,6 +48,7 @@ const Input = ({
4748
getOptions={() => $availableUnitVariables.get()}
4849
onChange={(styleValue) => {
4950
setIntermediateValue(styleValue);
51+
const activeProperties = getActiveProperties();
5052
if (styleValue === undefined) {
5153
const batch = createBatchUpdate();
5254
for (const property of activeProperties) {
@@ -66,6 +68,7 @@ const Input = ({
6668
onHighlight={(styleValue) => {
6769
if (styleValue === undefined) {
6870
const batch = createBatchUpdate();
71+
const activeProperties = getActiveProperties();
6972
for (const property of activeProperties) {
7073
batch.deleteProperty(property);
7174
}
@@ -76,7 +79,14 @@ const Input = ({
7679
batch.setProperty(property)(styleValue);
7780
batch.publish({ isEphemeral: true });
7881
}}
79-
onChangeComplete={({ value }) => {
82+
onChangeComplete={({ value, altKey, shiftKey }) => {
83+
const activeProperties = getActiveProperties({
84+
altKey,
85+
shiftKey,
86+
ctrlKey: false,
87+
metaKey: false,
88+
});
89+
8090
setIntermediateValue(undefined);
8191
const batch = createBatchUpdate();
8292
for (const property of activeProperties) {
@@ -93,6 +103,7 @@ const Input = ({
93103
onReset={() => {
94104
setIntermediateValue(undefined);
95105
const batch = createBatchUpdate();
106+
const activeProperties = getActiveProperties();
96107
for (const property of activeProperties) {
97108
batch.deleteProperty(property);
98109
}
@@ -123,14 +134,14 @@ const PopoverContentStyled = styled(PopoverContent, {
123134
export const InputPopover = ({
124135
styleSource,
125136
property,
126-
activeProperties,
137+
getActiveProperties,
127138
value,
128139
isOpen,
129140
onClose,
130141
}: {
131142
styleSource: StyleValueSourceColor;
132143
property: StyleProperty;
133-
activeProperties: StyleProperty[];
144+
getActiveProperties: (modifiers?: Modifiers) => readonly StyleProperty[];
134145
value: StyleValue;
135146
isOpen: boolean;
136147
onClose: () => void;
@@ -157,7 +168,7 @@ export const InputPopover = ({
157168
styleSource={styleSource}
158169
value={value}
159170
property={property}
160-
activeProperties={activeProperties}
171+
getActiveProperties={getActiveProperties}
161172
onClosePopover={onClose}
162173
/>
163174
</PopoverContentStyled>

apps/builder/app/builder/features/style-panel/sections/space/space.tsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@ import { SpaceTooltip } from "./tooltip";
99
import { StyleSection } from "../../shared/style-section";
1010
import { movementMapSpace, useKeyboardNavigation } from "../shared/keyboard";
1111
import { useComputedStyleDecl, useComputedStyles } from "../../shared/model";
12-
import { createBatchUpdate, deleteProperty } from "../../shared/use-style-data";
13-
import { useModifierKeys } from "../../shared/modifier-keys";
12+
import { createBatchUpdate } from "../../shared/use-style-data";
13+
import { useModifierKeys, type Modifiers } from "../../shared/modifier-keys";
1414
import { theme } from "@webstudio-is/design-system";
1515

1616
const Cell = ({
1717
isPopoverOpen,
1818
onPopoverClose,
1919
onHover,
2020
property,
21-
activeProperties,
21+
getActiveProperties,
2222
scrubStatus,
2323
}: {
2424
isPopoverOpen: boolean;
2525
onPopoverClose: () => void;
2626
onHover: (target: HoverTarget | undefined) => void;
2727
property: SpaceStyleProperty;
28-
activeProperties: SpaceStyleProperty[];
28+
getActiveProperties: (modifiers?: Modifiers) => readonly SpaceStyleProperty[];
2929
scrubStatus: ReturnType<typeof useScrub>;
3030
}) => {
3131
const styleDecl = useComputedStyleDecl(property);
@@ -40,7 +40,7 @@ const Cell = ({
4040
value={finalValue}
4141
isOpen={isPopoverOpen}
4242
property={property}
43-
activeProperties={activeProperties}
43+
getActiveProperties={getActiveProperties}
4444
onClose={onPopoverClose}
4545
/>
4646
<SpaceTooltip property={property} preventOpen={scrubStatus.isActive}>
@@ -114,10 +114,16 @@ export const Section = () => {
114114
// by deafult highlight hovered or scrubbed properties
115115
// if keyboard navigation is active, highlight its active property
116116
// if popover is open, highlight its property and hovered properties
117-
const activeProperties = [
117+
const activeProperties: readonly SpaceStyleProperty[] = [
118118
...(activePopoverProperties ?? scrubStatus.properties),
119119
];
120120

121+
const getActiveProperties = (modifiers?: Modifiers) => {
122+
return modifiers && openProperty
123+
? getSpaceModifiersGroup(openProperty, modifiers)
124+
: activeProperties;
125+
};
126+
121127
const handleHover = (target: HoverTarget | undefined) => {
122128
setHoverTarget(target);
123129
keyboardNavigation.handleHover(target?.property);
@@ -132,14 +138,27 @@ export const Section = () => {
132138
const styleValueSource = styles.find(
133139
(styleDecl) => styleDecl.property === property
134140
)?.source.name;
141+
135142
if (
136-
event.altKey &&
137143
property &&
138144
// reset when the value is set and after try to edit two sides
139145
(styleValueSource === "local" || styleValueSource === "overwritten")
140146
) {
141-
deleteProperty(property);
142-
return;
147+
if (event.shiftKey && event.altKey) {
148+
const properties = getSpaceModifiersGroup(property, {
149+
shiftKey: true,
150+
altKey: false,
151+
});
152+
const batch = createBatchUpdate();
153+
for (const property of properties) {
154+
batch.deleteProperty(property);
155+
}
156+
batch.publish();
157+
return;
158+
}
159+
if (event.altKey) {
160+
return;
161+
}
143162
}
144163
handleOpenProperty(property);
145164
}}
@@ -161,7 +180,7 @@ export const Section = () => {
161180
}}
162181
onHover={handleHover}
163182
property={property}
164-
activeProperties={activeProperties}
183+
getActiveProperties={getActiveProperties}
165184
scrubStatus={scrubStatus}
166185
/>
167186
)}

apps/builder/app/builder/features/style-panel/sections/space/tooltip.tsx

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { useState, type ReactElement } from "react";
2-
import { useModifierKeys } from "../../shared/modifier-keys";
3-
import { getSpaceModifiersGroup } from "../shared/scrub";
4-
import { createBatchUpdate } from "../../shared/use-style-data";
2+
import { deleteProperty } from "../../shared/use-style-data";
53
import { Tooltip } from "@webstudio-is/design-system";
64
import { PropertyInfo } from "../../property-label";
75
import { useComputedStyles } from "../../shared/model";
@@ -86,10 +84,7 @@ export const SpaceTooltip = ({
8684
preventOpen: boolean;
8785
}) => {
8886
const [isOpen, setIsOpen] = useState(false);
89-
90-
const modifiers = useModifierKeys();
91-
92-
const properties = [...getSpaceModifiersGroup(property, modifiers)];
87+
const properties = [property];
9388
const styles = useComputedStyles(properties);
9489

9590
const propertyContent = propertyContents.find((propertyContent) =>
@@ -103,14 +98,6 @@ export const SpaceTooltip = ({
10398
setIsOpen(value);
10499
};
105100

106-
const resetProperties = () => {
107-
const batch = createBatchUpdate();
108-
for (const property of properties) {
109-
batch.deleteProperty(property);
110-
}
111-
batch.publish();
112-
};
113-
114101
return (
115102
<Tooltip
116103
open={isOpen}
@@ -122,7 +109,7 @@ export const SpaceTooltip = ({
122109
onClick: (event) => {
123110
if (event.altKey) {
124111
event.preventDefault();
125-
resetProperties();
112+
deleteProperty(property);
126113
return;
127114
}
128115
},
@@ -133,7 +120,7 @@ export const SpaceTooltip = ({
133120
description={propertyContent?.description}
134121
styles={styles}
135122
onReset={() => {
136-
resetProperties();
123+
deleteProperty(property);
137124
handleOpenChange(false);
138125
}}
139126
/>

apps/builder/app/builder/features/style-panel/shared/modifier-keys.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import { useState, useEffect } from "react";
22
import { shallowEqual } from "shallow-equal";
33

4+
export type Modifiers = {
5+
altKey: boolean;
6+
shiftKey: boolean;
7+
ctrlKey: boolean;
8+
metaKey: boolean;
9+
};
10+
411
// Used for combined mouse and keyboard interactions, like scrubbing while holding ALT.
512
// If it's just a keyboard interaction, you should already have a keyboard event at hand.
613
export const useModifierKeys = () => {

0 commit comments

Comments
 (0)