Skip to content

Commit ac2e8d9

Browse files
authored
feat: support editing sides in space and inset controls (#4341)
Here made space and inset controls to remember active properties when open side input. Test inset and space controls with alt and shift pressed. https://github.com/user-attachments/assets/c6718245-9426-49ed-91a5-05f83c3ccb88
1 parent c7c9033 commit ac2e8d9

File tree

3 files changed

+79
-46
lines changed

3 files changed

+79
-46
lines changed

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

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +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";
1213

1314
const Cell = ({
1415
scrubStatus,
1516
property,
17+
activeProperties,
1618
onHover,
1719
isPopoverOpen,
1820
onPopoverClose,
@@ -21,6 +23,7 @@ const Cell = ({
2123
onPopoverClose: () => void;
2224
scrubStatus: ReturnType<typeof useScrub>;
2325
property: InsetProperty;
26+
activeProperties: InsetProperty[];
2427
onHover: (target: HoverTarget | undefined) => void;
2528
}) => {
2629
const styleDecl = useComputedStyleDecl(property);
@@ -35,6 +38,7 @@ const Cell = ({
3538
value={finalValue}
3639
isOpen={isPopoverOpen}
3740
property={property}
41+
activeProperties={activeProperties}
3842
onClose={onPopoverClose}
3943
/>
4044
<InsetTooltip property={property} preventOpen={scrubStatus.isActive}>
@@ -88,26 +92,30 @@ export const InsetControl = () => {
8892
});
8993

9094
const [openProperty, setOpenProperty] = useState<InsetProperty>();
95+
const [activePopoverProperties, setActivePopoverProperties] = useState<
96+
undefined | readonly InsetProperty[]
97+
>();
98+
const modifiers = useModifierKeys();
99+
const handleOpenProperty = (property: undefined | InsetProperty) => {
100+
setOpenProperty(property);
101+
setActivePopoverProperties(
102+
property ? getInsetModifiersGroup(property, modifiers) : undefined
103+
);
104+
};
91105

92106
const layoutRef = useRef<HTMLDivElement>(null);
93107

94108
const keyboardNavigation = useKeyboardNavigation({
95-
onOpen: setOpenProperty,
109+
onOpen: handleOpenProperty,
96110
movementMap: movementMapInset,
97111
});
98112

99113
// by deafult highlight hovered or scrubbed properties
100-
let activeProperties = scrubStatus.properties;
101-
102114
// if keyboard navigation is active, highlight its active property
103-
if (keyboardNavigation.isActive) {
104-
activeProperties = [keyboardNavigation.activeProperty];
105-
}
106-
107115
// if popover is open, highlight its property and hovered properties
108-
if (openProperty !== undefined) {
109-
activeProperties = [openProperty, ...scrubStatus.properties];
110-
}
116+
const activeProperties = [
117+
...(activePopoverProperties ?? scrubStatus.properties),
118+
];
111119

112120
const handleHover = (target: HoverTarget | undefined) => {
113121
setHoverTarget(target);
@@ -138,11 +146,19 @@ export const InsetControl = () => {
138146
onMouseLeave={keyboardNavigation.handleMouseLeave}
139147
onClick={(event) => {
140148
const property = hoverTarget?.property;
141-
if (event.altKey && property) {
149+
const styleValueSource = styles.find(
150+
(styleDecl) => styleDecl.property === property
151+
)?.source.name;
152+
if (
153+
event.altKey &&
154+
property &&
155+
// reset when the value is set and after try to edit two sides
156+
(styleValueSource === "local" || styleValueSource === "overwritten")
157+
) {
142158
deleteProperty(property);
143159
return;
144160
}
145-
setOpenProperty(property);
161+
handleOpenProperty(property);
146162
}}
147163
>
148164
<InsetLayout
@@ -151,11 +167,12 @@ export const InsetControl = () => {
151167
<Cell
152168
scrubStatus={scrubStatus}
153169
property={property}
170+
activeProperties={activeProperties}
154171
onHover={handleHover}
155172
isPopoverOpen={openProperty === property}
156173
onPopoverClose={() => {
157174
if (openProperty === property) {
158-
setOpenProperty(undefined);
175+
handleOpenProperty(undefined);
159176
layoutRef.current?.focus();
160177
}
161178
}}

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

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,31 @@ import {
66
PopoverTrigger,
77
PopoverContent,
88
} from "@webstudio-is/design-system";
9-
import type { StyleValue } from "@webstudio-is/css-engine";
9+
import type { StyleProperty, StyleValue } from "@webstudio-is/css-engine";
1010
import {
1111
CssValueInput,
1212
type IntermediateStyleValue,
1313
} from "../../shared/css-value-input";
1414
import type { StyleSource } from "../../shared/style-info";
1515
import { createBatchUpdate } from "../../shared/use-style-data";
1616
import { theme } from "@webstudio-is/design-system";
17-
import { getInsetModifiersGroup, getSpaceModifiersGroup } from "./scrub";
18-
import type { SpaceStyleProperty } from "../space/types";
19-
import type { InsetProperty } from "../position/inset-layout";
2017
import { $availableUnitVariables } from "../../shared/model";
2118

2219
const slideUpAndFade = keyframes({
2320
"0%": { opacity: 0, transform: "scale(0.8)" },
2421
"100%": { opacity: 1, transform: "scale(1)" },
2522
});
2623

27-
// We need to differentiate between marginTop and top for example.
28-
const isSpace = (property: string) => {
29-
return property.startsWith("margin") || property.startsWith("padding");
30-
};
31-
3224
const Input = ({
3325
styleSource,
3426
value,
3527
property,
28+
activeProperties,
3629
onClosePopover,
3730
}: {
3831
styleSource: StyleSource;
39-
property: SpaceStyleProperty | InsetProperty;
32+
property: StyleProperty;
33+
activeProperties: StyleProperty[];
4034
value: StyleValue;
4135
onClosePopover: () => void;
4236
}) => {
@@ -55,35 +49,37 @@ const Input = ({
5549
setIntermediateValue(styleValue);
5650
if (styleValue === undefined) {
5751
const batch = createBatchUpdate();
58-
batch.deleteProperty(property);
52+
for (const property of activeProperties) {
53+
batch.deleteProperty(property);
54+
}
5955
batch.publish({ isEphemeral: true });
6056
return;
6157
}
6258
if (styleValue.type !== "intermediate") {
6359
const batch = createBatchUpdate();
64-
batch.setProperty(property)(styleValue);
60+
for (const property of activeProperties) {
61+
batch.setProperty(property)(styleValue);
62+
}
6563
batch.publish({ isEphemeral: true });
6664
}
6765
}}
6866
onHighlight={(styleValue) => {
6967
if (styleValue === undefined) {
7068
const batch = createBatchUpdate();
71-
batch.deleteProperty(property);
69+
for (const property of activeProperties) {
70+
batch.deleteProperty(property);
71+
}
7272
batch.publish({ isEphemeral: true });
7373
return;
7474
}
7575
const batch = createBatchUpdate();
7676
batch.setProperty(property)(styleValue);
7777
batch.publish({ isEphemeral: true });
7878
}}
79-
onChangeComplete={({ value, altKey, shiftKey }) => {
79+
onChangeComplete={({ value }) => {
8080
const batch = createBatchUpdate();
81-
const modifiers = { shiftKey, altKey };
82-
const properties = isSpace(property)
83-
? getSpaceModifiersGroup(property as SpaceStyleProperty, modifiers)
84-
: getInsetModifiersGroup(property as InsetProperty, modifiers);
8581
setIntermediateValue(undefined);
86-
for (const property of properties) {
82+
for (const property of activeProperties) {
8783
batch.setProperty(property)(value);
8884
}
8985
batch.publish();
@@ -118,12 +114,14 @@ const PopoverContentStyled = styled(PopoverContent, {
118114
export const InputPopover = ({
119115
styleSource,
120116
property,
117+
activeProperties,
121118
value,
122119
isOpen,
123120
onClose,
124121
}: {
125122
styleSource: StyleSource;
126-
property: SpaceStyleProperty | InsetProperty;
123+
property: StyleProperty;
124+
activeProperties: StyleProperty[];
127125
value: StyleValue;
128126
isOpen: boolean;
129127
onClose: () => void;
@@ -151,6 +149,7 @@ export const InputPopover = ({
151149
styleSource={styleSource}
152150
value={value}
153151
property={property}
152+
activeProperties={activeProperties}
154153
onClosePopover={onClose}
155154
/>
156155
</PopoverContentStyled>

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

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,21 @@ import { StyleSection } from "../../shared/style-section";
1010
import { movementMapSpace, useKeyboardNavigation } from "../shared/keyboard";
1111
import { useComputedStyleDecl, useComputedStyles } from "../../shared/model";
1212
import { createBatchUpdate, deleteProperty } from "../../shared/use-style-data";
13+
import { useModifierKeys } from "../../shared/modifier-keys";
1314

1415
const Cell = ({
1516
isPopoverOpen,
1617
onPopoverClose,
1718
onHover,
1819
property,
20+
activeProperties,
1921
scrubStatus,
2022
}: {
2123
isPopoverOpen: boolean;
2224
onPopoverClose: () => void;
2325
onHover: (target: HoverTarget | undefined) => void;
2426
property: SpaceStyleProperty;
27+
activeProperties: SpaceStyleProperty[];
2528
scrubStatus: ReturnType<typeof useScrub>;
2629
}) => {
2730
const styleDecl = useComputedStyleDecl(property);
@@ -36,6 +39,7 @@ const Cell = ({
3639
value={finalValue}
3740
isOpen={isPopoverOpen}
3841
property={property}
42+
activeProperties={activeProperties}
3943
onClose={onPopoverClose}
4044
/>
4145
<SpaceTooltip property={property} preventOpen={scrubStatus.isActive}>
@@ -86,26 +90,30 @@ export const Section = () => {
8690
});
8791

8892
const [openProperty, setOpenProperty] = useState<SpaceStyleProperty>();
93+
const [activePopoverProperties, setActivePopoverProperties] = useState<
94+
undefined | readonly SpaceStyleProperty[]
95+
>();
96+
const modifiers = useModifierKeys();
97+
const handleOpenProperty = (property: undefined | SpaceStyleProperty) => {
98+
setOpenProperty(property);
99+
setActivePopoverProperties(
100+
property ? getSpaceModifiersGroup(property, modifiers) : undefined
101+
);
102+
};
89103

90104
const layoutRef = useRef<HTMLDivElement>(null);
91105

92106
const keyboardNavigation = useKeyboardNavigation({
93-
onOpen: setOpenProperty,
107+
onOpen: handleOpenProperty,
94108
movementMap: movementMapSpace,
95109
});
96110

97111
// by deafult highlight hovered or scrubbed properties
98-
let activeProperties = scrubStatus.properties;
99-
100112
// if keyboard navigation is active, highlight its active property
101-
if (keyboardNavigation.isActive) {
102-
activeProperties = [keyboardNavigation.activeProperty];
103-
}
104-
105113
// if popover is open, highlight its property and hovered properties
106-
if (openProperty !== undefined) {
107-
activeProperties = [openProperty, ...scrubStatus.properties];
108-
}
114+
const activeProperties = [
115+
...(activePopoverProperties ?? scrubStatus.properties),
116+
];
109117

110118
const handleHover = (target: HoverTarget | undefined) => {
111119
setHoverTarget(target);
@@ -118,11 +126,19 @@ export const Section = () => {
118126
ref={layoutRef}
119127
onClick={(event) => {
120128
const property = hoverTarget?.property;
121-
if (event.altKey && property) {
129+
const styleValueSource = styles.find(
130+
(styleDecl) => styleDecl.property === property
131+
)?.source.name;
132+
if (
133+
event.altKey &&
134+
property &&
135+
// reset when the value is set and after try to edit two sides
136+
(styleValueSource === "local" || styleValueSource === "overwritten")
137+
) {
122138
deleteProperty(property);
123139
return;
124140
}
125-
setOpenProperty(property);
141+
handleOpenProperty(property);
126142
}}
127143
onHover={handleHover}
128144
onFocus={keyboardNavigation.handleFocus}
@@ -136,12 +152,13 @@ export const Section = () => {
136152
isPopoverOpen={openProperty === property}
137153
onPopoverClose={() => {
138154
if (openProperty === property) {
139-
setOpenProperty(undefined);
155+
handleOpenProperty(undefined);
140156
layoutRef.current?.focus();
141157
}
142158
}}
143159
onHover={handleHover}
144160
property={property}
161+
activeProperties={activeProperties}
145162
scrubStatus={scrubStatus}
146163
/>
147164
)}

0 commit comments

Comments
 (0)