mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 00:48:25 +00:00
Merge pull request #12322 from ToolJet/feat/resizable-slots-form
feat: Makes header and footer resizable
This commit is contained in:
commit
23ca209a16
19 changed files with 818 additions and 752 deletions
|
|
@ -22,6 +22,8 @@ import {
|
|||
handleActivateTargets,
|
||||
handleDeactivateTargets,
|
||||
handleActivateNonDraggingComponents,
|
||||
computeScrollDelta,
|
||||
computeScrollDeltaOnDrag,
|
||||
} from './gridUtils';
|
||||
import { dragContextBuilder, getAdjustedDropPosition } from './helpers/dragEnd';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
|
|
@ -56,6 +58,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const canvasWidth = NO_OF_GRIDS * gridWidth;
|
||||
const getHoveredComponentForGrid = useStore((state) => state.getHoveredComponentForGrid, shallow);
|
||||
const getResolvedComponent = useStore((state) => state.getResolvedComponent, shallow);
|
||||
const updateContainerAutoHeight = useStore((state) => state.updateContainerAutoHeight, shallow);
|
||||
const [canvasBounds, setCanvasBounds] = useState(CANVAS_BOUNDS);
|
||||
const draggingComponentId = useStore((state) => state.draggingComponentId, shallow);
|
||||
const resizingComponentId = useGridStore((state) => state.resizingComponentId, shallow);
|
||||
|
|
@ -345,6 +348,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const handleDragEnd = useCallback(
|
||||
(boxPositions) => {
|
||||
let newParent = null;
|
||||
let oldParent = null;
|
||||
const updatedLayouts = boxPositions.reduce((layouts, { id, x, y, parent }) => {
|
||||
const currentWidget = boxList.find((box) => box.id === id);
|
||||
const containerWidth = parent ? useGridStore.getState().subContainerWidths[parent] : gridWidth;
|
||||
|
|
@ -389,7 +393,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
}
|
||||
}
|
||||
newParent = parent ? parent : null;
|
||||
|
||||
oldParent = currentWidget.component?.parent;
|
||||
layouts[id] = {
|
||||
width: _width,
|
||||
height: _height,
|
||||
|
|
@ -400,6 +404,11 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
return layouts;
|
||||
}, {});
|
||||
setComponentLayout(updatedLayouts, newParent, undefined, { updateParent: true });
|
||||
|
||||
// const currentWidget = boxList.find((box) => box.id === id);
|
||||
updateContainerAutoHeight(newParent);
|
||||
updateContainerAutoHeight(oldParent);
|
||||
|
||||
toggleCanvasUpdater();
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -870,20 +879,19 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
|
||||
const targetSlotId = target?.slotId;
|
||||
const targetGridWidth = useGridStore.getState().subContainerWidths[targetSlotId] || gridWidth;
|
||||
|
||||
// const restrictedWidgets = RESTRICTED_WIDGETS_CONFIG?.[source.widgetType] || [];
|
||||
// const draggedWidgetType = dragged.widgetType;
|
||||
const isParentChangeAllowed = dragContext.isDroppable;
|
||||
|
||||
// Compute new position
|
||||
let { left, top } = getAdjustedDropPosition(e, target, isParentChangeAllowed, targetGridWidth, dragged);
|
||||
|
||||
const isModalToCanvas = source.isModal && target.slotId === 'real-canvas';
|
||||
let scrollDelta = computeScrollDelta({ source });
|
||||
|
||||
if (isParentChangeAllowed && !isModalToCanvas) {
|
||||
const parent = target.slotId === 'real-canvas' ? null : target.slotId;
|
||||
// Special case for Modal; If source widget is modal, prevent drops to canvas
|
||||
handleDragEnd([{ id: e.target.id, x: left, y: top, parent }]);
|
||||
const parent = target.slotId === 'real-canvas' ? null : target.slotId;
|
||||
|
||||
handleDragEnd([{ id: e.target.id, x: left, y: top + scrollDelta, parent }]);
|
||||
} else {
|
||||
const sourcegridWidth = useGridStore.getState().subContainerWidths[source.slotId] || gridWidth;
|
||||
|
||||
|
|
@ -892,9 +900,8 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
!isModalToCanvas ??
|
||||
toast.error(`${dragged.widgetType} is not compatible as a child component of ${target.widgetType}`);
|
||||
}
|
||||
|
||||
// Apply transform for smooth transition
|
||||
e.target.style.transform = `translate(${left}px, ${top}px)`;
|
||||
e.target.style.transform = `translate(${left}px, ${top + scrollDelta}px)`;
|
||||
|
||||
// Force reordering of conatiner if the parent has not changed
|
||||
const newParentId = target.slotId === 'real-canvas' ? 'canvas' : target.slotId;
|
||||
|
|
@ -962,12 +969,6 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
setCanvasBounds({ ...relativePosition });
|
||||
}
|
||||
|
||||
e.target.style.transform = `translate(${left}px, ${top}px)`;
|
||||
e.target.setAttribute(
|
||||
'widget-pos2',
|
||||
`translate: ${e.translate[0]} | Round: ${Math.round(e.translate[0] / gridWidth) * gridWidth} | ${gridWidth}`
|
||||
);
|
||||
|
||||
// This block is to show grid lines on the canvas when the dragged element is over a new canvas
|
||||
if (document.elementFromPoint(e.clientX, e.clientY)) {
|
||||
const targetElems = document.elementsFromPoint(e.clientX, e.clientY);
|
||||
|
|
@ -995,6 +996,17 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
handleActivateTargets(newParentId);
|
||||
}
|
||||
}
|
||||
|
||||
// Build the drag context from the event
|
||||
const source = { slotId: oldParentId };
|
||||
let scrollDelta = computeScrollDeltaOnDrag({ source });
|
||||
|
||||
e.target.style.transform = `translate(${left}px, ${top - scrollDelta}px)`;
|
||||
e.target.setAttribute(
|
||||
'widget-pos2',
|
||||
`translate: ${e.translate[0]} | Round: ${Math.round(e.translate[0] / gridWidth) * gridWidth} | ${gridWidth}`
|
||||
);
|
||||
|
||||
// Postion ghost element exactly as same at dragged element
|
||||
if (document.getElementById(`moveable-drag-ghost`)) {
|
||||
document.getElementById(`moveable-drag-ghost`).style.transform = `translate(${left}px, ${top}px)`;
|
||||
|
|
@ -1084,6 +1096,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
}
|
||||
}}
|
||||
snapGridAll={true}
|
||||
scrollable={true}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -415,6 +415,20 @@ export function hideGridLines() {
|
|||
document.getElementById('real-canvas')?.classList.add('hide-grid');
|
||||
}
|
||||
|
||||
export function showGridLinesOnSlot(slotId) {
|
||||
var canvasElm = document.getElementById(`canvas-${slotId}`);
|
||||
|
||||
canvasElm.classList.remove('hide-grid');
|
||||
canvasElm.classList.add('show-grid');
|
||||
}
|
||||
|
||||
export function hideGridLinesOnSlot(slotId) {
|
||||
var canvasElm = document.getElementById(`canvas-${slotId}`);
|
||||
|
||||
canvasElm.classList.remove('show-grid');
|
||||
canvasElm.classList.add('hide-grid');
|
||||
}
|
||||
|
||||
// Track previously active elements for efficient cleanup
|
||||
let previousActiveWidgets = null;
|
||||
let previousActiveCanvas = null;
|
||||
|
|
@ -488,3 +502,18 @@ export const handleDeactivateTargets = () => {
|
|||
component.classList.remove('non-dragging-component');
|
||||
});
|
||||
};
|
||||
export const computeScrollDelta = ({ source }) => {
|
||||
// Only need to calculate scroll delta when moving from a sub-container
|
||||
if (source.slotId !== 'real-canvas') {
|
||||
const subContainerWrap = document
|
||||
.querySelector(`#canvas-${source.slotId}`)
|
||||
?.closest('.sub-container-overflow-wrap');
|
||||
|
||||
return subContainerWrap?.scrollTop || 0;
|
||||
}
|
||||
|
||||
// Default case: No scroll adjustment needed
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const computeScrollDeltaOnDrag = computeScrollDelta;
|
||||
|
|
|
|||
|
|
@ -175,7 +175,6 @@ export class DragContext {
|
|||
|
||||
const restrictedWidgets = [...restrictedWidgetsOnTarget, ...restrictedWidgetsOnTargetSlot];
|
||||
return !restrictedWidgets.includes(dragged.widgetType);
|
||||
ß;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ const SHOULD_ADD_BOX_SHADOW_AND_VISIBILITY = [
|
|||
'Divider',
|
||||
'VerticalDivider',
|
||||
'Link',
|
||||
'Form',
|
||||
];
|
||||
|
||||
const RenderWidget = ({
|
||||
|
|
|
|||
|
|
@ -182,25 +182,6 @@ export const baseComponentProperties = (
|
|||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: `${i18next.t('widget.common.general', 'General')}`,
|
||||
isOpen: true,
|
||||
children: (
|
||||
<>
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
layoutPropertyChanged,
|
||||
dataQueries,
|
||||
'tooltip',
|
||||
'general',
|
||||
currentState,
|
||||
allComponents
|
||||
)}
|
||||
</>
|
||||
),
|
||||
});
|
||||
|
||||
items.push({
|
||||
title: `${i18next.t('widget.common.devices', 'Devices')}`,
|
||||
isOpen: true,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ export const containerConfig = {
|
|||
displayName: 'Container',
|
||||
description: 'Group components',
|
||||
defaultSize: {
|
||||
width: 5,
|
||||
width: 10,
|
||||
height: 200,
|
||||
},
|
||||
component: 'Container',
|
||||
|
|
@ -47,10 +47,16 @@ export const containerConfig = {
|
|||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
},
|
||||
defaultChildren: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
slotName: 'header',
|
||||
layout: {
|
||||
top: 20,
|
||||
left: 1,
|
||||
|
|
@ -98,15 +104,6 @@ export const containerConfig = {
|
|||
},
|
||||
accordian: 'container',
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Height',
|
||||
validation: {
|
||||
schema: { type: 'number' },
|
||||
defaultValue: 80,
|
||||
},
|
||||
accordian: 'header',
|
||||
},
|
||||
borderRadius: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Border',
|
||||
|
|
@ -158,6 +155,7 @@ export const containerConfig = {
|
|||
loadingState: { value: `{{false}}` },
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
headerHeight: { value: `{{80}}` },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export const formConfig = {
|
|||
description: 'Wrapper for multiple components',
|
||||
defaultSize: {
|
||||
width: 13,
|
||||
height: 480,
|
||||
height: 450,
|
||||
},
|
||||
defaultChildren: [
|
||||
{
|
||||
|
|
@ -19,8 +19,8 @@ export const formConfig = {
|
|||
accessorKey: 'text',
|
||||
styles: ['fontWeight', 'textSize', 'textColor'],
|
||||
defaultValue: {
|
||||
text: 'Form title',
|
||||
textSize: 20,
|
||||
text: 'Form',
|
||||
textSize: 16,
|
||||
textColor: '#000',
|
||||
},
|
||||
},
|
||||
|
|
@ -34,203 +34,68 @@ export const formConfig = {
|
|||
},
|
||||
properties: ['text'],
|
||||
defaultValue: {
|
||||
text: 'Button2',
|
||||
text: 'Submit',
|
||||
padding: 'none',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 40,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 17,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'User Details',
|
||||
fontWeight: 'bold',
|
||||
textSize: 18,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 90,
|
||||
left: 10,
|
||||
height: 30,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'Name',
|
||||
fontWeight: 'normal',
|
||||
textSize: 14,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 160,
|
||||
left: 10,
|
||||
height: 30,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'Age',
|
||||
fontWeight: 'normal',
|
||||
textSize: 14,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'TextInput',
|
||||
layout: {
|
||||
top: 120,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 25,
|
||||
top: 20,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
placeholder: 'Enter your name',
|
||||
label: '',
|
||||
label: 'Name',
|
||||
width: '{{60}}',
|
||||
direction: 'left',
|
||||
alignment: 'side',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'NumberInput',
|
||||
layout: {
|
||||
top: 190,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 25,
|
||||
top: 80,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['value', 'label'],
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
value: 24,
|
||||
label: '',
|
||||
placeholder: 'Age',
|
||||
label: 'Age',
|
||||
width: '{{60}}',
|
||||
direction: 'left',
|
||||
alignment: 'side',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
componentName: 'TextInput',
|
||||
layout: {
|
||||
top: 240,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 10,
|
||||
top: 140,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['text'],
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
text: 'Submit',
|
||||
placeholder: 'Tomy',
|
||||
label: 'Pet name',
|
||||
width: '{{60}}',
|
||||
alignment: 'side',
|
||||
direction: 'left',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -276,6 +141,24 @@ export const formConfig = {
|
|||
},
|
||||
showHeader: { type: 'toggle', displayName: 'Header' },
|
||||
showFooter: { type: 'toggle', displayName: 'Footer' },
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
canvasHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Canvas height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
footerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Footer height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
visibility: {
|
||||
type: 'toggle',
|
||||
displayName: 'Visibility',
|
||||
|
|
@ -294,6 +177,13 @@ export const formConfig = {
|
|||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
type: 'code',
|
||||
displayName: 'Tooltip',
|
||||
validation: { schema: { type: 'string' } },
|
||||
section: 'additionalActions',
|
||||
placeholder: 'Enter tooltip text',
|
||||
},
|
||||
},
|
||||
events: {
|
||||
onSubmit: { displayName: 'On submit' },
|
||||
|
|
@ -316,22 +206,6 @@ export const formConfig = {
|
|||
defaultValue: '#ffffffff',
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'code',
|
||||
displayName: 'Header height',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '80px',
|
||||
},
|
||||
},
|
||||
footerHeight: {
|
||||
type: 'code',
|
||||
displayName: 'Footer height',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '80px',
|
||||
},
|
||||
},
|
||||
backgroundColor: {
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Background color',
|
||||
|
|
@ -403,18 +277,18 @@ export const formConfig = {
|
|||
value:
|
||||
"{{ {title: 'User registration form', properties: {firstname: {type: 'textinput',value: 'Maria',label:'First name', validation:{maxLength:6}, styles: {backgroundColor: '#f6f5ff',textColor: 'black'},},lastname:{type: 'textinput',value: 'Doe', label:'Last name', styles: {backgroundColor: '#f6f5ff',textColor: 'black'},},age:{type:'number', label:'Age'},}, submitButton: {value: 'Submit', styles: {backgroundColor: '#3a433b',borderColor:'#595959'}}} }}",
|
||||
},
|
||||
showHeader: { value: '{{false}}' },
|
||||
showFooter: { value: '{{false}}' },
|
||||
showHeader: { value: '{{true}}' },
|
||||
showFooter: { value: '{{true}}' },
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
headerHeight: { value: 60 },
|
||||
footerHeight: { value: 60 },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
backgroundColor: { value: '#fff' },
|
||||
borderRadius: { value: '0' },
|
||||
borderColor: { value: '#fff' },
|
||||
headerHeight: { value: '60px' },
|
||||
footerHeight: { value: '60px' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ export const Container = ({
|
|||
shallow
|
||||
);
|
||||
|
||||
const { borderRadius, borderColor, boxShadow, headerHeight = 80 } = styles;
|
||||
const { borderRadius, borderColor, boxShadow } = styles;
|
||||
const { headerHeight = 80 } = properties;
|
||||
const contentBgColor = useMemo(() => {
|
||||
return {
|
||||
backgroundColor:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Container as SubContainer } from '@/AppBuilder/AppCanvas/Container';
|
||||
import { showGridLinesOnSlot, hideGridLinesOnSlot } from '@/AppBuilder/AppCanvas/Grid/gridUtils';
|
||||
import { useResizable } from '@/AppBuilder/_hooks/useMoveable';
|
||||
|
||||
export const HorizontalSlot = React.memo(
|
||||
({
|
||||
id,
|
||||
height = 0,
|
||||
width,
|
||||
darkMode,
|
||||
isDisabled,
|
||||
isActive,
|
||||
slotName = 'header', // 'header' or 'footer'
|
||||
slotStyle = {},
|
||||
onResize,
|
||||
isEditing,
|
||||
maxHeight,
|
||||
}) => {
|
||||
const parsedHeight = parseInt(height, 10);
|
||||
|
||||
const { getRootProps, getHandleProps, getResizeState } = useResizable({
|
||||
initialHeight: parsedHeight,
|
||||
initialWidth: '100%', // Now respects parent's width
|
||||
minHeight: 10,
|
||||
maxHeight: maxHeight || 400,
|
||||
maxWidth: '100%',
|
||||
stepHeight: 10, // Height will change in steps of 10px
|
||||
onResize: () => {},
|
||||
onDragEnd: (values) => {
|
||||
onResize(values);
|
||||
},
|
||||
isReverseVerticalDrag: slotName === 'footer', // Reverse dragging for Footer
|
||||
});
|
||||
|
||||
const { height: resizedHeight, isDragging } = getResizeState();
|
||||
|
||||
useEffect(() => {
|
||||
if (isDragging) {
|
||||
showGridLinesOnSlot(id);
|
||||
} else {
|
||||
hideGridLinesOnSlot(id);
|
||||
}
|
||||
}, [isDragging, id]);
|
||||
|
||||
const canvasHeight = parseInt(resizedHeight, 10) / 10;
|
||||
|
||||
const resizeStyle = {
|
||||
backgroundColor: darkMode ? '#1F2837' : '#fff',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`jet-form-${slotName} wj-form-${slotName}`} style={slotStyle}>
|
||||
<div
|
||||
className={`resizable-slot only-${slotName} ${isActive ? 'active' : ''} ${isEditing && 'is-editing'} ${
|
||||
isDragging ? 'dragging' : ''
|
||||
}`}
|
||||
{...getRootProps()}
|
||||
>
|
||||
<SubContainer
|
||||
id={id}
|
||||
canvasHeight={canvasHeight}
|
||||
canvasWidth={width}
|
||||
allowContainerSelect={false}
|
||||
darkMode={darkMode}
|
||||
styles={{
|
||||
margin: 0,
|
||||
backgroundColor: 'transparent',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
componentType="Form"
|
||||
/>
|
||||
{isEditing && <div className="resize-handle" {...getHandleProps()} style={resizeStyle} />}
|
||||
</div>
|
||||
|
||||
{isDisabled && (
|
||||
<div
|
||||
id={`${id}-disabled`}
|
||||
className="tj-form-disabled-overlay"
|
||||
style={{ height: resizedHeight || '100%' }}
|
||||
onClick={() => {}}
|
||||
onDrop={(e) => e.stopPropagation()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
@ -2,7 +2,7 @@ import React, { useRef, useState, useEffect } from 'react';
|
|||
import { Container as SubContainer } from '@/AppBuilder/AppCanvas/Container';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import _, { debounce, omit } from 'lodash';
|
||||
import { generateUIComponents } from './FormUtils';
|
||||
import { generateUIComponents, getBodyHeight } from './FormUtils';
|
||||
import { useMounted } from '@/_hooks/use-mount';
|
||||
import { onComponentClick, removeFunctionObjects } from '@/_helpers/appUtils';
|
||||
import { deepClone } from '@/_helpers/utilities/utils.helpers';
|
||||
|
|
@ -14,12 +14,10 @@ import {
|
|||
CONTAINER_FORM_CANVAS_PADDING,
|
||||
SUBCONTAINER_CANVAS_BORDER_WIDTH,
|
||||
} from '@/AppBuilder/AppCanvas/appCanvasConstants';
|
||||
import './form.scss';
|
||||
import { HorizontalSlot } from './Components/HorizontalSlot';
|
||||
import { useActiveSlot } from '@/AppBuilder/_hooks/useActiveSlot';
|
||||
|
||||
const getCanvasHeight = (height) => {
|
||||
const parsedHeight = height.includes('px') ? parseInt(height, 10) : height;
|
||||
return Math.ceil(parsedHeight);
|
||||
};
|
||||
import './form.scss';
|
||||
|
||||
export const Form = function Form(props) {
|
||||
const {
|
||||
|
|
@ -35,26 +33,19 @@ export const Form = function Form(props) {
|
|||
properties,
|
||||
resetComponent = () => {},
|
||||
dataCy,
|
||||
onComponentClick,
|
||||
} = props;
|
||||
const childComponents = useStore((state) => state.getChildComponents(id), shallow);
|
||||
const {
|
||||
borderRadius,
|
||||
borderColor,
|
||||
boxShadow,
|
||||
headerHeight,
|
||||
footerHeight,
|
||||
footerBackgroundColor,
|
||||
headerBackgroundColor,
|
||||
} = styles;
|
||||
const { borderRadius, borderColor, boxShadow, footerBackgroundColor, headerBackgroundColor } = styles;
|
||||
const {
|
||||
buttonToSubmit,
|
||||
loadingState,
|
||||
advanced,
|
||||
JSONSchema,
|
||||
showHeader = false,
|
||||
showFooter = false,
|
||||
visibility,
|
||||
disabledState,
|
||||
headerHeight = 80,
|
||||
footerHeight = 80,
|
||||
canvasHeight,
|
||||
} = properties;
|
||||
const { isDisabled, isVisible, isLoading } = useExposeState(
|
||||
properties.loadingState,
|
||||
|
|
@ -65,6 +56,10 @@ export const Form = function Form(props) {
|
|||
);
|
||||
const backgroundColor =
|
||||
['#fff', '#ffffffff'].includes(styles.backgroundColor) && darkMode ? '#232E3C' : styles.backgroundColor;
|
||||
|
||||
const computedFormBodyHeight = getBodyHeight(height, showHeader, showFooter, headerHeight, footerHeight);
|
||||
const computedBorderRadius = `${borderRadius ? parseFloat(borderRadius) : 0}px`;
|
||||
|
||||
const computedStyles = {
|
||||
backgroundColor,
|
||||
borderRadius: borderRadius ? parseFloat(borderRadius) : 0,
|
||||
|
|
@ -74,16 +69,7 @@ export const Form = function Form(props) {
|
|||
position: 'relative',
|
||||
boxShadow,
|
||||
flexDirection: 'column',
|
||||
};
|
||||
|
||||
const formHeader = {
|
||||
flexShrink: 0,
|
||||
paddingBottom: '3px',
|
||||
paddingTop: '7px',
|
||||
paddingLeft: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
paddingRight: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(headerBackgroundColor) && darkMode ? '#1F2837' : headerBackgroundColor,
|
||||
clipPath: `inset(0 round ${computedBorderRadius})`,
|
||||
};
|
||||
|
||||
const formContent = {
|
||||
|
|
@ -96,13 +82,6 @@ export const Form = function Form(props) {
|
|||
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);
|
||||
const childDataRef = useRef({});
|
||||
|
||||
|
|
@ -110,8 +89,6 @@ export const Form = function Form(props) {
|
|||
const [isValid, setValidation] = useState(true);
|
||||
const [uiComponents, setUIComponents] = useState([]);
|
||||
const mounted = useMounted();
|
||||
const canvasHeaderHeight = getCanvasHeight(headerHeight) / 10;
|
||||
const canvasFooterHeight = getCanvasHeight(footerHeight) / 10;
|
||||
|
||||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
|
|
@ -287,9 +264,61 @@ export const Form = function Form(props) {
|
|||
setChildrenData(childDataRef.current);
|
||||
};
|
||||
|
||||
const mode = useStore((state) => state.currentMode, shallow);
|
||||
const isEditing = mode === 'edit';
|
||||
|
||||
const activeSlot = useActiveSlot(isEditing ? id : null); // Track the active slot for this widget
|
||||
const setComponentProperty = useStore((state) => state.setComponentProperty, shallow);
|
||||
// const updateContainerAutoHeight = useStore((state) => state.updateContainerAutoHeight);
|
||||
|
||||
const updateHeaderSizeInStore = ({ newHeight }) => {
|
||||
const _height = parseInt(newHeight, 10);
|
||||
setComponentProperty(id, `headerHeight`, _height, 'properties', 'value', false);
|
||||
};
|
||||
|
||||
const updateFooterSizeInStore = ({ newHeight }) => {
|
||||
const _height = parseInt(newHeight, 10);
|
||||
setComponentProperty(id, `footerHeight`, _height, 'properties', 'value', false);
|
||||
};
|
||||
|
||||
const [canHeight, setCanHeight] = useState('100%');
|
||||
useEffect(() => {
|
||||
// const newHeight = parseInt(height, 10) - 14;
|
||||
|
||||
// const autoCanvasHeight = document.querySelector(`#canvas-${id}`)?.scrollHeight;
|
||||
const wrapHeight = parseInt(computedFormBodyHeight, 10);
|
||||
// Set height to the larger value between computed body height and canvas scroll height
|
||||
const maxHeight = Math.max(wrapHeight, canvasHeight || 10);
|
||||
|
||||
const roundedHeight = Math.round(maxHeight / 10) * 10;
|
||||
setCanHeight(`${roundedHeight}px`);
|
||||
}, [computedFormBodyHeight, canvasHeight]);
|
||||
const headerMaxHeight = parseInt(height, 10) - parseInt(footerHeight, 10) - 100 - 10;
|
||||
const footerMaxHeight = parseInt(height, 10) - parseInt(headerHeight, 10) - 100 - 10;
|
||||
const formFooter = {
|
||||
flexShrink: 0,
|
||||
paddingTop: '3px',
|
||||
paddingBottom: '7px',
|
||||
paddingLeft: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
paddingRight: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
maxHeight: `${footerMaxHeight}px`,
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(footerBackgroundColor) && darkMode ? '#1F2837' : footerBackgroundColor,
|
||||
};
|
||||
const formHeader = {
|
||||
flexShrink: 0,
|
||||
paddingBottom: '3px',
|
||||
paddingTop: '7px',
|
||||
paddingLeft: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
paddingRight: `${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
maxHeight: `${headerMaxHeight}px`,
|
||||
backgroundColor:
|
||||
['#fff', '#ffffffff'].includes(headerBackgroundColor) && darkMode ? '#1F2837' : headerBackgroundColor,
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
className={`jet-container ${advanced && 'jet-container-json-form'}`}
|
||||
className={`jet-container jet-form-widget ${advanced && 'jet-container-json-form'}`}
|
||||
id={id}
|
||||
data-cy={dataCy}
|
||||
ref={parentRef}
|
||||
|
|
@ -300,31 +329,21 @@ export const Form = function Form(props) {
|
|||
}} //Hack, should find a better solution - to prevent losing z index+1 when container element is clicked
|
||||
>
|
||||
{!advanced && showHeader && (
|
||||
<div style={formHeader} className="wj-form-header">
|
||||
<SubContainer
|
||||
id={`${id}-header`}
|
||||
canvasHeight={canvasHeaderHeight}
|
||||
canvasWidth={width}
|
||||
allowContainerSelect={false}
|
||||
darkMode={darkMode}
|
||||
styles={{
|
||||
backgroundColor: 'transparent',
|
||||
height: headerHeight,
|
||||
}}
|
||||
componentType="Form"
|
||||
/>
|
||||
{isDisabled && (
|
||||
<div
|
||||
id={`${id}-header-disabled`}
|
||||
className="tj-form-disabled-overlay"
|
||||
style={{ height: headerHeight || '100%' }}
|
||||
onClick={() => {}}
|
||||
onDrop={(e) => e.stopPropagation()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<HorizontalSlot
|
||||
slotName="header"
|
||||
slotStyle={formHeader}
|
||||
isEditing={isEditing}
|
||||
id={`${id}-header`}
|
||||
height={headerHeight}
|
||||
width={width}
|
||||
darkMode={darkMode}
|
||||
isDisabled={isDisabled}
|
||||
isActive={activeSlot === `${id}-header`}
|
||||
onResize={updateHeaderSizeInStore}
|
||||
/>
|
||||
)}
|
||||
<div className="jet-form-body" style={formContent}>
|
||||
|
||||
<div className="jet-form-body sub-container-overflow-wrap" style={formContent}>
|
||||
{isLoading ? (
|
||||
<div className="p-2 tw-flex tw-items-center tw-justify-center" style={{ margin: '0px auto' }}>
|
||||
<div className="spinner-border" role="status"></div>
|
||||
|
|
@ -332,14 +351,17 @@ export const Form = function Form(props) {
|
|||
) : (
|
||||
<fieldset disabled={isDisabled} style={{ width: '100%' }}>
|
||||
{!advanced && (
|
||||
<div className={'json-form-wrapper-disabled'} style={{ width: '100%', height: '100%' }}>
|
||||
<div className={'json-form-wrapper-disabled'} style={{ width: '100%', height: canHeight || '100%' }}>
|
||||
<SubContainer
|
||||
id={id}
|
||||
canvasHeight={computedStyles.height}
|
||||
canvasHeight={parseInt(computedFormBodyHeight, 10)}
|
||||
canvasWidth={width}
|
||||
onOptionChange={onOptionChange}
|
||||
onOptionsChange={onOptionsChange}
|
||||
styles={{ backgroundColor: computedStyles.backgroundColor }}
|
||||
styles={{
|
||||
backgroundColor: computedStyles.backgroundColor,
|
||||
height: canHeight,
|
||||
}}
|
||||
darkMode={darkMode}
|
||||
componentType="Form"
|
||||
/>
|
||||
|
|
@ -382,30 +404,18 @@ export const Form = function Form(props) {
|
|||
)}
|
||||
</div>
|
||||
{!advanced && showFooter && (
|
||||
<div className="jet-form-footer wj-form-footer" style={formFooter}>
|
||||
<SubContainer
|
||||
id={`${id}-footer`}
|
||||
canvasHeight={canvasFooterHeight}
|
||||
canvasWidth={width}
|
||||
allowContainerSelect={false}
|
||||
darkMode={darkMode}
|
||||
styles={{
|
||||
margin: 0,
|
||||
backgroundColor: 'transparent',
|
||||
height: footerHeight,
|
||||
}}
|
||||
componentType="Form"
|
||||
/>
|
||||
{isDisabled && (
|
||||
<div
|
||||
id={`${id}-footer-disabled`}
|
||||
className="tj-form-disabled-overlay"
|
||||
style={{ height: footerHeight || '100%' }}
|
||||
onClick={() => {}}
|
||||
onDrop={(e) => e.stopPropagation()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<HorizontalSlot
|
||||
slotName="footer"
|
||||
slotStyle={formFooter}
|
||||
isEditing={isEditing}
|
||||
id={`${id}-footer`}
|
||||
height={footerHeight}
|
||||
width={width}
|
||||
darkMode={darkMode}
|
||||
isDisabled={isDisabled}
|
||||
onResize={updateFooterSizeInStore}
|
||||
isActive={activeSlot === `${id}-footer`}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -533,3 +533,22 @@ const validBooleanChecker = (input) => {
|
|||
if (/^(true|false)$/i.test(input) == true) return JSON.parse(input);
|
||||
return true;
|
||||
};
|
||||
|
||||
export const getBodyHeight = (height, showHeader, showFooter, headerHeight = 60, footerHeight = 60) => {
|
||||
let modalHeight = height ? parseInt(height, 10) : 0;
|
||||
let parsedHeaderHeight = showHeader ? parseInt(headerHeight, 10) : 0;
|
||||
let parsedFooterHeight = showFooter ? parseInt(footerHeight, 10) : 0;
|
||||
|
||||
if (showHeader) {
|
||||
// 10 is header padding
|
||||
modalHeight = modalHeight - parsedHeaderHeight - 10;
|
||||
}
|
||||
if (showFooter) {
|
||||
// 14 is footer padding
|
||||
modalHeight = modalHeight - parsedFooterHeight - 14;
|
||||
}
|
||||
|
||||
const rounded = Math.ceil(modalHeight / 10) * 10;
|
||||
|
||||
return `${Math.max(rounded - 20, 40)}px`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
.jet-form-widget {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.wj-form-header {
|
||||
position: relative;
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: -7px;
|
||||
right: -7px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
height: 1px;
|
||||
background-color: var(--border-weak);
|
||||
}
|
||||
|
|
@ -17,8 +23,8 @@
|
|||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -7px;
|
||||
right: -7px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
height: 1px;
|
||||
background-color: var(--border-weak);
|
||||
}
|
||||
|
|
@ -39,6 +45,67 @@
|
|||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.resizable-slot {
|
||||
position: relative;
|
||||
height: auto;
|
||||
box-shadow: 0 0 0 1px transparent; /* Acts as a border */
|
||||
transition: box-shadow 0.15s ease-in-out;
|
||||
max-height: 100%;
|
||||
|
||||
&.is-editing:hover {
|
||||
box-shadow: 0 0 0 1px var(--border-weak);
|
||||
}
|
||||
|
||||
&.is-editing.active {
|
||||
box-shadow: 0 0 0 1px var(--border-accent-weak);
|
||||
}
|
||||
|
||||
&.is-editing.dragging {
|
||||
box-shadow: 0 0 0 1px var(--border-accent-strong);
|
||||
}
|
||||
|
||||
.resize-handle {
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
left: 50%; /* Center horizontally */
|
||||
transform: translateX(-50%); /* Ensure proper centering */
|
||||
width: 24px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: initial;
|
||||
border: 1px solid var(--background-accent-strong);
|
||||
cursor: ns-resize;
|
||||
z-index: 1;
|
||||
visibility: hidden;
|
||||
transition: visibility 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
&.active .resize-handle {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.only-bottom {
|
||||
}
|
||||
|
||||
.jet-form-header {
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
.jet-form-body {
|
||||
min-height: 100px;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.jet-form-footer {
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
.jet-form-footer .resize-handle {
|
||||
top: -4px;
|
||||
bottom: unset;
|
||||
}
|
||||
|
||||
.jet-container.jet-container-json-form {
|
||||
padding: 0px;
|
||||
|
||||
|
|
|
|||
84
frontend/src/AppBuilder/_hooks/useActiveSlot.js
Normal file
84
frontend/src/AppBuilder/_hooks/useActiveSlot.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
const useIsWidgetSelected = (id) => {
|
||||
// Get selected components from store using shallow comparison
|
||||
const selectedComponents = useStore((state) => state.selectedComponents, shallow);
|
||||
|
||||
// Check if the only selected component is the provided `id`
|
||||
return selectedComponents.length === 1 && selectedComponents[0] === id;
|
||||
};
|
||||
|
||||
export const useActiveSlot = (widgetId) => {
|
||||
const [activeSlot, setActiveSlot] = useState(''); // Default to widget ID
|
||||
const isSelected = useIsWidgetSelected(widgetId); // Check if widget is selected
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSelected) {
|
||||
setActiveSlot('');
|
||||
}
|
||||
}, [isSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleDoubleClick = (event) => {
|
||||
let target = event.target;
|
||||
|
||||
if (!widgetId) {
|
||||
setActiveSlot(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Traverse up to find a slot with an id
|
||||
while (target && target !== document.body) {
|
||||
if (target.id && target.id.startsWith('canvas-')) {
|
||||
const slotId = target.id.replace(/^canvas-/, ''); // ✅ Strip "canvas-"
|
||||
setActiveSlot(slotId);
|
||||
return;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
|
||||
// If no slot is found, reset to widget ID
|
||||
setActiveSlot(widgetId);
|
||||
};
|
||||
const handleSingleClick = (event) => {
|
||||
let target = event.target;
|
||||
|
||||
if (!widgetId) {
|
||||
setActiveSlot(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Traverse up to find a valid main slot (not header/footer)
|
||||
while (target && target !== document.body) {
|
||||
if (
|
||||
target.id &&
|
||||
target.id.startsWith('canvas-') &&
|
||||
!target.id.endsWith('-header') &&
|
||||
!target.id.endsWith('-footer')
|
||||
) {
|
||||
const slotId = target.id.replace(/^canvas-/, ''); // Strip "canvas-"
|
||||
setActiveSlot(slotId);
|
||||
return;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
|
||||
// If no main slot is found, fallback to widget ID
|
||||
setActiveSlot(widgetId);
|
||||
};
|
||||
|
||||
// Attach single click if the widget is selected, otherwise listen for double-click
|
||||
|
||||
document.addEventListener('dblclick', handleDoubleClick);
|
||||
document.addEventListener('click', handleSingleClick);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('dblclick', handleDoubleClick);
|
||||
document.removeEventListener('click', handleSingleClick);
|
||||
};
|
||||
}, [widgetId]); // Re-run when widgetId or selection state changes
|
||||
|
||||
return activeSlot;
|
||||
};
|
||||
135
frontend/src/AppBuilder/_hooks/useMoveable.js
Normal file
135
frontend/src/AppBuilder/_hooks/useMoveable.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import { useRef, useState } from 'react';
|
||||
|
||||
const defaultProps = {
|
||||
minHeight: 50,
|
||||
maxHeight: 600,
|
||||
minWidth: 50,
|
||||
maxWidth: 600,
|
||||
lockHorizontal: false,
|
||||
lockVertical: false,
|
||||
stepHeight: 10, // Default step size for height
|
||||
stepWidth: 10, // Default step size for width
|
||||
onResize: null,
|
||||
onDragStart: null,
|
||||
onDragEnd: null,
|
||||
isReverseVerticalDrag: false,
|
||||
};
|
||||
|
||||
export const useResizable = (options = {}) => {
|
||||
const props = { ...defaultProps, ...options };
|
||||
const parentRef = useRef(null);
|
||||
const [isDragging, setIsDragging] = useState(false); // ✅ Track dragging state
|
||||
|
||||
const [height, setHeight] = useState(
|
||||
typeof props.initialHeight === 'string' ? props.initialHeight : `${props.initialHeight || 200}px`
|
||||
);
|
||||
const [width, setWidth] = useState(
|
||||
typeof props.initialWidth === 'string' ? props.initialWidth : `${props.initialWidth || 200}px`
|
||||
);
|
||||
|
||||
const getRootProps = () => ({
|
||||
ref: parentRef,
|
||||
style: { height, width },
|
||||
});
|
||||
|
||||
const getResizeState = () => ({
|
||||
height,
|
||||
width,
|
||||
isDragging,
|
||||
});
|
||||
|
||||
const getHandleProps = () => {
|
||||
const handleMouseDown = (e) => {
|
||||
// Prevent right-click drag activation (button === 2)
|
||||
if (e.button === 2) return;
|
||||
|
||||
if (!parentRef.current) return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
const startHeight = parseInt(parentRef.current.clientHeight);
|
||||
const startWidth = parseInt(parentRef.current.clientWidth);
|
||||
const parentWidth = parentRef.current.parentElement ? parentRef.current.parentElement.clientWidth : startWidth;
|
||||
const startY = e.clientY;
|
||||
const startX = e.clientX;
|
||||
const isPercentage = typeof props.initialWidth === 'string' && props.initialWidth.includes('%');
|
||||
|
||||
setIsDragging(true); // ✅ Set dragging state to true
|
||||
|
||||
if (props.onDragStart) {
|
||||
props.onDragStart({ newHeight: startHeight, newWidth: startWidth });
|
||||
}
|
||||
|
||||
const handleMouseMove = (moveEvent) => {
|
||||
moveEvent.stopPropagation();
|
||||
moveEvent.preventDefault();
|
||||
let newHeight = startHeight;
|
||||
let newWidth = startWidth;
|
||||
|
||||
if (!props.lockVertical) {
|
||||
const deltaY = props.isReverseVerticalDrag ? startY - moveEvent.clientY : moveEvent.clientY - startY;
|
||||
newHeight = startHeight + deltaY;
|
||||
newHeight = Math.max(props.minHeight, Math.min(props.maxHeight, newHeight));
|
||||
newHeight = Math.round(newHeight / props.stepHeight) * props.stepHeight; // Snap to stepHeight
|
||||
}
|
||||
|
||||
if (!props.lockHorizontal) {
|
||||
newWidth = startWidth + (moveEvent.clientX - startX);
|
||||
newWidth = Math.max(props.minWidth, Math.min(props.maxWidth, newWidth));
|
||||
newWidth = Math.round(newWidth / props.stepWidth) * props.stepWidth; // Snap to stepWidth
|
||||
|
||||
if (isPercentage) {
|
||||
newWidth = (newWidth / parentWidth) * 100; // Convert to percentage
|
||||
newWidth = `${newWidth.toFixed(2)}%`;
|
||||
} else {
|
||||
newWidth = `${newWidth}px`;
|
||||
}
|
||||
}
|
||||
|
||||
setHeight(`${newHeight}px`);
|
||||
setWidth(newWidth);
|
||||
|
||||
if (parentRef.current) {
|
||||
parentRef.current.style.height = `${newHeight}px`;
|
||||
parentRef.current.style.width = newWidth;
|
||||
}
|
||||
|
||||
if (props.onResize) {
|
||||
props.onResize({
|
||||
newHeight,
|
||||
newWidth,
|
||||
heightDiff: newHeight - startHeight,
|
||||
widthDiff: isPercentage
|
||||
? parseInt(newWidth) - (startWidth / parentWidth) * 100
|
||||
: parseInt(newWidth) - startWidth,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
setIsDragging(false); // ✅ Set dragging state to false
|
||||
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
|
||||
if (props.onDragEnd) {
|
||||
// Get the updated height and width from the DOM instead of relying on state
|
||||
const finalHeight = parentRef.current ? parseInt(parentRef.current.clientHeight) : parseInt(height);
|
||||
const finalWidth = parentRef.current ? parseInt(parentRef.current.clientWidth) : parseInt(width);
|
||||
|
||||
props.onDragEnd({ newHeight: finalHeight, newWidth: finalWidth });
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
|
||||
return {
|
||||
onMouseDown: handleMouseDown,
|
||||
};
|
||||
};
|
||||
|
||||
return { rootRef: parentRef, getRootProps, getHandleProps, getResizeState };
|
||||
};
|
||||
|
||||
export default useResizable;
|
||||
|
|
@ -1896,4 +1896,28 @@ export const createComponentsSlice = (set, get) => ({
|
|||
state.modalsOpenOnCanvas = newModalOpenOnCanvas;
|
||||
});
|
||||
},
|
||||
updateContainerAutoHeight: (componentId) => {
|
||||
if (
|
||||
!componentId ||
|
||||
componentId === 'canvas' ||
|
||||
componentId.includes('-header') ||
|
||||
componentId.includes('-footer')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const { currentLayout, getCurrentPageComponents, setComponentProperty } = get();
|
||||
const allComponents = getCurrentPageComponents();
|
||||
|
||||
const childComponents = getAllChildComponents(allComponents, componentId);
|
||||
const maxHeight = Object.values(childComponents).reduce((max, component) => {
|
||||
const layout = component?.layouts?.[currentLayout];
|
||||
if (!layout) {
|
||||
return max;
|
||||
}
|
||||
const sum = layout.top + layout.height;
|
||||
return Math.max(max, sum);
|
||||
}, 0);
|
||||
|
||||
setComponentProperty(componentId, `canvasHeight`, maxHeight, 'properties', 'value', false);
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ export const containerConfig = {
|
|||
displayName: 'Container',
|
||||
description: 'Group components',
|
||||
defaultSize: {
|
||||
width: 5,
|
||||
width: 10,
|
||||
height: 200,
|
||||
},
|
||||
component: 'Container',
|
||||
|
|
@ -47,10 +47,16 @@ export const containerConfig = {
|
|||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
},
|
||||
defaultChildren: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
slotName: 'header',
|
||||
layout: {
|
||||
top: 20,
|
||||
left: 1,
|
||||
|
|
@ -98,15 +104,6 @@ export const containerConfig = {
|
|||
},
|
||||
accordian: 'container',
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Height',
|
||||
validation: {
|
||||
schema: { type: 'number' },
|
||||
defaultValue: 80,
|
||||
},
|
||||
accordian: 'header',
|
||||
},
|
||||
borderRadius: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Border',
|
||||
|
|
@ -158,6 +155,7 @@ export const containerConfig = {
|
|||
loadingState: { value: `{{false}}` },
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
headerHeight: { value: `{{80}}` },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export const formConfig = {
|
|||
description: 'Wrapper for multiple components',
|
||||
defaultSize: {
|
||||
width: 13,
|
||||
height: 480,
|
||||
height: 450,
|
||||
},
|
||||
defaultChildren: [
|
||||
{
|
||||
|
|
@ -19,8 +19,8 @@ export const formConfig = {
|
|||
accessorKey: 'text',
|
||||
styles: ['fontWeight', 'textSize', 'textColor'],
|
||||
defaultValue: {
|
||||
text: 'Form title',
|
||||
textSize: 20,
|
||||
text: 'Form',
|
||||
textSize: 16,
|
||||
textColor: '#000',
|
||||
},
|
||||
},
|
||||
|
|
@ -34,203 +34,68 @@ export const formConfig = {
|
|||
},
|
||||
properties: ['text'],
|
||||
defaultValue: {
|
||||
text: 'Button2',
|
||||
text: 'Submit',
|
||||
padding: 'none',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 40,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 17,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'User Details',
|
||||
fontWeight: 'bold',
|
||||
textSize: 18,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 90,
|
||||
left: 10,
|
||||
height: 30,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'Name',
|
||||
fontWeight: 'normal',
|
||||
textSize: 14,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 160,
|
||||
left: 10,
|
||||
height: 30,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'Age',
|
||||
fontWeight: 'normal',
|
||||
textSize: 14,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'TextInput',
|
||||
layout: {
|
||||
top: 120,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 25,
|
||||
top: 20,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
placeholder: 'Enter your name',
|
||||
label: '',
|
||||
label: 'Name',
|
||||
width: '{{60}}',
|
||||
direction: 'left',
|
||||
alignment: 'side',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'NumberInput',
|
||||
layout: {
|
||||
top: 190,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 25,
|
||||
top: 80,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['value', 'label'],
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
value: 24,
|
||||
label: '',
|
||||
placeholder: 'Age',
|
||||
label: 'Age',
|
||||
width: '{{60}}',
|
||||
direction: 'left',
|
||||
alignment: 'side',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
componentName: 'TextInput',
|
||||
layout: {
|
||||
top: 240,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 10,
|
||||
top: 140,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['text'],
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
text: 'Submit',
|
||||
placeholder: 'Tomy',
|
||||
label: 'Pet name',
|
||||
width: '{{60}}',
|
||||
alignment: 'side',
|
||||
direction: 'left',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -276,6 +141,24 @@ export const formConfig = {
|
|||
},
|
||||
showHeader: { type: 'toggle', displayName: 'Header' },
|
||||
showFooter: { type: 'toggle', displayName: 'Footer' },
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
canvasHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Canvas height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
footerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Footer height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
visibility: {
|
||||
type: 'toggle',
|
||||
displayName: 'Visibility',
|
||||
|
|
@ -294,6 +177,13 @@ export const formConfig = {
|
|||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
type: 'code',
|
||||
displayName: 'Tooltip',
|
||||
validation: { schema: { type: 'string' } },
|
||||
section: 'additionalActions',
|
||||
placeholder: 'Enter tooltip text',
|
||||
},
|
||||
},
|
||||
events: {
|
||||
onSubmit: { displayName: 'On submit' },
|
||||
|
|
@ -316,24 +206,8 @@ export const formConfig = {
|
|||
defaultValue: '#ffffffff',
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'code',
|
||||
displayName: 'Header height',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '80px',
|
||||
},
|
||||
},
|
||||
footerHeight: {
|
||||
type: 'code',
|
||||
displayName: 'Footer height',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '80px',
|
||||
},
|
||||
},
|
||||
backgroundColor: {
|
||||
type: 'color',
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Background color',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
|
|
@ -351,7 +225,7 @@ export const formConfig = {
|
|||
},
|
||||
},
|
||||
borderColor: {
|
||||
type: 'color',
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Border color',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
|
|
@ -403,18 +277,18 @@ export const formConfig = {
|
|||
value:
|
||||
"{{ {title: 'User registration form', properties: {firstname: {type: 'textinput',value: 'Maria',label:'First name', validation:{maxLength:6}, styles: {backgroundColor: '#f6f5ff',textColor: 'black'},},lastname:{type: 'textinput',value: 'Doe', label:'Last name', styles: {backgroundColor: '#f6f5ff',textColor: 'black'},},age:{type:'number', label:'Age'},}, submitButton: {value: 'Submit', styles: {backgroundColor: '#3a433b',borderColor:'#595959'}}} }}",
|
||||
},
|
||||
showHeader: { value: '{{false}}' },
|
||||
showFooter: { value: '{{false}}' },
|
||||
showHeader: { value: '{{true}}' },
|
||||
showFooter: { value: '{{true}}' },
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
headerHeight: { value: 60 },
|
||||
footerHeight: { value: 60 },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
backgroundColor: { value: '#fff' },
|
||||
borderRadius: { value: '0' },
|
||||
borderColor: { value: '#fff' },
|
||||
headerHeight: { value: '60px' },
|
||||
footerHeight: { value: '60px' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ export const containerConfig = {
|
|||
displayName: 'Container',
|
||||
description: 'Group components',
|
||||
defaultSize: {
|
||||
width: 5,
|
||||
width: 10,
|
||||
height: 200,
|
||||
},
|
||||
component: 'Container',
|
||||
|
|
@ -47,10 +47,16 @@ export const containerConfig = {
|
|||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
},
|
||||
defaultChildren: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
slotName: 'header',
|
||||
layout: {
|
||||
top: 20,
|
||||
left: 1,
|
||||
|
|
@ -98,15 +104,6 @@ export const containerConfig = {
|
|||
},
|
||||
accordian: 'container',
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Height',
|
||||
validation: {
|
||||
schema: { type: 'number' },
|
||||
defaultValue: 80,
|
||||
},
|
||||
accordian: 'header',
|
||||
},
|
||||
borderRadius: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Border',
|
||||
|
|
@ -158,6 +155,7 @@ export const containerConfig = {
|
|||
loadingState: { value: `{{false}}` },
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
headerHeight: { value: `{{80}}` },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export const formConfig = {
|
|||
description: 'Wrapper for multiple components',
|
||||
defaultSize: {
|
||||
width: 13,
|
||||
height: 480,
|
||||
height: 450,
|
||||
},
|
||||
defaultChildren: [
|
||||
{
|
||||
|
|
@ -19,8 +19,8 @@ export const formConfig = {
|
|||
accessorKey: 'text',
|
||||
styles: ['fontWeight', 'textSize', 'textColor'],
|
||||
defaultValue: {
|
||||
text: 'Form title',
|
||||
textSize: 20,
|
||||
text: 'Form',
|
||||
textSize: 16,
|
||||
textColor: '#000',
|
||||
},
|
||||
},
|
||||
|
|
@ -34,203 +34,68 @@ export const formConfig = {
|
|||
},
|
||||
properties: ['text'],
|
||||
defaultValue: {
|
||||
text: 'Button2',
|
||||
text: 'Submit',
|
||||
padding: 'none',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 40,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 17,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'User Details',
|
||||
fontWeight: 'bold',
|
||||
textSize: 18,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 90,
|
||||
left: 10,
|
||||
height: 30,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'Name',
|
||||
fontWeight: 'normal',
|
||||
textSize: 14,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
layout: {
|
||||
top: 160,
|
||||
left: 10,
|
||||
height: 30,
|
||||
},
|
||||
properties: ['text'],
|
||||
styles: [
|
||||
'textSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textColor',
|
||||
'isScrollRequired',
|
||||
'lineHeight',
|
||||
'textIndent',
|
||||
'textAlign',
|
||||
'verticalAlignment',
|
||||
'decoration',
|
||||
'transformation',
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
'fontVariant',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderRadius',
|
||||
'boxShadow',
|
||||
'padding',
|
||||
],
|
||||
defaultValue: {
|
||||
text: 'Age',
|
||||
fontWeight: 'normal',
|
||||
textSize: 14,
|
||||
textColor: '#000',
|
||||
backgroundColor: '#fff00000',
|
||||
textAlign: 'left',
|
||||
decoration: 'none',
|
||||
transformation: 'none',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
textIndent: '0',
|
||||
letterSpacing: '0',
|
||||
wordSpacing: '0',
|
||||
fontVariant: 'normal',
|
||||
verticalAlignment: 'top',
|
||||
padding: 'default',
|
||||
boxShadow: '0px 0px 0px 0px #00000090',
|
||||
borderRadius: '0',
|
||||
isScrollRequired: 'enabled',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'TextInput',
|
||||
layout: {
|
||||
top: 120,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 25,
|
||||
top: 20,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
placeholder: 'Enter your name',
|
||||
label: '',
|
||||
label: 'Name',
|
||||
width: '{{60}}',
|
||||
direction: 'left',
|
||||
alignment: 'side',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'NumberInput',
|
||||
layout: {
|
||||
top: 190,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 25,
|
||||
top: 80,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['value', 'label'],
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
value: 24,
|
||||
label: '',
|
||||
placeholder: 'Age',
|
||||
label: 'Age',
|
||||
width: '{{60}}',
|
||||
direction: 'left',
|
||||
alignment: 'side',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
componentName: 'TextInput',
|
||||
layout: {
|
||||
top: 240,
|
||||
left: 10,
|
||||
height: 30,
|
||||
width: 10,
|
||||
top: 140,
|
||||
left: 5,
|
||||
height: 40,
|
||||
width: 31,
|
||||
},
|
||||
properties: ['text'],
|
||||
properties: ['placeholder', 'label'],
|
||||
styles: ['alignment', 'width', 'auto', 'padding', 'direction'],
|
||||
defaultValue: {
|
||||
text: 'Submit',
|
||||
placeholder: 'Tomy',
|
||||
label: 'Pet name',
|
||||
width: '{{60}}',
|
||||
alignment: 'side',
|
||||
direction: 'left',
|
||||
auto: '{{false}}',
|
||||
padding: 'default',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -276,6 +141,24 @@ export const formConfig = {
|
|||
},
|
||||
showHeader: { type: 'toggle', displayName: 'Header' },
|
||||
showFooter: { type: 'toggle', displayName: 'Footer' },
|
||||
headerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Header height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
canvasHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Canvas height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
footerHeight: {
|
||||
type: 'numberInput',
|
||||
displayName: 'Footer height',
|
||||
isHidden: true,
|
||||
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
|
||||
},
|
||||
visibility: {
|
||||
type: 'toggle',
|
||||
displayName: 'Visibility',
|
||||
|
|
@ -294,6 +177,13 @@ export const formConfig = {
|
|||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
type: 'code',
|
||||
displayName: 'Tooltip',
|
||||
validation: { schema: { type: 'string' } },
|
||||
section: 'additionalActions',
|
||||
placeholder: 'Enter tooltip text',
|
||||
},
|
||||
},
|
||||
events: {
|
||||
onSubmit: { displayName: 'On submit' },
|
||||
|
|
@ -316,22 +206,6 @@ export const formConfig = {
|
|||
defaultValue: '#ffffffff',
|
||||
},
|
||||
},
|
||||
headerHeight: {
|
||||
type: 'code',
|
||||
displayName: 'Header height',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '80px',
|
||||
},
|
||||
},
|
||||
footerHeight: {
|
||||
type: 'code',
|
||||
displayName: 'Footer height',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '80px',
|
||||
},
|
||||
},
|
||||
backgroundColor: {
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Background color',
|
||||
|
|
@ -403,19 +277,18 @@ export const formConfig = {
|
|||
value:
|
||||
"{{ {title: 'User registration form', properties: {firstname: {type: 'textinput',value: 'Maria',label:'First name', validation:{maxLength:6}, styles: {backgroundColor: '#f6f5ff',textColor: 'black'},},lastname:{type: 'textinput',value: 'Doe', label:'Last name', styles: {backgroundColor: '#f6f5ff',textColor: 'black'},},age:{type:'number', label:'Age'},}, submitButton: {value: 'Submit', styles: {backgroundColor: '#3a433b',borderColor:'#595959'}}} }}",
|
||||
},
|
||||
buttonToSubmit: { value: '{{"none"}}' },
|
||||
showHeader: { value: '{{false}}' },
|
||||
showFooter: { value: '{{false}}' },
|
||||
showHeader: { value: '{{true}}' },
|
||||
showFooter: { value: '{{true}}' },
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
headerHeight: { value: 60 },
|
||||
footerHeight: { value: 60 },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
backgroundColor: { value: '#fff' },
|
||||
borderRadius: { value: '0' },
|
||||
borderColor: { value: '#fff' },
|
||||
headerHeight: { value: '60px' },
|
||||
footerHeight: { value: '60px' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue