mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-08 21:15:18 +09:00
Border style (#1307)
## Description ref #1169 BorderStyle now is toggle group <img width="225" alt="image" src="https://user-images.githubusercontent.com/5077042/228191674-83d8dd64-490a-4a15-8307-c64ae425d74f.png"> ## Steps for reproduction Open borders, set ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [x] made a self-review - [x] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [x] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio-builder/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env.example` and the `builder/env-check.js` if mandatory
This commit is contained in:
1 parent
e87eeaa006
commit
83e2c01dc5
13 files changed
+179
-24
No files matched your search
@@ -1,13 +1,36 @@
|
||||
import type { StyleProperty } from "@webstudio-is/css-data";
|
||||
import { toValue } from "@webstudio-is/css-engine";
|
||||
import {
|
||||
Grid,
|
||||
theme,
|
||||
ToggleGroup,
|
||||
ToggleGroupItem,
|
||||
Tooltip,
|
||||
} from "@webstudio-is/design-system";
|
||||
import {
|
||||
DashBorderIcon,
|
||||
DashedBorderIcon,
|
||||
DottedBorderIcon,
|
||||
SmallXIcon,
|
||||
} from "@webstudio-is/icons";
|
||||
import { toPascalCase } from "../../shared/keyword-utils";
|
||||
import { PropertyName } from "../../shared/property-name";
|
||||
import type { RenderCategoryProps } from "../../style-sections";
|
||||
import { BorderProperty } from "./border-property";
|
||||
import { deleteAllProperties, setAllProperties } from "./border-utils";
|
||||
|
||||
const borderPropertyOptions = {
|
||||
borderTopStyle: {},
|
||||
borderRightStyle: {},
|
||||
borderLeftStyle: {},
|
||||
borderBottomStyle: {},
|
||||
} as const satisfies Partial<{ [property in StyleProperty]: unknown }>;
|
||||
const borderStyleProperties = [
|
||||
"borderTopStyle",
|
||||
"borderRightStyle",
|
||||
"borderLeftStyle",
|
||||
"borderBottomStyle",
|
||||
] as const satisfies readonly StyleProperty[];
|
||||
|
||||
const borderStyleValues = [
|
||||
{ value: "none", Icon: SmallXIcon },
|
||||
{ value: "solid", Icon: DashBorderIcon },
|
||||
{ value: "dashed", Icon: DashedBorderIcon },
|
||||
{ value: "dotted", Icon: DottedBorderIcon },
|
||||
] as const;
|
||||
|
||||
export const BorderStyle = (
|
||||
props: Pick<
|
||||
@@ -15,14 +38,66 @@ export const BorderStyle = (
|
||||
"currentStyle" | "setProperty" | "deleteProperty" | "createBatchUpdate"
|
||||
>
|
||||
) => {
|
||||
/**
|
||||
* We do not use shorthand properties such as borderWidth or borderRadius in our code.
|
||||
* However, in the UI, we can display a single field, and in that case, we can use any property
|
||||
* from the shorthand property set and pass it instead.
|
||||
**/
|
||||
const firstPropertyName = borderStyleProperties[0];
|
||||
|
||||
const deleteBorderProperties = deleteAllProperties(
|
||||
borderStyleProperties,
|
||||
props.createBatchUpdate
|
||||
);
|
||||
|
||||
const setBorderProperties = setAllProperties(
|
||||
borderStyleProperties,
|
||||
props.createBatchUpdate
|
||||
)(firstPropertyName);
|
||||
|
||||
const firstPropertyValue = toValue(
|
||||
props.currentStyle[firstPropertyName]?.value ?? {
|
||||
type: "keyword",
|
||||
value: "none",
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<BorderProperty
|
||||
currentStyle={props.currentStyle}
|
||||
setProperty={props.setProperty}
|
||||
deleteProperty={props.deleteProperty}
|
||||
createBatchUpdate={props.createBatchUpdate}
|
||||
label="Style"
|
||||
borderPropertyOptions={borderPropertyOptions}
|
||||
/>
|
||||
<Grid
|
||||
css={{
|
||||
gridTemplateColumns: `1fr ${theme.spacing[20]} ${theme.spacing[12]}`,
|
||||
}}
|
||||
gap={2}
|
||||
>
|
||||
<PropertyName
|
||||
style={props.currentStyle}
|
||||
property={borderStyleProperties}
|
||||
label={"Style"}
|
||||
onReset={() => deleteBorderProperties(firstPropertyName)}
|
||||
/>
|
||||
|
||||
<ToggleGroup
|
||||
css={{
|
||||
gridColumn: `span 2`,
|
||||
justifySelf: "end",
|
||||
}}
|
||||
type="single"
|
||||
value={firstPropertyValue}
|
||||
onValueChange={(value) => {
|
||||
setBorderProperties({
|
||||
type: "keyword",
|
||||
value,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{borderStyleValues.map(({ value, Icon }) => (
|
||||
<ToggleGroupItem key={value} value={value}>
|
||||
<Tooltip content={toPascalCase(value)}>
|
||||
<Icon />
|
||||
</Tooltip>
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -20,7 +20,6 @@ const styleInfoInitial: StyleInfo = {
|
||||
const Panel = styled("div", {
|
||||
width: theme.spacing[30],
|
||||
boxSizing: "border-box",
|
||||
padding: theme.spacing[9],
|
||||
});
|
||||
|
||||
export const Borders = () => {
|
||||
|
||||
@@ -42,6 +42,13 @@ export const BordersSection = (props: RenderCategoryProps) => {
|
||||
return (
|
||||
<CollapsibleSection label={props.label} isOpen={props.isOpen}>
|
||||
<Flex direction="column" gap={2}>
|
||||
<BorderStyle
|
||||
createBatchUpdate={createBatchUpdate}
|
||||
currentStyle={currentStyle}
|
||||
setProperty={setProperty}
|
||||
deleteProperty={deleteProperty}
|
||||
/>
|
||||
|
||||
<Grid
|
||||
css={{
|
||||
// Our aim is to maintain consistent styling throughout the property and align
|
||||
@@ -73,13 +80,6 @@ export const BordersSection = (props: RenderCategoryProps) => {
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
<BorderStyle
|
||||
createBatchUpdate={createBatchUpdate}
|
||||
currentStyle={currentStyle}
|
||||
setProperty={setProperty}
|
||||
deleteProperty={deleteProperty}
|
||||
/>
|
||||
|
||||
<BorderWidth
|
||||
createBatchUpdate={createBatchUpdate}
|
||||
currentStyle={currentStyle}
|
||||
|
||||
@@ -122,7 +122,7 @@ const PropertyPopoverContent = ({
|
||||
|
||||
type PropertyNameProps = {
|
||||
style: StyleInfo;
|
||||
property: StyleProperty | StyleProperty[];
|
||||
property: StyleProperty | readonly StyleProperty[];
|
||||
label: string | ReactElement;
|
||||
onReset: React.MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 8C3 7.44772 3.44772 7 4 7H12C12.5523 7 13 7.44772 13 8C13 8.55228 12.5523 9 12 9H4C3.44772 9 3 8.55228 3 8Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 255 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.00037 7.33C2.00037 7.14775 2.14811 7 2.33037 7H4.33669C4.51894 7 4.66669 7.14775 4.66669 7.33V8.67C4.66669 8.85225 4.51894 9 4.33669 9H2.33037C2.14811 9 2.00037 8.85225 2.00037 8.67V7.33Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66682 7.33C6.66682 7.14775 6.81456 7 6.99682 7H9.00314C9.18539 7 9.33314 7.14775 9.33314 7.33V8.67C9.33314 8.85225 9.18539 9 9.00314 9H6.99682C6.81456 9 6.66682 8.85225 6.66682 8.67V7.33Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3333 7.33C11.3333 7.14775 11.481 7 11.6633 7H13.6696C13.8519 7 13.9996 7.14775 13.9996 7.33V8.67C13.9996 8.85225 13.8519 9 13.6696 9H11.6633C11.481 9 11.3333 8.85225 11.3333 8.67V7.33Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 818 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.51999 7.99998C1.51999 7.36485 2.03486 6.84998 2.66999 6.84998H2.67999C3.31512 6.84998 3.82999 7.36485 3.82999 7.99998C3.82999 8.6351 3.31512 9.14998 2.67999 9.14998H2.66999C2.03486 9.14998 1.51999 8.6351 1.51999 7.99998Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.18 7.99998C12.18 7.36485 12.6949 6.84998 13.33 6.84998H13.34C13.9751 6.84998 14.49 7.36485 14.49 7.99998C14.49 8.6351 13.9751 9.14998 13.34 9.14998H13.33C12.6949 9.14998 12.18 8.6351 12.18 7.99998Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.07334 7.99998C5.07334 7.36485 5.58821 6.84998 6.22334 6.84998H6.23334C6.86846 6.84998 7.38334 7.36485 7.38334 7.99998C7.38334 8.6351 6.86846 9.14998 6.23334 9.14998H6.22334C5.58821 9.14998 5.07334 8.6351 5.07334 7.99998Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.62668 7.99998C8.62668 7.36485 9.14156 6.84998 9.77668 6.84998H9.78668C10.4218 6.84998 10.9367 7.36485 10.9367 7.99998C10.9367 8.6351 10.4218 9.14998 9.78668 9.14998H9.77668C9.14156 9.14998 8.62668 8.6351 8.62668 7.99998Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.495 4.50501C11.7683 4.77838 11.7683 5.2216 11.495 5.49496L8.98994 8L11.495 10.505C11.7683 10.7784 11.7683 11.2216 11.495 11.495C11.2216 11.7683 10.7784 11.7683 10.505 11.495L7.99999 8.98995L5.49498 11.495C5.22161 11.7683 4.7784 11.7683 4.50503 11.495C4.23166 11.2216 4.23166 10.7784 4.50503 10.505L7.01004 8L4.50503 5.49501C4.23166 5.22164 4.23166 4.77843 4.50503 4.50506C4.77839 4.23169 5.22161 4.23169 5.49498 4.50506L7.99999 7.01005L10.505 4.50501C10.7784 4.23165 11.2216 4.23165 11.495 4.50501Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 646 B |
+15
@@ -0,0 +1,15 @@
|
||||
// Generated from icons/dash-border.svg
|
||||
|
||||
import { forwardRef } from "react";
|
||||
import type { IconProps } from "../types";
|
||||
|
||||
// prettier-ignore
|
||||
export const DashBorderIcon = forwardRef<SVGSVGElement, IconProps>(
|
||||
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path fillRule="evenodd" d="M3 8a1 1 0 0 1 1-1h8a1 1 0 1 1 0 2H4a1 1 0 0 1-1-1Z" clipRule="evenodd" /></svg>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
DashBorderIcon.displayName = "DashBorderIcon";
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Generated from icons/dashed-border.svg
|
||||
|
||||
import { forwardRef } from "react";
|
||||
import type { IconProps } from "../types";
|
||||
|
||||
// prettier-ignore
|
||||
export const DashedBorderIcon = forwardRef<SVGSVGElement, IconProps>(
|
||||
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path fillRule="evenodd" d="M2 7.33A.33.33 0 0 1 2.33 7h2.007a.33.33 0 0 1 .33.33v1.34a.33.33 0 0 1-.33.33H2.33A.33.33 0 0 1 2 8.67V7.33ZM6.667 7.33a.33.33 0 0 1 .33-.33h2.006a.33.33 0 0 1 .33.33v1.34a.33.33 0 0 1-.33.33H6.997a.33.33 0 0 1-.33-.33V7.33ZM11.333 7.33a.33.33 0 0 1 .33-.33h2.007a.33.33 0 0 1 .33.33v1.34a.33.33 0 0 1-.33.33h-2.007a.33.33 0 0 1-.33-.33V7.33Z" clipRule="evenodd" /></svg>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
DashedBorderIcon.displayName = "DashedBorderIcon";
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Generated from icons/dotted-border.svg
|
||||
|
||||
import { forwardRef } from "react";
|
||||
import type { IconProps } from "../types";
|
||||
|
||||
// prettier-ignore
|
||||
export const DottedBorderIcon = forwardRef<SVGSVGElement, IconProps>(
|
||||
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path fillRule="evenodd" d="M1.52 8c0-.635.515-1.15 1.15-1.15h.01a1.15 1.15 0 1 1 0 2.3h-.01A1.15 1.15 0 0 1 1.52 8ZM12.18 8c0-.635.515-1.15 1.15-1.15h.01a1.15 1.15 0 0 1 0 2.3h-.01A1.15 1.15 0 0 1 12.18 8ZM5.073 8c0-.635.515-1.15 1.15-1.15h.01a1.15 1.15 0 0 1 0 2.3h-.01A1.15 1.15 0 0 1 5.073 8ZM8.627 8c0-.635.515-1.15 1.15-1.15h.01a1.15 1.15 0 0 1 0 2.3h-.01A1.15 1.15 0 0 1 8.627 8Z" clipRule="evenodd" /></svg>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
DottedBorderIcon.displayName = "DottedBorderIcon";
|
||||
+4
@@ -27,9 +27,12 @@ export * from "./chevron-filled-right";
|
||||
export * from "./chevron-filled-up";
|
||||
export * from "./code";
|
||||
export * from "./commit";
|
||||
export * from "./dash-border";
|
||||
export * from "./dash";
|
||||
export * from "./dashed-border";
|
||||
export * from "./div";
|
||||
export * from "./dot";
|
||||
export * from "./dotted-border";
|
||||
export * from "./drag-handle";
|
||||
export * from "./eyecon-closed";
|
||||
export * from "./eyecon-open";
|
||||
@@ -51,6 +54,7 @@ export * from "./repeat-grid";
|
||||
export * from "./repeat-row";
|
||||
export * from "./scroll";
|
||||
export * from "./slot-component";
|
||||
export * from "./small-x";
|
||||
export * from "./subscript";
|
||||
export * from "./subtract";
|
||||
export * from "./superscript";
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Generated from icons/small-x.svg
|
||||
|
||||
import { forwardRef } from "react";
|
||||
import type { IconProps } from "../types";
|
||||
|
||||
// prettier-ignore
|
||||
export const SmallXIcon = forwardRef<SVGSVGElement, IconProps>(
|
||||
({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width={size} height={size} fill={color} {...props} ref={forwardedRef}><path fillRule="evenodd" d="M11.495 4.505a.7.7 0 0 1 0 .99L8.99 8l2.505 2.505a.7.7 0 0 1-.99.99L8 8.99l-2.505 2.505a.7.7 0 0 1-.99-.99L7.01 8 4.505 5.495a.7.7 0 1 1 .99-.99L8 7.01l2.505-2.505a.7.7 0 0 1 .99 0Z" clipRule="evenodd" /></svg>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
SmallXIcon.displayName = "SmallXIcon";
|
||||
Reference in new issue
Block a user