feat: Adds header for container widget (#11309)

This commit is contained in:
Nithin David Thomas 2024-12-04 03:50:32 -08:00 committed by GitHub
parent 00f4ee4370
commit 558d66fed0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 233 additions and 19 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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;
};

View file

@ -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' },
},
},
};

View file

@ -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>
);

View file

@ -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' },
},
},
};

View file

@ -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`, },
},
},
};