mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
feat: Adds header for container widget (#11309)
This commit is contained in:
parent
00f4ee4370
commit
558d66fed0
7 changed files with 233 additions and 19 deletions
|
|
@ -685,7 +685,9 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
let left = e.lastEvent?.translate[0];
|
||||
let top = e.lastEvent?.translate[1];
|
||||
if (
|
||||
['Listview', 'Kanban'].includes(boxList.find((box) => box.id === draggedOverElemId)?.component?.component)
|
||||
['Listview', 'Kanban', 'Container'].includes(
|
||||
boxList.find((box) => box.id === draggedOverElemId)?.component?.component
|
||||
)
|
||||
) {
|
||||
const elemContainer = e.target.closest('.real-canvas');
|
||||
const containerHeight = elemContainer.clientHeight;
|
||||
|
|
@ -702,10 +704,19 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
if (draggedOverElemId !== currentParentId) {
|
||||
if (isParentChangeAllowed) {
|
||||
const draggedOverWidget = boxList.find((box) => box.id === draggedOverElemId);
|
||||
|
||||
let parentWidgetType = boxList.find((box) => box.id === draggedOverElemId)?.component?.component;
|
||||
// @TODO - When dropping back to container from canvas, the boxList doesn't have canvas header,
|
||||
// boxList will return null. But we need to tell getMouseDistanceFromParentDiv parentWidgetType is container
|
||||
// As container id is like 'canvas-2375e23765e-123234'
|
||||
if (parentId && !parentWidgetType && draggedOverElemId.includes('-header')) {
|
||||
parentWidgetType = 'Container';
|
||||
}
|
||||
|
||||
let { left: _left, top: _top } = getMouseDistanceFromParentDiv(
|
||||
e,
|
||||
draggedOverWidget?.component?.component === 'Kanban' ? draggedOverElem : draggedOverElemId,
|
||||
boxList.find((box) => box.id === draggedOverElemId)?.component?.component
|
||||
parentWidgetType
|
||||
);
|
||||
left = _left;
|
||||
top = _top;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export const CANVAS_WIDTHS = Object.freeze({
|
|||
rightSideBarWidth: 300,
|
||||
});
|
||||
|
||||
export const WIDGETS_WITH_DEFAULT_CHILDREN = ['Listview', 'Tabs', 'Form', 'Kanban'];
|
||||
export const WIDGETS_WITH_DEFAULT_CHILDREN = ['Listview', 'Tabs', 'Form', 'Kanban', 'Container'];
|
||||
|
||||
export const DEFAULT_CANVAS_WIDTH = 1292;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { deepClone } from '@/_helpers/utilities/utils.helpers';
|
|||
import { componentTypes } from '../WidgetManager';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { CANVAS_WIDTHS, NO_OF_GRIDS } from './appCanvasConstants';
|
||||
import { CANVAS_WIDTHS, NO_OF_GRIDS, WIDGETS_WITH_DEFAULT_CHILDREN } from './appCanvasConstants';
|
||||
import _ from 'lodash';
|
||||
|
||||
export function snapToGrid(canvasWidth, x, y) {
|
||||
|
|
@ -42,8 +42,6 @@ export const addNewWidgetToTheEditor = (componentType, eventMonitorObject, curre
|
|||
componentData.definition.others.showOnMobile.value = `{{true}}`;
|
||||
}
|
||||
|
||||
const widgetsWithDefaultComponents = ['Listview', 'Tabs', 'Form', 'Kanban'];
|
||||
|
||||
const nonActiveLayout = currentLayout === 'desktop' ? 'mobile' : 'desktop';
|
||||
const newComponent = {
|
||||
id: uuidv4(),
|
||||
|
|
@ -66,7 +64,7 @@ export const addNewWidgetToTheEditor = (componentType, eventMonitorObject, curre
|
|||
height: defaultHeight,
|
||||
},
|
||||
},
|
||||
withDefaultChildren: widgetsWithDefaultComponents.includes(componentData.component),
|
||||
withDefaultChildren: WIDGETS_WITH_DEFAULT_CHILDREN.includes(componentData.component),
|
||||
};
|
||||
|
||||
return newComponent;
|
||||
|
|
@ -136,13 +134,14 @@ export function addChildrenWidgetsToParent(componentType, parentId, currentLayou
|
|||
}
|
||||
|
||||
const nonActiveLayout = currentLayout === 'desktop' ? 'mobile' : 'desktop';
|
||||
const _parent = getParentComponentIdByType(child, parentMeta.component, parentId);
|
||||
|
||||
const newChildComponent = {
|
||||
id: uuidv4(),
|
||||
name: widgetName,
|
||||
component: {
|
||||
...componentData,
|
||||
parent: parentMeta.component === 'Tabs' ? parentId + '-' + tab : parentId,
|
||||
parent: getParentComponentIdByType(child, parentMeta.component, parentId),
|
||||
},
|
||||
layouts: {
|
||||
[currentLayout]: {
|
||||
|
|
@ -193,7 +192,8 @@ export const getAllChildComponents = (allComponents, parentId) => {
|
|||
const isParentTabORCalendar =
|
||||
allComponents[parentId]?.component?.component === 'Tabs' ||
|
||||
allComponents[parentId]?.component?.component === 'Calendar' ||
|
||||
allComponents[parentId]?.component?.component === 'Kanban';
|
||||
allComponents[parentId]?.component?.component === 'Kanban' ||
|
||||
allComponents[parentId]?.component?.component === 'Container';
|
||||
|
||||
if (componentParentId && isParentTabORCalendar) {
|
||||
let childComponent = deepClone(allComponents[componentId]);
|
||||
|
|
@ -336,7 +336,11 @@ const isChildOfTabsOrCalendar = (component, allComponents = [], componentParentI
|
|||
const parentComponent = allComponents?.[parentId];
|
||||
|
||||
if (parentComponent) {
|
||||
return parentComponent.component.component === 'Tabs' || parentComponent.component.component === 'Calendar';
|
||||
return (
|
||||
parentComponent.component.component === 'Tabs' ||
|
||||
parentComponent.component.component === 'Calendar' ||
|
||||
parentComponent.component.component === 'Container'
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -457,3 +461,11 @@ export const computeViewerBackgroundColor = (isAppDarkMode, canvasBgColor) => {
|
|||
}
|
||||
return canvasBgColor;
|
||||
};
|
||||
|
||||
export const getParentComponentIdByType = (child, parentComponent, parentId) => {
|
||||
const { tab } = child;
|
||||
|
||||
if (parentComponent === 'Tabs') return `${parentId}-${tab}`;
|
||||
else if (parentComponent === 'Container') return `${parentId}-header`;
|
||||
return parentId;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -39,17 +39,68 @@ export const containerConfig = {
|
|||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
showHeader: {
|
||||
type: 'toggle',
|
||||
displayName: 'Show header',
|
||||
validation: {
|
||||
schema: { type: 'boolean' },
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
accordian: 'field',
|
||||
},
|
||||
},
|
||||
defaultChildren: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 20,
|
||||
left: 1,
|
||||
height: 40,
|
||||
},
|
||||
displayName: 'ContainerText',
|
||||
properties: ['text'],
|
||||
accessorKey: 'text',
|
||||
styles: ['fontWeight', 'textSize', 'textColor'],
|
||||
defaultValue: {
|
||||
text: 'Container title',
|
||||
fontWeight: 'bold',
|
||||
textSize: 16,
|
||||
textColor: '#000',
|
||||
},
|
||||
},
|
||||
],
|
||||
events: {},
|
||||
styles: {
|
||||
backgroundColor: {
|
||||
type: 'color',
|
||||
displayName: 'Background color',
|
||||
displayName: 'Background',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#fff',
|
||||
},
|
||||
},
|
||||
headerBackgroundColor: {
|
||||
type: 'color',
|
||||
displayName: 'Header',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#fff',
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
validation: {
|
||||
schema: { type: 'number' },
|
||||
defaultValue: 80,
|
||||
},
|
||||
accordian: 'field',
|
||||
},
|
||||
borderRadius: {
|
||||
type: 'code',
|
||||
displayName: 'Border radius',
|
||||
|
|
@ -105,8 +156,10 @@ export const containerConfig = {
|
|||
events: [],
|
||||
styles: {
|
||||
backgroundColor: { value: '#fff' },
|
||||
headerBackgroundColor: { value: '#fff' },
|
||||
borderRadius: { value: '4' },
|
||||
borderColor: { value: '#fff' },
|
||||
headerHeight: { value: '80' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ export const Container = ({
|
|||
setExposedVariables,
|
||||
setExposedVariable,
|
||||
}) => {
|
||||
const { borderRadius, borderColor, boxShadow } = styles;
|
||||
|
||||
const { isDisabled, isVisible, isLoading } = useExposeState(
|
||||
properties.loadingState,
|
||||
properties.visibility,
|
||||
|
|
@ -23,15 +21,25 @@ export const Container = ({
|
|||
setExposedVariable
|
||||
);
|
||||
|
||||
const bgColor = useMemo(() => {
|
||||
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: bgColor.backgroundColor,
|
||||
backgroundColor: contentBgColor.backgroundColor,
|
||||
borderRadius: borderRadius ? parseFloat(borderRadius) : 0,
|
||||
border: `1px solid ${borderColor}`,
|
||||
height,
|
||||
|
|
@ -40,9 +48,26 @@ export const Container = ({
|
|||
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 ${isLoading && 'jet-container-loading'}`}
|
||||
className={`jet-container tw-flex tw-flex-col ${properties.loadingState && 'jet-container-loading'} ${
|
||||
properties.showHeader && 'jet-container--with-header'
|
||||
}`}
|
||||
id={id}
|
||||
data-disabled={isDisabled}
|
||||
style={computedStyles}
|
||||
|
|
@ -50,7 +75,25 @@ export const Container = ({
|
|||
{isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<ContainerComponent id={id} styles={bgColor} canvasHeight={height} canvasWidth={width} darkMode={darkMode} />
|
||||
<>
|
||||
{properties.showHeader && (
|
||||
<ContainerComponent
|
||||
id={`${id}-header`}
|
||||
styles={computedHeaderStyles}
|
||||
canvasHeight={headerHeight / 10}
|
||||
canvasWidth={width}
|
||||
allowContainerSelect={true}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
)}
|
||||
<ContainerComponent
|
||||
id={id}
|
||||
styles={computedContentStyles}
|
||||
canvasHeight={height}
|
||||
canvasWidth={width}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -39,17 +39,62 @@ export const containerConfig = {
|
|||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
showHeader: {
|
||||
type: 'toggle',
|
||||
displayName: 'Show header',
|
||||
validation: {
|
||||
schema: { type: 'boolean' },
|
||||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultChildren: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 20,
|
||||
left: 1,
|
||||
height: 40,
|
||||
},
|
||||
displayName: 'ContainerText',
|
||||
properties: ['text'],
|
||||
accessorKey: 'text',
|
||||
styles: ['fontWeight', 'textSize', 'textColor'],
|
||||
defaultValue: {
|
||||
text: 'Container title',
|
||||
fontWeight: 'bold',
|
||||
textSize: 16,
|
||||
textColor: '#000',
|
||||
},
|
||||
},
|
||||
],
|
||||
events: {},
|
||||
styles: {
|
||||
backgroundColor: {
|
||||
type: 'color',
|
||||
displayName: 'Background color',
|
||||
displayName: 'Background',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#fff',
|
||||
},
|
||||
},
|
||||
headerBackgroundColor: {
|
||||
type: 'color',
|
||||
displayName: 'Header',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#fff',
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
validation: {
|
||||
schema: { type: 'number' },
|
||||
defaultValue: 80,
|
||||
},
|
||||
accordian: 'field',
|
||||
},
|
||||
borderRadius: {
|
||||
type: 'code',
|
||||
displayName: 'Border radius',
|
||||
|
|
@ -105,10 +150,12 @@ export const containerConfig = {
|
|||
events: [],
|
||||
styles: {
|
||||
backgroundColor: { value: '#fff' },
|
||||
headerBackgroundColor: { value: '#fff' },
|
||||
borderRadius: { value: '4' },
|
||||
borderColor: { value: '#fff' },
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
headerHeight: { value: '80' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -39,17 +39,42 @@ export const containerConfig = {
|
|||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
showHeader: {
|
||||
type: 'toggle',
|
||||
displayName: 'Show header',
|
||||
validation: {
|
||||
schema: { type: 'boolean' },
|
||||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
events: {},
|
||||
styles: {
|
||||
backgroundColor: {
|
||||
type: 'color',
|
||||
displayName: 'Background color',
|
||||
displayName: 'Background',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#fff',
|
||||
},
|
||||
},
|
||||
headerBackgroundColor: {
|
||||
type: 'color',
|
||||
displayName: 'Header',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#fff',
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
validation: {
|
||||
schema: { type: 'number' },
|
||||
defaultValue: 80,
|
||||
},
|
||||
accordian: 'field',
|
||||
},
|
||||
borderRadius: {
|
||||
type: 'code',
|
||||
displayName: 'Border radius',
|
||||
|
|
@ -70,6 +95,27 @@ export const containerConfig = {
|
|||
},
|
||||
},
|
||||
},
|
||||
|
||||
defaultChildren: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 20,
|
||||
left: 1,
|
||||
height: 40,
|
||||
},
|
||||
displayName: 'ContainerText',
|
||||
properties: ['text'],
|
||||
accessorKey: 'text',
|
||||
styles: ['fontWeight', 'textSize', 'textColor'],
|
||||
defaultValue: {
|
||||
text: 'Container title',
|
||||
fontWeight: 'bold',
|
||||
textSize: 16,
|
||||
textColor: '#000',
|
||||
},
|
||||
},
|
||||
],
|
||||
exposedVariables: {
|
||||
isVisible: true,
|
||||
isDisabled: false,
|
||||
|
|
@ -105,8 +151,10 @@ export const containerConfig = {
|
|||
events: [],
|
||||
styles: {
|
||||
backgroundColor: { value: '#fff' },
|
||||
headerBackgroundColor: { value: '#fff' },
|
||||
borderRadius: { value: '4' },
|
||||
borderColor: { value: '#fff' },
|
||||
headerHeight: { value: `80`, },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue