chore: widgets alignment (#31923)

## Description
Plenty changes related to widgets alignment 
1. Paragraph, button, inputs, single checkbox and switch widgets are
aligned along the baseline of the text content
2. The icons in the buttons and the single checkbox and switch are now
positioned absolutely so as not to affect the height of the components.
The height of the components now depends on text content.
3. All unnecessary paddings and borders in the layout have been removed:
- Canvas padding: `--outer-spacing-4`
- Gap between sections and zones: `--outer-spacing-4`
- Zone padding: `--outer-spacing-3`
- Gap between widgets: `--outer-spacing-3`
4. In widget selection styles replace `border` with `outline`, since the
`outline` one does not take space.
5. Add Changes to the flex-basis calculation method. Now the gap between
the zones is taken into account there.
6. Add a lot of small fixes related to the changes described above.


https://github.com/appsmithorg/appsmith/assets/11555074/b7c220eb-3e27-4039-9c15-6281bafe8008

Fixes #29364

## Automation

/ok-to-test tags="@tag.Anvil"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!IMPORTANT]  
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/8375537665>
> Commit: `f85b63c0a49f30b9762379c2f8c3bd38c7a8355f`
> Cypress dashboard url: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=8375537665&attempt=2"
target="_blank">Click here!</a>
> All cypress tests have passed 🎉🎉🎉

<!-- end of auto-generated comment: Cypress test results  -->




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features**
	- Improved handling of CSS variable values in Flex components.
- Enhanced styling and layout configurations for a better user
experience.
- **Bug Fixes**
- Fixed label positioning and styling issues in Checkbox and Switch
components.
	- Adjusted padding, margin, and sizing for consistency and alignment.
- **Refactor**
- Enhanced flex layout handling and space distribution logic for
improved layout flexibility.
- **Style**
	- Updated component styles for refined user interface aesthetics.
- **Chores**
- Updated feature flags and configurations for widgets and components to
enable new functionalities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Valera Melnikov 2024-03-22 16:53:29 +03:00 committed by GitHub
parent 9b31527c6d
commit ba6262ffe6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 336 additions and 201 deletions

View File

@ -97,7 +97,7 @@ describe(
cy.get(".anvil-widget-wrapper").each(($widget) => {
cy.wrap($widget).should(
"have.css",
"border-color",
"outline-color",
"rgba(0, 0, 0, 0)",
);
});

View File

@ -50,7 +50,7 @@ const _Checkbox = (props: CheckboxProps, ref: CheckboxRef) => {
// it should be safe in this case since the checkbox is not expected to be added or removed from the group.
const context = useContext(CheckboxGroupContext) as CheckboxGroupContextType;
const isDisabled = isDisabledProp || context?.isDisabled;
const labelPosition = labelPositionProp ?? context?.optionsLabelPosition;
const labelPosition = context?.optionsLabelPosition ?? labelPositionProp;
const { hoverProps, isHovered } = useHover({ isDisabled });
const { inputProps } = Boolean(context?.state)
? // eslint-disable-next-line react-hooks/rules-of-hooks

View File

@ -43,7 +43,7 @@ const _Switch = (props: SwitchProps, ref: SwitchRef) => {
// it should be safe in this case since the switch is not expected to be added or removed from the group.
const context = useContext(CheckboxGroupContext) as CheckboxGroupContextType;
const isDisabled = isDisabledProp || context?.isDisabled;
const labelPosition = labelPositionProp ?? context?.optionsLabelPosition;
const labelPosition = context?.optionsLabelPosition ?? labelPositionProp;
const { hoverProps, isHovered } = useHover({ isDisabled });
const { inputProps } = Boolean(context?.state)

View File

@ -36,6 +36,9 @@ const getTypographyCss = (typography: Typography) => {
margin-top: ${after.marginTop};
}
}
--${currentKey}-line-height: ${lineHeight};
--${currentKey}-margin-start: ${after.marginTop};
--${currentKey}-margin-end: ${before.marginBottom};
`
);
}, "")}

View File

@ -28,7 +28,7 @@ const _ActionGroup = <T extends object>(
} = props;
const domRef = useDOMRef(ref);
const state = useListState({ ...props, suppressTextValueWarning: true });
const { actionGroupProps, isMeasuring, visibleItems } = useActionGroup(
const { actionGroupProps, visibleItems } = useActionGroup(
props,
state,
domRef,
@ -38,63 +38,52 @@ const _ActionGroup = <T extends object>(
const menuChildren = children.slice(visibleItems);
children = children.slice(0, visibleItems);
const style = {
flexBasis: isMeasuring ? "100%" : undefined,
display: "flex",
};
return (
<FocusScope>
<div
style={{
...style,
}}
className={styles.actionGroup}
data-density={Boolean(density) ? density : undefined}
data-orientation={orientation}
data-overflow={overflowMode}
ref={domRef}
{...actionGroupProps}
{...others}
>
<div
className={styles.actionGroup}
data-density={Boolean(density) ? density : undefined}
data-orientation={orientation}
data-overflow={overflowMode}
ref={domRef}
{...actionGroupProps}
{...others}
>
{children.map((item) => {
if (Boolean(item.props.isSeparator)) {
return <div data-separator="" key={item.key} />;
}
{children.map((item) => {
if (Boolean(item.props.isSeparator)) {
return <div data-separator="" key={item.key} />;
}
return (
<ActionGroupItem
color={color}
icon={item.props.icon}
iconPosition={item.props.iconPosition}
isDisabled={
Boolean(state.disabledKeys.has(item.key)) || isDisabled
}
isLoading={item.props.isLoading}
item={item}
key={item.key}
onPress={() => onAction?.(item.key)}
size={Boolean(size) ? size : undefined}
state={state}
variant={variant}
/>
);
})}
{menuChildren?.length > 0 && (
<Menu onAction={onAction}>
<IconButton color={color} icon="dots" variant={variant} />
<MenuList>
{menuChildren.map((item) => (
<Item isSeparator={item.props.isSeparator} key={item.key}>
{item.rendered}
</Item>
))}
</MenuList>
</Menu>
)}
</div>
return (
<ActionGroupItem
color={color}
icon={item.props.icon}
iconPosition={item.props.iconPosition}
isDisabled={
Boolean(state.disabledKeys.has(item.key)) || isDisabled
}
isLoading={item.props.isLoading}
item={item}
key={item.key}
onPress={() => onAction?.(item.key)}
size={Boolean(size) ? size : undefined}
state={state}
variant={variant}
/>
);
})}
{menuChildren?.length > 0 && (
<Menu onAction={onAction}>
<IconButton color={color} icon="dots" variant={variant} />
<MenuList>
{menuChildren.map((item) => (
<Item isSeparator={item.props.isSeparator} key={item.key}>
{item.rendered}
</Item>
))}
</MenuList>
</Menu>
)}
</div>
</FocusScope>
);

View File

@ -6,20 +6,28 @@
& :is([data-separator]) {
inline-size: var(--sizing-5);
block-size: var(--sizing-5);
}
[data-button]:not(:last-of-type) {
min-inline-size: fit-content;
/*
We use !important here to be sure that button width and the logic of useActionGroup hook will not be changed from the outside
*/
min-inline-size: fit-content !important;
}
&:has([data-icon-button]) [data-button]:nth-last-child(2) {
min-inline-size: var(--sizing-14);
min-inline-size: var(--sizing-14) !important;
}
&[data-orientation="vertical"] {
flex-direction: column;
}
&[data-orientation="vertical"] :is([data-button]) {
max-inline-size: none;
}
&[data-overflow="collapse"] {
flex-wrap: nowrap;
}
@ -51,6 +59,10 @@
* Horizontal orientation
*-----------------------------------------------------------------------------
*/
&[data-orientation="horizontal"] {
width: 100%;
}
&[data-orientation="horizontal"] [data-button]:not(:last-of-type) {
border-right-width: var(--border-width-1);
}

View File

@ -1,7 +1,5 @@
import clsx from "clsx";
import React, { forwardRef } from "react";
import { useVisuallyHidden } from "@react-aria/visually-hidden";
import { getTypographyClassName } from "@design-system/theming";
import { Button as HeadlessButton } from "@design-system/headless";
import type { ButtonRef as HeadlessButtonRef } from "@design-system/headless";
import type { SIZES } from "../../../shared";
@ -37,10 +35,20 @@ const _Button = (props: ButtonProps, ref: HeadlessButtonRef) => {
<span aria-hidden={isLoading ? true : undefined} data-content="">
{icon && <Icon name={icon} size={size as keyof typeof SIZES} />}
{Boolean(children) && (
<Text fontWeight={600} lineClamp={1} textAlign="center">
<Text
data-text=""
fontWeight={600}
lineClamp={1}
textAlign="center"
>
{children}
</Text>
)}
{/*
To align buttons in the case when we don't have text content, we create an empty block with the appropriate size.
See the styles for data-empty-text attribute.
*/}
{!Boolean(children) && <Text data-empty-text="">&#8203;</Text>}
</span>
{isLoading && (
@ -59,7 +67,7 @@ const _Button = (props: ButtonProps, ref: HeadlessButtonRef) => {
aria-disabled={
visuallyDisabled || isLoading || isDisabled ? true : undefined
}
className={clsx(styles.button, getTypographyClassName("body"))}
className={styles.button}
data-button=""
data-color={color}
data-icon-position={iconPosition === "start" ? "start" : "end"}

View File

@ -4,16 +4,11 @@
display: flex;
justify-content: center;
align-items: center;
outline: 0;
cursor: pointer;
user-select: none;
position: relative;
border: none;
font-family: inherit;
border-style: solid;
border-width: var(--border-width-1);
padding-inline: var(--inner-spacing-3);
padding-block: var(--inner-spacing-2);
min-inline-size: var(--sizing-14);
max-inline-size: var(--sizing-70);
border-radius: var(--border-radius-elevation-3);
@ -21,7 +16,6 @@
&[data-variant="filled"][data-color="$(color)"] {
background-color: var(--color-bg-$(color));
color: var(--color-fg-on-$(color));
border-color: transparent;
&[data-hovered]:not([aria-disabled]) {
background-color: var(--color-bg-$(color)-hover);
@ -35,7 +29,9 @@
&[data-variant="outlined"][data-color="$(color)"] {
background-color: transparent;
color: var(--color-fg-$(color));
border-color: var(--color-bd-$(color));
/** We use !important here to override the disabled outline styles in the main app. */
outline: var(--border-width-1) solid var(--color-bd-$(color)) !important;
outline-offset: calc(-1 * var(--border-width-1)) !important;
&[data-hovered]:not([aria-disabled]) {
background-color: var(--color-bg-$(color)-subtle-hover);
@ -49,7 +45,6 @@
&[data-variant="ghost"][data-color="$(color)"] {
background: transparent;
color: var(--color-fg-$(color));
border-color: transparent;
&[data-hovered]:not([aria-disabled]) {
background: var(--color-bg-$(color)-subtle-hover);
@ -61,45 +56,29 @@
}
}
/* Note: adding important here as ADS is overriding the color of blueprint icon globally */
/* TODO(pawan): Remove this once ADS team removes the global override */
&[data-button] [data-icon],
&[data-button] [icon] {
color: inherit !important;
}
/**
* ----------------------------------------------------------------------------
* CONTENT
*-----------------------------------------------------------------------------
*/
& [data-content] {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
&[data-icon-position="start"] {
[data-content] *:not([data-hidden]) + *:not([data-hidden]) {
margin-inline-start: var(--inner-spacing-1);
}
}
&[data-icon-position="end"] {
[data-content] *:not([data-hidden]) + *:not([data-hidden]) {
margin-inline-end: var(--inner-spacing-1);
}
}
&[data-icon-position="end"] [data-content] {
flex-direction: row-reverse;
}
/** Note: adding direct selector ">" here because blueprint also has data-icon attribute on their icons */
& [data-content] > [data-icon] {
display: flex;
justify-content: center;
align-items: center;
& [data-empty-text] {
block-size: var(--body-line-height);
margin-block-start: var(--body-margin-start);
margin-block-end: var(--body-margin-end);
inline-size: var(--body-line-height);
margin-inline-start: var(--body-margin-start);
margin-inline-end: var(--body-margin-end);
}
/**
@ -156,10 +135,22 @@
* ICON BUTTON
*-----------------------------------------------------------------------------
*/
&[data-icon-button] {
text-align: center;
padding: 0;
aspect-ratio: 1;
& [data-icon] {
/**
Icons are positioned absolutely because we need to align the elements along the baseline
but icons takes more space than the text content.
*/
position: absolute;
}
&[data-icon-position="start"]:has([data-text]):not(:has([data-loader]))
[data-icon] {
left: 0;
}
&[data-icon-position="end"]:has([data-text]):not(:has([data-loader]))
[data-icon] {
right: 0;
}
/**
@ -169,13 +160,42 @@
*/
&[data-size="small"] {
min-inline-size: var(--sizing-7);
block-size: var(--sizing-7);
padding-inline: var(--inner-spacing-2);
padding-block: var(--inner-spacing-2);
}
&[data-size="small"]:has([data-icon]):has([data-text]) {
min-inline-size: var(--sizing-12);
}
&[data-icon-position="end"][data-size="small"]
[data-content]:has([data-icon]):has([data-text]) {
padding-inline-end: calc(var(--icon-size-1) + var(--inner-spacing-1));
}
&[data-icon-position="start"][data-size="small"]
[data-content]:has([data-icon]):has([data-text]) {
padding-inline-start: calc(var(--icon-size-1) + var(--inner-spacing-1));
}
&[data-size="medium"] {
min-inline-size: var(--sizing-9);
block-size: var(--sizing-9);
padding-inline: var(--inner-spacing-3);
padding-block: var(--inner-spacing-3);
}
&[data-size="medium"]:has([data-icon]):has([data-text]) {
min-inline-size: var(--sizing-22);
}
&[data-icon-position="end"][data-size="medium"]
[data-content]:has([data-icon]):has([data-text]) {
padding-inline-end: calc(var(--icon-size-2) + var(--inner-spacing-1));
}
&[data-icon-position="start"][data-size="medium"]
[data-content]:has([data-icon]):has([data-text]) {
padding-inline-start: calc(var(--icon-size-2) + var(--inner-spacing-1));
}
}

View File

@ -15,11 +15,16 @@
}
&[data-orientation="vertical"] :is([data-button]) {
min-inline-size: 100%;
/*
We use !important here to be sure that button width and the logic of useButtonGroup hook will not be changed from the outside
*/
min-inline-size: 100% !important;
max-inline-size: none;
}
& :is([data-separator]) {
flex-grow: 1;
inline-size: var(--sizing-5);
block-size: var(--sizing-5);
}
}

View File

@ -1,4 +1,6 @@
.checkbox {
position: relative;
[data-icon] {
--checkbox-border-width: var(--border-width-2);
--checkbox-border-color: var(--color-bd-neutral);
@ -7,8 +9,13 @@
--checkbox-box-shadow: 0px 0px 0px var(--checkbox-border-width)
var(--checkbox-border-color) inset;
width: var(--sizing-4);
height: var(--sizing-4);
/**
Checkbox icon are positioned absolutely because we need to align the elements along the baseline
but icon takes more space than the text content.
*/
position: absolute;
width: var(--sizing-5);
height: var(--sizing-5);
box-shadow: var(--checkbox-box-shadow);
border-radius: clamp(0px, var(--border-radius-elevation-3), 25%);
color: transparent;
@ -23,6 +30,28 @@
--checkbox-border-color: var(--color-bd-neutral-hover);
}
/**
* ----------------------------------------------------------------------------
* LABEL POSITION
*-----------------------------------------------------------------------------
*/
&[data-label-position="end"] {
padding-inline-start: calc(var(--sizing-5) + var(--inner-spacing-2));
justify-content: flex-end;
}
&[data-label-position="end"] [data-icon] {
left: 0;
}
&[data-label-position="start"] {
padding-inline-end: calc(var(--sizing-5) + var(--inner-spacing-2));
}
&[data-label-position="start"] [data-icon] {
right: 0;
}
/**
* ----------------------------------------------------------------------------
* CHECKED AND INDETERMINATE - BUT NOT DISABLED

View File

@ -96,10 +96,10 @@ Checkbox is a component that allows the user to select one or more options from
parameters={{
width: 250,
}}
name="Label Position - Left"
name="Label Position - Start"
args={{
labelPosition: "left",
children: "Label Position - Left",
labelPosition: "start",
children: "Label Position - Start",
}}
>
{Template.bind({})}
@ -108,10 +108,10 @@ Checkbox is a component that allows the user to select one or more options from
parameters={{
width: 250,
}}
name="Label Position - Right"
name="Label Position - End"
args={{
labelPosition: "right",
children: "Label Position - Right",
labelPosition: "end",
children: "Label Position - End",
}}
>
{Template.bind({})}

View File

@ -138,6 +138,9 @@ export const flexWrapValue = (value: FlexCssProps["wrap"]) => {
return value;
};
const sizingRegexp = new RegExp("^sizing");
const spacingRegexp = new RegExp("^spacing");
const cssVarValue = (
value: CssVarValues,
extraProps?: Pick<FlexProps, "isInner">,
@ -146,15 +149,15 @@ const cssVarValue = (
if (value == null) return;
if ((value as string).includes("sizing")) {
if (sizingRegexp.test(value as string)) {
return `var(--${value})`;
}
if ((value as string).includes("spacing") && !isInner) {
if (spacingRegexp.test(value as string) && !isInner) {
return `var(--outer-${value})`;
}
if ((value as string).includes("spacing") && isInner) {
if (spacingRegexp.test(value as string) && isInner) {
return `var(--inner-${value})`;
}

View File

@ -8,33 +8,41 @@
}
.overlay .content {
background: var(--color-bg);
background: var(--color-bg-elevation-3);
border-radius: var(--border-radius-elevation-3);
box-shadow: var(--box-shadow-1);
outline: none;
display: flex;
flex-direction: column;
gap: var(--inner-spacing-3);
padding-inline: var(--inner-spacing-4);
padding-block: var(--inner-spacing-3);
padding-inline: var(--outer-spacing-4);
padding-block: var(--outer-spacing-4);
gap: var(--outer-spacing-2);
flex: 1;
}
.overlay .content .header {
padding-inline: 0 calc(var(--inner-spacing-2));
margin-inline: 0 calc(-1 * var(--inner-spacing-4));
/* Needed to align the close button */
padding-inline: 0 calc(var(--outer-spacing-2));
margin-inline: 0 calc(-1 * var(--outer-spacing-4));
}
.overlay .content .body {
overflow: auto;
padding-inline: var(--inner-spacing-4);
margin-inline: calc(var(--inner-spacing-4) * -1);
padding-block: var(--outer-spacing-2);
padding-inline: var(--outer-spacing-4);
margin-inline: calc(var(--outer-spacing-4) * -1);
flex: 1;
/* Needed to remove the height from the child element so that Scrollbar is displayed correctly */
& > *:last-child {
height: auto;
}
}
.overlay [role="dialog"] {
display: flex;
max-inline-size: calc(100% - var(--inner-spacing-8));
max-block-size: calc(100% - var(--inner-spacing-8));
max-inline-size: calc(100% - var(--outer-spacing-8));
max-block-size: calc(100% - var(--outer-spacing-8));
}
.overlay [role="dialog"][data-size="small"] {
@ -48,6 +56,7 @@
.overlay [role="dialog"][data-size="large"],
.overlay [role="dialog"][data-size="large"] .content {
inline-size: 100%;
min-block-size: 50%;
}
.overlay,

View File

@ -1,4 +1,7 @@
.radio {
position: relative;
padding-inline-start: calc(var(--sizing-5) + var(--inner-spacing-2));
[data-icon] {
--radio-border-width: var(--border-width-2);
--radio-border-color: var(--color-bd-neutral);
@ -7,8 +10,14 @@
--radio-box-shadow: 0px 0px 0px var(--radio-border-width)
var(--radio-border-color) inset;
width: var(--sizing-4);
height: var(--sizing-4);
/**
Checkbox icon are positioned absolutely because we need to align the elements along the baseline
but icon takes more space than the text content.
*/
position: absolute;
left: 0;
width: var(--sizing-5);
height: var(--sizing-5);
box-shadow: var(--radio-box-shadow);
border-radius: 100%;
color: transparent;

View File

@ -14,7 +14,7 @@ import { inlineLabelStyles } from "../../../styles";
export type SwitchProps = Omit<HeadlessSwitchProps, "icon" | "isIndeterminate">;
const _Switch = (props: SwitchProps, ref: HeadlessSwitchRef) => {
const { children, labelPosition, ...rest } = props;
const { children, labelPosition = "start", ...rest } = props;
return (
<HeadlessSwitch
@ -23,7 +23,9 @@ const _Switch = (props: SwitchProps, ref: HeadlessSwitchRef) => {
ref={ref}
{...rest}
>
{Boolean(children) && <Text>{children}</Text>}
{Boolean(children) && (
<Text className={switchStyles.text}>{children}</Text>
)}
</HeadlessSwitch>
);
};

View File

@ -1,15 +1,18 @@
.switch {
&[data-label-position="start"] {
width: 100%;
}
position: relative;
width: auto;
[data-icon] {
--gutter: 2px;
--knob-size: var(--sizing-3);
--knob-size: var(--sizing-4);
position: relative;
/**
Checkbox icon are positioned absolutely because we need to align the elements along the baseline
but icon takes more space than the text content.
*/
position: absolute;
width: var(--sizing-8);
height: var(--sizing-4);
height: var(--sizing-5);
background-color: var(--color-bd-neutral);
border-radius: var(--knob-size);
color: var(--color-bg);
@ -35,6 +38,36 @@
background-color: var(--color-bd-neutral-hover);
}
/**
* ----------------------------------------------------------------------------
* LABEL POSITION
*-----------------------------------------------------------------------------
*/
&[data-label-position="end"] {
padding-inline-start: calc(var(--sizing-8) + var(--inner-spacing-2));
justify-content: flex-end;
}
&[data-label-position="end"] [data-icon] {
left: 0;
}
&[data-label-position="end"] .text {
margin-inline-start: auto;
}
&[data-label-position="start"] {
padding-inline-end: calc(var(--sizing-8) + var(--inner-spacing-2));
}
&[data-label-position="start"] [data-icon] {
right: 0;
}
&[data-label-position="start"] .text {
margin-inline-end: auto;
}
/**
* ----------------------------------------------------------------------------
* CHECKED - BUT NOT DISABLED

View File

@ -69,10 +69,10 @@ Switch is a component that allows the user to select one or more options from a
parameters={{
width: 250,
}}
name="Label Position - Left"
name="Label Position - Start"
args={{
labelPosition: "left",
children: "Label Position - Left",
labelPosition: "start",
children: "Label Position - Start",
}}
>
{Template.bind({})}
@ -82,10 +82,10 @@ Switch is a component that allows the user to select one or more options from a
parameters={{
width: 250,
}}
name="Label Position - Right"
name="Label Position - End"
args={{
labelPosition: "right",
children: "Label Position - Right",
labelPosition: "end",
children: "Label Position - End",
}}
>
{Template.bind({})}

View File

@ -43,7 +43,7 @@ const _TextArea = (props: TextAreaProps, ref: HeadlessTextAreaRef) => {
inputClassName={getTypographyClassName("body")}
isRequired={isRequired}
label={label}
labelClassName={getTypographyClassName("body")}
labelClassName={getTypographyClassName("caption")}
ref={ref}
{...rest}
/>

View File

@ -110,7 +110,7 @@ const _TextInput = (props: TextInputProps, ref: HeadlessTextInputRef) => {
inputClassName={getTypographyClassName("body")}
isRequired={isRequired}
label={label}
labelClassName={getTypographyClassName("body")}
labelClassName={getTypographyClassName("caption")}
ref={ref}
startIcon={renderStartIcon()}
type={showPassword ? "text" : type}

View File

@ -68,7 +68,17 @@
flex-direction: column;
}
&[data-disabled] [data-field-group] [data-label] {
& [data-field-group] [data-icon] {
/** In the group components, the checkbox, radio and switch icons are positioned relatively */
position: relative;
}
& [data-field-group] [data-label] {
padding: 0;
gap: var(--inner-spacing-2);
}
&[data-disabled] [data-field-group] {
cursor: default;
}
@ -77,6 +87,10 @@
flex-direction: row;
}
& [data-field-group] [data-label-position="start"] {
flex-direction: row-reverse;
}
/**
* ----------------------------------------------------------------------------
* FIELD WRAPPER

View File

@ -2,8 +2,6 @@
.inline-label {
display: flex;
align-items: center;
gap: var(--inner-spacing-2);
min-height: var(--sizing-4);
position: relative;
cursor: pointer;
width: fit-content;
@ -15,16 +13,6 @@
-webkit-box-orient: vertical;
}
/**
* ----------------------------------------------------------------------------
* LABEL POSITION
*-----------------------------------------------------------------------------
*/
&[data-label-position="start"] {
flex-direction: row-reverse;
justify-content: space-between;
}
/**
* ----------------------------------------------------------------------------
* DISABLED

View File

@ -7,10 +7,11 @@
gap: var(--inner-spacing-1);
border-radius: var(--border-radius-elevation-3);
background-color: var(--color-bg-neutral-subtle);
padding-inline: var(--inner-spacing-1);
padding-inline: var(--inner-spacing-2);
flex: 1;
max-inline-size: 100%;
isolation: isolate;
padding-block: var(--inner-spacing-3);
}
& [data-field-input] :is(input, textarea) {
@ -22,6 +23,8 @@
text-overflow: ellipsis;
padding: 0;
max-inline-size: 100%;
margin-block-start: var(--body-margin-start);
margin-block-end: var(--body-margin-end);
&:autofill,
&:autofill:hover,
@ -167,7 +170,7 @@
*-----------------------------------------------------------------------------
*/
&:has(input[data-size="small"]) [data-field-input] {
block-size: var(--sizing-7);
padding-block: var(--inner-spacing-2);
& [data-icon] {
inline-size: var(--sizing-5);
@ -176,8 +179,6 @@
}
&:has(input[data-size="medium"]) [data-field-input] {
block-size: var(--sizing-9);
& [data-icon] {
inline-size: var(--sizing-7);
aspect-ratio: 1;

View File

@ -2,6 +2,7 @@
* TODO: (Balaji) Move all the types to different file
*/
import { IconNames } from "@blueprintjs/icons";
import type { SpacingDimension } from "@design-system/widgets";
import type { Responsive, SizingDimension } from "@design-system/widgets";
import type { Theme } from "constants/DefaultTheme";
import type { PropertyPaneConfig } from "constants/PropertyControlConstants";
@ -61,6 +62,8 @@ export interface SizeConfig {
maxWidth?: Responsive<SizingDimension>;
minHeight?: Responsive<SizingDimension>;
minWidth?: Responsive<SizingDimension>;
paddingTop?: Responsive<SpacingDimension>;
paddingBottom?: Responsive<SpacingDimension>;
}
export interface AnvilConfig {

View File

@ -257,14 +257,14 @@ div.bp3-popover-arrow {
/** Conditional vertical margin applied to widgets.
If in a row of widgets (.aligned-widget-row), one of the widgets has a label ([data-field-label-wrapper]), then
all widgets (.anvil-widget-wrapper) in the row other than the widget with the label, will shift down using the
all widgets (.anvil-widget-wrapper) in the row other than the widget with the label, will shift down using the
margin-block-start property. This is to ensure that the widgets are aligned vertically.
The value of the margin-block-start property is calculated based on the spacing tokens used by the labels in input like components
*/
.aligned-widget-row:has(.anvil-widget-wrapper [data-field-label-wrapper])
.anvil-widget-wrapper:not(:has([data-field-label-wrapper])) {
margin-block-start: calc(var(--inner-spacing-2) + var(--sizing-3));
margin-block-start: calc(var(--inner-spacing-3) + var(--sizing-3));
}
/** Asymmetric Padding
@ -273,7 +273,7 @@ div.bp3-popover-arrow {
elevation="1" is used to denote that the section has elevation.
*/
div.anvil-widget-wrapper:has(div[data-elevation="false"][elevation="1"]) {
padding: var(--outer-sizing-0);
padding: var(--outer-spacing-0);
}
/** Asymmetric Padding
@ -287,5 +287,5 @@ div.anvil-widget-wrapper:has(div[data-elevation="false"][elevation="1"]) {
*/
div.anvil-widget-wrapper:not(:has(div[data-elevation="true"][elevation="2"]))
div.anvil-widget-wrapper:has(div[data-elevation]) {
padding-block: var(--outer-sizing-0);
padding-block: var(--outer-spacing-0);
}

View File

@ -17,8 +17,6 @@ import { convertFlexGrowToFlexBasis } from "../sectionSpaceDistributor/utils/spa
const anvilWidgetStyleProps: CSSProperties = {
position: "relative",
// overflow is set to make sure widgets internal components/divs don't overflow this boundary causing scrolls
overflow: "hidden",
zIndex: Layers.positionedWidget,
// add transition ease-in animation when there is a flexgrow value change
transition: "flex-grow 0.1s ease-in",
@ -82,16 +80,24 @@ export const AnvilFlexComponent = forwardRef(
flexGrow: isFillWidget ? 1 : 0,
flexShrink: isFillWidget ? 1 : 0,
flexBasis,
padding: "spacing-1",
alignItems: "center",
width: "max-content",
};
if (widgetSize) {
const { maxHeight, maxWidth, minHeight, minWidth } = widgetSize;
const {
maxHeight,
maxWidth,
minHeight,
minWidth,
paddingBottom,
paddingTop,
} = widgetSize;
data.maxHeight = maxHeight;
data.maxWidth = maxWidth;
data.minHeight = minHeight;
data.minWidth = minWidth;
data.paddingTop = paddingTop;
data.paddingBottom = paddingBottom;
}
return data;
}, [widgetConfigProps, widgetSize, flexGrow]);
@ -99,6 +105,7 @@ export const AnvilFlexComponent = forwardRef(
// Render the Anvil Flex Component using the Flex component from WDS
return (
<Flex
isInner
{...flexProps}
className={_className}
id={getAnvilWidgetDOMId(widgetId)}

View File

@ -100,7 +100,8 @@ export function useAddBordersToDetachedWidgets(widgetId: string) {
);
if (element) {
element.style.border = borderStyled.border ?? "none";
element.style.outlineOffset = borderStyled.outlineOffset ?? "unset";
element.style.outline = borderStyled.outline ?? "none";
}
}

View File

@ -38,6 +38,7 @@ export function useWidgetBorderStyles(widgetId: string) {
const canShowBorder = !shouldHideBorder && (isFocused || isSelected);
return {
border: `2px solid ${canShowBorder ? borderColor : "transparent"}`,
outline: `2px solid ${canShowBorder ? borderColor : "transparent"}`,
outlineOffset: "4px",
};
}

View File

@ -55,7 +55,7 @@ export interface FlexLayoutProps
overflowX?: OverflowValues;
overflowY?: OverflowValues;
position?: PositionValues;
rowGap?: Responsive<SpacingDimension>;
gap?: Responsive<SpacingDimension>;
padding?: Responsive<SpacingDimension>;
width?: Responsive<SizingDimension>;
className?: string;
@ -73,6 +73,7 @@ export const FlexLayout = React.memo((props: FlexLayoutProps) => {
flexBasis,
flexGrow,
flexShrink,
gap,
height,
isContainer,
isDropTarget,
@ -88,7 +89,6 @@ export const FlexLayout = React.memo((props: FlexLayoutProps) => {
parentDropTarget,
position,
renderMode,
rowGap,
width,
wrap,
} = props;
@ -124,7 +124,7 @@ export const FlexLayout = React.memo((props: FlexLayoutProps) => {
minHeight: minHeight || "unset",
minWidth: minWidth || "unset",
padding: padding || "spacing-0",
rowGap: rowGap || "0px",
gap: gap || "spacing-3",
width: width || "auto",
wrap: wrap || "nowrap",
className: className || "",
@ -144,7 +144,7 @@ export const FlexLayout = React.memo((props: FlexLayoutProps) => {
minHeight,
minWidth,
padding,
rowGap,
gap,
width,
wrap,
]);

View File

@ -25,6 +25,7 @@ class AlignedWidgetRow extends BaseLayoutComponent {
alignSelf: "stretch",
direction: "row",
wrap: "wrap",
gap: "spacing-3",
className: "aligned-widget-row",
};
}

View File

@ -26,6 +26,7 @@ class Section extends WidgetRow {
...super.getFlexLayoutProps(),
alignSelf: "stretch",
direction: "row",
gap: "spacing-4",
};
}

View File

@ -14,7 +14,6 @@ export const modalPreset = (): LayoutProps[] => {
border: "none",
height: "100%",
minHeight: "sizing-16",
padding: "spacing-1",
},
isDropTarget: true,
isPermanent: true,

View File

@ -19,11 +19,10 @@ import {
* This is because when representing on the UI, we show column values which add up to SectionColumns
* Also space distribution algorithm(redistributeSpaceWithDynamicMinWidth) works with the flex-grow values.
*/
export const convertFlexGrowToFlexBasis = (
flexGrow: number,
columns = SectionColumns,
) => {
return `${(flexGrow / columns) * 100}%`;
export const convertFlexGrowToFlexBasis = (flexGrow: number) => {
const columns = SectionColumns / flexGrow;
// We calculate the total gap count and distribute it proportionally between the zones.
return `calc(100% / ${columns} - (${columns} - 1) * var(--outer-spacing-4) / ${columns})`;
};
/**

View File

@ -22,7 +22,8 @@ export function anvilDSLTransformer(dsl: DSLWidget) {
layoutStyle: {
border: "none",
height: "100%",
padding: "spacing-1",
padding: "spacing-4",
gap: "spacing-4",
},
isDropTarget: true,
isPermanent: true,

View File

@ -6,8 +6,9 @@ export const WIDGET_NAME_FONT_SIZE = 14;
export const WIDGET_NAME_LINE_HEIGHT = Math.floor(WIDGET_NAME_FONT_SIZE * 1.2);
export const WIDGET_NAME_VERTICAL_PADDING = 4;
export const WIDGET_NAME_HORIZONTAL_PADDING = 6;
export const WIDGET_OUTLINE_OFFSET = 1;
export const WIDGET_OUTLINE_OFFSET = 7;
export const WIDGET_NAME_ICON_PADDING = 16;
export const WIDGET_NAME_OFFSET = 6; // 4px — outline-offset and 2px outline-width of selection border
export const DEFAULT_WIDGET_NAME_CANVAS_HEIGHT = 600;
export const WIDGET_NAME_CANVAS_PADDING = 20;

View File

@ -12,6 +12,7 @@ import {
WIDGET_NAME_TEXT_COLOR,
WIDGET_NAME_VERTICAL_PADDING,
WIDGET_OUTLINE_OFFSET,
WIDGET_NAME_OFFSET,
} from "./WidgetNameConstants";
/**
@ -109,6 +110,7 @@ export const getWidgetNameComponent = (
width: componentWidth,
x: left,
y: top,
offsetY: WIDGET_NAME_OFFSET,
});
groupEl.add(rectEl);

View File

@ -19,7 +19,6 @@ const StyledContainerComponent = styled.div<
outline: none;
border: none;
position: relative;
border-radius: var(--border-radius-1);
/* If the elevatedBackground is true, then apply the elevation styles */
${(props) => {
if (props.elevatedBackground) {
@ -32,8 +31,9 @@ const StyledContainerComponent = styled.div<
/* Add padding to the container to maintain the visual spacing rhythm */
/* This is based on the hypothesis of asymmetric padding */
padding-block: var(--inner-spacing-1);
padding-inline: var(--inner-spacing-1);
padding-block:
${props.elevation === 1 ? 0 : "var(--outer-spacing-3)"};
padding-inline: ${props.elevation === 1 ? 0 : "var(--outer-spacing-3)"};
`;
}
}}

