Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const AdvancedStyleSection = (props: {
export const Section = () => {
const styleMap = useStore($advancedStylesLonghands);
const apiRef = useRef<CssEditorApi>();
const properties = Array.from(styleMap.keys()) as Array<CssProperty>;
const properties = Array.from(styleMap.keys());
const selectedInstanceKey = useStore($selectedInstanceKey);
// Memorizing recent properties by instance id, so that when user switches between instances and comes back
// they are still in-place
Expand All @@ -90,16 +90,14 @@ export const Section = () => {
setRecentPropertiesMap(newRecentPropertiesMap);
};

const handleAddProperties = (styleMap: CssStyleMap) => {
const handleAddDeclarations = (styleMap: CssStyleMap) => {
const batch = createBatchUpdate();
for (const [property, value] of styleMap) {
batch.setProperty(property as CssProperty)(value);
batch.setProperty(property)(value);
}
batch.publish({ listed: true });

const insertedProperties = Array.from(
styleMap.keys()
) as Array<CssProperty>;
const insertedProperties = Array.from(styleMap.keys());
updateRecentProperties([...recentProperties, ...insertedProperties]);
};

Expand All @@ -114,6 +112,19 @@ export const Section = () => {
);
};

const handleDeleteAllDeclarations = (styleMap: CssStyleMap) => {
const batch = createBatchUpdate();
for (const [property] of styleMap) {
batch.deleteProperty(property);
}
batch.publish();
updateRecentProperties(
recentProperties.filter(
(recentProperty) => styleMap.has(recentProperty) === false
)
);
};

return (
<AdvancedStyleSection
label="Advanced"
Expand All @@ -126,7 +137,8 @@ export const Section = () => {
styleMap={styleMap}
onDeleteProperty={handleDeleteProperty}
onSetProperty={setProperty}
onAddProperties={handleAddProperties}
onAddDeclarations={handleAddDeclarations}
onDeleteAllDeclarations={handleDeleteAllDeclarations}
apiRef={apiRef}
recentProperties={recentProperties}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
} from "@webstudio-is/design-system";
import {
generateStyleMap,
hyphenateProperty,
mergeStyles,
toValue,
type CssProperty,
Expand All @@ -17,54 +16,77 @@ import {

export const copyAttribute = "data-declaration";

export const CopyPasteMenu = ({
export const CssEditorContextMenu = ({
children,
properties,
styleMap,
onPaste,
onDeleteProperty,
onDeleteAllDeclarations,
}: {
children: ReactNode;
properties: Array<string>;
properties: Array<CssProperty>;
styleMap: CssStyleMap;
onPaste: (cssText: string) => void;
onDeleteProperty: (property: CssProperty) => void;
onDeleteAllDeclarations: (styleMap: CssStyleMap) => void;
}) => {
const lastClickedProperty = useRef<string>();

const handlePaste = () => {
navigator.clipboard.readText().then(onPaste);
};

const handleCopyAll = () => {
// Gets all currently visible declarations based on what's in the search or filters.
const getAllDeclarations = () => {
// We want to only copy properties that are currently in front of the user.
// That includes search or any future filters.
const currentStyleMap: CssStyleMap = new Map();
for (const [property, value] of styleMap) {
const isEmpty = toValue(value) === "";
if (properties.includes(property) && isEmpty === false) {
currentStyleMap.set(hyphenateProperty(property), value);
currentStyleMap.set(property, value);
}
}
return currentStyleMap;
};

const css = generateStyleMap(mergeStyles(currentStyleMap));
const handleCopyAll = () => {
const styleMap = getAllDeclarations();
const css = generateStyleMap(mergeStyles(styleMap));
navigator.clipboard.writeText(css);
};

const handleCopy = () => {
const property = lastClickedProperty.current;
const property = lastClickedProperty.current as CssProperty;

if (property === undefined) {
return;
}
const value = styleMap.get(property as CssProperty);
const value = styleMap.get(property);

if (value === undefined) {
return;
}
const style = new Map([[property, value]]);
const css = generateStyleMap(style);

const css = generateStyleMap(new Map([[property, value]]));
navigator.clipboard.writeText(css);
};

const handleDelete = () => {
const property = lastClickedProperty.current as CssProperty;
const value = styleMap.get(property);
if (value === undefined) {
return;
}
onDeleteProperty(property);
};

const handleDeleteAllDeclarations = () => {
const styleMap = getAllDeclarations();
onDeleteAllDeclarations(styleMap);
};

return (
<ContextMenu>
<ContextMenuTrigger
Expand Down Expand Up @@ -93,6 +115,12 @@ export const CopyPasteMenu = ({
<ContextMenuItem onSelect={handlePaste}>
Paste declarations
</ContextMenuItem>
<ContextMenuItem destructive onSelect={handleDelete}>
Delete declaration
</ContextMenuItem>
<ContextMenuItem destructive onSelect={handleDeleteAllDeclarations}>
Delete all declarations
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const CssEditor = () => {
styleMap={styleMap}
onDeleteProperty={() => undefined}
onSetProperty={() => () => undefined}
onAddProperties={() => undefined}
onAddDeclarations={() => undefined}
/>
);
};
Expand Down
17 changes: 11 additions & 6 deletions apps/builder/app/builder/shared/css-editor/css-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
type CssProperty,
type CssStyleMap,
} from "@webstudio-is/css-engine";
// @todo all style panel stuff needs to be moved to shared and/or decoupled from style panel
import { CssValueInputContainer } from "../../features/style-panel/shared/css-value-input";
import { styleConfigByName } from "../../features/style-panel/shared/configs";
import {
Expand All @@ -40,7 +41,7 @@ import {
import { PropertyInfo } from "../../features/style-panel/property-label";
import { ColorPopover } from "../../features/style-panel/shared/color-picker";
import { useClientSupports } from "~/shared/client-supports";
import { CopyPasteMenu, copyAttribute } from "./copy-paste-menu";
import { CssEditorContextMenu, copyAttribute } from "./css-editor-context-menu";
import { AddStyleInput } from "./add-style-input";
import { parseStyleInput } from "./parse-style-input";
import type {
Expand Down Expand Up @@ -309,15 +310,17 @@ export type CssEditorApi = { showAddStyleInput: () => void } | undefined;
export const CssEditor = ({
onDeleteProperty,
onSetProperty,
onAddProperties,
onAddDeclarations,
onDeleteAllDeclarations,
styleMap,
apiRef,
showSearch = true,
recentProperties = [],
}: {
onDeleteProperty: DeleteProperty;
onSetProperty: SetProperty;
onAddProperties: (styleMap: CssStyleMap) => void;
onAddDeclarations: (styleMap: CssStyleMap) => void;
onDeleteAllDeclarations: (styleMap: CssStyleMap) => void;
styleMap: CssStyleMap;
apiRef?: RefObject<CssEditorApi>;
showSearch?: boolean;
Expand Down Expand Up @@ -357,7 +360,7 @@ export const CssEditor = ({
if (styleMap.size === 0) {
return new Map();
}
onAddProperties(styleMap);
onAddDeclarations(styleMap);
return styleMap;
};

Expand Down Expand Up @@ -415,8 +418,10 @@ export const CssEditor = ({
/>
</Box>
)}
<CopyPasteMenu
<CssEditorContextMenu
onPaste={handleInsertStyles}
onDeleteProperty={onDeleteProperty}
onDeleteAllDeclarations={onDeleteAllDeclarations}
styleMap={styleMap}
properties={
searchProperties ?? [...recentProperties, ...currentProperties]
Expand Down Expand Up @@ -496,7 +501,7 @@ export const CssEditor = ({
})}
</Flex>
</Flex>
</CopyPasteMenu>
</CssEditorContextMenu>
</>
);
};
1 change: 1 addition & 0 deletions packages/design-system/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"match-sorter": "^8.0.0",
"react-hot-toast": "^2.5.1",
"token-transformer": "^0.0.28",
"type-fest": "^4.32.0",
"use-debounce": "^10.0.4",
"warn-once": "^0.1.1"
},
Expand Down
4 changes: 3 additions & 1 deletion packages/design-system/src/components/context-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
MenuCheckedIcon,
} from "./menu";
export { DropdownMenuArrow } from "./menu";
import type { Simplify } from "type-fest";

export const ContextMenu = ContextMenuPrimitive.Root;

Expand Down Expand Up @@ -57,9 +58,10 @@ export const ContextMenuLabel = styled(ContextMenuPrimitive.Label, labelCss);
const StyledMenuItem = styled(ContextMenuPrimitive.Item, menuItemCss, {
defaultVariants: { withIndicator: true },
});

export const ContextMenuItem = forwardRef<
ElementRef<typeof StyledMenuItem>,
ComponentProps<typeof StyledMenuItem> & { icon?: ReactNode }
Simplify<ComponentProps<typeof StyledMenuItem> & { icon?: ReactNode }>
>(({ icon, children, withIndicator, ...props }, forwardedRef) =>
icon ? (
<StyledMenuItem
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading