From c4b5ce499ddee694b3d3bc3b542372f9af0775a3 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Thu, 13 Feb 2025 15:40:51 +0530 Subject: [PATCH 01/66] Added shortcuts to run and preview a query in query panel. --- .../AppBuilder/QueryPanel/QueryKeyHooks.jsx | 48 +++++++++++++++++++ .../src/AppBuilder/QueryPanel/QueryPanel.jsx | 5 +- 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx diff --git a/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx b/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx new file mode 100644 index 0000000000..06d8958cab --- /dev/null +++ b/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import useStore from '@/AppBuilder/_stores/store'; +import { useHotkeys } from 'react-hotkeys-hook'; +import { useModuleId } from '@/AppBuilder/_contexts/ModuleContext'; + +const QueryKeyHooks = ({ children, isExpanded }) => { + const runQuery = useStore((state) => state.queryPanel.runQuery); + const selectedQuery = useStore((state) => state.queryPanel.selectedQuery); + const moduleId = useModuleId(); + const previewQuery = useStore((state) => state.queryPanel.previewQuery); + const selectedDataSource = useStore((state) => state.queryPanel.selectedDataSource); + const queryName = selectedQuery?.name ?? ''; + + const previewButtonOnClick = () => { + const _options = { ...selectedQuery.options }; + const query = { + data_source_id: selectedDataSource.id === 'null' ? null : selectedDataSource.id, + pluginId: selectedDataSource.pluginId, + options: _options, + kind: selectedDataSource.kind, + name: queryName, + id: selectedQuery?.id, + }; + previewQuery(query, false, undefined, moduleId).catch(({ error, data }) => { + console.log(error, data); + }); + }; + + const shortcutRef = useHotkeys( + ['mod+enter', 'mod+shift+enter'], + (event, handler) => { + if (handler.mod && handler.keys[0] === 'enter') { + if (handler.shift) { + previewButtonOnClick(); + } else runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true); + } + }, + { enabled: isExpanded } + ); + + return ( +
+ {children} +
+ ); +}; + +export default QueryKeyHooks; diff --git a/frontend/src/AppBuilder/QueryPanel/QueryPanel.jsx b/frontend/src/AppBuilder/QueryPanel/QueryPanel.jsx index 827efe6d33..8bf5d2ea53 100644 --- a/frontend/src/AppBuilder/QueryPanel/QueryPanel.jsx +++ b/frontend/src/AppBuilder/QueryPanel/QueryPanel.jsx @@ -11,6 +11,7 @@ import useStore from '@/AppBuilder/_stores/store'; import SectionCollapse from '@/_ui/Icon/solidIcons/SectionCollapse'; import SectionExpand from '@/_ui/Icon/solidIcons/SectionExpand'; import { shallow } from 'zustand/shallow'; +import QueryKeyHooks from './QueryKeyHooks'; const MemoizedQueryDataPane = memo(QueryDataPane); const MemoizedQueryManager = memo(QueryManager); @@ -192,14 +193,14 @@ export const QueryPanel = ({ darkMode }) => { }} > {isExpanded && ( -
+
-
+ )} From f234384f2528be4e08e05839fd5d1bbd8564a7af Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Fri, 7 Mar 2025 15:34:31 +0530 Subject: [PATCH 02/66] Fix: shortcut can be used throughtout the editor and added custom hook in codeeditor for query panel shortcuts. --- .../CodeEditor/MultiLineCodeEditor.jsx | 12 +++- .../CodeEditor/SingleLineCodeEditor.jsx | 10 +++- .../CodeEditor/useQueryPanelKeyHooks.js | 58 +++++++++++++++++++ .../AppBuilder/QueryPanel/QueryKeyHooks.jsx | 36 +++--------- .../_stores/slices/queryPanelSlice.js | 18 ++++++ 5 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js diff --git a/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx b/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx index 033d266e03..44259ce3a8 100644 --- a/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx +++ b/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx @@ -21,6 +21,7 @@ import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; import { search, searchKeymap, searchPanelOpen } from '@codemirror/search'; import { handleSearchPanel, SearchBtn } from './SearchBox'; +import { useQueryPanelKeyHooks } from './useQueryPanelKeyHooks'; const langSupport = Object.freeze({ javascript: javascript(), @@ -64,6 +65,8 @@ const MultiLineCodeEditor = (props) => { const [editorView, setEditorView] = React.useState(null); + const { queryPanelKeybindings } = useQueryPanelKeyHooks(onChange, currentValueRef, 'multiline'); + const handleOnBlur = () => { if (!delayOnChange) return onChange(currentValueRef.current); setTimeout(() => { @@ -85,6 +88,7 @@ const MultiLineCodeEditor = (props) => { highlightActiveLine: false, autocompletion: hideSuggestion ?? true, highlightActiveLineGutter: false, + defaultKeymap: false, completionKeymap: true, searchKeymap: false, }; @@ -187,7 +191,12 @@ const MultiLineCodeEditor = (props) => { }; } - const customKeyMaps = [...defaultKeymap, ...completionKeymap, ...searchKeymap]; + const customKeyMaps = [ + ...defaultKeymap.filter((keyBinding) => keyBinding.key !== 'Mod-Enter'), // Remove default keybinding for Mod-Enter + ...completionKeymap, + ...searchKeymap, + ]; + const customTabKeymap = keymap.of([ { key: 'Tab', @@ -208,6 +217,7 @@ const MultiLineCodeEditor = (props) => { return true; }, }, + ...queryPanelKeybindings, ]); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx b/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx index 1243f26f43..09489a6e2f 100644 --- a/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx +++ b/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx @@ -22,6 +22,7 @@ import CodeHinter from './CodeHinter'; import { removeNestedDoubleCurlyBraces } from '@/_helpers/utils'; import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; +import { useQueryPanelKeyHooks } from './useQueryPanelKeyHooks'; const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...restProps }) => { const { initialValue, onChange, enablePreview = true, portalProps } = restProps; @@ -170,6 +171,8 @@ const EditorInput = ({ onInputChange, }) => { const getSuggestions = useStore((state) => state.getSuggestions, shallow); + const { queryPanelKeybindings } = useQueryPanelKeyHooks(onBlurUpdate, currentValue, 'singleline'); + function autoCompleteExtensionConfig(context) { const hints = getSuggestions(); let word = context.matchBefore(/\w*/); @@ -229,7 +232,10 @@ const EditorInput = ({ maxRenderedOptions: 10, }); - const customKeyMaps = [...defaultKeymap, ...completionKeymap]; + const customKeyMaps = [ + ...defaultKeymap.filter((keyBinding) => keyBinding.key !== 'Mod-Enter'), // Remove default keybinding for Mod-Enter + ...completionKeymap, + ]; const customTabKeymap = keymap.of([ { key: 'Tab', @@ -251,6 +257,7 @@ const EditorInput = ({ } }, }, + ...queryPanelKeybindings, ]); const handleOnChange = React.useCallback((val) => { @@ -395,6 +402,7 @@ const EditorInput = ({ foldGutter: false, highlightActiveLine: false, autocompletion: true, + defaultKeymap: false, completionKeymap: true, searchKeymap: false, }} diff --git a/frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js b/frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js new file mode 100644 index 0000000000..1a41a7f19b --- /dev/null +++ b/frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js @@ -0,0 +1,58 @@ +import { useModuleId } from '@/AppBuilder/_contexts/ModuleContext'; +import useStore from '@/AppBuilder/_stores/store'; +import { useCallback, useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; + +export const useQueryPanelKeyHooks = (onChange, value, type) => { + const queryPanelHeight = useStore((state) => state.queryPanel.queryPanelHeight); + const runQueryOnShortcut = useStore((state) => state.queryPanel.runQueryOnShortcut); + const previewQueryOnShortcut = useStore((state) => state.queryPanel.previewQueryOnShortcut); + const moduleId = useModuleId(); + const location = useLocation(); + const { pathname } = location; + + const [queryPanelKeybindings, setQueryPanelKeybindings] = useState([]); + + const handleRunQuery = useCallback( + (view) => { + const isEditor = pathname.includes('/apps/'); + if (queryPanelHeight !== 0 && isEditor) { + onChange(type === 'multiline' ? value.current : value); + runQueryOnShortcut(); + } + return true; + }, + [queryPanelHeight, onChange, runQueryOnShortcut, value] + ); + + const handlePreviewQuery = useCallback( + (view) => { + const isEditor = pathname.includes('/apps/'); + if (queryPanelHeight !== 0 && isEditor) { + onChange(type === 'multiline' ? value.current : value); + previewQueryOnShortcut(moduleId); + } + return true; + }, + [queryPanelHeight, moduleId, onChange, previewQueryOnShortcut, value] + ); + + useEffect(() => { + setQueryPanelKeybindings([ + { + key: 'Mod-Enter', + preventDefault: true, + run: handleRunQuery, + }, + { + key: 'Mod-Shift-Enter', + preventDefault: true, + run: handlePreviewQuery, + }, + ]); + }, [handleRunQuery, handlePreviewQuery]); + + return { + queryPanelKeybindings, + }; +}; diff --git a/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx b/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx index 06d8958cab..13e29d5531 100644 --- a/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx +++ b/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx @@ -4,45 +4,23 @@ import { useHotkeys } from 'react-hotkeys-hook'; import { useModuleId } from '@/AppBuilder/_contexts/ModuleContext'; const QueryKeyHooks = ({ children, isExpanded }) => { - const runQuery = useStore((state) => state.queryPanel.runQuery); - const selectedQuery = useStore((state) => state.queryPanel.selectedQuery); + const runQueryOnShortcut = useStore((state) => state.queryPanel.runQueryOnShortcut); + const previewQueryOnShortcut = useStore((state) => state.queryPanel.previewQueryOnShortcut); const moduleId = useModuleId(); - const previewQuery = useStore((state) => state.queryPanel.previewQuery); - const selectedDataSource = useStore((state) => state.queryPanel.selectedDataSource); - const queryName = selectedQuery?.name ?? ''; - const previewButtonOnClick = () => { - const _options = { ...selectedQuery.options }; - const query = { - data_source_id: selectedDataSource.id === 'null' ? null : selectedDataSource.id, - pluginId: selectedDataSource.pluginId, - options: _options, - kind: selectedDataSource.kind, - name: queryName, - id: selectedQuery?.id, - }; - previewQuery(query, false, undefined, moduleId).catch(({ error, data }) => { - console.log(error, data); - }); - }; - - const shortcutRef = useHotkeys( + useHotkeys( ['mod+enter', 'mod+shift+enter'], (event, handler) => { if (handler.mod && handler.keys[0] === 'enter') { if (handler.shift) { - previewButtonOnClick(); - } else runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true); + previewQueryOnShortcut(moduleId); + } else runQueryOnShortcut(); } }, - { enabled: isExpanded } + { enabled: isExpanded, enableOnFormTags: ['input'] } ); - return ( -
- {children} -
- ); + return
{children}
; }; export default QueryKeyHooks; diff --git a/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js b/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js index fd49d2a5e4..bc695d3c9b 100644 --- a/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js @@ -1028,5 +1028,23 @@ export const createQueryPanelSlice = (set, get) => ({ isQuerySelected: (queryId) => { return get().queryPanel.selectedQuery?.id === queryId; }, + runQueryOnShortcut: () => { + const { queryPanel } = get(); + const { runQuery, selectedQuery } = queryPanel; + runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true); + }, + previewQueryOnShortcut: (moduleId = 'canvas') => { + const { queryPanel } = get(); + const { previewQuery, selectedQuery, selectedDataSource } = queryPanel; + const query = { + data_source_id: selectedDataSource.id === 'null' ? null : selectedDataSource.id, + pluginId: selectedDataSource.pluginId, + options: { ...selectedQuery?.options }, + kind: selectedDataSource.kind, + name: selectedQuery?.name ?? '', + id: selectedQuery?.id, + }; + previewQuery(query, false, undefined, moduleId); + }, }, }); From 2da6fdde4a49bf3f2fe593c62ad61badd36ae0f2 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Tue, 11 Mar 2025 14:18:32 +0530 Subject: [PATCH 03/66] Added icons for shortcuts in the run and preview button. --- .../QueryManager/Components/QueryManagerHeader.jsx | 13 +++++++++---- frontend/src/_styles/theme.scss | 8 ++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx index 75cbe9cef7..15eef7c6a6 100644 --- a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx +++ b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx @@ -252,7 +252,7 @@ const RunButton = ({ buttonLoadingState }) => { > {isInDraft && } @@ -299,6 +303,7 @@ const PreviewButton = ({ buttonLoadingState, onClick }) => { {t('editor.queryManager.preview', 'Preview')} + ⌘↑↩ ); }; diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss index 2d1f28be3d..f5f2131c59 100644 --- a/frontend/src/_styles/theme.scss +++ b/frontend/src/_styles/theme.scss @@ -8054,6 +8054,10 @@ tbody { min-width: 22px; } + .query-manager-btn-shortcut { + color: var(--text-disabled) !important; + } + &:hover { background-color: $color-light-indigo-04; color: $color-light-indigo-10; @@ -8142,6 +8146,10 @@ tbody { } + .query-manager-btn-shortcut { + color: var(--text-disabled) !important; + } + &:hover { border: 1px solid $color-light-slate-08; color: $color-light-slate-11; From 41cff2484022aa220a12c6d866a1452645b24c90 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Tue, 11 Mar 2025 17:29:15 +0530 Subject: [PATCH 04/66] Replaced run and preview buttons with design system button. --- .../Components/QueryManagerHeader.jsx | 47 +++++++------------ frontend/src/_styles/theme.scss | 12 ++--- frontend/src/_ui/Icon/solidIcons/Play01.jsx | 19 ++++++++ frontend/src/_ui/Icon/solidIcons/index.js | 3 ++ frontend/src/components/ui/Button/Button.jsx | 14 +++++- 5 files changed, 56 insertions(+), 39 deletions(-) create mode 100644 frontend/src/_ui/Icon/solidIcons/Play01.jsx diff --git a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx index 15eef7c6a6..6055c0538a 100644 --- a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx +++ b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx @@ -1,7 +1,5 @@ import React, { useState, forwardRef, useRef, useEffect } from 'react'; import RenameIcon from '../Icons/RenameIcon'; -import Eye1 from '@/_ui/Icon/solidIcons/Eye1'; -import Play from '@/_ui/Icon/solidIcons/Play'; import cx from 'classnames'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; @@ -13,6 +11,7 @@ import { decodeEntities } from '@/_helpers/utils'; import { canDeleteDataSource, canReadDataSource, canUpdateDataSource } from '@/_helpers'; import useStore from '@/AppBuilder/_stores/store'; import { useModuleId } from '@/AppBuilder/_contexts/ModuleContext'; +import { Button as ButtonComponent } from '@/components/ui/Button/Button'; export const QueryManagerHeader = forwardRef(({ darkMode, setActiveTab, activeTab }, ref) => { const moduleId = useModuleId(); @@ -250,29 +249,22 @@ const RunButton = ({ buttonLoadingState }) => { 'data-tooltip-content': 'Connect a data source to run', })} > - + Run ⌘↩ + {isInDraft && } ); @@ -291,19 +283,16 @@ const PreviewButton = ({ buttonLoadingState, onClick }) => { const { t } = useTranslation(); return ( - + Preview ⌘↑↩ + ); }; diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss index f5f2131c59..0eedde77da 100644 --- a/frontend/src/_styles/theme.scss +++ b/frontend/src/_styles/theme.scss @@ -8054,10 +8054,6 @@ tbody { min-width: 22px; } - .query-manager-btn-shortcut { - color: var(--text-disabled) !important; - } - &:hover { background-color: $color-light-indigo-04; color: $color-light-indigo-10; @@ -8146,10 +8142,6 @@ tbody { } - .query-manager-btn-shortcut { - color: var(--text-disabled) !important; - } - &:hover { border: 1px solid $color-light-slate-08; color: $color-light-slate-11; @@ -8257,6 +8249,10 @@ tbody { } } +.query-manager-btn-shortcut { + color: var(--text-disabled) !important; +} + .font-weight-500 { font-weight: 500; } diff --git a/frontend/src/_ui/Icon/solidIcons/Play01.jsx b/frontend/src/_ui/Icon/solidIcons/Play01.jsx new file mode 100644 index 0000000000..42d3c88835 --- /dev/null +++ b/frontend/src/_ui/Icon/solidIcons/Play01.jsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const Play01 = ({ fill = '#6A727C', width = '24', className = '', viewBox = '0 0 24 24' }) => ( + + + +); + +export default Play01; diff --git a/frontend/src/_ui/Icon/solidIcons/index.js b/frontend/src/_ui/Icon/solidIcons/index.js index 34a2410e1d..aa0c307fec 100644 --- a/frontend/src/_ui/Icon/solidIcons/index.js +++ b/frontend/src/_ui/Icon/solidIcons/index.js @@ -87,6 +87,7 @@ import Pin from './Pin.jsx'; import Unpin from './Unpin.jsx'; import AlignRight from './AlignRight'; import Play from './Play.jsx'; +import Play01 from './Play01.jsx'; import Plus from './Plus.jsx'; import Plus01 from './Plus01.jsx'; import Reload from './Reload.jsx'; @@ -692,6 +693,8 @@ const Icon = (props) => { return ; case 'ai-crown': return ; + case 'play01': + return ; default: return ; } diff --git a/frontend/src/components/ui/Button/Button.jsx b/frontend/src/components/ui/Button/Button.jsx index 03919b8e93..6f95221805 100644 --- a/frontend/src/components/ui/Button/Button.jsx +++ b/frontend/src/components/ui/Button/Button.jsx @@ -133,10 +133,20 @@ const Button = forwardRef( const iconFillColor = !defaultButtonFillColour.includes(fill) && fill ? fill : getDefaultIconFillColor(variant); const Comp = asChild ? Slot : 'Button'; const leadingIconElement = leadingIcon && ( - +
+ +
); const trailingIconElement = trailingIcon && ( - +
+ +
); return ( From a3db1f4b3b57ca0ee3baa8ef2b8c254779e00463 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Tue, 18 Mar 2025 14:45:19 +0530 Subject: [PATCH 05/66] Added tooltips to run and preview buttons. --- .../Components/QueryManagerHeader.jsx | 64 +++++++++---------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx index 6055c0538a..5edd5421d4 100644 --- a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx +++ b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx @@ -5,7 +5,7 @@ import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import { DATA_SOURCE_TYPE } from '@/_helpers/constants'; import { shallow } from 'zustand/shallow'; -import { Tooltip } from 'react-tooltip'; +import { ToolTip } from '@/_components'; import { Button } from 'react-bootstrap'; import { decodeEntities } from '@/_helpers/utils'; import { canDeleteDataSource, canReadDataSource, canUpdateDataSource } from '@/_helpers'; @@ -243,29 +243,21 @@ const RunButton = ({ buttonLoadingState }) => { ); return ( - - runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true)} - leadingIcon="play01" - disabled={isInDraft} - isLoading={isLoading} - className="!tw-w-[88px]" - data-cy="query-run-button" - {...(isInDraft && { - 'data-tooltip-id': 'query-header-btn-run', - 'data-tooltip-content': 'Publish the query to run', - })} - > - Run ⌘↩ - - {isInDraft && } + + + runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true)} + leadingIcon="play01" + disabled={isInDraft} + isLoading={isLoading} + className="!tw-w-[88px]" + data-cy="query-run-button" + > + Run ⌘↩ + + ); }; @@ -283,16 +275,18 @@ const PreviewButton = ({ buttonLoadingState, onClick }) => { const { t } = useTranslation(); return ( - - Preview ⌘↑↩ - + + + Preview + + ); }; From 2a4fb0055dc81bf5c6d1c97c460441557d3cf352 Mon Sep 17 00:00:00 2001 From: Kartik Gupta Date: Thu, 20 Mar 2025 14:16:29 +0530 Subject: [PATCH 06/66] control overflow chaining for code editor --- frontend/src/_styles/queryManager.scss | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/_styles/queryManager.scss b/frontend/src/_styles/queryManager.scss index 7b256c3812..057dbc7a2d 100644 --- a/frontend/src/_styles/queryManager.scss +++ b/frontend/src/_styles/queryManager.scss @@ -1250,6 +1250,7 @@ $border-radius: 4px; color: var(--slate12) !important; } } + &.data-source-exists { .cm-editor { border-radius: 0 4px 4px 0 !important; @@ -1834,6 +1835,7 @@ $border-radius: 4px; .cm-scroller { border-bottom-left-radius: 4px; + overscroll-behavior: auto !important; } } @@ -1853,18 +1855,19 @@ $border-radius: 4px; margin-left: 32px !important; } -.tjdb-codhinter-wrapper{ - .codehinter-input{ - .cm-editor{ +.tjdb-codhinter-wrapper { + .codehinter-input { + .cm-editor { height: 30px !important; min-height: 30px !important; border-radius: 0 !important; - border-right: 0 ; - } + border-right: 0; + } } } -.tjdb-limit-offset-codehinter{ - .cm-editor{ + +.tjdb-limit-offset-codehinter { + .cm-editor { height: 30px !important; min-height: 30px !important; } @@ -1894,7 +1897,7 @@ $border-radius: 4px; margin-right: auto; p { - display: flex; + display: flex; align-items: center; span { From d5b3708de00c299de8690ad6e4de93f62116a459 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Thu, 20 Mar 2025 21:38:15 +0530 Subject: [PATCH 07/66] feat: Makes header and footer resizable --- .../AppBuilder/AppCanvas/Grid/gridUtils.js | 21 +++ .../Inspector/Components/Form.jsx | 19 --- .../AppBuilder/WidgetManager/widgets/form.js | 7 + .../Form/Components/HorizontalSlot.jsx | 78 ++++++++++ frontend/src/AppBuilder/Widgets/Form/Form.jsx | 137 ++++++++---------- .../src/AppBuilder/Widgets/Form/form.scss | 45 ++++++ .../src/AppBuilder/_hooks/useActiveSlot.js | 46 ++++++ frontend/src/AppBuilder/_hooks/useMoveable.js | 135 +++++++++++++++++ .../src/Editor/WidgetManager/configs/form.js | 7 + .../apps/services/widget-config/form.js | 7 + 10 files changed, 408 insertions(+), 94 deletions(-) create mode 100644 frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx create mode 100644 frontend/src/AppBuilder/_hooks/useActiveSlot.js create mode 100644 frontend/src/AppBuilder/_hooks/useMoveable.js diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/gridUtils.js b/frontend/src/AppBuilder/AppCanvas/Grid/gridUtils.js index da179bc11d..b18dd9d311 100644 --- a/frontend/src/AppBuilder/AppCanvas/Grid/gridUtils.js +++ b/frontend/src/AppBuilder/AppCanvas/Grid/gridUtils.js @@ -415,6 +415,27 @@ 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'); + + document.getElementById('real-canvas')?.classList.add('hide-grid'); + document.getElementById('real-canvas')?.classList.remove('show-grid'); +} + +export function hideGridLinesOnSlot(slotId) { + var canvasElm = document.getElementById(`canvas-${slotId}`); + + + canvasElm.classList.remove('show-grid'); + canvasElm.classList.add('hide-grid'); + + document.getElementById('real-canvas')?.classList.remove('hide-grid'); + document.getElementById('real-canvas')?.classList.add('show-grid'); +} + // Track previously active elements for efficient cleanup let previousActiveWidgets = null; let previousActiveCanvas = null; diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx index b39924854e..e0178ddbeb 100644 --- a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx +++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx @@ -168,25 +168,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, diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/form.js b/frontend/src/AppBuilder/WidgetManager/widgets/form.js index 2d8eb7f0a8..c5194822b6 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/form.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/form.js @@ -294,6 +294,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' }, diff --git a/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx b/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx new file mode 100644 index 0000000000..0e15e4a058 --- /dev/null +++ b/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx @@ -0,0 +1,78 @@ +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 + }) => { + const parsedHeight = parseInt(height, 10); + + const { getRootProps, getHandleProps, getResizeState } = useResizable({ + initialHeight: parsedHeight, + initialWidth: '100%', // Now respects parent's width + minHeight: 40, + 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; + + return ( +
+
+ +
+
+ + {isDisabled && ( +
{}} + onDrop={(e) => e.stopPropagation()} + /> + )} +
+ ); + } +); diff --git a/frontend/src/AppBuilder/Widgets/Form/Form.jsx b/frontend/src/AppBuilder/Widgets/Form/Form.jsx index afeb4cf844..ffa2373a96 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Form.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Form.jsx @@ -14,6 +14,9 @@ import { CONTAINER_FORM_CANVAS_PADDING, SUBCONTAINER_CANVAS_BORDER_WIDTH, } from '@/AppBuilder/AppCanvas/appCanvasConstants'; +import { HorizontalSlot } from './Components/HorizontalSlot'; +import { useActiveSlot } from '@/AppBuilder/_hooks/useActiveSlot'; + import './form.scss'; const getCanvasHeight = (height) => { @@ -35,6 +38,7 @@ export const Form = function Form(props) { properties, resetComponent = () => {}, dataCy, + onComponentClick, } = props; const childComponents = useStore((state) => state.getChildComponents(id), shallow); const { @@ -46,16 +50,7 @@ export const Form = function Form(props) { footerBackgroundColor, headerBackgroundColor, } = styles; - const { - buttonToSubmit, - loadingState, - advanced, - JSONSchema, - showHeader = false, - showFooter = false, - visibility, - disabledState, - } = properties; + const { buttonToSubmit, advanced, JSONSchema, showHeader = false, showFooter = false } = properties; const { isDisabled, isVisible, isLoading } = useExposeState( properties.loadingState, properties.visibility, @@ -76,16 +71,6 @@ export const Form = function Form(props) { 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, - }; - const formContent = { overflow: 'hidden auto', display: 'flex', @@ -96,13 +81,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,7 +88,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(() => { @@ -287,6 +264,38 @@ export const Form = function Form(props) { setChildrenData(childDataRef.current); }; + const activeSlot = useActiveSlot(id); // Track the active slot for this widget + const setComponentProperty = useStore((state) => state.setComponentProperty, shallow); + const updateHeaderSizeInStore = ({ newHeight }) => { + const heightInPx = `${parseInt(newHeight, 10)}px`; + console.log('newHeight', newHeight); + setComponentProperty(id, `headerHeight`, heightInPx, 'properties', 'value', false); + }; + + const updateFooterSizeInStore = ({ newHeight }) => { + const heightInPx = `${parseInt(newHeight, 10)}px`; + console.log('newHeight', newHeight); + setComponentProperty(id, `footerHeight`, heightInPx, 'properties', 'value', false); + }; + const formFooter = { + flexShrink: 0, + paddingTop: '3px', + paddingBottom: '7px', + paddingLeft: `${CONTAINER_FORM_CANVAS_PADDING}px`, + paddingRight: `${CONTAINER_FORM_CANVAS_PADDING}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`, + backgroundColor: + ['#fff', '#ffffffff'].includes(headerBackgroundColor) && darkMode ? '#1F2837' : headerBackgroundColor, + }; + return (
{showHeader && ( -
- - {isDisabled && ( -
{}} - onDrop={(e) => e.stopPropagation()} - /> - )} -
+ )} +
{isLoading ? (
@@ -382,30 +381,18 @@ export const Form = function Form(props) { )}
{showFooter && ( -
- - {isDisabled && ( - + )} ); diff --git a/frontend/src/AppBuilder/Widgets/Form/form.scss b/frontend/src/AppBuilder/Widgets/Form/form.scss index 530e837eb2..1758e1d0d1 100644 --- a/frontend/src/AppBuilder/Widgets/Form/form.scss +++ b/frontend/src/AppBuilder/Widgets/Form/form.scss @@ -1,3 +1,7 @@ +.jet-form-body { + background-color: inherit; +} + .wj-form-header { position: relative; &::after { @@ -38,3 +42,44 @@ box-sizing: content-box; 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; + + &:hover { + box-shadow: 0 0 0 1px var(--border-weak); + } + + &.active { + 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: 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-footer .resize-handle { + top: -4px; + bottom: unset; +} diff --git a/frontend/src/AppBuilder/_hooks/useActiveSlot.js b/frontend/src/AppBuilder/_hooks/useActiveSlot.js new file mode 100644 index 0000000000..bc3269a7ca --- /dev/null +++ b/frontend/src/AppBuilder/_hooks/useActiveSlot.js @@ -0,0 +1,46 @@ +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(() => { + const handleClick = (event) => { + let target = event.target; + + // 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); + }; + + // Attach single click if the widget is selected, otherwise listen for double-click + const eventType = isSelected ? 'click' : 'dblclick'; + + document.addEventListener(eventType, handleClick); + + return () => { + document.removeEventListener(eventType, handleClick); + }; + }, [widgetId, isSelected]); // Re-run when widgetId or selection state changes + + return activeSlot; +}; diff --git a/frontend/src/AppBuilder/_hooks/useMoveable.js b/frontend/src/AppBuilder/_hooks/useMoveable.js new file mode 100644 index 0000000000..ea2687e473 --- /dev/null +++ b/frontend/src/AppBuilder/_hooks/useMoveable.js @@ -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; diff --git a/frontend/src/Editor/WidgetManager/configs/form.js b/frontend/src/Editor/WidgetManager/configs/form.js index 2d8eb7f0a8..c5194822b6 100644 --- a/frontend/src/Editor/WidgetManager/configs/form.js +++ b/frontend/src/Editor/WidgetManager/configs/form.js @@ -294,6 +294,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' }, diff --git a/server/src/modules/apps/services/widget-config/form.js b/server/src/modules/apps/services/widget-config/form.js index 2d8eb7f0a8..c5194822b6 100644 --- a/server/src/modules/apps/services/widget-config/form.js +++ b/server/src/modules/apps/services/widget-config/form.js @@ -294,6 +294,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' }, From 390445d5e1461f84c5eae5826a6c8904c73663bb Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Tue, 25 Mar 2025 00:07:27 +0530 Subject: [PATCH 08/66] revert submodule commit --- frontend/ee | 2 +- server/ee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/ee b/frontend/ee index 1cd7afea26..715a830c7a 160000 --- a/frontend/ee +++ b/frontend/ee @@ -1 +1 @@ -Subproject commit 1cd7afea262f3b72de08da83e544911571fc05b9 +Subproject commit 715a830c7a8d75efc7f77106292d9e4499005b69 diff --git a/server/ee b/server/ee index e2db6a5fa9..0eefbb71a1 160000 --- a/server/ee +++ b/server/ee @@ -1 +1 @@ -Subproject commit e2db6a5fa9f64a9795136b2874c8041dede4b480 +Subproject commit 0eefbb71a1d5288f49641af5efaaab25970f27d1 From 7a50c41103128ef312406d5b78e510528acada0d Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Tue, 25 Mar 2025 21:52:37 +0530 Subject: [PATCH 09/66] Fixes interaction bugs with slot resizing --- .../Form/Components/HorizontalSlot.jsx | 15 ++++--- frontend/src/AppBuilder/Widgets/Form/Form.jsx | 10 +++-- .../src/AppBuilder/Widgets/Form/form.scss | 25 ++++++++++- .../src/AppBuilder/_hooks/useActiveSlot.js | 41 ++++++++++++++++--- 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx b/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx index 0e15e4a058..e892dc4f9f 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx @@ -13,15 +13,16 @@ export const HorizontalSlot = React.memo( isActive, slotName = 'header', // 'header' or 'footer' slotStyle = {}, - onResize + onResize, + maxHeight, }) => { const parsedHeight = parseInt(height, 10); const { getRootProps, getHandleProps, getResizeState } = useResizable({ initialHeight: parsedHeight, initialWidth: '100%', // Now respects parent's width - minHeight: 40, - maxHeight: 400, + minHeight: 10, + maxHeight: maxHeight || 400, maxWidth: '100%', stepHeight: 10, // Height will change in steps of 10px onResize: () => {}, @@ -33,8 +34,6 @@ export const HorizontalSlot = React.memo( const { height: resizedHeight, isDragging } = getResizeState(); - - useEffect(() => { if (isDragging) { showGridLinesOnSlot(id); @@ -47,7 +46,10 @@ export const HorizontalSlot = React.memo( return (
-
+
diff --git a/frontend/src/AppBuilder/Widgets/Form/Form.jsx b/frontend/src/AppBuilder/Widgets/Form/Form.jsx index ffa2373a96..1328fc195d 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Form.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Form.jsx @@ -268,21 +268,24 @@ export const Form = function Form(props) { const setComponentProperty = useStore((state) => state.setComponentProperty, shallow); const updateHeaderSizeInStore = ({ newHeight }) => { const heightInPx = `${parseInt(newHeight, 10)}px`; - console.log('newHeight', newHeight); setComponentProperty(id, `headerHeight`, heightInPx, 'properties', 'value', false); }; const updateFooterSizeInStore = ({ newHeight }) => { const heightInPx = `${parseInt(newHeight, 10)}px`; - console.log('newHeight', newHeight); setComponentProperty(id, `footerHeight`, heightInPx, 'properties', 'value', false); }; + + // debugger; + 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, }; @@ -292,13 +295,14 @@ export const Form = function Form(props) { 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 (
{ export const useActiveSlot = (widgetId) => { const [activeSlot, setActiveSlot] = useState(''); // Default to widget ID const isSelected = useIsWidgetSelected(widgetId); // Check if widget is selected + useEffect(() => { - const handleClick = (event) => { + if (!isSelected) { + setActiveSlot(''); + } + }, [isSelected]); + + useEffect(() => { + const handleDoubleClick = (event) => { let target = event.target; // Traverse up to find a slot with an id @@ -31,16 +38,40 @@ export const useActiveSlot = (widgetId) => { // If no slot is found, reset to widget ID setActiveSlot(widgetId); }; + const handleSingleClick = (event) => { + let target = event.target; + + // 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 - const eventType = isSelected ? 'click' : 'dblclick'; + // const eventType = isSelected ? 'click' : 'dblclick'; + const eventType = 'dblclick'; - document.addEventListener(eventType, handleClick); + document.addEventListener(eventType, handleDoubleClick); + document.addEventListener('click', handleSingleClick); return () => { - document.removeEventListener(eventType, handleClick); + document.removeEventListener(eventType, handleDoubleClick); + document.removeEventListener('click', handleSingleClick); }; - }, [widgetId, isSelected]); // Re-run when widgetId or selection state changes + }, [widgetId]); // Re-run when widgetId or selection state changes return activeSlot; }; From b40b37f5c3835aab55b4c512fd88afec12682a1b Mon Sep 17 00:00:00 2001 From: Nakul Nagargade Date: Wed, 26 Mar 2025 11:45:12 +0530 Subject: [PATCH 10/66] add padding --- .../WidgetManager/widgets/buttonGroup.js | 14 ++++++++++++++ .../AppBuilder/WidgetManager/widgets/checkbox.js | 15 +++++++++++++++ .../WidgetManager/widgets/colorPicker.js | 14 ++++++++++++++ .../src/AppBuilder/WidgetManager/widgets/icon.js | 15 +++++++++++++++ .../WidgetManager/widgets/rangeslider.js | 14 ++++++++++++++ .../WidgetManager/widgets/starrating.js | 14 ++++++++++++++ .../src/AppBuilder/WidgetManager/widgets/tags.js | 14 ++++++++++++++ .../WidgetManager/widgets/toggleswitchv2.js | 15 +++++++++++++++ .../Editor/WidgetManager/configs/buttonGroup.js | 14 ++++++++++++++ .../src/Editor/WidgetManager/configs/checkbox.js | 15 +++++++++++++++ .../Editor/WidgetManager/configs/colorPicker.js | 14 ++++++++++++++ frontend/src/Editor/WidgetManager/configs/icon.js | 15 +++++++++++++++ .../Editor/WidgetManager/configs/rangeslider.js | 14 ++++++++++++++ .../Editor/WidgetManager/configs/starrating.js | 14 ++++++++++++++ frontend/src/Editor/WidgetManager/configs/tags.js | 14 ++++++++++++++ .../WidgetManager/configs/toggleswitchv2.js | 15 +++++++++++++++ .../apps/services/widget-config/buttonGroup.js | 14 ++++++++++++++ .../apps/services/widget-config/checkbox.js | 15 +++++++++++++++ .../apps/services/widget-config/colorPicker.js | 14 ++++++++++++++ .../modules/apps/services/widget-config/icon.js | 15 +++++++++++++++ .../apps/services/widget-config/rangeslider.js | 14 ++++++++++++++ .../apps/services/widget-config/starrating.js | 14 ++++++++++++++ .../modules/apps/services/widget-config/tags.js | 14 ++++++++++++++ .../apps/services/widget-config/toggleswitchv2.js | 15 +++++++++++++++ 24 files changed, 345 insertions(+) diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/buttonGroup.js b/frontend/src/AppBuilder/WidgetManager/widgets/buttonGroup.js index c0fa889dd5..d7f479624b 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/buttonGroup.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/buttonGroup.js @@ -123,6 +123,19 @@ export const buttonGroupConfig = { defaultValue: '#007bff', }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { selected: [1], @@ -148,6 +161,7 @@ export const buttonGroupConfig = { disabledState: { value: '{{false}}' }, selectedTextColor: { value: '#FFFFFF' }, selectedBackgroundColor: { value: '#4368E3' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/checkbox.js b/frontend/src/AppBuilder/WidgetManager/widgets/checkbox.js index c9b6424020..9f991be251 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/checkbox.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/checkbox.js @@ -126,6 +126,20 @@ export const checkboxConfig = { ], accordian: 'label', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'switch', + }, }, exposedVariables: { value: false, @@ -189,6 +203,7 @@ export const checkboxConfig = { handleColor: { value: '#FFFFFF' }, alignment: { value: 'right' }, boxShadow: { value: '0px 0px 0px 0px #00000090' }, + padding: { value: 'default' }, }, validation: { mandatory: { value: '{{false}}' }, diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/colorPicker.js b/frontend/src/AppBuilder/WidgetManager/widgets/colorPicker.js index b2fddd7e4c..6d93508891 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/colorPicker.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/colorPicker.js @@ -26,6 +26,19 @@ export const colorPickerConfig = { }, styles: { visibility: { type: 'toggle', displayName: 'Visibility' }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { selectedColorHex: '#000000', @@ -45,6 +58,7 @@ export const colorPickerConfig = { events: [], styles: { visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/icon.js b/frontend/src/AppBuilder/WidgetManager/widgets/icon.js index aea06c976c..8c0b0880e4 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/icon.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/icon.js @@ -78,6 +78,20 @@ export const iconConfig = { }, accordian: 'Icon', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'Icon', + }, }, exposedVariables: {}, actions: [ @@ -116,6 +130,7 @@ export const iconConfig = { styles: { iconColor: { value: '#000' }, iconAlign: { value: 'center' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/rangeslider.js b/frontend/src/AppBuilder/WidgetManager/widgets/rangeslider.js index 151dca3384..541ed95209 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/rangeslider.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/rangeslider.js @@ -84,6 +84,19 @@ export const rangeSliderConfig = { defaultValue: true, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { value: null, @@ -111,6 +124,7 @@ export const rangeSliderConfig = { handleColor: { value: '' }, trackColor: { value: '' }, visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/starrating.js b/frontend/src/AppBuilder/WidgetManager/widgets/starrating.js index 01240d0369..d6caf8013c 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/starrating.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/starrating.js @@ -89,6 +89,19 @@ export const starratingConfig = { defaultValue: false, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { value: 0, @@ -112,6 +125,7 @@ export const starratingConfig = { labelColor: { value: '' }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/tags.js b/frontend/src/AppBuilder/WidgetManager/widgets/tags.js index 8af289b23a..6479eeaad0 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/tags.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/tags.js @@ -38,6 +38,19 @@ export const tagsConfig = { defaultValue: true, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: {}, definition: { @@ -54,6 +67,7 @@ export const tagsConfig = { events: [], styles: { visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/toggleswitchv2.js b/frontend/src/AppBuilder/WidgetManager/widgets/toggleswitchv2.js index 6753fbb50d..6f61a7645d 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/toggleswitchv2.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/toggleswitchv2.js @@ -126,6 +126,20 @@ export const toggleSwitchV2Config = { validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } }, accordian: 'switch', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'switch', + }, }, exposedVariables: { value: false, @@ -187,6 +201,7 @@ export const toggleSwitchV2Config = { handleColor: { value: '#FFFFFF' }, alignment: { value: 'right' }, boxShadow: { value: '0px 0px 0px 0px #00000090' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/Editor/WidgetManager/configs/buttonGroup.js b/frontend/src/Editor/WidgetManager/configs/buttonGroup.js index 65b7e77807..4a1d5ff218 100644 --- a/frontend/src/Editor/WidgetManager/configs/buttonGroup.js +++ b/frontend/src/Editor/WidgetManager/configs/buttonGroup.js @@ -123,6 +123,19 @@ export const buttonGroupConfig = { defaultValue: '#007bff', }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { selected: [1], @@ -148,6 +161,7 @@ export const buttonGroupConfig = { disabledState: { value: '{{false}}' }, selectedTextColor: { value: '' }, selectedBackgroundColor: { value: '' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/Editor/WidgetManager/configs/checkbox.js b/frontend/src/Editor/WidgetManager/configs/checkbox.js index c9b6424020..9f991be251 100644 --- a/frontend/src/Editor/WidgetManager/configs/checkbox.js +++ b/frontend/src/Editor/WidgetManager/configs/checkbox.js @@ -126,6 +126,20 @@ export const checkboxConfig = { ], accordian: 'label', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'switch', + }, }, exposedVariables: { value: false, @@ -189,6 +203,7 @@ export const checkboxConfig = { handleColor: { value: '#FFFFFF' }, alignment: { value: 'right' }, boxShadow: { value: '0px 0px 0px 0px #00000090' }, + padding: { value: 'default' }, }, validation: { mandatory: { value: '{{false}}' }, diff --git a/frontend/src/Editor/WidgetManager/configs/colorPicker.js b/frontend/src/Editor/WidgetManager/configs/colorPicker.js index b2fddd7e4c..6d93508891 100644 --- a/frontend/src/Editor/WidgetManager/configs/colorPicker.js +++ b/frontend/src/Editor/WidgetManager/configs/colorPicker.js @@ -26,6 +26,19 @@ export const colorPickerConfig = { }, styles: { visibility: { type: 'toggle', displayName: 'Visibility' }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { selectedColorHex: '#000000', @@ -45,6 +58,7 @@ export const colorPickerConfig = { events: [], styles: { visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/Editor/WidgetManager/configs/icon.js b/frontend/src/Editor/WidgetManager/configs/icon.js index aea06c976c..8c0b0880e4 100644 --- a/frontend/src/Editor/WidgetManager/configs/icon.js +++ b/frontend/src/Editor/WidgetManager/configs/icon.js @@ -78,6 +78,20 @@ export const iconConfig = { }, accordian: 'Icon', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'Icon', + }, }, exposedVariables: {}, actions: [ @@ -116,6 +130,7 @@ export const iconConfig = { styles: { iconColor: { value: '#000' }, iconAlign: { value: 'center' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/Editor/WidgetManager/configs/rangeslider.js b/frontend/src/Editor/WidgetManager/configs/rangeslider.js index 151dca3384..541ed95209 100644 --- a/frontend/src/Editor/WidgetManager/configs/rangeslider.js +++ b/frontend/src/Editor/WidgetManager/configs/rangeslider.js @@ -84,6 +84,19 @@ export const rangeSliderConfig = { defaultValue: true, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { value: null, @@ -111,6 +124,7 @@ export const rangeSliderConfig = { handleColor: { value: '' }, trackColor: { value: '' }, visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/Editor/WidgetManager/configs/starrating.js b/frontend/src/Editor/WidgetManager/configs/starrating.js index 01240d0369..d6caf8013c 100644 --- a/frontend/src/Editor/WidgetManager/configs/starrating.js +++ b/frontend/src/Editor/WidgetManager/configs/starrating.js @@ -89,6 +89,19 @@ export const starratingConfig = { defaultValue: false, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { value: 0, @@ -112,6 +125,7 @@ export const starratingConfig = { labelColor: { value: '' }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/Editor/WidgetManager/configs/tags.js b/frontend/src/Editor/WidgetManager/configs/tags.js index 8af289b23a..6479eeaad0 100644 --- a/frontend/src/Editor/WidgetManager/configs/tags.js +++ b/frontend/src/Editor/WidgetManager/configs/tags.js @@ -38,6 +38,19 @@ export const tagsConfig = { defaultValue: true, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: {}, definition: { @@ -54,6 +67,7 @@ export const tagsConfig = { events: [], styles: { visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/frontend/src/Editor/WidgetManager/configs/toggleswitchv2.js b/frontend/src/Editor/WidgetManager/configs/toggleswitchv2.js index 6753fbb50d..6f61a7645d 100644 --- a/frontend/src/Editor/WidgetManager/configs/toggleswitchv2.js +++ b/frontend/src/Editor/WidgetManager/configs/toggleswitchv2.js @@ -126,6 +126,20 @@ export const toggleSwitchV2Config = { validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } }, accordian: 'switch', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'switch', + }, }, exposedVariables: { value: false, @@ -187,6 +201,7 @@ export const toggleSwitchV2Config = { handleColor: { value: '#FFFFFF' }, alignment: { value: 'right' }, boxShadow: { value: '0px 0px 0px 0px #00000090' }, + padding: { value: 'default' }, }, }, }; diff --git a/server/src/modules/apps/services/widget-config/buttonGroup.js b/server/src/modules/apps/services/widget-config/buttonGroup.js index c0fa889dd5..d7f479624b 100644 --- a/server/src/modules/apps/services/widget-config/buttonGroup.js +++ b/server/src/modules/apps/services/widget-config/buttonGroup.js @@ -123,6 +123,19 @@ export const buttonGroupConfig = { defaultValue: '#007bff', }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { selected: [1], @@ -148,6 +161,7 @@ export const buttonGroupConfig = { disabledState: { value: '{{false}}' }, selectedTextColor: { value: '#FFFFFF' }, selectedBackgroundColor: { value: '#4368E3' }, + padding: { value: 'default' }, }, }, }; diff --git a/server/src/modules/apps/services/widget-config/checkbox.js b/server/src/modules/apps/services/widget-config/checkbox.js index c9b6424020..9f991be251 100644 --- a/server/src/modules/apps/services/widget-config/checkbox.js +++ b/server/src/modules/apps/services/widget-config/checkbox.js @@ -126,6 +126,20 @@ export const checkboxConfig = { ], accordian: 'label', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'switch', + }, }, exposedVariables: { value: false, @@ -189,6 +203,7 @@ export const checkboxConfig = { handleColor: { value: '#FFFFFF' }, alignment: { value: 'right' }, boxShadow: { value: '0px 0px 0px 0px #00000090' }, + padding: { value: 'default' }, }, validation: { mandatory: { value: '{{false}}' }, diff --git a/server/src/modules/apps/services/widget-config/colorPicker.js b/server/src/modules/apps/services/widget-config/colorPicker.js index b2fddd7e4c..6d93508891 100644 --- a/server/src/modules/apps/services/widget-config/colorPicker.js +++ b/server/src/modules/apps/services/widget-config/colorPicker.js @@ -26,6 +26,19 @@ export const colorPickerConfig = { }, styles: { visibility: { type: 'toggle', displayName: 'Visibility' }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { selectedColorHex: '#000000', @@ -45,6 +58,7 @@ export const colorPickerConfig = { events: [], styles: { visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/server/src/modules/apps/services/widget-config/icon.js b/server/src/modules/apps/services/widget-config/icon.js index aea06c976c..8c0b0880e4 100644 --- a/server/src/modules/apps/services/widget-config/icon.js +++ b/server/src/modules/apps/services/widget-config/icon.js @@ -78,6 +78,20 @@ export const iconConfig = { }, accordian: 'Icon', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'Icon', + }, }, exposedVariables: {}, actions: [ @@ -116,6 +130,7 @@ export const iconConfig = { styles: { iconColor: { value: '#000' }, iconAlign: { value: 'center' }, + padding: { value: 'default' }, }, }, }; diff --git a/server/src/modules/apps/services/widget-config/rangeslider.js b/server/src/modules/apps/services/widget-config/rangeslider.js index 151dca3384..541ed95209 100644 --- a/server/src/modules/apps/services/widget-config/rangeslider.js +++ b/server/src/modules/apps/services/widget-config/rangeslider.js @@ -84,6 +84,19 @@ export const rangeSliderConfig = { defaultValue: true, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { value: null, @@ -111,6 +124,7 @@ export const rangeSliderConfig = { handleColor: { value: '' }, trackColor: { value: '' }, visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/server/src/modules/apps/services/widget-config/starrating.js b/server/src/modules/apps/services/widget-config/starrating.js index 01240d0369..d6caf8013c 100644 --- a/server/src/modules/apps/services/widget-config/starrating.js +++ b/server/src/modules/apps/services/widget-config/starrating.js @@ -89,6 +89,19 @@ export const starratingConfig = { defaultValue: false, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: { value: 0, @@ -112,6 +125,7 @@ export const starratingConfig = { labelColor: { value: '' }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/server/src/modules/apps/services/widget-config/tags.js b/server/src/modules/apps/services/widget-config/tags.js index 8af289b23a..6479eeaad0 100644 --- a/server/src/modules/apps/services/widget-config/tags.js +++ b/server/src/modules/apps/services/widget-config/tags.js @@ -38,6 +38,19 @@ export const tagsConfig = { defaultValue: true, }, }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + }, }, exposedVariables: {}, definition: { @@ -54,6 +67,7 @@ export const tagsConfig = { events: [], styles: { visibility: { value: '{{true}}' }, + padding: { value: 'default' }, }, }, }; diff --git a/server/src/modules/apps/services/widget-config/toggleswitchv2.js b/server/src/modules/apps/services/widget-config/toggleswitchv2.js index 6753fbb50d..6f61a7645d 100644 --- a/server/src/modules/apps/services/widget-config/toggleswitchv2.js +++ b/server/src/modules/apps/services/widget-config/toggleswitchv2.js @@ -126,6 +126,20 @@ export const toggleSwitchV2Config = { validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } }, accordian: 'switch', }, + padding: { + type: 'switch', + displayName: 'Padding', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: 'default', + }, + isFxNotRequired: true, + options: [ + { displayName: 'Default', value: 'default' }, + { displayName: 'None', value: 'none' }, + ], + accordian: 'switch', + }, }, exposedVariables: { value: false, @@ -187,6 +201,7 @@ export const toggleSwitchV2Config = { handleColor: { value: '#FFFFFF' }, alignment: { value: 'right' }, boxShadow: { value: '0px 0px 0px 0px #00000090' }, + padding: { value: 'default' }, }, }, }; From f2dd8343a056a6664938a7f6fd7bd55f43a2605d Mon Sep 17 00:00:00 2001 From: Nakul Nagargade Date: Wed, 26 Mar 2025 15:00:54 +0530 Subject: [PATCH 11/66] Adjust height for Icon and color picker widget --- frontend/src/Editor/Components/ColorPicker.jsx | 4 ++-- frontend/src/Editor/Components/Icon.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/Editor/Components/ColorPicker.jsx b/frontend/src/Editor/Components/ColorPicker.jsx index 58282490f1..fb5be51f5e 100644 --- a/frontend/src/Editor/Components/ColorPicker.jsx +++ b/frontend/src/Editor/Components/ColorPicker.jsx @@ -161,8 +161,8 @@ export const ColorPicker = function ({ : { display: 'none' }; return ( -
-
+
+
setShowColorPicker(true)} diff --git a/frontend/src/Editor/Components/Icon.jsx b/frontend/src/Editor/Components/Icon.jsx index 289e9d1194..8ebb400aed 100644 --- a/frontend/src/Editor/Components/Icon.jsx +++ b/frontend/src/Editor/Components/Icon.jsx @@ -84,7 +84,7 @@ export const Icon = ({
) : (
{ From d962755142266c317ad2230d470b7fdd10c9fbf3 Mon Sep 17 00:00:00 2001 From: Nakul Nagargade Date: Thu, 27 Mar 2025 13:59:48 +0530 Subject: [PATCH 12/66] Fix dropdown and multiselect menuPlacement in modals when menus don't have bottom space --- frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx | 2 ++ frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx b/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx index a01ed895e0..a1d15ad44a 100644 --- a/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx +++ b/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx @@ -469,6 +469,8 @@ export const DropdownV2 = ({ menuPlacement="auto" onMenuOpen={() => fireEvent('onFocus')} onMenuClose={() => fireEvent('onBlur')} + // This is not setting minheight, required to help calculate menuPlacement by providing fixed height upfront before rendering (required in the case of modal) + minMenuHeight={300} />
diff --git a/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx b/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx index 6ef45e84d9..855703dfe3 100644 --- a/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx +++ b/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx @@ -513,6 +513,8 @@ export const MultiselectV2 = ({ fireEvent={() => fireEvent('onSelect')} menuPlacement="auto" menuPortalTarget={document.body} + // This is not setting minheight, required to help calculate menuPlacement by providing fixed height upfront before rendering (required in the case of modal) + minMenuHeight={300} />
From 86355f6f9bb3a921f35b6470c8356fd309a41849 Mon Sep 17 00:00:00 2001 From: Johnson Cherian Date: Fri, 28 Mar 2025 16:45:54 +0530 Subject: [PATCH 13/66] chore: Adds new design for form, container default children (#12239) Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> --- .../WidgetManager/widgets/container.js | 18 +- .../AppBuilder/WidgetManager/widgets/form.js | 266 +++++------------- .../Widgets/Container/Container.jsx | 3 +- frontend/src/AppBuilder/Widgets/Form/Form.jsx | 21 +- .../src/AppBuilder/Widgets/Form/form.scss | 8 +- .../Editor/WidgetManager/configs/container.js | 18 +- .../src/Editor/WidgetManager/configs/form.js | 266 +++++------------- .../apps/services/widget-config/container.js | 20 +- .../apps/services/widget-config/form.js | 266 +++++------------- 9 files changed, 252 insertions(+), 634 deletions(-) diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/container.js b/frontend/src/AppBuilder/WidgetManager/widgets/container.js index 37a895f553..d1670b8a93 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/container.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/container.js @@ -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, @@ -97,15 +103,6 @@ export const containerConfig = { }, accordian: 'container', }, - headerHeight: { - type: 'numberInput', - displayName: 'Height', - validation: { - schema: { type: 'number' }, - defaultValue: 80, - }, - accordian: 'header', - }, borderRadius: { type: 'numberInput', displayName: 'Border', @@ -157,6 +154,7 @@ export const containerConfig = { loadingState: { value: `{{false}}` }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + headerHeight: { value: `{{80}}` }, }, events: [], styles: { diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/form.js b/frontend/src/AppBuilder/WidgetManager/widgets/form.js index c5194822b6..f28044d52c 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/form.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/form.js @@ -4,7 +4,7 @@ export const formConfig = { description: 'Wrapper for multiple components', defaultSize: { width: 13, - height: 480, + height: 450, }, defaultChildren: [ { @@ -19,7 +19,7 @@ export const formConfig = { accessorKey: 'text', styles: ['fontWeight', 'textSize', 'textColor'], defaultValue: { - text: 'Form title', + text: 'Form', textSize: 20, textColor: '#000', }, @@ -34,203 +34,83 @@ 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'], defaultValue: { placeholder: 'Enter your name', - label: '', + label: 'Name', + width: '{{60}}', + 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'], defaultValue: { - value: 24, - label: '', + placeholder: 'Age', + label: 'Age', + width: '{{60}}', + 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'], defaultValue: { - text: 'Submit', + placeholder: 'Tomy', + label: 'Pet name', + width: '{{60}}', + alignment: 'side', + auto: '{{false}}', + padding: 'default', + }, + }, + { + componentName: 'TextInput', + layout: { + top: 200, + left: 5, + height: 40, + width: 31, + }, + properties: ['placeholder', 'label'], + styles: ['alignment', 'width', 'auto'], + defaultValue: { + label: 'Favorite color?', + width: '{{60}}', + alignment: 'side', + auto: '{{false}}', + padding: 'default', }, }, ], @@ -276,6 +156,16 @@ export const formConfig = { }, showHeader: { type: 'toggle', displayName: 'Header' }, showFooter: { type: 'toggle', displayName: 'Footer' }, + headerHeight: { + type: 'numberInput', + displayName: 'Header height', + validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 }, + }, + footerHeight: { + type: 'numberInput', + displayName: 'Footer height', + validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 }, + }, visibility: { type: 'toggle', displayName: 'Visibility', @@ -323,22 +213,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: 'color', displayName: 'Background color', @@ -410,18 +284,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' }, }, }, }; diff --git a/frontend/src/AppBuilder/Widgets/Container/Container.jsx b/frontend/src/AppBuilder/Widgets/Container/Container.jsx index 4978427370..a706d29069 100644 --- a/frontend/src/AppBuilder/Widgets/Container/Container.jsx +++ b/frontend/src/AppBuilder/Widgets/Container/Container.jsx @@ -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: diff --git a/frontend/src/AppBuilder/Widgets/Form/Form.jsx b/frontend/src/AppBuilder/Widgets/Form/Form.jsx index 1328fc195d..d918a1a2b5 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Form.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Form.jsx @@ -41,16 +41,16 @@ export const Form = function Form(props) { onComponentClick, } = props; const childComponents = useStore((state) => state.getChildComponents(id), shallow); + const { borderRadius, borderColor, boxShadow, footerBackgroundColor, headerBackgroundColor } = styles; const { - borderRadius, - borderColor, - boxShadow, - headerHeight, - footerHeight, - footerBackgroundColor, - headerBackgroundColor, - } = styles; - const { buttonToSubmit, advanced, JSONSchema, showHeader = false, showFooter = false } = properties; + buttonToSubmit, + advanced, + JSONSchema, + showHeader = false, + showFooter = false, + headerHeight = 80, + footerHeight = 80, + } = properties; const { isDisabled, isVisible, isLoading } = useExposeState( properties.loadingState, properties.visibility, @@ -88,7 +88,8 @@ export const Form = function Form(props) { const [isValid, setValidation] = useState(true); const [uiComponents, setUIComponents] = useState([]); const mounted = useMounted(); - const canvasFooterHeight = getCanvasHeight(footerHeight) / 10; + const canvasHeaderHeight = headerHeight / 10; + const canvasFooterHeight = footerHeight / 10; useEffect(() => { const exposedVariables = { diff --git a/frontend/src/AppBuilder/Widgets/Form/form.scss b/frontend/src/AppBuilder/Widgets/Form/form.scss index e1e694d7c0..766b309a7f 100644 --- a/frontend/src/AppBuilder/Widgets/Form/form.scss +++ b/frontend/src/AppBuilder/Widgets/Form/form.scss @@ -10,8 +10,8 @@ content: ""; position: absolute; bottom: 0; - left: -7px; - right: -7px; + left: -2px; + right: -2px; height: 1px; background-color: var(--border-weak); } @@ -23,8 +23,8 @@ content: ""; position: absolute; top: 0; - left: -7px; - right: -7px; + left: -2px; + right: -2px; height: 1px; background-color: var(--border-weak); } diff --git a/frontend/src/Editor/WidgetManager/configs/container.js b/frontend/src/Editor/WidgetManager/configs/container.js index 37a895f553..d1670b8a93 100644 --- a/frontend/src/Editor/WidgetManager/configs/container.js +++ b/frontend/src/Editor/WidgetManager/configs/container.js @@ -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, @@ -97,15 +103,6 @@ export const containerConfig = { }, accordian: 'container', }, - headerHeight: { - type: 'numberInput', - displayName: 'Height', - validation: { - schema: { type: 'number' }, - defaultValue: 80, - }, - accordian: 'header', - }, borderRadius: { type: 'numberInput', displayName: 'Border', @@ -157,6 +154,7 @@ export const containerConfig = { loadingState: { value: `{{false}}` }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + headerHeight: { value: `{{80}}` }, }, events: [], styles: { diff --git a/frontend/src/Editor/WidgetManager/configs/form.js b/frontend/src/Editor/WidgetManager/configs/form.js index c5194822b6..f28044d52c 100644 --- a/frontend/src/Editor/WidgetManager/configs/form.js +++ b/frontend/src/Editor/WidgetManager/configs/form.js @@ -4,7 +4,7 @@ export const formConfig = { description: 'Wrapper for multiple components', defaultSize: { width: 13, - height: 480, + height: 450, }, defaultChildren: [ { @@ -19,7 +19,7 @@ export const formConfig = { accessorKey: 'text', styles: ['fontWeight', 'textSize', 'textColor'], defaultValue: { - text: 'Form title', + text: 'Form', textSize: 20, textColor: '#000', }, @@ -34,203 +34,83 @@ 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'], defaultValue: { placeholder: 'Enter your name', - label: '', + label: 'Name', + width: '{{60}}', + 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'], defaultValue: { - value: 24, - label: '', + placeholder: 'Age', + label: 'Age', + width: '{{60}}', + 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'], defaultValue: { - text: 'Submit', + placeholder: 'Tomy', + label: 'Pet name', + width: '{{60}}', + alignment: 'side', + auto: '{{false}}', + padding: 'default', + }, + }, + { + componentName: 'TextInput', + layout: { + top: 200, + left: 5, + height: 40, + width: 31, + }, + properties: ['placeholder', 'label'], + styles: ['alignment', 'width', 'auto'], + defaultValue: { + label: 'Favorite color?', + width: '{{60}}', + alignment: 'side', + auto: '{{false}}', + padding: 'default', }, }, ], @@ -276,6 +156,16 @@ export const formConfig = { }, showHeader: { type: 'toggle', displayName: 'Header' }, showFooter: { type: 'toggle', displayName: 'Footer' }, + headerHeight: { + type: 'numberInput', + displayName: 'Header height', + validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 }, + }, + footerHeight: { + type: 'numberInput', + displayName: 'Footer height', + validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 }, + }, visibility: { type: 'toggle', displayName: 'Visibility', @@ -323,22 +213,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: 'color', displayName: 'Background color', @@ -410,18 +284,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' }, }, }, }; diff --git a/server/src/modules/apps/services/widget-config/container.js b/server/src/modules/apps/services/widget-config/container.js index ec1d5174b0..d1670b8a93 100644 --- a/server/src/modules/apps/services/widget-config/container.js +++ b/server/src/modules/apps/services/widget-config/container.js @@ -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, @@ -97,15 +103,6 @@ export const containerConfig = { }, accordian: 'container', }, - headerHeight: { - type: 'numberInput', - displayName: 'Height', - validation: { - schema: { type: 'number' }, - defaultValue: 80, - }, - accordian: 'header', - }, borderRadius: { type: 'numberInput', displayName: 'Border', @@ -153,10 +150,11 @@ export const containerConfig = { showOnMobile: { value: '{{false}}' }, }, properties: { - showHeader: {value: `{{true}}`}, + showHeader: { value: `{{true}}` }, loadingState: { value: `{{false}}` }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + headerHeight: { value: `{{80}}` }, }, events: [], styles: { diff --git a/server/src/modules/apps/services/widget-config/form.js b/server/src/modules/apps/services/widget-config/form.js index c5194822b6..f28044d52c 100644 --- a/server/src/modules/apps/services/widget-config/form.js +++ b/server/src/modules/apps/services/widget-config/form.js @@ -4,7 +4,7 @@ export const formConfig = { description: 'Wrapper for multiple components', defaultSize: { width: 13, - height: 480, + height: 450, }, defaultChildren: [ { @@ -19,7 +19,7 @@ export const formConfig = { accessorKey: 'text', styles: ['fontWeight', 'textSize', 'textColor'], defaultValue: { - text: 'Form title', + text: 'Form', textSize: 20, textColor: '#000', }, @@ -34,203 +34,83 @@ 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'], defaultValue: { placeholder: 'Enter your name', - label: '', + label: 'Name', + width: '{{60}}', + 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'], defaultValue: { - value: 24, - label: '', + placeholder: 'Age', + label: 'Age', + width: '{{60}}', + 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'], defaultValue: { - text: 'Submit', + placeholder: 'Tomy', + label: 'Pet name', + width: '{{60}}', + alignment: 'side', + auto: '{{false}}', + padding: 'default', + }, + }, + { + componentName: 'TextInput', + layout: { + top: 200, + left: 5, + height: 40, + width: 31, + }, + properties: ['placeholder', 'label'], + styles: ['alignment', 'width', 'auto'], + defaultValue: { + label: 'Favorite color?', + width: '{{60}}', + alignment: 'side', + auto: '{{false}}', + padding: 'default', }, }, ], @@ -276,6 +156,16 @@ export const formConfig = { }, showHeader: { type: 'toggle', displayName: 'Header' }, showFooter: { type: 'toggle', displayName: 'Footer' }, + headerHeight: { + type: 'numberInput', + displayName: 'Header height', + validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 }, + }, + footerHeight: { + type: 'numberInput', + displayName: 'Footer height', + validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 }, + }, visibility: { type: 'toggle', displayName: 'Visibility', @@ -323,22 +213,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: 'color', displayName: 'Background color', @@ -410,18 +284,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' }, }, }, }; From 7384a96c6e0155ddba47d957ff458de3fe07fee0 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:55:15 +0530 Subject: [PATCH 14/66] Adds default height for body --- .../AppBuilder/WidgetManager/widgets/form.js | 27 +++------- frontend/src/AppBuilder/Widgets/Form/Form.jsx | 17 +++--- .../src/AppBuilder/Widgets/Form/FormUtils.js | 20 +++++++ .../src/Editor/WidgetManager/configs/form.js | 27 +++------- .../apps/services/widget-config/form.js | 52 +++++++++++++++++-- 5 files changed, 88 insertions(+), 55 deletions(-) diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/form.js b/frontend/src/AppBuilder/WidgetManager/widgets/form.js index f28044d52c..e5996062fb 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/form.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/form.js @@ -47,11 +47,12 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Enter your name', label: 'Name', width: '{{60}}', + direction: 'left', alignment: 'side', auto: '{{false}}', padding: 'default', @@ -66,11 +67,12 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Age', label: 'Age', width: '{{60}}', + direction: 'left', alignment: 'side', auto: '{{false}}', padding: 'default', @@ -85,30 +87,13 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Tomy', label: 'Pet name', width: '{{60}}', alignment: 'side', - auto: '{{false}}', - padding: 'default', - }, - }, - { - componentName: 'TextInput', - layout: { - top: 200, - left: 5, - height: 40, - width: 31, - }, - properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto'], - defaultValue: { - label: 'Favorite color?', - width: '{{60}}', - alignment: 'side', + direction: 'left', auto: '{{false}}', padding: 'default', }, diff --git a/frontend/src/AppBuilder/Widgets/Form/Form.jsx b/frontend/src/AppBuilder/Widgets/Form/Form.jsx index d918a1a2b5..eb09a1ad4d 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Form.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Form.jsx @@ -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'; @@ -19,11 +19,6 @@ import { useActiveSlot } from '@/AppBuilder/_hooks/useActiveSlot'; import './form.scss'; -const getCanvasHeight = (height) => { - const parsedHeight = height.includes('px') ? parseInt(height, 10) : height; - return Math.ceil(parsedHeight); -}; - export const Form = function Form(props) { const { id, @@ -60,6 +55,9 @@ 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 computedStyles = { backgroundColor, borderRadius: borderRadius ? parseFloat(borderRadius) : 0, @@ -336,10 +334,13 @@ export const Form = function Form(props) { ) : (
{!advanced && ( -
+
{ 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; + + console.log('rounded', rounded) + return `${Math.max(rounded - 20, 40)}px`; +}; diff --git a/frontend/src/Editor/WidgetManager/configs/form.js b/frontend/src/Editor/WidgetManager/configs/form.js index f28044d52c..e5996062fb 100644 --- a/frontend/src/Editor/WidgetManager/configs/form.js +++ b/frontend/src/Editor/WidgetManager/configs/form.js @@ -47,11 +47,12 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Enter your name', label: 'Name', width: '{{60}}', + direction: 'left', alignment: 'side', auto: '{{false}}', padding: 'default', @@ -66,11 +67,12 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Age', label: 'Age', width: '{{60}}', + direction: 'left', alignment: 'side', auto: '{{false}}', padding: 'default', @@ -85,30 +87,13 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Tomy', label: 'Pet name', width: '{{60}}', alignment: 'side', - auto: '{{false}}', - padding: 'default', - }, - }, - { - componentName: 'TextInput', - layout: { - top: 200, - left: 5, - height: 40, - width: 31, - }, - properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto'], - defaultValue: { - label: 'Favorite color?', - width: '{{60}}', - alignment: 'side', + direction: 'left', auto: '{{false}}', padding: 'default', }, diff --git a/server/src/modules/apps/services/widget-config/form.js b/server/src/modules/apps/services/widget-config/form.js index f28044d52c..d46c637849 100644 --- a/server/src/modules/apps/services/widget-config/form.js +++ b/server/src/modules/apps/services/widget-config/form.js @@ -47,11 +47,12 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Enter your name', label: 'Name', width: '{{60}}', + direction: 'left', alignment: 'side', auto: '{{false}}', padding: 'default', @@ -66,11 +67,12 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Age', label: 'Age', width: '{{60}}', + direction: 'left', alignment: 'side', auto: '{{false}}', padding: 'default', @@ -85,32 +87,72 @@ export const formConfig = { width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto', 'padding'], + styles: ['alignment', 'width', 'auto', 'padding', 'direction'], defaultValue: { placeholder: 'Tomy', label: 'Pet name', width: '{{60}}', alignment: 'side', + direction: 'left', auto: '{{false}}', padding: 'default', }, }, { - componentName: 'TextInput', + componentName: 'Text', layout: { top: 200, left: 5, + height: 30, + width: 10, + }, + properties: ['text'], + accessorKey: 'text', + styles: ['fontWeight', 'textSize', 'textColor', 'direction'], + defaultValue: { + text: 'Who are you', + textSize: 12, + direction: 'left', + textColor: '#000', + }, + }, + { + componentName: 'TextArea', + layout: { + top: 200, + left: 14, + height: 80, + width: 22, + }, + properties: ['placeholder', 'value'], + styles: ['alignment', 'width', 'auto', 'padding', 'visibility'], + defaultValue: { + placeholder: 'Tomy', + value: 'Pet name', + width: '{{60}}', + alignment: 'side', + auto: '{{false}}', + padding: 'default', + visibility: '{{true}}', + }, + }, + { + componentName: 'MultiselectV2', + layout: { + top: 400, + left: 5, height: 40, width: 31, }, properties: ['placeholder', 'label'], - styles: ['alignment', 'width', 'auto'], + styles: ['alignment', 'width', 'auto', 'direction'], defaultValue: { label: 'Favorite color?', width: '{{60}}', alignment: 'side', auto: '{{false}}', padding: 'default', + direction: 'left', }, }, ], From a3db2ab3d5e54f14d2f9db7a0d6ebd91a70b135a Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:39:26 +0530 Subject: [PATCH 15/66] Hides header footer height --- .../src/AppBuilder/WidgetManager/widgets/form.js | 4 +++- .../Widgets/Form/Components/HorizontalSlot.jsx | 5 ++++- frontend/src/AppBuilder/Widgets/Form/Form.jsx | 15 ++++++++------- frontend/src/AppBuilder/Widgets/Form/form.scss | 6 +++--- frontend/src/Editor/WidgetManager/configs/form.js | 4 +++- .../modules/apps/services/widget-config/form.js | 4 +++- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/form.js b/frontend/src/AppBuilder/WidgetManager/widgets/form.js index e5996062fb..0ef410b908 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/form.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/form.js @@ -20,7 +20,7 @@ export const formConfig = { styles: ['fontWeight', 'textSize', 'textColor'], defaultValue: { text: 'Form', - textSize: 20, + textSize: 16, textColor: '#000', }, }, @@ -144,11 +144,13 @@ export const formConfig = { headerHeight: { type: 'numberInput', displayName: 'Header 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: { diff --git a/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx b/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx index e892dc4f9f..ad58c835ca 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx @@ -14,6 +14,7 @@ export const HorizontalSlot = React.memo( slotName = 'header', // 'header' or 'footer' slotStyle = {}, onResize, + isEditing, maxHeight, }) => { const parsedHeight = parseInt(height, 10); @@ -47,7 +48,9 @@ export const HorizontalSlot = React.memo( return (
state.setComponentProperty, shallow); const updateHeaderSizeInStore = ({ newHeight }) => { - const heightInPx = `${parseInt(newHeight, 10)}px`; - setComponentProperty(id, `headerHeight`, heightInPx, 'properties', 'value', false); + const _height = parseInt(newHeight, 10); + setComponentProperty(id, `headerHeight`, _height, 'properties', 'value', false); }; const updateFooterSizeInStore = ({ newHeight }) => { - const heightInPx = `${parseInt(newHeight, 10)}px`; - setComponentProperty(id, `footerHeight`, heightInPx, 'properties', 'value', false); + const _height = parseInt(newHeight, 10); + setComponentProperty(id, `footerHeight`, _height, 'properties', 'value', false); }; - // debugger; + const mode = useStore((state) => state.currentMode, shallow); + const isEditing = mode === 'edit'; const headerMaxHeight = parseInt(height, 10) - parseInt(footerHeight, 10) - 100 - 10; const footerMaxHeight = parseInt(height, 10) - parseInt(headerHeight, 10) - 100 - 10; const formFooter = { @@ -315,7 +316,7 @@ export const Form = function Form(props) { Date: Thu, 3 Apr 2025 12:20:12 +0530 Subject: [PATCH 16/66] Fixes border color --- frontend/src/AppBuilder/Widgets/Form/form.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/AppBuilder/Widgets/Form/form.scss b/frontend/src/AppBuilder/Widgets/Form/form.scss index 6756ec51ed..e012f0a075 100644 --- a/frontend/src/AppBuilder/Widgets/Form/form.scss +++ b/frontend/src/AppBuilder/Widgets/Form/form.scss @@ -56,7 +56,7 @@ box-shadow: 0 0 0 1px var(--border-weak); } - &is-editing.active { + &.is-editing.active { box-shadow: 0 0 0 1px var(--border-accent-weak); } From 86625c01a06b853288641dc2bd5c18224b0c8885 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Thu, 3 Apr 2025 12:41:31 +0530 Subject: [PATCH 17/66] Fixes border radius --- frontend/src/AppBuilder/Widgets/Form/Form.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/AppBuilder/Widgets/Form/Form.jsx b/frontend/src/AppBuilder/Widgets/Form/Form.jsx index 5370bc6a55..8c015fefc1 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Form.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Form.jsx @@ -57,6 +57,7 @@ export const Form = function Form(props) { ['#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, @@ -67,6 +68,7 @@ export const Form = function Form(props) { position: 'relative', boxShadow, flexDirection: 'column', + clipPath: `inset(0 round ${computedBorderRadius})`, }; const formContent = { From ab762c44f63b4fd0ca85d3137304970252b1ea9b Mon Sep 17 00:00:00 2001 From: Nakul Nagargade Date: Fri, 4 Apr 2025 15:28:05 +0530 Subject: [PATCH 18/66] added box shadow --- frontend/src/AppBuilder/WidgetManager/widgets/icon.js | 10 ++++++++++ frontend/src/Editor/Components/Icon.jsx | 4 ++-- frontend/src/Editor/WidgetManager/configs/icon.js | 10 ++++++++++ .../1737039401111-UpdateVisibilityFrIconComponent.ts | 8 +++++++- .../src/modules/apps/services/widget-config/icon.js | 11 ++++++++++- 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/icon.js b/frontend/src/AppBuilder/WidgetManager/widgets/icon.js index 8c0b0880e4..40dc8185dd 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/icon.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/icon.js @@ -92,6 +92,15 @@ export const iconConfig = { ], accordian: 'Icon', }, + boxShadow: { + type: 'boxShadow', + displayName: 'Box shadow', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: '0px 0px 0px 0px #00000040', + }, + accordian: 'Icon', + }, }, exposedVariables: {}, actions: [ @@ -131,6 +140,7 @@ export const iconConfig = { iconColor: { value: '#000' }, iconAlign: { value: 'center' }, padding: { value: 'default' }, + boxShadow: { value: '0px 0px 0px 0px #00000040' }, }, }, }; diff --git a/frontend/src/Editor/Components/Icon.jsx b/frontend/src/Editor/Components/Icon.jsx index 8ebb400aed..1758e41be3 100644 --- a/frontend/src/Editor/Components/Icon.jsx +++ b/frontend/src/Editor/Components/Icon.jsx @@ -17,7 +17,7 @@ export const Icon = ({ }) => { const isInitialRender = useRef(true); const { icon, loadingState, disabledState } = properties; - const { iconAlign, iconColor } = styles; + const { iconAlign, iconColor, boxShadow } = styles; // eslint-disable-next-line import/namespace const IconElement = Icons[icon]; @@ -87,7 +87,7 @@ export const Icon = ({ className={cx('icon-widget h-100', { 'd-none': !visibility }, { 'cursor-pointer': false })} data-cy={dataCy} data-disabled={isDisabled} - style={{ textAlign: iconAlign }} + style={{ textAlign: iconAlign, boxShadow }} onMouseEnter={(event) => { event.stopPropagation(); fireEvent('onHover'); diff --git a/frontend/src/Editor/WidgetManager/configs/icon.js b/frontend/src/Editor/WidgetManager/configs/icon.js index 8c0b0880e4..40dc8185dd 100644 --- a/frontend/src/Editor/WidgetManager/configs/icon.js +++ b/frontend/src/Editor/WidgetManager/configs/icon.js @@ -92,6 +92,15 @@ export const iconConfig = { ], accordian: 'Icon', }, + boxShadow: { + type: 'boxShadow', + displayName: 'Box shadow', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: '0px 0px 0px 0px #00000040', + }, + accordian: 'Icon', + }, }, exposedVariables: {}, actions: [ @@ -131,6 +140,7 @@ export const iconConfig = { iconColor: { value: '#000' }, iconAlign: { value: 'center' }, padding: { value: 'default' }, + boxShadow: { value: '0px 0px 0px 0px #00000040' }, }, }, }; diff --git a/server/data-migrations/1737039401111-UpdateVisibilityFrIconComponent.ts b/server/data-migrations/1737039401111-UpdateVisibilityFrIconComponent.ts index 77ce0a232d..26a04fcf3b 100644 --- a/server/data-migrations/1737039401111-UpdateVisibilityFrIconComponent.ts +++ b/server/data-migrations/1737039401111-UpdateVisibilityFrIconComponent.ts @@ -30,6 +30,7 @@ export class UpdateVisibilityFrIconComponent1737039401111 implements MigrationIn const properties = component.properties; const styles = component.styles; const general = component.general; + const generalStyles = component.generalStyles; if (styles.visibility) { properties.visibility = styles.visibility; @@ -41,6 +42,11 @@ export class UpdateVisibilityFrIconComponent1737039401111 implements MigrationIn delete general?.tooltip; } + if (generalStyles?.boxShadow) { + styles.boxShadow = generalStyles?.boxShadow; + delete generalStyles?.boxShadow; + } + await entityManager.update(Component, component.id, { properties, styles, @@ -49,5 +55,5 @@ export class UpdateVisibilityFrIconComponent1737039401111 implements MigrationIn } } - public async down(queryRunner: QueryRunner): Promise {} + public async down(queryRunner: QueryRunner): Promise { } } diff --git a/server/src/modules/apps/services/widget-config/icon.js b/server/src/modules/apps/services/widget-config/icon.js index 8c0b0880e4..6e2035c7dd 100644 --- a/server/src/modules/apps/services/widget-config/icon.js +++ b/server/src/modules/apps/services/widget-config/icon.js @@ -92,6 +92,15 @@ export const iconConfig = { ], accordian: 'Icon', }, + boxShadow: { + type: 'boxShadow', + displayName: 'Box shadow', + validation: { + schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, + defaultValue: '0px 0px 0px 0px #00000040', + }, + accordian: 'Icon', + }, }, exposedVariables: {}, actions: [ @@ -131,6 +140,6 @@ export const iconConfig = { iconColor: { value: '#000' }, iconAlign: { value: 'center' }, padding: { value: 'default' }, - }, + boxShadow: { value: '0px 0px 0px 0px #00000040' }, }, }; From f9f3f841111a61edb9a2a2211229237df7bf5d5d Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Fri, 4 Apr 2025 16:07:19 +0530 Subject: [PATCH 19/66] Changes resize handle size --- .../AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx | 6 +++++- frontend/src/AppBuilder/Widgets/Form/Form.jsx | 8 +++----- frontend/src/AppBuilder/Widgets/Form/form.scss | 4 +++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx b/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx index ad58c835ca..86a5c58b14 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Components/HorizontalSlot.jsx @@ -45,6 +45,10 @@ export const HorizontalSlot = React.memo( const canvasHeight = parseInt(resizedHeight, 10) / 10; + const resizeStyle = { + backgroundColor: darkMode ? '#1F2837' : '#fff', + }; + return (
-
+
{isDisabled && ( diff --git a/frontend/src/AppBuilder/Widgets/Form/Form.jsx b/frontend/src/AppBuilder/Widgets/Form/Form.jsx index 8c015fefc1..7e536f5e1f 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Form.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Form.jsx @@ -88,8 +88,6 @@ export const Form = function Form(props) { const [isValid, setValidation] = useState(true); const [uiComponents, setUIComponents] = useState([]); const mounted = useMounted(); - const canvasHeaderHeight = headerHeight / 10; - const canvasFooterHeight = footerHeight / 10; useEffect(() => { const exposedVariables = { @@ -318,7 +316,7 @@ export const Form = function Form(props) { )} -
+
{isLoading ? (
@@ -393,7 +391,7 @@ export const Form = function Form(props) { Date: Mon, 7 Apr 2025 13:05:09 +0530 Subject: [PATCH 20/66] fix link QA bugs --- frontend/src/Editor/Components/Link/Link.jsx | 18 +++++++++++++----- frontend/src/Editor/Components/Link/link.scss | 18 ++++++++++++++---- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/frontend/src/Editor/Components/Link/Link.jsx b/frontend/src/Editor/Components/Link/Link.jsx index 0b33686278..b15c582818 100644 --- a/frontend/src/Editor/Components/Link/Link.jsx +++ b/frontend/src/Editor/Components/Link/Link.jsx @@ -19,8 +19,7 @@ export const Link = ({ height, properties, styles, fireEvent, setExposedVariable const computedStyles = { display: 'flex', alignItems: verticalAlignment === 'top' ? 'flex-start' : verticalAlignment === 'center' ? 'center' : 'flex-end', - justifyContent: - horizontalAlignment === 'left' ? 'flex-start' : horizontalAlignment === 'center' ? 'center' : 'flex-end', + textAlign: horizontalAlignment === 'left' ? 'left' : horizontalAlignment === 'center' ? 'center' : 'right', height: '100%', width: '100%', boxShadow, @@ -113,10 +112,19 @@ export const Link = ({ height, properties, styles, fireEvent, setExposedVariable onMouseOver={() => { fireEvent('onHover'); }} - style={{ color: textColor, fontSize: textSize, cursor: isDisabled ? 'not-allowed' : 'pointer' }} + style={{ width: '100%' }} ref={clickRef} > - + {iconVisibility && ( )} - {linkTextState} + {linkTextState}
diff --git a/frontend/src/Editor/Components/Link/link.scss b/frontend/src/Editor/Components/Link/link.scss index a92f19829f..20b375025f 100644 --- a/frontend/src/Editor/Components/Link/link.scss +++ b/frontend/src/Editor/Components/Link/link.scss @@ -1,8 +1,18 @@ .link-widget { a { - text-underline-offset: 32%; // Adds space between text and underline - &:hover { - color: var(--link-hover-color) !important; - } + text-decoration: none !important; + pointer-events: none; + cursor: none !important; + + .link-text { + pointer-events: all; + text-underline-offset: 32%; // Adds space between text and underline + cursor: pointer; + &:hover { + text-decoration: underline; + text-decoration-color: var(--link-hover-color) !important; + color: var(--link-hover-color) !important; + } } + } } From be01a024f253893cdbb44bed88b6103641ea028d Mon Sep 17 00:00:00 2001 From: Nakul Nagargade Date: Mon, 7 Apr 2025 13:11:44 +0530 Subject: [PATCH 21/66] fix --- frontend/src/Editor/Components/Link/Link.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/Editor/Components/Link/Link.jsx b/frontend/src/Editor/Components/Link/Link.jsx index b15c582818..3a5744df8e 100644 --- a/frontend/src/Editor/Components/Link/Link.jsx +++ b/frontend/src/Editor/Components/Link/Link.jsx @@ -123,6 +123,7 @@ export const Link = ({ height, properties, styles, fireEvent, setExposedVariable justifyContent: horizontalAlignment === 'left' ? 'flex-start' : horizontalAlignment === 'center' ? 'center' : 'flex-end', color: textColor, + paddingBottom: verticalAlignment === 'bottom' ? '1px' : '0px', }} > {iconVisibility && ( From 4ba13541ba6f4a1f103d76e88e3b3d2931e292b1 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:04:20 +0530 Subject: [PATCH 22/66] fix: Fixes widgets on old modal grid getting stuck --- frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx | 2 +- frontend/src/AppBuilder/Widgets/Modal.jsx | 2 +- frontend/src/AppBuilder/Widgets/ModalV2/Components/Modal.jsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx b/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx index 174c68b475..016b4c03a3 100644 --- a/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx @@ -942,7 +942,7 @@ export default function Grid({ gridWidth, currentLayout }) { const isParentModal = isParentNewModal || isParentLegacyModal || isParentModalSlot; if (isParentModal) { - const modalContainer = e.target.closest('.tj-modal-widget-content'); + const modalContainer = e.target.closest('.tj-modal--container'); const mainCanvas = document.getElementById('real-canvas'); const mainRect = mainCanvas.getBoundingClientRect(); diff --git a/frontend/src/AppBuilder/Widgets/Modal.jsx b/frontend/src/AppBuilder/Widgets/Modal.jsx index 128c98aed2..04e2668441 100644 --- a/frontend/src/AppBuilder/Widgets/Modal.jsx +++ b/frontend/src/AppBuilder/Widgets/Modal.jsx @@ -239,7 +239,7 @@ export const Modal = function Modal({ { return ( { e.preventDefault(); From 623487e7e0b31b8831bd5c497520e6ae27d185e4 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Tue, 8 Apr 2025 13:53:31 +0530 Subject: [PATCH 23/66] fix: Update container header for old data to false --- .../Inspector/Components/Form.jsx | 36 ++++++------- .../WidgetManager/widgets/container.js | 4 +- .../Editor/WidgetManager/configs/container.js | 4 +- ...097765065-UpdateContainerHeaderProperty.ts | 50 +++++++++++++++++++ .../apps/services/widget-config/container.js | 4 +- 5 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 server/migrations/1744097765065-UpdateContainerHeaderProperty.ts diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx index b39924854e..b6d7033b49 100644 --- a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx +++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx @@ -110,24 +110,6 @@ export const baseComponentProperties = ( }); } - items.push({ - title: 'Additional actions', - isOpen: true, - children: additionalActions?.map((property) => - renderElement( - component, - componentMeta, - paramUpdated, - dataQueries, - property, - 'properties', - currentState, - allComponents, - darkMode - ) - ), - }); - if (events.length > 0) { items.push({ title: `${i18next.t('widget.common.events', 'Events')}`, @@ -149,6 +131,24 @@ export const baseComponentProperties = ( }); } + items.push({ + title: 'Additional actions', + isOpen: true, + children: additionalActions?.map((property) => + renderElement( + component, + componentMeta, + paramUpdated, + dataQueries, + property, + 'properties', + currentState, + allComponents, + darkMode + ) + ), + }); + if (validations.length > 0) { items.push({ title: `${i18next.t('widget.common.validation', 'Validation')}`, diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/container.js b/frontend/src/AppBuilder/WidgetManager/widgets/container.js index 424b9a801d..04eb035abf 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/container.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/container.js @@ -44,7 +44,7 @@ export const containerConfig = { displayName: 'Show header', validation: { schema: { type: 'boolean' }, - defaultValue: false, + defaultValue: true, }, }, }, @@ -154,7 +154,7 @@ export const containerConfig = { showOnMobile: { value: '{{false}}' }, }, properties: { - showHeader: { value: `{{false}}` }, + showHeader: { value: `{{true}}` }, loadingState: { value: `{{false}}` }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, diff --git a/frontend/src/Editor/WidgetManager/configs/container.js b/frontend/src/Editor/WidgetManager/configs/container.js index 424b9a801d..04eb035abf 100644 --- a/frontend/src/Editor/WidgetManager/configs/container.js +++ b/frontend/src/Editor/WidgetManager/configs/container.js @@ -44,7 +44,7 @@ export const containerConfig = { displayName: 'Show header', validation: { schema: { type: 'boolean' }, - defaultValue: false, + defaultValue: true, }, }, }, @@ -154,7 +154,7 @@ export const containerConfig = { showOnMobile: { value: '{{false}}' }, }, properties: { - showHeader: { value: `{{false}}` }, + showHeader: { value: `{{true}}` }, loadingState: { value: `{{false}}` }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, diff --git a/server/migrations/1744097765065-UpdateContainerHeaderProperty.ts b/server/migrations/1744097765065-UpdateContainerHeaderProperty.ts new file mode 100644 index 0000000000..1e7dd95b9e --- /dev/null +++ b/server/migrations/1744097765065-UpdateContainerHeaderProperty.ts @@ -0,0 +1,50 @@ +import { Component } from 'src/entities/component.entity'; + +import { processDataInBatches } from '@helpers/migration.helper'; +import { EntityManager, MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateContainerHeaderProperty1744097765065 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + const componentTypes = ['Container']; + const batchSize = 100; + const entityManager = queryRunner.manager; + + for (const componentType of componentTypes) { + await processDataInBatches( + entityManager, + async (entityManager: EntityManager) => { + return await entityManager.find(Component, { + where: { type: componentType }, + order: { createdAt: 'ASC' }, + }); + }, + async (entityManager: EntityManager, components: Component[]) => { + await this.processUpdates(entityManager, components); + }, + batchSize + ); + } + } + + private async processUpdates(entityManager: EntityManager, components: Component[]) { + for (const component of components) { + const properties = component.properties; + const styles = component.styles; + const general = component.general; + + // Update showHeader property to false for old instances + if (!properties.showHeader) { + properties.showHeader = { value: '{{false}}' }; + } + + // Update the modal component with the modified properties + await entityManager.update(Component, component.id, { + properties, + styles, + general, + }); + } + } + + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/server/src/modules/apps/services/widget-config/container.js b/server/src/modules/apps/services/widget-config/container.js index 424b9a801d..04eb035abf 100644 --- a/server/src/modules/apps/services/widget-config/container.js +++ b/server/src/modules/apps/services/widget-config/container.js @@ -44,7 +44,7 @@ export const containerConfig = { displayName: 'Show header', validation: { schema: { type: 'boolean' }, - defaultValue: false, + defaultValue: true, }, }, }, @@ -154,7 +154,7 @@ export const containerConfig = { showOnMobile: { value: '{{false}}' }, }, properties: { - showHeader: { value: `{{false}}` }, + showHeader: { value: `{{true}}` }, loadingState: { value: `{{false}}` }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, From 0fc495536f5d2d0a1aec1cda85cbdf1732d3874f Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Wed, 9 Apr 2025 13:49:23 +0530 Subject: [PATCH 24/66] fix: Hides header footer options when custom schema is turned on --- .../RightSideBar/Inspector/Components/Form.jsx | 14 ++++++++++++++ frontend/src/AppBuilder/Widgets/Form/Form.jsx | 4 ++-- frontend/src/AppBuilder/Widgets/Form/form.scss | 13 +++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx index b39924854e..740691b5d3 100644 --- a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx +++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/Form.jsx @@ -40,6 +40,7 @@ export const Form = ({ const { id } = component; const newOptions = [{ name: 'None', value: 'none' }]; + Object.entries(allComponents).forEach(([componentId, _component]) => { const validParent = _component.component.parent === id || @@ -52,6 +53,19 @@ export const Form = ({ tempComponentMeta.properties.buttonToSubmit.options = newOptions; + // Hide header footer if custom schema is turned on + + if (component.component.definition.properties.advanced.value === '{{true}}') { + component.component.properties.showHeader = { + ...component.component.properties.headerHeight, + isHidden: true, + }; + component.component.properties.showFooter = { + ...component.component.properties.headerHeight, + isHidden: true, + }; + } + const accordionItems = baseComponentProperties( properties, events, diff --git a/frontend/src/AppBuilder/Widgets/Form/Form.jsx b/frontend/src/AppBuilder/Widgets/Form/Form.jsx index afeb4cf844..a5dbd2a865 100644 --- a/frontend/src/AppBuilder/Widgets/Form/Form.jsx +++ b/frontend/src/AppBuilder/Widgets/Form/Form.jsx @@ -299,7 +299,7 @@ export const Form = function Form(props) { if (e.target.className === 'real-canvas') onComponentClick(id, component); }} //Hack, should find a better solution - to prevent losing z index+1 when container element is clicked > - {showHeader && ( + {!advanced && showHeader && (
)}
- {showFooter && ( + {!advanced && showFooter && (
Date: Wed, 9 Apr 2025 14:15:46 +0530 Subject: [PATCH 25/66] Removed the extra space in bottom of query manager. --- .../src/AppBuilder/QueryManager/Components/QueryManagerBody.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerBody.jsx b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerBody.jsx index 9e6737f41c..244d8e53cf 100644 --- a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerBody.jsx +++ b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerBody.jsx @@ -381,7 +381,6 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () = {activeTab === 1 && renderQueryElement()} {activeTab === 2 && renderTransformation()} {activeTab === 3 && renderQueryOptions()} -
)} From f565449d884a66f235c8692d384f656cb3db00cd Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Wed, 9 Apr 2025 22:53:57 +0530 Subject: [PATCH 26/66] Fix: Filter dropdown not opening when you click on label. --- .../NewTable/_components/Header/Header.jsx | 2 +- .../Header/_components/Filter/Filter.jsx | 3 ++- .../Header/_components/Filter/FilterRow.jsx | 25 ++++++++++++++++++- frontend/src/_ui/Select/SelectComponent.jsx | 5 ++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/frontend/src/AppBuilder/Widgets/NewTable/_components/Header/Header.jsx b/frontend/src/AppBuilder/Widgets/NewTable/_components/Header/Header.jsx index 9e9a6f656f..1331889870 100644 --- a/frontend/src/AppBuilder/Widgets/NewTable/_components/Header/Header.jsx +++ b/frontend/src/AppBuilder/Widgets/NewTable/_components/Header/Header.jsx @@ -117,7 +117,7 @@ export const Header = memo(
{showFilter && ( - + )} ); diff --git a/frontend/src/AppBuilder/Widgets/NewTable/_components/Header/_components/Filter/Filter.jsx b/frontend/src/AppBuilder/Widgets/NewTable/_components/Header/_components/Filter/Filter.jsx index 905142fc59..7b1a3cdca6 100644 --- a/frontend/src/AppBuilder/Widgets/NewTable/_components/Header/_components/Filter/Filter.jsx +++ b/frontend/src/AppBuilder/Widgets/NewTable/_components/Header/_components/Filter/Filter.jsx @@ -6,7 +6,7 @@ import { FilterFooter } from './FilterFooter'; import { FilterHeader } from './FilterHeader'; import { debounce, isEqual } from 'lodash'; -export const Filter = memo(({ table, darkMode, setFilters, setShowFilter }) => { +export const Filter = memo(({ id, table, darkMode, setFilters, setShowFilter }) => { const { t } = useTranslation(); const [localFilters, setLocalFilters] = useState(table.getState().columnFilters); @@ -142,6 +142,7 @@ export const Filter = memo(({ table, darkMode, setFilters, setShowFilter }) => {
{localFilters.map((filter, index) => ( { + ({ id, filter, index, columns, darkMode, onColumnChange, onOperationChange, onValueChange, onRemove }) => { const { t } = useTranslation(); + const isDragging = useStore((state) => state.draggingComponentId === id); const selectStyles = (width) => { return { @@ -15,6 +18,10 @@ export const FilterRow = memo( menuList: (base) => ({ ...base, }), + menu: (base) => ({ + ...base, + display: isDragging ? 'none' : 'block', + }), }; }; @@ -29,11 +36,13 @@ export const FilterRow = memo( value={filter.id} search={true} onChange={(value) => onColumnChange(index, value)} + components={{ ValueContainer: CustomValueContainer }} placeholder={t('globals.select', 'Select') + '...'} className={`${darkMode ? 'select-search-dark' : 'select-search'} mb-0`} styles={selectStyles('100%')} useCustomStyles={true} darkMode={darkMode} + openMenuOnFocus={true} />
@@ -42,11 +51,13 @@ export const FilterRow = memo( value={filter.value.condition} search={true} onChange={(value) => onOperationChange(index, value)} + components={{ ValueContainer: CustomValueContainer }} className={`${darkMode ? 'select-search-dark' : 'select-search'}`} placeholder={t('globals.select', 'Select') + '...'} styles={selectStyles('100%')} useCustomStyles={true} darkMode={darkMode} + openMenuOnFocus={true} />
@@ -74,3 +85,15 @@ export const FilterRow = memo( ); } ); + +const CustomValueContainer = (props) => { + const handleClick = (e) => { + if (props.selectProps?.selectRef?.current) { + props.selectProps.selectRef.current.focus(); + } + if (props.innerProps?.onMouseDown) { + props.innerProps.onMouseDown(e); + } + }; + return ; +}; diff --git a/frontend/src/_ui/Select/SelectComponent.jsx b/frontend/src/_ui/Select/SelectComponent.jsx index 6675b090e3..2d62352000 100644 --- a/frontend/src/_ui/Select/SelectComponent.jsx +++ b/frontend/src/_ui/Select/SelectComponent.jsx @@ -4,6 +4,7 @@ import Select from 'react-select'; import defaultStyles from './styles'; export const SelectComponent = ({ options = [], value, onChange, closeMenuOnSelect, darkMode, ...restProps }) => { + const selectRef = React.useRef(null); const isDarkMode = darkMode ?? localStorage.getItem('darkMode') === 'true'; const { isMulti = false, @@ -22,6 +23,7 @@ export const SelectComponent = ({ options = [], value, onChange, closeMenuOnSele useCustomStyles = false, isDisabled = false, borderRadius, + openMenuOnFocus = false, } = restProps; const customStyles = useCustomStyles ? styles : defaultStyles(isDarkMode, width, height, styles, borderRadius); @@ -56,6 +58,8 @@ export const SelectComponent = ({ options = [], value, onChange, closeMenuOnSele return ( { - setIsMenuOpen(true); - fireEvent('onFocus'); - }} - onMenuClose={() => { - setIsMenuOpen(false); - fireEvent('onBlur'); - }} onKeyDown={(e) => { - if (e.key === 'Enter' && !isMenuOpen) { + if (e.key === 'Enter' && !isMenuOpen && !isDropdownLoading) { setIsMenuOpen(true); + fireEvent('onFocus'); e.preventDefault(); } if (e.key === 'Escape' && isMenuOpen) { setIsMenuOpen(false); + fireEvent('onBlur'); e.preventDefault(); } }} From 662f9c3aabae4bbb398d15a225ce472945163084 Mon Sep 17 00:00:00 2001 From: devanshu052000 Date: Thu, 10 Apr 2025 22:13:15 +0530 Subject: [PATCH 29/66] Fixed interactions in Multiselect --- .../MultiselectV2/CustomValueContainer.jsx | 2 +- .../MultiselectV2/MultiselectV2.jsx | 46 ++++++++----------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/frontend/src/Editor/Components/MultiselectV2/CustomValueContainer.jsx b/frontend/src/Editor/Components/MultiselectV2/CustomValueContainer.jsx index 2901abc106..9eb11ea4c6 100644 --- a/frontend/src/Editor/Components/MultiselectV2/CustomValueContainer.jsx +++ b/frontend/src/Editor/Components/MultiselectV2/CustomValueContainer.jsx @@ -42,7 +42,7 @@ const CustomValueContainer = ({ children, ...props }) => { {/* Rendering children except Placeholder component to preserve the default behavior of react-select like focus handling */} {React.Children.map(children, (child) => { - if (child.type !== Placeholder) { + if (child?.type !== Placeholder) { return child; } })} diff --git a/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx b/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx index 7d5109edee..63eec271f2 100644 --- a/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx +++ b/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx @@ -12,7 +12,6 @@ import Label from '@/_ui/Label'; const tinycolor = require('tinycolor2'); import { CustomDropdownIndicator, CustomClearIndicator } from '../DropdownV2/DropdownV2'; import { getInputBackgroundColor, getInputBorderColor, getInputFocusedColor, sortArray } from '../DropdownV2/utils'; -import useStore from '@/AppBuilder/_stores/store'; export const MultiselectV2 = ({ id, @@ -77,8 +76,6 @@ export const MultiselectV2 = ({ const [searchInputValue, setSearchInputValue] = useState(''); const _height = padding === 'default' ? `${height}px` : `${height + 4}px`; const [userInteracted, setUserInteracted] = useState(false); - const currentMode = useStore((state) => state.currentMode); - const isEditor = currentMode === 'edit'; const [isMultiselectOpen, setIsMultiselectOpen] = useState(false); useEffect(() => { @@ -270,26 +267,29 @@ export const MultiselectV2 = ({ fireEvent('onSearchTextChanged'); } }; - const handleClickOutside = (event) => { + const handleClickOutsideSelect = (event) => { let menu = document.getElementById(`dropdown-multiselect-widget-custom-menu-list-${id}`); if ( + isMultiselectOpen && multiselectRef.current && !multiselectRef.current.contains(event.target) && menu && !menu.contains(event.target) ) { - if (isMultiselectOpen) { - fireEvent('onBlur'); - setIsMultiselectOpen(false); - setSearchInputValue(''); - } + setIsMultiselectOpen(false); + fireEvent('onBlur'); } }; - const handleClickInEditor = (e) => { - if (e.target.className.includes('clear-indicator') || isMultiselectOpen) return; - e.stopPropagation(); - selectRef.current?.onControlMouseDown(e); + const handleClickInsideSelect = () => { + if (isMultiSelectDisabled || isMultiSelectLoading) return; + if (isMultiselectOpen) { + setIsMultiselectOpen(false); + fireEvent('onBlur'); + } else { + setIsMultiselectOpen(true); + fireEvent('onFocus'); + } }; const setInputValue = (values) => { @@ -304,11 +304,11 @@ export const MultiselectV2 = ({ }; useEffect(() => { - document.addEventListener('mousedown', handleClickOutside, { capture: true }); + document.addEventListener('mousedown', handleClickOutsideSelect, { capture: true }); return () => { - document.removeEventListener('mousedown', handleClickOutside, { capture: true }); + document.removeEventListener('mousedown', handleClickOutsideSelect, { capture: true }); }; - }, [isMultiselectOpen]); + }, [isMultiselectOpen, componentName]); // Handle Select all logic useEffect(() => { @@ -468,7 +468,7 @@ export const MultiselectV2 = ({ _width={_width} top={'1px'} /> -
+
- onInputChange(e.currentTarget.value, { - action: 'input-change', - }) - } - onMouseDown={(e) => { - e.stopPropagation(); - e.target.focus(); - }} - onTouchEnd={(e) => { - e.stopPropagation(); - e.target.focus(); - }} - onFocus={onMenuInputFocus} - placeholder="Search" - className="dropdown-multiselect-widget-search-box" - /> -
+ {showSearchInput && ( +
+ + + + + onInputChange(e.currentTarget.value, { + action: 'input-change', + }) + } + onMouseDown={(e) => { + e.stopPropagation(); + e.target.focus(); + }} + onTouchEnd={(e) => { + e.stopPropagation(); + e.target.focus(); + }} + onFocus={onMenuInputFocus} + placeholder="Search" + className="dropdown-multiselect-widget-search-box" + /> +
+ )} {showAllOption && !optionsLoadingState && (