Skip to content

Commit

Permalink
Fix and improve resizing in editor (incl. Add Spacer component) (#2818)
Browse files Browse the repository at this point in the history
  • Loading branch information
apedroferreira authored Feb 23, 2024
1 parent 2b66419 commit 4870ed8
Show file tree
Hide file tree
Showing 34 changed files with 476 additions and 176 deletions.
9 changes: 4 additions & 5 deletions docs/data/toolpad/reference/components/chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ A chart component.

## Properties

| Name | Type | Default | Description |
| :------------------------------------ | :------------------------------------ | :------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <span class="prop-name">data</span> | <span class="prop-type">array</span> | | The data to be displayed. |
| <span class="prop-name">height</span> | <span class="prop-type">number</span> | <span class="prop-default">300</span> | The height of the chart. |
| <span class="prop-name">sx</span> | <span class="prop-type">object</span> | | The [`sx` prop](https://mui.com/system/getting-started/the-sx-prop/) is used for defining custom styles that have access to the theme. All MUI System properties are available via the `sx` prop. In addition, the `sx` prop allows you to specify any other CSS rules you may need. |
| Name | Type | Default | Description |
| :---------------------------------- | :------------------------------------ | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <span class="prop-name">data</span> | <span class="prop-type">array</span> | | The data to be displayed. |
| <span class="prop-name">sx</span> | <span class="prop-type">object</span> | | The [`sx` prop](https://mui.com/system/getting-started/the-sx-prop/) is used for defining custom styles that have access to the theme. All MUI System properties are available via the `sx` prop. In addition, the `sx` prop allows you to specify any other CSS rules you may need. |
1 change: 0 additions & 1 deletion docs/data/toolpad/reference/components/data-grid.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ The datagrid lets users display tabular data in a flexible grid.
| <span class="prop-name">rowIdField</span> | <span class="prop-type">string</span> | | Defines which column contains the [id](https://mui.com/x/react-data-grid/row-definition/#row-identifier) that uniquely identifies each row. |
| <span class="prop-name">selection</span> | <span class="prop-type">object</span> | <span class="prop-default">null</span> | The currently selected row. Or `null` in case no row has been selected. |
| <span class="prop-name">density</span> | <span class="prop-type">string</span> | <span class="prop-default">"compact"</span> | The [density](https://mui.com/x/react-data-grid/accessibility/#density-prop) of the rows. Possible values are `compact`, `standard`, or `comfortable`. |
| <span class="prop-name">height</span> | <span class="prop-type">number</span> | <span class="prop-default">350</span> | The height of the data grid. |
| <span class="prop-name">loading</span> | <span class="prop-type">boolean</span> | | Displays a loading animation indicating the data grid isn't ready to present data yet. |
| <span class="prop-name">hideToolbar</span> | <span class="prop-type">boolean</span> | | Hide the toolbar area that contains the data grid user controls. |
| <span class="prop-name">sx</span> | <span class="prop-type">object</span> | | The [`sx` prop](https://mui.com/system/getting-started/the-sx-prop/) is used for defining custom styles that have access to the theme. All MUI System properties are available via the `sx` prop. In addition, the `sx` prop allows you to specify any other CSS rules you may need. |
1 change: 1 addition & 0 deletions docs/data/toolpad/reference/components/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [PageRow](/toolpad/reference/components/page-row/)
- [Paper](/toolpad/reference/components/paper/)
- [Select](/toolpad/reference/components/select/)
- [Spacer](/toolpad/reference/components/spacer/)
- [Stack](/toolpad/reference/components/stack/)
- [Tabs](/toolpad/reference/components/tabs/)
- [Text](/toolpad/reference/components/text/)
Expand Down
4 changes: 4 additions & 0 deletions docs/data/toolpad/reference/components/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
"title": "Select",
"pathname": "/toolpad/reference/components/select"
},
{
"title": "Spacer",
"pathname": "/toolpad/reference/components/spacer"
},
{
"title": "Stack",
"pathname": "/toolpad/reference/components/stack"
Expand Down
14 changes: 14 additions & 0 deletions docs/data/toolpad/reference/components/spacer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!-- ATTENTION: DO NOT EDIT! This file has been auto-generated using `pnpm docs:build:api`. -->

# Spacer

<p class="description">API docs for the Toolpad Spacer component.</p>

Spacer component.
It allows for creating space between elements.

## Properties

| Name | Type | Default | Description |
| :-------------------------------- | :------------------------------------ | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <span class="prop-name">sx</span> | <span class="prop-type">object</span> | | The [`sx` prop](https://mui.com/system/getting-started/the-sx-prop/) is used for defining custom styles that have access to the theme. All MUI System properties are available via the `sx` prop. In addition, the `sx` prop allows you to specify any other CSS rules you may need. |
9 changes: 9 additions & 0 deletions docs/pages/toolpad/reference/components/spacer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* ATTENTION: DO NOT EDIT! This file has been auto-generated using `pnpm docs:build:api`. */

import * as React from 'react';
import MarkdownDocs from '@mui/monorepo/docs/src/modules/components/MarkdownDocs';
import * as pageProps from '../../../../data/toolpad/reference/components/spacer.md?@mui/markdown';

export default function Page() {
return <MarkdownDocs {...pageProps} />;
}
4 changes: 4 additions & 0 deletions docs/schemas/v1/definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,10 @@
"columnSize": {
"type": "number",
"description": "The width this element takes up, expressed in terms of columns on the page."
},
"height": {
"type": "number",
"description": "The height this element takes up, in pixels."
}
},
"additionalProperties": false,
Expand Down
4 changes: 2 additions & 2 deletions packages/toolpad-app/src/runtime/ToolpadApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -442,10 +442,8 @@ function getQueryConfigBindings({ enabled, refetchInterval }: appDom.QueryNode['
}

function isBindableProp(componentConfig: ComponentConfig<any>, propName: string) {
const isResizableHeightProp = propName === componentConfig.resizableHeightProp;
const argType = componentConfig.argTypes?.[propName];
return (
!isResizableHeightProp &&
argType?.control?.bindable !== false &&
argType?.type !== 'template' &&
argType?.type !== 'event'
Expand Down Expand Up @@ -1195,6 +1193,8 @@ function RenderedNodeContent({ node, childNodeGroups, Component }: RenderedNodeC
display: 'flex',
alignItems: boundLayoutProps.verticalAlign,
justifyContent: boundLayoutProps.horizontalAlign,
height: node.layout?.height ?? componentConfig.defaultLayoutHeight,
minHeight: '100%',
}}
ref={nodeRef}
data-toolpad-node-id={nodeId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as appDom from '@mui/toolpad-core/appDom';

export const PAGE_ROW_COMPONENT_ID = 'PageRow';
export const PAGE_COLUMN_COMPONENT_ID = 'PageColumn';
export const SPACER_COMPONENT_ID = 'Spacer';
export const STACK_COMPONENT_ID = 'Stack';
export const FORM_COMPONENT_ID = 'Form';

Expand Down
14 changes: 11 additions & 3 deletions packages/toolpad-app/src/server/localMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ function expandFromDom<N extends appDom.AppDomNode>(
name: node.name,
layout: undefinedWhenEmpty({
columnSize: node.layout?.columnSize,
height: node.layout?.height,
horizontalAlign: stringOnly(node.layout?.horizontalAlign),
verticalAlign: stringOnly(node.layout?.verticalAlign),
}),
Expand Down Expand Up @@ -715,7 +716,14 @@ function mergePageIntoDom(dom: appDom.AppDom, pageName: string, pageFile: Page):
return dom;
}

function optimizePageElement(element: ElementType): ElementType {
function optimizePageElement(
element: ElementType,
isPageChild = false,
): ElementType | ElementType[] {
if (isPageChild && element.component === PAGE_COLUMN_COMPONENT_ID) {
return (element.children || []).flatMap((child) => optimizePageElement(child, true));
}

const isLayoutElement = (possibleLayoutElement: ElementType): boolean =>
possibleLayoutElement.component === PAGE_ROW_COMPONENT_ID ||
possibleLayoutElement.component === PAGE_COLUMN_COMPONENT_ID;
Expand All @@ -736,7 +744,7 @@ function optimizePageElement(element: ElementType): ElementType {

return {
...element,
children: element.children && element.children.map(optimizePageElement),
children: element.children && element.children.flatMap((child) => optimizePageElement(child)),
};
}

Expand All @@ -745,7 +753,7 @@ function optimizePage(page: Page): Page {
...page,
spec: {
...page.spec,
content: page.spec?.content?.map(optimizePageElement),
content: page.spec?.content?.flatMap((element) => optimizePageElement(element, true)),
},
};
}
Expand Down
1 change: 1 addition & 0 deletions packages/toolpad-app/src/server/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ const baseElementSchema = z.object({
.number()
.optional()
.describe('The width this element takes up, expressed in terms of columns on the page.'),
height: z.number().optional().describe('The height this element takes up, in pixels.'),
})
.optional()
.describe('Layout properties for this element.'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import TagIcon from '@mui/icons-material/Tag';
import PasswordIcon from '@mui/icons-material/Password';
import LinkIcon from '@mui/icons-material/Link';
import TextFormatIcon from '@mui/icons-material/TextFormat';
import SpaceBarIcon from '@mui/icons-material/SpaceBar';
import PieChartIcon from '@mui/icons-material/PieChart';

const iconMap = new Map<string, React.ComponentType<SvgIconProps>>([
Expand Down Expand Up @@ -70,6 +71,7 @@ const iconMap = new Map<string, React.ComponentType<SvgIconProps>>([
['PageRow', TableRowsIcon],
['PageColumn', ViewColumnIcon],
['Metric', TagIcon],
['Spacer', SpaceBarIcon],
]);

type ComponentItemKind = 'future' | 'builtIn' | 'create' | 'custom';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ function shouldRenderControl<P extends object>(
propTypeDef: ArgTypeDefinition<P>,
propName: keyof P,
props: P,
componentConfig: ComponentConfig<P>,
) {
if (propTypeDef.type === 'element' || propTypeDef.type === 'template') {
return (
Expand All @@ -60,10 +59,6 @@ function shouldRenderControl<P extends object>(
return propTypeDef.visible(props);
}

if (componentConfig.resizableHeightProp && propName === componentConfig.resizableHeightProp) {
return false;
}

return true;
}

Expand Down Expand Up @@ -141,7 +136,7 @@ function ComponentPropsEditor<P extends object>({
{category}:
</Typography>
{argTypeEntries.map(([propName, propTypeDef]) =>
propTypeDef && shouldRenderControl(propTypeDef, propName, props, componentConfig) ? (
propTypeDef && shouldRenderControl(propTypeDef, propName, props) ? (
<div key={propName} className={classes.control}>
<NodeAttributeEditor
node={node}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ export default function NodeDropArea({
const isPageNode = appDom.isPage(node);
const isPageChild = dropAreaNodeParent ? appDom.isPage(dropAreaNodeParent) : false;

const isPageRowNode = appDom.isElement(node) && isPageRow(node);

const isPageChildElement = isPageChild && appDom.isElement(node) && !isPageRow(node);
const isPageRowChild = dropAreaNodeParent
? appDom.isElement(dropAreaNodeParent) && isPageRow(dropAreaNodeParent)
Expand Down Expand Up @@ -242,7 +244,7 @@ export default function NodeDropArea({
}

// Is dragging over left, is page row and child of the page
if (dropAreaNodeParent && appDom.isElement(node) && isPageRow(node) && isPageChild) {
if (dropAreaNodeParent && isPageRowNode && isPageChild) {
return null;
}
}
Expand All @@ -252,21 +254,19 @@ export default function NodeDropArea({
if (
dropAreaNodeParent &&
dropAreaNodeParent.id === dragOverNodeId &&
pageAwareParentProp === dragOverSlotParentProp &&
!parentProp
(pageAwareParentProp === dragOverSlotParentProp || !parentProp)
) {
const parentLastChild =
pageAwareParentProp &&
(appDom.isPage(dropAreaNodeParent) || appDom.isElement(dropAreaNodeParent))
? appDom.getNodeLastChild(dom, dropAreaNodeParent, pageAwareParentProp)
appDom.isPage(dropAreaNodeParent) || appDom.isElement(dropAreaNodeParent)
? appDom.getNodeLastChild(dom, dropAreaNodeParent, pageAwareParentProp || 'children')
: null;

const isParentLastChild = parentLastChild ? node.id === parentLastChild.id : false;

const parentSlots = dropAreaNodeParentInfo?.slots || null;

const parentFlowDirection =
parentSlots && pageAwareParentProp && parentSlots[pageAwareParentProp]?.flowDirection;
parentSlots && parentSlots[pageAwareParentProp || 'children']?.flowDirection;

return parentFlowDirection && isParentLastChild
? getChildNodeHighlightedZone(parentFlowDirection)
Expand Down Expand Up @@ -298,18 +298,19 @@ export default function NodeDropArea({
? dragOverZone
: null;
}, [
dropAreaNodeParent,
dom,
dragOverZone,
availableDropZones,
isPageNode,
parentProp,
isEmptySlot,
dropAreaNodeParent,
dom,
isPageChild,
node,
dragOverNodeId,
dragOverSlotParentProp,
isPageRowChild,
isPageChild,
isPageRowNode,
dropAreaNodeParentInfo?.slots,
dropAreaNodeChildNodes,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
RECTANGLE_EDGE_BOTTOM,
RECTANGLE_EDGE_LEFT,
RECTANGLE_EDGE_RIGHT,
RECTANGLE_EDGE_TOP,
} from '../../../../utils/geometry';

const HINT_POSITION_TOP = 'top';
Expand Down Expand Up @@ -84,7 +85,10 @@ const SelectionHintWrapper = styled('div', {
zIndex: 1000,
...(hintPosition === HINT_POSITION_TOP
? { top: 0, transform: 'translate(0, -100%)' }
: { bottom: 0, transform: 'translate(0, 100%)' }),
: {
bottom: 0,
transform: 'translate(0, 100%)',
}),
},
}));

Expand Down Expand Up @@ -127,6 +131,15 @@ const DraggableEdge = styled('div', {
width: '100%',
};
}
if (edge === RECTANGLE_EDGE_TOP) {
dynamicStyles = {
cursor: 'ns-resize',
top: -10,
height: 22,
left: 0,
width: '100%',
};
}

return {
...dynamicStyles,
Expand Down Expand Up @@ -177,7 +190,10 @@ export default function NodeHud({
isHoverable = true,
isHovered = false,
}: NodeHudProps) {
const hintPosition = rect.y > HUD_HEIGHT ? HINT_POSITION_TOP : HINT_POSITION_BOTTOM;
let hintPosition: HintPosition = HINT_POSITION_BOTTOM;
if (rect.y > HUD_HEIGHT) {
hintPosition = HINT_POSITION_TOP;
}

const interactiveNodeClipPath = React.useMemo(
() =>
Expand Down
Loading

0 comments on commit 4870ed8

Please sign in to comment.