);
@@ -251,7 +256,12 @@ export default function generateColumnsData({
);
}
return (
-
+
{cellValue}
);
diff --git a/frontend/src/Editor/Components/Table/load-properties-and-styles.js b/frontend/src/Editor/Components/Table/load-properties-and-styles.js
index 6b8a39d8bc..c4ee5e648e 100644
--- a/frontend/src/Editor/Components/Table/load-properties-and-styles.js
+++ b/frontend/src/Editor/Components/Table/load-properties-and-styles.js
@@ -1,9 +1,8 @@
+import { has } from 'lodash';
+
export default function loadPropertiesAndStyles(properties, styles, darkMode, component) {
const color = styles.textColor !== '#000' ? styles.textColor : darkMode && '#fff';
- let serverSidePagination = properties.serverSidePagination ?? false;
- if (typeof serverSidePagination !== 'boolean') serverSidePagination = false;
-
const serverSideSearch = properties.serverSideSearch ?? false;
const enableNextButton = properties.enableNextButton ?? true;
const enablePrevButton = properties.enablePrevButton ?? true;
@@ -28,8 +27,25 @@ export default function loadPropertiesAndStyles(properties, styles, darkMode, co
const highlightSelectedRow = properties.highlightSelectedRow ?? false;
const rowsPerPage = properties.rowsPerPage ?? 10;
- let clientSidePagination = properties.clientSidePagination ?? !serverSidePagination;
- if (typeof clientSidePagination !== 'boolean') clientSidePagination = true;
+
+ let serverSidePagination = properties.serverSidePagination ?? false;
+ if (typeof serverSidePagination !== 'boolean') serverSidePagination = false;
+
+ let clientSidePagination = false;
+ if (
+ properties.clientSidePagination ||
+ typeof clientSidePagination !== 'boolean' ||
+ (properties.enablePagination && !serverSidePagination)
+ ) {
+ clientSidePagination = true;
+ }
+
+ let enablePagination;
+ if (!has(properties, 'enablePagination') && (properties.clientSidePagination || properties.serverSidePagination)) {
+ enablePagination = true;
+ } else {
+ enablePagination = properties.enablePagination;
+ }
const loadingState = properties.loadingState ?? false;
@@ -63,7 +79,7 @@ export default function loadPropertiesAndStyles(properties, styles, darkMode, co
return {
color,
serverSidePagination,
- clientSidePagination,
+ enablePagination,
serverSideSearch,
serverSideSort,
serverSideFilter,
diff --git a/frontend/src/Editor/Components/Tabs.jsx b/frontend/src/Editor/Components/Tabs.jsx
index 57d832c24d..a873dbe73e 100644
--- a/frontend/src/Editor/Components/Tabs.jsx
+++ b/frontend/src/Editor/Components/Tabs.jsx
@@ -13,7 +13,6 @@ export const Tabs = function Tabs({
removeComponent,
setExposedVariable,
fireEvent,
- registerAction,
styles,
darkMode,
dataCy,
@@ -70,11 +69,6 @@ export const Tabs = function Tabs({
setCurrentTab(parsedDefaultTab);
}, [parsedDefaultTab]);
- useEffect(() => {
- setExposedVariable('currentTab', currentTab);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [currentTab]);
-
useEffect(() => {
const currentTabData = parsedTabs.filter((tab) => tab.id === currentTab);
setBgColor(currentTabData[0]?.backgroundColor ? currentTabData[0]?.backgroundColor : darkMode ? '#324156' : '#fff');
@@ -97,16 +91,18 @@ export const Tabs = function Tabs({
return id === currentTab ? 'visible' : 'hidden';
}
- registerAction(
- 'setTab',
- async function (id) {
+ useEffect(() => {
+ setExposedVariable('setTab', async function (id) {
if (id) {
setCurrentTab(id);
- setExposedVariable('currentTab', id).then(() => fireEvent('onTabSwitch'));
+ setExposedVariable('currentTab', id);
+ fireEvent('onTabSwitch');
}
- },
- [setCurrentTab]
- );
+ });
+ setExposedVariable('currentTab', currentTab);
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [setCurrentTab, currentTab]);
const renderTabContent = (id, tab) => (
{
!tab?.disabled && setCurrentTab(tab.id);
- !tab?.disabled && setExposedVariable('currentTab', tab.id).then(() => fireEvent('onTabSwitch'));
+ !tab?.disabled && setExposedVariable('currentTab', tab.id);
+ fireEvent('onTabSwitch');
}}
key={tab.id}
>
diff --git a/frontend/src/Editor/Components/Text.jsx b/frontend/src/Editor/Components/Text.jsx
index b7b4c28082..45b44f938b 100644
--- a/frontend/src/Editor/Components/Text.jsx
+++ b/frontend/src/Editor/Components/Text.jsx
@@ -1,15 +1,7 @@
import React, { useState, useEffect } from 'react';
import DOMPurify from 'dompurify';
-export const Text = function Text({
- height,
- properties,
- styles,
- darkMode,
- registerAction,
- setExposedVariable,
- dataCy,
-}) {
+export const Text = function Text({ height, properties, styles, darkMode, setExposedVariable, dataCy }) {
let {
textSize,
textColor,
@@ -42,24 +34,18 @@ export const Text = function Text({
const text = computeText();
setText(text);
setExposedVariable('text', text);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [properties.text]);
- registerAction(
- 'setText',
- async function (text) {
+ setExposedVariable('setText', async function (text) {
setText(text);
setExposedVariable('text', text);
- },
- [setText]
- );
- registerAction(
- 'visibility',
- async function (value) {
+ });
+
+ setExposedVariable('visibility', async function (value) {
setVisibility(value);
- },
- [setVisibility]
- );
+ });
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [properties.text, setText, setVisibility]);
function computeText() {
return properties.text === 0 || properties.text === false ? properties.text?.toString() : properties.text;
diff --git a/frontend/src/Editor/Components/TextArea.jsx b/frontend/src/Editor/Components/TextArea.jsx
index 640b82ad7b..70e3650636 100644
--- a/frontend/src/Editor/Components/TextArea.jsx
+++ b/frontend/src/Editor/Components/TextArea.jsx
@@ -1,29 +1,23 @@
import React, { useState, useEffect } from 'react';
-export const TextArea = function TextArea({ height, properties, styles, setExposedVariable, registerAction, dataCy }) {
+export const TextArea = function TextArea({ height, properties, styles, setExposedVariable, dataCy }) {
const [value, setValue] = useState(properties.value);
useEffect(() => {
setValue(properties.value);
setExposedVariable('value', properties.value);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [properties.value]);
- registerAction(
- 'setText',
- async function (text) {
+ setExposedVariable('setText', async function (text) {
setValue(text);
setExposedVariable('value', text);
- },
- [setValue]
- );
- registerAction(
- 'clear',
- async function () {
+ });
+
+ setExposedVariable('clear', async function () {
setValue('');
setExposedVariable('value', '');
- },
- [setValue]
- );
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [properties.value, setValue]);
+
return (
);
diff --git a/frontend/src/Editor/Components/TreeSelect.jsx b/frontend/src/Editor/Components/TreeSelect.jsx
index ff8461ab7f..6e421fe2f0 100644
--- a/frontend/src/Editor/Components/TreeSelect.jsx
+++ b/frontend/src/Editor/Components/TreeSelect.jsx
@@ -72,10 +72,9 @@ export const TreeSelect = ({ height, properties, styles, setExposedVariable, fir
});
setExposedVariable('checkedPathArray', checkedPathArray);
setExposedVariable('checkedPathStrings', checkedPathString);
- setExposedVariable('checked', checked).then(() => {
- updatedNode.checked ? fireEvent('onCheck') : fireEvent('onUnCheck');
- fireEvent('onChange');
- });
+ setExposedVariable('checked', checked);
+ updatedNode.checked ? fireEvent('onCheck') : fireEvent('onUnCheck');
+ fireEvent('onChange');
setChecked(checked);
};
diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx
index 37c2d6acab..b59fa5ec71 100644
--- a/frontend/src/Editor/Container.jsx
+++ b/frontend/src/Editor/Container.jsx
@@ -37,13 +37,10 @@ export const Container = ({
zoomLevel,
removeComponent,
deviceWindowWidth,
- selectedComponents,
darkMode,
socket,
handleUndo,
handleRedo,
- onComponentHover,
- hoveredComponent,
sideBarDebugger,
currentPageId,
}) => {
@@ -65,10 +62,11 @@ export const Container = ({
}),
shallow
);
- const { showComments, currentLayout } = useEditorStore(
+ const { showComments, currentLayout, selectedComponents } = useEditorStore(
(state) => ({
showComments: state?.showComments,
currentLayout: state?.currentLayout,
+ selectedComponents: state?.selectedComponents,
}),
shallow
);
@@ -194,21 +192,24 @@ export const Container = ({
return (x * 100) / canvasWidth;
}
- function updateCanvasHeight(components) {
- const maxHeight = Object.values(components).reduce((max, component) => {
- const layout = component?.layouts?.[currentLayout];
- if (!layout) {
- return max;
- }
- const sum = layout.top + layout.height;
- return Math.max(max, sum);
- }, 0);
+ const updateCanvasHeight = useCallback(
+ (components) => {
+ const maxHeight = Object.values(components).reduce((max, component) => {
+ const layout = component?.layouts?.[currentLayout];
+ if (!layout) {
+ return max;
+ }
+ const sum = layout.top + layout.height;
+ return Math.max(max, sum);
+ }, 0);
- const bottomPadding = mode === 'view' ? 100 : 300;
- const frameHeight = mode === 'view' ? (appDefinition.globalSettings?.hideHeader ? 0 : 45) : 85;
+ const bottomPadding = mode === 'view' ? 100 : 300;
+ const frameHeight = mode === 'view' ? 45 : 85;
- setCanvasHeight(`max(100vh - ${frameHeight}px, ${maxHeight + bottomPadding}px)`);
- }
+ setCanvasHeight(`max(100vh - ${frameHeight}px, ${maxHeight + bottomPadding}px)`);
+ },
+ [setCanvasHeight, currentLayout, mode]
+ );
useEffect(() => {
setIsDragging(draggingState);
@@ -242,7 +243,6 @@ export const Container = ({
const canvasBoundingRect = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect();
const componentMeta = componentTypes.find((component) => component.component === item.component.component);
console.log('adding new component');
-
const newComponent = addNewWidgetToTheEditor(
componentMeta,
monitor,
@@ -274,129 +274,136 @@ export const Container = ({
[moveBox]
);
- function onDragStop(e, componentId, direction, currentLayout) {
- if (isVersionReleased) {
- enableReleasedVersionPopupState();
- return;
- }
- // const id = componentId ? componentId : uuidv4();
+ const onDragStop = useCallback(
+ (e, componentId, direction, currentLayout) => {
+ if (isVersionReleased) {
+ enableReleasedVersionPopupState();
+ return;
+ }
+ // const id = componentId ? componentId : uuidv4();
- // Get the width of the canvas
- const canvasBounds = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect();
- const canvasWidth = canvasBounds?.width;
- const nodeBounds = direction.node.getBoundingClientRect();
+ // Get the width of the canvas
+ const canvasBounds = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect();
+ const canvasWidth = canvasBounds?.width;
+ const nodeBounds = direction.node.getBoundingClientRect();
- // Computing the left offset
- const leftOffset = nodeBounds.x - canvasBounds.x;
- const currentLeftOffset = boxes[componentId].layouts[currentLayout].left;
- const leftDiff = currentLeftOffset - convertXToPercentage(leftOffset, canvasWidth);
+ // Computing the left offset
+ const leftOffset = nodeBounds.x - canvasBounds.x;
+ const currentLeftOffset = boxes[componentId]?.layouts?.[currentLayout]?.left;
+ const leftDiff = currentLeftOffset - convertXToPercentage(leftOffset, canvasWidth);
- // Computing the top offset
- // const currentTopOffset = boxes[componentId].layouts[currentLayout].top;
- const topDiff = boxes[componentId].layouts[currentLayout].top - (nodeBounds.y - canvasBounds.y);
+ // Computing the top offset
+ // const currentTopOffset = boxes[componentId].layouts[currentLayout].top;
+ const topDiff = boxes[componentId].layouts[currentLayout].top - (nodeBounds.y - canvasBounds.y);
- let newBoxes = { ...boxes };
+ let newBoxes = { ...boxes };
- for (const selectedComponent of selectedComponents) {
- newBoxes = produce(newBoxes, (draft) => {
- if (draft[selectedComponent.id]) {
- const topOffset = draft[selectedComponent.id].layouts[currentLayout].top;
- const leftOffset = draft[selectedComponent.id].layouts[currentLayout].left;
+ for (const selectedComponent of selectedComponents) {
+ newBoxes = produce(newBoxes, (draft) => {
+ if (draft[selectedComponent.id]) {
+ const topOffset = draft[selectedComponent.id].layouts[currentLayout].top;
+ const leftOffset = draft[selectedComponent.id].layouts[currentLayout].left;
- draft[selectedComponent.id].layouts[currentLayout].top = topOffset - topDiff;
- draft[selectedComponent.id].layouts[currentLayout].left = leftOffset - leftDiff;
- }
- });
- }
+ draft[selectedComponent.id].layouts[currentLayout].top = topOffset - topDiff;
+ draft[selectedComponent.id].layouts[currentLayout].left = leftOffset - leftDiff;
+ }
+ });
+ }
- setBoxes(newBoxes);
- updateCanvasHeight(newBoxes);
- }
+ setBoxes(newBoxes);
+ updateCanvasHeight(newBoxes);
+ },
+ [isVersionReleased, enableReleasedVersionPopupState, boxes, setBoxes, selectedComponents, updateCanvasHeight]
+ );
- function onResizeStop(id, e, direction, ref, d, position) {
- if (isVersionReleased) {
- enableReleasedVersionPopupState();
- return;
- }
+ const onResizeStop = useCallback(
+ (id, e, direction, ref, d, position) => {
+ if (isVersionReleased) {
+ enableReleasedVersionPopupState();
+ return;
+ }
- const deltaWidth = Math.round(d.width / gridWidth) * gridWidth; //rounding of width of element to nearest mulitple of gridWidth
- const deltaHeight = d.height;
+ const deltaWidth = Math.round(d.width / gridWidth) * gridWidth; //rounding of width of element to nearest mulitple of gridWidth
+ const deltaHeight = d.height;
- if (deltaWidth === 0 && deltaHeight === 0) {
- return;
- }
+ if (deltaWidth === 0 && deltaHeight === 0) {
+ return;
+ }
- let { x, y } = position;
- x = Math.round(x / gridWidth) * gridWidth;
+ let { x, y } = position;
+ x = Math.round(x / gridWidth) * gridWidth;
- const defaultData = {
- top: 100,
- left: 0,
- width: 445,
- height: 500,
- };
+ const defaultData = {
+ top: 100,
+ left: 0,
+ width: 445,
+ height: 500,
+ };
- let { left, top, width, height } = boxes[id]['layouts'][currentLayout] || defaultData;
+ let { left, top, width, height } = boxes[id]['layouts'][currentLayout] || defaultData;
- const boundingRect = document.getElementsByClassName('canvas-area')[0].getBoundingClientRect();
- const canvasWidth = boundingRect?.width;
+ const boundingRect = document.getElementsByClassName('canvas-area')[0].getBoundingClientRect();
+ const canvasWidth = boundingRect?.width;
- //round the width to nearest multiple of gridwidth before converting to %
- const currentWidth = (canvasWidth * width) / NO_OF_GRIDS;
- let newWidth = currentWidth + deltaWidth;
- newWidth = Math.round(newWidth / gridWidth) * gridWidth;
- width = (newWidth * NO_OF_GRIDS) / canvasWidth;
+ //round the width to nearest multiple of gridwidth before converting to %
+ const currentWidth = (canvasWidth * width) / NO_OF_GRIDS;
+ let newWidth = currentWidth + deltaWidth;
+ newWidth = Math.round(newWidth / gridWidth) * gridWidth;
+ width = (newWidth * NO_OF_GRIDS) / canvasWidth;
- height = height + deltaHeight;
+ height = height + deltaHeight;
- top = y;
- left = (x * 100) / canvasWidth;
+ top = y;
+ left = (x * 100) / canvasWidth;
- let newBoxes = {
- ...boxes,
- [id]: {
- ...boxes[id],
- layouts: {
- ...boxes[id]['layouts'],
- [currentLayout]: {
- ...boxes[id]['layouts'][currentLayout],
- width,
- height,
- top,
- left,
+ let newBoxes = {
+ ...boxes,
+ [id]: {
+ ...boxes[id],
+ layouts: {
+ ...boxes[id]['layouts'],
+ [currentLayout]: {
+ ...boxes[id]['layouts'][currentLayout],
+ width,
+ height,
+ top,
+ left,
+ },
},
},
- },
- };
+ };
- setBoxes(newBoxes);
- updateCanvasHeight(newBoxes);
- }
+ setBoxes(newBoxes);
+ updateCanvasHeight(newBoxes);
+ },
+ [setBoxes, currentLayout, boxes, enableReleasedVersionPopupState, isVersionReleased, updateCanvasHeight, gridWidth]
+ );
- function paramUpdated(id, param, value) {
- if (Object.keys(value).length > 0) {
- setBoxes((boxes) =>
- update(boxes, {
- [id]: {
- $merge: {
- component: {
- ...boxes[id].component,
- definition: {
- ...boxes[id].component.definition,
- properties: {
- ...boxes[id].component.definition.properties,
- [param]: value,
+ const paramUpdated = useCallback(
+ (id, param, value) => {
+ if (Object.keys(value).length > 0) {
+ setBoxes((boxes) =>
+ update(boxes, {
+ [id]: {
+ $merge: {
+ component: {
+ ...boxes[id].component,
+ definition: {
+ ...boxes[id].component.definition,
+ properties: {
+ ...boxes[id].component.definition.properties,
+ [param]: value,
+ },
},
},
},
},
- },
- })
- );
- }
- }
-
- React.useEffect(() => {}, [selectedComponents]);
+ })
+ );
+ }
+ },
+ [setBoxes]
+ );
const handleAddThread = async (e) => {
e.stopPropogation && e.stopPropogation();
@@ -503,6 +510,66 @@ export const Container = ({
return componentWithChildren;
}, [components]);
+ const resizingStatusChanged = useCallback(
+ (status) => {
+ setIsResizing(status);
+ },
+ [setIsResizing]
+ );
+
+ const draggingStatusChanged = useCallback(
+ (status) => {
+ setIsDragging(status);
+ },
+ [setIsDragging]
+ );
+
+ const containerProps = useMemo(() => {
+ return {
+ mode,
+ snapToGrid,
+ onComponentClick,
+ onEvent,
+ appDefinition,
+ appDefinitionChanged,
+ currentState,
+ onComponentOptionChanged,
+ onComponentOptionsChanged,
+ appLoading,
+ zoomLevel,
+ setSelectedComponent,
+ removeComponent,
+ currentLayout,
+ deviceWindowWidth,
+ selectedComponents,
+ darkMode,
+ sideBarDebugger,
+ currentPageId,
+ childComponents,
+ };
+ }, [
+ mode,
+ snapToGrid,
+ onComponentClick,
+ onEvent,
+ appDefinition,
+ appDefinitionChanged,
+ currentState,
+ onComponentOptionChanged,
+ onComponentOptionsChanged,
+ appLoading,
+ zoomLevel,
+ setSelectedComponent,
+ removeComponent,
+ currentLayout,
+ deviceWindowWidth,
+ selectedComponents,
+ darkMode,
+ sideBarDebugger,
+ currentPageId,
+ childComponents,
+ ]);
+
return (
setIsResizing(status)}
- draggingStatusChanged={(status) => setIsDragging(status)}
+ resizingStatusChanged={resizingStatusChanged}
+ draggingStatusChanged={draggingStatusChanged}
inCanvas={true}
zoomLevel={zoomLevel}
setSelectedComponent={setSelectedComponent}
removeComponent={removeComponent}
deviceWindowWidth={deviceWindowWidth}
- isSelectedComponent={
- mode === 'edit' ? selectedComponents.find((component) => component.id === key) : false
- }
darkMode={darkMode}
- onComponentHover={onComponentHover}
- hoveredComponent={hoveredComponent}
sideBarDebugger={sideBarDebugger}
- isMultipleComponentsSelected={selectedComponents?.length > 1 ? true : false}
childComponents={childComponents[key]}
- containerProps={{
- mode,
- snapToGrid,
- onComponentClick,
- onEvent,
- appDefinition,
- appDefinitionChanged,
- currentState,
- onComponentOptionChanged,
- onComponentOptionsChanged,
- appLoading,
- zoomLevel,
- setSelectedComponent,
- removeComponent,
- currentLayout,
- deviceWindowWidth,
- selectedComponents,
- darkMode,
- onComponentHover,
- hoveredComponent,
- sideBarDebugger,
- addDefaultChildren,
- currentPageId,
- childComponents,
- }}
- isVersionReleased={isVersionReleased}
+ containerProps={{ ...containerProps, addDefaultChildren }}
/>
);
}
@@ -615,10 +651,15 @@ export const Container = ({
{Object.keys(boxes).length === 0 && !appLoading && !isDragging && (
diff --git a/frontend/src/Editor/DraggableBox.jsx b/frontend/src/Editor/DraggableBox.jsx
index a600727135..3322088c35 100644
--- a/frontend/src/Editor/DraggableBox.jsx
+++ b/frontend/src/Editor/DraggableBox.jsx
@@ -1,5 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
-import React, { useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import cx from 'classnames';
import { useDrag } from 'react-dnd';
import { ItemTypes } from './ItemTypes';
@@ -63,271 +63,287 @@ function getStyles(isDragging, isSelectedComponent) {
};
}
-export const DraggableBox = function DraggableBox({
- id,
- className,
- mode,
- title,
- _left,
- _top,
- parent,
- allComponents,
- component,
- index,
- inCanvas,
- onEvent,
- onComponentClick,
- onComponentOptionChanged,
- onComponentOptionsChanged,
- onResizeStop,
- onDragStop,
- paramUpdated,
- resizingStatusChanged,
- zoomLevel,
- containerProps,
- setSelectedComponent,
- removeComponent,
- layouts,
- isSelectedComponent,
- draggingStatusChanged,
- darkMode,
- canvasWidth,
- readOnly,
- customResolvables,
- parentId,
- hoveredComponent,
- onComponentHover,
- sideBarDebugger,
- isMultipleComponentsSelected,
- childComponents = null,
- isVersionReleased,
-}) {
- const [isResizing, setResizing] = useState(false);
- const [isDragging2, setDragging] = useState(false);
- const [canDrag, setCanDrag] = useState(true);
- const [mouseOver, setMouseOver] = useState(false);
- const currentState = useCurrentState();
-
- const { currentLayout } = useEditorStore(
- (state) => ({
- currentLayout: state?.currentLayout,
- }),
- shallow
- );
- useEffect(() => {
- setMouseOver(hoveredComponent === id);
- }, [hoveredComponent]);
-
- const [{ isDragging }, drag, preview] = useDrag(
- () => ({
- type: ItemTypes.BOX,
- item: {
- id,
- title,
- component,
- zoomLevel,
- parent,
- layouts,
- canvasWidth,
- currentLayout,
- },
- collect: (monitor) => ({
- isDragging: monitor.isDragging(),
+export const DraggableBox = React.memo(
+ ({
+ id,
+ className,
+ mode,
+ title,
+ _left,
+ _top,
+ parent,
+ allComponents,
+ component,
+ index,
+ inCanvas,
+ onEvent,
+ onComponentClick,
+ onComponentOptionChanged,
+ onComponentOptionsChanged,
+ onResizeStop,
+ onDragStop,
+ paramUpdated,
+ resizingStatusChanged,
+ zoomLevel,
+ containerProps,
+ setSelectedComponent,
+ removeComponent,
+ layouts,
+ draggingStatusChanged,
+ darkMode,
+ canvasWidth,
+ readOnly,
+ customResolvables,
+ parentId,
+ sideBarDebugger,
+ childComponents = null,
+ }) => {
+ const [isResizing, setResizing] = useState(false);
+ const [isDragging2, setDragging] = useState(false);
+ const [canDrag, setCanDrag] = useState(true);
+ const {
+ currentLayout,
+ setHoveredComponent,
+ mouseOver,
+ selectionInProgress,
+ isSelectedComponent,
+ isMultipleComponentsSelected,
+ } = useEditorStore(
+ (state) => ({
+ currentLayout: state?.currentLayout,
+ setHoveredComponent: state?.actions?.setHoveredComponent,
+ mouseOver: state?.hoveredComponent === id,
+ selectionInProgress: state?.selectionInProgress,
+ isSelectedComponent:
+ mode === 'edit' ? state?.selectedComponents?.some((component) => component?.id === id) : false,
+ isMultipleComponentsSelected: state?.selectedComponents?.length > 1 ? true : false,
}),
- }),
- [id, title, component, index, zoomLevel, parent, layouts, canvasWidth, currentLayout]
- );
+ shallow
+ );
+ const currentState = useCurrentState();
- useEffect(() => {
- preview(getEmptyImage(), { captureDraggingState: true });
- }, [isDragging]);
+ const [{ isDragging }, drag, preview] = useDrag(
+ () => ({
+ type: ItemTypes.BOX,
+ item: {
+ id,
+ title,
+ component,
+ zoomLevel,
+ parent,
+ layouts,
+ canvasWidth,
+ currentLayout,
+ },
+ collect: (monitor) => ({
+ isDragging: monitor.isDragging(),
+ }),
+ }),
+ [id, title, component, index, currentLayout, zoomLevel, parent, layouts, canvasWidth]
+ );
- useEffect(() => {
- if (resizingStatusChanged) {
- resizingStatusChanged(isResizing);
- }
- }, [isResizing]);
+ useEffect(() => {
+ preview(getEmptyImage(), { captureDraggingState: true });
+ }, [isDragging]);
- useEffect(() => {
- if (draggingStatusChanged) {
- draggingStatusChanged(isDragging2);
- }
-
- if (isDragging2 && !isSelectedComponent) {
- setSelectedComponent(id, component);
- }
- }, [isDragging2]);
-
- const style = {
- display: 'inline-block',
- alignItems: 'center',
- justifyContent: 'center',
- padding: '0px',
- };
-
- let _refProps = {};
-
- if (mode === 'edit' && canDrag) {
- _refProps = {
- ref: drag,
- };
- }
-
- function changeCanDrag(newState) {
- setCanDrag(newState);
- }
-
- const defaultData = {
- top: 100,
- left: 0,
- width: 445,
- height: 500,
- };
-
- const layoutData = inCanvas ? layouts[currentLayout] || defaultData : defaultData;
- const gridWidth = canvasWidth / NO_OF_GRIDS;
- const width = (canvasWidth * layoutData.width) / NO_OF_GRIDS;
-
- const configWidgetHandlerForModalComponent =
- !isSelectedComponent &&
- component.component === 'Modal' &&
- resolveWidgetFieldValue(component.definition.properties.useDefaultButton, currentState)?.value === false;
-
- return (
-
{
+ if (resizingStatusChanged) {
+ resizingStatusChanged(isResizing);
}
- style={!inCanvas ? {} : { width: computeWidth() }}
- >
- {inCanvas ? (
-
{
- if (e.currentTarget.className.includes(`widget-${id}`)) {
- onComponentHover?.(id);
- e.stopPropagation();
- }
- }}
- onMouseLeave={() => onComponentHover?.(false)}
- style={getStyles(isDragging, isSelectedComponent)}
- >
-
setResizing(true)}
- onDrag={(e) => {
- e.preventDefault();
- e.stopImmediatePropagation();
- if (!isDragging2) {
- setDragging(true);
+ }, [isResizing]);
+
+ useEffect(() => {
+ if (draggingStatusChanged) {
+ draggingStatusChanged(isDragging2);
+ }
+
+ if (isDragging2 && !isSelectedComponent) {
+ setSelectedComponent(id, component);
+ }
+ }, [isDragging2]);
+
+ const style = {
+ display: 'inline-block',
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: '0px',
+ };
+
+ let _refProps = {};
+
+ if (mode === 'edit' && canDrag) {
+ _refProps = {
+ ref: drag,
+ };
+ }
+
+ const changeCanDrag = useCallback(
+ (newState) => {
+ setCanDrag(newState);
+ },
+ [setCanDrag]
+ );
+
+ const defaultData = {
+ top: 100,
+ left: 0,
+ width: 445,
+ height: 500,
+ };
+
+ const layoutData = inCanvas ? layouts[currentLayout] || defaultData : defaultData;
+ const gridWidth = canvasWidth / NO_OF_GRIDS;
+ const width = (canvasWidth * layoutData.width) / NO_OF_GRIDS;
+
+ const configWidgetHandlerForModalComponent =
+ !isSelectedComponent &&
+ component.component === 'Modal' &&
+ resolveWidgetFieldValue(component.definition.properties.useDefaultButton, currentState)?.value === false;
+
+ const onComponentHover = (id) => {
+ if (selectionInProgress) return;
+ setHoveredComponent(id);
+ };
+
+ return (
+
+ {inCanvas ? (
+
{
+ if (e.currentTarget.className.includes(`widget-${id}`)) {
+ onComponentHover?.(id);
+ e.stopPropagation();
}
}}
- resizeHandleClasses={isSelectedComponent || mouseOver ? resizerClasses : {}}
- resizeHandleStyles={resizerStyles}
- enableResizing={mode === 'edit' && !readOnly}
- disableDragging={mode !== 'edit' || readOnly}
- onDragStop={(e, direction) => {
- setDragging(false);
- onDragStop(e, id, direction, currentLayout, layoutData);
+ onMouseLeave={() => {
+ setHoveredComponent('');
}}
- cancel={`div.table-responsive.jet-data-table, div.calendar-widget, div.text-input, .textarea, .map-widget, .range-slider, .kanban-container, div.real-canvas`}
- onResizeStop={(e, direction, ref, d, position) => {
- setResizing(false);
- onResizeStop(id, e, direction, ref, d, position);
- }}
- bounds={parent !== undefined ? `#canvas-${parent}` : '.real-canvas'}
- widgetId={id}
+ style={getStyles(isDragging, isSelectedComponent)}
>
-
- {mode === 'edit' &&
- !readOnly &&
- (configWidgetHandlerForModalComponent || mouseOver || isSelectedComponent) &&
- !isResizing && (
-
setResizing(true)}
+ onDrag={(e) => {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ if (!isDragging2) {
+ setDragging(true);
+ }
+ }}
+ resizeHandleClasses={isSelectedComponent || mouseOver ? resizerClasses : {}}
+ resizeHandleStyles={resizerStyles}
+ enableResizing={mode === 'edit' && !readOnly}
+ disableDragging={mode !== 'edit' || readOnly}
+ onDragStop={(e, direction) => {
+ setDragging(false);
+ onDragStop(e, id, direction, currentLayout, layoutData);
+ }}
+ cancel={`div.table-responsive.jet-data-table, div.calendar-widget, div.text-input, .textarea, .map-widget, .range-slider, .kanban-container, div.real-canvas`}
+ onResizeStop={(e, direction, ref, d, position) => {
+ setResizing(false);
+ onResizeStop(id, e, direction, ref, d, position);
+ }}
+ bounds={parent !== undefined ? `#canvas-${parent}` : '.real-canvas'}
+ widgetId={id}
+ >
+
+ {mode === 'edit' &&
+ !readOnly &&
+ (configWidgetHandlerForModalComponent || mouseOver || isSelectedComponent) &&
+ !isResizing && (
+
+ )}
+
+
- )}
-
-
-
-
-
-
- ) : (
-
-
-
-
-
- )}
-
- );
-};
+
+
+
+
+ ) : (
+
+
+
+
+
+ )}
+
+ );
+ }
+);
diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx
index d20c542b7e..ce8cb2333a 100644
--- a/frontend/src/Editor/Editor.jsx
+++ b/frontend/src/Editor/Editor.jsx
@@ -95,10 +95,10 @@ class EditorComponent extends React.Component {
globalSettings: {
hideHeader: false,
appInMaintenance: false,
- canvasMaxWidth: 1292,
- canvasMaxWidthType: 'px',
+ canvasMaxWidth: 100,
+ canvasMaxWidthType: '%',
canvasMaxHeight: 2400,
- canvasBackgroundColor: props.darkMode ? '#2f3c4c' : '#edeff5',
+ canvasBackgroundColor: props.darkMode ? '#2f3c4c' : '#F2F2F5',
backgroundFxQuery: '',
},
};
@@ -122,7 +122,8 @@ class EditorComponent extends React.Component {
apps: [],
queryConfirmationList: [],
isSourceSelected: false,
- selectionInProgress: false,
+ isSaving: false,
+ isUnsavedQueriesAvailable: false,
scrollOptions: {},
currentPageId: defaultPageId,
pages: {},
@@ -642,10 +643,10 @@ class EditorComponent extends React.Component {
};
removeComponents = () => {
- if (!this.props.isVersionReleased && this.state?.selectedComponents?.length > 1) {
- let newDefinition = cloneDeep(this.state.appDefinition);
- const selectedComponents = this.state?.selectedComponents;
+ const selectedComponents = this.props?.selectedComponents;
+ if (!this.props.isVersionReleased && selectedComponents?.length > 1) {
+ let newDefinition = cloneDeep(this.state.appDefinition);
removeSelectedComponent(this.state.currentPageId, newDefinition, selectedComponents);
const platform = navigator?.userAgentData?.platform || navigator?.platform || 'unknown';
if (platform.toLowerCase().indexOf('mac') > -1) {
@@ -744,8 +745,8 @@ class EditorComponent extends React.Component {
};
handleEditorEscapeKeyPress = () => {
- if (this.state?.selectedComponents?.length > 0) {
- this.setState({ selectedComponents: [] });
+ if (this.props?.selectedComponents?.length > 0) {
+ this.props.setSelectedComponents([]);
this.handleInspectorView();
}
};
@@ -754,7 +755,7 @@ class EditorComponent extends React.Component {
let appDefinition = JSON.parse(JSON.stringify(this.state.appDefinition));
let newComponents = appDefinition.pages[this.state.currentPageId].components;
- for (const selectedComponent of this.state.selectedComponents) {
+ for (const selectedComponent of this.props.selectedComponents) {
newComponents = produce(newComponents, (draft) => {
let top = draft[selectedComponent.id].layouts[this.props.currentLayout].top;
let left = draft[selectedComponent.id].layouts[this.props.currentLayout].left;
@@ -835,20 +836,16 @@ class EditorComponent extends React.Component {
};
setSelectedComponent = (id, component, multiSelect = false) => {
- if (this.state.selectedComponents.length === 0 || !multiSelect) {
+ if (this.props.selectedComponents.length === 0 || !multiSelect) {
this.switchSidebarTab(1);
} else {
this.switchSidebarTab(2);
}
- const isAlreadySelected = this.state.selectedComponents.find((component) => component.id === id);
+ const isAlreadySelected = this.props.selectedComponents.find((component) => component.id === id);
if (!isAlreadySelected) {
- this.setState((prevState) => {
- return {
- selectedComponents: [...(multiSelect ? prevState.selectedComponents : []), { id, component }],
- };
- });
+ this.props.setSelectedComponents([...(multiSelect ? this.props.selectedComponents : []), { id, component }]);
}
};
@@ -884,9 +881,9 @@ class EditorComponent extends React.Component {
};
computeCanvasBackgroundColor = () => {
- const { canvasBackgroundColor } = this.state.appDefinition?.globalSettings ?? '#edeff5';
- if (['#2f3c4c', '#edeff5'].includes(canvasBackgroundColor)) {
- return this.props.darkMode ? '#2f3c4c' : '#edeff5';
+ const { canvasBackgroundColor } = this.state.appDefinition?.globalSettings ?? '#F2F2F5';
+ if (['#2f3c4c', '#F2F2F5', '#edeff5'].includes(canvasBackgroundColor)) {
+ return this.props.darkMode ? '#2f3c4c' : '#F2F2F5';
}
return canvasBackgroundColor;
};
@@ -944,19 +941,9 @@ class EditorComponent extends React.Component {
};
handleComponentClick = (id, component) => {
- this.setState({
- selectedComponent: { id, component },
- });
this.switchSidebarTab(1);
};
- handleComponentHover = (id) => {
- if (this.state.selectionInProgress) return;
- this.setState({
- hoveredComponent: id,
- });
- };
-
sideBarDebugger = {
error: (data) => {
debuggerActions.error(this, data);
@@ -982,20 +969,17 @@ class EditorComponent extends React.Component {
runQuery = (queryId, queryName) => runQuery(this, queryId, queryName);
onAreaSelectionStart = (e) => {
- const isMultiSelect = e.inputEvent.shiftKey || this.state.selectedComponents.length > 0;
- this.setState((prevState) => {
- return {
- selectionInProgress: true,
- selectedComponents: [...(isMultiSelect ? prevState.selectedComponents : [])],
- };
- });
+ const isMultiSelect = e.inputEvent.shiftKey || this.props.selectedComponents.length > 0;
+ this.props.setSelectionInProgress(true);
+ this.props.setSelectedComponents([...(isMultiSelect ? this.props.selectedComponents : [])]);
};
onAreaSelection = (e) => {
e.added.forEach((el) => {
el.classList.add('resizer-select');
});
- if (this.state.selectionInProgress) {
+
+ if (this.props.selectionInProgress) {
e.removed.forEach((el) => {
el.classList.remove('resizer-select');
});
@@ -1004,7 +988,7 @@ class EditorComponent extends React.Component {
onAreaSelectionEnd = (e) => {
const currentPageId = this.state.currentPageId;
- this.setState({ selectionInProgress: false });
+ this.props.setSelectionInProgress(false);
e.selected.forEach((el, index) => {
const id = el.getAttribute('widgetid');
const component = this.state.appDefinition.pages[currentPageId].components[id].component;
@@ -1024,13 +1008,13 @@ class EditorComponent extends React.Component {
onAreaSelectionDrag = (e) => {
if (this.selectionDragRef.current) {
e.stop();
- this.state.selectionInProgress && this.setState({ selectionInProgress: false });
+ this.props.selectionInProgress && this.props.setSelectionInProgress(false);
}
};
onAreaSelectionDragEnd = () => {
this.selectionDragRef.current = false;
- this.state.selectionInProgress && this.setState({ selectionInProgress: false });
+ this.props.selectionInProgress && this.props.setSelectionInProgress(false);
};
addNewPage = ({ name, handle }) => {
@@ -1376,6 +1360,30 @@ class EditorComponent extends React.Component {
);
};
+ disableEnablePage = ({ pageId, isDisabled }) => {
+ const newAppDefinition = {
+ ...this.state.appDefinition,
+ pages: {
+ ...this.state.appDefinition.pages,
+ [pageId]: {
+ ...this.state.appDefinition.pages[pageId],
+ disabled: isDisabled ?? false,
+ },
+ },
+ };
+
+ this.setState(
+ {
+ isSaving: true,
+ appDefinition: newAppDefinition,
+ appDefinitionLocalVersion: uuid(),
+ },
+ () => {
+ this.autoSave();
+ }
+ );
+ };
+
switchPage = (pageId, queryParams = []) => {
document.getElementById('real-canvas').scrollIntoView();
if (
@@ -1476,13 +1484,18 @@ class EditorComponent extends React.Component {
return defaultCanvasMinWidth;
}
};
+ handleCanvasContainerMouseUp = (e) => {
+ if (['real-canvas', 'modal'].includes(e.target.className)) {
+ this.props.setSelectedComponents([]);
+ this.setState({ currentSidebarTab: 2 });
+ }
+ };
handleEditorMarginLeftChange = (value) => this.setState({ editorMarginLeft: value });
render() {
const {
currentSidebarTab,
- selectedComponents = [],
appDefinition,
appId,
slug,
@@ -1493,9 +1506,9 @@ class EditorComponent extends React.Component {
deviceWindowWidth,
apps,
defaultComponentStateComputed,
- hoveredComponent,
queryConfirmationList,
} = this.state;
+ const selectedComponents = this?.props?.selectedComponents;
const currentState = this.props?.currentState;
const editingVersion = this.props?.editingVersion;
const appVersionPreviewLink = editingVersion
@@ -1550,6 +1563,9 @@ class EditorComponent extends React.Component {
+
{!this.props.showComments && (
{
- if (['real-canvas', 'modal'].includes(e.target.className)) {
- this.setState({ selectedComponents: [], currentSidebarTab: 2, hoveredComponent: false });
- }
+ background: !this.props.darkMode ? '#EBEBEF' : '#2E3035',
}}
+ onMouseUp={this.handleCanvasContainerMouseUp}
ref={this.canvasContainerRef}
onScroll={() => {
this.selectionRef.current.checkScroll();
@@ -1692,7 +1707,6 @@ class EditorComponent extends React.Component {
mode={'edit'}
zoomLevel={zoomLevel}
deviceWindowWidth={deviceWindowWidth}
- selectedComponents={selectedComponents}
appLoading={isLoading}
onEvent={this.handleEvent}
onComponentOptionChanged={this.handleOnComponentOptionChanged}
@@ -1702,8 +1716,6 @@ class EditorComponent extends React.Component {
handleRedo={this.handleRedo}
removeComponent={this.removeComponent}
onComponentClick={this.handleComponentClick}
- onComponentHover={this.handleComponentHover}
- hoveredComponent={hoveredComponent}
sideBarDebugger={this.sideBarDebugger}
currentPageId={this.state.currentPageId}
/>
@@ -1758,6 +1770,7 @@ class EditorComponent extends React.Component {
darkMode={this.props.darkMode}
appDefinitionLocalVersion={this.state.appDefinitionLocalVersion}
pages={this.getPagesWithIds()}
+ cloneComponents={this.cloneComponents}
>
) : (
@@ -1787,10 +1800,21 @@ class EditorComponent extends React.Component {
}
const withStore = (Component) => (props) => {
- const { showComments, currentLayout } = useEditorStore(
+ const {
+ showComments,
+ currentLayout,
+ selectionInProgress,
+ setSelectionInProgress,
+ selectedComponents,
+ setSelectedComponents,
+ } = useEditorStore(
(state) => ({
showComments: state?.showComments,
currentLayout: state?.currentLayout,
+ selectionInProgress: state?.selectionInProgress,
+ setSelectionInProgress: state?.actions?.setSelectionInProgress,
+ selectedComponents: state?.selectedComponents,
+ setSelectedComponents: state?.actions?.setSelectedComponents,
}),
shallow
);
@@ -1798,17 +1822,20 @@ const withStore = (Component) => (props) => {
(state) => ({ isVersionReleased: state.isVersionReleased, editingVersion: state.editingVersion }),
shallow
);
-
const currentState = useCurrentState();
return (
);
};
diff --git a/frontend/src/Editor/Header/GlobalSettings.jsx b/frontend/src/Editor/Header/GlobalSettings.jsx
index 6426fd2fd6..75dd10e4de 100644
--- a/frontend/src/Editor/Header/GlobalSettings.jsx
+++ b/frontend/src/Editor/Header/GlobalSettings.jsx
@@ -1,18 +1,18 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import cx from 'classnames';
import { SketchPicker } from 'react-color';
import { Confirm } from '../Viewer/Confirm';
-import { LeftSidebarItem } from '../LeftSidebar/SidebarItem';
+import { HeaderSection } from '@/_ui/LeftSidebar';
import FxButton from '../CodeBuilder/Elements/FxButton';
import { CodeHinter } from '../CodeBuilder/CodeHinter';
import { resolveReferences } from '@/_helpers/utils';
import { useTranslation } from 'react-i18next';
import _ from 'lodash';
-import Popover from '@/_ui/Popover';
import ExportAppModal from '../../HomePage/ExportAppModal';
import { useCurrentState } from '@/_stores/currentStateStore';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { shallow } from 'zustand/shallow';
+import { ButtonSolid } from '@/_ui/AppButton/AppButton';
export const GlobalSettings = ({
globalSettings,
@@ -24,12 +24,11 @@ export const GlobalSettings = ({
}) => {
const { t } = useTranslation();
const { hideHeader, canvasMaxWidth, canvasMaxWidthType, canvasBackgroundColor, backgroundFxQuery } = globalSettings;
- const [showPicker, setShowPicker] = React.useState(false);
+ const [showPicker, setShowPicker] = useState(false);
const currentState = useCurrentState();
- const [forceCodeBox, setForceCodeBox] = React.useState(true);
- const [realState, setRealState] = React.useState(currentState);
- const [showConfirmation, setConfirmationShow] = React.useState(false);
- const [show, setShow] = React.useState('');
+ const [forceCodeBox, setForceCodeBox] = useState(true);
+ const [realState, setRealState] = useState(currentState);
+ const [showConfirmation, setConfirmationShow] = useState(false);
const [isExportingApp, setIsExportingApp] = React.useState(false);
const { isVersionReleased } = useAppVersionStore(
(state) => ({
@@ -46,213 +45,29 @@ export const GlobalSettings = ({
left: '0px',
};
- React.useEffect(() => {
+ useEffect(() => {
setRealState(currentState);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentState.components]);
- React.useEffect(() => {
+ useEffect(() => {
backgroundFxQuery &&
globalSettingsChanged('canvasBackgroundColor', resolveReferences(backgroundFxQuery, realState));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(resolveReferences(backgroundFxQuery, realState))]);
- const popoverContent = (
-
-
-
-
-
-
- {t('leftSidebar.Settings.hideHeader', 'Hide header for launched apps')}
-
-
- globalSettingsChanged('hideHeader', e.target.checked)}
- />
-
-
- {t('leftSidebar.Settings.hideHeader', 'Hide header for launched apps')}
-
-
-
-
- {t('leftSidebar.Settings.maintenanceMode', 'Maintenance mode')}
-
-
- setConfirmationShow(true)}
- />
-
-
- {t('leftSidebar.Settings.maintenanceMode', 'Maintenance mode')}
-
-
-
-
- {t('leftSidebar.Settings.maxWidthOfCanvas', 'Max width of canvas')}
-
-
-
- {
- const width = e.target.value;
- if (!Number.isNaN(width) && width >= 0) globalSettingsChanged('canvasMaxWidth', width);
- }}
- value={canvasMaxWidth}
- />
-
-
-
-
- {/*
-
- {t('leftSidebar.Settings.maxHeightOfCanvas', 'Max height of canvas')}
-
-
-
- {
- const height = e.target.value;
- if (!Number.isNaN(height) && height <= 2400) globalSettingsChanged('canvasMaxHeight', height);
- }}
- value={canvasMaxHeight}
- />
-
-
-
*/}
-
-
- {t('leftSidebar.Settings.backgroundColorOfCanvas', 'Background color of canvas')}
-
-
- {showPicker && (
-
-
setShowPicker(false)} />
- setShowPicker(true)}
- color={canvasBackgroundColor}
- onChangeComplete={(color) => {
- globalSettingsChanged('canvasBackgroundColor', [color.hex, color.rgb]);
- globalSettingsChanged('backgroundFxQuery', color.hex);
- }}
- />
-
- )}
- {forceCodeBox && (
-
setShowPicker(true)}
- >
-
-
{canvasBackgroundColor}
-
- )}
-
- {!forceCodeBox && (
-
{
- globalSettingsChanged('canvasBackgroundColor', resolveReferences(color, realState));
- globalSettingsChanged('backgroundFxQuery', color);
- }}
- />
- )}
-
- {
- setForceCodeBox(!forceCodeBox);
- }}
- />
-
-
-
-
-
-
Export app
-
-
-
-
-
-
- );
-
+ const outerStyles = {
+ width: '142px',
+ height: '32px',
+ borderRadius: ' 6px',
+ display: 'flex',
+ paddingLeft: '4px',
+ alignItems: 'center',
+ gap: '4px',
+ background: showPicker && 'var(--indigo2)',
+ outline: showPicker && '1px solid var(--indigo9)',
+ boxShadow: showPicker && '0px 0px 0px 1px #C6D4F9',
+ };
return (
<>
)}
-
{
- if (show) setShow('settings');
- else {
- setShow('');
- setShowPicker(false);
- }
- }}
- popoverContentClassName="p-0 sidebar-h-100-popover global-settings-popover-content"
- side="bottom"
- popoverContent={popoverContent}
- popoverContentHeight="auto"
- >
-
-
+
+
+
+
+
+
+
+
+
+ {t('leftSidebar.Settings.hideHeader', 'Hide header for launched apps')}
+
+
+ globalSettingsChanged('hideHeader', e.target.checked)}
+ />
+
+
+
+
+ {t('leftSidebar.Settings.maintenanceMode', 'Maintenance mode')}
+
+
+ setConfirmationShow(true)}
+ />
+
+
+
+
+ {t('leftSidebar.Settings.maxWidthOfCanvas', 'Max width of canvas')}
+
+
+
+ {
+ const width = e.target.value;
+ if (!Number.isNaN(width) && width >= 0) globalSettingsChanged('canvasMaxWidth', width);
+ }}
+ value={canvasMaxWidth}
+ />
+
+
+
+
+ {/*
+
+ {t('leftSidebar.Settings.maxHeightOfCanvas', 'Max height of canvas')}
+
+
+
+ {
+ const height = e.target.value;
+ if (!Number.isNaN(height) && height <= 2400) globalSettingsChanged('canvasMaxHeight', height);
+ }}
+ value={canvasMaxHeight}
+ />
+
+
+
*/}
+
+
+ {t('leftSidebar.Settings.backgroundColorOfCanvas', 'Canvas bavkground')}
+
+
+ {showPicker && (
+
+
setShowPicker(false)} />
+ setShowPicker(true)}
+ color={canvasBackgroundColor}
+ onChangeComplete={(color) => {
+ globalSettingsChanged('canvasBackgroundColor', [color.hex, color.rgb]);
+ globalSettingsChanged('backgroundFxQuery', color.hex);
+ }}
+ />
+
+ )}
+ {forceCodeBox && (
+
setShowPicker(true)}
+ style={outerStyles}
+ >
+
+
+ {canvasBackgroundColor}
+
+
+ )}
+
+ {!forceCodeBox && (
+
{
+ globalSettingsChanged('canvasBackgroundColor', resolveReferences(color, realState));
+ globalSettingsChanged('backgroundFxQuery', color);
+ }}
+ />
+ )}
+
+ {
+ setForceCodeBox(!forceCodeBox);
+ }}
+ />
+
+
+
+
+
+
+
Export app
+
+ {
+ setIsExportingApp(true);
+ document.getElementById('maintenance-app-modal').click();
+ }}
+ fill={`var(--indigo9)`}
+ leftIcon="fileupload"
+ iconWidth="16"
+ data-cy="button-user-status-change"
+ >
+ Export this app
+
+
+
+
+
+
+
>
);
};
diff --git a/frontend/src/Editor/Header/HeaderActions.jsx b/frontend/src/Editor/Header/HeaderActions.jsx
index d5e2d58a02..5f341f39f3 100644
--- a/frontend/src/Editor/Header/HeaderActions.jsx
+++ b/frontend/src/Editor/Header/HeaderActions.jsx
@@ -3,6 +3,7 @@ import cx from 'classnames';
import { Tooltip } from 'react-tooltip';
import { useEditorStore } from '@/_stores/editorStore';
import { shallow } from 'zustand/shallow';
+import SolidIcon from '@/_ui/Icon/SolidIcons';
function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo }) {
const darkMode = localStorage.getItem('darkMode') === 'true';
@@ -15,10 +16,10 @@ function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo }) {
);
return (
-
+
@@ -36,14 +37,11 @@ function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo }) {
onClick={() => toggleCurrentLayout('desktop')}
data-cy={`button-change-layout-to-desktop`}
>
-
+
-
-
+
diff --git a/frontend/src/Editor/Header/index.js b/frontend/src/Editor/Header/index.js
index 3df9ab8e67..bf0102e4db 100644
--- a/frontend/src/Editor/Header/index.js
+++ b/frontend/src/Editor/Header/index.js
@@ -1,7 +1,6 @@
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import AppLogo from '@/_components/AppLogo';
-import { GlobalSettings } from './GlobalSettings';
import EditAppName from './EditAppName';
import HeaderActions from './HeaderActions';
import RealtimeAvatars from '../RealtimeAvatars';
@@ -14,12 +13,10 @@ import config from 'config';
import { useUpdatePresence } from '@y-presence/react';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { shallow } from 'zustand/shallow';
+import SolidIcon from '@/_ui/Icon/SolidIcons';
export default function EditorHeader({
- darkMode,
- globalSettingsChanged,
- appDefinition,
- toggleAppMaintenance,
+ M,
app,
appVersionPreviewLink,
slug,
@@ -37,8 +34,8 @@ export default function EditorHeader({
saveEditingVersion,
onVersionDelete,
currentUser,
+ darkMode,
}) {
- const { is_maintenance_on } = app;
const { isVersionReleased, editingVersion } = useAppVersionStore(
(state) => ({
isVersionReleased: state.isVersionReleased,
@@ -65,58 +62,67 @@ export default function EditorHeader({
return (
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
- {isSaving ? 'Saving...' : saveError ? 'Could not save changes' : 'Saved changes'}
-
-
+
+
+
+
+
+
+
+
+ {isSaving ? (
+ 'Saving...'
+ ) : saveError ? (
+
+
+
Could not save changes
+
+ ) : (
+
+ )}
+
+ {config.ENABLE_MULTIPLAYER_EDITING &&
}
-
-
+
+
+
- {config.ENABLE_MULTIPLAYER_EDITING && (
-
-
-
- )}
-
-
-
-
+
+
+
{app.id && (
-
+
)}
-
+
diff --git a/frontend/src/Editor/Inspector/Components/Table/NoListItem.jsx b/frontend/src/Editor/Inspector/Components/Table/NoListItem.jsx
new file mode 100644
index 0000000000..4f598e12e2
--- /dev/null
+++ b/frontend/src/Editor/Inspector/Components/Table/NoListItem.jsx
@@ -0,0 +1,25 @@
+import SolidIcon from '@/_ui/Icon/SolidIcons';
+import React from 'react';
+
+const NoListItem = ({ text, dataCy = '' }) => {
+ return (
+
+
+
+
+ {text}
+
+ );
+};
+
+export default NoListItem;
diff --git a/frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleToggleSwitch.jsx b/frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleProperties.jsx
similarity index 72%
rename from frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleToggleSwitch.jsx
rename to frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleProperties.jsx
index 66fc8f0913..695376bb8b 100644
--- a/frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleToggleSwitch.jsx
+++ b/frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleProperties.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import { CodeHinter } from '../../../CodeBuilder/CodeHinter';
-export const ProgramaticallyHandleToggleSwitch = ({
+export const ProgramaticallyHandleProperties = ({
darkMode,
// eslint-disable-next-line no-unused-vars
label,
@@ -18,10 +18,8 @@ export const ProgramaticallyHandleToggleSwitch = ({
switch (property) {
case 'isEditable':
return props.isEditable;
-
case 'disableActionButton':
return props.disableActionButton;
-
case 'columnVisibility':
return props.columnVisibility;
case 'linkTarget':
@@ -30,33 +28,15 @@ export const ProgramaticallyHandleToggleSwitch = ({
return;
}
};
- const getOptionsForSelectElement = (property, paramMeta) => {
- switch (property) {
- case 'linkTarget':
- return {
- ...paramMeta,
- options: [
- { name: 'Same window', value: '_self' },
- { name: 'New window', value: '_blank' },
- ],
- };
- default:
- break;
- }
- };
- if (paramMeta.type === 'select') {
- paramMeta = getOptionsForSelectElement(property, paramMeta);
- }
-
- const getInitialValue = (property, definition) => {
+ const getInitialValue = (property, definitionObj) => {
if (property === 'columnVisibility') {
- return definition?.value ?? `{{true}}`;
+ return definitionObj?.value ?? `{{true}}`;
}
if (property === 'linkTarget') {
- return definition?.value ?? '_blank';
+ return definitionObj?.value ?? '_blank';
}
- return definition?.value ?? `{{false}}`;
+ return definitionObj?.value ?? `{{false}}`;
};
const value = getValueBasedOnProperty(property, props);
diff --git a/frontend/src/Editor/Inspector/Components/Table/Table.jsx b/frontend/src/Editor/Inspector/Components/Table/Table.jsx
index 656d3dae4b..4364cf1997 100644
--- a/frontend/src/Editor/Inspector/Components/Table/Table.jsx
+++ b/frontend/src/Editor/Inspector/Components/Table/Table.jsx
@@ -1,6 +1,5 @@
import React from 'react';
import Accordion from '@/_ui/Accordion';
-
import { renderElement } from '../../Utils';
import { computeActionName, resolveReferences } from '@/_helpers/utils';
// eslint-disable-next-line import/no-unresolved
@@ -13,7 +12,11 @@ import { v4 as uuidv4 } from 'uuid';
import { EventManager } from '../../EventManager';
import { CodeHinter } from '../../../CodeBuilder/CodeHinter';
import { withTranslation } from 'react-i18next';
-import { ProgramaticallyHandleToggleSwitch } from './ProgramaticallyHandleToggleSwitch';
+import AddNewButton from '@/ToolJetUI/Buttons/AddNewButton/AddNewButton';
+import List from '@/ToolJetUI/List/List';
+import { capitalize, has } from 'lodash';
+import NoListItem from './NoListItem';
+import { ProgramaticallyHandleProperties } from './ProgramaticallyHandleProperties';
class TableComponent extends React.Component {
constructor(props) {
super(props);
@@ -168,7 +171,7 @@ class TableComponent extends React.Component {
return (
+
+
+ {
+ this.onColumnItemChange(index, 'horizontalAlignment', value);
+ }}
+ fuzzySearch
+ placeholder={this.props.t('globals.select', 'Select') + '...'}
+ />
+
{(column.columnType === 'string' || column.columnType === undefined || column.columnType === 'default') && (
@@ -544,7 +568,7 @@ class TableComponent extends React.Component {
-
+
)}
{!['image', 'link'].includes(column.columnType) && (
-
)}
-
+
-
+
@@ -783,7 +814,7 @@ class TableComponent extends React.Component {
onChange={(name, value, color) => this.onActionButtonPropertyChanged(index, 'textColor', color)}
cyLabel={`action-button-text`}
/>
-
this.setState({ showPopOver: showing })}
>
-
-
-
-
- {action.buttonText}
-
-
-
+
+
+
+
);
@@ -945,9 +972,15 @@ class TableComponent extends React.Component {
const serverSidePagination = component.component.definition.properties.serverSidePagination?.value
? resolveReferences(component.component.definition.properties.serverSidePagination?.value, currentState)
: false;
+
const clientSidePagination = component.component.definition.properties.clientSidePagination?.value
? resolveReferences(component.component.definition.properties.clientSidePagination?.value, currentState)
: false;
+
+ let enablePagination = !has(component.component.definition.properties, 'enablePagination')
+ ? clientSidePagination || serverSidePagination
+ : resolveReferences(component.component.definition.properties.enablePagination?.value, currentState);
+
const enabledSort = component.component.definition.properties.enabledSort?.value
? resolveReferences(component.component.definition.properties.enabledSort?.value, currentState)
: true;
@@ -964,9 +997,8 @@ class TableComponent extends React.Component {
};
let items = [];
-
items.push({
- title: 'Properties',
+ title: 'Data',
children: renderElement(
component,
componentMeta,
@@ -976,7 +1008,8 @@ class TableComponent extends React.Component {
'properties',
currentState,
components,
- darkMode
+ darkMode,
+ false
),
});
@@ -984,17 +1017,10 @@ class TableComponent extends React.Component {
title: 'Columns',
children: (
+
{renderCustomElement('useDynamicColumn')}
+ {useDynamicColumn &&
{renderCustomElement('columnData')}
}
{!useDynamicColumn && (
- <>
-
-
-
+
{
this.onDragEnd(result);
@@ -1009,7 +1035,6 @@ class TableComponent extends React.Component {
{(provided, snapshot) => (
-
-
-

-
-
-
- {resolvedItemName}
-
-
-
-
-
-
+
{
+ if (menuOptionLabel === 'Delete') this.removeColumn(index);
+ }}
+ darkMode={darkMode}
+ menuActions={[
+ {
+ label: 'Delete',
+ icon: '',
+ },
+ ]}
+ />
@@ -1064,10 +1078,16 @@ class TableComponent extends React.Component {
)}
- >
+
+ {columns?.value?.length === 0 &&
}
+
+
+ {this.props.t('widget.Table.addNewColumn', ' Add new column')}
+
+
+
+
)}
-
{renderCustomElement('useDynamicColumn')}
- {useDynamicColumn &&
{renderCustomElement('columnData')}
}
),
});
@@ -1075,69 +1095,48 @@ class TableComponent extends React.Component {
items.push({
title: 'Action buttons',
children: (
-
+
-
-
-
+
{actions.value.map((action, index) => this.renderActionButton(action, index))}
+ {actions.value.length === 0 && (
+
+ )}
+
+ New action button
+
-
{actions.value.map((action, index) => this.actionButton(action, index))}
- {actions.value.length === 0 && (
-
-
- {this.props.t('widget.Table.noActionMessage', "This table doesn't have any action buttons")}
-
-
- )}
),
});
- const options = [
- 'serverSidePagination',
- ...(serverSidePagination ? ['enablePrevButton'] : []),
- ...(serverSidePagination ? ['enableNextButton'] : []),
- ...(serverSidePagination ? ['totalRecords'] : []),
- ...(clientSidePagination && !serverSidePagination ? ['rowsPerPage'] : []),
- 'enabledSort',
- ...(enabledSort ? ['serverSideSort'] : []),
- 'showDownloadButton',
- 'showFilterButton',
- 'showAddNewRowButton',
- ...(displayServerSideFilter ? ['serverSideFilter'] : []),
- 'showBulkUpdateActions',
+ const rowSelectionsOptions = [
'allowSelection',
...(allowSelection ? ['highlightSelectedRow', 'showBulkSelector', 'defaultSelectedRow'] : []),
+ ];
+ const searchSortFilterOptions = [
+ ...(displaySearchBox ? ['displaySearchBox'] : []),
+ ...(displayServerSideSearch ? ['serverSideSearch'] : []),
+ 'enabledSort',
+ ...(enabledSort ? ['serverSideSort'] : []),
+ 'showFilterButton',
+ ...(displayServerSideFilter ? ['serverSideFilter'] : []),
+ ];
+ const paginationOptions = [
+ 'enablePagination',
+ ...(enablePagination ? ['serverSidePagination'] : []),
+ ...(enablePagination && !serverSidePagination ? ['rowsPerPage'] : []),
+ ...(enablePagination && serverSidePagination ? ['enablePrevButton'] : []),
+ ...(enablePagination && serverSidePagination ? ['enableNextButton'] : []),
+ ...(enablePagination && serverSidePagination ? ['totalRecords'] : []),
+ ];
+ const additionalActions = [
+ 'showAddNewRowButton',
+ 'showDownloadButton',
'hideColumnSelectorButton',
+ 'loadingState',
+ 'showBulkUpdateActions',
];
- let renderOptions = [];
-
- !serverSidePagination && options.splice(1, 0, 'clientSidePagination');
-
- options.map((option) => renderOptions.push(renderCustomElement(option)));
-
- const conditionalOptions = [
- { name: 'displaySearchBox', condition: displaySearchBox },
- { name: 'serverSideSearch', condition: displayServerSideSearch },
- { name: 'loadingState', condition: true },
- ];
-
- conditionalOptions.map(({ name, condition }) => {
- if (condition) renderOptions.push(renderCustomElement(name));
- });
-
- items.push({
- title: 'Options',
- children: renderOptions,
- });
-
items.push({
title: 'Events',
isOpen: true,
@@ -1155,6 +1154,25 @@ class TableComponent extends React.Component {
),
});
+ items.push({
+ title: 'Row Selection',
+ children: rowSelectionsOptions.map((option) => renderCustomElement(option)),
+ });
+ items.push({
+ title: 'Search, sort and filter',
+ children: searchSortFilterOptions.map((option) => renderCustomElement(option)),
+ });
+
+ items.push({
+ title: 'Pagination',
+ children: paginationOptions.map((option) => renderCustomElement(option)),
+ });
+
+ items.push({
+ title: 'Additional actions',
+ children: additionalActions.map((option) => renderCustomElement(option)),
+ });
+
items.push({
title: 'Layout',
isOpen: true,
diff --git a/frontend/src/Editor/Inspector/Elements/Code.jsx b/frontend/src/Editor/Inspector/Elements/Code.jsx
index b252373d5c..986586bef5 100644
--- a/frontend/src/Editor/Inspector/Elements/Code.jsx
+++ b/frontend/src/Editor/Inspector/Elements/Code.jsx
@@ -4,6 +4,8 @@ import _ from 'lodash';
import { resolveReferences } from '@/_helpers/utils';
import { useCurrentState } from '@/_stores/currentStateStore';
+const CLIENT_SERVER_TOGGLE_FIELDS = ['serverSidePagination', 'serverSideSort', 'serverSideFilter'];
+
export const Code = ({
param,
definition,
@@ -15,9 +17,20 @@ export const Code = ({
onFxPress,
fxActive,
component,
+ verticalLine,
}) => {
const currentState = useCurrentState();
+
const getDefinitionForNewProps = (param) => {
+ if (param === 'enablePagination') {
+ const clientSidePagination = component?.component?.definition?.properties?.clientSidePagination?.value ?? false;
+ const serverSidePagination = component?.component?.definition?.properties?.serverSidePagination?.value ?? false;
+ const isPaginationEnabled =
+ resolveReferences(clientSidePagination, currentState) || resolveReferences(serverSidePagination, currentState);
+
+ if (isPaginationEnabled) return '{{true}}';
+ return '{{false}}';
+ }
if (['showAddNewRowButton', 'allowSelection', 'defaultSelectedRow'].includes(param)) {
if (param === 'allowSelection') {
const highlightSelectedRow = component?.component?.definition?.properties?.highlightSelectedRow?.value ?? false;
@@ -36,10 +49,28 @@ export const Code = ({
}
};
- const initialValue = !_.isEmpty(definition) ? definition.value : getDefinitionForNewProps(param.name);
+ let initialValue = !_.isEmpty(definition) ? definition.value : getDefinitionForNewProps(param.name);
const paramMeta = componentMeta[paramType][param.name];
const displayName = paramMeta.displayName || param.name;
+ /*
+ following block is written for cellSize Prop to support backward compatibility,
+ because from older app we also get cellSize value as compact or spacious,
+ so accordigly we update the initial value with the new values respectively
+ */
+ if (paramType === 'styles' && param.name === 'cellSize') {
+ switch (initialValue) {
+ case 'compact':
+ initialValue = 'condensed';
+ break;
+ case 'spacious':
+ initialValue = 'regular';
+ break;
+ default:
+ break;
+ }
+ }
+
function handleCodeChanged(value) {
onChange(param, 'value', value, paramType);
}
@@ -49,9 +80,8 @@ export const Code = ({
const getfieldName = React.useMemo(() => {
return param.name;
}, [param]);
-
return (
-
+
);
diff --git a/frontend/src/Editor/Inspector/Elements/Color.jsx b/frontend/src/Editor/Inspector/Elements/Color.jsx
index fc82f41abd..c92cb27cdb 100644
--- a/frontend/src/Editor/Inspector/Elements/Color.jsx
+++ b/frontend/src/Editor/Inspector/Elements/Color.jsx
@@ -12,6 +12,18 @@ export const Color = ({ param, definition, onChange, paramType, componentMeta, c
bottom: '0px',
left: '0px',
};
+ const outerStyles = {
+ width: '142px',
+ height: '32px',
+ borderRadius: ' 6px',
+ display: 'flex',
+ paddingLeft: '4px',
+ alignItems: 'center',
+ gap: '4px',
+ background: showPicker && 'var(--indigo2)',
+ outline: showPicker && '1px solid var(--indigo9)',
+ boxShadow: showPicker && '0px 0px 0px 1px #C6D4F9',
+ };
const paramMeta = componentMeta[paramType][param.name] || {};
const displayName = paramMeta.displayName || param.name;
@@ -41,18 +53,21 @@ export const Color = ({ param, definition, onChange, paramType, componentMeta, c
)}
setShowPicker(true)}
data-cy={`${String(cyLabel)}-picker`}
+ style={outerStyles}
>
diff --git a/frontend/src/Editor/Inspector/Elements/Select.jsx b/frontend/src/Editor/Inspector/Elements/Select.jsx
index 85a8d5ba1d..5377b10a62 100644
--- a/frontend/src/Editor/Inspector/Elements/Select.jsx
+++ b/frontend/src/Editor/Inspector/Elements/Select.jsx
@@ -1,6 +1,8 @@
import React from 'react';
import { ToolTip } from './Components/ToolTip';
-import SelectSearch from 'react-select-search';
+// import SelectSearch from 'react-select-search';
+import SelectSearch from 'react-select';
+
// eslint-disable-next-line import/no-unresolved
import { useTranslation } from 'react-i18next';
diff --git a/frontend/src/Editor/Inspector/EventManager.jsx b/frontend/src/Editor/Inspector/EventManager.jsx
index 7a05d8db0c..6bc3da16de 100644
--- a/frontend/src/Editor/Inspector/EventManager.jsx
+++ b/frontend/src/Editor/Inspector/EventManager.jsx
@@ -12,18 +12,18 @@ import { componentTypes } from '../WidgetManager/components';
import Select from '@/_ui/Select';
import defaultStyles from '@/_ui/Select/styles';
import { useTranslation } from 'react-i18next';
-
import { useDataQueriesStore } from '@/_stores/dataQueriesStore';
-import AddRectangle from '@/_ui/Icon/bulkIcons/AddRectangle';
-import { Tooltip } from 'react-tooltip';
-import { ButtonSolid } from '@/_ui/AppButton/AppButton';
import RunjsParameters from './ActionConfigurationPanels/RunjsParamters';
+import AddNewButton from '@/ToolJetUI/Buttons/AddNewButton/AddNewButton';
import { isQueryRunnable } from '@/_helpers/utils';
import { shallow } from 'zustand/shallow';
+import ManageEventButton from './ManageEventButton';
+import NoListItem from './Components/Table/NoListItem';
export const EventManager = ({
component,
componentMeta,
+ currentState = {},
components,
eventsChanged,
apps,
@@ -121,18 +121,19 @@ export const EventManager = ({
});
return componentOptions;
}
-
+ // currently blocking items inside subcontainer because they can't be controlled through event manager
+ // use components instead of currentState?.components to get all the components in canvas
function getComponentOptionsOfComponentsWithActions(componentType = '') {
let componentOptions = [];
- Object.keys(components || {}).forEach((key) => {
+ Object.values(currentState?.components || {}).forEach((value) => {
const targetComponentMeta = componentTypes.find(
- (componentType) => components[key].component.component === componentType.component
+ (componentType) => components[value.id]?.component?.component === componentType?.component
);
if ((targetComponentMeta?.actions?.length ?? 0) > 0) {
- if (componentType === '' || components[key].component.component === componentType) {
+ if (componentType === '' || components[value.id].component.component === componentType) {
componentOptions.push({
- name: components[key].component.name,
- value: key,
+ name: components[value.id].component.name,
+ value: value.id,
});
}
}
@@ -192,11 +193,20 @@ export const EventManager = ({
return appsOptionsList;
}
- function getPageOptions() {
- return pages.map((page) => ({
- name: page.name,
- value: page.id,
- }));
+ function getPageOptions(event) {
+ // If disabled page is already selected then don't remove from page options
+ if (pages.find((page) => page.id === event.pageId)?.disabled) {
+ return pages.map((page) => ({
+ name: page.name,
+ value: page.id,
+ }));
+ }
+ return pages
+ .filter((page) => !page.disabled)
+ .map((page) => ({
+ name: page.name,
+ value: page.id,
+ }));
}
function handlerChanged(index, param, value) {
@@ -251,7 +261,7 @@ export const EventManager = ({
@@ -662,7 +672,7 @@ export const EventManager = ({
event={event}
handlerChanged={handlerChanged}
eventIndex={index}
- getPages={getPageOptions}
+ getPages={() => getPageOptions(event)}
darkMode={darkMode}
/>
)}
@@ -834,92 +844,14 @@ export const EventManager = ({
if (typeof popOverCallback === 'function') popOverCallback(showing);
}}
>
-
-
-
-
-
-
- {componentMeta.events[event.eventId]['displayName']}
-
-
-
- {actionMeta.name}
-
-
-
-
{
- e.stopPropagation();
- removeHandler(index);
- }}
- data-cy="delete-button"
- data-tooltip-id="event-delete-btn-icon"
- data-tooltip-content="Delete"
- >
-
-
-
-
-
-
-
+
+
);
@@ -937,48 +869,27 @@ export const EventManager = ({
const renderAddHandlerBtn = () => {
return (
-
-
-
-
- {t('editor.inspector.eventManager.addHandler', 'Add handler')}
-
-
+
+ {t('editor.inspector.eventManager.addHandler', 'New event handler')}
+
);
};
- const componentName = componentMeta.name ? componentMeta.name : 'query';
-
if (events.length === 0) {
return (
<>
+ {!hideEmptyEventsAlert &&
}
{renderAddHandlerBtn()}
- {!hideEmptyEventsAlert ? (
-
-
- {t(
- 'editor.inspector.eventManager.emptyMessage',
- "This {{componentName}} doesn't have any event handlers",
- {
- componentName: componentName.toLowerCase(),
- }
- )}
-
-
- ) : null}
>
);
}
return (
<>
- {renderHandlers(events)}
- {renderAddHandlerBtn()}
+
+ {renderHandlers(events)}
+ {renderAddHandlerBtn()}
+
>
);
};
diff --git a/frontend/src/Editor/Inspector/Inspector.jsx b/frontend/src/Editor/Inspector/Inspector.jsx
index f349abe3a7..ae98efeb58 100644
--- a/frontend/src/Editor/Inspector/Inspector.jsx
+++ b/frontend/src/Editor/Inspector/Inspector.jsx
@@ -1,5 +1,4 @@
import React, { useState, useRef, useEffect } from 'react';
-import cx from 'classnames';
import { componentTypes } from '../WidgetManager/components';
import { Table } from './Components/Table/Table.jsx';
import { Chart } from './Components/Chart';
@@ -23,6 +22,35 @@ import { useCurrentState } from '@/_stores/currentStateStore';
import { useDataQueries } from '@/_stores/dataQueriesStore';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { shallow } from 'zustand/shallow';
+import Tabs from '@/ToolJetUI/Tabs/Tabs';
+import Tab from '@/ToolJetUI/Tabs/Tab';
+import Student from '@/_ui/Icon/solidIcons/Student';
+import ArrowRight from '@/_ui/Icon/solidIcons/ArrowRight';
+import ArrowLeft from '@/_ui/Icon/solidIcons/ArrowLeft';
+import SolidIcon from '@/_ui/Icon/SolidIcons';
+import { OverlayTrigger, Popover } from 'react-bootstrap';
+import Edit from '@/_ui/Icon/bulkIcons/Edit';
+import Copy from '@/_ui/Icon/solidIcons/Copy';
+import Trash from '@/_ui/Icon/solidIcons/Trash';
+import classNames from 'classnames';
+
+const INSPECTOR_HEADER_OPTIONS = [
+ {
+ label: 'Rename',
+ value: 'rename',
+ icon:
,
+ },
+ {
+ label: 'Duplicate',
+ value: 'duplicate',
+ icon:
,
+ },
+ {
+ label: 'Delete',
+ value: 'delete',
+ icon:
,
+ },
+];
export const Inspector = ({
selectedComponentId,
@@ -33,6 +61,7 @@ export const Inspector = ({
switchSidebarTab,
removeComponent,
pages,
+ cloneComponents,
}) => {
const dataQueries = useDataQueries();
const component = {
@@ -49,6 +78,7 @@ export const Inspector = ({
const [newComponentName, setNewComponentName] = useState(component.component.name);
const [inputRef, setInputFocus] = useFocus();
const [selectedTab, setSelectedTab] = useState('properties');
+ const [showHeaderActionsMenu, setShowHeaderActionsMenu] = useState(false);
const { isVersionReleased } = useAppVersionStore(
(state) => ({
isVersionReleased: state.isVersionReleased,
@@ -91,6 +121,7 @@ export const Inspector = ({
function handleComponentNameChange(newName) {
if (component.component.name === newName) return;
+
if (newName.length === 0) {
toast.error(t('widget.common.widgetNameEmptyError', 'Widget name cannot be empty'));
return setInputFocus();
@@ -132,6 +163,23 @@ export const Inspector = ({
if (attr) {
allParams[param.name][attr] = value;
const defaultValue = getDefaultValue(value);
+ // This is needed to have enable pagination as backward compatible
+ // Whenever enable pagination is false, we turn client and server side pagination as false
+ if (param.name === 'enablePagination' && !resolveReferences(value, currentState)) {
+ if (allParams?.['clientSidePagination']?.[attr]) {
+ allParams['clientSidePagination'][attr] = value;
+ }
+ allParams['serverSidePagination'][attr] = value;
+ }
+ // This case is required to handle for older apps when serverSidePagination is connected to Fx
+ if (param.name === 'serverSidePagination' && !allParams?.['enablePagination']?.[attr]) {
+ allParams = {
+ ...allParams,
+ enablePagination: {
+ value: true,
+ },
+ };
+ }
if (param.type === 'select' && defaultValue) {
allParams[defaultValue.paramName]['value'] = defaultValue.value;
}
@@ -246,6 +294,17 @@ export const Inspector = ({
componentDefinitionChanged(newComponent);
}
+ const handleInspectorHeaderActions = (value) => {
+ if (value === 'rename') {
+ setTimeout(() => setInputFocus(), 0);
+ }
+ if (value === 'delete') {
+ setWidgetDeleteConfirmation(true);
+ }
+ if (value === 'duplicate') {
+ cloneComponents();
+ }
+ };
const buildGeneralStyle = () => {
const items = [];
@@ -272,27 +331,29 @@ export const Inspector = ({
};
const propertiesTab = isMounted && (
-
+
+
+
);
const stylesTab = (
-
+
);
+ React.useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (showHeaderActionsMenu && event.target.closest('.list-menu') === null) {
+ setShowHeaderActionsMenu(false);
+ }
+ };
+
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [JSON.stringify({ showHeaderActionsMenu })]);
+
return (
-
-
-
+
+
switchSidebarTab(2)}>
+
+
+
+
+
+
+
-
-
switchSidebarTab(2)}>
-
+
-
-
-
-
-
-
-
-
- {selectedTab === 'properties' && propertiesTab}
- {selectedTab === 'styles' && stylesTab}
+
+
+
+ {propertiesTab}
+
+
+ {stylesTab}
+
+
-
-
+
);
};
diff --git a/frontend/src/Editor/Inspector/ManageEventButton.jsx b/frontend/src/Editor/Inspector/ManageEventButton.jsx
new file mode 100644
index 0000000000..4448538f6f
--- /dev/null
+++ b/frontend/src/Editor/Inspector/ManageEventButton.jsx
@@ -0,0 +1,61 @@
+import React, { useState } from 'react';
+import './manageEventButton.scss';
+import AddRectangle from '@/_ui/Icon/solidIcons/AddRectangle';
+import Trash from '@/_ui/Icon/solidIcons/Trash';
+import { ButtonSolid } from '@/_ui/AppButton/AppButton';
+
+const ManageEventButton = ({ eventDisplayName = 'Upon events', actionName, index, removeHandler }) => {
+ const [isHovered, setIsHovered] = useState(false);
+
+ return (
+
+
setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ >
+
+
+
+ {eventDisplayName}
+
+
+
+ {actionName ? actionName : 'Select action'}
+
+ {!actionName &&
}
+
+
+ {isHovered && (
+ {
+ event.stopPropagation();
+ }}
+ >
+ {
+ e.stopPropagation();
+ removeHandler(index);
+ }}
+ >
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+export default ManageEventButton;
diff --git a/frontend/src/Editor/Inspector/Utils.js b/frontend/src/Editor/Inspector/Utils.js
index 2e2f8415cb..6ffba6b90f 100644
--- a/frontend/src/Editor/Inspector/Utils.js
+++ b/frontend/src/Editor/Inspector/Utils.js
@@ -27,7 +27,8 @@ export function renderElement(
paramType,
currentState,
components = {},
- darkMode = false
+ darkMode = false,
+ verticalLine = true
) {
const componentConfig = component.component;
const componentDefinition = componentConfig.definition;
@@ -70,6 +71,7 @@ export function renderElement(
paramUpdated({ name: param, ...component.component.properties[param] }, 'fxActive', active, paramType);
}}
component={component}
+ verticalLine={verticalLine}
/>
);
}
diff --git a/frontend/src/Editor/Inspector/manageEventButton.scss b/frontend/src/Editor/Inspector/manageEventButton.scss
new file mode 100644
index 0000000000..abdf6445e8
--- /dev/null
+++ b/frontend/src/Editor/Inspector/manageEventButton.scss
@@ -0,0 +1,29 @@
+.manage-event-btn {
+ border-radius: 6px;
+ background-color: var(--slate3);
+ &:hover {
+ background-color: var(--slate4);
+ }
+ .event-handler-text {
+ font-size: 12px;
+ line-height: 20px;
+ color: var(--slate12);
+ font-weight: 500;
+ }
+ .event-name-text {
+ font-size: 12px;
+ line-height: 20px;
+ color: var(--slate11);
+ font-weight: 400;
+ display: flex;
+ align-items: center;
+ }
+ .event-action {
+ margin-right: 4px;
+ }
+
+ .list-menu-option-btn {
+ margin-left: 20px;
+ }
+
+}
\ No newline at end of file
diff --git a/frontend/src/Editor/LeftSidebar/SidebarComment.jsx b/frontend/src/Editor/LeftSidebar/SidebarComment.jsx
index 4a241cc9f7..8fe411c1ca 100644
--- a/frontend/src/Editor/LeftSidebar/SidebarComment.jsx
+++ b/frontend/src/Editor/LeftSidebar/SidebarComment.jsx
@@ -8,7 +8,6 @@ import { useEditorStore } from '@/_stores/editorStore';
import { shallow } from 'zustand/shallow';
export const LeftSidebarComment = forwardRef(({ selectedSidebarItem, currentPageId }, ref) => {
- const darkMode = localStorage.getItem('darkMode') === 'true';
const { appVersionsId } = useAppVersionStore(
(state) => ({
appVersionsId: state?.editingVersion?.id,
@@ -37,12 +36,11 @@ export const LeftSidebarComment = forwardRef(({ selectedSidebarItem, currentPage
0}
selectedSidebarItem={selectedSidebarItem}
- title={appVersionsId ? 'toggle comments' : 'Comments section will be available once you save this application'}
- icon={darkMode ? `comments-dark` : 'comments-light'}
- className={cx(`left-sidebar-item left-sidebar-layout sidebar-comments`, {
+ title={appVersionsId ? 'Toggle comments' : 'Comments section will be available once you save this application'}
+ icon={'comments'}
+ className={cx(`left-sidebar-item left-sidebar-layout`, {
disabled: !appVersionsId,
active: isActive,
- dark: darkMode,
})}
onClick={() => {
toggleActive(!isActive);
diff --git a/frontend/src/Editor/LeftSidebar/SidebarDatasources.jsx b/frontend/src/Editor/LeftSidebar/SidebarDatasources.jsx
index 6d2d5b344d..3d87129b0e 100644
--- a/frontend/src/Editor/LeftSidebar/SidebarDatasources.jsx
+++ b/frontend/src/Editor/LeftSidebar/SidebarDatasources.jsx
@@ -1,7 +1,7 @@
/* eslint-disable import/no-named-as-default */
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
-import { HeaderSection, Button } from '@/_ui/LeftSidebar';
+import { HeaderSection } from '@/_ui/LeftSidebar';
import { DataSourceManager } from '../DataSourceManager';
import { DataSourceTypes } from '../DataSourceManager/SourceComponents';
import { getSvgIcon } from '@/_helpers/appUtils';
@@ -17,6 +17,7 @@ import { getPrivateRoute } from '@/_helpers/routes';
import { useDataSources } from '@/_stores/dataSourcesStore';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { shallow } from 'zustand/shallow';
+import { ButtonSolid } from '@/_ui/AppButton/AppButton';
export const LeftSidebarDataSources = ({
appId,
@@ -130,7 +131,7 @@ export const LeftSidebarDataSources = ({
const popover = (
-
+
convertToGlobalDataSource(dataSource)}>
Change scope
@@ -238,18 +239,18 @@ const LeftSidebarDataSourcesContainer = ({ darkMode, RenderDataSource, dataSourc
-
+ data-cy={`left-sidebar-inspector`}
+ variant="tertiary"
+ className="left-sidebar-header-btn"
+ leftIcon={pinned ? 'unpin' : 'pin'}
+ iconWidth="14"
+ fill={`var(--slate12)`}
+ >
@@ -259,7 +260,7 @@ const LeftSidebarDataSourcesContainer = ({ darkMode, RenderDataSource, dataSourc
{dataSources.length ? (
<>
Local Data Sources
-
+
{dataSources?.map((source, idx) => (
{
+ const text = message.split(logProps.page);
+ return (
+
+
+ {text[0]}
+ {`'${logProps.page}'`}
+ {text[1]}
+
+
+ {moment(logProps?.timestamp).fromNow()}
+
+
+ );
};
return (
-
+
{
- console.log('Debug debugger: setOpen:', e);
setOpen((prev) => !prev);
}}
style={{ pointerEvents: logProps?.isQuerySuccessLog ? 'none' : 'default' }}
>
-
-
+
+
-
-
- {title}
- {moment(logProps?.timestamp).fromNow()}
-
-
- {message}
-
+
+ {logProps.type === 'navToDisablePage' ? (
+ renderNavToDisabledPageMessage()
+ ) : (
+ <>
+
+ {title}
+ {moment(logProps?.timestamp).fromNow()}
+
+
+ {message}
+
+ >
+ )}
diff --git a/frontend/src/Editor/LeftSidebar/SidebarDebugger/SidebarDebuggerHeader.jsx b/frontend/src/Editor/LeftSidebar/SidebarDebugger/SidebarDebuggerHeader.jsx
index d3ec7185d0..eeebad9ed3 100644
--- a/frontend/src/Editor/LeftSidebar/SidebarDebugger/SidebarDebuggerHeader.jsx
+++ b/frontend/src/Editor/LeftSidebar/SidebarDebugger/SidebarDebuggerHeader.jsx
@@ -1,27 +1,34 @@
import React from 'react';
-import { Button, HeaderSection } from '@/_ui/LeftSidebar';
+import { HeaderSection } from '@/_ui/LeftSidebar';
import _ from 'lodash';
+import { ButtonSolid } from '@/_ui/AppButton/AppButton';
export const SidebarDebuggerHeader = ({ darkMode, clearErrorLogs, setPinned, pinned }) => {
return (
-
-
-
+ variant="tertiary"
+ leftIcon={pinned ? 'unpin' : 'pin'}
+ iconWidth="14"
+ className="left-sidebar-header-btn"
+ fill={`var(--slate12)`}
+ >
diff --git a/frontend/src/Editor/LeftSidebar/SidebarDebugger/SidebarDebuggerTabs.jsx b/frontend/src/Editor/LeftSidebar/SidebarDebugger/SidebarDebuggerTabs.jsx
index db2a37a993..cd4f510ec1 100644
--- a/frontend/src/Editor/LeftSidebar/SidebarDebugger/SidebarDebuggerTabs.jsx
+++ b/frontend/src/Editor/LeftSidebar/SidebarDebugger/SidebarDebuggerTabs.jsx
@@ -8,9 +8,9 @@ import cx from 'classnames';
const DebuggerTabContent = ({ logs, darkMode, tabName }) => {
const { t } = useTranslation();
return (
-
+
{logs.length === 0 && (
-
+
{tabName === 'errors'
? t(`leftSidebar.Debugger.noErrors`, 'No errors found.')
: t(`leftSidebar.Debugger.noLogs`, 'No Logs found.')}
@@ -19,7 +19,7 @@ const DebuggerTabContent = ({ logs, darkMode, tabName }) => {
{logs.map((error, index) => (
@@ -35,8 +35,8 @@ const SidebarDebuggerTabs = ({ darkMode, errors, allLog }) => {
diff --git a/frontend/src/Editor/LeftSidebar/SidebarItem.jsx b/frontend/src/Editor/LeftSidebar/SidebarItem.jsx
index ef0adefee3..9516cdaa32 100644
--- a/frontend/src/Editor/LeftSidebar/SidebarItem.jsx
+++ b/frontend/src/Editor/LeftSidebar/SidebarItem.jsx
@@ -1,3 +1,4 @@
+import SolidIcon from '@/_ui/Icon/SolidIcons';
import React, { forwardRef } from 'react';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import Tooltip from 'react-bootstrap/Tooltip';
@@ -9,18 +10,22 @@ export const LeftSidebarItem = forwardRef(
ref
) => {
const { t } = useTranslation();
- const displayIcon = selectedSidebarItem === icon ? `${icon}-selected` : icon;
-
- const Icon = require('@assets/images/icons/editor/left-sidebar/' + displayIcon + '.svg');
-
+ let displayIcon = icon;
+ if (icon == 'page') displayIcon = 'file01';
const content = (
{icon && (
-
+
{commentBadge && }
)}
diff --git a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/EditInput.jsx b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/EditInput.jsx
index 4146a64237..f7c5f79df2 100644
--- a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/EditInput.jsx
+++ b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/EditInput.jsx
@@ -37,7 +37,7 @@ export const EditInput = ({ slug, error, setError, pageHandle, setPageHandle, is
event.stopPropagation()}
>
diff --git a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/GlobalSettings.jsx b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/GlobalSettings.jsx
index 7466ad626d..d2097a4c72 100644
--- a/frontend/src/Editor/LeftSidebar/SidebarPageSelector/GlobalSettings.jsx
+++ b/frontend/src/Editor/LeftSidebar/SidebarPageSelector/GlobalSettings.jsx
@@ -1,8 +1,8 @@
import React from 'react';
import { OverlayTrigger, Popover } from 'react-bootstrap';
-import MenuIcon from '@assets/images/icons/3dots-menu.svg';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { shallow } from 'zustand/shallow';
+import SolidIcon from '@/_ui/Icon/SolidIcons';
export const GlobalSettings = ({ darkMode, showHideViewerNavigationControls, showPageViwerPageNavitation }) => {
const { isVersionReleased, enableReleasedVersionPopupState } = useAppVersionStore(
@@ -27,7 +27,7 @@ export const GlobalSettings = ({ darkMode, showHideViewerNavigationControls, sho
placement={'bottom-end'}
rootClose={true}
overlay={
-
+