Skip to content

Commit

Permalink
experimental: support css variables in space section (#4226)
Browse files Browse the repository at this point in the history
Ref #3399

Here migrated space section to new style engine and added support for
autocompleting css variables. Rendering css variables was already in
place.

![Screenshot 2024-10-06 at 12 28
48](https://github.com/user-attachments/assets/b0c4d644-787d-4ca0-bb43-e8cbd8545a85)
  • Loading branch information
TrySound authored Oct 6, 2024
1 parent 40e9c1e commit 54a97c1
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ const Cell = ({
isOpen={isPopoverOpen}
property={property}
onClose={onPopoverClose}
createBatchUpdate={createBatchUpdate}
/>
<InsetTooltip
property={property}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ComponentProps, useState } from "react";
import { useState } from "react";
import {
keyframes,
styled,
Expand All @@ -12,15 +12,12 @@ import {
type IntermediateStyleValue,
} from "../../shared/css-value-input";
import type { StyleSource } from "../../shared/style-info";
import type {
CreateBatchUpdate,
StyleUpdate,
StyleUpdateOptions,
} from "../../shared/use-style-data";
import { createBatchUpdate } from "../../shared/use-style-data";
import { theme } from "@webstudio-is/design-system";
import { getInsetModifiersGroup, getSpaceModifiersGroup } from "./scrub";
import type { SpaceStyleProperty } from "../space/types";
import type { InsetProperty } from "../position/inset-layout";
import { $availableVariables } from "../../shared/model";

const slideUpAndFade = keyframes({
"0%": { opacity: 0, transform: "scale(0.8)" },
Expand All @@ -37,85 +34,67 @@ const Input = ({
value,
property,
onClosePopover,
createBatchUpdate,
}: {
styleSource: StyleSource;
property: SpaceStyleProperty | InsetProperty;
value: StyleValue;
onClosePopover: () => void;
createBatchUpdate: CreateBatchUpdate;
}) => {
const [intermediateValue, setIntermediateValue] = useState<
StyleValue | IntermediateStyleValue
>();

const onChange = (
updates: Array<StyleUpdate>,
options: StyleUpdateOptions
) => {
const batch = createBatchUpdate();
for (const update of updates) {
if (update.operation === "set") {
batch.setProperty(update.property)(update.value);
}
if (update.operation === "delete") {
batch.deleteProperty(update.property);
}
}
batch.publish(options);
};

return (
<CssValueInput
styleSource={styleSource}
property={property}
value={value}
intermediateValue={intermediateValue}
getOptions={() => $availableVariables.get()}
onChange={(styleValue) => {
setIntermediateValue(styleValue);

if (styleValue === undefined) {
onChange([{ operation: "delete", property }], { isEphemeral: true });
const batch = createBatchUpdate();
batch.deleteProperty(property);
batch.publish({ isEphemeral: true });
return;
}

if (styleValue.type !== "intermediate") {
onChange([{ operation: "set", property, value: styleValue }], {
isEphemeral: true,
});
const batch = createBatchUpdate();
batch.setProperty(property)(styleValue);
batch.publish({ isEphemeral: true });
}
}}
onHighlight={(styleValue) => {
if (styleValue === undefined) {
onChange([{ operation: "delete", property }], { isEphemeral: true });
const batch = createBatchUpdate();
batch.deleteProperty(property);
batch.publish({ isEphemeral: true });
return;
}

onChange([{ operation: "set", property, value: styleValue }], {
isEphemeral: true,
});
const batch = createBatchUpdate();
batch.setProperty(property)(styleValue);
batch.publish({ isEphemeral: true });
}}
onChangeComplete={({ value, type, altKey, shiftKey }) => {
const updates: Array<StyleUpdate> = [];
const options = { isEphemeral: false };
const batch = createBatchUpdate();
const modifiers = { shiftKey, altKey };
const properties = isSpace(property)
? getSpaceModifiersGroup(property as SpaceStyleProperty, modifiers)
: getInsetModifiersGroup(property as InsetProperty, modifiers);

setIntermediateValue(undefined);

properties.forEach((property) => {
updates.push({ operation: "set", property, value });
});
onChange(updates, options);

for (const property of properties) {
batch.setProperty(property)(value);
}
batch.publish({ isEphemeral: false });
if (type === "blur" || type === "enter") {
onClosePopover();
}
}}
onAbort={() => {
onChange([{ operation: "delete", property }], { isEphemeral: true });
const batch = createBatchUpdate();
batch.deleteProperty(property);
batch.publish({ isEphemeral: true });
}}
/>
);
Expand Down Expand Up @@ -144,11 +123,10 @@ export const InputPopover = ({
value,
isOpen,
onClose,
createBatchUpdate,
}: Pick<
ComponentProps<typeof Input>,
"styleSource" | "property" | "value" | "createBatchUpdate"
> & {
}: {
styleSource: StyleSource;
property: SpaceStyleProperty | InsetProperty;
value: StyleValue;
isOpen: boolean;
onClose: () => void;
}) => {
Expand All @@ -169,7 +147,6 @@ export const InputPopover = ({
styleSource={styleSource}
value={value}
property={property}
createBatchUpdate={createBatchUpdate}
onClosePopover={onClose}
/>
</PopoverContentStyled>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,44 @@
import { useState, useRef } from "react";
import type { SectionProps } from "../shared/section";
import { SpaceLayout } from "./layout";
import { ValueText } from "../shared/value-text";
import { getSpaceModifiersGroup, useScrub } from "../shared/scrub";
import { spaceProperties } from "./properties";
import type { SpaceStyleProperty, HoverTarget } from "./types";
import { InputPopover } from "../shared/input-popover";
import { SpaceTooltip } from "./tooltip";
import { getStyleSource } from "../../shared/style-info";
import { StyleSection } from "../../shared/style-section";
import { movementMapSpace, useKeyboardNavigation } from "../shared/keyboard";
import type { CreateBatchUpdate } from "../../shared/use-style-data";
import { useComputedStyleDecl, useComputedStyles } from "../../shared/model";
import { createBatchUpdate, deleteProperty } from "../../shared/use-style-data";

const Cell = ({
isPopoverOpen,
onPopoverClose,
onHover,
property,
scrubStatus,
currentStyle,
createBatchUpdate,
}: {
isPopoverOpen: boolean;
onPopoverClose: () => void;
onHover: (target: HoverTarget | undefined) => void;
property: SpaceStyleProperty;
scrubStatus: ReturnType<typeof useScrub>;
currentStyle: SectionProps["currentStyle"];
createBatchUpdate: CreateBatchUpdate;
}) => {
const styleInfo = currentStyle[property];
const styleDecl = useComputedStyleDecl(property);
const finalValue =
(scrubStatus.isActive && scrubStatus.values[property]) || styleInfo?.value;
const styleSource = getStyleSource(styleInfo);

// for TypeScript
if (finalValue === undefined) {
return;
}
(scrubStatus.isActive && scrubStatus.values[property]) ||
styleDecl.cascadedValue;

return (
<>
<InputPopover
styleSource={styleSource}
styleSource={styleDecl.source.name}
value={finalValue}
isOpen={isPopoverOpen}
property={property}
onClose={onPopoverClose}
createBatchUpdate={createBatchUpdate}
/>
<SpaceTooltip
property={property}
style={currentStyle}
createBatchUpdate={createBatchUpdate}
preventOpen={scrubStatus.isActive}
>
<SpaceTooltip property={property} preventOpen={scrubStatus.isActive}>
<ValueText
css={{
// We want value to have `default` cursor to indicate that it's clickable,
Expand All @@ -66,7 +50,7 @@ const Cell = ({
pointerEvents: "all",
}}
value={finalValue}
source={styleSource}
source={styleDecl.source.name}
onMouseEnter={(event) =>
onHover({ property, element: event.currentTarget })
}
Expand All @@ -79,18 +63,17 @@ const Cell = ({

export { spaceProperties as properties };

export const Section = ({
deleteProperty,
createBatchUpdate,
currentStyle,
}: SectionProps) => {
export const Section = () => {
const styles = useComputedStyles(spaceProperties);
const [hoverTarget, setHoverTarget] = useState<HoverTarget>();

const scrubStatus = useScrub({
value:
hoverTarget === undefined
? undefined
: currentStyle[hoverTarget.property]?.value,
: styles.find(
(styleDecl) => styleDecl.property === hoverTarget.property
)?.usedValue,
target: hoverTarget,
getModifiersGroup: getSpaceModifiersGroup,
onChange: (values, options) => {
Expand Down Expand Up @@ -163,8 +146,6 @@ export const Section = ({
onHover={handleHover}
property={property}
scrubStatus={scrubStatus}
currentStyle={currentStyle}
createBatchUpdate={createBatchUpdate}
/>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { SpaceStyleProperty } from "./types";
import { PropertyTooltip } from "../../shared/property-name";
import type { StyleInfo } from "../../shared/style-info";
import { useState, type ReactElement } from "react";
import { useModifierKeys } from "../../shared/modifier-keys";
import { getSpaceModifiersGroup } from "../shared/scrub";
import type { CreateBatchUpdate } from "../../shared/use-style-data";
import { createBatchUpdate } from "../../shared/use-style-data";
import { Tooltip } from "@webstudio-is/design-system";
import { PropertyInfo } from "../../property-label";
import { useComputedStyles } from "../../shared/model";
import type { SpaceStyleProperty } from "./types";

const sides = {
paddingTop: "top",
Expand Down Expand Up @@ -77,22 +78,19 @@ const isSameUnorderedArrays = (

export const SpaceTooltip = ({
property,
style,
children,
createBatchUpdate,
preventOpen,
}: {
property: SpaceStyleProperty;
style: StyleInfo;
children: ReactElement;
createBatchUpdate: CreateBatchUpdate;
preventOpen: boolean;
}) => {
const [open, setOpen] = useState(false);
const [isOpen, setIsOpen] = useState(false);

const modifiers = useModifierKeys();

const properties = [...getSpaceModifiersGroup(property, modifiers)];
const styles = useComputedStyles(properties);

const propertyContent = propertyContents.find((propertyContent) =>
isSameUnorderedArrays(propertyContent.properties, properties)
Expand All @@ -102,27 +100,47 @@ export const SpaceTooltip = ({
if (preventOpen && value === true) {
return;
}
setOpen(value);
setIsOpen(value);
};

const resetProperties = () => {
const batch = createBatchUpdate();
for (const property of properties) {
batch.deleteProperty(property);
}
batch.publish();
};

return (
<PropertyTooltip
open={open}
<Tooltip
open={isOpen}
onOpenChange={handleOpenChange}
properties={properties}
style={style}
title={propertyContent?.label}
description={propertyContent?.description}
side={sides[property]}
onReset={() => {
const batch = createBatchUpdate();
for (const property of properties) {
batch.deleteProperty(property);
}
batch.publish();
// prevent closing tooltip on content click
onPointerDown={(event) => event.preventDefault()}
triggerProps={{
onClick: (event) => {
if (event.altKey) {
event.preventDefault();
resetProperties();
return;
}
},
}}
content={
<PropertyInfo
title={propertyContent?.label ?? ""}
description={propertyContent?.description}
styles={styles}
onReset={() => {
resetProperties();
handleOpenChange(false);
}}
/>
}
>
{/* @todo show tooltip on focus */}
<div>{children}</div>
</PropertyTooltip>
</Tooltip>
);
};

0 comments on commit 54a97c1

Please sign in to comment.