mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 17:08:34 +00:00
Merge pull request #12178 from ToolJet/gutter-issue-container-padding
Fix gutter issue at the bottom of the container
This commit is contained in:
commit
dea079939b
20 changed files with 277 additions and 276 deletions
|
|
@ -3,9 +3,13 @@ import { shallow } from 'zustand/shallow';
|
|||
import './configHandle.scss';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { findHighestLevelofSelection } from '../Grid/gridUtils';
|
||||
import SolidIcon from '@/_ui/Icon/solidIcons/index';
|
||||
|
||||
const CONFIG_HANDLE_HEIGHT = 20;
|
||||
const BUFFER_HEIGHT = 1;
|
||||
|
||||
export const ConfigHandle = ({
|
||||
id,
|
||||
position,
|
||||
widgetTop,
|
||||
widgetHeight,
|
||||
setSelectedComponentAsModal = () => null, //! Only Modal widget passes this uses props down. All other widgets use selecto lib
|
||||
|
|
@ -27,6 +31,7 @@ export const ConfigHandle = ({
|
|||
(state) => componentType === 'Tabs' && state.getExposedValueOfComponent(id)?.currentTab,
|
||||
shallow
|
||||
);
|
||||
const position = widgetTop < 15 ? 'bottom' : 'top';
|
||||
|
||||
const setComponentToInspect = useStore((state) => state.setComponentToInspect);
|
||||
const isModal = componentType === 'Modal' || componentType === 'ModalV2';
|
||||
|
|
@ -36,9 +41,7 @@ export const ConfigHandle = ({
|
|||
// If one component is hovered and one is selected, show the handle for the hovered component
|
||||
return (
|
||||
isWidgetHovered ||
|
||||
(showHandle &&
|
||||
(!isMultipleComponentsSelected || (isModal && isModalOpen)) &&
|
||||
!anyComponentHovered)
|
||||
(showHandle && (!isMultipleComponentsSelected || (isModal && isModalOpen)) && !anyComponentHovered)
|
||||
);
|
||||
}, shallow);
|
||||
let height = visibility === false ? 10 : widgetHeight;
|
||||
|
|
@ -48,7 +51,12 @@ export const ConfigHandle = ({
|
|||
className={`config-handle ${customClassName}`}
|
||||
widget-id={id}
|
||||
style={{
|
||||
top: position === 'top' ? '-20px' : widgetTop + height - (widgetTop < 10 ? 15 : 10),
|
||||
top:
|
||||
componentType === 'Modal' && isModalOpen
|
||||
? '0px'
|
||||
: position === 'top'
|
||||
? '-20px'
|
||||
: `${height - (CONFIG_HANDLE_HEIGHT + BUFFER_HEIGHT)}px`,
|
||||
visibility: _showHandle ? 'visible' : 'hidden',
|
||||
left: '-1px',
|
||||
}}
|
||||
|
|
@ -63,7 +71,10 @@ export const ConfigHandle = ({
|
|||
>
|
||||
<span
|
||||
style={{
|
||||
background: isModal && isModalOpen ? '#c6cad0' : '#4D72FA',
|
||||
background:
|
||||
visibility === false ? '#c6cad0' : componentType === 'Modal' && isModalOpen ? '#c6cad0' : '#4D72FA',
|
||||
border: position === 'bottom' ? '1px solid white' : 'none',
|
||||
color: visibility === false && 'var(--text-placeholder)',
|
||||
}}
|
||||
className="badge handle-content"
|
||||
>
|
||||
|
|
@ -77,17 +88,30 @@ export const ConfigHandle = ({
|
|||
data-cy={`${componentName?.toLowerCase()}-config-handle`}
|
||||
className="text-truncate"
|
||||
>
|
||||
<img
|
||||
style={{ cursor: 'pointer', marginRight: '5px', verticalAlign: 'middle' }}
|
||||
src="assets/images/icons/settings.svg"
|
||||
width="12"
|
||||
height="12"
|
||||
draggable="false"
|
||||
/>
|
||||
{/* Settings Icon */}
|
||||
<span style={{ cursor: 'pointer', marginRight: '5px' }}>
|
||||
<SolidIcon
|
||||
name="settings"
|
||||
width="12"
|
||||
height="12"
|
||||
fill={visibility === false ? 'var(--text-placeholder)' : '#fff'}
|
||||
/>
|
||||
</span>
|
||||
<span>{componentName}</span>
|
||||
{/* Divider */}
|
||||
<hr
|
||||
style={{
|
||||
marginLeft: '10px',
|
||||
height: '12px',
|
||||
width: '2px',
|
||||
backgroundColor: visibility === false ? 'var(--text-placeholder)' : '#fff',
|
||||
opacity: 0.5,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* Delete Button */}
|
||||
{!isMultipleComponentsSelected && !shouldFreeze && (
|
||||
<div className="delete-part">
|
||||
<div>
|
||||
<img
|
||||
style={{ cursor: 'pointer', marginLeft: '5px' }}
|
||||
src="assets/images/icons/inspect.svg"
|
||||
|
|
@ -99,19 +123,20 @@ export const ConfigHandle = ({
|
|||
data-cy={`${componentName.toLowerCase()}-inspect-button`}
|
||||
className="config-handle-inspect"
|
||||
/>
|
||||
<img
|
||||
<span
|
||||
style={{ cursor: 'pointer', marginLeft: '5px' }}
|
||||
src="assets/images/icons/trash-light.svg"
|
||||
width="12"
|
||||
role="button"
|
||||
height="12"
|
||||
draggable="false"
|
||||
onClick={() => {
|
||||
deleteComponents([id]);
|
||||
}}
|
||||
data-cy={`${componentName.toLowerCase()}-delete-button`}
|
||||
className="delete-icon"
|
||||
/>
|
||||
>
|
||||
<SolidIcon
|
||||
name="trash"
|
||||
width="12"
|
||||
height="12"
|
||||
fill={visibility === false ? 'var(--text-placeholder)' : '#fff'}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -31,22 +31,7 @@
|
|||
.badge {
|
||||
font-size: 9px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
.delete-part {
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.delete-part::before {
|
||||
height: 12px;
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
opacity: 0.5;
|
||||
content: "";
|
||||
vertical-align: middle;
|
||||
}
|
||||
border-bottom-right-radius: 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,15 @@ import useStore from '@/AppBuilder/_stores/store';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
import { useDrop } from 'react-dnd';
|
||||
import { addChildrenWidgetsToParent, addNewWidgetToTheEditor, computeViewerBackgroundColor } from './appCanvasUtils';
|
||||
import { CANVAS_WIDTHS, NO_OF_GRIDS, WIDGETS_WITH_DEFAULT_CHILDREN } from './appCanvasConstants';
|
||||
import {
|
||||
CANVAS_WIDTHS,
|
||||
NO_OF_GRIDS,
|
||||
WIDGETS_WITH_DEFAULT_CHILDREN,
|
||||
GRID_HEIGHT,
|
||||
CONTAINER_FORM_CANVAS_PADDING,
|
||||
SUBCONTAINER_CANVAS_BORDER_WIDTH,
|
||||
BOX_PADDING,
|
||||
} from './appCanvasConstants';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import NoComponentCanvasContainer from './NoComponentCanvasContainer';
|
||||
import { RIGHT_SIDE_BAR_TAB } from '../RightSideBar/rightSidebarConstants';
|
||||
|
|
@ -35,10 +43,10 @@ export const Container = React.memo(
|
|||
canvasMaxWidth,
|
||||
isViewerSidebarPinned,
|
||||
pageSidebarStyle,
|
||||
componentType,
|
||||
}) => {
|
||||
const realCanvasRef = useRef(null);
|
||||
const components = useStore((state) => state.getContainerChildrenMapping(id), shallow);
|
||||
const componentType = useStore((state) => state.getComponentTypeFromId(id), shallow);
|
||||
const addComponentToCurrentPage = useStore((state) => state.addComponentToCurrentPage, shallow);
|
||||
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab, shallow);
|
||||
const setLastCanvasClickPosition = useStore((state) => state.setLastCanvasClickPosition, shallow);
|
||||
|
|
@ -95,12 +103,16 @@ export const Container = React.memo(
|
|||
if (canvasWidth !== undefined) {
|
||||
if (componentType === 'Listview' && listViewMode == 'grid') return canvasWidth / columns - 2;
|
||||
if (id === 'canvas') return canvasWidth;
|
||||
return canvasWidth - 2;
|
||||
if (componentType === 'Container' || componentType === 'Form') {
|
||||
return (
|
||||
canvasWidth - (2 * CONTAINER_FORM_CANVAS_PADDING + 2 * SUBCONTAINER_CANVAS_BORDER_WIDTH + 2 * BOX_PADDING)
|
||||
);
|
||||
}
|
||||
return canvasWidth - 2; // Need to update this 2 to correct value for other subcontainers
|
||||
}
|
||||
return realCanvasRef?.current?.offsetWidth;
|
||||
}
|
||||
const gridWidth = getContainerCanvasWidth() / NO_OF_GRIDS;
|
||||
|
||||
useEffect(() => {
|
||||
useGridStore.getState().actions.setSubContainerWidths(id, getContainerCanvasWidth() / NO_OF_GRIDS);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -143,7 +155,7 @@ export const Container = React.memo(
|
|||
}}
|
||||
style={{
|
||||
height: id === 'canvas' ? `${canvasHeight}` : '100%',
|
||||
backgroundSize: `${gridWidth}px ${10}px`,
|
||||
backgroundSize: `${gridWidth}px ${GRID_HEIGHT}px`,
|
||||
backgroundColor:
|
||||
currentMode === 'view'
|
||||
? computeViewerBackgroundColor(darkMode, canvasBgColor)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const getHoveredComponentForGrid = useStore((state) => state.getHoveredComponentForGrid, shallow);
|
||||
const getResolvedComponent = useStore((state) => state.getResolvedComponent, shallow);
|
||||
const [canvasBounds, setCanvasBounds] = useState(CANVAS_BOUNDS);
|
||||
const draggingComponentId = useGridStore((state) => state.draggingComponentId, shallow);
|
||||
const draggingComponentId = useStore((state) => state.draggingComponentId, shallow);
|
||||
const resizingComponentId = useGridStore((state) => state.resizingComponentId, shallow);
|
||||
const [dragParentId, setDragParentId] = useState(null);
|
||||
const [elementGuidelines, setElementGuidelines] = useState([]);
|
||||
|
|
@ -641,8 +641,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const currentWidget = boxList.find(({ id }) => {
|
||||
return id === e.target.id;
|
||||
});
|
||||
document.getElementById('real-canvas')?.classList.remove('show-grid');
|
||||
document.getElementById('canvas-' + currentWidget.component?.parent)?.classList.remove('show-grid');
|
||||
hideGridLines();
|
||||
let _gridWidth = useGridStore.getState().subContainerWidths[currentWidget.component?.parent] || gridWidth;
|
||||
let width = Math.round(e?.lastEvent?.width / _gridWidth) * _gridWidth;
|
||||
const height = Math.round(e?.lastEvent?.height / GRID_HEIGHT) * GRID_HEIGHT;
|
||||
|
|
@ -830,7 +829,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
onDragEnd={(e) => {
|
||||
try {
|
||||
if (isDraggingRef.current) {
|
||||
useGridStore.getState().actions.setDraggingComponentId(null);
|
||||
useStore.getState().setDraggingComponentId(null);
|
||||
isDraggingRef.current = false;
|
||||
}
|
||||
prevDragParentId.current = null;
|
||||
|
|
@ -884,7 +883,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
onDrag={(e) => {
|
||||
// Since onDrag is called multiple times when dragging, hence we are using isDraggingRef to prevent setting state again and again
|
||||
if (!isDraggingRef.current) {
|
||||
useGridStore.getState().actions.setDraggingComponentId(e.target.id);
|
||||
useStore.getState().setDraggingComponentId(e.target.id);
|
||||
showGridLines();
|
||||
isDraggingRef.current = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { OverlayTrigger } from 'react-bootstrap';
|
|||
import { renderTooltip } from '@/_helpers/appUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ErrorBoundary from '@/_ui/ErrorBoundary';
|
||||
|
||||
import { BOX_PADDING } from './appCanvasConstants';
|
||||
const shouldAddBoxShadowAndVisibility = [
|
||||
'Table',
|
||||
'TextInput',
|
||||
|
|
@ -164,7 +164,7 @@ const RenderWidget = ({
|
|||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
padding: resolvedStyles?.padding == 'none' ? '0px' : '2px', //chart and image has a padding property other than container padding
|
||||
padding: resolvedStyles?.padding == 'none' ? '0px' : `${BOX_PADDING}px`, //chart and image has a padding property other than container padding
|
||||
}}
|
||||
role={'Box'}
|
||||
className={inCanvas ? `_tooljet-${component?.component} _tooljet-${component?.name}` : ''} //required for custom CSS
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const WidgetWrapper = memo(
|
|||
);
|
||||
const layoutData = useStore((state) => state.getComponentDefinition(id)?.layouts?.[currentLayout], shallow);
|
||||
const isWidgetActive = useStore((state) => state.selectedComponents.find((sc) => sc === id) && !readOnly, shallow);
|
||||
const isDragging = useGridStore((state) => state.draggingComponentId === id);
|
||||
const isDragging = useStore((state) => state.draggingComponentId === id);
|
||||
const isResizing = useGridStore((state) => state.resizingComponentId === id);
|
||||
const componentType = useStore((state) => state.getComponentDefinition(id)?.component?.component, shallow);
|
||||
const setHoveredComponentForGrid = useStore((state) => state.setHoveredComponentForGrid, shallow);
|
||||
|
|
@ -52,7 +52,9 @@ const WidgetWrapper = memo(
|
|||
height: visibility === false ? '10px' : `${height}px`,
|
||||
transform: `translate(${layoutData.left * gridWidth}px, ${layoutData.top}px)`,
|
||||
WebkitFontSmoothing: 'antialiased',
|
||||
border: visibility === false ? `1px solid var(--border-default)` : 'none',
|
||||
};
|
||||
|
||||
if (!componentType) return null;
|
||||
return (
|
||||
<>
|
||||
|
|
@ -68,7 +70,6 @@ const WidgetWrapper = memo(
|
|||
id={id}
|
||||
widgetid={id}
|
||||
style={{
|
||||
// transform: `translate(332px, -134px)`,
|
||||
// zIndex: mode === 'view' && widget.component.component == 'Datepicker' ? 2 : null,
|
||||
...styles,
|
||||
}}
|
||||
|
|
@ -84,7 +85,6 @@ const WidgetWrapper = memo(
|
|||
{mode == 'edit' && (
|
||||
<ConfigHandle
|
||||
id={id}
|
||||
position={layoutData.top < 15 ? 'bottom' : 'top'}
|
||||
widgetTop={layoutData.top}
|
||||
widgetHeight={layoutData.height}
|
||||
showHandle={isWidgetActive}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
export const NO_OF_GRIDS = 43;
|
||||
|
||||
export const GRID_HEIGHT = 10;
|
||||
|
||||
export const CANVAS_WIDTHS = Object.freeze({
|
||||
deviceWindowWidth: 450,
|
||||
leftSideBarWidth: 48,
|
||||
|
|
@ -15,3 +17,9 @@ export const APP_HEADER_HEIGHT = 47;
|
|||
export const LEFT_SIDEBAR_WIDTH = 348; // exclusive of border
|
||||
|
||||
export const SUBCONTAINER_WIDGETS = ['Container', 'Tabs', 'Listview', 'Kanban', 'Form'];
|
||||
|
||||
export const CONTAINER_FORM_CANVAS_PADDING = 7;
|
||||
|
||||
export const SUBCONTAINER_CANVAS_BORDER_WIDTH = 1;
|
||||
|
||||
export const BOX_PADDING = 2;
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { Container as ContainerComponent } from '@/AppBuilder/AppCanvas/Container';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import { useExposeState } from '@/AppBuilder/_hooks/useExposeVariables';
|
||||
|
||||
export const Container = ({
|
||||
id,
|
||||
properties,
|
||||
styles,
|
||||
darkMode,
|
||||
height,
|
||||
width,
|
||||
setExposedVariables,
|
||||
setExposedVariable,
|
||||
}) => {
|
||||
const { borderRadius, borderColor, boxShadow, headerHeight = 80 } = styles;
|
||||
|
||||
const { isDisabled, isVisible, isLoading } = useExposeState(
|
||||
properties.loadingState,
|
||||
properties.visibility,
|
||||
properties.disabledState,
|
||||
setExposedVariables,
|
||||
setExposedVariable
|
||||
);
|
||||
|
||||
const contentBgColor = useMemo(() => {
|
||||
return {
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(styles.backgroundColor) && darkMode ? '#232E3C' : styles.backgroundColor,
|
||||
};
|
||||
}, [styles.backgroundColor, darkMode]);
|
||||
|
||||
const headerBgColor = useMemo(() => {
|
||||
return {
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(styles.headerBackgroundColor) && darkMode
|
||||
? '#232E3C'
|
||||
: styles.headerBackgroundColor,
|
||||
};
|
||||
}, [styles.headerBackgroundColor, darkMode]);
|
||||
|
||||
const computedStyles = {
|
||||
backgroundColor: contentBgColor.backgroundColor,
|
||||
borderRadius: borderRadius ? parseFloat(borderRadius) : 0,
|
||||
border: `1px solid ${borderColor}`,
|
||||
height,
|
||||
display: isVisible ? 'flex' : 'none',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
boxShadow,
|
||||
};
|
||||
|
||||
const computedHeaderStyles = {
|
||||
...headerBgColor,
|
||||
height: `${headerHeight}px`,
|
||||
flexShrink: 0,
|
||||
flexGrow: 0,
|
||||
borderBottom: `1px solid var(--border-weak)`,
|
||||
};
|
||||
|
||||
const computedContentStyles = {
|
||||
...contentBgColor,
|
||||
flex: 1,
|
||||
overflow: 'auto',
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`jet-container widget-type-container ${properties.loadingState && 'jet-container-loading'}`}
|
||||
id={id}
|
||||
data-disabled={isDisabled}
|
||||
style={computedStyles}
|
||||
>
|
||||
{properties.showHeader && (
|
||||
<ContainerComponent
|
||||
id={`${id}-header`}
|
||||
styles={computedHeaderStyles}
|
||||
canvasHeight={headerHeight / 10}
|
||||
canvasWidth={width}
|
||||
allowContainerSelect={true}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
)}
|
||||
{isLoading ? (
|
||||
<div className="h-100 d-flex align-items-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<ContainerComponent
|
||||
id={id}
|
||||
styles={computedContentStyles}
|
||||
canvasHeight={height}
|
||||
canvasWidth={width}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
119
frontend/src/AppBuilder/Widgets/Container/Container.jsx
Normal file
119
frontend/src/AppBuilder/Widgets/Container/Container.jsx
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { Container as ContainerComponent } from '@/AppBuilder/AppCanvas/Container';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import { useExposeState } from '@/AppBuilder/_hooks/useExposeVariables';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import {
|
||||
CONTAINER_FORM_CANVAS_PADDING,
|
||||
SUBCONTAINER_CANVAS_BORDER_WIDTH,
|
||||
} from '@/AppBuilder/AppCanvas/appCanvasConstants';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import './container.scss';
|
||||
|
||||
export const Container = ({
|
||||
id,
|
||||
properties,
|
||||
styles,
|
||||
darkMode,
|
||||
height,
|
||||
width,
|
||||
setExposedVariables,
|
||||
setExposedVariable,
|
||||
}) => {
|
||||
const { isDisabled, isVisible, isLoading } = useExposeState(
|
||||
properties.loadingState,
|
||||
properties.visibility,
|
||||
properties.disabledState,
|
||||
setExposedVariables,
|
||||
setExposedVariable
|
||||
);
|
||||
|
||||
const isWidgetInContainerDragging = useStore(
|
||||
(state) => state.containerChildrenMapping?.[id]?.includes(state?.draggingComponentId),
|
||||
shallow
|
||||
);
|
||||
|
||||
const { borderRadius, borderColor, boxShadow, headerHeight = 80 } = styles;
|
||||
const contentBgColor = useMemo(() => {
|
||||
return {
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(styles.backgroundColor) && darkMode ? '#232E3C' : styles.backgroundColor,
|
||||
};
|
||||
}, [styles.backgroundColor, darkMode]);
|
||||
|
||||
const headerBgColor = useMemo(() => {
|
||||
return {
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(styles.headerBackgroundColor) && darkMode
|
||||
? '#232E3C'
|
||||
: styles.headerBackgroundColor,
|
||||
};
|
||||
}, [styles.headerBackgroundColor, darkMode]);
|
||||
|
||||
const computedStyles = {
|
||||
backgroundColor: contentBgColor.backgroundColor,
|
||||
borderRadius: borderRadius ? parseFloat(borderRadius) : 0,
|
||||
border: `${SUBCONTAINER_CANVAS_BORDER_WIDTH}px solid ${borderColor}`,
|
||||
height,
|
||||
display: isVisible ? 'flex' : 'none',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
boxShadow,
|
||||
};
|
||||
|
||||
const containerHeaderStyles = {
|
||||
flexShrink: 0,
|
||||
padding: `${CONTAINER_FORM_CANVAS_PADDING}px ${CONTAINER_FORM_CANVAS_PADDING}px 3px ${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
...headerBgColor,
|
||||
};
|
||||
|
||||
const containerContentStyles = {
|
||||
overflow: 'hidden auto',
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
padding: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`jet-container ${isLoading ? 'jet-container-loading' : ''}`}
|
||||
id={id}
|
||||
data-disabled={isDisabled}
|
||||
style={computedStyles}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
{properties.showHeader && (
|
||||
<div style={containerHeaderStyles} className="wj-container-header">
|
||||
<ContainerComponent
|
||||
id={`${id}-header`}
|
||||
styles={{ ...headerBgColor, height: `${headerHeight}px` }}
|
||||
canvasHeight={headerHeight / 10}
|
||||
canvasWidth={width}
|
||||
allowContainerSelect={true}
|
||||
darkMode={darkMode}
|
||||
componentType="Container"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div style={containerContentStyles}>
|
||||
<ContainerComponent
|
||||
id={id}
|
||||
styles={{
|
||||
...contentBgColor,
|
||||
// Prevent the scroll when dragging a widget inside the container or moving out of the container
|
||||
overflow: isWidgetInContainerDragging ? 'hidden' : 'hidden auto',
|
||||
}}
|
||||
canvasHeight={height}
|
||||
canvasWidth={width}
|
||||
darkMode={darkMode}
|
||||
componentType="Container"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
13
frontend/src/AppBuilder/Widgets/Container/container.scss
Normal file
13
frontend/src/AppBuilder/Widgets/Container/container.scss
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.wj-container-header {
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: -7px;
|
||||
right: -7px;
|
||||
height: 1px;
|
||||
background-color: var(--border-weak);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,21 +1,22 @@
|
|||
import React, { useRef, useState, useEffect, useMemo } from 'react';
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { Container as SubContainer } from '@/AppBuilder/AppCanvas/Container';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { diff } from 'deep-object-diff';
|
||||
import _, { debounce, omit } from 'lodash';
|
||||
import { Box } from '@/Editor/Box';
|
||||
import { generateUIComponents } from './FormUtils';
|
||||
import { useMounted } from '@/_hooks/use-mount';
|
||||
import { onComponentClick, removeFunctionObjects } from '@/_helpers/appUtils';
|
||||
import { useAppInfo } from '@/_stores/appDataStore';
|
||||
import { deepClone } from '@/_helpers/utilities/utils.helpers';
|
||||
import RenderSchema from './RenderSchema';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import {
|
||||
CONTAINER_FORM_CANVAS_PADDING,
|
||||
SUBCONTAINER_CANVAS_BORDER_WIDTH,
|
||||
} from '@/AppBuilder/AppCanvas/appCanvasConstants';
|
||||
import './form.scss';
|
||||
|
||||
const getCanvasHeight = (height) => {
|
||||
const parsedHeight = height.includes('px') ? parseInt(height, 10) : height;
|
||||
|
||||
return Math.ceil(parsedHeight);
|
||||
};
|
||||
|
||||
|
|
@ -59,7 +60,7 @@ export const Form = function Form(props) {
|
|||
const computedStyles = {
|
||||
backgroundColor,
|
||||
borderRadius: borderRadius ? parseFloat(borderRadius) : 0,
|
||||
border: `1px solid ${borderColor}`,
|
||||
border: `${SUBCONTAINER_CANVAS_BORDER_WIDTH}px solid ${borderColor}`,
|
||||
height,
|
||||
display: visibility ? 'flex' : 'none',
|
||||
position: 'relative',
|
||||
|
|
@ -69,25 +70,29 @@ export const Form = function Form(props) {
|
|||
|
||||
const formHeader = {
|
||||
flexShrink: 0,
|
||||
// height: headerHeight,
|
||||
padding: '10px 6px',
|
||||
borderBottom: '1px solid var(--border-weak)',
|
||||
paddingBottom: '3px',
|
||||
paddingTop: '7px',
|
||||
paddingLeft: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
paddingRight: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(headerBackgroundColor) && darkMode ? '#1F2837' : headerBackgroundColor,
|
||||
};
|
||||
const formFooter = {
|
||||
flexShrink: 0,
|
||||
// height: footerHeight,
|
||||
padding: '10px 6px',
|
||||
borderTop: '1px solid var(--border-weak)',
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(footerBackgroundColor) && darkMode ? '#1F2837' : footerBackgroundColor,
|
||||
};
|
||||
|
||||
const formContent = {
|
||||
overflow: 'hidden auto',
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
padding: '10px 6px',
|
||||
paddingTop: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
paddingBottom: showFooter ? '3px' : '7px',
|
||||
paddingLeft: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
paddingRight: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
};
|
||||
|
||||
const formFooter = {
|
||||
flexShrink: 0,
|
||||
padding: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(footerBackgroundColor) && darkMode ? '#1F2837' : footerBackgroundColor,
|
||||
};
|
||||
|
||||
const parentRef = useRef(null);
|
||||
|
|
@ -287,7 +292,7 @@ export const Form = function Form(props) {
|
|||
}} //Hack, should find a better solution - to prevent losing z index+1 when container element is clicked
|
||||
>
|
||||
{showHeader && (
|
||||
<div style={formHeader}>
|
||||
<div style={formHeader} className="wj-form-header">
|
||||
<SubContainer
|
||||
id={`${id}-header`}
|
||||
canvasHeight={canvasHeaderHeight}
|
||||
|
|
@ -298,6 +303,7 @@ export const Form = function Form(props) {
|
|||
backgroundColor: 'transparent',
|
||||
height: headerHeight,
|
||||
}}
|
||||
componentType="Form"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -320,29 +326,8 @@ export const Form = function Form(props) {
|
|||
onOptionsChange={onOptionsChange}
|
||||
styles={{ backgroundColor: computedStyles.backgroundColor }}
|
||||
darkMode={darkMode}
|
||||
componentType="Form"
|
||||
/>
|
||||
{/* <SubContainer
|
||||
parentComponent={component}
|
||||
containerCanvasWidth={width}
|
||||
parent={id}
|
||||
parentRef={parentRef}
|
||||
removeComponent={removeComponent}
|
||||
onOptionChange={function ({ component, optionName, value, componentId }) {
|
||||
if (componentId) {
|
||||
onOptionChange({ component, optionName, value, componentId });
|
||||
}
|
||||
}}
|
||||
currentPageId={props.currentPageId}
|
||||
{...props}
|
||||
{...containerProps}
|
||||
height={'100%'} // This height is required since Subcontainer has a issue if height is provided, it stores it in the ref and never updates that ref
|
||||
/>
|
||||
<SubCustomDragLayer
|
||||
containerCanvasWidth={width}
|
||||
parent={id}
|
||||
parentRef={parentRef}
|
||||
currentLayout={currentLayout}
|
||||
/> */}
|
||||
</div>
|
||||
)}
|
||||
{advanced &&
|
||||
|
|
@ -375,28 +360,6 @@ export const Form = function Form(props) {
|
|||
onOptionsChange={onComponentOptionsChangedForSubcontainer}
|
||||
/>
|
||||
</div>
|
||||
{/* <Box
|
||||
{...props}
|
||||
component={item}
|
||||
id={index}
|
||||
width={width}
|
||||
height={item.defaultSize.height}
|
||||
mode={mode}
|
||||
inCanvas={true}
|
||||
paramUpdated={paramUpdated}
|
||||
onEvent={onEvent}
|
||||
onComponentClick={onComponentClick}
|
||||
darkMode={darkMode}
|
||||
removeComponent={removeComponent}
|
||||
// canvasWidth={width}
|
||||
// readOnly={readOnly}
|
||||
// customResolvables={customResolvables}
|
||||
parentId={id}
|
||||
getContainerProps={getContainerProps}
|
||||
onOptionChanged={onComponentOptionChangedForSubcontainer}
|
||||
onOptionsChanged={onComponentOptionsChanged}
|
||||
isFromSubContainer={true}
|
||||
/> */}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
|
@ -404,7 +367,7 @@ export const Form = function Form(props) {
|
|||
)}
|
||||
</div>
|
||||
{showFooter && (
|
||||
<div className="jet-form-footer" style={formFooter}>
|
||||
<div className="jet-form-footer wj-form-footer" style={formFooter}>
|
||||
<SubContainer
|
||||
id={`${id}-footer`}
|
||||
canvasHeight={canvasFooterHeight}
|
||||
|
|
@ -416,6 +379,7 @@ export const Form = function Form(props) {
|
|||
backgroundColor: 'transparent',
|
||||
height: footerHeight,
|
||||
}}
|
||||
componentType="Form"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
25
frontend/src/AppBuilder/Widgets/Form/form.scss
Normal file
25
frontend/src/AppBuilder/Widgets/Form/form.scss
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
.wj-form-header {
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: -7px;
|
||||
right: -7px;
|
||||
height: 1px;
|
||||
background-color: var(--border-weak);
|
||||
}
|
||||
}
|
||||
|
||||
.wj-form-footer {
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -7px;
|
||||
right: -7px;
|
||||
height: 1px;
|
||||
background-color: var(--border-weak);
|
||||
}
|
||||
}
|
||||
|
|
@ -410,6 +410,7 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef, id }) {
|
|||
width: `${(Number(cardWidth) || 300) + 48}px`,
|
||||
}}
|
||||
kanbanProps={kanbanProps}
|
||||
componentType="Kanban"
|
||||
>
|
||||
{items[columnId] && (
|
||||
<SortableContext items={items[columnId]} strategy={verticalListSortingStrategy}>
|
||||
|
|
|
|||
|
|
@ -12,11 +12,8 @@ import { shallow } from 'zustand/shallow';
|
|||
|
||||
export const Listview = function Listview({
|
||||
id,
|
||||
component,
|
||||
width,
|
||||
height,
|
||||
containerProps,
|
||||
removeComponent,
|
||||
properties,
|
||||
styles,
|
||||
fireEvent,
|
||||
|
|
@ -270,38 +267,8 @@ export const Listview = function Listview({
|
|||
columns={positiveColumns}
|
||||
listViewMode={mode}
|
||||
darkMode={darkMode}
|
||||
componentType="Listview"
|
||||
/>
|
||||
{/* <SubContainer
|
||||
columns={positiveColumns}
|
||||
listmode={mode}
|
||||
parentComponent={component}
|
||||
containerCanvasWidth={width}
|
||||
parent={`${id}`}
|
||||
parentName={component.name}
|
||||
{...containerProps}
|
||||
readOnly={index !== 0}
|
||||
customResolvables={{ listItem }}
|
||||
parentRef={parentRef}
|
||||
removeComponent={removeComponent}
|
||||
listViewItemOptions={{ index }}
|
||||
exposedVariables={childrenData[index]}
|
||||
onOptionChange={function ({ component, optionName, value, componentId }) {
|
||||
setChildrenData((prevData) => {
|
||||
const changedData = { [component.name]: { [optionName]: value } };
|
||||
const existingDataAtIndex = prevData[index] ?? {};
|
||||
const newDataAtIndex = {
|
||||
...prevData[index],
|
||||
[component.name]: {
|
||||
...existingDataAtIndex[component.name],
|
||||
...changedData[component.name],
|
||||
id: componentId,
|
||||
},
|
||||
};
|
||||
const newChildrenData = { ...prevData, [index]: newDataAtIndex };
|
||||
return { ...prevData, ...newChildrenData };
|
||||
});
|
||||
}}
|
||||
/> */}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ export const Tabs = function Tabs({
|
|||
allowContainerSelect={true}
|
||||
styles={{ backgroundColor: bgColor }}
|
||||
darkMode={darkMode}
|
||||
componentType="Tabs"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ import { BoundedBox } from '@/Editor/Components/BoundedBox/BoundedBox';
|
|||
import { isPDFSupported } from '@/_helpers/appUtils';
|
||||
import { resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
import { useEditorStore } from '@/_stores/editorStore';
|
||||
import { Container } from '@/AppBuilder/Widgets/Container';
|
||||
import { Container } from '@/AppBuilder/Widgets/Container/Container';
|
||||
import { Listview } from '@/AppBuilder/Widgets/Listview';
|
||||
import { Tabs } from '@/AppBuilder/Widgets/Tabs';
|
||||
import { Kanban } from '@/AppBuilder/Widgets/Kanban/Kanban';
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ const initialState = {
|
|||
triggerCanvasUpdater: false,
|
||||
lastCanvasIdClick: '',
|
||||
lastCanvasClickPosition: null,
|
||||
draggingComponentId: null,
|
||||
};
|
||||
|
||||
export const createGridSlice = (set, get) => ({
|
||||
|
|
@ -21,6 +22,7 @@ export const createGridSlice = (set, get) => ({
|
|||
debouncedToggleCanvasUpdater: debounce(() => {
|
||||
get().toggleCanvasUpdater();
|
||||
}, 200),
|
||||
setDraggingComponentId: (id) => set(() => ({ draggingComponentId: id })),
|
||||
moveComponentPosition: (direction) => {
|
||||
const { setComponentLayout, currentLayout, getSelectedComponentsDefinition, debouncedToggleCanvasUpdater } = get();
|
||||
let layouts = {};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { resolveWidgetFieldValue } from '@/_helpers/utils';
|
|||
import ErrorBoundary from './ErrorBoundary';
|
||||
import { useEditorStore } from '@/_stores/editorStore';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { useNoOfGrid, useGridStore } from '@/_stores/gridStore';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import WidgetBox from './WidgetBox';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { findHighestLevelofSelection } from './DragContainer';
|
||||
|
|
@ -61,7 +61,7 @@ const DraggableBox = React.memo(
|
|||
}) => {
|
||||
const isResizing = useGridStore((state) => state.resizingComponentId === id);
|
||||
const [canDrag, setCanDrag] = useState(true);
|
||||
const noOfGrid = useNoOfGrid();
|
||||
const noOfGrid = 43;
|
||||
const {
|
||||
currentLayout,
|
||||
setHoveredComponent,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { useEditorStore } from '@/_stores/editorStore';
|
|||
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { diff } from 'deep-object-diff';
|
||||
import { useGridStore, useResizingComponentId } from '@/_stores/gridStore';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import GhostWidget from './GhostWidget';
|
||||
import { deepClone } from '@/_helpers/utilities/utils.helpers';
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ export const SubContainer = ({
|
|||
shallow
|
||||
);
|
||||
|
||||
const resizingComponentId = useResizingComponentId();
|
||||
const resizingComponentId = useGridStore((state) => state.resizingComponentId, shallow);
|
||||
|
||||
const noOfGrids = 43;
|
||||
const { isGridActive } = useGridStore((state) => ({ isGridActive: state.activeGrid === parent }), shallow);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ const initialState = {
|
|||
noOfGrid: 43,
|
||||
draggedSubContainer: false,
|
||||
resizingComponentId: null,
|
||||
draggingComponentId: null,
|
||||
dragTarget: null,
|
||||
isGroupHandleHoverd: false,
|
||||
idGroupDragged: false,
|
||||
|
|
@ -20,11 +19,7 @@ export const useGridStore = create(
|
|||
(set) => ({
|
||||
...initialState,
|
||||
actions: {
|
||||
setActiveGrid: (gridId) => set({ activeGrid: gridId }),
|
||||
setNoOfGrid: (noOfGrid) => set({ noOfGrid }),
|
||||
setDraggedSubContainer: (draggedSubContainer) => set({ draggedSubContainer }),
|
||||
setResizingComponentId: (id) => set({ resizingComponentId: id }),
|
||||
setDraggingComponentId: (id) => set({ draggingComponentId: id }),
|
||||
setDragTarget: (dragTarget) => set({ dragTarget }),
|
||||
setIsGroupHandleHoverd: (isGroupHandleHoverd) => set({ isGroupHandleHoverd }),
|
||||
setIdGroupDragged: (idGroupDragged) => set({ idGroupDragged }),
|
||||
|
|
@ -46,21 +41,5 @@ useGridStore.subscribe(({ draggingComponentId }) => {
|
|||
}
|
||||
});
|
||||
|
||||
// useEditorStore.subscribe(({ hoveredComponent }) => {
|
||||
// console.log('hoveredComponent--', hoveredComponent);
|
||||
// if (hoveredComponent) {
|
||||
// document.querySelector(`[data-hovered-control]`)?.removeAttribute('data-hovered-control');
|
||||
// document.querySelector(`[target-id='${hoveredComponent}']`)?.setAttribute('data-hovered-control', true);
|
||||
// } else if (document.querySelector(`[data-hovered-control]`)) {
|
||||
// document.querySelector(`[data-hovered-control]`)?.removeAttribute('data-hovered-control');
|
||||
// }
|
||||
// });
|
||||
|
||||
export const useActiveGrid = () => useGridStore((state) => state.activeGrid, shallow);
|
||||
export const useNoOfGrid = () => useGridStore((state) => state.noOfGrid, shallow);
|
||||
export const useDraggedSubContainer = () => useGridStore((state) => state.draggedSubContainer, shallow);
|
||||
export const useGridStoreActions = () => useGridStore((state) => state.actions, shallow);
|
||||
export const useDragTarget = () => useGridStore((state) => state.dragTarget, shallow);
|
||||
export const useResizingComponentId = () => useGridStore((state) => state.resizingComponentId, shallow);
|
||||
export const useIsGroupHandleHoverd = () => useGridStore((state) => state.isGroupHandleHoverd, shallow);
|
||||
export const useOpenModalWidgetId = () => useGridStore((state) => state.openModalWidgetId, shallow);
|
||||
|
|
|
|||
Loading…
Reference in a new issue