mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-09 14:03:33 +09:00
experimental: support css variables in space section (#4226)
Ref https://github.com/webstudio-is/webstudio/issues/3399 Here migrated space section to new style engine and added support for autocompleting css variables. Rendering css variables was already in place. 
This commit is contained in:
1 parent
40e9c1e7ad
commit
54a97c10c2
4 files changed
+82
-107
No files matched your search
@@ -48,7 +48,6 @@ const Cell = ({
|
||||
isOpen={isPopoverOpen}
|
||||
property={property}
|
||||
onClose={onPopoverClose}
|
||||
createBatchUpdate={createBatchUpdate}
|
||||
/>
|
||||
<InsetTooltip
|
||||
property={property}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type ComponentProps, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
keyframes,
|
||||
styled,
|
||||
@@ -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)" },
|
||||
@@ -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 });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -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;
|
||||
}) => {
|
||||
@@ -169,7 +147,6 @@ export const InputPopover = ({
|
||||
styleSource={styleSource}
|
||||
value={value}
|
||||
property={property}
|
||||
createBatchUpdate={createBatchUpdate}
|
||||
onClosePopover={onClose}
|
||||
/>
|
||||
</PopoverContentStyled>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
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";
|
||||
@@ -7,10 +6,10 @@ 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,
|
||||
@@ -18,43 +17,28 @@ const Cell = ({
|
||||
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,
|
||||
@@ -66,7 +50,7 @@ const Cell = ({
|
||||
pointerEvents: "all",
|
||||
}}
|
||||
value={finalValue}
|
||||
source={styleSource}
|
||||
source={styleDecl.source.name}
|
||||
onMouseEnter={(event) =>
|
||||
onHover({ property, element: event.currentTarget })
|
||||
}
|
||||
@@ -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) => {
|
||||
@@ -163,8 +146,6 @@ export const Section = ({
|
||||
onHover={handleHover}
|
||||
property={property}
|
||||
scrubStatus={scrubStatus}
|
||||
currentStyle={currentStyle}
|
||||
createBatchUpdate={createBatchUpdate}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -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",
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user