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 { shallow } from 'zustand/shallow';
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 FreezeVersionInfo from '@/AppBuilder/Header/FreezeVersionInfo';
import { computeCanvasContainerHeight } from '../_helpers/editorHelpers';
@ -134,7 +140,7 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
width: currentMode === 'edit' ? `calc(100% - 96px)` : '100%',
alignItems: 'unset',
justifyContent: 'unset',
borderRight: currentMode === 'edit' && isRightSidebarOpen && '299' + 'px solid',
borderRight: currentMode === 'edit' && isRightSidebarOpen && `300px solid ${canvasBgColor}`,
padding: currentMode === 'edit' && '8px',
paddingBottom: currentMode === 'edit' && '2px',
};
@ -152,15 +158,34 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
const shouldAdjust = isSidebarOpen || (isRightSidebarOpen && currentMode === 'edit');
if (!shouldAdjust) return '';
let offset;
if (isViewerSidebarPinned) {
offset = position === 'side' ? '352px' : '126px';
if (isViewerSidebarPinned && !isPagesSidebarHidden) {
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 {
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 (
@ -177,7 +202,7 @@ export const AppCanvas = ({ appId, isViewer = false, switchDarkMode, darkMode })
'canvas-container d-flex page-container',
{ 'dark-theme theme-dark': isAppDarkMode, close: !isViewerSidebarPinned },
{ '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
)}
style={canvasContainerStyles}

View file

@ -26,6 +26,7 @@
.empty-box-cont{
display: flex;
justify-content: center;
margin: unset !important;
.dotted-cont{
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 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'];

View file

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

View file

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

View file

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

View file

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

View file

@ -74,8 +74,10 @@ export const ComponentsManagerTab = ({ darkMode, isModuleEditor }) => {
}
}, [hasModuleAccess, activeTab]);
const toggleRightSidebarPin = useStore((state) => state.toggleRightSidebarPin);
const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned);
const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen);
const activeRightSideBarTab = useStore((state) => state.activeRightSideBarTab);
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab);
const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen);
const handleSearchQueryChange = useCallback(
debounce((value) => {
@ -88,6 +90,11 @@ export const ComponentsManagerTab = ({ darkMode, isModuleEditor }) => {
[activeTab]
);
const handleToggle = () => {
setActiveRightSideBarTab(null);
setRightSidebarOpen(false);
};
const filterComponents = useCallback((value) => {
if (value !== '') {
const fuse = new Fuse(componentList, {
@ -223,11 +230,16 @@ export const ComponentsManagerTab = ({ darkMode, isModuleEditor }) => {
return (
<div className={`components-container ${shouldFreeze ? 'disabled' : ''}`}>
{isModuleEditor ? (
<p className="widgets-manager-header">Components</p>
) : (
<ComponentModuleTab onChangeTab={handleChangeTab} hasModuleAccess={hasModuleAccess} />
)}
<div className="d-flex align-items-center">
{isModuleEditor ? (
<p className="widgets-manager-header">Components</p>
) : (
<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">
<SearchBox
dataCy={`widget-search-box`}

View file

@ -118,7 +118,13 @@ const NEW_REVAMPED_COMPONENTS = [
'FilePicker',
];
export const Inspector = ({ componentDefinitionChanged, darkMode, pages, selectedComponentId }) => {
export const Inspector = ({
componentDefinitionChanged,
darkMode,
pages,
selectedComponentId,
handleRightSidebarToggle,
}) => {
const allComponents = useStore((state) => state.getCurrentPageComponents());
const setComponentProperty = useStore((state) => state.setComponentProperty, 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>
<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
data-cy={`inspector-close-icon`}
className="cursor-pointer d-flex align-items-center "
style={{ height: '28px', width: '28px' }}
style={{ height: '28px' }}
>
<ArrowLeft fill={'var(--slate12)'} width={'14'} />
</span>
</div>
<div className={`col-9 p-0 ${shouldFreeze && 'disabled'}`}>{renderAppNameInput()}</div>
<div className={`flex-shrink p-0 width-unset ${shouldFreeze && 'disabled'}`}>{renderAppNameInput()}</div>
{!isModuleContainer && (
<>
<div className="col-2" data-cy={'component-inspector-options'}>
<div className="width-unset" data-cy={'component-inspector-options'}>
<OverlayTrigger
trigger={'click'}
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 className={`${shouldFreeze && 'disabled'}`}>{renderTabs()}</div>

View file

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

View file

@ -338,22 +338,10 @@ export const AddEditPagePopup = forwardRef(({ darkMode, ...props }, ref) => {
type="checkbox"
checked={isHomePage}
onChange={() => markAsHomePage(page?.id)}
disabled={isHomePage}
disabled={isHomePage || resolveReferences(page?.hidden?.value) || page?.disabled}
/>
</label>
</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
hidden={page?.hidden}
page={page}

View file

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

View file

@ -1,5 +1,5 @@
/* eslint-disable import/namespace */
import React, { useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import _ from 'lodash';
import * as Icons from '@tabler/icons-react';
// eslint-disable-next-line import/no-unresolved
@ -26,12 +26,13 @@ export const RenderPage = ({
isSidebarPinned,
callback,
position,
onPageClick,
}) => {
const currentMode = useStore((state) => state.currentMode);
const isHomePage = page.id === homePageId;
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
const IconElement = (props) => {
const Icon = Icons?.[iconName] ?? Icons?.['IconFileDescription'];
const Icon = Icons?.[iconName] ?? Icons?.['IconFile'];
if (!isSidebarPinned || labelStyle?.label?.hidden) {
return (
@ -59,7 +60,7 @@ export const RenderPage = ({
key={page.handle}
onClick={() => {
switchPageWrapper(page);
callback && position !== 'side' && callback();
position !== 'side' && onPageClick();
}}
selectedItem={page?.id === currentPageId}
CustomIcon={!labelStyle?.icon?.hidden && IconElement}
@ -67,14 +68,15 @@ export const RenderPage = ({
darkMode={darkMode}
>
{!labelStyle?.label?.hidden && (
<span
<div
style={{ position: 'relative', overflow: 'hidden' }}
// className={isSelected && 'tj-list-item-selected'}
data-cy={`pages-name-${String(page?.name).toLowerCase()}`}
>
<OverflowTooltip style={{ width: '110px', position: 'relative' }} childrenClassName={'page-name'}>
{page.name}
</OverflowTooltip>
</span>
</div>
)}
</FolderList>
</div>
@ -94,12 +96,15 @@ const RenderPageGroup = ({
linkRefs,
isSidebarPinned,
position,
isExpanded,
onToggle,
onPageClick,
}) => {
const currentMode = useStore((state) => state.currentMode);
const [hovered, setHovered] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
const contentRef = useRef(null);
const groupItemRootRef = useRef(null);
const IconElement = (props) => {
const Icon = Icons?.[pageGroup.icon] ?? Icons?.['IconHome2'];
@ -116,9 +121,25 @@ const RenderPageGroup = ({
};
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) {
return (
<>
@ -133,6 +154,8 @@ const RenderPageGroup = ({
darkMode={darkMode}
homePageId={homePageId}
position={position}
callback={handleToggle}
onPageClick={onPageClick}
/>
))}
</>
@ -145,7 +168,12 @@ const RenderPageGroup = ({
<div
key={pageGroup.name}
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' : ''}`}
>
<div
@ -153,40 +181,31 @@ const RenderPageGroup = ({
style={{
position: 'relative',
}}
onClick={isSidebarPinned && handleToggle}
>
<FolderList
key={pageGroup.id}
onClick={isSidebarPinned && handleToggle}
CustomIcon={!labelStyle?.icon?.hidden && IconElement}
customStyles={computeStyles}
darkMode={darkMode}
hovered={hovered}
>
{!labelStyle?.label?.hidden && (
<span data-cy={`pages-name-${String(pageGroup?.name).toLowerCase()}`}>
<OverflowTooltip style={{ width: '110px' }} childrenClassName={'page-name'}>
{pageGroup.name}
</OverflowTooltip>
</span>
<div
style={{ position: 'relative', overflow: 'hidden' }}
data-cy={`pages-name-${String(pageGroup?.name).toLowerCase()}`}
>
<OverflowTooltip childrenClassName={'page-name'}>{pageGroup.name}</OverflowTooltip>
</div>
)}
</FolderList>
<div style={{ marginRight: '12px' }}>
<svg
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
onClick={handleToggle}
className={`page-group-collapse ${isExpanded ? 'expanded' : 'collapsed'}`}
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 className="icon-btn cursor-pointer flex-shrink-0">
<SolidIcon
fill="var(--icon-default)"
name={isExpanded ? 'caretup' : 'caretdown'}
width="16"
viewBox="0 0 16 16"
/>
</div>
</div>
@ -205,6 +224,7 @@ const RenderPageGroup = ({
linkRefs={linkRefs}
callback={handleToggle}
position={position}
onPageClick={onPageClick}
/>
))}
</div>
@ -228,6 +248,7 @@ export const RenderPageAndPageGroup = ({
isSidebarPinned,
}) => {
const { moduleId } = useModuleContext();
const [expandedPageGroupId, setExpandedPageGroupId] = useState(null);
// Don't render empty folders if displaying only icons
const visibleTree = buildTree(position === 'top' ? visibleLinks : pages, !!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 homePageId = useStore((state) => state.appStore.modules[moduleId].app.homePageId);
const [showPopover, setShowPopover] = useState(false);
const handleAccordionToggle = (groupId) => {
setExpandedPageGroupId((prevId) => (prevId === groupId ? null : groupId));
};
const closeAllAccordions = () => {
if (showPopover) {
setShowPopover(false);
}
setExpandedPageGroupId(null);
};
return (
<div className={cx('page-handler-wrapper viewer', { 'dark-theme': darkMode })}>
{/* <Accordion alwaysOpen defaultActiveKey={tree.map((page) => page.id)}> */}
@ -273,6 +305,9 @@ export const RenderPageAndPageGroup = ({
linkRefs={linkRefs}
isSidebarPinned={isSidebarPinned}
position={position}
isExpanded={expandedPageGroupId === page.id}
onToggle={handleAccordionToggle}
onPageClick={closeAllAccordions}
/>
</>
);
@ -290,6 +325,7 @@ export const RenderPageAndPageGroup = ({
linkRefs={linkRefs}
isSidebarPinned={isSidebarPinned}
position={position}
onPageClick={closeAllAccordions}
/>
);
}
@ -299,10 +335,11 @@ export const RenderPageAndPageGroup = ({
<button
ref={moreBtnRef}
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' }}
>
<SolidIcon fill={'var(--icon-weak)'} viewBox="0 3 21 18" width="16px" name="morevertical" />
<div style={{ marginLeft: '6px' }}>More</div>
</button>
@ -344,6 +381,9 @@ export const RenderPageAndPageGroup = ({
darkMode={darkMode}
linkRefs={linkRefs}
isSidebarPinned={isSidebarPinned}
isExpanded={expandedPageGroupId === page.id}
onToggle={handleAccordionToggle}
onPageClick={closeAllAccordions}
/>
</>
);
@ -360,6 +400,7 @@ export const RenderPageAndPageGroup = ({
homePageId={homePageId}
linkRefs={linkRefs}
isSidebarPinned={isSidebarPinned}
onPageClick={closeAllAccordions}
/>
);
}

View file

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

View file

@ -72,7 +72,7 @@ export const PageMenuItem = withRouter(
const icon = (props) => {
const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon;
// eslint-disable-next-line import/namespace
const Icon = Icons?.[iconName] ?? Icons?.['IconFileDescription'];
const Icon = Icons?.[iconName] ?? Icons?.['IconFile'];
return (
<Icon {...props} style={{ width: '16px', height: '16px', color: 'var(--icons-default)', marginRight: '6px' }} />
@ -269,7 +269,6 @@ export const PageMenuItem = withRouter(
return '';
}
return (
<div
onMouseEnter={() => setIsHovered(true)}
@ -281,7 +280,7 @@ export const PageMenuItem = withRouter(
<>
<div
className={`page-menu-item ${darkMode && 'dark-theme'} ${
showPageOptions && isEditingPage ? 'is-selected' : ''
showPageOptions || showEditingPopover || isEditingPage ? 'is-selected' : ''
}`}
style={{
position: 'relative',
@ -308,14 +307,14 @@ export const PageMenuItem = withRouter(
<>
{' '}
<div ref={optionBtnRef} className="left" data-cy={`pages-name-${page.name.toLowerCase()}`}>
{icon()}
<OverflowTooltip childrenClassName="page-name" style={{ ...computedStyles?.text, maxWidth: '159px' }}>
<div className="main-page-icon-wrapper">{icon()}</div>
<OverflowTooltip childrenClassName="page-name" style={{ ...computedStyles?.text }}>
{page.name}
</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">
{PAGE_TYPES[page?.type] && ( // If 'page' object has a 'type' property like 'URL'
<span className="page-type-text">{PAGE_TYPES[page?.type]}</span>
)}
{isHomePage && (
<ToolTip message="Home page" placement="bottom">
<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 editingPage = useStore((state) => state.editingPage);
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 selectedUsers = useStore((state) => state.selectedUsers);
const selectedUsers = useStore((state) => state.appPermission.selectedUsers);
const setSelectedUsers = useStore((state) => state.setSelectedUsers);
const pagePermission = useStore((state) => state.pagePermission);
const setPagePermission = useStore((state) => state.setPagePermission);
@ -353,7 +353,7 @@ export default function PagePermission({ darkMode }) {
const UserGroupSelect = () => {
const { moduleId } = useModuleContext();
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 [userGroups, setUserGroups] = useState([]);
useEffect(() => {
@ -415,7 +415,7 @@ const UserSelect = () => {
const { moduleId } = useModuleContext();
const appId = useStore((state) => state.appStore.modules[moduleId].app.appId);
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 [users, setUsers] = useState([]);
useEffect(() => {

View file

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

View file

@ -60,6 +60,7 @@
align-items: center;
padding: 7px 8px;
margin-top: 2px;
width: 100%;
justify-content: space-between;
background-color: var(--interactive-default);
&.highlight {
@ -112,6 +113,8 @@
line-height: 18px;
margin-left: 8px;
gap: 6px;
flex-shrink: 0;
margin-right: 6px;
}
button.edit-page-overlay-toggle{
opacity: 0;
@ -134,13 +137,23 @@
// margin-left: 15px;
display: flex;
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{
overflow: hidden;
color: var(--slate12);
font-size: 14px;
font-style: normal;
font-weight: 400;
max-width: 246px;
flex-shrink: 1;
min-width: 0;
}
}
@ -410,8 +423,9 @@
}
.licensed-page-option {
pointer-events: unset !important;
button {
pointer-events: none;
cursor: default;
&:hover {
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-empty-events {
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 useStore from '@/AppBuilder/_stores/store';
import ArrowLeft from '@/_ui/Icon/solidIcons/ArrowLeft';
@ -45,7 +45,12 @@ export const PageSettings = () => {
const isVersionReleased = useStore((state) => state.isVersionReleased);
const switchPage = useStore((state) => state.switchPage);
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 license = useStore((state) => state.license);
@ -161,10 +166,12 @@ export const PageSettings = () => {
<div className="inspector pages-settings">
<div>
<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="icon-btn cursor-pointer" onClick={() => toggleRightSidebarPin()}>
<SolidIcon fill="var(--icon-strong)" name={isRightSidebarPinned ? 'unpin' : 'pin'} width="16" />
<div className="icon-btn cursor-pointer flex-shrink-0 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>
@ -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 [appName] = useStore((state) => [state.appStore.modules[moduleId].app.appName], shallow);
const { definition: { properties = {} } = {} } = pageSettings ?? {};
const { hideHeader, name, hideLogo } = properties ?? {};
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 (
<>
@ -279,14 +324,17 @@ const AppHeaderMenu = ({ darkMode, pageSettings, pageSettingChanged, licenseVali
<label className="form-label font-weight-400 mb-0">Title</label>
<input
type="text"
onBlur={(e) => {
pageSettingChanged({ name: e.target.value }, 'properties');
}}
onChange={(e) => _setName(e.target.value)}
className="form-control"
onBlur={handleNameBlur}
onChange={handleNameChange}
className={`form-control ${error ? 'is-invalid' : ''}`}
value={_name}
minLength="1"
maxLength={32}
/>
{error && (
<div className="invalid-feedback" style={{ display: 'block' }}>
{error}
</div>
)}
</div>
</div>
</>
@ -318,6 +366,8 @@ const NavigationMenu = ({ darkMode, pageSettings, pageSettingChanged }) => {
return str.toLowerCase() === 'true';
}
const [selectedStyle, setSelectedStyle] = useState(style);
return (
<>
<div className="section-header pb-2">
@ -357,6 +407,7 @@ const NavigationMenu = ({ darkMode, pageSettings, pageSettingChanged }) => {
pageSettingChanged({ position: value }, 'properties');
}}
defaultValue={position?.toString()}
style={{ width: '168px' }}
>
{POSTIONS.map((mode) => (
<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>
<Select
options={styleOptions}
search={true}
value={style}
value={selectedStyle}
onChange={(value) => {
setSelectedStyle(value);
pageSettingChanged({ style: value }, 'properties');
}}
placeholder={'Select...'}
useMenuPortal={false}
width={'142px'}
width={'168px'}
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
/>
</div>
@ -389,12 +440,10 @@ const NavigationMenu = ({ darkMode, pageSettings, pageSettingChanged }) => {
<div className="ms-auto position-relative app-mode-switch" style={{ paddingLeft: '0px' }}>
<ToggleGroup
onValueChange={(value) => {
// if (position === 'side' && value == 'false') {
// pageSettingChanged({ style: 'texticon' }, 'properties');
// }
pageSettingChanged({ collapsable: stringToBoolean(value) }, 'properties');
}}
defaultValue={collapsable?.toString()}
style={{ width: '168px' }}
>
{COLLAPSABLE_TOGGLES.map((mode) => (
<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 { ComponentConfigurationTab } from './ComponentConfigurationTab';
import ComponentsManagerTab from './ComponentManagerTab';
@ -8,14 +8,29 @@ import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
export const RightSideBar = ({ darkMode }) => {
const { isModuleEditor } = useModuleContext();
const queryPanelHeight = useStore((state) => state.queryPanel.queryPanelHeight);
const isDraggingQueryPane = useStore((state) => state.queryPanel.isDraggingQueryPane);
const activeTab = useStore((state) => state.activeRightSideBarTab);
const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen);
const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen);
const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned);
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab);
const [popoverContentHeight, setPopoverContentHeight] = useState(queryPanelHeight);
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(() => {
// const rigthSidebarMenu = document.querySelector('.right-sidebar-toggle');
// function handleClickOutside(event) {
@ -40,7 +55,10 @@ export const RightSideBar = ({ darkMode }) => {
return (
<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%' }}>
{activeTab === 'pages' && <PageSettings />}
{activeTab === 'components' && <ComponentsManagerTab darkMode={darkMode} isModuleEditor={isModuleEditor} />}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -119,14 +119,7 @@ export const createPageMenuSlice = (set, get) => {
state.showSearch = show;
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) =>
set((state) => {
state.newPagePopupConfig = {
@ -134,14 +127,6 @@ export const createPageMenuSlice = (set, get) => {
...config,
};
}),
closePageEditPopover: () =>
set((state) => {
state.showEditingPopover = false;
state.showEditPageEventsModal = false;
state.showRenamePageHandleModal = false;
state.showEditPageNameInput = false;
state.showDeleteConfirmationModal = false;
}),
toggleEditPageHandleModal: (show) =>
set((state) => {
@ -481,5 +466,28 @@ export const createPageMenuSlice = (set, get) => {
set((state) => {
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 { id, handle } = e.state;
switchPage(id, handle, [], true);
switchPage(id, handle, []);
};
return <RouteLoader isLoading={isLoading}>{clonedElement}</RouteLoader>;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -662,11 +662,13 @@ button {
padding: 4px 12px;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid var(--border-weak);
.header {
color: var(--text-default);
font-weight: 500;
font-size: 12px;
padding: 7px 6px;
}
}
@ -823,8 +825,13 @@ button {
background: transparent !important;
}
.canvas-container {
scrollbar-width: none;
:hover {
.canvas-container {
scrollbar-width: thin;
&::-webkit-scrollbar-track {
background: transparent !important;
}
}
}
.canvas-container {
@ -839,6 +846,16 @@ button {
justify-content: center;
-webkit-box-align: 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 {
outline: 1px dotted transparent;
@ -5203,12 +5220,13 @@ input[type="text"] {
}
.inspector-component-title-input-holder {
padding: 2px 12px;
padding: 4px 12px;
margin: 0;
display: flex;
align-items: center;
height: 36px;
flex-direction: column;
justify-content: center;
.icon-btn {
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-pull.active {
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
fill-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}
/>
</svg>

View file

@ -24,6 +24,8 @@ import CheveronLeft from './CheveronLeft.jsx';
import CheveronRight from './CheveronRight.jsx';
import CheveronUp from './CheveronUp.jsx';
import ClearRectangle from './ClearRectangle.jsx';
import CaretDown from './CaretDown.jsx';
import CaretUp from './CaretUp.jsx';
import Clock from './Clock.jsx';
import CursorClick from './CursorClick.jsx';
import LockGradient from './LockGradient.jsx';
@ -261,6 +263,7 @@ import Delete01 from './Delete01.jsx';
import SourceControl from './SourceControl.jsx';
import Push from './PushIcon.jsx';
import Pull from './PullIcon.jsx';
import PropertiesStyles from './PropertiesStyles.jsx';
import RemoveFolder from './RemoveFolder.jsx';
const Icon = (props) => {
@ -335,6 +338,10 @@ const Icon = (props) => {
return <Debugger {...props} />;
case 'calender':
return <Calender {...props} />;
case 'caretdown':
return <CaretDown {...props} />;
case 'caretup':
return <CaretUp {...props} />;
case 'checkrectangle':
return <CheckRectangle {...props} />;
case 'cheverondown':
@ -535,6 +542,8 @@ const Icon = (props) => {
return <Pin {...props} />;
case 'unpin01':
return <Unpin01 {...props} />;
case 'propertiesstyles':
return <PropertiesStyles {...props} />;
case 'unpin':
return <Unpin {...props} />;
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);
const pages = await this.findPagesForVersion(appVersionId, organizationId, manager);
const pages = await this.findPagesForVersion(appVersionId, organizationId, '', manager);
const events = await this.eventHandlerService.findEventsForVersion(appVersionId, manager);
return { pages, events };
}, appVersionId);
}
async cloneGroup(groupPageId: string, appVersionId: string) {
async cloneGroup(groupPageId: string, appVersionId: string, organizationId) {
return dbTransactionForAppVersionAssociationsUpdate(async (manager) => {
const groupToClone = await manager.findOne(Page, {
where: { id: groupPageId, appVersionId, isPageGroup: true },
@ -185,7 +185,7 @@ export class PageService implements IPageService {
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);
return { pages, events };