Enhance widget restriction logic for header and footer slots.

This commit is contained in:
Nakul Nagargade 2026-05-20 20:33:14 +05:30
parent d5287caa2c
commit 4ffced9f4e
5 changed files with 71 additions and 15 deletions

View file

@ -54,7 +54,11 @@ import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
import { useElementGuidelines } from './hooks/useElementGuidelines';
import { RIGHT_SIDE_BAR_TAB } from '../../RightSideBar/rightSidebarConstants';
import ConfigHandleButton from '@/_components/ConfigHandleButton';
import { RESTRICTED_WIDGETS_CONFIG } from '@/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig';
import { getDropTargetLabel } from '../appCanvasUtils';
import {
RESTRICTED_WIDGETS_CONFIG,
RESTRICTED_WIDGET_SLOTS_CONFIG,
} from '@/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig';
// Lazy load editor-only component to reduce viewer bundle size
const MentionComponentInChat = lazy(() => import('../ConfigHandle/MentionComponentInChat'));
@ -547,11 +551,13 @@ export default function Grid({ gridWidth, currentLayout, mainCanvasWidth }) {
const parentId = targetSlotId?.length > 36 ? targetSlotId.slice(0, 36) : targetSlotId;
const parentWidgetType = getComponentTypeFromId(parentId);
const parentSlotType = targetSlotId ? targetSlotId.split('-').pop() : undefined;
let restrictedWidgetsTobeDropped =
RESTRICTED_WIDGETS_CONFIG?.[parentWidgetType]?.filter((widgetType) =>
widgetsTypeToBeDropped.includes(widgetType)
) || [];
[
...(RESTRICTED_WIDGETS_CONFIG?.[parentWidgetType] || []),
...(['header', 'footer'].includes(parentSlotType) ? RESTRICTED_WIDGET_SLOTS_CONFIG : []),
].filter((widgetType) => widgetsTypeToBeDropped.includes(widgetType)) || [];
// Check nesting depth restrictions for all widget types in NESTING_LEVEL_LIMITS
let nestingDepthExceeded = false;
@ -602,7 +608,12 @@ export default function Grid({ gridWidth, currentLayout, mainCanvasWidth }) {
} else if (isParentModuleContainer) {
toast.error('Modules cannot be edited inside an app');
} else if (!isParentChangeAllowed) {
toast.error(`${restrictedWidgetsTobeDropped} is not compatible as a child component of ${parentWidgetType}`);
toast.error(
`${restrictedWidgetsTobeDropped} is not compatible as a child component of ${getDropTargetLabel(
parentWidgetType,
parentSlotType
)}`
);
}
}
@ -1069,7 +1080,12 @@ export default function Grid({ gridWidth, currentLayout, mainCanvasWidth }) {
if (isParentModuleContainer) {
toast.error('Modules cannot be edited inside an app');
} else if (!isModalToCanvas) {
toast.error(`${dragged.widgetType} is not compatible as a child component of ${target.widgetType}`);
toast.error(
`${dragged.widgetType} is not compatible as a child component of ${getDropTargetLabel(
target.widgetType,
target.slotType
)}`
);
}
}

View file

@ -728,7 +728,9 @@ export const updateDashedBordersOnDragResize = (targetId, moveableControlBoxClas
export const isDroppingRestrictedWidget = (target, dragged) => {
const restrictedWidgetsOnTarget = RESTRICTED_WIDGETS_CONFIG?.[target.widgetType] || [];
const restrictedWidgetsOnTargetSlot = RESTRICTED_WIDGET_SLOTS_CONFIG?.[target.slotType] || [];
const restrictedWidgetsOnTargetSlot = ['header', 'footer'].includes(target.slotType)
? RESTRICTED_WIDGET_SLOTS_CONFIG
: [];
const restrictedWidgets = [...restrictedWidgetsOnTarget, ...restrictedWidgetsOnTargetSlot];
return restrictedWidgets.includes(dragged.widgetType);

View file

@ -340,6 +340,9 @@ export const getParentWidgetFromId = (parentType, parentId) => {
return parentType;
};
export const getDropTargetLabel = (widgetType, slotType) =>
slotType === 'header' || slotType === 'footer' ? slotType : widgetType;
export const getTabId = (parentId) => {
return parentId.split('-').slice(0, -1).join('-');
};

View file

@ -12,7 +12,31 @@ export const RESTRICTED_WIDGETS_CONFIG = {
Table: ['Kanban'],
};
export const RESTRICTED_WIDGET_SLOTS_CONFIG = {
header: ['Calendar', 'Kanban', 'Table', 'Listview', 'Container', 'Accordion'],
footer: ['Calendar', 'Kanban', 'Table', 'Listview', 'Container', 'Accordion'],
};
export const RESTRICTED_WIDGET_SLOTS_CONFIG = [
'Form',
'Container',
'Listview',
'Tabs',
'Kanban',
'Calendar',
'Chart',
'Accordion',
'Map',
'IFrame',
'KeyValuePair',
'RichTextEditor',
'Timeline',
'JSONExplorer',
'JSONEditor',
'Html',
'CodeEditor',
'Chat',
'TreeSelect',
'PDF',
'Camera',
'QrScanner',
'Text',
'TextArea',
'AudioRecorder',
'CustomComponent',
];

View file

@ -11,6 +11,7 @@ import { deepClone } from '@/_helpers/utilities/utils.helpers';
import { cloneDeep, merge, set as lodashSet, isEmpty } from 'lodash';
import {
computeComponentName,
getDropTargetLabel,
getAllChildComponents,
getParentWidgetFromId,
} from '@/AppBuilder/AppCanvas/appCanvasUtils';
@ -19,7 +20,10 @@ import { RIGHT_SIDE_BAR_TAB } from '@/AppBuilder/RightSideBar/rightSidebarConsta
import { DEFAULT_COMPONENT_STRUCTURE } from './resolvedSlice';
import { savePageChanges } from './pageMenuSlice';
import { toast } from 'react-hot-toast';
import { RESTRICTED_WIDGETS_CONFIG } from '@/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig';
import {
RESTRICTED_WIDGETS_CONFIG,
RESTRICTED_WIDGET_SLOTS_CONFIG,
} from '@/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig';
import moment from 'moment';
import { getDateTimeFormat } from '@/_helpers/appUtils';
import { findHighestLevelofSelection } from '@/AppBuilder/AppCanvas/Grid/gridUtils';
@ -1225,10 +1229,16 @@ export const createComponentsSlice = (set, get) => ({
const transformedParentId = parentId?.length > 36 ? parentId.slice(0, 36) : parentId;
let parentType = getComponentTypeFromId(transformedParentId, moduleId);
const parentWidget = getParentWidgetFromId(parentType, parentId);
const restrictedWidgets = RESTRICTED_WIDGETS_CONFIG?.[parentWidget] || [];
const parentSlotType = parentId ? parentId.split('-').pop() : undefined;
const restrictedWidgets = [
...(RESTRICTED_WIDGETS_CONFIG?.[parentWidget] || []),
...(['header', 'footer'].includes(parentSlotType) ? RESTRICTED_WIDGET_SLOTS_CONFIG : []),
];
const isParentChangeAllowed = !restrictedWidgets.includes(currentWidget);
if (!isParentChangeAllowed) {
toast.error(`${currentWidget} is not compatible as a child component of ${parentWidget}`);
toast.error(
`${currentWidget} is not compatible as a child component of ${getDropTargetLabel(parentWidget, parentSlotType)}`
);
return false;
}
@ -1280,7 +1290,8 @@ export const createComponentsSlice = (set, get) => ({
moduleId
) === false
) {
return false;
resolve(false);
return;
}
const newComponents = buildComponentDefinition(componentDefinitions, moduleId);