View File

@ -5,6 +5,6 @@ export const anvilConfig = {
base: "100%",
"280px": "sizing-70",
},
minWidth: "sizing-14",
minWidth: "sizing-9",
},
};

View File

@ -3,6 +3,8 @@ import type { AnvilConfig } from "WidgetProvider/constants";
export const anvilConfig: AnvilConfig = {
isLargeWidget: false,
widgetSize: {
paddingTop: "spacing-3",
paddingBottom: "spacing-3",
minWidth: {
base: "100%",
"180px": "sizing-30",

View File

@ -1,6 +1,6 @@
export const anvilConfig = {
isLargeWidget: false,
widgetSize: {
minWidth: "sizing-10",
minWidth: "sizing-9",
},
};

View File

@ -3,6 +3,8 @@ import type { AnvilConfig } from "WidgetProvider/constants";
export const anvilConfig: AnvilConfig = {
isLargeWidget: false,
widgetSize: {
paddingTop: "spacing-3",
paddingBottom: "spacing-3",
minWidth: {
base: "100%",
"180px": "sizing-30",

View File

@ -12,14 +12,6 @@ export const propertyPaneStyleConfig = [
controlType: "DROP_DOWN",
defaultValue: "body",
options: [
{
label: "Footnote",
value: "footnote",
},
{
label: "Caption",
value: "caption",
},
{
label: "Body",
value: "body",

View File

@ -65,7 +65,6 @@ class WDSParagraphWidget extends BaseWidget<TextWidgetProps, WidgetState> {
getWidgetView() {
return (
<Text
color="neutral"
isBold={this.props?.fontStyle?.includes("bold")}
isItalic={this.props?.fontStyle?.includes("italic")}
lineClamp={this.props.lineClamp ? this.props.lineClamp : undefined}

View File

@ -22,7 +22,6 @@ export const StatBoxComponent = (props: StatBoxComponentProps) => {
direction={iconAlign === "end" ? "row-reverse" : "row"}
gap="spacing-2"
isInner
padding="spacing-3 "
>
{iconName && iconName !== "(none)" && (
<Icon name={iconName} size="large" />

View File

@ -1,6 +1,4 @@
.statbox {
border: 1px solid var(--color-bd);
border-radius: var(--border-radius-elevation-3);
width: 100%;
background: var(--color-bg-elevation-2);
}

View File

@ -3,6 +3,8 @@ import type { AnvilConfig } from "WidgetProvider/constants";
export const anvilConfig: AnvilConfig = {
isLargeWidget: false,
widgetSize: {
paddingTop: "spacing-3",
paddingBottom: "spacing-3",
minWidth: {
base: "100%",
"180px": "sizing-30",

View File

@ -5,7 +5,7 @@ export const defaultsConfig = {
label: "Label",
defaultSwitchState: true,
widgetName: "Switch",
labelPosition: "left",
labelPosition: "start",
version: 1,
isDisabled: false,
animateLoading: true,

View File

@ -21,8 +21,8 @@ export const propertyPaneContentConfig = [
controlType: "ICON_TABS",
fullWidth: true,
options: [
{ label: "Left", value: "left" },
{ label: "Right", value: "right" },
{ label: "Start", value: "start" },
{ label: "End", value: "end" },
],
defaultValue: "left",
isBindProperty: false,

View File

@ -3,7 +3,7 @@ export const anvilConfig = {
widgetSize: {
minWidth: {
base: "100%",
[`280px`]: "sizing-70",
[`280px`]: "sizing-60",
},
},
};