Merge pull request #13231 from ToolJet/fix/page-bugs-01

Fix: page bugs and design changes
This commit is contained in:
Johnson Cherian 2025-07-09 19:18:48 +05:30 committed by GitHub
commit 33f77dda7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 770 additions and 334 deletions

@ -1 +1 @@
Subproject commit 3b2d439f681f3879da6663f4c9b6d30deffc0380 Subproject commit 8ed7be478b266e8b812ce1d3c43f2cb7e2ee3d07

View file

@ -8,7 +8,13 @@ import './appCanvas.scss';
import useStore from '@/AppBuilder/_stores/store'; import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { computeViewerBackgroundColor, getCanvasWidth } from './appCanvasUtils'; import { computeViewerBackgroundColor, getCanvasWidth } from './appCanvasUtils';
import { NO_OF_GRIDS } from './appCanvasConstants'; import {
LEFT_SIDEBAR_WIDTH,
NO_OF_GRIDS,
PAGES_SIDEBAR_WIDTH_COLLAPSED,
PAGES_SIDEBAR_WIDTH_EXPANDED,
RIGHT_SIDEBAR_WIDTH,
} from './appCanvasConstants';
import cx from 'classnames'; import cx from 'classnames';
import FreezeVersionInfo from '@/AppBuilder/Header/FreezeVersionInfo'; import FreezeVersionInfo from '@/AppBuilder/Header/FreezeVersionInfo';
import { computeCanvasContainerHeight } from '../_helpers/editorHelpers'; import { computeCanvasContainerHeight } from '../_helpers/editorHelpers';
@ -134,7 +140,7 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
width: currentMode === 'edit' ? `calc(100% - 96px)` : '100%', width: currentMode === 'edit' ? `calc(100% - 96px)` : '100%',
alignItems: 'unset', alignItems: 'unset',
justifyContent: 'unset', justifyContent: 'unset',
borderRight: currentMode === 'edit' && isRightSidebarOpen && '299' + 'px solid', borderRight: currentMode === 'edit' && isRightSidebarOpen && `300px solid ${canvasBgColor}`,
padding: currentMode === 'edit' && '8px', padding: currentMode === 'edit' && '8px',
paddingBottom: currentMode === 'edit' && '2px', paddingBottom: currentMode === 'edit' && '2px',
}; };
@ -152,15 +158,34 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
const shouldAdjust = isSidebarOpen || (isRightSidebarOpen && currentMode === 'edit'); const shouldAdjust = isSidebarOpen || (isRightSidebarOpen && currentMode === 'edit');
if (!shouldAdjust) return ''; if (!shouldAdjust) return '';
let offset; let offset;
if (isViewerSidebarPinned) { if (isViewerSidebarPinned && !isPagesSidebarHidden) {
offset = position === 'side' ? '352px' : '126px'; if (position === 'side' && isSidebarOpen && isRightSidebarOpen && !isPagesSidebarHidden) {
offset = `${LEFT_SIDEBAR_WIDTH + RIGHT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_EXPANDED}px`;
} else if (position === 'side' && isSidebarOpen && !isRightSidebarOpen && !isPagesSidebarHidden) {
offset = `${LEFT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_EXPANDED}px`;
} else if (position === 'side' && isRightSidebarOpen && !isSidebarOpen && !isPagesSidebarHidden) {
offset = `${RIGHT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_EXPANDED}px`;
}
} else { } else {
offset = position === 'side' ? '171px' : '126px'; if (position === 'side' && isSidebarOpen && isRightSidebarOpen && !isPagesSidebarHidden) {
offset = `${LEFT_SIDEBAR_WIDTH + RIGHT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_COLLAPSED}px`;
} else if (position === 'side' && isSidebarOpen && !isRightSidebarOpen && !isPagesSidebarHidden) {
offset = `${LEFT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_COLLAPSED}px`;
} else if (position === 'side' && isRightSidebarOpen && !isSidebarOpen && !isPagesSidebarHidden) {
offset = `${RIGHT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_COLLAPSED}px`;
}
} }
return `calc(100vw - ${offset})`; if ((position === 'top' || isPagesSidebarHidden) && isSidebarOpen && isRightSidebarOpen) {
offset = `${LEFT_SIDEBAR_WIDTH + RIGHT_SIDEBAR_WIDTH}px`;
} else if ((position === 'top' || isPagesSidebarHidden) && isSidebarOpen && !isRightSidebarOpen) {
offset = `${LEFT_SIDEBAR_WIDTH}px`;
} else if ((position === 'top' || isPagesSidebarHidden) && isRightSidebarOpen && !isSidebarOpen) {
offset = `${RIGHT_SIDEBAR_WIDTH}px`;
}
return `calc(100% + ${offset})`;
} }
return ( return (
@ -177,7 +202,7 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
'canvas-container d-flex page-container', 'canvas-container d-flex page-container',
{ 'dark-theme theme-dark': isAppDarkMode, close: !isViewerSidebarPinned }, { 'dark-theme theme-dark': isAppDarkMode, close: !isViewerSidebarPinned },
{ 'overflow-x-auto': currentMode === 'edit' }, { 'overflow-x-auto': currentMode === 'edit' },
{ 'position-top': position === 'top' }, { 'position-top': position === 'top' || isPagesSidebarHidden },
{ 'overflow-x-hidden': moduleId !== 'canvas' } // Disbling horizontal scroll for modules in view mode { 'overflow-x-hidden': moduleId !== 'canvas' } // Disbling horizontal scroll for modules in view mode
)} )}
style={canvasContainerStyles} style={canvasContainerStyles}

View file

@ -26,6 +26,7 @@
.empty-box-cont{ .empty-box-cont{
display: flex; display: flex;
justify-content: center; justify-content: center;
margin: unset !important;
.dotted-cont{ .dotted-cont{
border: 1px dashed var(--indigo8); border: 1px dashed var(--indigo8);

View file

@ -14,9 +14,13 @@ export const DEFAULT_CANVAS_WIDTH = 1292;
export const APP_HEADER_HEIGHT = 47; export const APP_HEADER_HEIGHT = 47;
export const LEFT_SIDEBAR_WIDTH = 348; // exclusive of border export const LEFT_SIDEBAR_WIDTH = 350;
export const RIGHT_SIDEBAR_WIDTH = 299; export const RIGHT_SIDEBAR_WIDTH = 300;
export const PAGES_SIDEBAR_WIDTH_EXPANDED = 226;
export const PAGES_SIDEBAR_WIDTH_COLLAPSED = 44;
export const SUBCONTAINER_WIDGETS = ['Container', 'Tabs', 'Listview', 'Kanban', 'Form']; export const SUBCONTAINER_WIDGETS = ['Container', 'Tabs', 'Listview', 'Kanban', 'Form'];

View file

@ -9,6 +9,7 @@ const useSidebarMargin = (canvasContainerRef) => {
const { moduleId } = useModuleContext(); const { moduleId } = useModuleContext();
const [editorMarginLeft, setEditorMarginLeft] = useState(0); const [editorMarginLeft, setEditorMarginLeft] = useState(0);
const isSidebarOpen = useStore((state) => state.isSidebarOpen, shallow); const isSidebarOpen = useStore((state) => state.isSidebarOpen, shallow);
const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen, shallow);
const mode = useStore((state) => state.modeStore.modules[moduleId].currentMode, shallow); const mode = useStore((state) => state.modeStore.modules[moduleId].currentMode, shallow);
useEffect(() => { useEffect(() => {
@ -17,10 +18,10 @@ const useSidebarMargin = (canvasContainerRef) => {
}, [isSidebarOpen, mode]); }, [isSidebarOpen, mode]);
useEffect(() => { useEffect(() => {
if (!isEmpty(canvasContainerRef?.current)) { if (!isEmpty(canvasContainerRef?.current) && isSidebarOpen && canvasContainerRef.current.scrollLeft === 0) {
canvasContainerRef.current.scrollLeft += editorMarginLeft; canvasContainerRef.current.scrollLeft += editorMarginLeft;
} }
}, [editorMarginLeft, canvasContainerRef]); }, [editorMarginLeft, canvasContainerRef, isSidebarOpen]);
return editorMarginLeft; return editorMarginLeft;
}; };

View file

@ -31,7 +31,6 @@ const GlobalSettings = ({ darkMode }) => {
</div> </div>
<div style={{ padding: '12px 16px' }} className={cx({ disabled: shouldFreeze })}> <div style={{ padding: '12px 16px' }} className={cx({ disabled: shouldFreeze })}>
<MaintenanceMode darkMode={darkMode} /> <MaintenanceMode darkMode={darkMode} />
<HideHeaderToggle darkMode={darkMode} />
</div> </div>
<div className={cx({ 'dark-theme': darkMode })}> <div className={cx({ 'dark-theme': darkMode })}>
<span className="canvas-styles-header">Canvas Styles</span> <span className="canvas-styles-header">Canvas Styles</span>

View file

@ -155,6 +155,7 @@ export const QueryPanel = ({ darkMode }) => {
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
zIndex: 2, zIndex: 2,
width: '100%',
}} }}
> >
<div <div

View file

@ -9,17 +9,21 @@ import SolidIcon from '@/_ui/Icon/SolidIcons';
export const ComponentConfigurationTab = ({ darkMode, isModuleEditor }) => { export const ComponentConfigurationTab = ({ darkMode, isModuleEditor }) => {
const selectedComponentId = useStore((state) => state.selectedComponents?.[0], shallow); const selectedComponentId = useStore((state) => state.selectedComponents?.[0], shallow);
const activeTab = useStore((state) => state.activeRightSideBarTab, shallow); const activeTab = useStore((state) => state.activeRightSideBarTab, shallow);
const toggleRightSidebarPin = useStore((state) => state.toggleRightSidebarPin); const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen);
const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned);
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab); const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab);
const handleToggle = () => {
setActiveRightSideBarTab(null);
setRightSidebarOpen(false);
};
if (!selectedComponentId && activeTab !== RIGHT_SIDE_BAR_TAB.PAGES) { if (!selectedComponentId && activeTab !== RIGHT_SIDE_BAR_TAB.PAGES) {
// return setActiveRightSideBarTab(RIGHT_SIDE_BAR_TAB.COMPONENTS); // return setActiveRightSideBarTab(RIGHT_SIDE_BAR_TAB.COMPONENTS);
return ( return (
<> <>
<div className="empty-configuration-header"> <div className="empty-configuration-header">
<div className="header">Component properties</div> <div className="header">Component properties</div>
<div className="icon-btn cursor-pointer" onClick={() => toggleRightSidebarPin()}> <div className="icon-btn cursor-pointer flex-shrink-0 p-2 h-4 w-4" onClick={handleToggle}>
<SolidIcon fill="var(--icon-strong)" name={isRightSidebarPinned ? 'unpin' : 'pin'} width="16" /> <SolidIcon fill="var(--icon-strong)" name={'remove03'} width="16" viewBox="0 0 16 16" />
</div> </div>
</div> </div>
<div className="d-flex align-items-center justify-content-center no-component-selected"> <div className="d-flex align-items-center justify-content-center no-component-selected">
@ -39,6 +43,7 @@ export const ComponentConfigurationTab = ({ darkMode, isModuleEditor }) => {
selectedComponentId={selectedComponentId} selectedComponentId={selectedComponentId}
pages={[]} pages={[]}
isModuleEditor={isModuleEditor} isModuleEditor={isModuleEditor}
handleRightSidebarToggle={handleToggle}
/> />
); );
}; };

View file

@ -74,8 +74,10 @@ export const ComponentsManagerTab = ({ darkMode, isModuleEditor }) => {
} }
}, [hasModuleAccess, activeTab]); }, [hasModuleAccess, activeTab]);
const toggleRightSidebarPin = useStore((state) => state.toggleRightSidebarPin); const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen);
const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned); const activeRightSideBarTab = useStore((state) => state.activeRightSideBarTab);
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab);
const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen);
const handleSearchQueryChange = useCallback( const handleSearchQueryChange = useCallback(
debounce((value) => { debounce((value) => {
@ -88,6 +90,11 @@ export const ComponentsManagerTab = ({ darkMode, isModuleEditor }) => {
[activeTab] [activeTab]
); );
const handleToggle = () => {
setActiveRightSideBarTab(null);
setRightSidebarOpen(false);
};
const filterComponents = useCallback((value) => { const filterComponents = useCallback((value) => {
if (value !== '') { if (value !== '') {
const fuse = new Fuse(componentList, { const fuse = new Fuse(componentList, {
@ -223,11 +230,16 @@ export const ComponentsManagerTab = ({ darkMode, isModuleEditor }) => {
return ( return (
<div className={`components-container ${shouldFreeze ? 'disabled' : ''}`}> <div className={`components-container ${shouldFreeze ? 'disabled' : ''}`}>
{isModuleEditor ? ( <div className="d-flex align-items-center">
<p className="widgets-manager-header">Components</p> {isModuleEditor ? (
) : ( <p className="widgets-manager-header">Components</p>
<ComponentModuleTab onChangeTab={handleChangeTab} hasModuleAccess={hasModuleAccess} /> ) : (
)} <ComponentModuleTab onChangeTab={handleChangeTab} hasModuleAccess={hasModuleAccess} />
)}
<div className="icon-btn cursor-pointer flex-shrink-0 me-3 p-2 h-4 w-4" onClick={handleToggle}>
<SolidIcon fill="var(--icon-strong)" name={'remove03'} width="16" viewBox="0 0 16 16" />
</div>
</div>
<div className="input-icon tj-app-input"> <div className="input-icon tj-app-input">
<SearchBox <SearchBox
dataCy={`widget-search-box`} dataCy={`widget-search-box`}

View file

@ -118,7 +118,13 @@ const NEW_REVAMPED_COMPONENTS = [
'FilePicker', 'FilePicker',
]; ];
export const Inspector = ({ componentDefinitionChanged, darkMode, pages, selectedComponentId }) => { export const Inspector = ({
componentDefinitionChanged,
darkMode,
pages,
selectedComponentId,
handleRightSidebarToggle,
}) => {
const allComponents = useStore((state) => state.getCurrentPageComponents()); const allComponents = useStore((state) => state.getCurrentPageComponents());
const setComponentProperty = useStore((state) => state.setComponentProperty, shallow); const setComponentProperty = useStore((state) => state.setComponentProperty, shallow);
const setComponentName = useStore((state) => state.setComponentName, shallow); const setComponentName = useStore((state) => state.setComponentName, shallow);
@ -528,19 +534,19 @@ export const Inspector = ({ componentDefinitionChanged, darkMode, pages, selecte
<div className={`inspector ${isModuleContainer && 'module-editor-inspector'}`}> <div className={`inspector ${isModuleContainer && 'module-editor-inspector'}`}>
<div> <div>
<div className={`row inspector-component-title-input-holder ${shouldFreeze && 'disabled'}`}> <div className={`row inspector-component-title-input-holder ${shouldFreeze && 'disabled'}`}>
<div className="col-1" onClick={() => clearSelectedComponents()}> <div className="p-0 width-unset flex-shrink-0" onClick={() => clearSelectedComponents()}>
<span <span
data-cy={`inspector-close-icon`} data-cy={`inspector-close-icon`}
className="cursor-pointer d-flex align-items-center " className="cursor-pointer d-flex align-items-center "
style={{ height: '28px', width: '28px' }} style={{ height: '28px' }}
> >
<ArrowLeft fill={'var(--slate12)'} width={'14'} /> <ArrowLeft fill={'var(--slate12)'} width={'14'} />
</span> </span>
</div> </div>
<div className={`col-9 p-0 ${shouldFreeze && 'disabled'}`}>{renderAppNameInput()}</div> <div className={`flex-shrink p-0 width-unset ${shouldFreeze && 'disabled'}`}>{renderAppNameInput()}</div>
{!isModuleContainer && ( {!isModuleContainer && (
<> <>
<div className="col-2" data-cy={'component-inspector-options'}> <div className="width-unset" data-cy={'component-inspector-options'}>
<OverlayTrigger <OverlayTrigger
trigger={'click'} trigger={'click'}
placement={'bottom-end'} placement={'bottom-end'}
@ -613,6 +619,9 @@ export const Inspector = ({ componentDefinitionChanged, darkMode, pages, selecte
/> />
</> </>
)} )}
<div className="icon-btn cursor-pointer flex-shrink-0 p-2 h-4 w-4" onClick={handleRightSidebarToggle}>
<SolidIcon fill="var(--icon-strong)" name={'remove03'} width="16" viewBox="0 0 16 16" />
</div>
</div> </div>
<div className={`${shouldFreeze && 'disabled'}`}>{renderTabs()}</div> <div className={`${shouldFreeze && 'disabled'}`}>{renderTabs()}</div>

View file

@ -20,7 +20,7 @@ export function AddNewPageMenu({ darkMode, isLicensed }) {
}; };
return ( return (
<div className="page-type-buttons-container"> <div className={`page-type-buttons-container ${darkMode && 'dark-mode'}`}>
<Button <Button
ref={newPageBtnRef} ref={newPageBtnRef}
key="new-page-btn" key="new-page-btn"
@ -54,7 +54,7 @@ export function AddNewPageMenu({ darkMode, isLicensed }) {
rootClose rootClose
onHide={() => setShowMenuPopover(false)} onHide={() => setShowMenuPopover(false)}
> >
<Popover id="add-new-page-popover"> <Popover className={darkMode && 'darkMode'} id="add-new-page-popover">
<div className="menu-options mb-0"> <div className="menu-options mb-0">
<PageOptions <PageOptions
type="url" type="url"
@ -70,7 +70,7 @@ export function AddNewPageMenu({ darkMode, isLicensed }) {
darkMode={darkMode} darkMode={darkMode}
onClick={() => handleOpenPopup('app')} onClick={() => handleOpenPopup('app')}
/> />
<div className={`d-flex ${!isLicensed && 'disabled licensed-page-option'}`}> <div className={`${!isLicensed && 'd-flex disabled licensed-page-option'}`}>
<PageOptions <PageOptions
type="group" type="group"
text="Add nav group" text="Add nav group"
@ -79,7 +79,7 @@ export function AddNewPageMenu({ darkMode, isLicensed }) {
onClick={() => handleOpenPopup('group')} onClick={() => handleOpenPopup('group')}
/> />
<LicenseTooltip <LicenseTooltip
message={"App header can't be hidden on free plans"} message={"Nav group can't be created on free plans"}
placement="bottom" placement="bottom"
show={!isLicensed} show={!isLicensed}
> >

View file

@ -338,22 +338,10 @@ export const AddEditPagePopup = forwardRef(({ darkMode, ...props }, ref) => {
type="checkbox" type="checkbox"
checked={isHomePage} checked={isHomePage}
onChange={() => markAsHomePage(page?.id)} onChange={() => markAsHomePage(page?.id)}
disabled={isHomePage} disabled={isHomePage || resolveReferences(page?.hidden?.value) || page?.disabled}
/> />
</label> </label>
</div> </div>
{/* <div className=" d-flex justify-content-between align-items-center pb-2">
<label className="form-label font-weight-400 mb-0">Hide this page on navigation</label>
<label className={`form-switch`}>
<input
className="form-check-input"
type="checkbox"
checked={page?.hidden}
onChange={(e) => updatePageVisibility(page?.id, !page?.hidden)}
disabled={isHomePage}
/>
</label>
</div> */}
<HidePageOnNavigation <HidePageOnNavigation
hidden={page?.hidden} hidden={page?.hidden}
page={page} page={page}

View file

@ -71,7 +71,7 @@ export default function IconSelector({ iconName, iconColor, pageId, iconStyles,
}; };
// eslint-disable-next-line import/namespace // eslint-disable-next-line import/namespace
const IconElement = Icons?.[iconName] ?? Icons?.['IconFileDescription']; const IconElement = Icons?.[iconName] ?? Icons?.['IconFile'];
return ( return (
<OverlayTrigger <OverlayTrigger

View file

@ -1,5 +1,5 @@
/* eslint-disable import/namespace */ /* eslint-disable import/namespace */
import React, { useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import _ from 'lodash'; import _ from 'lodash';
import * as Icons from '@tabler/icons-react'; import * as Icons from '@tabler/icons-react';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
@ -26,12 +26,13 @@ export const RenderPage = ({
isSidebarPinned, isSidebarPinned,
callback, callback,
position, position,
onPageClick,
}) => { }) => {
const currentMode = useStore((state) => state.currentMode); const currentMode = useStore((state) => state.currentMode);
const isHomePage = page.id === homePageId; const isHomePage = page.id === homePageId;
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon; const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
const IconElement = (props) => { const IconElement = (props) => {
const Icon = Icons?.[iconName] ?? Icons?.['IconFileDescription']; const Icon = Icons?.[iconName] ?? Icons?.['IconFile'];
if (!isSidebarPinned || labelStyle?.label?.hidden) { if (!isSidebarPinned || labelStyle?.label?.hidden) {
return ( return (
@ -59,7 +60,7 @@ export const RenderPage = ({
key={page.handle} key={page.handle}
onClick={() => { onClick={() => {
switchPageWrapper(page); switchPageWrapper(page);
callback && position !== 'side' && callback(); position !== 'side' && onPageClick();
}} }}
selectedItem={page?.id === currentPageId} selectedItem={page?.id === currentPageId}
CustomIcon={!labelStyle?.icon?.hidden && IconElement} CustomIcon={!labelStyle?.icon?.hidden && IconElement}
@ -67,14 +68,15 @@ export const RenderPage = ({
darkMode={darkMode} darkMode={darkMode}
> >
{!labelStyle?.label?.hidden && ( {!labelStyle?.label?.hidden && (
<span <div
style={{ position: 'relative', overflow: 'hidden' }}
// className={isSelected && 'tj-list-item-selected'} // className={isSelected && 'tj-list-item-selected'}
data-cy={`pages-name-${String(page?.name).toLowerCase()}`} data-cy={`pages-name-${String(page?.name).toLowerCase()}`}
> >
<OverflowTooltip style={{ width: '110px', position: 'relative' }} childrenClassName={'page-name'}> <OverflowTooltip style={{ width: '110px', position: 'relative' }} childrenClassName={'page-name'}>
{page.name} {page.name}
</OverflowTooltip> </OverflowTooltip>
</span> </div>
)} )}
</FolderList> </FolderList>
</div> </div>
@ -94,12 +96,15 @@ const RenderPageGroup = ({
linkRefs, linkRefs,
isSidebarPinned, isSidebarPinned,
position, position,
isExpanded,
onToggle,
onPageClick,
}) => { }) => {
const currentMode = useStore((state) => state.currentMode); const currentMode = useStore((state) => state.currentMode);
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
const contentRef = useRef(null); const contentRef = useRef(null);
const groupItemRootRef = useRef(null);
const IconElement = (props) => { const IconElement = (props) => {
const Icon = Icons?.[pageGroup.icon] ?? Icons?.['IconHome2']; const Icon = Icons?.[pageGroup.icon] ?? Icons?.['IconHome2'];
@ -116,9 +121,25 @@ const RenderPageGroup = ({
}; };
const handleToggle = () => { const handleToggle = () => {
setIsExpanded(!isExpanded); onToggle(pageGroup.id);
}; };
useEffect(() => {
const handleClickOutside = (event) => {
if (isExpanded && groupItemRootRef.current && !groupItemRootRef.current.contains(event.target)) {
onToggle(pageGroup.id);
}
};
if (isExpanded) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isExpanded, onToggle, pageGroup.id]);
if (labelStyle?.label?.hidden) { if (labelStyle?.label?.hidden) {
return ( return (
<> <>
@ -133,6 +154,8 @@ const RenderPageGroup = ({
darkMode={darkMode} darkMode={darkMode}
homePageId={homePageId} homePageId={homePageId}
position={position} position={position}
callback={handleToggle}
onPageClick={onPageClick}
/> />
))} ))}
</> </>
@ -145,7 +168,12 @@ const RenderPageGroup = ({
<div <div
key={pageGroup.name} key={pageGroup.name}
data-id={pageGroup.id} data-id={pageGroup.id}
ref={(el) => linkRefs?.current && (linkRefs.current[pageGroup.id] = el)} ref={(el) => {
if (linkRefs?.current) {
linkRefs.current[pageGroup.id] = el;
}
groupItemRootRef.current = el;
}}
className={`accordion-item ${darkMode ? 'dark-mode' : ''}`} className={`accordion-item ${darkMode ? 'dark-mode' : ''}`}
> >
<div <div
@ -153,40 +181,31 @@ const RenderPageGroup = ({
style={{ style={{
position: 'relative', position: 'relative',
}} }}
onClick={isSidebarPinned && handleToggle}
> >
<FolderList <FolderList
key={pageGroup.id} key={pageGroup.id}
onClick={isSidebarPinned && handleToggle}
CustomIcon={!labelStyle?.icon?.hidden && IconElement} CustomIcon={!labelStyle?.icon?.hidden && IconElement}
customStyles={computeStyles} customStyles={computeStyles}
darkMode={darkMode} darkMode={darkMode}
hovered={hovered} hovered={hovered}
> >
{!labelStyle?.label?.hidden && ( {!labelStyle?.label?.hidden && (
<span data-cy={`pages-name-${String(pageGroup?.name).toLowerCase()}`}> <div
<OverflowTooltip style={{ width: '110px' }} childrenClassName={'page-name'}> style={{ position: 'relative', overflow: 'hidden' }}
{pageGroup.name} data-cy={`pages-name-${String(pageGroup?.name).toLowerCase()}`}
</OverflowTooltip> >
</span> <OverflowTooltip childrenClassName={'page-name'}>{pageGroup.name}</OverflowTooltip>
</div>
)} )}
</FolderList> </FolderList>
<div style={{ marginRight: '12px' }}> <div className="icon-btn cursor-pointer flex-shrink-0">
<svg <SolidIcon
onMouseEnter={() => setHovered(true)} fill="var(--icon-default)"
onMouseLeave={() => setHovered(false)} name={isExpanded ? 'caretup' : 'caretdown'}
onClick={handleToggle} width="16"
className={`page-group-collapse ${isExpanded ? 'expanded' : 'collapsed'}`} viewBox="0 0 16 16"
width={17} />
height={16}
viewBox="0 0 17 16"
fill="black"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.1257 4L5.27446 4C4.50266 4 4.02179 4.83721 4.41068 5.50387L7.33631 10.5192C7.72218 11.1807 8.67798 11.1807 9.06386 10.5192L11.9895 5.50387C12.3784 4.83721 11.8975 4 11.1257 4Z"
fill="#ACB2B9"
/>
</svg>
</div> </div>
</div> </div>
@ -205,6 +224,7 @@ const RenderPageGroup = ({
linkRefs={linkRefs} linkRefs={linkRefs}
callback={handleToggle} callback={handleToggle}
position={position} position={position}
onPageClick={onPageClick}
/> />
))} ))}
</div> </div>
@ -228,6 +248,7 @@ export const RenderPageAndPageGroup = ({
isSidebarPinned, isSidebarPinned,
}) => { }) => {
const { moduleId } = useModuleContext(); const { moduleId } = useModuleContext();
const [expandedPageGroupId, setExpandedPageGroupId] = useState(null);
// Don't render empty folders if displaying only icons // Don't render empty folders if displaying only icons
const visibleTree = buildTree(position === 'top' ? visibleLinks : pages, !!labelStyle?.label?.hidden); const visibleTree = buildTree(position === 'top' ? visibleLinks : pages, !!labelStyle?.label?.hidden);
const overflowTree = buildTree(overflowLinks, !!labelStyle?.label?.hidden); const overflowTree = buildTree(overflowLinks, !!labelStyle?.label?.hidden);
@ -241,6 +262,17 @@ export const RenderPageAndPageGroup = ({
const currentPage = pages.find((page) => page.id === currentPageId); const currentPage = pages.find((page) => page.id === currentPageId);
const homePageId = useStore((state) => state.appStore.modules[moduleId].app.homePageId); const homePageId = useStore((state) => state.appStore.modules[moduleId].app.homePageId);
const [showPopover, setShowPopover] = useState(false); const [showPopover, setShowPopover] = useState(false);
const handleAccordionToggle = (groupId) => {
setExpandedPageGroupId((prevId) => (prevId === groupId ? null : groupId));
};
const closeAllAccordions = () => {
if (showPopover) {
setShowPopover(false);
}
setExpandedPageGroupId(null);
};
return ( return (
<div className={cx('page-handler-wrapper viewer', { 'dark-theme': darkMode })}> <div className={cx('page-handler-wrapper viewer', { 'dark-theme': darkMode })}>
{/* <Accordion alwaysOpen defaultActiveKey={tree.map((page) => page.id)}> */} {/* <Accordion alwaysOpen defaultActiveKey={tree.map((page) => page.id)}> */}
@ -273,6 +305,9 @@ export const RenderPageAndPageGroup = ({
linkRefs={linkRefs} linkRefs={linkRefs}
isSidebarPinned={isSidebarPinned} isSidebarPinned={isSidebarPinned}
position={position} position={position}
isExpanded={expandedPageGroupId === page.id}
onToggle={handleAccordionToggle}
onPageClick={closeAllAccordions}
/> />
</> </>
); );
@ -290,6 +325,7 @@ export const RenderPageAndPageGroup = ({
linkRefs={linkRefs} linkRefs={linkRefs}
isSidebarPinned={isSidebarPinned} isSidebarPinned={isSidebarPinned}
position={position} position={position}
onPageClick={closeAllAccordions}
/> />
); );
} }
@ -299,10 +335,11 @@ export const RenderPageAndPageGroup = ({
<button <button
ref={moreBtnRef} ref={moreBtnRef}
onClick={() => setShowPopover(!showPopover)} onClick={() => setShowPopover(!showPopover)}
className="tj-list-item page-name" className={`tj-list-item page-name more-btn-pages ${showPopover && 'tj-list-item-selected'}`}
style={{ cursor: 'pointer', fontSize: '14px', marginLeft: '0px' }} style={{ cursor: 'pointer', fontSize: '14px', marginLeft: '0px' }}
> >
<SolidIcon fill={'var(--icon-weak)'} viewBox="0 3 21 18" width="16px" name="morevertical" /> <SolidIcon fill={'var(--icon-weak)'} viewBox="0 3 21 18" width="16px" name="morevertical" />
<div style={{ marginLeft: '6px' }}>More</div> <div style={{ marginLeft: '6px' }}>More</div>
</button> </button>
@ -344,6 +381,9 @@ export const RenderPageAndPageGroup = ({
darkMode={darkMode} darkMode={darkMode}
linkRefs={linkRefs} linkRefs={linkRefs}
isSidebarPinned={isSidebarPinned} isSidebarPinned={isSidebarPinned}
isExpanded={expandedPageGroupId === page.id}
onToggle={handleAccordionToggle}
onPageClick={closeAllAccordions}
/> />
</> </>
); );
@ -360,6 +400,7 @@ export const RenderPageAndPageGroup = ({
homePageId={homePageId} homePageId={homePageId}
linkRefs={linkRefs} linkRefs={linkRefs}
isSidebarPinned={isSidebarPinned} isSidebarPinned={isSidebarPinned}
onPageClick={closeAllAccordions}
/> />
); );
} }

View file

@ -138,7 +138,7 @@ export const PageGroupItem = memo(({ page, index, collapsed, onCollapse, highlig
> >
<div <div
className={`page-menu-item page-group-item ${highlight ? 'highlight' : ''} ${darkMode ? 'dark-theme' : ''} ${ className={`page-menu-item page-group-item ${highlight ? 'highlight' : ''} ${darkMode ? 'dark-theme' : ''} ${
showPageOptions && isEditing ? 'is-selected' : '' (showPageOptions || showEditPopover) && isEditing ? 'is-selected' : ''
}`} }`}
onClick={() => { onClick={() => {
handleOpenPopup('group', page); handleOpenPopup('group', page);

View file

@ -72,7 +72,7 @@ export const PageMenuItem = withRouter(
const icon = (props) => { const icon = (props) => {
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon; const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
// eslint-disable-next-line import/namespace // eslint-disable-next-line import/namespace
const Icon = Icons?.[iconName] ?? Icons?.['IconFileDescription']; const Icon = Icons?.[iconName] ?? Icons?.['IconFile'];
return ( return (
<Icon {...props} style={{ width: '16px', height: '16px', color: 'var(--icons-default)', marginRight: '6px' }} /> <Icon {...props} style={{ width: '16px', height: '16px', color: 'var(--icons-default)', marginRight: '6px' }} />
@ -269,7 +269,6 @@ export const PageMenuItem = withRouter(
return ''; return '';
} }
return ( return (
<div <div
onMouseEnter={() => setIsHovered(true)} onMouseEnter={() => setIsHovered(true)}
@ -281,7 +280,7 @@ export const PageMenuItem = withRouter(
<> <>
<div <div
className={`page-menu-item ${darkMode && 'dark-theme'} ${ className={`page-menu-item ${darkMode && 'dark-theme'} ${
showPageOptions && isEditingPage ? 'is-selected' : '' showPageOptions || showEditingPopover || isEditingPage ? 'is-selected' : ''
}`} }`}
style={{ style={{
position: 'relative', position: 'relative',
@ -308,14 +307,14 @@ export const PageMenuItem = withRouter(
<> <>
{' '} {' '}
<div ref={optionBtnRef} className="left" data-cy={`pages-name-${page.name.toLowerCase()}`}> <div ref={optionBtnRef} className="left" data-cy={`pages-name-${page.name.toLowerCase()}`}>
{icon()} <div className="main-page-icon-wrapper">{icon()}</div>
<OverflowTooltip childrenClassName="page-name" style={{ ...computedStyles?.text, maxWidth: '159px' }}> <OverflowTooltip childrenClassName="page-name" style={{ ...computedStyles?.text }}>
{page.name} {page.name}
</OverflowTooltip> </OverflowTooltip>
<span className="meta-text" style={{ marginLeft: '6px' }}>
{PAGE_TYPES[page?.type]}
</span>
<span className="color-slate09 meta-text d-flex align-items-center justify-content-center"> <span className="color-slate09 meta-text d-flex align-items-center justify-content-center">
{PAGE_TYPES[page?.type] && ( // If 'page' object has a 'type' property like 'URL'
<span className="page-type-text">{PAGE_TYPES[page?.type]}</span>
)}
{isHomePage && ( {isHomePage && (
<ToolTip message="Home page" placement="bottom"> <ToolTip message="Home page" placement="bottom">
<div className=" d-flex align-items-center justify-content-center"> <div className=" d-flex align-items-center justify-content-center">

View file

@ -22,9 +22,9 @@ export default function PagePermission({ darkMode }) {
const togglePagePermissionModal = useStore((state) => state.togglePagePermissionModal); const togglePagePermissionModal = useStore((state) => state.togglePagePermissionModal);
const editingPage = useStore((state) => state.editingPage); const editingPage = useStore((state) => state.editingPage);
const appId = useStore((state) => state.appStore.modules[moduleId].app.appId); const appId = useStore((state) => state.appStore.modules[moduleId].app.appId);
const selectedUserGroups = useStore((state) => state.selectedUserGroups); const selectedUserGroups = useStore((state) => state.appPermission.selectedUserGroups);
const setSelectedUserGroups = useStore((state) => state.setSelectedUserGroups); const setSelectedUserGroups = useStore((state) => state.setSelectedUserGroups);
const selectedUsers = useStore((state) => state.selectedUsers); const selectedUsers = useStore((state) => state.appPermission.selectedUsers);
const setSelectedUsers = useStore((state) => state.setSelectedUsers); const setSelectedUsers = useStore((state) => state.setSelectedUsers);
const pagePermission = useStore((state) => state.pagePermission); const pagePermission = useStore((state) => state.pagePermission);
const setPagePermission = useStore((state) => state.setPagePermission); const setPagePermission = useStore((state) => state.setPagePermission);
@ -353,7 +353,7 @@ export default function PagePermission({ darkMode }) {
const UserGroupSelect = () => { const UserGroupSelect = () => {
const { moduleId } = useModuleContext(); const { moduleId } = useModuleContext();
const appId = useStore((state) => state.appStore.modules[moduleId].app.appId); const appId = useStore((state) => state.appStore.modules[moduleId].app.appId);
const selectedUserGroups = useStore((state) => state.selectedUserGroups); const selectedUserGroups = useStore((state) => state.appPermission.selectedUserGroups);
const setSelectedUserGroups = useStore((state) => state.setSelectedUserGroups); const setSelectedUserGroups = useStore((state) => state.setSelectedUserGroups);
const [userGroups, setUserGroups] = useState([]); const [userGroups, setUserGroups] = useState([]);
useEffect(() => { useEffect(() => {
@ -415,7 +415,7 @@ const UserSelect = () => {
const { moduleId } = useModuleContext(); const { moduleId } = useModuleContext();
const appId = useStore((state) => state.appStore.modules[moduleId].app.appId); const appId = useStore((state) => state.appStore.modules[moduleId].app.appId);
const editingPage = useStore((state) => state.editingPage); const editingPage = useStore((state) => state.editingPage);
const selectedUsers = useStore((state) => state.selectedUsers); const selectedUsers = useStore((state) => state.appPermission.selectedUsers);
const setSelectedUsers = useStore((state) => state.setSelectedUsers); const setSelectedUsers = useStore((state) => state.setSelectedUsers);
const [users, setUsers] = useState([]); const [users, setUsers] = useState([]);
useEffect(() => { useEffect(() => {

View file

@ -34,7 +34,7 @@ export const PagesSidebarNavigation = ({
const { moduleId } = useModuleContext(); const { moduleId } = useModuleContext();
const { definition: { styles = {}, properties = {} } = {} } = useStore((state) => state.pageSettings) || {}; const { definition: { styles = {}, properties = {} } = {} } = useStore((state) => state.pageSettings) || {};
const selectedVersionName = useStore((state) => state.selectedVersion?.name); const selectedVersionName = useStore((state) => state.selectedVersion?.name);
const currentMode = useStore((state) => state.currentMode); const currentMode = useStore((state) => state.modeStore.modules[moduleId].currentMode);
const selectedEnvironmentName = useStore((state) => state.selectedEnvironment?.name); const selectedEnvironmentName = useStore((state) => state.selectedEnvironment?.name);
const homePageId = useStore((state) => state.appStore.modules[moduleId].app.homePageId); const homePageId = useStore((state) => state.appStore.modules[moduleId].app.homePageId);
const license = useStore((state) => state.license); const license = useStore((state) => state.license);
@ -266,6 +266,10 @@ export const PagesSidebarNavigation = ({
const headerHidden = isLicensed ? hideHeader : false; const headerHidden = isLicensed ? hideHeader : false;
const isPagesSidebarHidden = resolveReferences(disableMenu?.value); const isPagesSidebarHidden = resolveReferences(disableMenu?.value);
if (hideHeader && hideLogo && isPagesSidebarHidden) {
return null;
}
return ( return (
<div> <div>
<button <button
@ -297,58 +301,60 @@ export const PagesSidebarNavigation = ({
ref={navRef} ref={navRef}
className={cx('navigation-area', { className={cx('navigation-area', {
close: !isSidebarPinned && properties?.collapsable && style !== 'text' && position === 'side', close: !isSidebarPinned && properties?.collapsable && style !== 'text' && position === 'side',
// 'sidebar-overlay': !isSidebarPinned && properties?.collapsable,
'icon-only': style === 'icon' || (style === 'texticon' && !isSidebarPinned && position === 'side'), 'icon-only': style === 'icon' || (style === 'texticon' && !isSidebarPinned && position === 'side'),
'position-top': position === 'top', 'position-top': position === 'top' || isPagesSidebarHidden,
'text-only': style === 'text', 'text-only': style === 'text',
'right-sidebar-open': isRightSidebarOpen && position === 'top', 'right-sidebar-open': isRightSidebarOpen && (position === 'top' || isPagesSidebarHidden),
'left-sidebar-open': isSidebarOpen && position === 'top', 'left-sidebar-open': isSidebarOpen && (position === 'top' || isPagesSidebarHidden),
})} })}
style={{ style={{
width: 226, width: 226,
position: 'sticky', position: 'sticky',
// height: `calc(100% - ${showHeader ? APP_HEADER_HEIGHT : 0}px)`, height: currentMode === 'edit' ? '100%' : `calc(100% - 32px)`,
// height,
height: '100%',
// top: showHeader ? '47px' : '0px',
top: '4px', top: '4px',
bottom: '0px', bottom: '0px',
background: !styles?.backgroundColor?.isDefault && styles?.backgroundColor?.value, background: !styles?.backgroundColor?.isDefault && styles?.backgroundColor?.value,
border: `${styles?.pillRadius?.value}px`, border: `${styles?.pillRadius?.value}px`,
borderRight: !styles?.borderColor?.isDefault ? `1px solid ${styles?.borderColor?.value}` : '', borderRight:
borderTop: !styles?.borderColor?.isDefault ? `1px solid ${styles?.borderColor?.value}` : '', !styles?.borderColor?.isDefault && position === 'side' ? `1px solid ${styles?.borderColor?.value}` : '',
borderBottom:
!styles?.borderColor?.isDefault && position === 'top' ? `1px solid ${styles?.borderColor?.value}` : '',
overflow: 'scroll', overflow: 'scroll',
boxShadow: 'var(--elevation-100-box-shadow)', boxShadow: 'var(--elevation-100-box-shadow)',
scrollbarWidth: 'none',
// ...(position === 'side' && isSidebarOpen ? { marginLeft: isSidebarPinned ? '574px' : '392px' } : {}),
}} }}
> >
<div className="position-relative"> <div style={{ overflow: 'hidden' }} className="position-relative">
<div {(collapsable || !headerHidden || !hideLogo) && (
style={{ <div
marginRight: headerHidden && position == 'top' && '0px', style={{
}} marginRight: hideHeader && hideLogo && position == 'top' && '0px',
className="app-name" }}
> className="app-name"
{!hideLogo && ( >
<div onClick={switchToHomePage} className="cursor-pointer"> {!hideLogo && (
<AppLogo isLoadingFromHeader={false} /> <div onClick={switchToHomePage} className="cursor-pointer flex-shrink-0">
</div> <AppLogo isLoadingFromHeader={false} />
)} </div>
{!headerHidden && ((isPinnedWithLabel && !labelHidden) || position === 'top') && ( )}
<span>{name?.trim() ? name : appName}</span> {!headerHidden && ((isPinnedWithLabel && !labelHidden) || position === 'top') && (
)} <OverflowTooltip>{name?.trim() ? name : appName}</OverflowTooltip>
{collapsable && !isTopPositioned && style == 'texticon' && position === 'side' && ( )}
<div onClick={toggleSidebarPinned} className="icon-btn collapse-icon "> {collapsable &&
<SolidIcon !isTopPositioned &&
className="cursor-pointer" style == 'texticon' &&
fill="var(--icon-strong)" position === 'side' &&
width="14px" !isPagesSidebarHidden && (
name={isSidebarPinned ? 'remove03' : 'menu'} <div onClick={toggleSidebarPinned} className="icon-btn collapse-icon ">
/> <SolidIcon
</div> className="cursor-pointer"
)} fill="var(--icon-strong)"
</div> width="14px"
name={isSidebarPinned ? 'remove03' : 'menu'}
/>
</div>
)}
</div>
)}
{isLicensed && !isPagesSidebarHidden ? ( {isLicensed && !isPagesSidebarHidden ? (
<RenderPageAndPageGroup <RenderPageAndPageGroup
switchPageWrapper={switchPageWrapper} switchPageWrapper={switchPageWrapper}
@ -414,12 +420,13 @@ const RenderPagesWithoutGroup = ({
moreBtnRef, moreBtnRef,
}) => { }) => {
const [showPopover, setShowPopover] = useState(false); const [showPopover, setShowPopover] = useState(false);
const filteredPagesVisible = visibleLinks.filter( const filteredPagesVisible = (position == 'top' ? visibleLinks : pages).filter(
(page) => (!page?.isPageGroup || page.children?.length > 0) && !page?.restricted (page) => (!page?.isPageGroup || page.children?.length > 0) && !page?.restricted
); );
const filteredPagesOverflow = overflowLinks.filter( const filteredPagesOverflow = overflowLinks.filter(
(page) => (!page?.isPageGroup || page.children?.length > 0) && !page?.restricted (page) => (!page?.isPageGroup || page.children?.length > 0) && !page?.restricted
); );
return ( return (
<div className={cx('page-handler-wrapper', { 'dark-theme': darkMode })}> <div className={cx('page-handler-wrapper', { 'dark-theme': darkMode })}>
{filteredPagesVisible.map((page) => { {filteredPagesVisible.map((page) => {

View file

@ -60,6 +60,7 @@
align-items: center; align-items: center;
padding: 7px 8px; padding: 7px 8px;
margin-top: 2px; margin-top: 2px;
width: 100%;
justify-content: space-between; justify-content: space-between;
background-color: var(--interactive-default); background-color: var(--interactive-default);
&.highlight { &.highlight {
@ -112,6 +113,8 @@
line-height: 18px; line-height: 18px;
margin-left: 8px; margin-left: 8px;
gap: 6px; gap: 6px;
flex-shrink: 0;
margin-right: 6px;
} }
button.edit-page-overlay-toggle{ button.edit-page-overlay-toggle{
opacity: 0; opacity: 0;
@ -134,13 +137,23 @@
// margin-left: 15px; // margin-left: 15px;
display: flex; display: flex;
align-items: center; align-items: center;
flex-grow: 1;
flex-shrink: 1;
min-width: 0;
.main-page-icon-wrapper {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.page-name{ .page-name{
overflow: hidden; overflow: hidden;
color: var(--slate12); color: var(--slate12);
font-size: 14px; font-size: 14px;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
max-width: 246px; flex-shrink: 1;
min-width: 0;
} }
} }
@ -410,8 +423,9 @@
} }
.licensed-page-option { .licensed-page-option {
pointer-events: unset !important;
button { button {
pointer-events: none; cursor: default;
&:hover { &:hover {
background-color: unset !important; background-color: unset !important;
} }
@ -468,6 +482,17 @@
} }
} }
.form-control.is-invalid {
background-image: none;
}
input[type='text'], textarea {
border-radius: 6px;
&:focus {
border: 2px solid var(--border-accent-strong);
}
}
.page-events { .page-events {
.page-empty-events { .page-empty-events {
display: flex; display: flex;

View file

@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useRef, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import cx from 'classnames'; import cx from 'classnames';
import useStore from '@/AppBuilder/_stores/store'; import useStore from '@/AppBuilder/_stores/store';
import ArrowLeft from '@/_ui/Icon/solidIcons/ArrowLeft'; import ArrowLeft from '@/_ui/Icon/solidIcons/ArrowLeft';
@ -45,7 +45,12 @@ export const PageSettings = () => {
const isVersionReleased = useStore((state) => state.isVersionReleased); const isVersionReleased = useStore((state) => state.isVersionReleased);
const switchPage = useStore((state) => state.switchPage); const switchPage = useStore((state) => state.switchPage);
const toggleRightSidebarPin = useStore((state) => state.toggleRightSidebarPin); const toggleRightSidebarPin = useStore((state) => state.toggleRightSidebarPin);
const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned); const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen);
const handleToggle = () => {
setActiveRightSideBarTab(null);
setRightSidebarOpen(false);
};
const treeRef = useRef(null); const treeRef = useRef(null);
const license = useStore((state) => state.license); const license = useStore((state) => state.license);
@ -161,10 +166,12 @@ export const PageSettings = () => {
<div className="inspector pages-settings"> <div className="inspector pages-settings">
<div> <div>
<div className="row inspector-component-title-input-holder d-flex align-items-center"> <div className="row inspector-component-title-input-holder d-flex align-items-center">
<div className={`col-9 p-0 ${isVersionReleased && 'disabled'}`}>Pages and navigation</div> <div style={{ padding: '7px 6px' }} className={`col-9 ${isVersionReleased && 'disabled'}`}>
Pages and navigation
</div>
<div className="d-flex icon-holder"> <div className="d-flex icon-holder">
<div className="icon-btn cursor-pointer" onClick={() => toggleRightSidebarPin()}> <div className="icon-btn cursor-pointer flex-shrink-0 p-2 h-4 w-4" onClick={handleToggle}>
<SolidIcon fill="var(--icon-strong)" name={isRightSidebarPinned ? 'unpin' : 'pin'} width="16" /> <SolidIcon fill="var(--icon-strong)" name={'remove03'} width="16" viewBox="0 0 16 16" />
</div> </div>
</div> </div>
</div> </div>
@ -223,13 +230,51 @@ const RenderStyles = React.memo(({ pagesMeta, renderCustomStyles }) => {
}); });
}); });
const AppHeaderMenu = ({ darkMode, pageSettings, pageSettingChanged, licenseValid }) => { export const AppHeaderMenu = ({ darkMode, pageSettings, pageSettingChanged, licenseValid }) => {
const { moduleId } = useModuleContext(); const { moduleId } = useModuleContext();
const [appName] = useStore((state) => [state.appStore.modules[moduleId].app.appName], shallow); const [appName] = useStore((state) => [state.appStore.modules[moduleId].app.appName], shallow);
const { definition: { properties = {} } = {} } = pageSettings ?? {}; const { definition: { properties = {} } = {} } = pageSettings ?? {};
const { hideHeader, name, hideLogo } = properties ?? {}; const { hideHeader, name, hideLogo } = properties ?? {};
const [_name, _setName] = useState(name?.trim() ? name : appName); const [_name, _setName] = useState(name?.trim() ? name : appName);
const [error, setError] = useState(null);
useEffect(() => {
const newNameValue = name?.trim() ? name : appName;
if (_name !== newNameValue) {
_setName(newNameValue);
}
}, [name, appName]);
const handleNameChange = (e) => {
const newValue = e.target.value;
_setName(newValue);
setError(null);
};
const handleNameBlur = (e) => {
const newValue = e.target.value.trim();
if (newValue === '') {
setError('Title cannot be empty.');
_setName(name?.trim() ? name : appName);
return;
}
if (newValue.length > 32) {
setError('Title cannot exceed 32 characters.');
_setName(name?.trim() ? name : appName);
return;
}
if (newValue !== (name?.trim() ? name : appName)) {
pageSettingChanged({ name: newValue }, 'properties');
setError(null);
} else {
setError(null);
}
};
return ( return (
<> <>
@ -279,14 +324,17 @@ const AppHeaderMenu = ({ darkMode, pageSettings, pageSettingChanged, licenseVali
<label className="form-label font-weight-400 mb-0">Title</label> <label className="form-label font-weight-400 mb-0">Title</label>
<input <input
type="text" type="text"
onBlur={(e) => { onBlur={handleNameBlur}
pageSettingChanged({ name: e.target.value }, 'properties'); onChange={handleNameChange}
}} className={`form-control ${error ? 'is-invalid' : ''}`}
onChange={(e) => _setName(e.target.value)}
className="form-control"
value={_name} value={_name}
minLength="1" maxLength={32}
/> />
{error && (
<div className="invalid-feedback" style={{ display: 'block' }}>
{error}
</div>
)}
</div> </div>
</div> </div>
</> </>
@ -318,6 +366,8 @@ const NavigationMenu = ({ darkMode, pageSettings, pageSettingChanged }) => {
return str.toLowerCase() === 'true'; return str.toLowerCase() === 'true';
} }
const [selectedStyle, setSelectedStyle] = useState(style);
return ( return (
<> <>
<div className="section-header pb-2"> <div className="section-header pb-2">
@ -357,6 +407,7 @@ const NavigationMenu = ({ darkMode, pageSettings, pageSettingChanged }) => {
pageSettingChanged({ position: value }, 'properties'); pageSettingChanged({ position: value }, 'properties');
}} }}
defaultValue={position?.toString()} defaultValue={position?.toString()}
style={{ width: '168px' }}
> >
{POSTIONS.map((mode) => ( {POSTIONS.map((mode) => (
<ToggleGroupItem key={mode.value} value={mode.value}> <ToggleGroupItem key={mode.value} value={mode.value}>
@ -371,14 +422,14 @@ const NavigationMenu = ({ darkMode, pageSettings, pageSettingChanged }) => {
<label className="form-label font-weight-400 mb-0">Style</label> <label className="form-label font-weight-400 mb-0">Style</label>
<Select <Select
options={styleOptions} options={styleOptions}
search={true} value={selectedStyle}
value={style}
onChange={(value) => { onChange={(value) => {
setSelectedStyle(value);
pageSettingChanged({ style: value }, 'properties'); pageSettingChanged({ style: value }, 'properties');
}} }}
placeholder={'Select...'} placeholder={'Select...'}
useMenuPortal={false} useMenuPortal={false}
width={'142px'} width={'168px'}
className={`${darkMode ? 'select-search-dark' : 'select-search'}`} className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
/> />
</div> </div>
@ -389,12 +440,10 @@ const NavigationMenu = ({ darkMode, pageSettings, pageSettingChanged }) => {
<div className="ms-auto position-relative app-mode-switch" style={{ paddingLeft: '0px' }}> <div className="ms-auto position-relative app-mode-switch" style={{ paddingLeft: '0px' }}>
<ToggleGroup <ToggleGroup
onValueChange={(value) => { onValueChange={(value) => {
// if (position === 'side' && value == 'false') {
// pageSettingChanged({ style: 'texticon' }, 'properties');
// }
pageSettingChanged({ collapsable: stringToBoolean(value) }, 'properties'); pageSettingChanged({ collapsable: stringToBoolean(value) }, 'properties');
}} }}
defaultValue={collapsable?.toString()} defaultValue={collapsable?.toString()}
style={{ width: '168px' }}
> >
{COLLAPSABLE_TOGGLES.map((mode) => ( {COLLAPSABLE_TOGGLES.map((mode) => (
<ToggleGroupItem key={mode.value} value={mode.value}> <ToggleGroupItem key={mode.value} value={mode.value}>

View file

@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import useStore from '@/AppBuilder/_stores/store'; import useStore from '@/AppBuilder/_stores/store';
import { ComponentConfigurationTab } from './ComponentConfigurationTab'; import { ComponentConfigurationTab } from './ComponentConfigurationTab';
import ComponentsManagerTab from './ComponentManagerTab'; import ComponentsManagerTab from './ComponentManagerTab';
@ -8,14 +8,29 @@ import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
export const RightSideBar = ({ darkMode }) => { export const RightSideBar = ({ darkMode }) => {
const { isModuleEditor } = useModuleContext(); const { isModuleEditor } = useModuleContext();
const queryPanelHeight = useStore((state) => state.queryPanel.queryPanelHeight);
const isDraggingQueryPane = useStore((state) => state.queryPanel.isDraggingQueryPane);
const activeTab = useStore((state) => state.activeRightSideBarTab); const activeTab = useStore((state) => state.activeRightSideBarTab);
const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen); const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen);
const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen); const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen);
const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned); const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned);
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab); const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab);
const [popoverContentHeight, setPopoverContentHeight] = useState(queryPanelHeight);
const sidebarRef = useRef(null); const sidebarRef = useRef(null);
useEffect(() => {
if (!isDraggingQueryPane) {
setPopoverContentHeight(
((window.innerHeight - (queryPanelHeight == 0 ? 40 : queryPanelHeight) - 45) / window.innerHeight) * 100
);
} else {
setPopoverContentHeight(100);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [queryPanelHeight, isDraggingQueryPane]);
// useEffect(() => { // useEffect(() => {
// const rigthSidebarMenu = document.querySelector('.right-sidebar-toggle'); // const rigthSidebarMenu = document.querySelector('.right-sidebar-toggle');
// function handleClickOutside(event) { // function handleClickOutside(event) {
@ -40,7 +55,10 @@ export const RightSideBar = ({ darkMode }) => {
return ( return (
<div ref={sidebarRef} className="sub-section"> <div ref={sidebarRef} className="sub-section">
<div className={cx('editor-sidebar', { 'dark-theme theme-dark': darkMode })}> <div
style={{ height: `${popoverContentHeight}vh`, overflow: 'auto' }}
className={cx('editor-sidebar', { 'dark-theme theme-dark': darkMode })}
>
<div className={cx({ 'dark-theme theme-dark': darkMode })} style={{ position: 'relative', height: '100%' }}> <div className={cx({ 'dark-theme theme-dark': darkMode })} style={{ position: 'relative', height: '100%' }}>
{activeTab === 'pages' && <PageSettings />} {activeTab === 'pages' && <PageSettings />}
{activeTab === 'components' && <ComponentsManagerTab darkMode={darkMode} isModuleEditor={isModuleEditor} />} {activeTab === 'components' && <ComponentsManagerTab darkMode={darkMode} isModuleEditor={isModuleEditor} />}

View file

@ -48,7 +48,8 @@ const RightSidebarToggle = ({ darkMode = false }) => {
handleToggle(RIGHT_SIDE_BAR_TAB.CONFIGURATION); handleToggle(RIGHT_SIDE_BAR_TAB.CONFIGURATION);
}} }}
darkMode={darkMode} darkMode={darkMode}
icon="inspect" icon="propertiesstyles"
iconWidth="14"
className={`left-sidebar-item left-sidebar-layout left-sidebar-inspector`} className={`left-sidebar-item left-sidebar-layout left-sidebar-inspector`}
tip="Component properties" tip="Component properties"
/> />

View file

@ -1,26 +1,39 @@
import SolidIcon from '@/_ui/Icon/SolidIcons'; import SolidIcon from '@/_ui/Icon/SolidIcons';
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import Tooltip from 'react-bootstrap/Tooltip'; import { ToolTip } from '@/_components';
import { useTranslation } from 'react-i18next';
// TODO: remove refs and related dependancies // TODO: remove refs and related dependancies
export const SidebarItem = forwardRef( export const SidebarItem = forwardRef(
({ tip = '', selectedSidebarItem, className, icon, iconFill = 'var(--slate8)', text, onClick, ...rest }, ref) => { (
const { t } = useTranslation(); {
tip = '',
selectedSidebarItem,
className,
icon,
iconFill = 'var(--slate8)',
text,
onClick,
iconWidth = 20,
...rest
},
ref
) => {
let displayIcon = icon; let displayIcon = icon;
return ( return (
<div {...rest} className={className} onClick={onClick && onClick} ref={ref}> <ToolTip placement="left" message={tip}>
{icon && ( <div {...rest} className={className} onClick={onClick && onClick} ref={ref}>
<div {icon && (
className={`sidebar-svg-icon position-relative ${selectedSidebarItem && 'sidebar-item'}`} <div
data-cy={`right-sidebar-${icon.toLowerCase()}-button`} className={`sidebar-svg-icon position-relative ${selectedSidebarItem && 'sidebar-item'}`}
> data-cy={`right-sidebar-${icon.toLowerCase()}-button`}
<SolidIcon name={displayIcon} width={20} fill={selectedSidebarItem ? '#3E63DD' : iconFill} /> >
</div> <SolidIcon name={displayIcon} width={iconWidth} fill={selectedSidebarItem ? '#3E63DD' : iconFill} />
)} </div>
<p>{text && t(`leftSidebar.${text}.text`, text)}</p> )}
</div> <p></p>
</div>
</ToolTip>
); );
} }
); );

View file

@ -13,6 +13,7 @@ import { redirectToDashboard } from '@/_helpers/routes';
import AppLogo from '@/_components/AppLogo'; import AppLogo from '@/_components/AppLogo';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext'; import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
import OverflowTooltip from '@/_components/OverflowTooltip';
const RenderGroup = ({ pages, pageGroup, currentPage, darkMode, handlepageSwitch, currentPageId, icon }) => { const RenderGroup = ({ pages, pageGroup, currentPage, darkMode, handlepageSwitch, currentPageId, icon }) => {
const { moduleId } = useModuleContext(); const { moduleId } = useModuleContext();
@ -23,7 +24,7 @@ const RenderGroup = ({ pages, pageGroup, currentPage, darkMode, handlepageSwitch
setIsExpanded(!isExpanded); setIsExpanded(!isExpanded);
}; };
// eslint-disable-next-line import/namespace // eslint-disable-next-line import/namespace
const IconElement = Icons?.[pageGroup?.icon] ?? Icons?.['IconFileDescription']; const IconElement = Icons?.[pageGroup?.icon] ?? Icons?.['IconFile'];
return ( return (
<> <>
<div style={{ border: 'none' }} className={`accordion-item ${darkMode ? 'dark-mode' : ''} `}> <div style={{ border: 'none' }} className={`accordion-item ${darkMode ? 'dark-mode' : ''} `}>
@ -60,7 +61,7 @@ const RenderGroup = ({ pages, pageGroup, currentPage, darkMode, handlepageSwitch
const isHomePage = page.id === homePageId; const isHomePage = page.id === homePageId;
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon; const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
// eslint-disable-next-line import/namespace // eslint-disable-next-line import/namespace
const IconElement = Icons?.[iconName] ?? Icons?.['IconFileDescription']; const IconElement = Icons?.[iconName] ?? Icons?.['IconFile'];
return page?.hidden || page?.disabled ? null : ( return page?.hidden || page?.disabled ? null : (
<div <div
key={page.handle} key={page.handle}
@ -107,7 +108,7 @@ const RenderPageGroups = ({ pages, handlepageSwitch, darkMode, currentPageId, cu
const isHomePage = page.id === homePageId; const isHomePage = page.id === homePageId;
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon; const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
// eslint-disable-next-line import/namespace // eslint-disable-next-line import/namespace
const IconElement = Icons?.[iconName] ?? Icons?.['IconFileDescription']; const IconElement = Icons?.[iconName] ?? Icons?.['IconFile'];
return page?.hidden || page?.disabled ? null : ( return page?.hidden || page?.disabled ? null : (
<div <div
key={page.handle} key={page.handle}
@ -187,6 +188,7 @@ const MobileNavigationMenu = ({
display: 'inline-block', display: 'inline-block',
padding: '0.5rem 0rem', padding: '0.5rem 0rem',
width: '100%', width: '100%',
overflow: 'hidden',
}, },
bmOverlay: { bmOverlay: {
background: 'rgba(0, 0, 0, 0.3)', background: 'rgba(0, 0, 0, 0.3)',
@ -242,7 +244,7 @@ const MobileNavigationMenu = ({
{!hideLogo && <AppLogo isLoadingFromHeader={false} viewer={true} />} {!hideLogo && <AppLogo isLoadingFromHeader={false} viewer={true} />}
{!hideHeader && ( {!hideHeader && (
<div className="d-flex align-items-center app-title"> <div className="d-flex align-items-center app-title">
<span>{name?.trim() ? name : appName}</span> <OverflowTooltip>{name?.trim() ? name : appName}</OverflowTooltip>
</div> </div>
)} )}
</Link> </Link>
@ -250,7 +252,7 @@ const MobileNavigationMenu = ({
</div> </div>
</Header> </Header>
<div className="w-100"> <div style={{ paddingBottom: '48px' }} className="w-100 overflow-auto h-100">
<div className={`pages-container ${darkMode && 'dark'}`}> <div className={`pages-container ${darkMode && 'dark'}`}>
{isLicensed ? ( {isLicensed ? (
<RenderPageGroups <RenderPageGroups
@ -265,7 +267,7 @@ const MobileNavigationMenu = ({
const isHomePage = page.id === homePageId; const isHomePage = page.id === homePageId;
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon; const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
// eslint-disable-next-line import/namespace // eslint-disable-next-line import/namespace
const IconElement = Icons?.[iconName] ?? Icons?.['IconFileDescription']; const IconElement = Icons?.[iconName] ?? Icons?.['IconFile'];
return page?.hidden || page?.disabled ? null : ( return page?.hidden || page?.disabled ? null : (
<div <div
key={page.handle} key={page.handle}

View file

@ -16,7 +16,9 @@ import Popups from '../Popups';
import { ModuleProvider } from '@/AppBuilder/_contexts/ModuleContext'; import { ModuleProvider } from '@/AppBuilder/_contexts/ModuleContext';
import { getPatToken, setPatToken } from '@/AppBuilder/EmbedApp'; import { getPatToken, setPatToken } from '@/AppBuilder/EmbedApp';
import Spinner from '@/_ui/Spinner'; import Spinner from '@/_ui/Spinner';
import { checkIfLicenseNotValid } from '@/_helpers/appUtils';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import TooljetBanner from '../../Editor/Viewer/TooljetBanner';
export const Viewer = ({ export const Viewer = ({
id: appId, id: appId,
@ -107,15 +109,13 @@ export const Viewer = ({
const { position, hideHeader } = properties ?? {}; const { position, hideHeader } = properties ?? {};
const canvasRef = useRef(null); const canvasRef = useRef(null);
const isLoading = false;
const isMobilePreviewMode = selectedVersion?.id && currentLayout === 'mobile'; const isMobilePreviewMode = selectedVersion?.id && currentLayout === 'mobile';
const isAppLoaded = !!editingVersion; const isAppLoaded = !!editingVersion;
const isMobileDevice = deviceWindowWidth < 600;
const switchPage = useStore((state) => state.switchPage); const switchPage = useStore((state) => state.switchPage);
const showHeader = !globalSettings?.hideHeader && isAppLoaded; const showHeader = !globalSettings?.hideHeader && isAppLoaded;
const isLicenseValid = useStore((state) => state.isLicenseValid); const isLicenseNotValid = checkIfLicenseNotValid();
const licenseValid = isLicenseValid();
// ---remove // ---remove
const handleAppEnvironmentChanged = useCallback((environment) => { const handleAppEnvironmentChanged = useCallback((environment) => {
console.log('setAppVersionCurrentEnvironment', environment); console.log('setAppVersionCurrentEnvironment', environment);
@ -229,19 +229,6 @@ export const Viewer = ({
}} }}
> >
<div className={`areas d-flex flex-rows app-${appId}`}> <div className={`areas d-flex flex-rows app-${appId}`}>
{/* {currentLayout !== 'mobile' && !isPagesSidebarHidden && (
<PagesSidebarNavigation
showHeader={showHeader}
isMobileDevice={currentLayout === 'mobile'}
pages={pages}
currentPageId={currentPageId ?? homePageId}
darkMode={darkMode}
isSidebarPinned={isSidebarPinned}
toggleSidebarPinned={toggleSidebarPinned}
switchPage={switchPage}
/>
)} */}
<div <div
className={cx('flex-grow-1 d-flex justify-content-center canvas-box', { className={cx('flex-grow-1 d-flex justify-content-center canvas-box', {
close: !isSidebarPinned, close: !isSidebarPinned,
@ -292,6 +279,7 @@ export const Viewer = ({
darkMode={darkMode} darkMode={darkMode}
/> />
</div> </div>
{isLicenseNotValid && isAppLoaded && <TooljetBanner isDarkMode={darkMode} />}
{isMobilePreviewMode && <div className="hide-drawer-transition" style={{ right: 0 }}></div>} {isMobilePreviewMode && <div className="hide-drawer-transition" style={{ right: 0 }}></div>}
{isMobilePreviewMode && <div className="hide-drawer-transition" style={{ left: 0 }}></div>} {isMobilePreviewMode && <div className="hide-drawer-transition" style={{ left: 0 }}></div>}
</div> </div>

View file

@ -193,6 +193,7 @@
.mobile-nav-container { .mobile-nav-container {
.header-container { .header-container {
background-color: unset !important; background-color: unset !important;
flex-wrap: unset;
} }
} }
} }

View file

@ -1557,7 +1557,6 @@ export const createComponentsSlice = (set, get) => ({
!selectedText !selectedText
) { ) {
clearSelectedComponents(); clearSelectedComponents();
setActiveRightSideBarTab(RIGHT_SIDE_BAR_TAB.COMPONENTS);
// if (!isRightSidebarPinned) { // if (!isRightSidebarPinned) {
// setRightSidebarOpen(false); // setRightSidebarOpen(false);
// } // }

View file

@ -119,14 +119,7 @@ export const createPageMenuSlice = (set, get) => {
state.showSearch = show; state.showSearch = show;
if (!show) state.pageSearchResults = null; if (!show) state.pageSearchResults = null;
}), }),
openPageEditPopover: (page, ref) =>
set((state) => {
state.editingPage = page;
if (ref) {
state.popoverTargetId = ref?.current?.id;
state.showEditingPopover = true;
}
}),
setNewPagePopupConfig: (config) => setNewPagePopupConfig: (config) =>
set((state) => { set((state) => {
state.newPagePopupConfig = { state.newPagePopupConfig = {
@ -134,14 +127,6 @@ export const createPageMenuSlice = (set, get) => {
...config, ...config,
}; };
}), }),
closePageEditPopover: () =>
set((state) => {
state.showEditingPopover = false;
state.showEditPageEventsModal = false;
state.showRenamePageHandleModal = false;
state.showEditPageNameInput = false;
state.showDeleteConfirmationModal = false;
}),
toggleEditPageHandleModal: (show) => toggleEditPageHandleModal: (show) =>
set((state) => { set((state) => {
@ -481,5 +466,28 @@ export const createPageMenuSlice = (set, get) => {
set((state) => { set((state) => {
state.editingPage = page; state.editingPage = page;
}), }),
openPageEditPopover: (type, page, ref) => {
// Assuming ref is passed for targeting
set((state) => ({
editingPage: page,
showEditingPopover: true, // Make sure this is explicitly set to true
newPagePopupConfig: {
// Set default values or infer from page
show: true, // This might be redundant if showEditingPopover is the primary flag
mode: type,
type: page?.type || 'default',
},
}));
// You might store the target ref in the state if overlays need to dynamically pick it up
// For react-bootstrap Overlay, the target is passed as a prop, not globally
},
// And when closing:
closePageEditPopover: () => {
set((state) => ({
editingPage: null,
showEditingPopover: false,
newPagePopupConfig: { show: false, mode: null, type: null },
}));
},
}; };
}; };

View file

@ -77,7 +77,7 @@ export const AppsRoute = ({ children, componentType }) => {
const handleBrowserNavigation = (e) => { const handleBrowserNavigation = (e) => {
const { id, handle } = e.state; const { id, handle } = e.state;
switchPage(id, handle, [], true); switchPage(id, handle, []);
}; };
return <RouteLoader isLoading={isLoading}>{clonedElement}</RouteLoader>; return <RouteLoader isLoading={isLoading}>{clonedElement}</RouteLoader>;

View file

@ -55,7 +55,8 @@ export const DarkModeToggle = function DarkModeToggle({
springConfig: { mass: 4, tension: 250, friction: 35 }, springConfig: { mass: 4, tension: 250, friction: 35 },
}; };
const { r, transform, cx, cy, opacity } = properties[darkMode ? 'moon' : 'sun']; const { r, transform, cx, cy, opacity } =
properties[darkMode || (appMode === 'dark' && toggleForCanvas) ? 'moon' : 'sun'];
const svgContainerProps = useSpring({ const svgContainerProps = useSpring({
transform, transform,

View file

@ -1,6 +1,15 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState, useCallback } from 'react';
import { ToolTip } from '@/_components'; import { ToolTip } from '@/_components';
const isTextOverflowing = (element, verticalTolerance = 4) => {
if (!element) return false;
const horizontalOverflow = element.scrollWidth > element.clientWidth;
const verticalOverflow = element.scrollHeight > element.clientHeight + verticalTolerance;
return horizontalOverflow || verticalOverflow;
};
export default function OverflowTooltip({ export default function OverflowTooltip({
children, children,
className, className,
@ -10,21 +19,44 @@ export default function OverflowTooltip({
maxLetters, maxLetters,
...rest ...rest
}) { }) {
const [isOverflowed, setIsOverflow] = useState(false); const [isOverflowed, setIsOverflowed] = useState(false);
const textElementRef = useRef(); const textContentRef = useRef(null);
const checkOverflow = useCallback(() => {
if (textContentRef.current) {
setIsOverflowed(isTextOverflowing(textContentRef.current));
}
}, []);
useEffect(() => { useEffect(() => {
setIsOverflow( const currentTextElement = textContentRef.current;
textElementRef.current.scrollWidth > textElementRef.current.clientWidth || if (!currentTextElement) {
textElementRef.current.clientHeight < textElementRef.current.scrollHeight - 4 return;
); }
}, [children, boxWidth]);
checkOverflow();
const observer = new ResizeObserver((entries) => {
checkOverflow();
});
observer.observe(currentTextElement);
return () => {
observer.unobserve(currentTextElement);
observer.disconnect();
};
}, [children, checkOverflow]);
const displayText = const displayText =
maxLetters && typeof children === 'string' && children.length > maxLetters maxLetters && typeof children === 'string' && children.length > maxLetters
? `${children.substring(0, maxLetters)}...` ? `${children.substring(0, maxLetters)}...`
: children; : children;
useEffect(() => {
checkOverflow();
}, [maxLetters, checkOverflow]);
return ( return (
<ToolTip <ToolTip
className={className} className={className}
@ -36,7 +68,7 @@ export default function OverflowTooltip({
width={rest?.width} width={rest?.width}
> >
<div <div
ref={textElementRef} ref={textContentRef}
className={rest.childrenClassName} className={rest.childrenClassName}
style={{ style={{
whiteSpace, whiteSpace,

View file

@ -1106,42 +1106,42 @@ export function previewQuery(_ref, query, calledFromQuery = false, userSuppliedP
queryStatusCode === 400 || queryStatusCode === 400 ||
queryStatusCode === 404 || queryStatusCode === 404 ||
queryStatusCode === 422: { queryStatusCode === 422: {
let errorData = {}; let errorData = {};
switch (query.kind) { switch (query.kind) {
case 'runpy': case 'runpy':
errorData = data.data; errorData = data.data;
break; break;
case 'tooljetdb': case 'tooljetdb':
if (data?.error) { if (data?.error) {
errorData = { errorData = {
message: data?.error?.message || 'Something went wrong', message: data?.error?.message || 'Something went wrong',
description: data?.error?.message || 'Something went wrong', description: data?.error?.message || 'Something went wrong',
status: data?.statusText || 'Failed', status: data?.statusText || 'Failed',
data: data?.error || {}, data: data?.error || {},
}; };
} else { } else {
errorData = data;
errorData.description = data.errorMessage || 'Something went wrong';
}
break;
default:
errorData = data; errorData = data;
break; errorData.description = data.errorMessage || 'Something went wrong';
} }
break;
onEvent(_ref, 'onDataQueryFailure', queryEvents); default:
useCurrentStateStore.getState().actions.setErrors({ errorData = data;
[query.name]: { break;
type: 'query',
kind: query.kind,
data: errorData,
options: options,
},
});
if (!calledFromQuery) setPreviewData(errorData);
break;
} }
onEvent(_ref, 'onDataQueryFailure', queryEvents);
useCurrentStateStore.getState().actions.setErrors({
[query.name]: {
type: 'query',
kind: query.kind,
data: errorData,
options: options,
},
});
if (!calledFromQuery) setPreviewData(errorData);
break;
}
case queryStatus === 'needs_oauth': { case queryStatus === 'needs_oauth': {
const url = data.data.auth_url; // Backend generates and return sthe auth url const url = data.data.auth_url; // Backend generates and return sthe auth url
const kind = data.data?.kind; const kind = data.data?.kind;
@ -1158,44 +1158,44 @@ export function previewQuery(_ref, query, calledFromQuery = false, userSuppliedP
queryStatus === 'Created' || queryStatus === 'Created' ||
queryStatus === 'Accepted' || queryStatus === 'Accepted' ||
queryStatus === 'No Content': { queryStatus === 'No Content': {
if (query.options.enableTransformation) { if (query.options.enableTransformation) {
finalData = await runTransformation( finalData = await runTransformation(
_ref, _ref,
finalData, finalData,
query.options.transformation, query.options.transformation,
query.options.transformationLanguage, query.options.transformationLanguage,
query, query,
'edit' 'edit'
); );
if (finalData?.status === 'failed') { if (finalData?.status === 'failed') {
useCurrentStateStore.getState().actions.setErrors({ useCurrentStateStore.getState().actions.setErrors({
[query.name]: {
type: 'transformations',
data: finalData,
options: options,
},
});
onEvent(_ref, 'onDataQueryFailure', queryEvents);
setPreviewLoading(false);
resolve({ status: data.status, data: finalData });
// console.log('Test', finalData);
if (!calledFromQuery) setPreviewData(finalData);
return;
}
}
useCurrentStateStore.getState().actions.setCurrentState({
succededQuery: {
[query.name]: { [query.name]: {
type: 'query', type: 'transformations',
kind: query.kind, data: finalData,
options: options,
}, },
}, });
}); onEvent(_ref, 'onDataQueryFailure', queryEvents);
if (!calledFromQuery) setPreviewData(finalData); setPreviewLoading(false);
onEvent(_ref, 'onDataQuerySuccess', queryEvents, 'edit'); resolve({ status: data.status, data: finalData });
break; // console.log('Test', finalData);
if (!calledFromQuery) setPreviewData(finalData);
return;
}
} }
useCurrentStateStore.getState().actions.setCurrentState({
succededQuery: {
[query.name]: {
type: 'query',
kind: query.kind,
},
},
});
if (!calledFromQuery) setPreviewData(finalData);
onEvent(_ref, 'onDataQuerySuccess', queryEvents, 'edit');
break;
}
} }
setPreviewLoading(false); setPreviewLoading(false);
@ -1411,10 +1411,10 @@ export function runQuery(
}, },
query.kind === 'restapi' query.kind === 'restapi'
? { ? {
request: data.data.requestObject, request: data.data.requestObject,
response: data.data.responseObject, response: data.data.responseObject,
responseHeaders: data.data.responseHeaders, responseHeaders: data.data.responseHeaders,
} }
: {} : {}
), ),
}, },
@ -2405,11 +2405,11 @@ export const removeFunctionObjects = (obj) => {
}; };
export const checkIfLicenseNotValid = () => { export const checkIfLicenseNotValid = () => {
const licenseStatus = useEditorStore.getState().featureAccess?.licenseStatus; const licenseStatus = useStore.getState().license.featureAccess?.licenseStatus;
// When purchased, then isExpired key is also avialale else its not available // When purchased, then isExpired key is also avialale else its not available
if (licenseStatus) { if (licenseStatus) {
if (_.has(licenseStatus, 'isExpired')) { if (_.has(licenseStatus, 'isExpired')) {
return licenseStatus?.isExpired && !licenseStatus?.isLicenseValid; return licenseStatus?.isExpired;
} }
return !licenseStatus?.isLicenseValid; return !licenseStatus?.isLicenseValid;
} }
@ -2431,12 +2431,12 @@ export function isPDFSupported() {
function getBrowserUserAgent(userAgent) { function getBrowserUserAgent(userAgent) {
var regexps = { var regexps = {
Chrome: [/Chrome\/(\S+)/], Chrome: [/Chrome\/(\S+)/],
Firefox: [/Firefox\/(\S+)/], Firefox: [/Firefox\/(\S+)/],
MSIE: [/MSIE (\S+);/], MSIE: [/MSIE (\S+);/],
Opera: [/Opera\/.*?Version\/(\S+)/ /* Opera 10 */, /Opera\/(\S+)/ /* Opera 9 and older */], Opera: [/Opera\/.*?Version\/(\S+)/ /* Opera 10 */, /Opera\/(\S+)/ /* Opera 9 and older */],
Safari: [/Version\/(\S+).*?Safari\//], Safari: [/Version\/(\S+).*?Safari\//],
}, },
re, re,
m, m,
browser, browser,

View file

@ -617,15 +617,14 @@
.page-handler-wrapper { .page-handler-wrapper {
background: transparent; background: transparent;
height: 100%;
scrollbar-width: none;
overflow: auto; overflow: auto;
&.viewer{ scrollbar-width: thin;
overflow: visible !important; height: 100%;
} padding-bottom: 48px;
scrollbar-color: var(--interactive-selected) transparent;
&::-webkit-scrollbar { &::-webkit-scrollbar-track {
display: none; background: transparent;
} }
.tj-list-item-selected { .tj-list-item-selected {

View file

@ -142,7 +142,6 @@
.accordion-header { .accordion-header {
height: 32px; height: 32px;
background-color: var(--interactive-default);
.accordion-title-text { .accordion-title-text {
color: var(--text-default); color: var(--text-default);
@ -150,6 +149,17 @@
} }
} }
.form-control.is-invalid {
background-image: none;
}
input[type='text'] {
border-radius: 6px;
&:focus {
border: 2px solid var(--border-accent-strong);
}
}
.accordion-body { .accordion-body {
padding: 16px !important; padding: 16px !important;
@ -217,7 +227,7 @@
position: relative; position: relative;
bottom: 2px; bottom: 2px;
justify-content: center; justify-content: center;
padding-top: 32px; padding-top: 16px;
margin-top: auto; margin-top: auto;
.sidebar-svg-icon { .sidebar-svg-icon {
@ -241,6 +251,7 @@
align-items: center; align-items: center;
width: unset !important; width: unset !important;
margin-bottom: 20px; margin-bottom: 20px;
min-height: 28px;
.collapse-icon { .collapse-icon {
margin-left: auto; margin-left: auto;
@ -276,6 +287,23 @@
} }
.page-group-wrapper { .page-group-wrapper {
padding: 0px !important;
.icon-btn {
height: unset !important;
width: unset !important;
padding: 0px !important;
padding-right: 12px !important;
&:hover {
background-color: unset !important
}
}
.tj-list-item {
margin-bottom: 0px !important;
padding-right: 0px !important;
flex-shrink: 1;
min-width: 0;
}
&.tj-list-item-selected { &.tj-list-item-selected {
border-radius: 6px; border-radius: 6px;
background: var(--background-accent-weak); background: var(--background-accent-weak);
@ -342,14 +370,15 @@
.app-name { .app-name {
flex-direction: column-reverse; flex-direction: column-reverse;
margin-bottom: 0px; margin-bottom: 0px;
gap: 0px;
.cursor-pointer { .cursor-pointer {
img { img {
margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
} }
.icon-btn { .icon-btn {
margin-left: 0px; margin-left: 0px;
margin-bottom: 20px
} }
} }
} }
@ -365,6 +394,7 @@
} }
.accordion-body{ .accordion-body{
padding: 4px 0 4px 16px !important; padding: 4px 0 4px 16px !important;
border-bottom: 0px !important;
} }
.accordion-header{ .accordion-header{
height: auto !important; height: auto !important;
@ -429,11 +459,12 @@
padding-top: 0px; padding-top: 0px;
overflow: hidden !important; overflow: hidden !important;
justify-content: space-between; justify-content: space-between;
border-right: 0px !important;
.page-handler-wrapper { .page-handler-wrapper {
width: 100%; width: 100%;
align-items: center; align-items: center;
gap: 1px; gap: 8px;
} }
.page-dark-mode-btn-wrapper { .page-dark-mode-btn-wrapper {
@ -453,6 +484,36 @@
.tj-list-item { .tj-list-item {
padding-right: 0px !important; padding-right: 0px !important;
width: unset !important; width: unset !important;
margin-bottom: 0px !important;
}
.page-group-wrapper {
gap: 2px;
padding: 6px 10px !important;
padding-right: 0px !important;
&:hover {
background-color: var(--interactive-overlays-fill-hover);
}
button {
&:hover {
background-color: unset !important;
}
}
.icon-btn {
height: unset !important;
width: unset !important;
padding: 0px !important;
padding-right: 10px !important;
&:hover {
background-color: unset !important
}
}
.tj-list-item {
padding: 0px !important;
}
} }
} }
@ -484,8 +545,9 @@
.tj-list-item { .tj-list-item {
outline: none !important; outline: none !important;
padding: 6px 10px;
height: 32px; height: 32px;
padding: 6px 10px !important;
margin-bottom: 0px !important;
} }
.page-name { .page-name {
@ -498,11 +560,23 @@
.page-handler-wrapper { .page-handler-wrapper {
display: flex; display: flex;
padding-bottom: unset !important;
overflow: hidden;
.more-btn-pages {
&.tj-list-item-selected {
svg {
path {
fill: var(--icon-accent);
}
}
}
}
} }
} }
&.right-sidebar-open { &.right-sidebar-open {
width: calc(100% + 299px) !important; width: calc(100% + 300px) !important;
position: sticky !important; position: sticky !important;
} }
@ -514,7 +588,7 @@
} }
&.right-sidebar-open.left-sidebar-open { &.right-sidebar-open.left-sidebar-open {
width: calc(100% + 646px) !important; width: calc(100% + 650px) !important;
position: sticky !important; position: sticky !important;
} }
} }
@ -526,7 +600,7 @@
width: fit-content; width: fit-content;
.tj-list-item{ .tj-list-item{
padding: 8px 12px; padding: 6px 12px;
color: var(--text-placeholder); color: var(--text-placeholder);
justify-content: flex-start; justify-content: flex-start;
@ -550,12 +624,9 @@
.accordion-body { .accordion-body {
padding: 0px 12px !important; padding: 0px 12px !important;
padding-left: 28px !important; padding-left: 28px !important;
border-bottom: 0px !important;
} }
.accordion-body {
}
.page-group-collapse { .page-group-collapse {
// right: 4px; // right: 4px;
transform: rotate(30deg); transform: rotate(30deg);
@ -601,8 +672,19 @@
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0px; padding: 0px;
.icon-btn {
height: unset !important;
width: unset !important;
padding: 0px !important;
padding-right: 12px !important;
&:hover {
background-color: unset !important
}
}
.tj-list-item { .tj-list-item {
padding-right: 0px; padding: 8px 12px !important;
padding-right: 0px !important;
width: unset !important; width: unset !important;
} }
&.tj-list-item-selected { &.tj-list-item-selected {

View file

@ -51,7 +51,7 @@
div[data-radix-popper-content-wrapper]:has(.PopoverContent.drawer-height) { div[data-radix-popper-content-wrapper]:has(.PopoverContent.drawer-height) {
margin-top: 48px; margin-top: 48px;
margin-left: 47px !important; margin-left: 48px !important;
transform: none !important; transform: none !important;
z-index: 2 !important; z-index: 2 !important;
} }
@ -59,6 +59,6 @@ div[data-radix-popper-content-wrapper]:has(.PopoverContent.drawer-height) {
@-moz-document url-prefix() { @-moz-document url-prefix() {
div[data-radix-popper-content-wrapper] { div[data-radix-popper-content-wrapper] {
z-index: 100 !important; z-index: 100 !important;
left: 1px !important; // left: 1px !important;
} }
} }

View file

@ -93,7 +93,7 @@ $border-radius: 4px;
height: 400px; height: 400px;
position: fixed; position: fixed;
left: 48px; left: 48px;
right: 348px; right: 48px;
bottom: 0; bottom: 0;
overflow-x: hidden; overflow-x: hidden;
flex: 1 1 auto; flex: 1 1 auto;

View file

@ -662,11 +662,13 @@ button {
padding: 4px 12px; padding: 4px 12px;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
border-bottom: 1px solid var(--border-weak);
.header { .header {
color: var(--text-default); color: var(--text-default);
font-weight: 500; font-weight: 500;
font-size: 12px; font-size: 12px;
padding: 7px 6px;
} }
} }
@ -823,8 +825,13 @@ button {
background: transparent !important; background: transparent !important;
} }
.canvas-container { :hover {
scrollbar-width: none; .canvas-container {
scrollbar-width: thin;
&::-webkit-scrollbar-track {
background: transparent !important;
}
}
} }
.canvas-container { .canvas-container {
@ -839,6 +846,16 @@ button {
justify-content: center; justify-content: center;
-webkit-box-align: center; -webkit-box-align: center;
align-items: center; align-items: center;
scrollbar-color: var(--interactive-selected) transparent;
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: var(--interactive-default) !important;
border-radius: 3px;
}
.real-canvas { .real-canvas {
outline: 1px dotted transparent; outline: 1px dotted transparent;
@ -5203,12 +5220,13 @@ input[type="text"] {
} }
.inspector-component-title-input-holder { .inspector-component-title-input-holder {
padding: 2px 12px; padding: 4px 12px;
margin: 0; margin: 0;
display: flex; display: flex;
align-items: center; align-items: center;
height: 36px; height: 36px;
flex-direction: column; flex-direction: column;
justify-content: center;
.icon-btn { .icon-btn {
width: 32px; width: 32px;
@ -18706,4 +18724,12 @@ section.ai-message-prompt-input-wrapper {
.git-sync-modal .modal-header .modal-title .push-pull-tabs .tab-push.active, .git-sync-modal .modal-header .modal-title .push-pull-tabs .tab-push.active,
.git-sync-modal .modal-header .modal-title .push-pull-tabs .tab-pull.active { .git-sync-modal .modal-header .modal-title .push-pull-tabs .tab-pull.active {
border-bottom: 2px solid var(--indigo9) !important; border-bottom: 2px solid var(--indigo9) !important;
}
.min-width-0 {
min-width: 0 !important;
}
.width-unset {
width: unset !important;
} }

View file

@ -0,0 +1,14 @@
import React from 'react';
const CaretDown = ({ fill = '#C1C8CD', width = '16', className = '', viewBox = '0 0 16 16' }) => (
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={width} viewBox={viewBox} fill="none">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M11.3271 7.16832C11.4633 6.97259 11.504 6.67822 11.4303 6.42249C11.3566 6.16674 11.183 6 10.9903 6H5.2761C5.08348 6 4.90986 6.16674 4.83615 6.42249C4.76244 6.67822 4.8032 6.97259 4.93939 7.16832L7.62814 11.0327C7.90709 11.4335 8.35935 11.4335 8.63829 11.0327L11.3271 7.16832Z"
fill={fill}
/>
</svg>
);
export default CaretDown;

View file

@ -0,0 +1,14 @@
import React from 'react';
const CaretUp = ({ fill = '#C1C8CD', width = '16', className = '', viewBox = '0 0 16 16' }) => (
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={width} viewBox={viewBox} fill="none">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12.5272 8.83298C12.6634 9.02872 12.7041 9.32308 12.6304 9.57882C12.5567 9.83456 12.3831 10.0013 12.1905 10.0013L6.47621 10.0013C6.28359 10.0013 6.10998 9.83456 6.03626 9.57882C5.96255 9.32308 6.00331 9.02872 6.1395 8.83298L8.82826 4.9686C9.1072 4.56776 9.55946 4.56776 9.83841 4.9686L12.5272 8.83298Z"
fill={fill}
/>
</svg>
);
export default CaretUp;

View file

@ -0,0 +1,14 @@
import React from 'react';
const PropertiesStyles = ({ fill = '#ACB2B9', width = '14', viewBox = '0 0 14 14', className = '' }) => (
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={width} viewBox={viewBox} fill={fill}>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.98645 1.1867L3.98282 2.19035C3.73248 2.44068 3.73248 2.84655 3.98282 3.09689C4.23315 3.34722 4.63902 3.34722 4.88936 3.09689L5.893 2.09325L6.73764 2.9379L2.92525 6.72979L0.483747 4.28827C0.283478 4.08801 0.283478 3.76331 0.483747 3.56305L3.56066 0.486139C3.76092 0.28587 4.08562 0.28587 4.28588 0.486139L4.98645 1.1867ZM11.9101 8.10968L11.0505 7.25008L7.24656 11.0504L9.71517 13.519C9.91545 13.7193 10.2402 13.7193 10.4405 13.519L13.5174 10.4421C13.7176 10.2419 13.7176 9.91719 13.5174 9.71693L12.8167 9.01622L11.8129 10.02C11.5625 10.2703 11.1567 10.2703 10.9063 10.02C10.6559 9.76964 10.6559 9.36377 10.9063 9.11344L11.9101 8.10968ZM10.6981 0.806912C11.0981 0.409053 11.7444 0.408969 12.1445 0.806721L13.1895 1.84555C13.5921 2.24587 13.593 2.89717 13.1913 3.2985L3.64133 12.8396C3.57898 12.9018 3.50164 12.947 3.41675 12.9707L0.98224 13.6502C0.805496 13.6996 0.615861 13.6507 0.484968 13.5221C0.354074 13.3935 0.301872 13.2048 0.34808 13.0271L0.988875 10.5645C1.01197 10.4757 1.05848 10.3948 1.12353 10.33L10.6981 0.806912Z"
fill={fill}
/>
</svg>
);
export default PropertiesStyles;

View file

@ -12,7 +12,7 @@ const Remove03 = ({ width = '14', fill = '#6A727C', className = '', viewBox = '0
<path <path
fill-rule="evenodd" fill-rule="evenodd"
clip-rule="evenodd" clip-rule="evenodd"
d="M12.5487 2.82638C12.9283 2.44677 12.9283 1.83131 12.5487 1.45169C12.1691 1.07209 11.5536 1.07209 11.1741 1.45169L7.0001 5.62566L2.82614 1.45169C2.44652 1.07209 1.83106 1.07209 1.45145 1.45169C1.07185 1.83131 1.07185 2.44677 1.45145 2.82638L5.62542 7.00035L1.45145 11.1743C1.07185 11.5539 1.07185 12.1694 1.45145 12.549C1.83106 12.9286 2.44652 12.9286 2.82614 12.549L7.0001 8.37502L11.1741 12.549C11.5536 12.9286 12.1691 12.9286 12.5487 12.549C12.9283 12.1694 12.9283 11.5539 12.5487 11.1743L8.37478 7.00035L12.5487 2.82638Z" d="M14.3412 3.22848C14.775 2.79463 14.775 2.09125 14.3412 1.65741C13.9074 1.22357 13.204 1.22357 12.7702 1.65741L7.99994 6.42765L3.2297 1.65741C2.79585 1.22357 2.09247 1.22357 1.65863 1.65741C1.22479 2.09125 1.22479 2.79463 1.65863 3.22848L6.42887 7.99872L1.65863 12.769C1.22479 13.2028 1.22479 13.9062 1.65863 14.34C2.09247 14.7738 2.79585 14.7738 3.2297 14.34L7.99994 9.56978L12.7702 14.34C13.204 14.7738 13.9074 14.7738 14.3412 14.34C14.775 13.9062 14.775 13.2028 14.3412 12.769L9.571 7.99872L14.3412 3.22848Z"
fill={fill} fill={fill}
/> />
</svg> </svg>

View file

@ -24,6 +24,8 @@ import CheveronLeft from './CheveronLeft.jsx';
import CheveronRight from './CheveronRight.jsx'; import CheveronRight from './CheveronRight.jsx';
import CheveronUp from './CheveronUp.jsx'; import CheveronUp from './CheveronUp.jsx';
import ClearRectangle from './ClearRectangle.jsx'; import ClearRectangle from './ClearRectangle.jsx';
import CaretDown from './CaretDown.jsx';
import CaretUp from './CaretUp.jsx';
import Clock from './Clock.jsx'; import Clock from './Clock.jsx';
import CursorClick from './CursorClick.jsx'; import CursorClick from './CursorClick.jsx';
import LockGradient from './LockGradient.jsx'; import LockGradient from './LockGradient.jsx';
@ -261,6 +263,7 @@ import Delete01 from './Delete01.jsx';
import SourceControl from './SourceControl.jsx'; import SourceControl from './SourceControl.jsx';
import Push from './PushIcon.jsx'; import Push from './PushIcon.jsx';
import Pull from './PullIcon.jsx'; import Pull from './PullIcon.jsx';
import PropertiesStyles from './PropertiesStyles.jsx';
import RemoveFolder from './RemoveFolder.jsx'; import RemoveFolder from './RemoveFolder.jsx';
const Icon = (props) => { const Icon = (props) => {
@ -335,6 +338,10 @@ const Icon = (props) => {
return <Debugger {...props} />; return <Debugger {...props} />;
case 'calender': case 'calender':
return <Calender {...props} />; return <Calender {...props} />;
case 'caretdown':
return <CaretDown {...props} />;
case 'caretup':
return <CaretUp {...props} />;
case 'checkrectangle': case 'checkrectangle':
return <CheckRectangle {...props} />; return <CheckRectangle {...props} />;
case 'cheverondown': case 'cheverondown':
@ -535,6 +542,8 @@ const Icon = (props) => {
return <Pin {...props} />; return <Pin {...props} />;
case 'unpin01': case 'unpin01':
return <Unpin01 {...props} />; return <Unpin01 {...props} />;
case 'propertiesstyles':
return <PropertiesStyles {...props} />;
case 'unpin': case 'unpin':
return <Unpin {...props} />; return <Unpin {...props} />;
case 'play': case 'play':

View file

@ -0,0 +1,49 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class MoveHideHeaderAndAddPositionToPageSettings1751283157638 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
const appVersions = await queryRunner.manager.query(`
SELECT id, page_settings, global_settings FROM app_versions
`);
for (const version of appVersions) {
let pageSettings = version.page_settings;
let globalSettings = version.global_settings;
if (typeof pageSettings === 'string') {
pageSettings = JSON.parse(pageSettings);
}
if (typeof globalSettings === 'string') {
globalSettings = JSON.parse(globalSettings);
}
if (!pageSettings) {
pageSettings = { properties: {} };
}
if (!pageSettings.properties) {
pageSettings.properties = {};
}
if (!('position' in pageSettings.properties)) {
pageSettings.properties.position = 'side';
}
if (globalSettings && 'hideHeader' in globalSettings) {
pageSettings.properties.hideHeader = globalSettings.hideHeader;
pageSettings.properties.hideLogo = globalSettings.hideHeader;
}
await queryRunner.manager.query(
`UPDATE app_versions SET page_settings = $1, global_settings = $2 WHERE id = $3`,
[JSON.stringify(pageSettings), JSON.stringify(globalSettings), version.id]
);
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
}
}

@ -1 +1 @@
Subproject commit 8c0e6dec37f1b0bb7fb5552d8eef4db3ddc18b31 Subproject commit dac59b75498c7b7cf4356432c24991eabd13b568

View file

@ -92,14 +92,14 @@ export class PageService implements IPageService {
await this.clonePageEventsAndComponents(pageId, clonedpage.id, manager); await this.clonePageEventsAndComponents(pageId, clonedpage.id, manager);
const pages = await this.findPagesForVersion(appVersionId, organizationId, manager); const pages = await this.findPagesForVersion(appVersionId, organizationId, '', manager);
const events = await this.eventHandlerService.findEventsForVersion(appVersionId, manager); const events = await this.eventHandlerService.findEventsForVersion(appVersionId, manager);
return { pages, events }; return { pages, events };
}, appVersionId); }, appVersionId);
} }
async cloneGroup(groupPageId: string, appVersionId: string) { async cloneGroup(groupPageId: string, appVersionId: string, organizationId) {
return dbTransactionForAppVersionAssociationsUpdate(async (manager) => { return dbTransactionForAppVersionAssociationsUpdate(async (manager) => {
const groupToClone = await manager.findOne(Page, { const groupToClone = await manager.findOne(Page, {
where: { id: groupPageId, appVersionId, isPageGroup: true }, where: { id: groupPageId, appVersionId, isPageGroup: true },
@ -185,7 +185,7 @@ export class PageService implements IPageService {
await this.clonePageEventsAndComponents(child.id, newChildPage.id, manager); await this.clonePageEventsAndComponents(child.id, newChildPage.id, manager);
} }
const pages = await this.findPagesForVersion(appVersionId, '', manager); const pages = await this.findPagesForVersion(appVersionId,organizationId, '', manager);
const events = await this.eventHandlerService.findEventsForVersion(appVersionId, manager); const events = await this.eventHandlerService.findEventsForVersion(appVersionId, manager);
return { pages, events }; return { pages, events };