mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-21 21:47:17 +00:00
* Add routes for multi-page apps * Modify Editor, Viewer and Inspector to accept new app structure * Show a page selector on left side bar * Align component deletion logic with new app schema * Make subcontainer work with multi-page apps * Load components state properly in viewer * Use UUID instead of handle for pages * Display sidebar on viewer to switch pages * Add proper URL suffixing for pages in viewer * Add action to switch page * Revert translation file back to its pre-existing linting * Fix bug that caused modal to not open/close * Add support for query params in page switch * Fix the issue that caused navigation to fail while accessed via slug * Add missing SwitchPage file * Add support for page level variables * Add migration to convert existing apps to new schema * Add rollback for converting multi-page definitions back to single-page * Fix migration for multi-page apps * Adapt import/export service for multi-pages * [improvements] Multi-page applications (#4755) * UI updates for page selector popup card * delete page * delete page check: if only one page exits * switch to home page if the selected page is removed * adds and switch to new page * updating page name * updates to home page and starting page * handle updating the home page when home page is deleted * search box for filtering pages and minor style updates for the page handler card * header search box style fixes * for creating a new page, page handle needs to be unique * seperating into smaller components * updated pinned icon for page selector styles and settinf styles * Leftsidebar header ui component * handle dark theme * page handle ui and dark theme fixes for page menu * page handler edit modal * pinned state and update pinned state for menu options triggered * dark theme fixes for edit modal * handle on update should not be empty or prev * page handler updater * added loading state for saving * handles cancels * fixes slug ui * fixes crash for older app versions * updates the query params when handle gets an update * update homePage to homePageId * removes console.log * go back to the popover for modal close * fixes: Difficult to select page * fixes: Difficult to select the three-dot menu * fixes: on visiting the root url, navigate to homepage on viewer * adds tooltip for url * updates the page selector sidebar with sync with query manager * refactor and cleanup * refactor and cleanup * Compute component state when page is switched * modal should not close on click outside * disable save button if there is not change in the page handle input * should show/hide page menu when hovered * page icon * updates delete icon for disabled state * query manager should always be on top of page selector * checks if homePage key exists in pages def * updates page handler menu * updates the clear icon * page handler menu position * page handler menu position * handle icon * alert msg * global settings handler for updating viewer page navigation * show/hode page navigation for viewer * info text for toggle * Multipages:with sortable list [DnD] (#4783) * applied sortable list * on sort updates the definitions * fixies: app crash for dnd * viwer: canvas width should be 100% when navigation drawer is disbaled * fixes: homepage/startpage reload * clean up Co-authored-by: Sherfin Shamsudeen <sherfin94@gmail.com> * Multipage UI viewer (#4801) * new ui changes for viewer pages * fixes postions for debugger and datasources popover * removes console.log * Multipage : hide page and unhide page feature (#4803) * adds: ability to hide pages * hides pages in viewer * unhide page * hide icon * allow accessing hidden pages from url * add: duplicate page (#4802) * add: duplicate page * do not copy the same references from the original page * page name and page handler should be unique for duplicate pages too * Add support for on-page-load events * Add icon from page settings menu item * Convert existing templates to multi-page schema * error logs for page level and app level errors (#4842) * Adapt comments feature for multi-pages * [Bugfix] multipage - page menu interactions (#4844) * fixes: menu popup interaction * fixes: on modal input focus, we switch the page * Adapt multi-player to multi-pages * Add editingPageId to ymap * Log self, others and editor props in real-time avatar generation * Save editing page id to appDef * Add editingPageId to presence in RealtimeCursors * adds no results ui for empty search results (#4869) * page icon updated (#4870) * fixes:Version switching crashes if the target version does not contain the current page (#4868) * Remove unnecessary setting of editingPageId on ymap * Remove unnecessary console.log * [Bugfix] Multipages: widget inspector event popover unmounts (#4887) * introduced a local state for events * cleaned up inspector.jsx * fixes: table widget inspector event accordion * Do not run switchPage twice when viewer is loaded * Preview should open the currently editing page * Properly place navigation and canvas in viewer * Update app definition whenever event manager changes are made * Add support for browser back and forward button in multi-pages * Rename handleBackButton to handlePageSwitchingBasedOnURLparam * Add support for cut/copy/paste and clone * Fix the crash caused by boxShadow * Add support for background colors in viewer in multi-pages * Run queries to be run on load on viewer, in multi-pages * Fix issue that caused inspector popovers to collapse * resolves workspace vars in viewer mode (#4892) * Multipage : Navigation for Mobile-ui (#4814) * refactored to components * burger menu for mobile ui * merge conflict fix for hidden pages * hamburger menu positioned in the header * viewer header reafctored * viewer mobile page manu styles * handles dark theme * mobile menu with dark mode toggle in the footer * components are moved to page level, handle for mobile layout * style fixes * removing unwanted code block * dark theme fixes * style fixes * fixes: events are sortable (#4895) * fixes: events are sortable * Remove uneccesarily repeated call of setEvents in EventManager Co-authored-by: Sherfin Shamsudeen <sherfin94@gmail.com> * renamed settings to Event handlers (#4898) * updates the page setting title to Page Events * temp commit * Add support for setting max width in percentage * fixes: paramUpdates for boxes: 🙌🏻 * [Bugfix] Multipage - viewer canvas dark theme (#4897) * fixes: darktheme bg for viewer canvas * reverts canvas size * Fix for inspector bouncing back to previous values * resolves pages variables in pythong and js transformation (#4905) * csa support to event manager for pages (#4907) * Add support for setting canvas width in percentages * Persist page level variables across page switches * latest definitions is merged with the current appdef (#4914) * latest definitions is merged with the current appdef * mutating the local obj * cleanup * iterate through pages for new versions are created Co-authored-by: Arpit <arpitnath42@gmail.com>
225 lines
9.4 KiB
JavaScript
225 lines
9.4 KiB
JavaScript
import React from 'react';
|
|
import usePopover from '@/_hooks/use-popover';
|
|
import { SketchPicker } from 'react-color';
|
|
import { Confirm } from '../Viewer/Confirm';
|
|
|
|
import { LeftSidebarItem } from './SidebarItem';
|
|
import FxButton from '../CodeBuilder/Elements/FxButton';
|
|
import { CodeHinter } from '../CodeBuilder/CodeHinter';
|
|
import { resolveReferences } from '@/_helpers/utils';
|
|
import { useTranslation } from 'react-i18next';
|
|
import _ from 'lodash';
|
|
|
|
export const LeftSidebarGlobalSettings = ({
|
|
globalSettings,
|
|
globalSettingsChanged,
|
|
darkMode,
|
|
toggleAppMaintenance,
|
|
is_maintenance_on,
|
|
currentState,
|
|
}) => {
|
|
const { t } = useTranslation();
|
|
const [open, trigger, content] = usePopover(false);
|
|
const { hideHeader, canvasMaxWidth, canvasMaxWidthType, canvasMaxHeight, canvasBackgroundColor, backgroundFxQuery } =
|
|
globalSettings;
|
|
const [showPicker, setShowPicker] = React.useState(false);
|
|
const [forceCodeBox, setForceCodeBox] = React.useState(true);
|
|
const [realState, setRealState] = React.useState(currentState);
|
|
const [showConfirmation, setConfirmationShow] = React.useState(false);
|
|
const coverStyles = {
|
|
position: 'fixed',
|
|
top: '0px',
|
|
right: '0px',
|
|
bottom: '0px',
|
|
left: '0px',
|
|
};
|
|
|
|
React.useEffect(() => {
|
|
setRealState(currentState);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [currentState.components]);
|
|
|
|
React.useEffect(() => {
|
|
backgroundFxQuery &&
|
|
globalSettingsChanged('canvasBackgroundColor', resolveReferences(backgroundFxQuery, realState));
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [JSON.stringify(resolveReferences(backgroundFxQuery, realState))]);
|
|
|
|
return (
|
|
<>
|
|
<Confirm
|
|
show={showConfirmation}
|
|
message={
|
|
is_maintenance_on
|
|
? 'Users will now be able to launch the released version of this app, do you wish to continue?'
|
|
: 'Users will not be able to launch the app until maintenance mode is turned off, do you wish to continue?'
|
|
}
|
|
onConfirm={() => toggleAppMaintenance()}
|
|
onCancel={() => setConfirmationShow(false)}
|
|
darkMode={darkMode}
|
|
/>
|
|
<LeftSidebarItem
|
|
tip="Global settings"
|
|
{...trigger}
|
|
icon="settings"
|
|
className={`left-sidebar-item left-sidebar-layout ${open && 'active'}`}
|
|
text={'Settings'}
|
|
/>
|
|
<div {...content} className={`card popover global-settings-popover ${open ? 'show' : 'hide'}`}>
|
|
<div style={{ marginTop: '1rem' }} className="card-body">
|
|
<div>
|
|
<div className="d-flex mb-3">
|
|
<span>{t('leftSidebar.Settings.hideHeader', 'Hide header for launched apps')}</span>
|
|
<div className="ms-auto form-check form-switch position-relative">
|
|
<input
|
|
className="form-check-input"
|
|
type="checkbox"
|
|
checked={hideHeader}
|
|
onChange={(e) => globalSettingsChanged('hideHeader', e.target.checked)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="d-flex mb-3">
|
|
<span>{t('leftSidebar.Settings.maintenanceMode', 'Maintenance mode')}</span>
|
|
<div className="ms-auto form-check form-switch position-relative">
|
|
<input
|
|
className="form-check-input"
|
|
type="checkbox"
|
|
checked={is_maintenance_on}
|
|
onChange={() => setConfirmationShow(true)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="d-flex mb-3">
|
|
<span className="w-full m-auto">{t('leftSidebar.Settings.maxWidthOfCanvas', 'Max width of canvas')}</span>
|
|
<div className="position-relative">
|
|
<div className="input-with-icon">
|
|
<input
|
|
data-cy="maximum-canvas-width-input-field"
|
|
type="text"
|
|
className={`form-control form-control-sm`}
|
|
placeholder={'0'}
|
|
onChange={(e) => {
|
|
const width = e.target.value;
|
|
if (!Number.isNaN(width) && width >= 0) globalSettingsChanged('canvasMaxWidth', width);
|
|
}}
|
|
value={canvasMaxWidth}
|
|
/>
|
|
<select
|
|
className="form-select"
|
|
aria-label="Select canvas width type"
|
|
onChange={(event) => {
|
|
const newCanvasMaxWidthType = event.currentTarget.value;
|
|
globalSettingsChanged('canvasMaxWidthType', newCanvasMaxWidthType);
|
|
if (newCanvasMaxWidthType === '%') {
|
|
globalSettingsChanged('canvasMaxWidth', 100);
|
|
} else if (newCanvasMaxWidthType === 'px') {
|
|
globalSettingsChanged('canvasMaxWidth', 1292);
|
|
}
|
|
}}
|
|
>
|
|
<option value="%" selected={canvasMaxWidthType === '%'}>
|
|
%
|
|
</option>
|
|
<option value="px" selected={canvasMaxWidthType === 'px' || _.isUndefined(canvasMaxWidthType)}>
|
|
px
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="d-flex mb-3">
|
|
<span className="w-full m-auto">
|
|
{t('leftSidebar.Settings.maxHeightOfCanvas', 'Max height of canvas')}
|
|
</span>
|
|
<div className="position-relative">
|
|
<div className="input-with-icon">
|
|
<input
|
|
data-cy="maximum-canvas-height-input-field"
|
|
type="text"
|
|
className={`form-control form-control-sm maximum-canvas-height-input-field`}
|
|
placeholder={'0'}
|
|
onChange={(e) => {
|
|
const height = e.target.value;
|
|
if (!Number.isNaN(height) && height <= 2400) globalSettingsChanged('canvasMaxHeight', height);
|
|
}}
|
|
value={canvasMaxHeight}
|
|
/>
|
|
<span className="input-group-text">px</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="d-flex">
|
|
<span className="w-full">
|
|
{t('leftSidebar.Settings.backgroundColorOfCanvas', 'Background color of canvas')}
|
|
</span>
|
|
<div className="canvas-codehinter-container">
|
|
{showPicker && (
|
|
<div>
|
|
<div style={coverStyles} onClick={() => setShowPicker(false)} />
|
|
<SketchPicker
|
|
className="canvas-background-picker"
|
|
onFocus={() => setShowPicker(true)}
|
|
color={canvasBackgroundColor}
|
|
onChangeComplete={(color) => {
|
|
globalSettingsChanged('canvasBackgroundColor', [color.hex, color.rgb]);
|
|
globalSettingsChanged('backgroundFxQuery', color.hex);
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
{forceCodeBox && (
|
|
<div
|
|
className="row mx-0 form-control form-control-sm canvas-background-holder"
|
|
onClick={() => setShowPicker(true)}
|
|
>
|
|
<div
|
|
className="col-auto"
|
|
style={{
|
|
float: 'right',
|
|
width: '20px',
|
|
height: '20px',
|
|
backgroundColor: canvasBackgroundColor,
|
|
border: `0.25px solid ${
|
|
['#ffffff', '#fff', '#1f2936'].includes(canvasBackgroundColor) && '#c5c8c9'
|
|
}`,
|
|
}}
|
|
></div>
|
|
<div className="col">{canvasBackgroundColor}</div>
|
|
</div>
|
|
)}
|
|
<div
|
|
className={`${!forceCodeBox && 'hinter-canvas-input'} ${!darkMode && 'hinter-canvas-input-light'} `}
|
|
>
|
|
{!forceCodeBox && (
|
|
<CodeHinter
|
|
currentState={realState}
|
|
initialValue={backgroundFxQuery ? backgroundFxQuery : canvasBackgroundColor}
|
|
value={backgroundFxQuery ? backgroundFxQuery : canvasBackgroundColor}
|
|
theme={darkMode ? 'monokai' : 'duotone-light'}
|
|
mode="javascript"
|
|
className="canvas-hinter-wrap"
|
|
lineNumbers={false}
|
|
onChange={(color) => {
|
|
globalSettingsChanged('canvasBackgroundColor', resolveReferences(color, realState));
|
|
globalSettingsChanged('backgroundFxQuery', color);
|
|
}}
|
|
/>
|
|
)}
|
|
<div className={`fx-canvas ${!darkMode && 'fx-canvas-light'} `}>
|
|
<FxButton
|
|
active={!forceCodeBox ? true : false}
|
|
onPress={() => {
|
|
setForceCodeBox(!forceCodeBox);
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|