Skip to content

Commit 27c870a

Browse files
committed
Merge branch 'main' into fix-wf-paste
2 parents ac0c2c4 + f1aa37c commit 27c870a

File tree

20 files changed

+570
-313
lines changed

20 files changed

+570
-313
lines changed

apps/builder/app/builder/features/settings-panel/props-section/props-section.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import {
99
Flex,
1010
Box,
1111
} from "@webstudio-is/design-system";
12-
import { descendantComponent } from "@webstudio-is/react-sdk";
12+
import {
13+
descendantComponent,
14+
isAttributeNameSafe,
15+
} from "@webstudio-is/react-sdk";
1316
import {
1417
$propValuesByInstanceSelector,
1518
$propsIndex,
@@ -21,7 +24,6 @@ import { renderControl } from "../controls/combined";
2124
import { usePropsLogic, type PropAndMeta } from "./use-props-logic";
2225
import { Row } from "../shared";
2326
import { serverSyncStore } from "~/shared/sync";
24-
import { isAttributeNameSafe } from "~/shared/dom-utils";
2527

2628
type Item = {
2729
name: string;

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

Lines changed: 104 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { colord } from "colord";
12
import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
3+
import { useStore } from "@nanostores/react";
24
import { computed } from "nanostores";
35
import { matchSorter } from "match-sorter";
46
import { isFeatureEnabled } from "@webstudio-is/feature-flags";
@@ -19,9 +21,10 @@ import {
1921
properties as propertiesData,
2022
propertyDescriptions,
2123
} from "@webstudio-is/css-data";
22-
import { useStore } from "@nanostores/react";
24+
import { ROOT_INSTANCE_ID } from "@webstudio-is/sdk";
2325
import {
2426
hyphenateProperty,
27+
toValue,
2528
type StyleProperty,
2629
} from "@webstudio-is/css-engine";
2730
import {
@@ -33,12 +36,20 @@ import { styleConfigByName } from "../../shared/configs";
3336
import { deleteProperty, setProperty } from "../../shared/use-style-data";
3437
import {
3538
$definedStyles,
39+
$matchingBreakpoints,
40+
getDefinedStyles,
3641
useComputedStyleDecl,
3742
useComputedStyles,
3843
} from "../../shared/model";
3944
import { getDots } from "../../shared/style-section";
4045
import { PropertyInfo } from "../../property-label";
4146
import { sections } from "../sections";
47+
import { ColorPopover } from "../../shared/color-picker";
48+
import {
49+
$selectedInstanceSelector,
50+
$styles,
51+
$styleSourceSelections,
52+
} from "~/shared/nano-states";
4253

4354
// Only here to keep the same section module interface
4455
export const properties = [];
@@ -165,45 +176,41 @@ const AdvancedPropertyLabel = ({ property }: { property: StyleProperty }) => {
165176
styleDecl.source.name === "default" ? "code" : styleDecl.source.name;
166177
const [isOpen, setIsOpen] = useState(false);
167178
return (
168-
<Flex align="center">
169-
<Tooltip
170-
open={isOpen}
171-
onOpenChange={setIsOpen}
172-
// prevent closing tooltip on content click
173-
onPointerDown={(event) => event.preventDefault()}
174-
triggerProps={{
175-
onClick: (event) => {
176-
if (event.altKey) {
177-
event.preventDefault();
178-
deleteProperty(property);
179-
return;
180-
}
181-
setIsOpen(true);
182-
},
183-
}}
184-
content={
185-
<PropertyInfo
186-
title={label}
187-
description={description}
188-
styles={[styleDecl]}
189-
onReset={() => {
190-
deleteProperty(property);
191-
setIsOpen(false);
192-
}}
193-
/>
194-
}
195-
>
196-
<Flex shrink gap={1} align="center">
197-
<Label color={color} text="mono" truncate>
198-
{label}
199-
</Label>
200-
</Flex>
201-
</Tooltip>
202-
</Flex>
179+
<Tooltip
180+
open={isOpen}
181+
onOpenChange={setIsOpen}
182+
// prevent closing tooltip on content click
183+
onPointerDown={(event) => event.preventDefault()}
184+
triggerProps={{
185+
onClick: (event) => {
186+
if (event.altKey) {
187+
event.preventDefault();
188+
deleteProperty(property);
189+
return;
190+
}
191+
setIsOpen(true);
192+
},
193+
}}
194+
content={
195+
<PropertyInfo
196+
title={label}
197+
description={description}
198+
styles={[styleDecl]}
199+
onReset={() => {
200+
deleteProperty(property);
201+
setIsOpen(false);
202+
}}
203+
/>
204+
}
205+
>
206+
<Label color={color} text="mono">
207+
{label}
208+
</Label>
209+
</Tooltip>
203210
);
204211
};
205212

206-
const $customProperties = computed($definedStyles, (definedStyles) => {
213+
const $availableCustomProperties = computed($definedStyles, (definedStyles) => {
207214
const customProperties = new Set<StyleProperty>();
208215
for (const { property } of definedStyles) {
209216
if (property.startsWith("--")) {
@@ -221,21 +228,41 @@ const AdvancedPropertyValue = ({
221228
property: StyleProperty;
222229
}) => {
223230
const styleDecl = useComputedStyleDecl(property);
224-
const customProperties = useStore($customProperties);
231+
const availableCustomProperties = useStore($availableCustomProperties);
225232
const { items } = styleConfigByName(property);
226233
const inputRef = useRef<HTMLInputElement>(null);
227234
useEffect(() => {
228235
if (autoFocus) {
229236
inputRef.current?.focus();
230237
}
231238
}, [autoFocus]);
239+
const isColor = colord(toValue(styleDecl.usedValue)).isValid();
232240
return (
233241
<CssValueInputContainer
234242
inputRef={inputRef}
235243
variant="chromeless"
236244
size="2"
237245
text="mono"
238246
fieldSizing="content"
247+
prefix={
248+
isColor && (
249+
<ColorPopover
250+
size={1}
251+
value={styleDecl.usedValue}
252+
onChange={(styleValue) => {
253+
const options = { isEphemeral: true, listed: true };
254+
if (styleValue) {
255+
setProperty(property)(styleValue, options);
256+
} else {
257+
deleteProperty(property, options);
258+
}
259+
}}
260+
onChangeComplete={(styleValue) => {
261+
setProperty(property)(styleValue);
262+
}}
263+
/>
264+
)
265+
}
239266
property={property}
240267
styleSource={styleDecl.source.name}
241268
keywords={[
@@ -244,7 +271,7 @@ const AdvancedPropertyValue = ({
244271
value: item.name,
245272
})),
246273
// very basic custom properties autocomplete
247-
...Array.from(customProperties).map((name) => ({
274+
...Array.from(availableCustomProperties).map((name) => ({
248275
type: "keyword" as const,
249276
value: name,
250277
})),
@@ -276,23 +303,46 @@ const initialProperties = new Set<StyleProperty>([
276303
"opacity",
277304
]);
278305

279-
const $advancedProperties = computed($definedStyles, (definedStyles) => {
280-
// All properties used by the panels except the advanced panel
281-
const baseProperties = new Set<StyleProperty>([]);
282-
for (const { properties } of sections.values()) {
283-
for (const property of properties) {
284-
baseProperties.add(property);
306+
const $advancedProperties = computed(
307+
[
308+
$selectedInstanceSelector,
309+
$styleSourceSelections,
310+
$matchingBreakpoints,
311+
$styles,
312+
],
313+
(instanceSelector, styleSourceSelections, matchingBreakpoints, styles) => {
314+
if (instanceSelector === undefined) {
315+
return [];
285316
}
286-
}
287-
const advancedProperties = new Set<StyleProperty>(initialProperties);
288-
for (const { property, listed } of definedStyles) {
289-
// exclude properties from style panel UI unless edited in advanced section
290-
if (baseProperties.has(property) === false || listed) {
291-
advancedProperties.add(property);
317+
const instanceAndRootSelector =
318+
instanceSelector[0] === ROOT_INSTANCE_ID
319+
? instanceSelector
320+
: // prevent showing properties inherited from root
321+
// to not bloat advanced panel
322+
instanceSelector;
323+
const definedStyles = getDefinedStyles({
324+
instanceSelector: instanceAndRootSelector,
325+
matchingBreakpoints,
326+
styleSourceSelections,
327+
styles,
328+
});
329+
// All properties used by the panels except the advanced panel
330+
const baseProperties = new Set<StyleProperty>([]);
331+
for (const { properties } of sections.values()) {
332+
for (const property of properties) {
333+
baseProperties.add(property);
334+
}
292335
}
336+
const advancedProperties = new Set<StyleProperty>(initialProperties);
337+
for (const { property, listed } of definedStyles) {
338+
// exclude properties from style panel UI unless edited in advanced section
339+
if (baseProperties.has(property) === false || listed) {
340+
advancedProperties.add(property);
341+
}
342+
}
343+
return Array.from(advancedProperties).reverse();
293344
}
294-
return Array.from(advancedProperties).reverse();
295-
});
345+
);
296346

297347
export const Section = () => {
298348
const [isAdding, setIsAdding] = useState(false);
@@ -320,7 +370,7 @@ export const Section = () => {
320370
)}
321371
<Box>
322372
{advancedProperties.map((property) => (
323-
<Flex key={property} wrap="wrap" align="center">
373+
<Flex key={property} wrap="wrap" align="center" justify="start">
324374
<AdvancedPropertyLabel property={property} />
325375
<Text>:</Text>
326376
<AdvancedPropertyValue

apps/builder/app/builder/features/style-panel/sections/borders/border-property.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import { type ReactNode } from "react";
22
import type { StyleProperty } from "@webstudio-is/css-engine";
33
import { toValue } from "@webstudio-is/css-engine";
4-
import {
5-
Box,
6-
Grid,
7-
NestedIconLabel,
8-
ToggleButton,
9-
} from "@webstudio-is/design-system";
4+
import { Box, Grid, ToggleButton } from "@webstudio-is/design-system";
105
import { CssValueInputContainer } from "../../shared/css-value-input";
116
import { styleConfigByName } from "../../shared/configs";
127
import { rowCss } from "./utils";
@@ -124,12 +119,7 @@ export const BorderProperty = ({
124119
<CssValueInputContainer
125120
key={styleDecl.property}
126121
icon={
127-
<NestedIconLabel>
128-
{
129-
borderPropertyOptions[styleDecl.property as StyleProperty]
130-
?.icon
131-
}
132-
</NestedIconLabel>
122+
borderPropertyOptions[styleDecl.property as StyleProperty]?.icon
133123
}
134124
property={styleDecl.property as StyleProperty}
135125
styleSource={styleDecl.source.name}

apps/builder/app/builder/features/style-panel/shared/color-picker.tsx

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -176,25 +176,21 @@ type ColorPickerProps = {
176176
disabled?: boolean;
177177
};
178178

179-
export const ColorPicker = ({
179+
export const ColorPopover = ({
180+
size,
180181
value,
181-
currentColor,
182-
keywords,
183-
property,
184-
disabled,
185182
onChange,
186183
onChangeComplete,
187-
onAbort,
188-
}: ColorPickerProps) => {
184+
}: {
185+
size?: 1 | 2;
186+
value: StyleValue;
187+
onChange: (value: undefined | StyleValue) => void;
188+
onChangeComplete: (value: StyleValue) => void;
189+
}) => {
189190
const [displayColorPicker, setDisplayColorPicker] = useState(false);
190191
const { enableCanvasPointerEvents, disableCanvasPointerEvents } =
191192
useDisableCanvasPointerEvents();
192193

193-
const [intermediateValue, setIntermediateValue] = useState<
194-
StyleValue | IntermediateStyleValue
195-
>();
196-
const currentValue = intermediateValue ?? value;
197-
198194
const handleOpenChange = (open: boolean) => {
199195
setDisplayColorPicker(open);
200196
if (open) {
@@ -209,15 +205,16 @@ export const ColorPicker = ({
209205
enableCanvasPointerEvents();
210206
};
211207

212-
const prefix = (
208+
return (
213209
<Popover modal open={displayColorPicker} onOpenChange={handleOpenChange}>
214210
<PopoverTrigger
215211
asChild
216212
aria-label="Open color picker"
217213
onClick={() => setDisplayColorPicker((shown) => !shown)}
218214
>
219215
<ColorThumb
220-
color={styleValueToRgbaColor(currentColor)}
216+
color={styleValueToRgbaColor(value)}
217+
size={size}
221218
css={{ margin: theme.spacing[2] }}
222219
tabIndex={-1}
223220
/>
@@ -230,7 +227,36 @@ export const ColorPicker = ({
230227
}}
231228
>
232229
<ColorPickerPopoverContent
233-
value={currentValue}
230+
value={value}
231+
onChange={onChange}
232+
onChangeComplete={onChangeComplete}
233+
/>
234+
</PopoverContent>
235+
</Popover>
236+
);
237+
};
238+
239+
export const ColorPicker = ({
240+
value,
241+
currentColor,
242+
keywords,
243+
property,
244+
disabled,
245+
onChange,
246+
onChangeComplete,
247+
onAbort,
248+
}: ColorPickerProps) => {
249+
const [intermediateValue, setIntermediateValue] = useState<
250+
StyleValue | IntermediateStyleValue
251+
>();
252+
253+
return (
254+
<CssValueInput
255+
aria-disabled={disabled}
256+
styleSource="default"
257+
prefix={
258+
<ColorPopover
259+
value={currentColor}
234260
onChange={(styleValue) => {
235261
setIntermediateValue(styleValue);
236262
if (styleValue) {
@@ -244,15 +270,7 @@ export const ColorPicker = ({
244270
onChangeComplete(value);
245271
}}
246272
/>
247-
</PopoverContent>
248-
</Popover>
249-
);
250-
251-
return (
252-
<CssValueInput
253-
aria-disabled={disabled}
254-
styleSource="default"
255-
prefix={prefix}
273+
}
256274
showSuffix={false}
257275
property={property}
258276
value={value}

0 commit comments

Comments
 (0)