This commit is contained in:
Vijaykant Yadav 2024-10-22 18:35:50 +05:30
commit a86c7addd0
108 changed files with 9894 additions and 1414 deletions

View file

@ -6,11 +6,11 @@ import LogoNavDropdown from '@/_components/LogoNavDropdown';
import HeaderActions from './HeaderActions';
import { AppVersionsManager } from './AppVersionsManager';
import RealtimeAvatars from '@/Editor/RealtimeAvatars';
// import UpdatePresence from '@/Editor/Header/UpdatePresence';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import useStore from '@/AppBuilder/_stores/store';
import RightTopHeaderButtons from './RightTopHeaderButtons/RightTopHeaderButtons';
import BuildSuggestions from './BuildSuggestions';
import UpdatePresenceMultiPlayer from './UpdatePresenceMultiPlayer';
export const EditorHeader = ({ darkMode }) => {
const { isSaving, saveError, isVersionReleased } = useStore(
@ -98,18 +98,14 @@ export const EditorHeader = ({ darkMode }) => {
<RealtimeAvatars />
</div>
)}
{/* {shouldEnableMultiplayer && <UpdatePresence />} */}
{shouldEnableMultiplayer && <UpdatePresenceMultiPlayer />}
</div>
</div>
<div className="navbar-seperator"></div>
{/* <div className="d-flex align-items-center p-0" style={{ marginRight: '12px' }}></div> */}
</div>
<div className="d-flex align-items-center p-0">
<div className="d-flex version-manager-container p-0 mx-2 align-items-center ">
{/* {editingVersion && ( */}
{/* <EnvironmentManager darkMode={darkMode} /> */}
{/* )} */}
{/* <div className="navbar-seperator"></div> */}
<div className="d-flex p-0 mx-2 align-items-center ">
<AppVersionsManager darkMode={darkMode} />
</div>
</div>

View file

@ -1,416 +0,0 @@
import React from 'react';
import { appService, appsService, authenticationService } from '@/_services';
import Modal from 'react-bootstrap/Modal';
import { toast } from 'react-hot-toast';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import _, { debounce } from 'lodash';
import { validateName } from '@/_helpers/utils';
import { withTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { getPrivateRoute, replaceEditorURL, getHostURL } from '@/_helpers/routes';
import { ToolTip } from '@/_components/ToolTip';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import cx from 'classnames';
import { TOOLTIP_MESSAGES } from '@/_helpers/constants';
import { useAppDataStore } from '@/_stores/appDataStore';
import { retrieveWhiteLabelText } from '@white-label/whiteLabelling';
import useStore from '@/AppBuilder/_stores/store';
class ManageAppUsersComponent extends React.Component {
constructor(props) {
super(props);
this.isUserAdmin = authenticationService.currentSessionValue?.admin;
this.whiteLabelText = retrieveWhiteLabelText();
this.state = {
showModal: false,
appId: null,
isSlugVerificationInProgress: false,
addingUser: false,
newUser: {},
newSlug: {
value: null,
error: '',
},
isSlugUpdated: false,
};
}
/*
Only will fail for existed apps before the app/workspace url revamp which has
special chars or spaces in their app slugs
*/
validateThePreExistingSlugs = () => {
const existedSlugErrors = validateName(this.props.slug, 'App slug', true, false, false, false);
this.setState({
newSlug: {
value: this.props.slug,
error: existedSlugErrors.errorMsg,
},
});
};
componentDidMount() {
const appId = this.props.appId;
this.setState({ appId });
}
hideModal = () => {
this.setState({
showModal: false,
newSlug: {
value: this.props.slug,
error: '',
},
isSlugVerificationInProgress: false,
isSlugUpdated: false,
});
};
addUser = () => {
this.setState({
addingUser: true,
});
const { organizationUserId, role } = this.state.newUser;
appService
.createAppUser(this.state.appId, organizationUserId, role)
.then(() => {
this.setState({ addingUser: false, newUser: {} });
toast.success('Added user successfully');
})
.catch(({ error }) => {
this.setState({ addingUser: false });
toast.error(error);
});
};
toggleAppVisibility = () => {
const newState = !this.props.isPublic;
this.setState({
ischangingVisibility: true,
});
useStore.getState().setIsPublic(newState);
// eslint-disable-next-line no-unused-vars
appsService
.setVisibility(this.state.appId, newState)
.then(() => {
this.setState({
ischangingVisibility: false,
});
if (newState) {
toast('Application is now public.');
} else {
toast('Application visibility set to private');
}
})
.catch((error) => {
this.setState({
ischangingVisibility: false,
});
toast.error(error);
});
};
delayedSlugChange = debounce((e) => {
this.handleInputChange(e.target.value, 'slug');
}, 500);
handleInputChange = (value, field) => {
this.setState({
newSlug: {
value: this.state.newSlug?.value,
error: '',
isSlugUpdated: false,
},
});
const error = validateName(value, `App ${field}`, true, false, !(field === 'slug'), !(field === 'slug'));
if (!_.isEmpty(value) && value !== this.props.slug && _.isEmpty(error.errorMsg)) {
this.setState({
isSlugVerificationInProgress: true,
});
appsService
.setSlug(this.state.appId, value)
.then(() => {
this.setState({
newSlug: {
value: value,
error: '',
},
isSlugVerificationInProgress: false,
isSlugUpdated: true,
});
replaceEditorURL(value, this.props.pageHandle);
useStore.getState().setSlug(value);
})
.catch(({ error }) => {
this.setState({
newSlug: {
value,
error,
},
isSlugVerificationInProgress: false,
isSlugUpdated: false,
});
});
} else {
this.setState({
newSlug: {
value,
error: error?.errorMsg,
},
isSlugVerificationInProgress: false,
isSlugUpdated: false,
});
}
};
render() {
const { appId, isSlugVerificationInProgress, newSlug, isSlugUpdated } = this.state;
const appLink = `${getHostURL()}/applications/`;
const shareableLink = appLink + (this.props.slug || appId);
const slugButtonClass = !_.isEmpty(newSlug.error) ? 'is-invalid' : 'is-valid';
const embeddableLink = `<iframe width="560" height="315" src="${appLink}${this.props.slug}" title="${this.whiteLabelText} app - ${this.props.slug}" frameborder="0" allowfullscreen></iframe>`;
const shouldShowShareModal = this.props.isVersionReleased
? this.props.multiEnvironmentEnabled
? this.props.currentEnvironment?.isDefault
? true
: false
: this.props.currentEnvironment?.priority === 1
: false;
const envTooltipFlag =
(!this.props.isVersionReleased && this.props.currentEnvironment?.isDefault) ||
(!this.props.multiEnvironmentEnabled && this.props.currentEnvironment?.priority === 1);
return (
<ToolTip
message={envTooltipFlag ? TOOLTIP_MESSAGES.SHARE_URL_UNAVAILABLE : 'You can only share apps in production'}
placement="left"
show={!shouldShowShareModal}
>
<div
title={shouldShowShareModal ? 'Share' : ''}
className="manage-app-users editor-header-icon tj-secondary-btn"
data-cy="share-button-link"
>
<span
className={cx('d-flex', {
'share-disabled': !shouldShowShareModal,
})}
onClick={() => {
this.validateThePreExistingSlugs();
shouldShowShareModal && this.setState({ showModal: true });
}}
>
<SolidIcon name="share" width="14" className="cursor-pointer" fill="#3E63DD" />
</span>
<Modal
show={this.state.showModal}
size="lg"
backdrop="static"
centered={true}
keyboard={true}
animation={false}
onEscapeKeyDown={this.hideModal}
className={`app-sharing-modal animation-fade ${this.props.darkMode ? 'dark-theme' : ''}`}
contentClassName={this.props.darkMode ? 'dark-theme' : ''}
>
<Modal.Header>
<Modal.Title data-cy="modal-header">{this.props.t('editor.share', 'Share')}</Modal.Title>
<span onClick={this.hideModal} data-cy="modal-close-button">
<SolidIcon name="remove" className="cursor-pointer" aria-label="Close" />
</span>
</Modal.Header>
<Modal.Body>
{
<div class="shareable-link-container">
<div className="make-public mb-3">
<div className="form-check form-switch d-flex align-items-center">
<input
className="form-check-input"
type="checkbox"
onClick={this.toggleAppVisibility}
checked={this?.props?.isPublic}
disabled={this.state.ischangingVisibility}
data-cy="make-public-app-toggle"
/>
<span className="form-check-label field-name" data-cy="make-public-app-label">
{this.props.t('editor.shareModal.makeApplicationPublic', 'Make application public')}
</span>
</div>
</div>
<div className="shareable-link tj-app-input mb-2">
<label data-cy="shareable-app-link-label" className="field-name">
{this.props.t('editor.shareModal.shareableLink', 'Shareable app link')}
</label>
<div className="input-group">
<span className="input-group-text applink-text flex-grow-1 slug-ellipsis" data-cy="app-link">
{appLink}
</span>
<div className="input-with-icon">
<input
type="text"
className={`form-control form-control-sm ${slugButtonClass}`}
placeholder={this.props.slug}
maxLength={50}
onChange={(e) => {
e.persist();
this.delayedSlugChange(e);
}}
style={{ maxWidth: '150px' }}
defaultValue={this.props.slug}
data-cy="app-name-slug-input"
/>
{isSlugVerificationInProgress && (
<div className="icon-container">
<div class="spinner-border text-secondary " role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
)}
<div className="icon-container">
{newSlug?.error ? (
<svg
width="21"
height="20"
viewBox="0 0 21 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.94252 3.61195C4.31445 3.24003 4.91746 3.24003 5.28939 3.61195L10.3302 8.6528L15.3711 3.61195C15.743 3.24003 16.346 3.24003 16.718 3.61195C17.0899 3.98388 17.0899 4.5869 16.718 4.95882L11.6771 9.99967L16.718 15.0405C17.0899 15.4125 17.0899 16.0155 16.718 16.3874C16.346 16.7593 15.743 16.7593 15.3711 16.3874L10.3302 11.3465L5.28939 16.3874C4.91746 16.7593 4.31445 16.7593 3.94252 16.3874C3.57059 16.0155 3.57059 15.4125 3.94252 15.0405L8.98337 9.99967L3.94252 4.95882C3.57059 4.5869 3.57059 3.98388 3.94252 3.61195Z"
fill="#E54D2E"
/>
</svg>
) : (
isSlugUpdated &&
!isSlugVerificationInProgress && (
<svg
width="21"
height="20"
viewBox="0 0 21 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M17.5859 5.24408C17.9114 5.56951 17.9114 6.09715 17.5859 6.42259L9.25259 14.7559C8.92715 15.0814 8.39951 15.0814 8.07407 14.7559L3.90741 10.5893C3.58197 10.2638 3.58197 9.73618 3.90741 9.41074C4.23284 9.08531 4.76048 9.08531 5.08592 9.41074L8.66333 12.9882L16.4074 5.24408C16.7328 4.91864 17.2605 4.91864 17.5859 5.24408Z"
fill="#46A758"
/>
</svg>
)
)}
</div>
</div>
<span className="input-group-text">
<CopyToClipboard text={shareableLink} onCopy={() => toast.success('Link copied to clipboard')}>
<svg
className="cursor-pointer"
width="17"
height="18"
viewBox="0 0 17 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-cy="copy-app-link-button"
>
<path
d="M9.11154 5.18031H5.88668V4.83302C5.88668 3.29859 7.13059 2.05469 8.66502 2.05469H12.8325C14.3669 2.05469 15.6109 3.29859 15.6109 4.83302V9.00052C15.6109 10.535 14.3669 11.7789 12.8325 11.7789H12.4852V8.554C12.4852 6.69076 10.9748 5.18031 9.11154 5.18031Z"
fill="#889096"
/>
<path
d="M8.66502 15.9464H4.49752C2.96309 15.9464 1.71918 14.7025 1.71918 13.168V9.00052C1.71918 7.46609 2.96309 6.22219 4.49752 6.22219H8.66502C10.1994 6.22219 11.4434 7.46609 11.4434 9.00052V13.168C11.4434 14.7025 10.1994 15.9464 8.66502 15.9464Z"
fill="#889096"
/>
</svg>
</CopyToClipboard>
</span>
</div>
{newSlug?.error ? (
<label className="label tj-input-error" data-cy="app-slug-error-label">
{newSlug?.error || ''}
</label>
) : isSlugUpdated ? (
<label
className="label label-success"
data-cy="app-slug-accepted-label"
>{`Slug accepted!`}</label>
) : (
<label
className="label label-info"
data-cy="app-slug-info-label"
>{`URL-friendly 'slug' consists of lowercase letters, numbers, and hyphens`}</label>
)}
</div>
{this?.props?.isPublic && window?.public_config?.ENABLE_PRIVATE_APP_EMBED === 'true' && (
<div className="tj-app-input">
<label className="field-name" data-cy="iframe-link-label">
{this.props.t('editor.shareModal.embeddableLink', 'Embedded app link')}
</label>
<span className={`tj-text-input justify-content-between ${this.props.darkMode ? 'dark' : ''}`}>
<span data-cy="iframe-link">{embeddableLink}</span>
<span className="copy-container">
<CopyToClipboard
text={embeddableLink}
onCopy={() => toast.success('Link copied to clipboard')}
>
<svg
className="cursor-pointer"
width="17"
height="18"
viewBox="0 0 17 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-cy="iframe-link-copy-button"
>
<path
d="M9.11154 5.18031H5.88668V4.83302C5.88668 3.29859 7.13059 2.05469 8.66502 2.05469H12.8325C14.3669 2.05469 15.6109 3.29859 15.6109 4.83302V9.00052C15.6109 10.535 14.3669 11.7789 12.8325 11.7789H12.4852V8.554C12.4852 6.69076 10.9748 5.18031 9.11154 5.18031Z"
fill="#889096"
/>
<path
d="M8.66502 15.9464H4.49752C2.96309 15.9464 1.71918 14.7025 1.71918 13.168V9.00052C1.71918 7.46609 2.96309 6.22219 4.49752 6.22219H8.66502C10.1994 6.22219 11.4434 7.46609 11.4434 9.00052V13.168C11.4434 14.7025 10.1994 15.9464 8.66502 15.9464Z"
fill="#889096"
/>
</svg>
</CopyToClipboard>
</span>
</span>
</div>
)}
</div>
}
</Modal.Body>
<Modal.Footer className="manage-app-users-footer">
{this.isUserAdmin && (
<Link
to={getPrivateRoute('workspace_settings')}
target="_blank"
className={`btn border-0 default-secondary-button float-right1`}
data-cy="manage-users-button"
>
Manage users
</Link>
)}
</Modal.Footer>
</Modal>
</div>
</ToolTip>
);
}
}
export const ManageAppUsers = withTranslation()(ManageAppUsersComponent);

View file

@ -0,0 +1,33 @@
import React, { useEffect } from 'react';
// eslint-disable-next-line import/no-unresolved
import { useUpdatePresence } from '@y-presence/react';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
export default function UpdatePresenceMultiPlayer() {
const { user } = useStore(
(state) => ({
user: state.user,
}),
shallow
);
const updatePresence = useUpdatePresence();
useEffect(() => {
if (user) {
const initialPresence = {
firstName: user.firstName ?? '',
lastName: user.lastName ?? '',
email: user.email ?? '',
image: '',
editingVersionId: '',
x: 0,
y: 0,
color: '',
};
updatePresence(initialPresence);
}
}, [user, updatePresence]);
return <></>;
}

View file

@ -33,7 +33,6 @@ const PreviewSettings = ({ isMobileLayout, showHeader, darkMode }) => {
<>
<AppVersionsManager darkMode={darkMode} />
<div className="navbar-seperator"></div>
{/* <EnvironmentManager darkMode={darkMode} /> */}
</>
)}
<span>
@ -78,8 +77,6 @@ const PreviewSettings = ({ isMobileLayout, showHeader, darkMode }) => {
</Offcanvas.Header>
{previewNavbar && (
<Offcanvas.Body>
<span style={{ marginTop: '4px' }}>{/* <EnvironmentManager darkMode={darkMode} /> */}</span>
<hr className="m-0" />
<span>
<AppVersionsManager darkMode={darkMode} />
</span>

View file

@ -97,10 +97,6 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
const switchPage = useStore((state) => state.switchPage);
const showHeader = !globalSettings?.hideHeader && isAppLoaded;
// ---remove
const handleAppEnvironmentChanged = useCallback((environment) => {
console.log('setAppVersionCurrentEnvironment', environment);
}, []);
useEffect(() => {
updateCanvasHeight(currentPageComponents);
@ -151,7 +147,6 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
pages={pages}
currentPageId={currentPageId ?? homePageId}
showViewerNavigation={!isPagesSidebarHidden}
handleAppEnvironmentChanged={handleAppEnvironmentChanged}
changeToDarkMode={changeToDarkMode}
/>
)}
@ -163,7 +158,6 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
pages={pages}
currentPageId={currentPageId ?? homePageId}
showViewerNavigation={!isPagesSidebarHidden}
handleAppEnvironmentChanged={handleAppEnvironmentChanged}
changeToDarkMode={changeToDarkMode}
/>
)}
@ -216,14 +210,13 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
pages={pages}
currentPageId={currentPageId ?? homePageId}
showViewerNavigation={!isPagesSidebarHidden}
handleAppEnvironmentChanged={handleAppEnvironmentChanged}
switchPage={switchPage}
changeToDarkMode={changeToDarkMode}
/>
)}
<AppCanvas moduleId={moduleId} isViewerSidebarPinned={isSidebarPinned} />
</div>
{isAppLoaded && <TooljetBanner isDarkMode={darkMode} />}
<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

@ -163,7 +163,7 @@ const useAppData = (appId, moduleId, mode = 'edit', { environmentId, versionId }
}
const constantsResp = isPublicAccess
? await orgEnvironmentConstantService.getConstantsFromPublicApp(slug)
: await orgEnvironmentConstantService.getConstantsFromEnvironment(editorEnvironmentId);
: await orgEnvironmentConstantService.getConstantsFromApp(editorEnvironmentId);
const pages = appData.pages.map((page) => {
return page;

View file

@ -26,7 +26,7 @@ export const BoundedBox = ({ properties, fireEvent, darkMode, setExposedVariable
useEffect(() => {
const handleImageLoad = () => {
const wrapperElement = document.querySelector(`.widget-${id} .lmGPCf`);
const wrapperElement = document.querySelector(`[widgetid="${id}"] .lmGPCf`);
if (wrapperElement) {
const { width, height } = wrapperElement.getBoundingClientRect();
// Use the width and height of bounding image for further calculations
@ -35,7 +35,7 @@ export const BoundedBox = ({ properties, fireEvent, darkMode, setExposedVariable
}
};
const imageElement = document.querySelector(`.widget-${id} .gVmiLs`);
const imageElement = document.querySelector(`[widgetid="${id}"] .gVmiLs`);
if (imageElement) {
imageElement.addEventListener('load', handleImageLoad);
}

View file

@ -95,7 +95,7 @@ export const Button = function Button(props) {
useEffect(() => {
const exposedVariables = {
click: async function () {
if (!disable) {
if (!disable && !loading) {
fireEvent('onClick');
}
},
@ -170,9 +170,11 @@ export const Button = function Button(props) {
computedStyles['--tblr-btn-color-clicked'] = tinycolor(computedBgColor).darken(15).toString();
}
const handleClick = () => {
const event1 = new CustomEvent('submitForm', { detail: { buttonComponentId: id } });
document.dispatchEvent(event1);
fireEvent('onClick');
if (!disable && !loading) {
const event1 = new CustomEvent('submitForm', { detail: { buttonComponentId: id } });
document.dispatchEvent(event1);
fireEvent('onClick');
}
};
const renderButton = () => (
<div

View file

@ -1,11 +1,12 @@
import React, { useState, useEffect, useMemo, memo, useCallback } from 'react';
import React, { useState, useEffect, useMemo, memo, useCallback, useRef } from 'react';
// eslint-disable-next-line import/no-unresolved
import Plotly from 'plotly.js-dist-min';
import createPlotlyComponent from 'react-plotly.js/factory';
import { isJson } from '@/_helpers/utils';
import { isStringValidJson } from '@/_helpers/utils';
const Plot = createPlotlyComponent(Plotly);
import { isEqual } from 'lodash';
import { deepClone } from '@/_helpers/utilities/utils.helpers';
var tinycolor = require('tinycolor2');
export const Chart = function Chart({
@ -19,6 +20,7 @@ export const Chart = function Chart({
setExposedVariables,
dataCy,
}) {
const isInitialRender = useRef(true);
const [loadingState, setLoadingState] = useState(false);
const getColor = (color) => {
@ -51,7 +53,13 @@ export const Chart = function Chart({
const jsonData = typeof jsonDescription === 'object' ? JSON.stringify(jsonDescription) : jsonDescription;
const isDescriptionJson = plotFromJson ? isJson(jsonData) : false;
let isDescriptionJson = false;
if (plotFromJson) {
isDescriptionJson = isStringValidJson(jsonData);
if (!isDescriptionJson) {
console.log('Throw error');
}
}
const jsonChartData = isDescriptionJson ? JSON.parse(jsonData).data : [];
@ -67,6 +75,7 @@ export const Chart = function Chart({
const chartTitle = plotFromJson ? chartLayout?.title ?? title : title;
useEffect(() => {
if (isInitialRender.current) return;
const { xaxis, yaxis } = chartLayout;
let xAxisTitle, yAxisTitle;
if (xaxis) {
@ -172,7 +181,7 @@ export const Chart = function Chart({
);
const handleClick = useCallback((data) => {
if (data.length > 0) {
if (!disabledState && data.length > 0) {
const {
x: xAxisLabel,
y: yAxisLabel,
@ -194,13 +203,32 @@ export const Chart = function Chart({
}, []);
const handleDoubleClick = useCallback(() => {
fireEvent('onDoubleClick');
if (!disabledState) {
fireEvent('onDoubleClick');
}
}, []);
useEffect(() => {
setExposedVariable('clearClickedPoint', () => {
setExposedVariable('clickedDataPoint', {});
});
const { xaxis, yaxis } = chartLayout;
let xAxisTitle, yAxisTitle;
if (xaxis) {
xAxisTitle = xaxis?.title?.text;
}
if (yaxis) {
yAxisTitle = yaxis?.title?.text;
}
const exposedVariables = {
chartTitle: chartTitle,
xAxisTitle: xAxisTitle,
yAxisTitle: yAxisTitle,
clearClickedPoint: () => {
setExposedVariable('clickedDataPoint', {});
},
};
setExposedVariables(exposedVariables);
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@ -221,6 +249,7 @@ export const Chart = function Chart({
}}
onClick={handleClick}
onDoubleClick={handleDoubleClick}
disabledState={disabledState}
/>
)}
</div>
@ -229,17 +258,17 @@ export const Chart = function Chart({
// onClick event was not working when the component is re-rendered for every click. Hance, memoization is used
const PlotComponent = memo(
({ data, layout, config, onClick, onDoubleClick }) => {
({ data, layout, config, onClick, onDoubleClick, disabledState }) => {
return (
<Plot
data={data}
layout={deepClone(layout)} // Cloning the layout since the object is getting mutated inside the package
config={config}
onClick={(e) => {
onClick(e.points);
if (!disabledState) onClick(e.points);
}}
onDoubleClick={() => {
onDoubleClick();
if (!disabledState) onDoubleClick();
}}
/>
);

View file

@ -1,5 +1,4 @@
import React, { useEffect, useState } from 'react';
import { resolveWidgetFieldValue } from '@/_helpers/utils';
import React, { useEffect, useRef, useState } from 'react';
import Loader from '@/ToolJetUI/Loader/Loader';
import OverflowTooltip from '@/_components/OverflowTooltip';
@ -8,21 +7,26 @@ export const Checkbox = ({
properties,
styles,
fireEvent,
componentName,
setExposedVariable,
setExposedVariables,
validation,
dataCy,
component,
validate,
width,
}) => {
const isInitialRender = useRef(true);
const defaultValueFromProperties = properties.defaultValue ?? false;
const isMandatory = validation?.mandatory ?? false;
const [defaultValue, setDefaultValue] = useState(defaultValueFromProperties);
const [checked, setChecked] = useState(defaultValueFromProperties);
const [value, setValue] = React.useState(defaultValueFromProperties);
const [userInteracted, setUserInteracted] = useState(false);
const { label } = properties;
const textColor = styles.textColor === '#1B1F24' ? 'var(--text-primary)' : styles.textColor;
const textColor = ['#1B1F24', '#000', '#000000ff'].includes(styles.textColor)
? 'var(--text-primary)'
: styles.textColor;
const { loadingState, disabledState } = properties;
const { checkboxColor, boxShadow, alignment, uncheckedColor, borderColor, handleColor } = styles;
@ -31,8 +35,6 @@ export const Checkbox = ({
const [visibility, setVisibility] = useState(properties.visibility);
const { isValid, validationError } = validate(checked);
const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value);
const toggleValue = (e) => {
const isChecked = e.target.checked;
setChecked(isChecked);
@ -48,28 +50,11 @@ export const Checkbox = ({
};
useEffect(() => {
const setCheckedAndNotify = async (status) => {
await setExposedVariable('value', status);
if (status) {
fireEvent('onCheck');
} else {
fireEvent('onUnCheck');
}
setChecked(status);
setValue(status);
};
const exposedVariables = {
value: defaultValueFromProperties,
setChecked: setCheckedAndNotify,
setValue: setCheckedAndNotify,
};
if (isInitialRender.current) return;
setDefaultValue(defaultValueFromProperties);
setChecked(defaultValueFromProperties);
setValue(defaultValueFromProperties);
setExposedVariables(exposedVariables);
setExposedVariable('value', defaultValueFromProperties);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultValueFromProperties]);
@ -89,79 +74,108 @@ export const Checkbox = ({
}, [loadingState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('label', label);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [label]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isMandatory', isMandatory);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMandatory]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isLoading', loading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loading]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isVisible', visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibility]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isDisabled', disable);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disable]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isValid', isValid);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isValid]);
useEffect(() => {
setExposedVariable('setLoading', async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.loadingState]);
useEffect(() => {
setExposedVariable('setVisibility', async function (state) {
setVisibility(state);
setExposedVariable('isVisible', state);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.visibility]);
const setCheckedAndNotify = async (status) => {
await setExposedVariable('value', status);
if (status) {
fireEvent('onCheck');
} else {
fireEvent('onUnCheck');
}
setChecked(status);
setValue(status);
};
useEffect(() => {
setExposedVariable('setDisable', async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disable]);
const exposedVariables = {
value: defaultValueFromProperties,
setChecked: setCheckedAndNotify,
setValue: setCheckedAndNotify,
setLoading: async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
},
setVisibility: async function (visibility) {
setVisibility(visibility);
setExposedVariable('isVisible', visibility);
},
setDisable: async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
},
toggle: () => {
setExposedVariable('toggle', async function () {
setExposedVariable('value', !checked);
fireEvent('onChange');
setChecked(!checked);
setValue(!checked);
setUserInteracted(true);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
},
label: label,
isMandatory: isMandatory,
isLoading: loading,
isVisible: visibility,
isDisabled: disable,
isValid: isValid,
};
setDefaultValue(defaultValueFromProperties);
setChecked(defaultValueFromProperties);
setValue(defaultValueFromProperties);
setExposedVariables(exposedVariables);
isInitialRender.current = false;
useEffect(() => {
setExposedVariable('toggle', async function () {
setExposedVariable('value', !checked);
fireEvent('onChange');
setChecked(!checked);
setValue(!checked);
setUserInteracted(true);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [checked, value]);
}, []);
const handleToggleChange = () => {
const newCheckedState = !checked;
setChecked(newCheckedState);
setValue(newCheckedState);
setExposedVariable('value', newCheckedState).then(() => {
fireEvent('onChange');
if (newCheckedState) {
fireEvent('onCheck');
} else {
fireEvent('onUnCheck');
}
});
setExposedVariable('value', newCheckedState);
fireEvent('onChange');
if (newCheckedState) {
fireEvent('onCheck');
} else {
fireEvent('onUnCheck');
}
setUserInteracted(true);
};
@ -239,7 +253,7 @@ export const Checkbox = ({
</div>
{validationError && visibility && !checked && userInteracted && (
<div
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
data-cy={`${String(componentName).toLowerCase()}-invalid-feedback`}
style={{
color: 'var(--status-error-strong)',
fontSize: '11px',

View file

@ -1,5 +1,5 @@
/* eslint-disable import/no-unresolved */
import React from 'react';
import React, { useState } from 'react';
import CodeMirror from '@uiw/react-codemirror';
import { okaidia } from '@uiw/codemirror-theme-okaidia';
import { githubLight } from '@uiw/codemirror-theme-github';
@ -17,12 +17,14 @@ const langSupport = Object.freeze({
css: sass(),
});
export const CodeEditor = ({ height, darkMode, properties, styles, exposedVariables, setExposedVariable, dataCy }) => {
export const CodeEditor = ({ id, height, darkMode, properties, styles, setExposedVariable, dataCy }) => {
const { enableLineNumber, mode, placeholder } = properties;
const { visibility, disabledState } = styles;
const [value, setValue] = useState('');
const codeChanged = debounce((code) => {
setExposedVariable('value', code);
setValue(code);
}, 500);
const editorStyles = {
@ -63,7 +65,7 @@ export const CodeEditor = ({ height, darkMode, properties, styles, exposedVariab
}}
>
<CodeMirror
value={exposedVariables.value}
value={value}
placeholder={placeholder}
height={'100%'}
minHeight={editorHeight}

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { SketchPicker } from 'react-color';
export const ColorPicker = function ({
@ -17,6 +17,7 @@ export const ColorPicker = function ({
const defaultColor = properties.defaultColor;
const [showColorPicker, setShowColorPicker] = useState(false);
const [color, setColor] = useState(defaultColor);
const colorPickerRef = useRef(null);
const getRGBAValueFromHex = (hex) => {
let c = hex.substring(1).split('');
@ -82,21 +83,19 @@ export const ColorPicker = function ({
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setColor]);
}, []);
useEffect(() => {
let exposedVariables = {};
if (/^#(([\dA-Fa-f]{3}){1,2}|([\dA-Fa-f]{4}){1,2})$/.test(defaultColor)) {
if (defaultColor !== color) {
exposedVariables = {
selectedColorHex: defaultColor,
selectedColorRGB: hexToRgb(defaultColor),
selectedColorRGBA: hexToRgba(defaultColor),
};
setExposedVariables(exposedVariables);
exposedVariables = {
selectedColorHex: defaultColor,
selectedColorRGB: hexToRgb(defaultColor),
selectedColorRGBA: hexToRgba(defaultColor),
};
setExposedVariables(exposedVariables);
setColor(defaultColor);
}
setColor(defaultColor);
} else {
exposedVariables = {
selectedColorHex: undefined,
@ -110,6 +109,21 @@ export const ColorPicker = function ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultColor]);
useEffect(() => {
if (showColorPicker) {
const handleClickOutside = (event) => {
if (colorPickerRef.current && !colorPickerRef.current.contains(event.target)) {
setShowColorPicker(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}
}, [showColorPicker]);
const handleColorChange = (colorCode) => {
let exposedVariables = {};
const { r, g, b, a } = colorCode.rgb;
@ -147,23 +161,20 @@ export const ColorPicker = function ({
: { display: 'none' };
return (
<div style={{ baseStyle, boxShadow }} className="form-control" data-cy={dataCy}>
<div className="d-flex h-100 justify-content-between align-items-center" onClick={() => setShowColorPicker(true)}>
<span>{color}</span>
{!(color === `Invalid Color`) && <div style={backgroundColorDivStyle}></div>}
<div>
<div style={{ baseStyle, boxShadow }} className="form-control" data-cy={dataCy}>
<div
className="d-flex h-100 justify-content-between align-items-center"
onClick={() => setShowColorPicker(true)}
>
<span>{color}</span>
{!(color === `Invalid Color`) && <div style={backgroundColorDivStyle}></div>}
</div>
</div>
{showColorPicker && (
<>
<div
className="position-absolute bottom-0"
style={{ left: 0, right: 0 }}
onMouseLeave={() => setShowColorPicker(false)}
width={width}
>
<SketchPicker color={color} onChangeComplete={handleColorChange} />
</div>
<div onClick={() => setShowColorPicker(false)}></div>
</>
<div className="position-relative top-0 mt-1" ref={colorPickerRef} width={width}>
<SketchPicker color={color} onChangeComplete={handleColorChange} />
</div>
)}
</div>
);

View file

@ -1,33 +1,18 @@
import React, { useEffect, useState, useRef } from 'react';
import { isEqual } from 'lodash';
import iframeContent from './iframe.html';
import { useDataQueries } from '@/_stores/dataQueriesStore';
import { useGridStore } from '@/_stores/gridStore';
import { isQueryRunnable } from '@/_helpers/utils';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
export const CustomComponent = (props) => {
const { height, properties, styles, id, setExposedVariable, exposedVariables, fireEvent, dataCy, component } = props;
const dataQueries = useDataQueries();
const showPlaceholder = useGridStore((state) => {
const { resizingComponentId, draggingComponentId } = state;
if (
(resizingComponentId === null && draggingComponentId === id) ||
(draggingComponentId === null && resizingComponentId === id) ||
id === 'resizingComponentId'
) {
return true;
}
return false;
}, shallow);
const { height, properties, styles, id, setExposedVariable, dataCy } = props;
const exposedVariables = useStore((state) => state.getExposedValueOfComponent(id), shallow);
const onEvent = useStore((state) => state.eventsSlice.onEvent, shallow);
const { visibility, boxShadow } = styles;
const { code, data } = properties;
const [customProps, setCustomProps] = useState(data);
const iFrameRef = useRef(null);
const dataQueryRef = useRef(dataQueries);
const customPropRef = useRef(data);
useEffect(() => {
@ -42,17 +27,13 @@ export const CustomComponent = (props) => {
sendMessageToIframe({ message: 'DATA_UPDATED' });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setExposedVariable, customProps, exposedVariables.data]);
}, [customProps, exposedVariables.data]);
useEffect(() => {
sendMessageToIframe({ message: 'CODE_UPDATED' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [code]);
useEffect(() => {
dataQueryRef.current = dataQueries;
}, [dataQueries]);
useEffect(() => {
window.addEventListener('message', (e) => {
try {
@ -60,17 +41,11 @@ export const CustomComponent = (props) => {
if (e.data.message === 'UPDATE_DATA') {
setCustomProps({ ...customPropRef.current, ...e.data.updatedObj });
} else if (e.data.message === 'RUN_QUERY') {
const filteredQuery = dataQueryRef.current.filter(
(query) => query.name === e.data.queryName && isQueryRunnable(query)
);
const parameters = e.data.parameters ? JSON.parse(e.data.parameters) : {};
filteredQuery.length === 1 &&
fireEvent('onTrigger', {
component,
queryId: filteredQuery[0].id,
queryName: filteredQuery[0].name,
parameters,
});
const options = {
parameters: e.data.parameters,
queryName: e.data.queryName,
};
onEvent('onTrigger', [], options);
} else {
sendMessageToIframe(e.data);
}
@ -90,7 +65,7 @@ export const CustomComponent = (props) => {
{
message: 'INIT_RESPONSE',
componentId: id,
data: customProps,
data: customPropRef.current,
code: code,
},
'*'
@ -121,14 +96,12 @@ export const CustomComponent = (props) => {
return (
<div className="card" style={{ display: visibility ? '' : 'none', height, boxShadow }} data-cy={dataCy}>
{showPlaceholder ? null : (
<iframe
srcDoc={iframeContent}
style={{ width: '100%', height: '100%', border: 'none' }}
ref={iFrameRef}
data-id={id}
></iframe>
)}
<iframe
srcDoc={iframeContent}
style={{ width: '100%', height: '100%', border: 'none' }}
ref={iFrameRef}
data-id={id}
></iframe>
</div>
);
};

View file

@ -1,27 +1,29 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import DatePickerComponent from 'react-datepicker';
import moment from 'moment';
import 'react-datepicker/dist/react-datepicker.css';
import './datepicker.scss';
import cx from 'classnames';
export const Datepicker = function Datepicker({
height,
properties,
styles,
exposedVariables,
setExposedVariable,
setExposedVariables,
validate,
onComponentClick,
component,
id,
darkMode,
fireEvent,
dataCy,
}) {
const isInitialRender = useRef(true);
const { enableTime, enableDate, defaultValue, disabledDates } = properties;
const format = typeof properties.format === 'string' ? properties.format : '';
const { visibility, disabledState, borderRadius, boxShadow } = styles;
const [date, setDate] = useState(null);
const [date, setDate] = useState(defaultValue);
const [excludedDates, setExcludedDates] = useState([]);
const [showValidationError, setShowValidationError] = useState(false);
@ -46,6 +48,7 @@ export const Datepicker = function Datepicker({
};
useEffect(() => {
if (isInitialRender.current) return;
const dateMomentInstance = defaultValue && moment(defaultValue, selectedDateFormat);
if (dateMomentInstance && dateMomentInstance.isValid()) {
setDate(dateMomentInstance.toDate());
@ -70,14 +73,32 @@ export const Datepicker = function Datepicker({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disabledDates, format]);
const validationData = validate(exposedVariables.value);
const validationData = validate(computeDateString(date));
const { isValid, validationError } = validationData;
useEffect(() => {
isInitialRender.current = false;
setExposedVariable('isValid', isValid);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isValid]);
useEffect(() => {
const exposedVariables = {
isValid,
};
const dateMomentInstance = defaultValue && moment(defaultValue, selectedDateFormat);
if (dateMomentInstance && dateMomentInstance.isValid()) {
setDate(dateMomentInstance.toDate());
exposedVariables.value = defaultValue;
} else {
setDate(null);
exposedVariables.value = undefined;
}
setExposedVariables(exposedVariables);
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div
data-disabled={disabledState}
@ -94,13 +115,14 @@ export const Datepicker = function Datepicker({
className={`input-field form-control ${
!isValid && showValidationError ? 'is-invalid' : ''
} validation-without-icon px-2 ${darkMode ? 'bg-dark color-white' : 'bg-light'}`}
popperClassName={cx('tj-datepicker-widget', { 'dark-theme': darkMode })}
selected={date}
value={date !== null ? computeDateString(date) : 'select date'}
onChange={(date) => onDateChange(date)}
showTimeInput={enableTime ? true : false}
showTimeSelectOnly={enableDate ? false : true}
onFocus={(event) => {
onComponentClick(id, component, event);
onComponentClick(id);
}}
showMonthDropdown
showYearDropdown

View file

@ -10,11 +10,14 @@ export const DaterangePicker = function DaterangePicker({
properties,
styles,
setExposedVariable,
setExposedVariables,
width,
darkMode,
fireEvent,
dataCy,
id,
}) {
const isInitialRender = useRef(true);
const { borderRadius, visibility, disabledState, boxShadow } = styles;
const { defaultStartDate, defaultEndDate } = properties;
const formatProp = typeof properties.format === 'string' ? properties.format : '';
@ -26,12 +29,28 @@ export const DaterangePicker = function DaterangePicker({
const dateRangeRef = useRef(null);
useEffect(() => {
if (isInitialRender.current) return;
setStartDate(moment(defaultStartDate, formatProp));
setExposedVariable('startDate', moment(defaultStartDate, formatProp).format(formatProp));
}, [defaultStartDate, formatProp]);
useEffect(() => {
if (isInitialRender.current) return;
setEndDate(moment(defaultEndDate, formatProp));
setExposedVariable('endDate', moment(defaultEndDate, formatProp).format(formatProp));
}, [defaultEndDate, formatProp]);
useEffect(() => {
const exposedVariables = {
startDate: moment(defaultStartDate, formatProp).format(formatProp),
endDate: moment(defaultEndDate, formatProp).format(formatProp),
};
setExposedVariables(exposedVariables);
setStartDate(moment(defaultStartDate, formatProp));
setEndDate(moment(defaultEndDate, formatProp));
setExposedVariable('startDate', startDate.format(formatProp));
setExposedVariable('endDate', endDate.format(formatProp));
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultEndDate, defaultStartDate, formatProp]);
}, []);
useEffect(() => {
dateRangeRef.current.container.querySelector('.DateRangePickerInput').style.borderRadius = `${Number.parseFloat(
@ -63,6 +82,11 @@ export const DaterangePicker = function DaterangePicker({
function focusChanged(focus) {
setFocusedInput(focus);
if (focus) {
document.querySelector(`.ele-${id}`).style.zIndex = 3;
} else {
document.querySelector(`.ele-${id}`).style.zIndex = '';
}
}
return (

View file

@ -1,5 +1,5 @@
import _ from 'lodash';
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import Select, { components } from 'react-select';
import TriangleDownArrow from '@/_ui/Icon/bulkIcons/TriangleDownArrow';
import TriangleUpArrow from '@/_ui/Icon/bulkIcons/TriangleUpArrow';
@ -10,22 +10,20 @@ export const DropDown = function DropDown({
properties,
styles,
setExposedVariable,
setExposedVariables,
fireEvent,
darkMode,
onComponentClick,
id,
component,
exposedVariables,
dataCy,
}) {
const isInitialRender = useRef(true);
let { label, value, advanced, schema, placeholder, display_values, values } = properties;
const { selectedTextColor, borderRadius, visibility, disabledState, justifyContent, boxShadow } = styles;
const [currentValue, setCurrentValue] = useState(() => (advanced ? findDefaultItem(schema) : value));
const { value: exposedValue } = exposedVariables;
const [showValidationError, setShowValidationError] = useState(false);
const validationData = validate(value);
const { isValid, validationError } = validationData;
function findDefaultItem(schema) {
const foundItem = schema?.find((item) => item?.default === true);
return !hasVisibleFalse(foundItem?.value) ? foundItem?.value : undefined;
@ -63,10 +61,13 @@ export const DropDown = function DropDown({
const setExposedItem = (value, index, onSelectFired = false) => {
setCurrentValue(value);
if (onSelectFired) {
setExposedVariable('value', value);
fireEvent('onSelect');
} else setExposedVariable('value', value);
setExposedVariable('selectedOptionLabel', index === undefined ? undefined : display_values?.[index]);
}
const exposedVariables = {
value,
selectedOptionLabel: index === undefined ? undefined : display_values?.[index],
};
setExposedVariables(exposedVariables);
};
function selectOption(value) {
@ -79,19 +80,71 @@ export const DropDown = function DropDown({
setExposedItem(undefined, undefined, true);
}
}
useEffect(() => {
setExposedVariable('selectOption', async function (value) {
selectOption(value);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(values), setCurrentValue, JSON.stringify(display_values)]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isValid', isValid);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isValid]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('value', currentValue);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentValue]);
useEffect(() => {
if (isInitialRender.current) return;
const index = values?.indexOf(currentValue);
setExposedVariable('selectedOptionLabel', display_values?.[index]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentValue, JSON.stringify(display_values), JSON.stringify(values)]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('label', label);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [label]);
useEffect(() => {
if (isInitialRender.current) return;
if (advanced) {
setExposedVariable(
'optionLabels',
schema?.filter((item) => item?.visible)?.map((item) => item.label)
);
if (hasVisibleFalse(currentValue)) {
setCurrentValue(findDefaultItem(schema));
}
} else setExposedVariable('optionLabels', display_values);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(schema), advanced, JSON.stringify(display_values), currentValue]);
useEffect(() => {
const index = values?.indexOf(currentValue);
let optionLabels = display_values;
if (advanced) {
optionLabels = schema?.filter((item) => item?.visible)?.map((item) => item.label);
}
const exposedVariables = {
selectOption: async function (value) {
selectOption(value);
},
isValid: isValid,
value: currentValue,
selectedOptionLabel: display_values?.[index],
label: label,
optionLabels: optionLabels,
};
setExposedVariables(exposedVariables);
if (hasVisibleFalse(currentValue)) {
setCurrentValue(findDefaultItem(schema));
}
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
let newValue = undefined;
let index = null;
@ -104,16 +157,6 @@ export const DropDown = function DropDown({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(value), JSON.stringify(values)]);
useEffect(() => {
let index = null;
if (exposedValue !== currentValue) {
setExposedVariable('value', currentValue);
}
index = values?.indexOf(currentValue);
setExposedVariable('selectedOptionLabel', display_values?.[index]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentValue, JSON.stringify(display_values), JSON.stringify(values)]);
useEffect(() => {
let newValue = undefined;
let index = null;
@ -125,24 +168,6 @@ export const DropDown = function DropDown({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(values)]);
useEffect(() => {
setExposedVariable('label', label);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [label]);
useEffect(() => {
if (advanced) {
setExposedVariable(
'optionLabels',
schema?.filter((item) => item?.visible)?.map((item) => item.label)
);
if (hasVisibleFalse(currentValue)) {
setCurrentValue(findDefaultItem(schema));
}
} else setExposedVariable('optionLabels', display_values);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(schema), advanced, JSON.stringify(display_values), currentValue]);
function hasVisibleFalse(value) {
for (let i = 0; i < schema?.length; i++) {
if (schema[i].value === value && schema[i].visible === false) {
@ -227,42 +252,6 @@ export const DropDown = function DropDown({
backgroundColor: darkMode ? 'rgb(31,40,55)' : 'white',
}),
};
const [isOpen, setIsOpen] = useState(false);
const handleDropdownOpen = () => {
setIsOpen(true);
};
const handleDropdownClose = () => {
setIsOpen(false);
};
const DropdownIndicator = (props) => {
return (
<components.DropdownIndicator {...props}>
<div onClick={() => (isOpen ? handleDropdownClose() : handleDropdownOpen())}>
{isOpen ? (
<TriangleUpArrow width={'18'} className="cursor-pointer" fill={'var(--borders-strong)'} />
) : (
<TriangleDownArrow width={'18'} className="cursor-pointer" fill={'var(--borders-strong)'} />
)}
</div>
</components.DropdownIndicator>
);
};
useEffect(() => {
const handleClickOutside = (event) => {
if (isOpen && !event.target.closest('.dropdown-widget')) {
handleDropdownClose();
}
};
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [isOpen]);
return (
<>
@ -271,7 +260,7 @@ export const DropDown = function DropDown({
style={{ height, display: visibility ? '' : 'none' }}
onClick={(event) => {
event.stopPropagation();
onComponentClick(id, component, event);
onComponentClick(id);
}}
data-cy={dataCy}
>
@ -297,13 +286,9 @@ export const DropDown = function DropDown({
styles={customStyles}
isLoading={properties.loadingState}
onInputChange={onSearchTextChange}
onFocus={(event) => onComponentClick(event, component, id)}
onFocus={(event) => onComponentClick(id)}
menuPortalTarget={document.body}
placeholder={placeholder}
onMenuOpen={handleDropdownOpen}
onMenuClose={handleDropdownClose}
menuIsOpen={isOpen}
components={{ DropdownIndicator }}
/>
</div>
</div>

View file

@ -1,5 +1,3 @@
import { resolveReferences } from '@/_helpers/utils';
import { useCurrentState } from '@/_stores/currentStateStore';
import React, { useState, useEffect, useMemo, useRef } from 'react';
import Select, { components } from 'react-select';
import ClearIndicatorIcon from '@/_ui/Icon/bulkIcons/ClearIndicator';
@ -16,6 +14,8 @@ import CustomOption from './CustomOption';
import Label from '@/_ui/Label';
import cx from 'classnames';
import { getInputBackgroundColor, getInputBorderColor, getInputFocusedColor } from './utils';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
const { DropdownIndicator, ClearIndicator } = components;
const INDICATOR_CONTAINER_WIDTH = 60;
@ -55,11 +55,20 @@ export const DropdownV2 = ({
darkMode,
onComponentClick,
id,
component,
exposedVariables,
componentName,
validation,
dataCy,
}) => {
const { label, value, advanced, schema, placeholder, loadingState: dropdownLoadingState, disabledState } = properties;
const {
label,
value,
advanced,
schema,
placeholder,
loadingState: dropdownLoadingState,
disabledState,
optionsLoadingState,
} = properties;
const {
selectedTextColor,
fieldBorderRadius,
@ -79,11 +88,11 @@ export const DropdownV2 = ({
accentColor,
padding,
} = styles;
const isInitialRender = useRef(true);
const [currentValue, setCurrentValue] = useState(() => (advanced ? findDefaultItem(schema) : value));
const { value: exposedValue } = exposedVariables;
const currentState = useCurrentState();
const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState);
const options = component?.definition?.properties?.options?.value;
const getResolvedValue = useStore((state) => state.getResolvedValue, shallow);
const isMandatory = validation?.mandatory ?? false;
const options = properties?.options;
const validationData = validate(currentValue);
const { isValid, validationError } = validationData;
const ref = React.useRef(null);
@ -106,13 +115,14 @@ export const DropdownV2 = ({
let _options = advanced ? schema : options;
if (Array.isArray(_options)) {
let _selectOptions = _options
.filter((data) => resolveReferences(advanced ? data?.visible : data?.visible?.value, currentState))
.filter((data) => getResolvedValue(advanced ? data?.visible : data?.visible?.value) ?? true)
.map((data) => ({
...data,
label: resolveReferences(data?.label, currentState),
value: resolveReferences(data?.value, currentState),
isDisabled: resolveReferences(advanced ? data?.disable : data?.disable?.value, currentState),
label: String(getResolvedValue(data?.label)),
value: getResolvedValue(data?.value),
isDisabled: getResolvedValue(advanced ? data?.disable : data?.disable?.value) ?? false,
}));
return _selectOptions;
} else {
return [];
@ -139,6 +149,7 @@ export const DropdownV2 = ({
const onSearchTextChange = (searchText, actionProps) => {
if (actionProps.action === 'input-change') {
setSearchInputValue(searchText);
setExposedVariable('searchText', searchText);
fireEvent('onSearchTextChanged');
}
};
@ -174,11 +185,21 @@ export const DropdownV2 = ({
}, [properties.visibility, dropdownLoadingState, disabledState]);
// Exposed variables
useEffect(() => {
if (exposedValue !== currentValue) {
const _selectedOption = selectOptions.find((option) => option.value === currentValue);
setExposedVariable('selectedOption', pick(_selectedOption, ['label', 'value']));
}
if (isInitialRender.current) return;
setExposedVariable('value', currentValue);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentValue]);
useEffect(() => {
const _selectedOption = selectOptions.find((option) => option.value === currentValue);
setExposedVariable('selectedOption', pick(_selectedOption, ['label', 'value']));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentValue, JSON.stringify(selectOptions)]);
useEffect(() => {
if (isInitialRender.current) return;
const _options = selectOptions?.map(({ label, value }) => ({ label, value }));
setExposedVariable('options', _options);
@ -191,33 +212,75 @@ export const DropdownV2 = ({
}, [currentValue, JSON.stringify(selectOptions)]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('label', label);
setExposedVariable('searchText', searchInputValue);
setExposedVariable('isValid', isValid);
setExposedVariable('isVisible', properties.visibility);
setExposedVariable('isLoading', dropdownLoadingState);
setExposedVariable('isDisabled', disabledState);
setExposedVariable('isMandatory', isMandatory);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.visibility, dropdownLoadingState, disabledState, isMandatory, label, searchInputValue, isValid]);
}, [label]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('searchText', searchInputValue);
}, [searchInputValue]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isValid', isValid);
}, [isValid]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isVisible', properties.visibility);
}, [properties.visibility]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isLoading', dropdownLoadingState);
}, [dropdownLoadingState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isDisabled', disabledState);
}, [disabledState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isMandatory', isMandatory);
}, [isMandatory]);
useEffect(() => {
const _options = selectOptions?.map(({ label, value }) => ({ label, value }));
const exposedVariables = {
clear: async function () {
setCurrentValue(null);
},
setVisibility: async function (value) {
setVisibility(value);
setExposedVariable('isVisible', value);
},
setLoading: async function (value) {
setIsDropdownLoading(value);
setExposedVariable('isLoading', value);
},
setDisable: async function (value) {
setIsDropdownDisabled(value);
setExposedVariable('isDisabled', value);
},
selectOption: async function (value) {
let _value = value;
if (isObject(value) && has(value, 'value')) _value = value?.value;
selectOption(_value);
},
options: _options,
value: currentValue,
label: label,
searchText: searchInputValue,
isValid: isValid,
isVisible: properties.visibility,
isLoading: dropdownLoadingState,
isDisabled: disabledState,
isMandatory: isMandatory,
};
setExposedVariables(exposedVariables);
isInitialRender.current = false;
}, []);
const customStyles = {
@ -343,7 +406,7 @@ export const DropdownV2 = ({
return (
<>
<div
data-cy={`label-${String(component.name).toLowerCase()} `}
data-cy={`label-${String(componentName).toLowerCase()} `}
className={cx('dropdown-widget', 'd-flex', {
[alignment === 'top' &&
((labelWidth != 0 && label?.length != 0) ||
@ -361,7 +424,7 @@ export const DropdownV2 = ({
width: '100%',
}}
onMouseDown={(event) => {
onComponentClick(id, component, event);
onComponentClick(id);
// This following line is needed because sometimes after clicking on canvas then also dropdown remains selected
useEditorStore.getState().actions.setHoveredComponent('');
}}
@ -397,7 +460,7 @@ export const DropdownV2 = ({
isLoading={isDropdownLoading}
onInputChange={onSearchTextChange}
inputValue={searchInputValue}
onFocus={() => {
onMenuOpen={() => {
fireEvent('onFocus');
}}
onMenuInputFocus={() => setIsFocused(true)}
@ -425,7 +488,7 @@ export const DropdownV2 = ({
iconColor={iconColor}
isSearchable={false}
darkMode={darkMode}
optionsLoadingState={properties.optionsLoadingState}
optionsLoadingState={optionsLoadingState && advanced}
menuPlacement="auto"
/>
</div>

View file

@ -1,57 +1,46 @@
import React, { useEffect, useMemo } from 'react';
import React, { useEffect, useMemo, useRef } from 'react';
import { useDropzone } from 'react-dropzone';
import { resolveWidgetFieldValue } from '@/_helpers/utils';
import { toast } from 'react-hot-toast';
// eslint-disable-next-line import/no-unresolved
import * as XLSX from 'xlsx/xlsx.mjs';
import { useAppInfo } from '@/_stores/appDataStore';
import useStore from '@/AppBuilder/_stores/store';
export const FilePicker = ({
id,
width,
height,
component,
fireEvent,
onComponentOptionChanged,
onEvent,
darkMode,
styles,
properties,
setExposedVariable,
setExposedVariables,
dataCy,
}) => {
//* properties definitions
const instructionText =
component.definition.properties.instructionText?.value ?? 'Drag and drop files here or click to select files';
const enableDropzone = component.definition.properties.enableDropzone.value ?? true;
const enablePicker = component.definition.properties?.enablePicker?.value ?? true;
const maxFileCount = component.definition.properties.maxFileCount?.value ?? 2;
const enableMultiple = component.definition.properties.enableMultiple?.value ?? false;
const fileType = component.definition.properties.fileType?.value ?? 'image/*';
const maxSize = component.definition.properties.maxSize?.value ?? 1048576;
const minSize = component.definition.properties.minSize?.value ?? 0;
const parseContent = resolveWidgetFieldValue(component.definition.properties.parseContent?.value);
const fileTypeFromExtension = component.definition.properties.parseFileType?.value ?? 'auto-detect';
const parsedEnableDropzone = typeof enableDropzone !== 'boolean' ? resolveWidgetFieldValue(enableDropzone) : true;
const parsedEnablePicker = typeof enablePicker !== 'boolean' ? resolveWidgetFieldValue(enablePicker) : true;
const parsedMaxFileCount = typeof maxFileCount !== 'number' ? resolveWidgetFieldValue(maxFileCount) : maxFileCount;
const parsedEnableMultiple =
typeof enableMultiple !== 'boolean' ? resolveWidgetFieldValue(enableMultiple) : enableMultiple;
const parsedFileType = resolveWidgetFieldValue(fileType);
const parsedMinSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(minSize) : minSize;
const parsedMaxSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(maxSize) : maxSize;
//* styles definitions
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
const disabledState = component.definition.styles?.disabledState?.value ?? false;
const parsedDisabledState =
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState;
const parsedWidgetVisibility =
typeof widgetVisibility !== 'boolean' ? resolveWidgetFieldValue(widgetVisibility) : widgetVisibility;
//* resolved properties d
const isInitialRender = useRef(true);
const instructionText = properties?.instructionText ?? 'Drag and drop files here or click to select files';
const enableDropzone = properties?.enableDropzone ?? true;
const enablePicker = properties?.enablePicker ?? true;
const maxFileCount = properties?.maxFileCount ?? 2;
const enableMultiple = properties?.enableMultiple ?? false;
const fileType = properties?.fileType ?? 'image/*';
const maxSize = properties?.maxSize ?? 1048576;
const minSize = properties?.minSize ?? 0;
const parseContent = properties.parseContent;
const fileTypeFromExtension = properties.parseFileType ?? 'auto-detect';
//* resolved styles
const widgetVisibility = styles?.visibility ?? true;
const disabledState = styles?.disabledState ?? false;
const { events: allAppEvents } = useAppInfo();
const filePickerEvents = allAppEvents.filter((event) => event.target === 'component' && event.sourceId === id);
console.log(filePickerEvents);
const bgThemeColor = darkMode ? '#232E3C' : '#fff';
@ -68,9 +57,9 @@ export const FilePicker = ({
color: '#bdbdbd',
outline: 'none',
transition: 'border .24s ease-in-out',
display: parsedWidgetVisibility ? 'flex' : 'none',
display: widgetVisibility ? 'flex' : 'none',
height,
backgroundColor: !parsedDisabledState && bgThemeColor,
backgroundColor: !disabledState && bgThemeColor,
boxShadow: styles.boxShadow,
};
@ -90,14 +79,14 @@ export const FilePicker = ({
const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, acceptedFiles, fileRejections } =
useDropzone({
accept: { parsedFileType: [parsedFileType] },
noClick: !parsedEnablePicker || disablePicker,
noDrag: !parsedEnableDropzone || disablePicker,
accept: { parsedFileType: [fileType] },
noClick: !enablePicker || disablePicker,
noDrag: !enableDropzone || disablePicker,
noKeyboard: true,
maxFiles: parsedMaxFileCount,
minSize: parsedMinSize,
maxSize: parsedMaxSize,
multiple: parsedEnableMultiple,
maxFiles: maxFileCount,
minSize: minSize,
maxSize: maxSize,
multiple: enableMultiple,
disabled: disablePicker,
validator: validateFileExists,
onDropRejected: () => (selectedFiles.length > 0 ? setShowSelectedFiles(true) : setShowSelectedFiles(false)),
@ -107,9 +96,9 @@ export const FilePicker = ({
const style = useMemo(
() => ({
...baseStyle,
...(isDragActive && parsedEnableDropzone ? activeStyle : {}),
...(isDragAccept && parsedEnableDropzone ? acceptStyle : {}),
...(isDragReject && parsedEnableDropzone ? rejectStyle : {}),
...(isDragActive && enableDropzone ? activeStyle : {}),
...(isDragAccept && enableDropzone ? acceptStyle : {}),
...(isDragReject && enableDropzone ? rejectStyle : {}),
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[baseStyle, isDragActive, isDragAccept, acceptStyle, isDragReject]
@ -123,7 +112,7 @@ export const FilePicker = ({
function validateFileExists(_file) {
const selectedFilesCount = selectedFiles.length;
if (selectedFilesCount === parsedMaxFileCount) {
if (selectedFilesCount === maxFileCount) {
return {
code: 'max_file_count_reached',
message: `Max file count reached`,
@ -134,14 +123,13 @@ export const FilePicker = ({
}
useEffect(() => {
if (parsedDisabledState) setDisablePicker(true);
if (selectedFiles.length === parsedMaxFileCount && parsedEnableMultiple) {
if (disabledState) setDisablePicker(true);
else if (selectedFiles.length === maxFileCount && enableMultiple) {
setDisablePicker(true);
} else {
setDisablePicker(false);
}
}, [selectedFiles.length, parsedDisabledState, parsedMaxFileCount, parsedEnableMultiple]);
}, [selectedFiles.length, disabledState, maxFileCount, enableMultiple]);
/**
* *getFileData()
@ -220,35 +208,38 @@ export const FilePicker = ({
const fileSize = formatFileSize(rejectedFileSize);
if (code === errorType.MIN_SIZE) {
return `File size ${fileSize} is too small. Minimum size is ${formatFileSize(parsedMinSize)}`;
return `File size ${fileSize} is too small. Minimum size is ${formatFileSize(minSize)}`;
}
if (code === errorType.MAX_SIZE) {
return `File size ${fileSize} is too large. Maximum size is ${formatFileSize(parsedMaxSize)}`;
return `File size ${fileSize} is too large. Maximum size is ${formatFileSize(maxSize)}`;
}
return message;
};
useEffect(() => {
if (isInitialRender.current) return;
if (acceptedFiles.length === 0 && selectedFiles.length === 0) {
onComponentOptionChanged(component, 'file', [], id);
setExposedVariable('file', []);
// onComponentOptionChanged(component, 'file', [], id);
}
if (acceptedFiles.length !== 0 && onEvent) {
const fileData = parsedEnableMultiple ? [...selectedFiles] : [];
if (acceptedFiles.length !== 0 && fireEvent) {
const fileData = enableMultiple ? [...selectedFiles] : [];
if (parseContent) {
onComponentOptionChanged(component, 'isParsing', true, id);
setExposedVariable('isParsing', true);
// onComponentOptionChanged(component, 'isParsing', true, id);
}
acceptedFiles.map((acceptedFile) => {
const acceptedFileData = fileReader(acceptedFile);
acceptedFileData.then((data) => {
if (fileData.length < parsedMaxFileCount) {
if (fileData.length < maxFileCount) {
fileData.push(data);
}
});
});
onEvent('onFileSelected', filePickerEvents, { component })
fireEvent('onFileSelected', id, 'canvas', { component })
.then(() => {
setAccepted(true);
// eslint-disable-next-line no-unused-vars
@ -256,15 +247,17 @@ export const FilePicker = ({
setTimeout(() => {
setShowSelectedFiles(true);
setAccepted(false);
onComponentOptionChanged(component, 'isParsing', false, id);
setExposedVariable('isParsing', false);
// onComponentOptionChanged(component, 'isParsing', false, id);
resolve();
}, 600);
});
})
.then(() => onEvent('onFileLoaded', filePickerEvents, { component }))
.then(() => fireEvent('onFileLoaded', id, 'canvas', { component }))
.then(() => {
setSelectedFiles(fileData);
onComponentOptionChanged(component, 'file', fileData, id);
setExposedVariable('file', fileData);
// onComponentOptionChanged(component, 'file', fileData, id);
});
}
@ -288,20 +281,32 @@ export const FilePicker = ({
copy.splice(index, 1);
return copy;
});
onEvent('onFileDeselected', filePickerEvents);
fireEvent('onFileDeselected', id);
};
useEffect(() => {
isInitialRender.current = false;
if (selectedFiles.length === 0) {
setShowSelectedFiles(false);
}
onComponentOptionChanged(component, 'file', selectedFiles, id);
setExposedVariable('clearFiles', async function () {
setSelectedFiles([]);
});
setExposedVariable('file', selectedFiles);
// onComponentOptionChanged(component, 'file', selectedFiles, id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedFiles]);
useEffect(() => {
const exposedVariables = {
clearFiles: async function () {
setSelectedFiles([]);
},
file: [],
};
setExposedVariables(exposedVariables);
setShowSelectedFiles(false);
isInitialRender.current = false;
}, []);
return (
<section>
<div className="container" {...getRootProps({ style, className: 'dropzone' })} data-cy={dataCy}>
@ -342,12 +347,12 @@ export const FilePicker = ({
)}
<FilePicker.Signifiers
signifier={isDragAccept && !(selectedFiles.length === parsedMaxFileCount)}
signifier={isDragAccept && !(selectedFiles.length === maxFileCount)}
feedback={'All files will be accepted'}
cls="text-lime mt-3"
/>
<FilePicker.Signifiers
signifier={isDragAccept && selectedFiles.length === parsedMaxFileCount}
signifier={isDragAccept && selectedFiles.length === maxFileCount}
feedback={'Max file reached!'}
cls="text-red mt-3"
/>

View file

@ -77,7 +77,6 @@ export const Form = function Form(props) {
fireEvent('onInvalid');
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@ -112,7 +111,7 @@ export const Form = function Form(props) {
}, [advanced]);
useEffect(() => {
setUIComponents(generateUIComponents(JSONSchema, advanced));
setUIComponents(generateUIComponents(JSONSchema, advanced, component.name));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(JSONSchema), advanced]);
@ -142,7 +141,8 @@ export const Form = function Form(props) {
} else {
Object.keys(childComponents ?? {}).forEach((childId) => {
if (childrenData[childId]?.name) {
formattedChildData[childrenData[childId].name] = { ...omit(childrenData[childId], 'name'), id: childId };
const componentName = childComponents?.[childId]?.component?.name;
formattedChildData[componentName] = { ...omit(childrenData[childId], 'name'), id: childId };
childValidation = childValidation && (childrenData[childId]?.isValid ?? true);
}
});
@ -247,7 +247,7 @@ export const Form = function Form(props) {
) : (
<fieldset disabled={disabledState}>
{!advanced && (
<>
<div className={'json-form-wrapper-disabled'}>
<SubContainer
parentComponent={component}
containerCanvasWidth={width}
@ -270,7 +270,7 @@ export const Form = function Form(props) {
parentRef={parentRef}
currentLayout={currentLayout}
/>
</>
</div>
)}
{advanced &&
uiComponents?.map((item, index) => {
@ -287,8 +287,8 @@ export const Form = function Form(props) {
'ToggleSwitch',
'ToggleSwitchV2',
].includes(uiComponents?.[index + 1]?.component)
? `json-form-wrapper`
: `json-form-wrapper form-label-restricted`
? `json-form-wrapper json-form-wrapper-disabled`
: `json-form-wrapper json-form-wrapper-disabled form-label-restricted`
}
key={index}
>

View file

@ -1,5 +1,7 @@
import { componentTypes } from '@/Editor/WidgetManager/components';
export function generateUIComponents(JSONSchema, advanced) {
import { useCurrentStateStore } from '@/_stores/currentStateStore';
export function generateUIComponents(JSONSchema, advanced, componentName) {
if (advanced) {
if (typeof JSONSchema?.properties !== 'object' || JSONSchema?.properties == null) {
return;
@ -11,6 +13,17 @@ export function generateUIComponents(JSONSchema, advanced) {
if (itemType) {
uiComponentsDraft.push(structuredClone(componentTypes.find((component) => component?.component == 'Text')));
//only add if there is a valid item type
} else {
useCurrentStateStore.getState().actions.setErrors({
[componentName]: {
type: 'component',
data: {
message: `JSON Schema consists of invalid input type: ${value?.type}`,
status: 'Failed',
},
},
});
uiComponentsDraft.push(undefined);
}
uiComponentsDraft.push(structuredClone(componentTypes.find((component) => component?.component == itemType)));
});

View file

@ -3,24 +3,11 @@ import React, { useState, useEffect } from 'react';
import * as Icons from '@tabler/icons-react';
import cx from 'classnames';
export const Icon = ({
properties,
styles,
fireEvent,
width,
height,
setExposedVariable,
setExposedVariables,
darkMode,
dataCy,
component,
}) => {
export const Icon = ({ properties, styles, fireEvent, width, height, setExposedVariables, darkMode, dataCy }) => {
const { icon } = properties;
const { iconColor, visibility, boxShadow } = styles;
// eslint-disable-next-line import/namespace
const IconElement = Icons[icon];
const { definition } = component;
const { events = [] } = definition;
const color = iconColor === '#000' ? (darkMode ? '#fff' : '#000') : iconColor;
@ -42,13 +29,10 @@ export const Icon = ({
};
setExposedVariables(exposedVariables);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setIconVisibility]);
}, []);
return (
<div
className={cx('icon-widget', { 'd-none': !showIcon }, { 'cursor-pointer': events.length > 0 })}
data-cy={dataCy}
>
<div className={cx('icon-widget', { 'd-none': !showIcon }, { 'cursor-pointer': false })} data-cy={dataCy}>
<IconElement
color={color}
style={{ width, height, boxShadow, color: iconColor }}

View file

@ -4,7 +4,7 @@ import LazyLoad, { forceCheck } from 'react-lazyload';
import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
export const Image = function Image({
component,
componentName,
height,
properties,
styles,
@ -15,10 +15,7 @@ export const Image = function Image({
}) {
const { source, loadingState, alternativeText, zoomButtons, rotateButton } = properties;
const { visibility, disabledState, borderType, backgroundColor, padding, imageFit, boxShadow } = styles;
const {
definition: { events },
} = component;
const hasOnClickEvent = events.some((event) => event.eventId === 'onClick');
const hasOnClickEvent = false;
const widgetVisibility = visibility ?? true;
const imageRef = useRef(null);
const [imageOffset, setImageOffset] = useState(0);
@ -130,7 +127,7 @@ export const Image = function Image({
return (
<div
data-disabled={disabledState}
data-cy={`draggable-widget-${String(component.name).toLowerCase()}`}
data-cy={`draggable-widget-${String(componentName).toLowerCase()}`}
style={{
display: widgetVisibility ? 'flex' : 'none',
justifyContent: 'center',

View file

@ -1,11 +1,14 @@
import React, { useRef } from 'react';
import { default as BootstrapModal } from 'react-bootstrap/Modal';
import { SubContainer } from '@/Editor/SubContainer';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
import '@/_styles/widgets/kanban.scss';
export const Modal = function Modal({ showModal, setShowModal, kanbanProps }) {
const parentRef = useRef(null);
const { id, containerProps, exposedVariables, component } = kanbanProps;
const exposedVariables = useStore((state) => state.getExposedValueOfComponent(kanbanProps.id), shallow);
const { id, containerProps, component } = kanbanProps;
const renderCloseButton = () => {
return (

View file

@ -106,6 +106,16 @@ export const Listview = function Listview({
);
const filteredData = deepClone(childrenData);
if (filteredData?.[0]) {
// update the name of the component in the data
Object.keys(filteredData?.[0]).forEach((item) => {
const { id } = _.get(filteredData?.[0], item, {});
const oldName = item;
const newName = _.get(childComponents, `${id}.component.name`, '');
if (oldName !== newName) {
_.set(filteredData[0], newName, _.get(filteredData[0], oldName));
_.unset(filteredData[0], oldName);
}
});
Object.keys(filteredData?.[0]).forEach((item) => {
if (!componentNamesSet?.has(item)) {
for (const key in filteredData) {
@ -173,13 +183,17 @@ export const Listview = function Listview({
removeComponent={removeComponent}
listViewItemOptions={{ index }}
exposedVariables={childrenData[index]}
onOptionChange={function ({ component, optionName, value }) {
onOptionChange={function ({ component, optionName, value, componentId }) {
setChildrenData((prevData) => {
const changedData = { [component.name]: { [optionName]: value } };
const existingDataAtIndex = prevData[index] ?? {};
const newDataAtIndex = {
...prevData[index],
[component.name]: { ...existingDataAtIndex[component.name], ...changedData[component.name] },
[component.name]: {
...existingDataAtIndex[component.name],
...changedData[component.name],
id: componentId,
},
};
const newChildrenData = { ...prevData, [index]: newDataAtIndex };
return { ...prevData, ...newChildrenData };

View file

@ -1,5 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useCallback, useEffect } from 'react';
import React, { useState, useCallback, useEffect, useRef } from 'react';
import { GoogleMap, LoadScript, Marker, Autocomplete, Polygon } from '@react-google-maps/api';
import { resolveWidgetFieldValue } from '@/_helpers/utils';
import { darkModeStyles } from './styles';
@ -9,32 +9,30 @@ export const Map = function Map({
id,
width,
height,
component,
darkMode,
onComponentClick,
onComponentOptionChanged,
onComponentOptionsChanged,
onComponentClick = () => {},
onComponentOptionChanged = () => {},
onComponentOptionsChanged = () => {},
styles,
setExposedVariable,
setExposedVariables,
dataCy,
properties,
fireEvent,
}) {
const center = component.definition.properties.initialLocation.value;
const isInitialRender = useRef(true);
const center = properties?.initialLocation ?? { lat: 0, lng: 0 };
const { polygonPoints = [], defaultMarkers = [] } = properties;
const { t } = useTranslation();
const addNewMarkersProp = component.definition.properties.addNewMarkers;
const canAddNewMarkers = addNewMarkersProp ? resolveWidgetFieldValue(addNewMarkersProp.value) : false;
const canAddNewMarkers = properties?.addNewMarkers ?? false;
const canSearch = properties?.canSearch ?? false;
const widgetVisibility = styles?.visibility ?? true;
const disabledState = styles?.disabledState ?? false;
const canSearchProp = component.definition.properties.canSearch;
const canSearch = canSearchProp ? resolveWidgetFieldValue(canSearchProp.value) : false;
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
const disabledState = component.definition.styles?.disabledState?.value ?? false;
const parsedDisabledState =
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState;
// const parsedDisabledState =
// typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState;
let parsedWidgetVisibility = widgetVisibility;
@ -58,6 +56,16 @@ export const Map = function Map({
setMarkers(defaultMarkers);
}, [JSON.stringify(defaultMarkers)]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('center', center);
}, [center]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('markers', defaultMarkers);
}, [defaultMarkers]);
function handleMapClick(e) {
if (!canAddNewMarkers) {
return;
@ -66,11 +74,12 @@ export const Map = function Map({
const lat = e.latLng.lat();
const lng = e.latLng.lng();
const newMarkers = markers;
const newMarkers = [...markers];
newMarkers.push({ lat, lng });
setMarkers(newMarkers);
onComponentOptionChanged(component, 'markers', newMarkers).then(() => fireEvent('onCreateMarker'));
setExposedVariable('markers', newMarkers);
fireEvent('onCreateMarker');
// onComponentOptionChanged(component, 'markers', newMarkers).then(() => fireEvent('onCreateMarker'));
}
function addMapUrlToJson(centerJson) {
@ -88,17 +97,24 @@ export const Map = function Map({
const newCenter = gmap.center?.toJSON();
setMapCenter(newCenter);
onComponentOptionsChanged(component, [
['bounds', bounds],
['center', addMapUrlToJson(newCenter)],
]).then(() => fireEvent('onBoundsChange'));
const exposedVariables = {
bounds,
center: addMapUrlToJson(newCenter),
};
setExposedVariables(exposedVariables);
fireEvent('onBoundsChange');
// onComponentOptionsChanged(component, [
// ['bounds', bounds],
// ['center', addMapUrlToJson(newCenter)],
// ]).then(() => fireEvent('onBoundsChange'));
}
useEffect(() => {
if (isInitialRender.current) return;
const resolvedCenter = resolveWidgetFieldValue(center);
setMapCenter(resolvedCenter);
onComponentOptionsChanged(component, [['center', addMapUrlToJson(resolvedCenter)]]);
setExposedVariable('center', addMapUrlToJson(resolvedCenter));
// onComponentOptionsChanged(component, [['center', addMapUrlToJson(resolvedCenter)]]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [center]);
@ -106,11 +122,14 @@ export const Map = function Map({
const onLoad = useCallback(function onLoad(mapInstance) {
setGmap(mapInstance);
const centerJson = mapInstance.center?.toJSON();
onComponentOptionsChanged(component, [['center', addMapUrlToJson(centerJson)]]);
setExposedVariable('center', addMapUrlToJson(centerJson));
// onComponentOptionsChanged(component, [['center', addMapUrlToJson(centerJson)]]);
});
function handleMarkerClick(index) {
onComponentOptionChanged(component, 'selectedMarker', markers[index]).then(() => fireEvent('onMarkerClick'));
setExposedVariable('selectedMarker', markers[index]);
fireEvent('onMarkerClick');
// onComponentOptionChanged(component, 'selectedMarker', markers[index]).then(() => fireEvent('onMarkerClick'));
}
function onPlaceChanged() {
@ -124,18 +143,27 @@ export const Map = function Map({
}
useEffect(() => {
setExposedVariable('setLocation', async function (lat, lng) {
if (lat && lng) setMapCenter(resolveWidgetFieldValue({ lat, lng }));
});
}, [setMapCenter]);
const resolvedCenter = resolveWidgetFieldValue(center);
const exposedVariables = {
setLocation: async function (lat, lng) {
if (lat && lng) setMapCenter(resolveWidgetFieldValue({ lat, lng }));
},
center: addMapUrlToJson(resolvedCenter),
markers: defaultMarkers,
};
setMapCenter(resolvedCenter);
setExposedVariables(exposedVariables);
isInitialRender.current = false;
}, []);
return (
<div
data-disabled={parsedDisabledState}
data-disabled={disabledState}
style={{ height, display: parsedWidgetVisibility ? '' : 'none', boxShadow: styles.boxShadow }}
onClick={(event) => {
event.stopPropagation();
onComponentClick(id, component, event);
onComponentClick(id);
}}
className="map-widget"
data-cy={dataCy}

View file

@ -45,8 +45,8 @@ export const Modal = function Modal({
const parentRef = useRef(null);
const controlBoxRef = useRef(null);
const isInitialRender = useRef(true);
const title = properties.title ?? '';
const titleAlignment = properties.titleAlignment ?? 'left';
const size = properties.size ?? 'lg';
/**** Start - Logic to reset the zIndex of modal control box ****/
@ -251,6 +251,7 @@ export const Modal = function Modal({
parentRef,
id,
title,
titleAlignment,
hideTitleBar,
hideCloseButton,
hideModal,
@ -288,6 +289,7 @@ const Component = ({ children, ...restProps }) => {
parentRef,
id,
title,
titleAlignment,
hideTitleBar,
hideCloseButton,
hideModal,
@ -310,7 +312,14 @@ const Component = ({ children, ...restProps }) => {
)}
{!hideTitleBar && (
<BootstrapModal.Header style={{ ...customStyles.modalHeader }} data-cy={`modal-header`}>
<BootstrapModal.Title id="contained-modal-title-vcenter" data-cy={`modal-title`}>
<BootstrapModal.Title
style={{
textAlign: titleAlignment,
width: '100%',
}}
id="contained-modal-title-vcenter"
data-cy={`modal-title`}
>
{title}
</BootstrapModal.Title>
{!hideCloseButton && (

View file

@ -25,23 +25,21 @@ const DropdownIndicator = ({ isOpen, toggleDropdown }) => {
export const Multiselect = function Multiselect({
id,
component,
height,
properties,
styles,
exposedVariables,
setExposedVariable,
setExposedVariables,
onComponentClick,
darkMode,
fireEvent,
componentName,
dataCy,
}) {
const { label, value, values, display_values, showAllOption } = properties;
const { borderRadius, visibility, disabledState, boxShadow } = styles;
const [selected, setSelected] = useState([]);
const [searched, setSearched] = useState('');
const [isOpen, setIsOpen] = useState(false);
let selectOptions = [];
try {
@ -54,40 +52,15 @@ export const Multiselect = function Multiselect({
console.log(err);
}
const handleDropdownOpen = () => {
setIsOpen(true);
};
// useEffect(() => {
// let newValues = [];
const handleDropdownClose = () => {
setIsOpen(false);
};
// if (_.intersection(values, value)?.length === value?.length) newValues = value;
const toggleDropdown = () => {
setIsOpen((prevState) => !prevState);
};
useEffect(() => {
const handleClickOutside = (event) => {
if (isOpen && !event.target.closest('.multiselect-widget')) {
handleDropdownClose();
}
};
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [isOpen]);
useEffect(() => {
let newValues = [];
if (_.intersection(values, value)?.length === value?.length) newValues = value;
setExposedVariable('values', newValues);
setSelected(selectOptions.filter((option) => newValues.includes(option.value)));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(values), JSON.stringify(display_values)]);
// setExposedVariable('values', newValues);
// setSelected(selectOptions.filter((option) => newValues.includes(option.value)));
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, [JSON.stringify(values), JSON.stringify(display_values)]);
useEffect(() => {
setExposedVariable('values', value);
@ -96,14 +69,9 @@ export const Multiselect = function Multiselect({
}, [JSON.stringify(value), JSON.stringify(display_values)]);
useEffect(() => {
if (value && !selected) {
if (value) {
setSelected(selectOptions.filter((option) => properties.value.includes(option.value)));
}
if (JSON.stringify(exposedVariables.values) === '{}') {
setSelected(selectOptions.filter((option) => properties.value.includes(option.value)));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const onChangeHandler = (items) => {
@ -189,14 +157,14 @@ export const Multiselect = function Multiselect({
style={{ height, display: visibility ? '' : 'none' }}
onClick={(event) => {
event.stopPropagation();
onComponentClick(id, component, event);
onComponentClick(id);
}}
>
<div className="col-auto my-auto d-flex align-items-center">
<label
style={{ marginRight: label ? '1rem' : '', marginBottom: 0 }}
className={`form-label py-1 ${darkMode ? 'text-light' : 'text-secondary'}`}
data-cy={`multiselect-label-${component.name.toLowerCase()}`}
data-cy={`multiselect-label-${componentName.toLowerCase()}`}
>
{label}
</label>
@ -213,22 +181,34 @@ export const Multiselect = function Multiselect({
ItemRenderer={ItemRenderer}
filterOptions={filterOptions}
debounceDuration={0}
isOpen={isOpen}
onMenuOpen={handleDropdownOpen}
onMenuClose={handleDropdownClose}
ArrowRenderer={() => <DropdownIndicator isOpen={isOpen} toggleDropdown={toggleDropdown} />}
// isOpen={isOpen}
// onMenuOpen={handleDropdownOpen}
// onMenuClose={handleDropdownClose}
// ArrowRenderer={() => <DropdownIndicator isOpen={isOpen} toggleDropdown={toggleDropdown} />}
onMenuToggle={(isOpen) => {
/*
This is a hack added so that elememt shows up above the other sibling elements.
This is needed since dropdown is added attached to the widget itself and not the body.
*/
if (!document.querySelector(`.ele-${id}`)) {
return;
}
if (isOpen) {
document.querySelector(`.ele-${id}`).style.zIndex = 3;
// get all instances to handle for listview
const elements = document.querySelectorAll(`[id='${id}']`) || [];
elements.forEach((element) => {
// check if dropdown is open and set z-index
const child = element.querySelector(`.dropdown-container`);
if (child && child.hasAttribute('aria-expanded') && child.getAttribute('aria-expanded') === 'true') {
const listViewParent = child?.closest('.list-item');
if (listViewParent) listViewParent.style.zIndex = 1;
element.style.zIndex = 3;
}
});
} else {
document.querySelector(`.ele-${id}`).style.zIndex = '';
const elements = document.querySelectorAll(`[id='${id}']`) || [];
elements.forEach((element) => {
// check if dropdown is open and unset z-index
const child = element.querySelector(`.dropdown-container`);
if (child && child.hasAttribute('aria-expanded') && child.getAttribute('aria-expanded') === 'false') {
const listViewParent = child?.closest('.list-item');
if (listViewParent) listViewParent.style.zIndex = '';
element.style.zIndex = '';
}
});
}
}}
/>

View file

@ -1,7 +1,5 @@
import { resolveReferences } from '@/_helpers/utils';
import { useCurrentState } from '@/_stores/currentStateStore';
import _, { has, isEmpty, isObject } from 'lodash';
import React, { useState, useEffect, useMemo } from 'react';
import _, { has, isObject } from 'lodash';
import React, { useState, useEffect, useMemo, useRef } from 'react';
import Select from 'react-select';
import './multiselectV2.scss';
import CustomMenuList from '../DropdownV2/CustomMenuList';
@ -14,10 +12,11 @@ import Label from '@/_ui/Label';
const tinycolor = require('tinycolor2');
import { CustomDropdownIndicator, CustomClearIndicator } from '../DropdownV2/DropdownV2';
import { getInputBackgroundColor, getInputBorderColor, getInputFocusedColor } from '../DropdownV2/utils';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
export const MultiselectV2 = ({
id,
component,
height,
properties,
styles,
@ -27,6 +26,8 @@ export const MultiselectV2 = ({
darkMode,
fireEvent,
validate,
validation,
componentName,
width,
}) => {
let {
@ -57,11 +58,11 @@ export const MultiselectV2 = ({
padding,
accentColor,
} = styles;
const isInitialRender = useRef(true);
const [selected, setSelected] = useState([]);
const currentState = useCurrentState();
const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState);
const options = component?.definition?.properties?.options?.value;
const values = component?.definition?.properties?.values?.value;
const options = properties?.options;
const values = properties?.values;
const isMandatory = validation?.mandatory ?? false;
const multiselectRef = React.useRef(null);
const labelRef = React.useRef(null);
const validationData = validate(selected?.length ? selected?.map((option) => option.value) : null);
@ -69,6 +70,7 @@ export const MultiselectV2 = ({
const valueContainerRef = React.useRef(null);
const [visibility, setVisibility] = useState(properties.visibility);
const [isMultiSelectLoading, setIsMultiSelectLoading] = useState(multiSelectLoadingState);
const getResolvedValue = useStore((state) => state.getResolvedValue, shallow);
const [isMultiSelectDisabled, setIsMultiSelectDisabled] = useState(disabledState);
const [isSelectAllSelected, setIsSelectAllSelected] = useState(false);
const [searchInputValue, setSearchInputValue] = useState('');
@ -87,12 +89,12 @@ export const MultiselectV2 = ({
const _options = advanced ? schema : options;
let _selectOptions = Array.isArray(_options)
? _options
.filter((data) => resolveReferences(advanced ? data?.visible : data?.visible?.value, currentState))
.filter((data) => getResolvedValue(advanced ? data?.visible : data?.visible?.value) ?? true)
.map((data) => ({
...data,
label: resolveReferences(data?.label, currentState),
value: resolveReferences(data?.value, currentState),
isDisabled: resolveReferences(advanced ? data?.disable : data?.disable?.value, currentState),
label: getResolvedValue(data?.label),
value: getResolvedValue(data?.value),
isDisabled: getResolvedValue(advanced ? data?.disable : data?.disable?.value) ?? false,
}))
: [];
return _selectOptions;
@ -126,6 +128,10 @@ export const MultiselectV2 = ({
const onChangeHandler = (items, action) => {
setSelected(items);
if (action.action === 'select-option') {
setExposedVariable(
'values',
items.map((item) => item.value)
);
fireEvent('onSelect');
}
};
@ -142,6 +148,7 @@ export const MultiselectV2 = ({
}, [advanced, JSON.stringify(schema), JSON.stringify(values)]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable(
'selectedOptions',
Array.isArray(selected) && selected?.map(({ label, value }) => ({ label, value }))
@ -154,15 +161,34 @@ export const MultiselectV2 = ({
}, [JSON.stringify(selected), selectOptions]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('label', label);
setExposedVariable('isVisible', properties.visibility);
setExposedVariable('isLoading', multiSelectLoadingState);
setExposedVariable('isDisabled', disabledState);
setExposedVariable('isMandatory', isMandatory);
setExposedVariable('isValid', isValid);
}, [label]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [label, properties.visibility, multiSelectLoadingState, disabledState, isMandatory, isValid]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isVisible', properties.visibility);
}, [properties.visibility]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isLoading', multiSelectLoadingState);
}, [multiSelectLoadingState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isDisabled', disabledState);
}, [disabledState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isMandatory', isMandatory);
}, [isMandatory]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isValid', isValid);
}, [isValid]);
useEffect(() => {
const exposedVariables = {
@ -178,8 +204,17 @@ export const MultiselectV2 = ({
setDisable: async function (value) {
setIsMultiSelectDisabled(value);
},
label: label,
isVisible: properties.visibility,
isLoading: multiSelectLoadingState,
isDisabled: disabledState,
isMandatory: isMandatory,
isValid: isValid,
selectedOptions: Array.isArray(selected) && selected?.map(({ label, value }) => ({ label, value })),
options: Array.isArray(selectOptions) && selectOptions?.map(({ label, value }) => ({ label, value })),
};
setExposedVariables(exposedVariables);
isInitialRender.current = false;
}, []);
useEffect(() => {
@ -216,7 +251,7 @@ export const MultiselectV2 = ({
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectOptions, selected, setSelected]);
}, [selectOptions, selected]);
const onSearchTextChange = (searchText, actionProps) => {
if (actionProps.action === 'input-change') {
@ -370,7 +405,7 @@ export const MultiselectV2 = ({
<>
<div
ref={multiselectRef}
data-cy={`label-${String(component.name).toLowerCase()} `}
data-cy={`label-${String(componentName).toLowerCase()} `}
className={cx('multiselect-widget', 'd-flex', {
[alignment === 'top' &&
((labelWidth != 0 && label?.length != 0) || (auto && labelWidth == 0 && label && label?.length != 0))
@ -387,7 +422,7 @@ export const MultiselectV2 = ({
width: '100%',
}}
onMouseDown={(event) => {
onComponentClick(id, component, event);
onComponentClick(id);
// This following line is needed because sometimes after clicking on canvas then also dropdown remains selected
useEditorStore.getState().actions.setHoveredComponent('');
}}
@ -451,7 +486,7 @@ export const MultiselectV2 = ({
setIsSelectAllSelected={setIsSelectAllSelected}
setSelected={setSelected}
iconColor={iconColor}
optionsLoadingState={optionsLoadingState}
optionsLoadingState={optionsLoadingState && advanced}
darkMode={darkMode}
fireEvent={() => fireEvent('onSelect')}
menuPlacement="auto"

View file

@ -3,24 +3,28 @@ import './numberinput.scss';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import * as Icons from '@tabler/icons-react';
import Loader from '@/ToolJetUI/Loader/Loader';
import { resolveWidgetFieldValue } from '@/_helpers/utils';
const tinycolor = require('tinycolor2');
import Label from '@/_ui/Label';
import { useGridStore } from '@/_stores/gridStore';
export const NumberInput = function NumberInput({
id,
height,
properties,
validate,
styles,
setExposedVariable,
setExposedVariables,
fireEvent,
component,
darkMode,
dataCy,
isResizing,
validation,
componentName,
}) {
const isInitialRender = useRef(true);
const { loadingState, disabledState, label, placeholder } = properties;
const isResizing = useGridStore((state) => state.resizingComponentId === id);
const {
padding,
borderRadius,
@ -38,10 +42,9 @@ export const NumberInput = function NumberInput({
} = styles;
const textColor = darkMode && ['#232e3c', '#000000ff'].includes(styles.textColor) ? '#CFD3D8' : styles.textColor;
const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value) ?? false;
const minValue = resolveWidgetFieldValue(component?.definition?.validation?.minValue?.value) ?? null;
const maxValue = resolveWidgetFieldValue(component?.definition?.validation?.maxValue?.value) ?? null;
const isMandatory = validation?.mandatory ?? false;
const minValue = validation?.minValue ?? null;
const maxValue = validation?.maxValue ?? null;
const [visibility, setVisibility] = useState(properties.visibility);
const [loading, setLoading] = useState(loadingState);
const [showValidationError, setShowValidationError] = useState(false);
@ -56,6 +59,7 @@ export const NumberInput = function NumberInput({
const _width = (width / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('label', label);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [label]);
@ -79,6 +83,7 @@ export const NumberInput = function NumberInput({
};
useEffect(() => {
if (isInitialRender.current) return;
if (!isNaN(value)) {
setExposedVariable('value', value);
}
@ -86,54 +91,103 @@ export const NumberInput = function NumberInput({
}, [value]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isMandatory', isMandatory);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMandatory]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isLoading', loading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loading]);
useEffect(() => {
setExposedVariable('setLoading', async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.loadingState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isVisible', visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibility]);
useEffect(() => {
setExposedVariable('setVisibility', async function (state) {
setVisibility(state);
setExposedVariable('isVisible', state);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.visibility]);
useEffect(() => {
setExposedVariable('setDisable', async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disabledState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isDisabled', disable);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disable]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isValid', isValid);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isValid]);
useEffect(() => {
disable !== disabledState && setDisable(disabledState);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disabledState]);
useEffect(() => {
visibility !== properties.visibility && setVisibility(properties.visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.visibility]);
useEffect(() => {
loading !== loadingState && setLoading(loadingState);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loadingState]);
useEffect(() => {
const exposedVariables = {
setFocus: async function () {
inputRef.current.focus();
},
setBlur: async function () {
inputRef.current.blur();
},
setText: async function (text) {
if (text) {
const newValue = Number(parseFloat(text));
setValue(newValue);
setExposedVariable('value', text);
fireEvent('onChange');
}
},
clear: async function () {
setValue('');
setExposedVariable('value', '');
fireEvent('onChange');
},
setLoading: async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
},
setVisibility: async function (state) {
setVisibility(state);
setExposedVariable('isVisible', state);
},
setDisable: async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
},
label: label,
isMandatory: isMandatory,
isLoading: loading,
isVisible: visibility,
isDisabled: disable,
isValid: isValid,
};
if (!isNaN(value)) {
exposedVariables.value = value;
}
setExposedVariables(exposedVariables);
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (labelRef?.current) {
const absolutewidth = labelRef?.current?.getBoundingClientRect()?.width;
setLabelWidth(absolutewidth);
} else setLabelWidth(0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
isResizing,
@ -148,11 +202,6 @@ export const NumberInput = function NumberInput({
alignment,
]);
useEffect(() => {
setExposedVariable('isValid', isValid);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isValid]);
const computedStyles = {
height: height == 36 ? (padding == 'default' ? '36px' : '40px') : padding == 'default' ? height : height + 4,
borderRadius: `${borderRadius}px`,
@ -206,18 +255,6 @@ export const NumberInput = function NumberInput({
fireEvent('onChange');
}
};
useEffect(() => {
disable !== disabledState && setDisable(disabledState);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disabledState]);
useEffect(() => {
visibility !== properties.visibility && setVisibility(properties.visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.visibility]);
useEffect(() => {
loading !== loadingState && setLoading(loadingState);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loadingState]);
const handleIncrement = (e) => {
e.preventDefault(); // Prevent the default button behavior (form submission, page reload)
@ -238,29 +275,6 @@ export const NumberInput = function NumberInput({
fireEvent('onChange');
}
};
useEffect(() => {
setExposedVariable('setFocus', async function () {
inputRef.current.focus();
});
setExposedVariable('setBlur', async function () {
inputRef.current.blur();
});
setExposedVariable('setText', async function (text) {
if (text) {
const newValue = Number(parseFloat(text));
setValue(newValue);
setExposedVariable('value', text);
fireEvent('onChange');
}
});
setExposedVariable('clear', async function () {
setValue('');
setExposedVariable('value', '');
fireEvent('onChange');
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const loaderStyle = {
right:
@ -286,7 +300,7 @@ export const NumberInput = function NumberInput({
return (
<>
<div
data-cy={`label-${String(component.name).toLowerCase()}`}
data-cy={`label-${String(componentName).toLowerCase()}`}
className={`text-input tj-number-input-widget d-flex ${
defaultAlignment === 'top' &&
((width != 0 && label && label?.length != 0) || (auto && width == 0 && label && label?.length != 0))
@ -358,6 +372,7 @@ export const NumberInput = function NumberInput({
data-cy={dataCy}
min={minValue}
max={maxValue}
autoComplete="off"
onKeyUp={(e) => {
if (e.key === 'Enter') {
setValue(e.target.value);
@ -440,7 +455,7 @@ export const NumberInput = function NumberInput({
</div>
{showValidationError && visibility && (
<div
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
data-cy={`${String(componentName).toLowerCase()}-invalid-feedback`}
style={{
color: errTextColor !== '#D72D39' ? errTextColor : 'var(--status-error-strong)',
textAlign: direction == 'left' && 'end',

View file

@ -4,9 +4,11 @@ import { Document, Page } from 'react-pdf/dist/esm/entry.webpack';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css'; // Required to fix duplicate text appearing at the bottom from the previous page
import { debounce } from 'lodash';
import PasswordResponses from 'react-pdf/dist/cjs/PasswordResponses';
require('pdfjs-dist/build/pdf.worker.entry.js');
// The above line is required to fix the issue of pdf becoming black when resizing
export const PDF = React.memo(({ styles, properties, width, height, component, dataCy }) => {
const pdfName = component.name;
export const PDF = React.memo(({ styles, properties, width, height, componentName, dataCy }) => {
const { visibility, boxShadow } = styles;
const { url, scale, pageControls, showDownloadOption } = properties;
const [numPages, setNumPages] = useState(null);
@ -17,6 +19,7 @@ export const PDF = React.memo(({ styles, properties, width, height, component, d
const [error, setError] = useState(true);
const [pageLoading, setPageLoading] = useState(true);
const [hasButtonClicked, setButtonClick] = useState(false);
const [isPasswordPromptClosed, setIsPasswordPromptClosed] = useState(false);
const onDocumentLoadSuccess = async (document) => {
const { numPages: nextNumPages } = document;
@ -60,6 +63,10 @@ export const PDF = React.memo(({ styles, properties, width, height, component, d
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [numPages, options]);
useEffect(() => {
setIsPasswordPromptClosed(false);
}, [url]);
const updatePage = useCallback(
(offset) => {
const { offsetTop } = pageRef.current[pageNumber + offset - 1];
@ -82,25 +89,74 @@ export const PDF = React.memo(({ styles, properties, width, height, component, d
width: '15px',
height: '15px',
};
const renderPDF = () => (
<Document
file={url}
onLoadSuccess={onDocumentLoadSuccess}
onLoadError={onDocumentLoadError}
className="pdf-document"
>
{Array.from(new Array(numPages), (el, index) => (
<Page
pageNumber={index + 1}
width={scale ? width - 12 : undefined}
height={scale ? undefined : height}
key={`page_${index + 1}`}
inputRef={(el) => (pageRef.current[index] = el)}
/>
))}
</Document>
);
function onPassword(callback, reason) {
function callbackProxy(password) {
setIsPasswordPromptClosed(false);
if (password === null) {
setIsPasswordPromptClosed(true);
return;
}
callback(password);
}
switch (reason) {
case PasswordResponses.NEED_PASSWORD: {
const password = prompt('Enter the password to open this PDF file.');
callbackProxy(password);
break;
}
case PasswordResponses.INCORRECT_PASSWORD: {
const password = prompt('Invalid password. Please try again.');
callbackProxy(password);
break;
}
default:
}
}
const renderPDF = () => {
if (isPasswordPromptClosed)
return (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' }}>
<p style={{ marginBottom: '6px' }}>Password prompt closed</p>
<button
class="pdf-retry-button"
data-cy="draggable-widget-button3"
type="default"
onClick={() => setIsPasswordPromptClosed(false)}
>
<div>
<div>
<span>
<p class="tj-text-sm">Retry</p>
</span>
</div>
</div>
</button>
</div>
);
return (
<Document
file={url}
onLoadSuccess={onDocumentLoadSuccess}
onLoadError={onDocumentLoadError}
onPassword={onPassword}
className="pdf-document"
>
{Array.from(new Array(numPages), (el, index) => (
<Page
pageNumber={index + 1}
width={scale ? width - 12 : undefined}
height={scale ? undefined : height}
key={`page_${index + 1}`}
inputRef={(el) => (pageRef.current[index] = el)}
/>
))}
</Document>
);
};
async function downloadFile(url, pdfName) {
const pdf = await fetch(url);
const pdfBlog = await pdf.blob();
@ -168,7 +224,7 @@ export const PDF = React.memo(({ styles, properties, width, height, component, d
<div
className="download-icon-outer-wrapper text-dark"
style={downloadIconOuterWrapperStyles}
onClick={() => downloadFile(url, pdfName)}
onClick={() => downloadFile(url, componentName)}
>
<img
src="assets/images/icons/download.svg"

View file

@ -1,26 +1,20 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
export const Pagination = ({
id,
height,
properties,
styles,
exposedVariables,
setExposedVariable,
fireEvent,
darkMode,
dataCy,
width,
}) => {
const isInitialRender = useRef(true);
const { visibility, disabledState, boxShadow } = styles;
const [currentPage, setCurrentPage] = useState(() => properties?.defaultPageIndex ?? 1);
useEffect(() => {
if (exposedVariables.currentPageIndex === null) setExposedVariable('currentPageIndex', currentPage);
if (exposedVariables.totalPages === null) setExposedVariable('totalPages', properties.numberOfPages);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [exposedVariables]);
const pageChanged = (number) => {
setCurrentPage(number);
setExposedVariable('currentPageIndex', number);
@ -49,7 +43,13 @@ export const Pagination = ({
useEffect(() => {
if (properties.defaultPageIndex) {
pageChanged(properties.defaultPageIndex);
if (!isInitialRender.current) {
pageChanged(properties.defaultPageIndex);
} else {
setCurrentPage(properties.defaultPageIndex);
setExposedVariable('currentPageIndex', properties.defaultPageIndex);
isInitialRender.current = false;
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.defaultPageIndex]);

View file

@ -4,7 +4,8 @@ import * as Icons from '@tabler/icons-react';
import Loader from '@/ToolJetUI/Loader/Loader';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import Label from '@/_ui/Label';
import { useEditorStore } from '@/_stores/editorStore';
import useStore from '@/AppBuilder/_stores/store';
import { useGridStore } from '@/_stores/gridStore';
export const PasswordInput = function PasswordInput({
height,
@ -12,16 +13,17 @@ export const PasswordInput = function PasswordInput({
properties,
styles,
setExposedVariable,
setExposedVariables,
fireEvent,
component,
darkMode,
dataCy,
isResizing,
validation,
componentName,
id,
}) {
const textInputRef = useRef();
const labelRef = useRef();
const isInitialRender = useRef(true);
const { loadingState, disabledState, label, placeholder } = properties;
const {
padding,
@ -40,13 +42,14 @@ export const PasswordInput = function PasswordInput({
accentColor,
} = styles;
const components = useStore((state) => state.getCurrentPageComponents() || {});
const isMandatory = validation?.mandatory ?? false;
const isResizing = useGridStore((state) => state.resizingComponentId === id);
const [disable, setDisable] = useState(disabledState || loadingState);
const [passwordValue, setPasswordValue] = useState(properties.value);
const [visibility, setVisibility] = useState(properties.visibility);
const { isValid, validationError } = validate(passwordValue);
const [showValidationError, setShowValidationError] = useState(false);
const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value);
const [labelWidth, setLabelWidth] = useState(0);
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
const [iconVisibility, setIconVisibility] = useState(false);
@ -55,7 +58,6 @@ export const PasswordInput = function PasswordInput({
const tinycolor = require('tinycolor2');
const _width = (width / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value
console.log('style---', styles);
const computedStyles = {
height: height == 36 ? (padding == 'default' ? '36px' : '40px') : padding == 'default' ? height : height + 4,
borderRadius: `${borderRadius}px`,
@ -70,7 +72,11 @@ export const PasswordInput = function PasswordInput({
padding: styles?.iconVisibility ? '8px 10px 8px 29px' : '8px 10px 8px 10px',
overflow: 'hidden',
textOverflow: 'ellipsis',
color: textColor !== '#1B1F24' ? textColor : disable || loading ? 'var(--text-disabled)' : 'var(--text-primary)',
color: !['#11181C', '#1B1F24'].includes(textColor)
? textColor
: disable || loading
? 'var(--text-disabled)'
: 'var(--text-primary)',
borderColor: isFocused
? accentColor != '4368E3'
? accentColor
@ -104,6 +110,7 @@ export const PasswordInput = function PasswordInput({
};
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('label', label);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [label]);
@ -146,92 +153,93 @@ export const PasswordInput = function PasswordInput({
}, [loadingState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isValid', isValid);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isValid]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isMandatory', isMandatory);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMandatory]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isLoading', loading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loading]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isVisible', visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibility]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isDisabled', disable);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disable]);
useEffect(() => {
if (isInitialRender.current) return;
setPasswordValue(properties.value);
setExposedVariable('value', properties?.value ?? '');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.value]);
useEffect(() => {
setExposedVariable('setFocus', async function () {
textInputRef.current.focus();
});
setExposedVariable('setBlur', async function () {
textInputRef.current.blur();
});
const exposedVariables = {
setFocus: async function () {
textInputRef.current.focus();
},
setBlur: async function () {
textInputRef.current.blur();
},
setText: async function (text) {
setPasswordValue(text);
setExposedVariable('value', text);
fireEvent('onChange');
},
clear: async function () {
setPasswordValue('');
setExposedVariable('value', '');
fireEvent('onChange');
},
setLoading: async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
},
setVisibility: async function (state) {
setVisibility(state);
setExposedVariable('isVisible', state);
},
setDisable: async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
},
label: label,
isValid: isValid,
isMandatory: isMandatory,
isLoading: loading,
isVisible: visibility,
isDisabled: disable,
value: properties?.value ?? '',
};
setPasswordValue(properties.value ?? '');
setExposedVariables(exposedVariables);
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
setExposedVariable('setText', async function (text) {
setPasswordValue(text);
setExposedVariable('value', text);
fireEvent('onChange');
});
setExposedVariable('clear', async function () {
setPasswordValue('');
setExposedVariable('value', '');
fireEvent('onChange');
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setPasswordValue]);
const iconName = styles.icon; // Replace with the name of the icon you want
// eslint-disable-next-line import/namespace
const IconElement = Icons[iconName] == undefined ? Icons['IconHome2'] : Icons[iconName];
// eslint-disable-next-line import/namespace
useEffect(() => {
setExposedVariable('isMandatory', isMandatory);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMandatory]);
useEffect(() => {
setExposedVariable('isLoading', loading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loading]);
useEffect(() => {
setExposedVariable('setLoading', async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.loadingState]);
useEffect(() => {
setExposedVariable('isVisible', visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibility]);
useEffect(() => {
setExposedVariable('setVisibility', async function (state) {
setVisibility(state);
setExposedVariable('isVisible', state);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.visibility]);
useEffect(() => {
setExposedVariable('setDisable', async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disabledState]);
useEffect(() => {
setExposedVariable('isDisabled', disable);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disable]);
const currentPageId = useEditorStore.getState().currentPageId;
const components = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components || {};
const isChildOfForm = Object.keys(components).some((key) => {
if (key == id) {
const { parent } = components[key].component;
@ -249,7 +257,7 @@ export const PasswordInput = function PasswordInput({
const renderInput = () => (
<>
<div
data-cy={`label-${String(component.name).toLowerCase()}`}
data-cy={`label-${String(componentName).toLowerCase()}`}
className={`text-input d-flex ${
defaultAlignment === 'top' &&
((width != 0 && label && label?.length != 0) || (auto && width == 0 && label && label?.length != 0))
@ -350,6 +358,7 @@ export const PasswordInput = function PasswordInput({
!isValid && showValidationError ? 'is-invalid' : ''
} validation-without-icon `}
ref={textInputRef}
autoComplete="new-password"
onKeyUp={(e) => {
if (e.key === 'Enter') {
setPasswordValue(e.target.value);
@ -385,7 +394,7 @@ export const PasswordInput = function PasswordInput({
</div>
{showValidationError && visibility && (
<div
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
data-cy={`${String(componentName).toLowerCase()}-invalid-feedback`}
style={{
color: errTextColor !== '#D72D39' ? errTextColor : 'var(--status-error-strong)',
textAlign: direction == 'left' && 'end',
@ -400,7 +409,13 @@ export const PasswordInput = function PasswordInput({
</>
);
const renderContainer = (children) => {
return !isChildOfForm ? <form autoComplete="off">{children}</form> : <div>{children}</div>;
return !isChildOfForm ? (
<form onSubmit={(e) => e.preventDefault()} autoComplete="off">
{children}
</form>
) : (
<div>{children}</div>
);
};
return renderContainer(renderInput());

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import QrReader from 'react-qr-reader';
import ErrorModal from './ErrorModal';

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
export const RadioButton = function RadioButton({
@ -12,6 +12,7 @@ export const RadioButton = function RadioButton({
darkMode,
dataCy,
}) {
const isInitialRender = useRef(true);
const { label, value, values, display_values } = properties;
const { visibility, disabledState, activeColor, boxShadow } = styles;
const textColor = darkMode && styles.textColor === '#000' ? '#fff' : styles.textColor;
@ -36,6 +37,12 @@ export const RadioButton = function RadioButton({
fireEvent('onSelectionChange');
}
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('value', value);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
const exposedVariables = {
value: value,
@ -44,8 +51,9 @@ export const RadioButton = function RadioButton({
},
};
setExposedVariables(exposedVariables);
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value, setValue]);
}, []);
return (
<div

View file

@ -3,6 +3,7 @@ import Slider from 'rc-slider';
import 'rc-slider/assets/index.css';
export const RangeSlider = function RangeSlider({ height, properties, styles, setExposedVariable, fireEvent, dataCy }) {
const isInitialRender = useRef(true);
const { value, min, max, enableTwoHandle } = properties;
const { trackColor, handleColor, lineColor, visibility, boxShadow } = styles;
const sliderRef = useRef(null);
@ -23,12 +24,14 @@ export const RangeSlider = function RangeSlider({ height, properties, styles, se
};
useEffect(() => {
if (isInitialRender.current) return;
setSliderValue(singleHandleValue);
setExposedVariable('value', singleHandleValue);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [singleHandleValue]);
useEffect(() => {
if (isInitialRender.current) return;
setRangeValue(twoHandlesArray);
setExposedVariable('value', twoHandlesArray);
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -36,6 +39,10 @@ export const RangeSlider = function RangeSlider({ height, properties, styles, se
useEffect(() => {
setExposedVariable('value', enableTwoHandle ? twoHandlesArray : singleHandleValue);
if (isInitialRender.current) {
enableTwoHandle ? setRangeValue(twoHandlesArray) : setSliderValue(singleHandleValue);
}
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [enableTwoHandle]);

View file

@ -23,6 +23,7 @@ export const Pagination = function Pagination({
useEffect(() => {
setPageCount(autoPageCount);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [autoPageCount]);
useEffect(() => {
@ -43,11 +44,11 @@ export const Pagination = function Pagination({
}
function goToNextPage() {
gotoPage(pageIndex + 1);
gotoPage(Number(pageIndex) + 1);
}
function goToPreviousPage() {
gotoPage(pageIndex - 1);
gotoPage(Number(pageIndex) - 1);
}
if (loadingState) {
@ -63,7 +64,7 @@ export const Pagination = function Pagination({
return (
<div className="pagination-container d-flex h-100 align-items-center custom-gap-4" data-cy="pagination-section">
<div className="d-flex">
{!serverSide && tableWidth > 460 && (
{tableWidth > 460 && (
<ButtonSolid
variant="ghostBlack"
className="tj-text-xsm"
@ -119,27 +120,24 @@ export const Pagination = function Pagination({
className="d-flex align-items-center tj-text-xsm h-100 page-info custom-gap-4"
data-cy={`page-index-details`}
>
{serverSide && <span className="color-slate-11">{pageIndex}</span>}
{!serverSide && (
<>
<input
type="text"
className={`form-control h-100`}
value={pageIndex}
onChange={(event) => {
const value = Number(event.target.value);
if (value <= pageCount) gotoPage(value);
}}
/>
<span
className="font-weight-500 total-page-number"
style={{ width: 'max-content' }}
data-cy={`total-page-number-${autoPageOptions.length || 1}`}
>
of {pageCount || 1}
</span>
</>
)}
<>
<input
type="text"
className={`form-control h-100`}
value={pageIndex}
onChange={(event) => {
const value = Number(event.target.value);
if (value <= pageCount) gotoPage(value);
}}
/>
<span
className="font-weight-500 total-page-number"
style={{ width: 'max-content' }}
data-cy={`total-page-number-${autoPageOptions.length || 1}`}
>
of {pageCount || 1}
</span>
</>
</div>
<div className="d-flex">
<ButtonSolid
@ -153,20 +151,20 @@ export const Pagination = function Pagination({
borderRadius: '6px',
display: 'flex',
justifyContent: 'center',
cursor: (!autoCanNextPage && !serverSide) || !enableNextButton ? 'not-allowed' : 'pointer',
cursor: pageIndex === pageCount || !enableNextButton ? 'not-allowed' : 'pointer',
}}
leftIcon="cheveronright"
fill={`var(--icons-default)`}
iconWidth="14"
size="md"
disabled={(!autoCanNextPage && !serverSide) || !enableNextButton}
disabled={pageIndex === pageCount || !enableNextButton}
onClick={(event) => {
event.stopPropagation();
goToNextPage();
}}
data-cy={`pagination-button-to-next`}
></ButtonSolid>
{!serverSide && tableWidth > 460 && (
{tableWidth > 460 && (
<ButtonSolid
variant="ghostBlack"
className="tj-text-xsm"
@ -178,7 +176,7 @@ export const Pagination = function Pagination({
borderRadius: '6px',
display: 'flex',
justifyContent: 'center',
cursor: !autoCanNextPage && !serverSide ? 'not-allowed' : 'pointer',
cursor: pageIndex === pageCount ? 'not-allowed' : 'pointer',
}}
leftIcon="cheveronrightdouble"
fill={`var(--icons-default)`}
@ -188,7 +186,7 @@ export const Pagination = function Pagination({
event.stopPropagation();
gotoPage(pageCount);
}}
disabled={!autoCanNextPage && !serverSide}
disabled={pageIndex === pageCount}
data-cy={`pagination-button-to-last`}
></ButtonSolid>
)}

View file

@ -147,9 +147,9 @@ const StringColumn = ({
<span
style={{
maxHeight: isMaxRowHeightAuto
? 'auto'
? 'fit-content'
: maxRowHeightValue
? maxRowHeightValue
? `${maxRowHeightValue}px`
: cellSize === 'condensed'
? '39px'
: '45px',

View file

@ -20,6 +20,8 @@ import {
determineJustifyContentValue,
resolveWidgetFieldValue,
} from '@/_helpers/utils';
import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
import { useExportData } from 'react-table-plugins';
import Papa from 'papaparse';
import { Pagination } from './Pagination';
@ -107,7 +109,6 @@ export function Table({
// events,
setProperty,
mode,
exposedVariables,
}) {
const {
color,
@ -150,8 +151,10 @@ export function Table({
isMaxRowHeightAuto,
columnHeaderWrap,
} = loadPropertiesAndStyles(properties, styles, darkMode, component);
const exposedVariables = useStore((state) => state.getExposedValueOfComponent(id), shallow);
const updatedDataReference = useRef([]);
const preSelectRow = useRef(false);
const initialPageCountRef = useRef(null);
const { events: allAppEvents } = useAppInfo();
const tableEvents = allAppEvents.filter((event) => event.target === 'component' && event.sourceId === id);
@ -424,7 +427,7 @@ export function Table({
}
}
tableData = tableData || [];
tableData = _.isArray(tableData) ? tableData : [];
const tableRef = useRef();
@ -575,6 +578,8 @@ export function Table({
highlightSelectedRow,
JSON.stringify(tableActionEvents),
JSON.stringify(tableColumnEvents),
maxRowHeightValue,
isMaxRowHeightAuto,
] // Hack: need to fix
);
@ -661,8 +666,8 @@ export function Table({
data,
defaultColumn,
initialState: { pageIndex: 0, pageSize: 1 },
pageCount: -1,
manualPagination: false,
pageCount: initialPageCountRef.current,
manualPagination: serverSidePagination,
getExportFileBlob,
getExportFileName,
disableSortBy: !enabledSort,
@ -870,6 +875,15 @@ export function Table({
}
}, [clientSidePagination, serverSidePagination, rows, rowsPerPage]);
useEffect(() => {
if (!initialPageCountRef.current && serverSidePagination && data?.length && totalRecords) {
initialPageCountRef.current = Math.ceil(totalRecords / data?.length);
}
if (!serverSidePagination) {
initialPageCountRef.current = Math.ceil(data?.length / rowsPerPage);
}
}, [serverSidePagination, totalRecords, data?.length, rowsPerPage]);
useEffect(() => {
const pageData = page.map((row) => row.original);
if (preSelectRow.current) {
@ -1763,7 +1777,7 @@ export function Table({
serverSide={serverSidePagination}
autoGotoPage={gotoPage}
autoCanNextPage={canNextPage}
autoPageCount={pageCount}
autoPageCount={initialPageCountRef.current}
autoPageOptions={pageOptions}
onPageIndexChanged={onPageIndexChanged}
pageIndex={paginationInternalPageIndex}

File diff suppressed because one or more lines are too long

View file

@ -109,12 +109,12 @@ const Text = ({
<span
style={{
maxHeight: isMaxRowHeightAuto
? 'auto'
? 'fit-content'
: maxRowHeightValue
? maxRowHeightValue - 16 // decreasing 16px for padding fix
? `${maxRowHeightValue}px`
: cellSize === 'condensed'
? '23px'
: '29px',
? '39px'
: '45px',
}}
ref={nonEditableCellValueRef}
>

View file

@ -35,6 +35,12 @@ export default function generateColumnsData({
cellSize,
maxRowHeightValue,
}) {
const isValueRowDependent = (value) => {
return (
(typeof value === 'string' || Array.isArray(value)) && (value.includes('cellValue') || value.includes('rowData'))
);
};
return columnProperties.map((column) => {
if (!column) return;
const columnSize = columnSizes[column?.id] || columnSizes[column?.name] || column.columnSize;
@ -51,8 +57,8 @@ export default function generateColumnsData({
columnType === 'image'
) {
columnOptions.selectOptions = [];
const values = resolveReferences(column.values, []);
const labels = resolveReferences(column.labels, []);
const values = resolveReferences(column.values);
const labels = resolveReferences(column.labels);
if (Array.isArray(labels) && Array.isArray(values)) {
columnOptions.selectOptions = labels.map((label, index) => {
@ -60,13 +66,34 @@ export default function generateColumnsData({
});
}
}
// This is done to ensure that if row dependent properties are not used, the default value is used and performance is not affected
const rowDependentMap = {
cellBackgroundColor: isValueRowDependent(column.cellBackgroundColor),
textColor: isValueRowDependent(column.textColor),
minValue: isValueRowDependent(column.minValue),
maxValue: isValueRowDependent(column.maxValue),
minLength: isValueRowDependent(column.minLength),
maxLength: isValueRowDependent(column.maxLength),
regex: isValueRowDependent(column.regex),
customRule: isValueRowDependent(column.customRule),
isEditable: isValueRowDependent(column.isEditable),
minDate: isValueRowDependent(column.minDate),
maxDate: isValueRowDependent(column.maxDate),
borderRadius: isValueRowDependent(column.borderRadius),
multiselect: false,
};
let useDynamicOptions = false;
if (columnType === 'select' || columnType === 'newMultiSelect') {
columnOptions.selectOptions = [];
const useDynamicOptions = resolveReferences(column?.useDynamicOptions);
useDynamicOptions = resolveReferences(column?.useDynamicOptions, currentState);
if (useDynamicOptions) {
const dynamicOptions = resolveReferences(column?.dynamicOptions || []);
rowDependentMap['multiselect'] = isValueRowDependent(column?.dynamicOptions);
const dynamicOptions = resolveReferences(column?.dynamicOptions || [], currentState);
columnOptions.selectOptions = Array.isArray(dynamicOptions) ? dynamicOptions : [];
} else {
rowDependentMap['multiselect'] = isValueRowDependent(JSON.stringify(column?.options));
const options = column?.options ?? [];
columnOptions.selectOptions =
options?.map((option) => ({
@ -101,6 +128,7 @@ export default function generateColumnsData({
};
}
const width = columnSize || defaultColumn.width;
return {
id: column.id,
Header: resolveReferences(column.name) ?? '',
@ -146,6 +174,14 @@ export default function generateColumnsData({
exposeToCodeHinter((prevState) => ({ ...prevState, ...customResolvables }));
}
cellValue = cellValue === undefined || cellValue === null ? '' : cellValue;
column = { ...column };
Object.keys(rowDependentMap).map((key) => {
column[key] = rowDependentMap[key]
? resolveReferences(cell.column?.[key], currentState, '', { cellValue, rowData })
: column[key];
});
switch (columnType) {
case 'string':
case undefined:
@ -260,9 +296,8 @@ export default function generateColumnsData({
};
const allowedDecimalPlaces = column?.decimalPlaces ?? null;
const removingExcessDecimalPlaces = (cellValue, allowedDecimalPlaces) => {
allowedDecimalPlaces = resolveReferences(allowedDecimalPlaces);
allowedDecimalPlaces = resolveReferences(allowedDecimalPlaces, currentState, 2, { cellValue, rowData });
if (cellValue?.toString()?.includes('.')) {
const splittedCellValue = cellValue?.toString()?.split('.');
const decimalPlacesUnderLimit = splittedCellValue[1].split('').splice(0, allowedDecimalPlaces).join('');
@ -270,7 +305,6 @@ export default function generateColumnsData({
}
return cellValue;
};
cellValue = allowedDecimalPlaces ? removingExcessDecimalPlaces(cellValue, allowedDecimalPlaces) : cellValue;
if (isEditable) {
@ -445,8 +479,26 @@ export default function generateColumnsData({
currentState,
customResolveObjects: { cellValue },
});
const { isValid, validationError } = validationData;
let rowOptions = [];
if ((columnType === 'select' || columnType === 'newMultiSelect') && rowDependentMap['multiselect']) {
if (useDynamicOptions) {
const dynamicOptions = resolveReferences(column?.dynamicOptions || [], currentState, [], {
rowData,
cellValue,
});
rowOptions = Array.isArray(dynamicOptions) ? dynamicOptions : [];
} else {
const options = column?.options ?? [];
rowOptions =
options?.map((option) => ({
label: resolveReferences(option.label, currentState, [], { rowData, cellValue }),
value: resolveReferences(option.value, currentState, [], { rowData, cellValue }),
})) ?? [];
}
}
return (
<div
className="h-100 d-flex align-items-center flex-column justify-content-center"
@ -468,7 +520,7 @@ export default function generateColumnsData({
)}
{['newMultiSelect', 'select'].includes(columnType) && (
<CustomSelect
options={columnOptions.selectOptions}
options={rowOptions.length > 0 ? rowOptions : columnOptions.selectOptions}
value={cellValue}
search={true}
onChange={(value) => {
@ -614,12 +666,23 @@ export default function generateColumnsData({
);
}
case 'datepicker': {
const textColor = resolveReferences(column.textColor, '', { cellValue, rowData });
const isTimeChecked = resolveReferences(column?.isTimeChecked);
const isTwentyFourHrFormatEnabled = resolveReferences(column?.isTwentyFourHrFormatEnabled);
const disabledDates = resolveReferences(column?.disabledDates);
const parseInUnixTimestamp = resolveReferences(column?.parseInUnixTimestamp);
const isDateSelectionEnabled = resolveReferences(column?.isDateSelectionEnabled);
const textColor = resolveReferences(column.textColor, currentState, '', { cellValue, rowData });
const isTimeChecked = resolveReferences(column?.isTimeChecked, currentState, false, { cellValue, rowData });
const isTwentyFourHrFormatEnabled = resolveReferences(
column?.isTwentyFourHrFormatEnabled,
currentState,
false,
{ cellValue, rowData }
);
const disabledDates = resolveReferences(column?.disabledDates, currentState, [], { cellValue, rowData });
const parseInUnixTimestamp = resolveReferences(column?.parseInUnixTimestamp, currentState, '', {
cellValue,
rowData,
});
const isDateSelectionEnabled = resolveReferences(column?.isDateSelectionEnabled, currentState, true, {
cellValue,
rowData,
});
const cellStyles = {
color: textColor ?? '',
};
@ -688,20 +751,30 @@ export default function generateColumnsData({
);
}
case 'link': {
const linkTarget = resolveReferences(column?.linkTarget ?? '{{true}}');
const displayText = column?.displayText ? resolveReferences(column.displayText) : '';
column = {
...column,
linkColor: column?.linkColor ?? '#1B1F24',
underlineColor: column?.underlineColor ?? '#4368E3',
};
const linkTarget = resolveReferences(column?.linkTarget ?? '{{true}}', currentState, '', {
cellValue,
rowData,
});
const displayText = resolveReferences(column?.displayText ?? '{{}}', currentState, '', {
cellValue,
rowData,
});
const linkColor = resolveReferences(column?.linkColor ?? '#1B1F24', currentState, '', {
cellValue,
rowData,
});
const underlineColor = resolveReferences(column.underlineColor ?? '', currentState, '', {
cellValue,
rowData,
});
return (
<div className="h-100 d-flex align-items-center">
<Link
cellValue={cellValue}
linkTarget={linkTarget}
linkColor={column.linkColor}
underlineColor={column.underlineColor}
linkColor={linkColor}
underlineColor={underlineColor}
underline={column.underline}
displayText={displayText}
darkMode={darkMode}

View file

@ -1,7 +1,8 @@
.tj-datepicker-widget {
&.react-datepicker-popper {
z-index: 3;
z-index: 3;
}
border-radius: 6px !important;
border: 1px solid var(--slate5) !important;
box-shadow: 0px 32px 64px -12px rgba(16, 24, 40, 0.14);
@ -35,11 +36,11 @@
border: none !important;
}
}
input[type="time"] {
position: relative;
}
input[type="time"]::-webkit-calendar-picker-indicator {
display: block;
top: 0;
@ -58,17 +59,17 @@
padding: 6px 0px;
border: none;
}
.tj-datepicker-widget-left {
position: absolute;
left: 10px;
}
.tj-datepicker-widget-right {
position: absolute;
right: 10px;
}
.tj-datepicker-widget-arrows {
box-shadow: 0px 1px 0px 0px #0000000B;
border: 1px solid var(--borders-default);
@ -78,87 +79,87 @@
padding: 4px;
background-color: var(--surfaces-surface-01) !important;
}
.react-datepicker__navigation-icon--previous {
right: 0px;
top: 3px;
}
.react-datepicker__navigation-icon--next {
left: 0px;
top: 3px;
}
.react-datepicker {
width: 250px;
border: none;
background-color: var(--surfaces-surface-01);
}
.dark-theme {
.react-datepicker {
background-color: #232e3c;
}
}
.react-datepicker__month {
margin: 6px
}
.react-datepicker__navigation--next {
right: 14px;
}
.react-datepicker__navigation--previous {
left: 14px;
}
.react-datepicker__week,
.react-datepicker__day-names {
display: flex;
justify-content: space-between;
}
.react-datepicker__day-names {
.react-datepicker__day-name {
color: var(--text-placeholder)
}
}
.react-datepicker__day--selected {
border-radius: 8px;
background-color: var(--primary-brand) !important;
color: var(--surfaces-surface-01) !important
}
.react-datepicker__navigation-icon::before {
border-color: #000;
border-width: 1px 1px 0 0;
}
.react-datepicker__day {
font-size: 12px;
font-style: normal;
font-weight: 500;
color: var(--text-primary);
&:hover {
background-color: var(--interactive-overlays-fill-hover);
border-radius: 8px;
}
}
.react-datepicker__day--today {
border: 1px solid var(--primary-brand);
border-radius: 8px;
background-color: var(--surfaces-surface-01);
}
.react-datepicker__day--keyboard-selected {
background-color: var(--surfaces-surface-01);
}
.react-datepicker-time__input {
input {
width: 205px !important;
@ -174,55 +175,58 @@
.react-datepicker__month-container {
float: inherit;
}
.react-datepicker__time-container .react-datepicker__time {
color: var(--text-primary);
background: var(--surfaces-surface-01);
}
.dark-theme {
.react-datepicker-popper,
.tj-datepicker-widget {
background-color: #232e3c;
}
.react-datepicker__time-container,
.react-datepicker__time-container .react-datepicker__time {
background-color: #232e3c;
color: #fff
}
.react-datepicker__header {
background-color: #232e3c;
color: #fff
}
.react-datepicker__month-container {
background-color: #232e3c;
color: #fff;
.react-datepicker__month {
background-color: #232e3c;
.react-datepicker__day {
color: #fff
}
}
}
li.react-datepicker__time-list-item:hover {
background-color: #CCD1D533 !important;
}
select {
color: #fff
}
}
.tj-datepicker-widget-month-selector,
.tj-datepicker-widget-year-selector {
&:hover {
background-color: var(--interactive-overlays-fill-hover);
border-radius: 6px;
background-color: var(--interactive-overlays-fill-hover);
border-radius: 6px;
}
}
@ -247,7 +251,6 @@
.react-datepicker__day,
.react-datepicker__time-name {
margin: 0px;
// width: 32px;
}
.react-datepicker__day-names {
@ -259,7 +262,8 @@
line-height: 20px;
}
.tj-datepicker-widget-month-selector, .tj-datepicker-widget-year-selector {
.tj-datepicker-widget-month-selector,
.tj-datepicker-widget-year-selector {
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
@ -279,7 +283,6 @@
padding: 0px;
height: 24px;
text-align: center;
// margin-right: 6px !important;
color: var(--text-primary);
font-weight: 500;
}
@ -287,9 +290,6 @@
/* Optional: Style when select is focused */
select:focus {
outline: none;
/* Remove default focus outline */
// box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
/* Add a subtle box-shadow when focused */
}
.react-datepicker__triangle {
@ -317,10 +317,6 @@
margin: 0px;
}
.react-datepicker__time-list {
// padding: 4px 6px;
}
.react-datepicker__time-list-item {
height: 28px !important;
color: var(--text-primary) !important;
@ -354,6 +350,7 @@
.react-datepicker__time-box {
display: flex;
ul {
padding: 0px 10px 0px 10px !important;
}
@ -420,10 +417,16 @@
td {
&.isEditable {
&:hover, &:focus-within {
&:hover,
&:focus-within {
.table-column-datepicker-input {
padding-right: 40px;
padding-right: 40px;
}
}
}
}
}
.react-datepicker-popper {
z-index: 4 !important;
}

View file

@ -17,12 +17,13 @@ export const Tabs = function Tabs({
styles,
darkMode,
dataCy,
properties,
}) {
const { tabWidth, boxShadow } = styles;
const { defaultTab } = properties;
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
const disabledState = component.definition.styles?.disabledState?.value ?? false;
const defaultTab = component.definition.properties.defaultTab.value;
// config for tabs. Includes title
const tabs = isExpectedDataType(resolveWidgetFieldValue(component.definition.properties?.tabs?.value), 'array');
let parsedTabs = tabs;
@ -43,7 +44,6 @@ export const Tabs = function Tabs({
// Default tab
let parsedDefaultTab = defaultTab;
parsedDefaultTab = resolveWidgetFieldValue(parsedDefaultTab, 1);
const parsedDisabledState =
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState;
@ -137,7 +137,6 @@ export const Tabs = function Tabs({
}
return true; // Render by default if no specific conditions are met
}
return (
<div
data-disabled={parsedDisabledState}

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import DOMPurify from 'dompurify';
// eslint-disable-next-line import/no-unresolved
import Markdown from 'react-markdown';
@ -11,7 +11,18 @@ const VERTICAL_ALIGNMENT_VS_CSS_VALUE = {
bottom: 'flex-end',
};
export const Text = function Text({ height, properties, fireEvent, styles, darkMode, setExposedVariable, dataCy }) {
let count = 0;
export const Text = function Text({
height,
properties,
fireEvent,
styles,
darkMode,
setExposedVariable,
setExposedVariables,
dataCy,
}) {
let {
textSize,
textColor,
@ -32,13 +43,14 @@ export const Text = function Text({ height, properties, fireEvent, styles, darkM
borderRadius,
isScrollRequired,
} = styles;
const isInitialRender = useRef(true);
const { loadingState, textFormat, disabledState } = properties;
const [text, setText] = useState(() => computeText());
const [visibility, setVisibility] = useState(properties.visibility);
const [isLoading, setLoading] = useState(loadingState);
const [isDisabled, setIsDisabled] = useState(disabledState);
const color = ['#000', '#000000'].includes(textColor) ? (darkMode ? '#fff' : '#000') : textColor;
count = count + 1;
useEffect(() => {
if (visibility !== properties.visibility) setVisibility(properties.visibility);
if (isLoading !== loadingState) setLoading(loadingState);
@ -48,49 +60,63 @@ export const Text = function Text({ height, properties, fireEvent, styles, darkM
}, [properties.visibility, loadingState, disabledState]);
useEffect(() => {
if (isInitialRender.current) return;
const text = computeText();
setText(text);
setExposedVariable('text', text);
}, [properties.text]);
setExposedVariable('setText', async function (text) {
setText(text);
setExposedVariable('text', text);
});
setExposedVariable('clear', async function (text) {
setText('');
setExposedVariable('text', '');
});
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isVisible', properties.visibility);
}, [properties.visibility]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isLoading', loadingState);
}, [loadingState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isDisabled', disabledState);
}, [disabledState]);
setExposedVariable('visibility', async function (value) {
setVisibility(value);
});
setExposedVariable('setVisibility', async function (value) {
setVisibility(value);
});
setExposedVariable('setLoading', async function (value) {
setLoading(value);
});
setExposedVariable('setDisable', async function (value) {
setIsDisabled(value);
});
useEffect(() => {
const exposedVariables = {
text: computeText(),
setText: async function (text) {
setText(text);
setExposedVariable('text', text);
},
clear: async function () {
setText('');
setExposedVariable('text', '');
},
isVisible: properties.visibility,
isLoading: loadingState,
isDisabled: disabledState,
visibility: async function (value) {
setExposedVariable('isVisible', value);
setVisibility(value);
},
setVisibility: async function (value) {
setExposedVariable('isVisible', value);
setVisibility(value);
},
setLoading: async function (value) {
setExposedVariable('isLoading', value);
setLoading(value);
},
setDisable: async function (value) {
setExposedVariable('isDisabled', value);
setIsDisabled(value);
},
};
setExposedVariables(exposedVariables);
setText(text);
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
properties.text,
setText,
setVisibility,
properties.visibility,
loadingState,
disabledState,
setIsDisabled,
setLoading,
]);
}, []);
function computeText() {
return properties.text === 0 || properties.text === false ? properties.text?.toString() : properties.text;

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
export const TextArea = function TextArea({
height,
@ -8,9 +8,11 @@ export const TextArea = function TextArea({
setExposedVariables,
dataCy,
}) {
const isInitialRender = useRef(true);
const [value, setValue] = useState(properties.value);
useEffect(() => {
if (isInitialRender.current) return;
setValue(properties.value);
setExposedVariable('value', properties.value);
@ -27,11 +29,14 @@ export const TextArea = function TextArea({
setValue('');
setExposedVariable('value', '');
},
value: properties.value,
};
setExposedVariables(exposedVariables);
setValue(properties.value);
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setValue]);
}, []);
return (
<textarea

View file

@ -5,24 +5,28 @@ import * as Icons from '@tabler/icons-react';
import Loader from '@/ToolJetUI/Loader/Loader';
const tinycolor = require('tinycolor2');
import Label from '@/_ui/Label';
import { useGridStore } from '@/_stores/gridStore';
export const TextInput = function TextInput({
id,
height,
validate,
properties,
validation,
styles,
setExposedVariable,
setExposedVariables,
fireEvent,
component,
componentName,
darkMode,
dataCy,
isResizing,
validate,
}) {
const isInitialRender = useRef(true);
const textInputRef = useRef();
const labelRef = useRef();
const { loadingState, disabledState, label, placeholder } = properties;
const isResizing = useGridStore((state) => state.resizingComponentId === id);
const isMandatory = validation?.mandatory || false;
const {
padding,
@ -43,10 +47,10 @@ export const TextInput = function TextInput({
const [disable, setDisable] = useState(disabledState || loadingState);
const [value, setValue] = useState(properties.value);
const [visibility, setVisibility] = useState(properties.visibility);
// const isValid = true; // TODO: remove this and uncomment the below line
const { isValid, validationError } = validate(value);
const [showValidationError, setShowValidationError] = useState(false);
const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value);
const [labelWidth, setLabelWidth] = useState(0);
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
const [loading, setLoading] = useState(loadingState);
@ -56,7 +60,11 @@ export const TextInput = function TextInput({
const computedStyles = {
height: height == 36 ? (padding == 'default' ? '36px' : '40px') : padding == 'default' ? height : height + 4,
borderRadius: `${borderRadius}px`,
color: textColor !== '#1B1F24' ? textColor : disable || loading ? 'var(--text-disabled)' : 'var(--text-primary)',
color: !['#1B1F24', '#000', '#000000ff'].includes(textColor)
? textColor
: disable || loading
? 'var(--text-disabled)'
: 'var(--text-primary)',
borderColor: isFocused
? accentColor != '4368E3'
? accentColor
@ -127,6 +135,7 @@ export const TextInput = function TextInput({
]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('label', label);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [label]);
@ -152,29 +161,41 @@ export const TextInput = function TextInput({
}, [isValid]);
useEffect(() => {
setValue(properties.value);
setExposedVariable('value', properties.value);
if (isInitialRender.current) return;
if (properties.value === undefined) {
setValue('');
setExposedVariable('value', '');
} else {
setValue(properties.value);
setExposedVariable('value', properties.value);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.value]);
useEffect(() => {
const exposedVariables = {
setFocus: async function () {
textInputRef.current.focus();
},
setBlur: async function () {
textInputRef.current.blur();
},
disable: async function (value) {
setDisable(value);
},
visibility: async function (value) {
setVisibility(value);
},
};
setExposedVariables(exposedVariables);
if (isInitialRender.current) return;
setExposedVariable('isMandatory', isMandatory);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [isMandatory]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isLoading', loading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loading]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isVisible', visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibility]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isDisabled', disable);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disable]);
useEffect(() => {
const exposedVariables = {
@ -188,63 +209,54 @@ export const TextInput = function TextInput({
setExposedVariable('value', '');
fireEvent('onChange');
},
setFocus: async function () {
textInputRef.current.focus();
},
setBlur: async function () {
textInputRef.current.blur();
},
disable: async function (value) {
setDisable(value);
},
visibility: async function (value) {
setVisibility(value);
},
setVisibility: async function (value) {
setVisibility(value);
setExposedVariable('isVisible', value);
},
setDisable: async function (value) {
setDisable(value);
setExposedVariable('isDisabled', value);
},
setLoading: async function (value) {
setLoading(value);
setExposedVariable('isLoading', value);
},
label: label,
isValid: isValid,
value: properties.value,
isMandatory: isMandatory,
isLoading: loading,
isVisible: visibility,
isDisabled: disable,
};
setValue(properties.value);
setExposedVariables(exposedVariables);
isInitialRender.current = false;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setValue]);
}, []);
const iconName = styles.icon; // Replace with the name of the icon you want
// eslint-disable-next-line import/namespace
const IconElement = Icons[iconName] == undefined ? Icons['IconHome2'] : Icons[iconName];
// eslint-disable-next-line import/namespace
useEffect(() => {
setExposedVariable('isMandatory', isMandatory);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMandatory]);
useEffect(() => {
setExposedVariable('isLoading', loading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loading]);
useEffect(() => {
setExposedVariable('setLoading', async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.loadingState]);
useEffect(() => {
setExposedVariable('isVisible', visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibility]);
useEffect(() => {
setExposedVariable('setVisibility', async function (state) {
setVisibility(state);
setExposedVariable('isVisible', state);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.visibility]);
useEffect(() => {
setExposedVariable('setDisable', async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disabledState]);
useEffect(() => {
setExposedVariable('isDisabled', disable);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disable]);
return (
<>
<div
data-cy={`label-${String(component.name).toLowerCase()} `}
data-cy={`label-${String(componentName).toLowerCase()} `}
className={`text-input d-flex ${
defaultAlignment === 'top' &&
((width != 0 && label?.length != 0) || (auto && width == 0 && label && label?.length != 0))
@ -343,7 +355,7 @@ export const TextInput = function TextInput({
</div>
{showValidationError && visibility && (
<div
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
data-cy={`${String(componentName).toLowerCase()}-invalid-feedback`}
style={{
color: errTextColor,
textAlign: direction == 'left' && 'end',

View file

@ -38,6 +38,10 @@ export const Timer = function Timer({ height, properties = {}, styles, setExpose
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [time]);
useEffect(() => {
setExposedVariable('value', {});
}, []);
useEffect(() => {
intervalId && clearInterval(intervalId);
setState('initial');

View file

@ -1,5 +1,4 @@
import React, { useEffect, useState } from 'react';
import { resolveWidgetFieldValue } from '@/_helpers/utils';
import React, { useEffect, useRef, useState } from 'react';
import Loader from '@/ToolJetUI/Loader/Loader';
import OverflowTooltip from '@/_components/OverflowTooltip';
@ -94,15 +93,18 @@ export const ToggleSwitchV2 = ({
styles,
fireEvent,
setExposedVariable,
setExposedVariables,
dataCy,
component,
validation,
componentName,
validate,
width,
}) => {
const isInitialRender = useRef(true);
const defaultValue = properties.defaultValue ?? false;
const [on, setOn] = useState(Boolean(defaultValue));
const label = properties.label;
const isMandatory = validation?.mandatory ?? false;
const { isValid, validationError } = validate(on);
const [showValidationError, setShowValidationError] = useState(true);
const [loading, setLoading] = useState(properties?.loadingState);
@ -110,8 +112,6 @@ export const ToggleSwitchV2 = ({
const [visibility, setVisibility] = useState(properties.visibility);
const [userInteracted, setUserInteracted] = useState(false);
const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value);
const { toggleSwitchColor, boxShadow, alignment, borderColor } = styles;
const textColor = styles.textColor === '#1B1F24' ? 'var(--text-primary)' : styles.textColor;
@ -124,6 +124,7 @@ export const ToggleSwitchV2 = ({
// Exposing the initially set false value once on load
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('value', defaultValue);
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -152,55 +153,70 @@ export const ToggleSwitchV2 = ({
}, [properties.loadingState]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('label', label);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [label]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isMandatory', isMandatory);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMandatory]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isLoading', loading);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loading]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isVisible', visibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibility]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isDisabled', disable);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disable]);
useEffect(() => {
if (isInitialRender.current) return;
setExposedVariable('isValid', isValid);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isValid]);
useEffect(() => {
setExposedVariable('setLoading', async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.loadingState]);
useEffect(() => {
setExposedVariable('setVisibility', async function (state) {
setVisibility(state);
setExposedVariable('isVisible', state);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.visibility]);
useEffect(() => {
setExposedVariable('setDisable', async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.disabledState]);
const exposedVariables = {
setValue: async function (value) {
setOn(value);
setExposedVariable('value', value);
setUserInteracted(true);
},
setVisibility: async function (state) {
setVisibility(state);
setExposedVariable('isVisible', state);
},
setDisable: async function (disable) {
setDisable(disable);
setExposedVariable('isDisabled', disable);
},
setLoading: async function (loading) {
setLoading(loading);
setExposedVariable('isLoading', loading);
},
label: label,
isMandatory: isMandatory,
isLoading: loading,
isVisible: visibility,
isDisabled: disable,
isValid: isValid,
value: defaultValue,
};
setExposedVariables(exposedVariables);
setOn(defaultValue);
isInitialRender.current = false;
}, []);
useEffect(() => {
setExposedVariable('toggle', async function () {
@ -212,15 +228,6 @@ export const ToggleSwitchV2 = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [on]);
useEffect(() => {
setExposedVariable('setValue', async function (value) {
setOn(value);
setExposedVariable('value', value);
setUserInteracted(true);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const renderInput = () => (
<div
data-disabled={properties.disabledState}
@ -289,7 +296,7 @@ export const ToggleSwitchV2 = ({
{renderInput()}
{showValidationError && isMandatory && userInteracted && !on && visibility && (
<div
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
data-cy={`${String(componentName).toLowerCase()}-invalid-feedback`}
style={{
color: 'var(--status-error-strong)',
fontSize: '11px',

View file

@ -0,0 +1,22 @@
.tj-datepicker-widget {
select {
color: var(--text-primary) !important;
background-color: var(--surfaces-surface-01) !important
}
select option {
color: var(--text-primary) !important;
background-color: var(--surfaces-surface-01) !important
}
}
// .dark-theme {
// select {
// color: #fff !important
// }
// select option {
// color: #fff !important // This needs to be black since this has no effect on mac but has on windows
// }
// }

View file

@ -8,6 +8,7 @@ import {
redirectToErrorPage,
} from './routes';
import { ERROR_TYPES } from './constants';
import useStore from '@/AppBuilder/_stores/store';
/* [* Be cautious: READ THE CASES BEFORE TOUCHING THE CODE. OTHERWISE YOU MAY SEE ENDLESS REDIRECTIONS (AKA ROUTES-BURMUDA-TRIANGLE) *]
What is this function?
@ -133,6 +134,14 @@ export const authorizeUserAndHandleErrors = (workspace_id, workspace_slug, callb
authenticationService
.authorize()
.then((data) => {
useStore.getState().setUser({
email: data.current_user.email,
firstName: data.current_user.first_name,
lastName: data.current_user.last_name,
id: data.current_user.id,
avatarId: data.current_user.avatar_id,
groups: data.group_permissions.map((group) => group.group),
});
/* CASE-1 */
const { current_organization_name } = data;
/* add the user details like permission and user previlliage details to the subject */

View file

@ -35,6 +35,7 @@ import { CreateEventHandlerDto, UpdateEventHandlerDto } from '@dto/event-handler
import { APP_RESOURCE_ACTIONS } from 'src/constants/global.constant';
import { VersionReleaseDto } from '@dto/version-release.dto';
import { AppsServiceSep } from '@apps/services/apps.service.sep';
import { mergeDefaultComponentData } from 'src/helpers/components.helper';
@Controller({
path: 'apps',
@ -93,7 +94,7 @@ export class AppsControllerV2 {
response['data_queries'] = seralizedQueries;
response['definition'] = app.editingVersion?.definition;
response['pages'] = pagesForVersion;
response['pages'] = mergeDefaultComponentData(pagesForVersion);
response['events'] = eventsForVersion;
//! if editing version exists, camelize the definition
@ -148,7 +149,7 @@ export class AppsControllerV2 {
name: app.name,
slug: app.slug,
events: eventsForVersion,
pages: pagesForVersion,
pages: mergeDefaultComponentData(pagesForVersion),
homePageId: versionToLoad.homePageId,
globalSettings: { ...versionToLoad.globalSettings, theme: appTheme },
showViewerNavigation: versionToLoad.showViewerNavigation,
@ -198,7 +199,7 @@ export class AppsControllerV2 {
return {
...appData,
editing_version: editingVersion,
pages: pagesForVersion,
pages: mergeDefaultComponentData(pagesForVersion),
events: eventsForVersion,
};
}

View file

@ -1,3 +1,9 @@
import { cloneDeep } from 'lodash';
import { merge } from 'lodash';
import { mergeWith } from 'lodash';
import { isArray } from 'lodash';
import { componentTypes } from './widget-config';
export enum LayoutDimensionUnits {
COUNT = 'count',
PERCENT = 'percent',
@ -8,3 +14,58 @@ export const resolveGridPositionForComponent = (dimension: number, type: string)
const numberOfGrids = 43;
return Math.round((dimension * numberOfGrids) / 100);
};
export const mergeDefaultComponentData = (pages) => {
return pages.map((page) => ({
...page,
components: buildComponentMetaDefinition(page.components),
}));
};
export const buildComponentMetaDefinition = (components = {}) => {
for (const componentId in components) {
const currentComponentData = components[componentId];
const componentMeta = cloneDeep(
componentTypes.find((comp) => currentComponentData.component.component === comp.component)
);
const mergedDefinition = {
// ...componentMeta.definition,
properties: mergeWith(
componentMeta.definition.properties,
currentComponentData?.component?.definition?.properties,
(objValue, srcValue) => {
if (currentComponentData?.component?.component === 'Table' && isArray(objValue)) {
return srcValue;
}
}
),
styles: merge(componentMeta.definition.styles, currentComponentData?.component.definition.styles),
generalStyles: merge(
componentMeta.definition.generalStyles,
currentComponentData?.component.definition.generalStyles
),
validation: merge(componentMeta.definition.validation, currentComponentData?.component.definition.validation),
others: merge(componentMeta.definition.others, currentComponentData?.component.definition.others),
general: merge(componentMeta.definition.general, currentComponentData?.component.definition.general),
};
const mergedComponent = {
component: {
...componentMeta,
...currentComponentData.component,
},
layouts: {
...currentComponentData.layouts,
},
withDefaultChildren: componentMeta.withDefaultChildren ?? false,
};
mergedComponent.component.definition = mergedDefinition;
components[componentId] = mergedComponent;
}
return components;
};

View file

@ -0,0 +1,121 @@
export const boundedBoxConfig = {
name: 'BoundedBox',
displayName: 'Bounded Box',
description: 'An infinitely customizable image annotation widget',
component: 'BoundedBox',
defaultSize: {
width: 30,
height: 420,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
imageUrl: {
type: 'code',
displayName: 'Image URL',
validation: {
schema: { type: 'string' },
defaultValue: `https://exaple.com/photos/three-cars.jpg`,
},
},
defaultValue: {
type: 'code',
displayName: 'Default value',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'array', element: { type: 'object' } }] },
defaultValue: "{{[{type: 'RECTANGLE',width: 40,height:24, x:41,y:62,text:'Car'}]}}",
},
},
selector: {
type: 'select',
displayName: 'Selector',
options: [
{ name: 'Rectangle', value: 'RECTANGLE' },
{ name: 'Point', value: 'POINT' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'RECTANGLE',
},
},
labels: {
type: 'code',
displayName: 'List of labels',
validation: {
schema: { type: 'array' },
element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: `{{['Car', 'Tree']}}`,
},
},
},
events: {
onChange: { displayName: 'On change' },
},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
exposedVariables: {
annotations: [
{
type: 'RECTANGLE',
x: 41,
y: 62,
width: 40,
height: 24,
text: 'Car',
id: 'ce103db2-b2a6-46f5-a4f0-5f4eaa6f3663',
},
{
type: 'RECTANGLE',
x: 41,
y: 12,
width: 40,
height: 24,
text: 'Tree',
id: 'b1a7315e-2b15-4bc8-a1c6-a042dab44f27',
},
],
},
actions: [],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
defaultValue: {
value:
"{{[\t{type: 'RECTANGLE',width: 40,height:24, x:41,y:62,text:'Car'},{type: 'RECTANGLE',width: 40,height:24, x:41,y:12,text:'Tree'}\t]}}",
},
imageUrl: {
value: `https://burst.shopifycdn.com/photos/three-cars-are-parked-on-stone-paved-street.jpg?width=746&format=pjpg&exif=1&iptc=1`,
},
selector: { value: `RECTANGLE` },
labels: { value: `{{['Tree', 'Car', 'Stree light']}}` },
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,238 @@
export const buttonConfig = {
name: 'Button',
displayName: 'Button',
description: 'Trigger actions: queries, alerts, set variables etc.',
component: 'Button',
defaultSize: {
width: 4,
height: 40,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
text: {
type: 'code',
displayName: 'Label',
validation: {
schema: { type: 'string' },
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' } },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onClick: { displayName: 'On click' },
onHover: { displayName: 'On hover' },
},
styles: {
type: {
type: 'switch',
displayName: 'Type',
validation: { schema: { type: 'string' } },
options: [
{ displayName: 'Solid', value: 'primary' },
{ displayName: 'Outline', value: 'outline' },
],
accordian: 'button',
},
backgroundColor: {
type: 'color',
displayName: 'Background',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
conditionallyRender: {
key: 'type',
value: 'primary',
},
accordian: 'button',
},
textColor: {
type: 'color',
displayName: 'Text color',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
accordian: 'button',
},
borderColor: {
type: 'color',
displayName: 'Border color',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
accordian: 'button',
},
loaderColor: {
type: 'color',
displayName: 'Loader color',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
accordian: 'button',
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' } },
accordian: 'button',
visibility: false,
},
iconColor: {
type: 'color',
displayName: 'Icon color',
validation: { schema: { type: 'string' } },
accordian: 'button',
visibility: false,
},
direction: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' } },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'button',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: {
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: false,
},
accordian: 'button',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
accordian: 'button',
conditionallyRender: {
key: 'type',
value: 'primary',
},
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'container',
},
},
exposedVariables: {
buttonText: 'Button',
isVisible: true,
isDisabled: false,
isLoading: false,
},
actions: [
{
handle: 'click',
displayName: 'Click',
},
{
handle: 'setText',
displayName: 'Set text',
params: [{ handle: 'text', displayName: 'Text', defaultValue: 'New Text' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'disable',
displayName: 'Disable(deprecated)',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
{
handle: 'visibility',
displayName: 'Visibility(deprecated)',
params: [{ handle: 'visible', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
{
handle: 'loading',
displayName: 'Loading(deprecated)',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
text: { value: `Button` },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
textColor: { value: '#FFFFFF' },
borderColor: { value: '#4368E3' },
loaderColor: { value: '#FFFFFF' },
borderRadius: { value: '{{6}}' },
backgroundColor: { value: '#4368E3' },
iconColor: { value: '#FFFFFF' },
direction: { value: 'left' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000090' },
icon: { value: 'IconAlignBoxBottomLeft' },
iconVisibility: { value: false },
type: { value: 'primary' },
},
},
};

View file

@ -0,0 +1,153 @@
export const buttonGroupConfig = {
name: 'ButtonGroup',
displayName: 'Button Group',
description: 'Group of buttons',
component: 'ButtonGroup',
properties: {
label: {
type: 'code',
displayName: 'label',
validation: {
schema: { type: 'string' },
defaultValue: 'Button group',
},
},
values: {
type: 'code',
displayName: 'values',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'array', element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } }],
},
defaultValue: '[]',
},
},
labels: {
type: 'code',
displayName: 'Labels',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'array', element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } }],
},
defaultValue: '[]',
},
},
defaultSelected: {
type: 'code',
displayName: 'Default selected',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'array', element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } }],
},
defaultValue: '[1]',
},
},
multiSelection: {
type: 'toggle',
displayName: 'Enable multiple selection',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
defaultSize: {
width: 12,
height: 80,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
events: {
onClick: { displayName: 'On click' },
},
styles: {
backgroundColor: {
type: 'color',
displayName: 'Background color',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
},
textColor: {
type: 'color',
displayName: 'Text color',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
borderRadius: {
type: 'number',
displayName: 'Border radius',
validation: {
schema: { type: 'number' },
defaultValue: false,
},
},
selectedTextColor: {
type: 'color',
displayName: 'Selected text colour',
validation: {
schema: { type: 'string' },
defaultValue: '#fff',
},
},
selectedBackgroundColor: {
type: 'color',
displayName: 'Selected background color',
validation: {
schema: { type: 'string' },
defaultValue: '#007bff',
},
},
},
exposedVariables: {
selected: [1],
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: { value: `Button group` },
defaultSelected: { value: '{{[1]}}' },
values: { value: '{{[1,2,3]}}' },
labels: { value: '{{[]}}' },
multiSelection: { value: '{{false}}' },
},
events: [],
styles: {
backgroundColor: { value: '' },
textColor: { value: '' },
visibility: { value: '{{true}}' },
borderRadius: { value: '{{4}}' },
disabledState: { value: '{{false}}' },
selectedTextColor: { value: '' },
selectedBackgroundColor: { value: '' },
},
},
};

View file

@ -0,0 +1,111 @@
export const calendarConfig = {
name: 'Calendar',
displayName: 'Calendar',
description: 'Display calendar events',
component: 'Calendar',
defaultSize: {
width: 30,
height: 600,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
dateFormat: { type: 'code', displayName: 'Date format' },
defaultDate: { type: 'code', displayName: 'Default date' },
events: { type: 'code', displayName: 'Events' },
resources: { type: 'code', displayName: 'Resources' },
defaultView: { type: 'code', displayName: 'Default view' },
startTime: {
type: 'code',
displayName: 'Start time on week and day view',
},
endTime: { type: 'code', displayName: 'End time on week and day view' },
displayToolbar: { type: 'toggle', displayName: 'Show toolbar' },
displayViewSwitcher: {
type: 'toggle',
displayName: 'Show view switcher',
},
highlightToday: { type: 'toggle', displayName: 'Highlight today' },
showPopOverOnEventClick: {
type: 'toggle',
displayName: 'Show popover when event is clicked',
},
},
events: {
onCalendarEventSelect: { displayName: 'On Event Select' },
onCalendarSlotSelect: { displayName: 'On Slot Select' },
onCalendarNavigate: { displayName: 'On Date Navigate' },
onCalendarViewChange: { displayName: 'On View Change' },
},
styles: {
visibility: { type: 'toggle', displayName: 'Visibility' },
cellSizeInViewsClassifiedByResource: {
type: 'select',
displayName: 'Cell size in views classified by resource',
options: [
{ name: 'Compact', value: 'compact' },
{ name: 'Spacious', value: 'spacious' },
],
},
weekDateFormat: {
type: 'code',
displayName: 'Header date format on week view',
},
},
exposedVariables: {
selectedEvent: {},
selectedSlots: {},
currentView: 'month',
currentDate: undefined,
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
dateFormat: {
value: 'MM-DD-YYYY HH:mm:ss A Z',
},
defaultDate: {
value: '{{moment().format("MM-DD-YYYY HH:mm:ss A Z")}}',
},
events: {
value:
"{{[\n\t\t{\n\t\t\t title: 'Sample event',\n\t\t\t start: `${moment().startOf('day').format('MM-DD-YYYY HH:mm:ss A Z')}`,\n\t\t\t end: `${moment().endOf('day').format('MM-DD-YYYY HH:mm:ss A Z')}`,\n\t\t\t allDay: false,\n\t\t\t color: '#4D72DA'\n\t\t}\n]}}",
},
resources: {
value: '{{[]}}',
},
defaultView: {
value: "{{'month'}}",
},
startTime: {
value: "{{moment().startOf('day').format('MM-DD-YYYY HH:mm:ss A Z')}}",
},
endTime: {
value: "{{moment().endOf('day').format('MM-DD-YYYY HH:mm:ss A Z')}}",
},
displayToolbar: {
value: true,
},
displayViewSwitcher: {
value: true,
},
highlightToday: {
value: true,
},
showPopOverOnEventClick: {
value: false,
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
cellSizeInViewsClassifiedByResource: { value: 'spacious' },
weekDateFormat: { value: 'DD MMM' },
},
},
};

View file

@ -0,0 +1,238 @@
export const chartConfig = {
name: 'Chart',
displayName: 'Chart',
description: 'Visualize data',
component: 'Chart',
defaultSize: {
width: 20,
height: 400,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
title: {
type: 'code',
displayName: 'Title',
validation: {
schema: {
type: 'string',
},
defaultValue: 'This title can be changed',
},
},
data: {
type: 'json',
displayName: 'Data',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'array' }] },
defaultValue: '',
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
markerColor: {
type: 'color',
displayName: 'Marker color',
validation: {
schema: {
type: 'string',
},
defaultValue: '#CDE1F8',
},
},
showAxes: {
type: 'toggle',
displayName: 'Show axes',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
showGridLines: {
type: 'toggle',
displayName: 'Show grid lines',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
type: {
type: 'select',
displayName: 'Chart type',
options: [
{ name: 'Line', value: 'line' },
{ name: 'Bar', value: 'bar' },
{ name: 'Pie', value: 'pie' },
],
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'boolean' }, { type: 'number' }],
},
defaultValue: 'line',
},
},
jsonDescription: {
type: 'json',
displayName: 'Json Description',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'object' }],
},
defaultValue: '{ "data": [ { "x": [ "Jan", "Feb", "Mar" ], "y": [ 100, 80, 40 ], "type": "bar" } ] }',
},
},
plotFromJson: {
type: 'toggle',
displayName: 'Use Plotly JSON schema',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
barmode: {
type: 'select',
displayName: 'Bar mode',
options: [
{ name: 'Stack', value: 'stack' },
{ name: 'Group', value: 'group' },
{ name: 'Overlay', value: 'overlay' },
{ name: 'Relative', value: 'relative' },
],
validation: {
schema: {
schemas: { type: 'string' },
},
defaultValue: 'group',
},
},
},
actions: [
{
handle: 'clearClickedPoint',
displayName: 'Clear clicked point',
},
],
events: {
onClick: { displayName: 'On data point click' },
onDoubleClick: { displayName: 'On double click' },
},
styles: {
backgroundColor: {
type: 'color',
displayName: 'Background color',
validation: { schema: { type: 'string' }, defaultValue: '#fff' },
},
padding: {
type: 'code',
displayName: 'Padding',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'number' }, { type: 'string' }],
},
defaultValue: 50,
},
},
borderRadius: {
type: 'number',
displayName: 'Border radius',
validation: {
schema: { type: 'number' },
defaultValue: 4,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
},
exposedVariables: {
show: null,
chartTitle: null,
xAxisTitle: null,
yAxisTitle: null,
clickedDataPoint: {},
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
title: { value: 'This title can be changed' },
markerColor: { value: '#CDE1F8' },
showAxes: { value: '{{true}}' },
showGridLines: { value: '{{true}}' },
plotFromJson: { value: '{{false}}' },
loadingState: { value: `{{false}}` },
barmode: { value: `group` },
jsonDescription: {
value: `{
"data": [
{
"x": [
"Jan",
"Feb",
"Mar"
],
"y": [
100,
80,
40
],
"type": "bar"
}
]
}`,
},
type: { value: `line` },
data: {
value: `[
{ "x": "Jan", "y": 100},
{ "x": "Feb", "y": 80},
{ "x": "Mar", "y": 40}
]`,
},
},
events: [],
styles: {
backgroundColor: { value: '#fff' },
padding: { value: '50' },
borderRadius: { value: '{{4}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,198 @@
export const checkboxConfig = {
name: 'Checkbox',
displayName: 'Checkbox',
description: 'Single checkbox toggle',
component: 'Checkbox',
defaultSize: {
width: 6,
height: 30,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: {
schema: { type: 'string' },
},
},
defaultValue: {
type: 'switch',
displayName: 'Default state',
validation: { schema: { type: 'boolean' } },
options: [
{ displayName: 'On', value: '{{true}}' },
{ displayName: 'Off', value: '{{false}}' },
],
accordian: 'label',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' } },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' } },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
validation: {
mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
customRule: {
type: 'code',
displayName: 'Custom validation',
placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
},
},
events: {
onChange: { displayName: 'On change' },
onCheck: { displayName: 'On check (Deprecated)' },
onUnCheck: { displayName: 'On uncheck (Deprecated)' },
},
styles: {
textColor: {
type: 'color',
displayName: 'Text color',
validation: {
schema: { type: 'string' },
},
accordian: 'label',
},
borderColor: {
type: 'color',
displayName: 'Border color',
validation: {
schema: { type: 'string' },
},
accordian: 'switch',
},
checkboxColor: {
type: 'color',
displayName: 'Checked color',
validation: {
schema: { type: 'string' },
},
accordian: 'switch',
},
uncheckedColor: {
type: 'color',
displayName: 'Unchecked color',
validation: {
schema: { type: 'string' },
},
accordian: 'switch',
},
handleColor: {
type: 'color',
displayName: 'Handle color',
validation: {
schema: { type: 'string' },
},
accordian: 'switch',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
accordian: 'switch',
},
alignment: {
type: 'switch',
displayName: 'Alignment',
validation: { schema: { type: 'string' } },
options: [
{ displayName: 'Left', value: 'left' },
{ displayName: 'Right', value: 'right' },
],
accordian: 'label',
},
},
exposedVariables: {
value: false,
label: 'Label',
isMandatory: false,
isVisible: true,
isDisabled: false,
isLoading: false,
},
actions: [
{
handle: 'toggle',
displayName: 'toggle',
},
{
handle: 'setValue',
displayName: 'Set value',
params: [{ handle: 'value', displayName: 'value' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setChecked',
displayName: 'Set checked (Deprecated)',
params: [{ handle: 'status', displayName: 'status' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: { value: 'Label' },
defaultValue: { value: '{{false}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
disabledState: { value: '{{false}}' },
textColor: { value: '#1B1F24' },
checkboxColor: { value: '#4368E3' },
uncheckedColor: { value: '#E4E7EB' },
borderColor: { value: '#CCD1D5' },
handleColor: { value: '#FFFFFF' },
alignment: { value: 'right' },
boxShadow: { value: '0px 0px 0px 0px #00000090' },
},
validation: {
mandatory: { value: '{{false}}' },
customRule: { value: null },
},
},
};

View file

@ -0,0 +1,116 @@
export const circularProgressbarConfig = {
name: 'CircularProgressBar',
displayName: 'Circular Progressbar',
description: 'Show circular progress',
component: 'CircularProgressBar',
defaultSize: {
width: 7,
height: 50,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
text: {
type: 'code',
displayName: 'Text',
validation: {
schema: { type: 'string' },
defaultValue: 'text',
},
},
progress: {
type: 'code',
displayName: 'Progress',
validation: {
schema: { type: 'number' },
defaultValue: 50,
},
},
},
events: {},
styles: {
color: {
type: 'color',
displayName: 'Color',
validation: {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
},
textColor: {
type: 'color',
displayName: 'Text Color',
validation: {
schema: { type: 'string' },
defaultValue: '#fff',
},
},
textSize: {
type: 'code',
displayName: 'Text size',
validation: {
schema: { type: 'number' },
defaultValue: 16,
},
},
strokeWidth: {
type: 'code',
displayName: 'Stroke width',
validation: {
schema: { type: 'number' },
defaultValue: 8,
},
},
counterClockwise: {
type: 'code',
displayName: 'Counter clockwise',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
circleRatio: {
type: 'code',
displayName: 'Circle ratio',
validation: {
schema: { type: 'number' },
defaultValue: 1,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
text: {
value: '',
},
progress: {
value: '{{50}}',
},
},
events: [],
styles: {
color: { value: '' },
textColor: { value: '' },
textSize: { value: '{{16}}' },
strokeWidth: { value: '{{8}}' },
counterClockwise: { value: '{{false}}' },
circleRatio: { value: '{{1}}' },
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,87 @@
export const codeEditorConfig = {
name: 'CodeEditor',
displayName: 'Code Editor',
description: 'Edit source code',
component: 'CodeEditor',
defaultSize: {
width: 15,
height: 120,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
enableLineNumber: {
type: 'code',
displayName: 'Show line number',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
mode: {
type: 'code',
displayName: 'Mode',
validation: {
schema: { type: 'string' },
defaultValue: 'javascript',
},
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
defaultValue: 'placeholder',
},
},
},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
borderRadius: {
type: 'code',
displayName: 'Border radius',
validation: {
schema: { type: 'number' },
defaultValue: 4,
},
},
},
exposedVariables: {
value: '',
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
enableLineNumber: { value: '{{true}}' },
mode: { value: 'javascript' },
placeholder: { value: '' },
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
borderRadius: { value: '{{4}}' },
},
},
};

View file

@ -0,0 +1,50 @@
export const colorPickerConfig = {
name: 'ColorPicker',
displayName: 'Color Picker',
description: 'Choose colors from a palette',
component: 'ColorPicker',
properties: {
defaultColor: { type: 'color', displayName: 'Default color' },
},
defaultSize: {
width: 9,
height: 40,
},
actions: [
{
displayName: 'Set Color',
handle: 'setColor',
params: [{ handle: 'color', displayName: 'color', defaultValue: '#ffffff', type: 'color' }],
},
],
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
events: {
onChange: { displayName: 'On change' },
},
styles: {
visibility: { type: 'toggle', displayName: 'Visibility' },
},
exposedVariables: {
selectedColorHex: '#000000',
selectedColorRGB: 'rgb(0,0,0)',
selectedColorRGBA: 'rgba(0, 0, 0, 1)',
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
defaultColor: {
value: '#000000',
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,89 @@
export const containerConfig = {
name: 'Container',
displayName: 'Container',
description: 'Group components',
defaultSize: {
width: 5,
height: 200,
},
component: 'Container',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
events: {},
styles: {
backgroundColor: {
type: 'color',
displayName: 'Background color',
validation: {
schema: { type: 'string' },
defaultValue: '#fff',
},
},
borderRadius: {
type: 'code',
displayName: 'Border radius',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }],
},
defaultValue: 4,
},
},
borderColor: {
type: 'color',
displayName: 'Border color',
validation: {
schema: { type: 'string' },
defaultValue: '#fff',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
},
defaultValue: false,
},
},
exposedVariables: {},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
visible: { value: '{{true}}' },
loadingState: { value: `{{false}}` },
},
events: [],
styles: {
backgroundColor: { value: '#fff' },
borderRadius: { value: '4' },
borderColor: { value: '#fff' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,65 @@
export const customComponentConfig = {
name: 'CustomComponent',
displayName: 'Custom Component',
description: 'Create React components',
component: 'CustomComponent',
properties: {
data: { type: 'code', displayName: 'Data', validation: { schema: { type: 'object' }, defaultValue: '{}' } },
code: { type: 'code', displayName: 'Code' },
},
defaultSize: {
width: 20,
height: 140,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
},
},
exposedVariables: {
data: { value: `{{{ title: 'Hi! There', buttonText: 'Update Title'}}}` },
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
visible: { value: '{{true}}' },
data: {
value: `{{{ title: 'Hi! There', buttonText: 'Update Title'}}}`,
},
code: {
value: `import React from 'https://cdn.skypack.dev/react';
import ReactDOM from 'https://cdn.skypack.dev/react-dom';
import { Button, Container } from 'https://cdn.skypack.dev/@material-ui/core';
const MyCustomComponent = ({data, updateData, runQuery}) => (
<Container>
<h1>{data.title}</h1>
<Button
color="primary"
variant="outlined"
onClick={() => {updateData({title: 'Hello World!!'})}}
>
{data.buttonText}
</Button>
</Container>
);
const ConnectedComponent = Tooljet.connectComponent(MyCustomComponent);
ReactDOM.render(<ConnectedComponent />, document.body);`,
skipResolve: true,
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,113 @@
export const datepickerConfig = {
name: 'Datepicker',
displayName: 'Date Picker',
description: 'Choose date and time',
component: 'Datepicker',
defaultSize: {
width: 5,
height: 30,
},
validation: {
customRule: { type: 'code', displayName: 'Custom validation' },
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
defaultValue: {
type: 'code',
displayName: 'Default value',
validation: {
schema: { type: 'string' },
defaultValue: '01/01/2022',
},
},
format: {
type: 'code',
displayName: 'Format',
validation: {
schema: { type: 'string' },
defaultValue: 'DD/MM/YYYY',
},
},
enableTime: {
type: 'toggle',
displayName: 'Enable time selection?',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
enableDate: {
type: 'toggle',
displayName: 'Enable date selection?',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledDates: {
type: 'code',
displayName: 'Disabled dates',
validation: {
schema: { type: 'array', element: { type: 'string' } },
defaultValue: "['01/01/2022']",
},
},
},
events: {
onSelect: { displayName: 'On select' },
},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
borderRadius: {
type: 'code',
displayName: 'Border radius',
validation: {
schema: { type: 'number' },
defaultValue: 4,
},
},
},
exposedVariables: {
value: '',
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
validation: {
customRule: { value: '' },
},
properties: {
defaultValue: { value: '01/01/2022' },
format: { value: 'DD/MM/YYYY' },
enableTime: { value: '{{false}}' },
enableDate: { value: '{{true}}' },
disabledDates: { value: '{{[]}}' },
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
borderRadius: { value: '{{4}}' },
},
},
};

View file

@ -0,0 +1,104 @@
export const daterangepickerConfig = {
name: 'DateRangePicker',
displayName: 'Range Picker',
description: 'Choose date ranges',
component: 'DaterangePicker',
defaultSize: {
width: 10,
height: 30,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
defaultStartDate: {
type: 'code',
displayName: 'Default start date',
validation: {
schema: {
type: 'string',
},
defautlValue: '01/04/2022',
},
},
defaultEndDate: {
type: 'code',
displayName: 'Default end date',
validation: {
schema: {
type: 'string',
},
defautlValue: '10/04/2022',
},
},
format: {
type: 'code',
displayName: 'Format',
validation: {
schema: {
type: 'string',
},
defautlValue: 'DD/MM/YYYY',
},
},
},
events: {
onSelect: { displayName: 'On select' },
},
styles: {
borderRadius: {
type: 'code',
displayName: 'Border radius',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'number' }, { type: 'string' }],
},
defautlValue: 4,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: {
type: 'boolean',
},
defautlValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: {
type: 'boolean',
},
defautlValue: false,
},
},
},
exposedVariables: {
endDate: {},
startDate: {},
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
defaultStartDate: { value: '01/04/2022' },
defaultEndDate: { value: '10/04/2022' },
format: { value: 'DD/MM/YYYY' },
},
events: [],
styles: {
borderRadius: { value: '4' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,49 @@
export const dividerConfig = {
name: 'Divider',
displayName: 'Divider',
description: 'Separator between components',
component: 'Divider',
defaultSize: {
width: 10,
height: 10,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {},
events: {},
styles: {
dividerColor: {
type: 'color',
displayName: 'Divider color',
validation: {
schema: { type: 'string' },
defaultValue: '#000000',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {
value: {},
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {},
events: [],
styles: {
visibility: { value: '{{true}}' },
dividerColor: { value: '#000000' },
},
},
};

View file

@ -0,0 +1,205 @@
export const dropdownConfig = {
name: 'DropdownLegacy',
displayName: 'Dropdown (Legacy)',
description: 'Single item selector',
defaultSize: {
width: 8,
height: 30,
},
component: 'DropDown',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
validation: {
customRule: { type: 'code', displayName: 'Custom validation' },
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: {
schema: { type: 'string' },
defaultValue: 'Select',
},
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
defaultValue: 'Select an option',
},
},
advanced: {
type: 'toggle',
displayName: 'Advanced',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
value: {
type: 'code',
displayName: 'Default value',
conditionallyRender: {
key: 'advanced',
value: false,
},
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }],
},
defaultValue: 2,
},
},
values: {
type: 'code',
displayName: 'Option values',
conditionallyRender: {
key: 'advanced',
value: false,
},
validation: {
schema: {
type: 'array',
element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] },
},
defaultValue: "['one', 'two', 'three']",
},
},
display_values: {
type: 'code',
displayName: 'Option labels',
conditionallyRender: {
key: 'advanced',
value: false,
},
validation: {
schema: {
type: 'array',
element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] },
},
defaultValue: "['one', 'two', 'three']",
},
},
schema: {
type: 'code',
displayName: 'Schema',
conditionallyRender: {
key: 'advanced',
value: true,
},
},
loadingState: {
type: 'toggle',
displayName: 'Options loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
events: {
onSelect: { displayName: 'On select' },
onSearchTextChanged: { displayName: 'On search text changed' },
},
styles: {
borderRadius: {
type: 'code',
displayName: 'Border radius',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'number' }, { type: 'string' }],
},
defaultValue: 4,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
},
defaultValue: true,
},
selectedTextColor: {
type: 'color',
displayName: 'Selected text color',
validation: {
schema: {
type: 'string',
},
defaultValue: '#000000',
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
justifyContent: {
type: 'alignButtons',
displayName: 'Align Text',
validation: {
schema: {
type: 'string',
},
defaultValue: 'left',
},
},
},
exposedVariables: {
value: 2,
searchText: '',
label: 'Select',
optionLabels: ['one', 'two', 'three'],
selectedOptionLabel: 'two',
},
actions: [
{
handle: 'selectOption',
displayName: 'Select option',
params: [{ handle: 'select', displayName: 'Select' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
validation: {
customRule: { value: null },
},
properties: {
advanced: { value: `{{false}}` },
schema: {
value:
"{{[\t{label: 'One',value: 1,disable: false,visible: true,default: true},{label: 'Two',value: 2,disable: false,visible: true},{label: 'Three',value: 3,disable: false,visible: true}\t]}}",
},
label: { value: 'Select' },
value: { value: '{{2}}' },
values: { value: '{{[1,2,3]}}' },
display_values: { value: '{{["one", "two", "three"]}}' },
loadingState: { value: '{{false}}' },
placeholder: { value: 'Select an option' },
},
events: [],
styles: {
borderRadius: { value: '4' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
justifyContent: { value: 'left' },
},
},
};

View file

@ -0,0 +1,330 @@
export const dropdownV2Config = {
name: 'Dropdown',
displayName: 'Dropdown',
description: 'Single item selector',
defaultSize: {
width: 10,
height: 40,
},
component: 'DropdownV2',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
validation: {
mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
customRule: {
type: 'code',
displayName: 'Custom validation',
placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
},
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: {
schema: { type: 'string' },
defaultValue: 'Select',
},
accordian: 'Data',
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
defaultValue: 'Select an option',
},
accordian: 'Data',
},
advanced: {
type: 'toggle',
displayName: 'Dynamic options',
validation: {
schema: { type: 'boolean' },
},
accordian: 'Options',
},
schema: {
type: 'code',
displayName: 'Schema',
conditionallyRender: {
key: 'advanced',
value: true,
},
accordian: 'Options',
},
optionsLoadingState: {
type: 'toggle',
displayName: 'Options loading state',
validation: {
schema: { type: 'boolean' },
},
accordian: 'Options',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: {
schema: { type: 'string' },
defaultValue: 'Enter tooltip text',
},
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onSelect: { displayName: 'On select' },
onSearchTextChanged: { displayName: 'On search text changed' },
onFocus: { displayName: 'On focus' },
onBlur: { displayName: 'On blur' },
},
styles: {
labelColor: {
type: 'color',
displayName: 'Color',
validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
accordian: 'label',
},
alignment: {
type: 'switch',
displayName: 'Alignment',
validation: { schema: { type: 'string' }, defaultValue: 'side' },
options: [
{ displayName: 'Side', value: 'side' },
{ displayName: 'Top', value: 'top' },
],
accordian: 'label',
},
direction: {
type: 'switch',
displayName: 'Direction',
validation: { schema: { type: 'string' }, defaultValue: 'left' },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'label',
isFxNotRequired: true,
},
labelWidth: {
type: 'slider',
displayName: 'Width',
accordian: 'label',
conditionallyRender: {
key: 'alignment',
value: 'side',
},
isFxNotRequired: true,
},
auto: {
type: 'checkbox',
displayName: 'auto',
showLabel: false,
validation: { schema: { type: 'boolean' } },
accordian: 'label',
conditionallyRender: {
key: 'alignment',
value: 'side',
},
isFxNotRequired: true,
},
fieldBackgroundColor: {
type: 'color',
displayName: 'Background',
validation: { schema: { type: 'string' }, defaultValue: '#fff' },
accordian: 'field',
},
fieldBorderColor: {
type: 'color',
displayName: 'Border',
validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
accordian: 'field',
},
accentColor: {
type: 'color',
displayName: 'Accent',
validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
accordian: 'field',
},
selectedTextColor: {
type: 'color',
displayName: 'Text',
validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
accordian: 'field',
},
errTextColor: {
type: 'color',
displayName: 'Error text',
validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
accordian: 'field',
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
accordian: 'field',
visibility: false,
},
iconColor: {
type: 'color',
displayName: '',
showLabel: false,
validation: {
schema: { type: 'string' },
defaultValue: '#6A727C',
},
accordian: 'field',
},
fieldBorderRadius: {
type: 'input',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: '6' },
accordian: 'field',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000040',
},
accordian: 'field',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: 'default',
},
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'container',
},
},
exposedVariables: {
searchText: '',
label: 'Select',
},
actions: [
{
handle: 'selectOption',
displayName: 'Select option',
params: [{ handle: 'select', displayName: 'Select' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'setVisibility', displayName: 'Value', defaultValue: `{{true}}`, type: 'toggle' }],
},
{
handle: 'clear',
displayName: 'Clear',
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'setLoading', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'setDisable', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
validation: {
mandatory: { value: '{{false}}' },
customRule: { value: null },
},
properties: {
advanced: { value: `{{false}}` },
schema: {
value:
"{{[\t{label: 'option1',value: 1,disable: false,visible: true,default: true},{label: 'option2',value: 2,disable: false,visible: true},{label: 'option3',value: 3,disable: false,visible: true}\t]}}",
},
options: {
value: [
{
label: 'option1',
value: '1',
disable: { value: false },
visible: { value: true },
default: { value: false },
},
{
label: 'option2',
value: '2',
disable: { value: false },
visible: { value: true },
default: { value: true },
},
{
label: 'option3',
value: '3',
disable: { value: false },
visible: { value: true },
default: { value: false },
},
],
},
label: { value: 'Select' },
value: { value: '{{"2"}}' },
optionsLoadingState: { value: '{{false}}' },
placeholder: { value: 'Select an option' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
tooltip: { value: '' },
},
events: [],
styles: {
labelColor: { value: '#1B1F24' },
labelWidth: { value: '33' },
auto: { value: '{{true}}' },
fieldBorderRadius: { value: '6' },
selectedTextColor: { value: '#1B1F24' },
fieldBorderColor: { value: '#CCD1D5' },
errTextColor: { value: '#D72D39' },
fieldBackgroundColor: { value: '#fff' },
direction: { value: 'left' },
alignment: { value: 'side' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000090' },
icon: { value: 'IconHome2' },
iconVisibility: { value: false },
iconColor: { value: '#6A727C' },
accentColor: { value: '#4368E3' },
},
},
};

View file

@ -0,0 +1,192 @@
export const filepickerConfig = {
name: 'FilePicker',
displayName: 'File Picker',
description: 'File Picker',
component: 'FilePicker',
defaultSize: {
width: 15,
height: 100,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
actions: [
{
handle: 'clearFiles',
displayName: 'Clear Files',
},
],
properties: {
instructionText: {
type: 'code',
displayName: 'Instruction text',
validation: {
schema: { type: 'string' },
defaultValue: 'Instruction text',
},
},
enableDropzone: {
type: 'code',
displayName: 'Use drop zone',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
enablePicker: {
type: 'code',
displayName: 'Use file picker',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
enableMultiple: {
type: 'code',
displayName: 'Pick multiple files',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
maxFileCount: {
type: 'code',
displayName: 'Max file count',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }],
},
defaultValue: 2,
},
},
fileType: {
type: 'code',
displayName: 'Accept file types',
validation: {
schema: {
type: 'string',
},
defaultValue: 'image/*',
},
},
maxSize: {
type: 'code',
displayName: 'Max size limit (Bytes)',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }],
},
defaultValue: 1048576,
},
},
minSize: {
type: 'code',
displayName: 'Min size limit (Bytes)',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }],
},
defaultValue: 50,
},
},
parseContent: {
type: 'toggle',
displayName: 'Parse content',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
parseFileType: {
type: 'select',
displayName: 'File type',
options: [
{ name: 'Autodetect from extension', value: 'auto-detect' },
{ name: 'CSV', value: 'csv' },
{ name: 'Microsoft Excel - xls', value: 'vnd.ms-excel' },
{
name: 'Microsoft Excel - xlsx',
value: 'vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
],
validation: {
schema: {
type: 'string',
},
defaultValue: 'auto-detect',
},
},
},
events: {
onFileSelected: { displayName: 'On File Selected' },
onFileLoaded: { displayName: 'On File Loaded' },
onFileDeselected: { displayName: 'On File Deselected' },
},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
borderRadius: {
type: 'code',
displayName: 'Border radius',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }],
},
defaultValue: 4,
},
},
},
exposedVariables: {
file: [{ name: '', content: '', dataURL: '', type: '', parsedData: '' }],
isParsing: false,
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
instructionText: { value: 'Drag and drop files here or click to select files' },
enableDropzone: { value: '{{true}}' },
enablePicker: { value: '{{true}}' },
maxFileCount: { value: '{{2}}' },
enableMultiple: { value: '{{false}}' },
fileType: { value: '{{"image/*"}}' },
maxSize: { value: '{{1048576}}' },
minSize: { value: '{{50}}' },
parseContent: { value: '{{false}}' },
parseFileType: { value: 'auto-detect' },
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
borderRadius: { value: '{{4}}' },
},
},
};

View file

@ -0,0 +1,330 @@
export const formConfig = {
name: 'Form',
displayName: 'Form',
description: 'Wrapper for multiple components',
defaultSize: {
width: 13,
height: 330,
},
defaultChildren: [
{
componentName: 'Text',
layout: {
top: 40,
left: 10,
height: 30,
width: 17,
},
properties: ['text'],
styles: [
'textSize',
'fontWeight',
'fontStyle',
'textColor',
'isScrollRequired',
'lineHeight',
'textIndent',
'textAlign',
'verticalAlignment',
'decoration',
'transformation',
'letterSpacing',
'wordSpacing',
'fontVariant',
'backgroundColor',
'borderColor',
'borderRadius',
'boxShadow',
'padding',
],
defaultValue: {
text: 'User Details',
fontWeight: 'bold',
textSize: 18,
textColor: '#000',
backgroundColor: '#fff00000',
textAlign: 'left',
decoration: 'none',
transformation: 'none',
fontStyle: 'normal',
lineHeight: 1.5,
textIndent: '0',
letterSpacing: '0',
wordSpacing: '0',
fontVariant: 'normal',
verticalAlignment: 'top',
padding: 'default',
boxShadow: '0px 0px 0px 0px #00000090',
borderRadius: '0',
isScrollRequired: 'enabled',
},
},
{
componentName: 'Text',
layout: {
top: 90,
left: 10,
height: 30,
},
properties: ['text'],
styles: [
'textSize',
'fontWeight',
'fontStyle',
'textColor',
'isScrollRequired',
'lineHeight',
'textIndent',
'textAlign',
'verticalAlignment',
'decoration',
'transformation',
'letterSpacing',
'wordSpacing',
'fontVariant',
'backgroundColor',
'borderColor',
'borderRadius',
'boxShadow',
'padding',
],
defaultValue: {
text: 'Name',
fontWeight: 'normal',
textSize: 14,
textColor: '#000',
backgroundColor: '#fff00000',
textAlign: 'left',
decoration: 'none',
transformation: 'none',
fontStyle: 'normal',
lineHeight: 1.5,
textIndent: '0',
letterSpacing: '0',
wordSpacing: '0',
fontVariant: 'normal',
verticalAlignment: 'top',
padding: 'default',
boxShadow: '0px 0px 0px 0px #00000090',
borderRadius: '0',
isScrollRequired: 'enabled',
},
},
{
componentName: 'Text',
layout: {
top: 160,
left: 10,
height: 30,
},
properties: ['text'],
styles: [
'textSize',
'fontWeight',
'fontStyle',
'textColor',
'isScrollRequired',
'lineHeight',
'textIndent',
'textAlign',
'verticalAlignment',
'decoration',
'transformation',
'letterSpacing',
'wordSpacing',
'fontVariant',
'backgroundColor',
'borderColor',
'borderRadius',
'boxShadow',
'padding',
],
defaultValue: {
text: 'Age',
fontWeight: 'normal',
textSize: 14,
textColor: '#000',
backgroundColor: '#fff00000',
textAlign: 'left',
decoration: 'none',
transformation: 'none',
fontStyle: 'normal',
lineHeight: 1.5,
textIndent: '0',
letterSpacing: '0',
wordSpacing: '0',
fontVariant: 'normal',
verticalAlignment: 'top',
padding: 'default',
boxShadow: '0px 0px 0px 0px #00000090',
borderRadius: '0',
isScrollRequired: 'enabled',
},
},
{
componentName: 'TextInput',
layout: {
top: 120,
left: 10,
height: 30,
width: 25,
},
properties: ['placeholder', 'label'],
defaultValue: {
placeholder: 'Enter your name',
label: '',
},
},
{
componentName: 'NumberInput',
layout: {
top: 190,
left: 10,
height: 30,
width: 25,
},
properties: ['value', 'label'],
defaultValue: {
value: 24,
label: '',
},
},
{
componentName: 'Button',
layout: {
top: 240,
left: 10,
height: 30,
width: 10,
},
properties: ['text'],
defaultValue: {
text: 'Submit',
},
},
],
component: 'Form',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
buttonToSubmit: {
type: 'select',
displayName: 'Button to submit form',
options: [{ name: 'None', value: 'none' }],
validation: {
schema: { type: 'string' },
defaultValue: 'none',
},
conditionallyRender: {
key: 'advanced',
value: false,
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
advanced: {
type: 'toggle',
displayName: ' Use custom schema',
},
JSONSchema: {
type: 'code',
displayName: 'JSON Schema',
conditionallyRender: {
key: 'advanced',
value: true,
},
},
},
events: {
onSubmit: { displayName: 'On submit' },
onInvalid: { displayName: 'On invalid' },
},
styles: {
backgroundColor: {
type: 'color',
displayName: 'Background color',
validation: {
schema: { type: 'string' },
},
},
borderRadius: {
type: 'code',
displayName: 'Border radius',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }],
},
defaultValue: 0,
},
},
borderColor: {
type: 'color',
displayName: 'Border color',
validation: {
schema: { type: 'string' },
defaultValue: '#fff',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
exposedVariables: {
data: {},
isValid: true,
},
actions: [
{
handle: 'submitForm',
displayName: 'Submit Form',
},
{
handle: 'resetForm',
displayName: 'Reset Form',
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
loadingState: { value: '{{false}}' },
advanced: { value: '{{false}}' },
JSONSchema: {
value:
"{{ {title: 'User registration form', properties: {firstname: {type: 'textinput',value: 'Maria',label:'First name', validation:{maxLength:6}, styles: {backgroundColor: '#f6f5ff',textColor: 'black'},},lastname:{type: 'textinput',value: 'Doe', label:'Last name', styles: {backgroundColor: '#f6f5ff',textColor: 'black'},},age:{type:'number', label:'Age'},}, submitButton: {value: 'Submit', styles: {backgroundColor: '#3a433b',borderColor:'#595959'}}} }}",
},
},
events: [],
styles: {
backgroundColor: { value: '#fff' },
borderRadius: { value: '0' },
borderColor: { value: '#fff' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,52 @@
export const htmlConfig = {
name: 'Html',
displayName: 'HTML Viewer',
description: 'View HTML content',
component: 'Html',
defaultSize: {
width: 10,
height: 310,
},
properties: {
rawHtml: {
type: 'code',
displayName: 'Raw HTML',
validation: {
schema: { type: 'string' },
defaultValue: `<body><div><h1>Hello World</h1></div></body>`,
},
},
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
rawHtml: {
value: `<body><main><section class="hero" style="height:306px;display: flex;
justify-content: center;padding:0 1px;align-items: center;text-align:center">You can build your custom HTML-CSS template here</section></main></body>`,
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,72 @@
export const iconConfig = {
name: 'Icon',
displayName: 'Icon',
description: 'Icon',
defaultSize: {
width: 5,
height: 48,
},
component: 'Icon',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
icon: {
type: 'iconPicker',
displayName: 'Icon',
validation: {
schema: { type: 'string' },
defaultValue: 'IconHome2',
},
},
},
events: {
onClick: { displayName: 'On click' },
onHover: { displayName: 'On hover' },
},
styles: {
iconColor: {
type: 'color',
displayName: 'Icon color',
validation: {
schema: { type: 'string' },
defaultValue: '#000',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {},
actions: [
{
handle: 'click',
displayName: 'Click',
},
{
displayName: 'Set Visibility',
handle: 'setVisibility',
params: [{ handle: 'value', displayName: 'Value', defaultValue: '{{true}}', type: 'toggle' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
icon: { value: 'IconHome2' },
},
events: [],
styles: {
iconColor: { value: '#000' },
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,59 @@
export const iframeConfig = {
name: 'Iframe',
displayName: 'Iframe',
description: 'Embed external content',
defaultSize: {
width: 10,
height: 310,
},
component: 'IFrame',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
source: {
type: 'code',
displayName: 'URL',
validation: {
schema: { type: 'string' },
defaultValue: 'https://tooljet.io/',
},
},
},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
exposedVariables: {},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
source: { value: 'https://tooljet.io/' },
visible: { value: '{{true}}' },
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,145 @@
export const imageConfig = {
name: 'Image',
displayName: 'Image',
description: 'Show image files',
defaultSize: {
width: 3,
height: 100,
},
component: 'Image',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
source: {
type: 'code',
displayName: 'URL',
validation: {
schema: { type: 'string' },
defaultValue: 'https://www.svgrepo.com/image.svg',
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
alternativeText: {
type: 'code',
displayName: 'Alternative text',
validation: {
schema: { type: 'string' },
defaultValue: 'this is an image',
},
},
zoomButtons: {
type: 'toggle',
displayName: 'Zoom button',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
rotateButton: {
type: 'toggle',
displayName: 'Rotate button',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
events: {
onClick: { displayName: 'On click' },
},
styles: {
borderType: {
type: 'select',
displayName: 'Border type',
options: [
{ name: 'None', value: 'none' },
{ name: 'Rounded', value: 'rounded' },
{ name: 'Circle', value: 'rounded-circle' },
{ name: 'Thumbnail', value: 'img-thumbnail' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'none',
},
},
backgroundColor: {
type: 'color',
displayName: 'Background color',
validation: {
schema: { type: 'string' },
defaultValue: '#ffffff',
},
},
padding: {
type: 'code',
displayName: 'Padding',
validation: {
schema: { type: 'number' },
defaultValue: 0,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
imageFit: {
type: 'select',
displayName: 'Image fit',
options: [
{ name: 'fill', value: 'fill' },
{ name: 'contain', value: 'contain' },
{ name: 'cover', value: 'cover' },
{ name: 'scale-down', value: 'scale-down' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'contain',
},
},
},
exposedVariables: {},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
source: { value: 'https://www.svgrepo.com/show/34217/image.svg' },
visible: { value: '{{true}}' },
loadingState: { value: '{{false}}' },
alternativeText: { value: '' },
zoomButtons: { value: '{{false}}' },
rotateButton: { value: '{{false}}' },
},
events: [],
styles: {
borderType: { value: 'none' },
padding: { value: '0' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
imageFit: { value: 'contain' },
backgroundColor: { value: '' },
},
},
};

View file

@ -0,0 +1,158 @@
import { buttonConfig } from './button';
import { tableConfig } from './table';
import { chartConfig } from './chart';
import { modalConfig } from './modal';
import { formConfig } from './form';
import { textinputConfig } from './textinput';
import { numberinputConfig } from './numberinput';
import { passinputConfig } from './passwordinput';
import { datepickerConfig } from './datepicker';
import { checkboxConfig } from './checkbox';
import { radiobuttonConfig } from './radiobutton';
import { toggleswitchConfig } from './toggleswitch';
import { toggleSwitchV2Config } from './toggleswitchv2';
import { textareaConfig } from './textarea';
import { daterangepickerConfig } from './daterangepicker';
import { textConfig } from './text';
import { imageConfig } from './image';
import { containerConfig } from './container';
import { dropdownConfig } from './dropdown';
import { dropdownV2Config } from './dropdownV2';
import { multiselectConfig } from './multiselect';
import { multiselectV2Config } from './multiselectV2';
import { richtextareaConfig } from './richtextarea';
import { mapConfig } from './map';
import { qrscannerConfig } from './qrscanner';
import { starratingConfig } from './starrating';
import { dividerConfig } from './divider';
import { filepickerConfig } from './filepicker';
import { calendarConfig } from './calendar';
import { iframeConfig } from './iframe';
import { codeEditorConfig } from './codeEditor';
import { tabsConfig } from './tabs';
import { timerConfig } from './timer';
import { listviewConfig } from './listview';
import { tagsConfig } from './tags';
import { paginationConfig } from './pagination';
import { circularProgressbarConfig } from './circularProgressbar';
import { spinnerConfig } from './spinner';
import { statisticsConfig } from './statistics';
import { rangeSliderConfig } from './rangeslider';
import { timelineConfig } from './timeline';
import { svgImageConfig } from './svgImage';
import { htmlConfig } from './html';
import { verticalDividerConfig } from './verticalDivider';
import { customComponentConfig } from './customComponent';
import { buttonGroupConfig } from './buttonGroup';
import { pdfConfig } from './pdf';
import { stepsConfig } from './steps';
import { kanbanConfig } from './kanban';
import { colorPickerConfig } from './colorPicker';
import { treeSelectConfig } from './treeSelect';
import { linkConfig } from './link';
import { iconConfig } from './icon';
import { boundedBoxConfig } from './boundedBox';
import { kanbanBoardConfig } from './kanbanBoard';
const widgets = {
buttonConfig,
tableConfig,
chartConfig,
modalConfig,
formConfig,
textinputConfig,
numberinputConfig,
passinputConfig,
datepickerConfig,
checkboxConfig,
radiobuttonConfig,
toggleswitchConfig, //!Depreciated
toggleSwitchV2Config,
textareaConfig,
daterangepickerConfig,
textConfig,
imageConfig,
containerConfig,
dropdownConfig, //!Depreciated
dropdownV2Config,
multiselectConfig,
multiselectV2Config, //!Depreciated
richtextareaConfig,
mapConfig,
qrscannerConfig,
starratingConfig,
dividerConfig,
filepickerConfig,
calendarConfig,
iframeConfig,
codeEditorConfig,
tabsConfig,
timerConfig,
listviewConfig,
tagsConfig,
paginationConfig,
circularProgressbarConfig,
spinnerConfig,
statisticsConfig,
rangeSliderConfig,
timelineConfig,
svgImageConfig,
htmlConfig,
verticalDividerConfig,
customComponentConfig,
buttonGroupConfig,
pdfConfig,
stepsConfig,
kanbanConfig,
kanbanBoardConfig, //!Depreciated
colorPickerConfig,
treeSelectConfig,
linkConfig,
iconConfig,
boundedBoxConfig,
};
const universalProps = {
properties: {},
general: {
tooltip: { type: 'code', displayName: 'Tooltip', validation: { schema: { type: 'string' } } },
},
others: {},
events: {},
styles: {},
validate: true,
generalStyles: {
boxShadow: { type: 'boxShadow', displayName: 'Box Shadow' },
},
definition: {
others: {},
events: [],
styles: {},
generalStyles: {
boxShadow: { value: '0px 0px 0px 0px #00000040' },
},
},
};
const combineProperties = (widget, universal, isArray = false) => {
return {
...universal,
...widget,
properties: { ...universal.properties, ...widget.properties },
general: { ...universal.general, ...widget.general },
others: { ...universal.others, ...widget.others },
events: isArray ? [...universal.events, ...widget.events] : { ...universal.events, ...widget.events },
styles: { ...universal.styles, ...widget.styles },
generalStyles: { ...universal.generalStyles, ...widget.generalStyles },
exposedVariables: { ...universal.exposedVariables, ...widget.exposedVariables },
};
};
export const componentTypes = Object.values(widgets).map((widget) => {
return {
...combineProperties(widget, universalProps),
definition: combineProperties(widget.definition, universalProps.definition, true),
};
});
export default widgets;

View file

@ -0,0 +1,163 @@
export const kanbanConfig = {
name: 'Kanban',
displayName: 'Kanban',
description: 'Task management board',
component: 'Kanban',
defaultSize: {
width: 40,
height: 490,
},
defaultChildren: [
{
componentName: 'Text',
layout: {
top: 20,
left: 4,
height: 30,
},
properties: ['text'],
accessorKey: 'text',
styles: ['fontWeight', 'textSize', 'textColor'],
defaultValue: {
text: '{{cardData.title}}',
fontWeight: 'bold',
textSize: 16,
textColor: '#000',
},
},
{
componentName: 'Text',
layout: {
top: 50,
left: 4,
height: 30,
},
properties: ['text'],
accessorKey: 'text',
styles: ['textSize', 'textColor'],
defaultValue: {
text: '{{cardData.description}}',
textSize: 14,
textColor: '#000',
},
},
],
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
columnData: { type: 'code', displayName: 'Column data' },
cardData: { type: 'code', displayName: 'Card data' },
cardWidth: {
type: 'code',
displayName: 'Card width',
validation: {
schema: { type: 'number' },
},
},
cardHeight: {
type: 'code',
displayName: 'Card height',
validation: {
schema: { type: 'number' },
},
},
enableAddCard: { type: 'toggle', displayName: 'Enable add card' },
showDeleteButton: { type: 'toggle', displayName: 'Show delete button' },
},
events: {
onUpdate: { displayName: 'On update' },
onAddCardClick: { displayName: 'On add card click' },
onCardRemoved: { displayName: 'Card removed' },
onCardAdded: { displayName: 'Card added' },
onCardMoved: { displayName: 'Card moved' },
onCardSelected: { displayName: 'Card selected' },
},
styles: {
disabledState: { type: 'toggle', displayName: 'Disable' },
visibility: { type: 'toggle', displayName: 'Visibility' },
accentColor: { type: 'color', displayName: 'Accent color' },
},
actions: [
{
handle: 'addCard',
displayName: 'Add Card',
params: [
{
handle: 'cardDetails',
displayName: 'Card Details',
defaultValue: `{{{ id: "c11", title: "Title 11", description: "Description 11", columnId: "r3" }}}`,
},
],
},
{
handle: 'deleteCard',
displayName: 'Delete Card',
params: [{ handle: 'id', displayName: 'Card Id', defaultValue: `{{components.kanban1?.lastSelectedCard?.id}}` }],
},
{
handle: 'moveCard',
displayName: 'Move Card',
params: [
{ handle: 'cardId', displayName: 'Card Id', defaultValue: `{{components.kanban1?.lastSelectedCard?.id}}` },
{ handle: 'columnId', displayName: 'Destination Column Id', defaultValue: '' },
],
},
{
handle: 'updateCardData',
displayName: 'Update Card Data',
params: [
{ handle: 'id', displayName: 'Card Id', defaultValue: `{{components.kanban1?.lastSelectedCard?.id}}` },
{
handle: 'value',
displayName: 'Value',
defaultValue: `{{{...components.kanban1?.lastSelectedCard, title: 'New Title'}}}`,
},
],
},
],
exposedVariables: {
updatedCardData: {},
lastAddedCard: {},
lastRemovedCard: {},
lastCardMovement: {},
lastSelectedCard: {},
lastUpdatedCard: {},
lastCardUpdate: [],
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
columnData: {
value:
'{{[{ "id": "r1", "title": "To Do" },{ "id": "r2", "title": "In Progress" },{ "id": "r3", "title": "Done" }]}}',
},
cardData: {
value:
'{{[{ id: "c1", title: "Title 1", description: "Description 1", columnId: "r1" },{ id: "c2", title: "Title 2", description: "Description 2", columnId: "r1" },{ id: "c3", title: "Title 3", description: "Description 3",columnId: "r2" },{ id: "c4", title: "Title 4", description: "Description 4",columnId: "r3" },{ id: "c5", title: "Title 5", description: "Description 5",columnId: "r3" }, { id: "c6", title: "Title 6", description: "Description 6", columnId: "r1" },{ id: "c7", title: "Title 7", description: "Description 7", columnId: "r1" },{ id: "c8", title: "Title 8", description: "Description 8",columnId: "r2" },{ id: "c9", title: "Title 9", description: "Description 9",columnId: "r3" },{ id: "c10", title: "Title 10", description: "Description 10",columnId: "r3" }]}}',
},
cardWidth: {
value: '{{302}}',
},
cardHeight: {
value: '{{100}}',
},
enableAddCard: {
value: `{{true}}`,
},
showDeleteButton: {
value: `{{true}}`,
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
accentColor: { value: '#4d72fa' },
},
},
};

View file

@ -0,0 +1,68 @@
//! KanbanBoard widget is deprecated. This config allows backward compatibility with existing KanbanBoard widgets.
export const kanbanBoardConfig = {
name: 'KanbanBoard',
displayName: 'Kanban Board',
description: 'Task management board',
component: 'KanbanBoard',
defaultSize: {
width: 40,
height: 490,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
columns: { type: 'code', displayName: 'Columns' },
cardData: { type: 'code', displayName: 'Card Data' },
enableAddCard: { type: 'toggle', displayName: 'Enable Add Card' },
},
events: {
onCardAdded: { displayName: 'Card added' },
onCardRemoved: { displayName: 'Card removed' },
onCardMoved: { displayName: 'Card moved' },
onCardSelected: { displayName: 'Card selected' },
onCardUpdated: { displayName: 'Card updated' },
},
styles: {
disabledState: { type: 'toggle', displayName: 'Disable' },
visibility: { type: 'toggle', displayName: 'Visibility' },
width: { type: 'number', displayName: 'Width' },
minWidth: { type: 'number', displayName: 'Min Width' },
accentColor: { type: 'color', displayName: 'Accent color' },
},
exposedVariables: {
columns: {},
lastAddedCard: {},
lastRemovedCard: {},
lastCardMovement: {},
lastUpdatedCard: {},
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
columns: {
value: '{{[{ "id": "1", "title": "to do" },{ "id": "2", "title": "in progress" }]}}',
},
cardData: {
value:
'{{[{ id: "01", title: "one", columnId: "1" },{ id: "02", title: "two", columnId: "1" },{ id: "03", title: "three", columnId: "2" }]}}',
},
enableAddCard: {
value: `{{true}}`,
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
width: { value: '{{400}}' },
minWidth: { value: '{{200}}' },
textColor: { value: '' },
},
},
};

View file

@ -0,0 +1,111 @@
export const linkConfig = {
name: 'Link',
displayName: 'Link',
description: 'Add link to the text',
defaultSize: {
width: 6,
height: 30,
},
component: 'Link',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
linkTarget: {
type: 'code',
displayName: 'Link target',
validation: {
schema: { type: 'string' },
defaultValue: 'https://dev.to/',
},
},
linkText: {
type: 'code',
displayName: 'Link text',
validation: {
schema: { type: 'string' },
defaultValue: 'Click here',
},
},
targetType: {
type: 'select',
displayName: 'Target type',
options: [
{ name: 'New Tab', value: 'new' },
{ name: 'Same Tab', value: 'same' },
],
validation: {
schema: { type: 'string' },
},
},
},
events: {
onClick: { displayName: 'On click' },
onHover: { displayName: 'On hover' },
},
styles: {
textColor: {
type: 'color',
displayName: 'Text color',
validation: {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
},
textSize: {
type: 'number',
displayName: 'Text size',
validation: {
schema: { type: 'number' },
defaultValue: 14,
},
},
underline: {
type: 'select',
displayName: 'Underline',
options: [
{ name: 'Never', value: 'no-underline' },
{ name: 'On Hover', value: 'on-hover' },
{ name: 'Always', value: 'underline' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'on-hover',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {},
actions: [
{
handle: 'click',
displayName: 'Click',
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
linkTarget: { value: 'https://dev.to/' },
linkText: { value: 'Click here' },
targetType: { value: 'new' },
},
events: [],
styles: {
textColor: { value: '#375FCF' },
textSize: { value: '{{14}}' },
underline: { value: 'on-hover' },
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,198 @@
export const listviewConfig = {
name: 'Listview',
displayName: 'List View',
description: 'List multiple items',
defaultSize: {
width: 30,
height: 300,
},
defaultChildren: [
{
componentName: 'Image',
layout: {
top: 15,
left: 3,
height: 100,
},
properties: ['source'],
accessorKey: 'imageURL',
},
{
componentName: 'Text',
layout: {
top: 50,
left: 11,
height: 30,
},
properties: ['text'],
accessorKey: 'text',
},
{
componentName: 'Button',
layout: {
top: 50,
left: 26,
height: 30,
},
incrementWidth: 2,
properties: ['text'],
accessorKey: 'buttonText',
},
],
component: 'Listview',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
data: {
type: 'code',
displayName: 'List data',
validation: {
schema: { type: 'array', element: { type: 'object' } },
defaultValue: "[{text: 'Sample text 1'}]",
},
},
mode: {
type: 'select',
displayName: 'Mode',
options: [
{ name: 'list', value: 'list' },
{ name: 'grid', value: 'grid' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'list',
},
},
columns: {
type: 'number',
displayName: 'Columns',
validation: {
schema: { type: 'number' },
defaultValue: 3,
},
conditionallyRender: {
key: 'mode',
value: 'grid',
},
},
rowHeight: {
type: 'code',
displayName: 'Row height',
validation: {
schema: { type: 'number' },
defaultValue: 100,
},
},
showBorder: {
type: 'code',
displayName: 'Show bottom border',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
conditionallyRender: {
key: 'mode',
value: 'list',
},
},
enablePagination: {
type: 'toggle',
displayName: 'Enable pagination',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
rowsPerPage: {
type: 'code',
displayName: 'Rows per page',
validation: {
schema: { type: 'number' },
defaultValue: 10,
},
},
},
events: {
onRowClicked: { displayName: 'Row clicked (Deprecated)' },
onRecordClicked: { displayName: 'Record clicked' },
},
styles: {
backgroundColor: {
type: 'color',
displayName: 'Background color',
validation: {
schema: { type: 'string' },
defaultValue: '#fff',
},
},
borderColor: {
type: 'color',
displayName: 'Border color',
validation: {
schema: { type: 'string' },
defaultValue: '#dadcde',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
borderRadius: {
type: 'number',
displayName: 'Border radius',
validation: {
schema: { type: 'number' },
defaultValue: 4,
},
},
},
exposedVariables: {
data: [{}],
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
data: {
value: `{{[
{ imageURL: 'https://www.svgrepo.com/show/34217/image.svg', text: 'Sample text 1', buttonText: 'Button 1' },
{ imageURL: 'https://www.svgrepo.com/show/34217/image.svg', text: 'Sample text 1', buttonText: 'Button 2' },
{ imageURL: 'https://www.svgrepo.com/show/34217/image.svg', text: 'Sample text 1', buttonText: 'Button 3' },
]}}`,
},
mode: { value: 'list' },
columns: { value: '{{3}}' },
rowHeight: {
value: '100',
},
visible: { value: '{{true}}' },
showBorder: { value: '{{true}}' },
rowsPerPage: { value: '{{10}}' },
enablePagination: { value: '{{false}}' },
},
events: [],
styles: {
backgroundColor: { value: '#fff' },
borderColor: { value: '#dadcde' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
borderRadius: { value: '{{4}}' },
},
},
};

View file

@ -0,0 +1,152 @@
export const mapConfig = {
name: 'Map',
displayName: 'Map',
description: 'Display map locations',
component: 'Map',
defaultSize: {
width: 16,
height: 420,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
initialLocation: {
type: 'code',
displayName: 'Initial location',
tip: 'This location will be the initial center of the map',
options: {
mode: 'javascript',
theme: 'duotone-light',
className: 'map-location-input pr-2',
},
validation: {
schema: {
type: 'union',
schemas: [{ type: 'array', element: { type: 'object' } }, { type: 'object' }],
},
defaultValue: `{{ {"lat": 40.7128, "lng": -73.935242} }}`,
},
},
defaultMarkers: {
type: 'code',
displayName: 'Default markers',
options: {
mode: 'javascript',
theme: 'duotone-light',
className: 'map-location-input pr-2',
},
validation: {
schema: {
type: 'union',
schemas: [{ type: 'array', element: { type: 'object' } }, { type: 'object' }],
},
defaultValue: `{{ [{"lat": 40.7128, "lng": -73.935242}] }}`,
},
},
polygonPoints: {
type: 'code',
displayName: 'Polygon points',
options: {
mode: 'javascript',
theme: 'duotone-light',
className: 'map-location-input pr-2',
},
validation: {
schema: {
type: 'union',
schemas: [{ type: 'array', element: { type: 'object' } }, { type: 'object' }],
},
defaultValue: `{{[{"lat": 40.7032, "lng": -73.975242},{"lat": 40.7532, "lng": -73.943242},{"lat": 40.7032, "lng": -73.916242}]}}`,
},
},
addNewMarkers: {
type: 'toggle',
displayName: 'Add new markers',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
canSearch: {
type: 'toggle',
displayName: 'Search for places',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
},
events: {
onBoundsChange: { displayName: 'On bounds change' },
onCreateMarker: { displayName: 'On create marker' },
onMarkerClick: { displayName: 'On marker click' },
onPolygonClick: { displayName: 'On polygon click' },
},
actions: [
{
handle: 'setLocation',
displayName: 'Set Location',
params: [
{ handle: 'lat', displayName: 'Latitude' },
{ handle: 'lng', displayName: 'Longitude' },
],
},
],
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
},
exposedVariables: {
center: {},
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
initialLocation: {
value: `{{ {"lat": 40.7128, "lng": -73.935242} }}`,
},
defaultMarkers: {
value: `{{ [{"lat": 40.7128, "lng": -73.935242}] }}`,
},
polygonPoints: {
value: `{{[\n\t\t{"lat": 40.7032, "lng": -73.975242},\n\t\t{"lat": 40.7532, "lng": -73.943242},\n\t\t{"lat": 40.7032, "lng": -73.916242}\n]}}`,
},
canSearch: {
value: `{{true}}`,
},
addNewMarkers: { value: `{{true}}` },
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,195 @@
export const modalConfig = {
name: 'Modal',
displayName: 'Modal',
description: 'Show pop-up windows',
component: 'Modal',
defaultSize: {
width: 10,
height: 34,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
title: {
type: 'code',
displayName: 'Title',
validation: {
schema: { type: 'string' },
defaultValue: 'This title can be changed',
},
},
titleAlignment: {
type: 'select',
displayName: 'Title alignment',
options: [
{ name: 'left', value: 'left' },
{ name: 'center', value: 'center' },
{ name: 'right', value: 'right' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'left',
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
useDefaultButton: {
type: 'toggle',
displayName: 'Use default trigger button',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
triggerButtonLabel: {
type: 'code',
displayName: 'Trigger button label',
validation: {
schema: {
type: 'string',
},
defaultValue: 'Launch Modal',
},
},
hideTitleBar: { type: 'toggle', displayName: 'Hide title bar' },
hideCloseButton: { type: 'toggle', displayName: 'Hide close button' },
hideOnEsc: { type: 'toggle', displayName: 'Close on escape key' },
closeOnClickingOutside: { type: 'toggle', displayName: 'Close on clicking outside' },
size: {
type: 'select',
displayName: 'Modal size',
options: [
{ name: 'small', value: 'sm' },
{ name: 'medium', value: 'lg' },
{ name: 'large', value: 'xl' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'lg',
},
},
modalHeight: {
type: 'code',
displayName: 'Modal height',
validation: {
schema: { type: 'string' },
defaultValue: '400px',
},
},
},
events: {
onOpen: { displayName: 'On open' },
onClose: { displayName: 'On close' },
},
styles: {
headerBackgroundColor: {
type: 'color',
displayName: 'Header background color',
validation: {
schema: { type: 'string' },
defaultValue: '#ffffffff',
},
},
headerTextColor: {
type: 'color',
displayName: 'Header title color',
validation: {
schema: { type: 'string' },
defaultValue: '#000000',
},
},
bodyBackgroundColor: {
type: 'color',
displayName: 'Body background color',
validation: {
schema: { type: 'string' },
defaultValue: '#ffffffff',
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
triggerButtonBackgroundColor: {
type: 'color',
displayName: 'Trigger button background color',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
},
triggerButtonTextColor: {
type: 'color',
displayName: 'Trigger button text color',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
},
},
exposedVariables: {
show: false,
},
actions: [
{
handle: 'open',
displayName: 'Open',
},
{
handle: 'close',
displayName: 'Close',
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
title: { value: 'This title can be changed' },
titleAlignment: { value: 'left' },
loadingState: { value: `{{false}}` },
useDefaultButton: { value: `{{true}}` },
triggerButtonLabel: { value: `Launch Modal` },
size: { value: 'lg' },
hideTitleBar: { value: '{{false}}' },
hideCloseButton: { value: '{{false}}' },
hideOnEsc: { value: '{{true}}' },
closeOnClickingOutside: { value: '{{false}}' },
modalHeight: { value: '400px' },
},
events: [],
styles: {
headerBackgroundColor: { value: '#ffffffff' },
headerTextColor: { value: '#000000' },
bodyBackgroundColor: { value: '#ffffffff' },
disabledState: { value: '{{false}}' },
visibility: { value: '{{true}}' },
triggerButtonBackgroundColor: { value: '#4D72FA' },
triggerButtonTextColor: { value: '#ffffffff' },
},
},
};

View file

@ -0,0 +1,136 @@
export const multiselectConfig = {
name: 'MultiselectLegacy',
displayName: 'Multiselect (Legacy)',
description: 'Multiple item selector',
defaultSize: {
width: 12,
height: 30,
},
component: 'Multiselect',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
actions: [
{
handle: 'selectOption',
displayName: 'Select Option',
params: [
{
handle: 'option',
displayName: 'Option',
},
],
},
{
handle: 'deselectOption',
displayName: 'Deselect Option',
params: [
{
handle: 'option',
displayName: 'Option',
},
],
},
{
handle: 'clearSelections',
displayName: 'Clear selections',
},
],
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: {
schema: { type: 'string' },
defaultValue: 'Select',
},
},
value: {
type: 'code',
displayName: 'Default value',
validation: {
schema: { type: 'array', element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: "['two', 'three']",
},
},
values: {
type: 'code',
displayName: 'Option values',
validation: {
schema: { type: 'array', element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: "['one', 'two', 'three']",
},
},
display_values: {
type: 'code',
displayName: 'Option labels',
validation: {
schema: { type: 'array', element: { type: 'string' } },
defaultValue: "['one', 'two', 'three']",
},
},
showAllOption: {
type: 'toggle',
displayName: 'Enable select All option',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
events: {
onSelect: { displayName: 'On select' },
onSearchTextChanged: { displayName: 'On search text changed' },
},
styles: {
borderRadius: {
type: 'code',
displayName: 'Border radius',
validation: {
schema: { type: 'number' },
defaultValue: 4,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
exposedVariables: {
values: {},
searchText: '',
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: { value: 'Select' },
value: { value: '{{[2,3]}}' },
values: { value: '{{[1,2,3]}}' },
display_values: { value: '{{["one", "two", "three"]}}' },
visible: { value: '{{true}}' },
showAllOption: { value: '{{false}}' },
},
events: [],
styles: {
borderRadius: { value: '4' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,371 @@
export const multiselectV2Config = {
name: 'Multiselect',
displayName: 'Multiselect',
description: 'Multiple item selector',
defaultSize: {
width: 10,
height: 40,
},
component: 'MultiselectV2',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
validation: {
mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
customRule: {
type: 'code',
displayName: 'Custom validation',
placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
},
},
actions: [
{
handle: 'selectOptions',
displayName: 'Select Options',
params: [
{
handle: 'option',
displayName: 'Option',
},
],
},
{
handle: 'deselectOptions',
displayName: 'Deselect Options',
params: [
{
handle: 'option',
displayName: 'Option',
},
],
},
{
handle: 'clear',
displayName: 'Clear',
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'setVisibility', displayName: 'Value', defaultValue: `{{true}}`, type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'setLoading', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'setDisable', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
],
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: {
schema: { type: 'string' },
defaultValue: 'Label',
},
accordian: 'Data',
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
defaultValue: 'Select the options',
},
accordian: 'Data',
},
advanced: {
type: 'toggle',
displayName: 'Dynamic options',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
accordian: 'Options',
},
value: {
type: 'code',
displayName: 'Default value',
conditionallyRender: {
key: 'advanced',
value: false,
},
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }],
},
},
accordian: 'Options',
},
schema: {
type: 'code',
displayName: 'Schema',
conditionallyRender: {
key: 'advanced',
value: true,
},
accordian: 'Options',
},
showAllOption: {
type: 'toggle',
displayName: 'Enable select all option',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
accordian: 'Options',
},
optionsLoadingState: {
type: 'toggle',
displayName: 'Options loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
accordian: 'Options',
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: '' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onSelect: { displayName: 'On select' },
onSearchTextChanged: { displayName: 'On search text changed' },
onFocus: { displayName: 'On focus' },
onBlur: { displayName: 'On blur' },
},
styles: {
labelColor: {
type: 'color',
displayName: 'Color',
validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
accordian: 'label',
},
alignment: {
type: 'switch',
displayName: 'Alignment',
validation: { schema: { type: 'string' }, defaultValue: 'side' },
options: [
{ displayName: 'Side', value: 'side' },
{ displayName: 'Top', value: 'top' },
],
accordian: 'label',
},
direction: {
type: 'switch',
displayName: 'Direction',
validation: { schema: { type: 'string' }, defaultValue: 'left' },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'label',
isFxNotRequired: true,
},
labelWidth: {
type: 'slider',
displayName: 'Width',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
accordian: 'label',
conditionallyRender: {
key: 'alignment',
value: 'side',
},
isFxNotRequired: true,
},
auto: {
type: 'checkbox',
displayName: 'auto',
showLabel: false,
validation: { schema: { type: 'boolean' } },
accordian: 'label',
conditionallyRender: {
key: 'alignment',
value: 'side',
},
isFxNotRequired: true,
},
fieldBackgroundColor: {
type: 'color',
displayName: 'Background',
validation: { schema: { type: 'string' }, defaultValue: '#fff' },
accordian: 'field',
},
fieldBorderColor: {
type: 'color',
displayName: 'Border',
validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
accordian: 'field',
},
accentColor: {
type: 'color',
displayName: 'Accent',
validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
accordian: 'field',
},
selectedTextColor: {
type: 'color',
displayName: 'Text',
validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
accordian: 'field',
},
errTextColor: {
type: 'color',
displayName: 'Error Text',
validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
accordian: 'field',
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
accordian: 'field',
visibility: false,
},
iconColor: {
type: 'color',
displayName: 'Icon color',
validation: {
schema: { type: 'string' },
defaultValue: '#6A727C',
},
accordian: 'field',
showLabel: false,
},
fieldBorderRadius: {
type: 'input',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: '6' },
accordian: 'field',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box Shadow',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000090',
},
accordian: 'field',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: 'default',
},
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'container',
},
},
exposedVariables: {
searchText: '',
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
validation: {
mandatory: { value: false },
customRule: { value: null },
},
properties: {
label: { value: 'Select' },
values: { value: ['1', '2'] },
advanced: { value: `{{false}}` },
showAllOption: { value: '{{false}}' },
optionsLoadingState: { value: '{{false}}' },
placeholder: { value: 'Select the options' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
schema: {
value:
"{{[\t{label: 'option1',value: 1,disable: false,visible: true,default: true},{label: 'option2',value: 2,disable: false,visible: true},{label: 'option3',value: 3,disable: false,visible: true}\t]}}",
},
options: {
value: [
{
label: 'option1',
value: '1',
disable: { value: false },
visible: { value: true },
default: { value: false },
},
{
label: 'option2',
value: '2',
disable: { value: false },
visible: { value: true },
default: { value: true },
},
{
label: 'option3',
value: '3',
disable: { value: false },
visible: { value: true },
default: { value: false },
},
],
},
tooltip: { value: '' },
},
events: [],
styles: {
labelColor: { value: '#1B1F24' },
labelWidth: { value: '33' },
auto: { value: '{{true}}' },
fieldBorderRadius: { value: '6' },
selectedTextColor: { value: '#1B1F24' },
fieldBorderColor: { value: '#CCD1D5' },
errTextColor: { value: '#D72D39' },
fieldBackgroundColor: { value: '#fff' },
direction: { value: 'left' },
alignment: { value: 'side' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000090' },
icon: { value: 'IconHome2' },
iconVisibility: { value: false },
iconColor: { value: '#6A727C' },
accentColor: { value: '#4368E3' },
},
},
};

View file

@ -0,0 +1,300 @@
export const numberinputConfig = {
name: 'NumberInput',
displayName: 'Number Input',
description: 'Numeric input field',
component: 'NumberInput',
defaultSize: {
width: 10,
height: 40,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: { schema: { type: 'string' }, defaultValue: 'Label' },
},
value: {
type: 'code',
displayName: 'Default value',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: 0,
},
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
defaultValue: 'Enter your input',
},
},
decimalPlaces: {
type: 'code',
displayName: 'Decimal places',
validation: {
schema: { type: 'number' },
defaultValue: 2,
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onChange: { displayName: 'On change' },
onFocus: { displayName: 'On focus' },
onBlur: { displayName: 'On blur' },
onEnterPressed: { displayName: 'On enter pressed' },
},
styles: {
color: {
type: 'color',
displayName: 'Text',
validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
accordian: 'label',
},
alignment: {
type: 'switch',
displayName: 'Alignment',
validation: { schema: { type: 'string' }, defaultValue: 'side' },
options: [
{ displayName: 'Side', value: 'side' },
{ displayName: 'Top', value: 'top' },
],
accordian: 'label',
},
direction: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' }, defaultValue: 'left' },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'label',
isFxNotRequired: true,
},
width: {
type: 'slider',
displayName: 'Width',
accordian: 'label',
conditionallyRender: {
key: 'alignment',
value: 'side',
},
isFxNotRequired: true,
},
auto: {
type: 'checkbox',
displayName: 'auto',
showLabel: false,
validation: { schema: { type: 'boolean' }, defaultValue: true },
accordian: 'label',
conditionallyRender: {
key: 'alignment',
value: 'side',
},
isFxNotRequired: true,
},
backgroundColor: {
type: 'color',
displayName: 'Background',
validation: { schema: { type: 'string' }, defaultValue: '#fff' },
accordian: 'field',
},
borderColor: {
type: 'color',
displayName: 'Border',
validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
accordian: 'field',
},
accentColor: {
type: 'color',
displayName: 'Accent',
validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
accordian: 'field',
},
textColor: {
type: 'color',
displayName: 'Text',
validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
accordian: 'field',
},
errTextColor: {
type: 'color',
displayName: 'Error text',
validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
accordian: 'field',
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
accordian: 'field',
visibility: false,
},
iconColor: {
type: 'color',
displayName: 'Icon color',
validation: { schema: { type: 'string' }, defaultValue: '#CFD3D859' },
accordian: 'field',
visibility: false,
showLabel: false,
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
accordian: 'field',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box Shadow',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000040',
},
accordian: 'field',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: 'default',
},
isFxNotRequired: true,
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'container',
},
},
actions: [
{
handle: 'setText',
displayName: 'Set text',
params: [{ handle: 'text', displayName: 'text', defaultValue: '100' }],
},
{
handle: 'clear',
displayName: 'Clear',
},
{
handle: 'setFocus',
displayName: 'Set focus',
},
{
handle: 'setBlur',
displayName: 'Set blur',
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
],
exposedVariables: {
value: 99,
isMandatory: false,
isVisible: true,
isDisabled: false,
isLoading: false,
},
validation: {
mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
regex: { type: 'code', displayName: 'Regex', placeholder: '^d+$' },
minValue: { type: 'code', displayName: 'Min value', placeholder: 'Enter min value' },
maxValue: { type: 'code', displayName: 'Max value', placeholder: 'Enter max value' },
customRule: {
type: 'code',
displayName: 'Custom validation',
placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
},
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
validation: {
mandatory: { value: '{{false}}' },
regex: { value: '' },
minValue: { value: '' },
maxValue: { value: '' },
customRule: { value: '' },
},
properties: {
value: { value: '0' },
label: { value: 'Label' },
maxValue: { value: '' },
minValue: { value: '' },
placeholder: { value: '0' },
decimalPlaces: { value: '{{2}}' },
tooltip: { value: '' },
visibility: { value: '{{true}}' },
loadingState: { value: '{{false}}' },
disabledState: { value: '{{false}}' },
},
events: [],
styles: {
borderRadius: { value: '{{6}}' },
backgroundColor: { value: '#fff' },
borderColor: { value: '#CCD1D5' },
accentColor: { value: '#4368E3' },
errTextColor: { value: '#D72D39' },
textColor: { value: '#1B1F24' },
color: { value: '#1B1F24' },
iconColor: { value: '#CFD3D859' },
direction: { value: 'left' },
width: { value: '{{33}}' },
alignment: { value: 'side' },
auto: { value: '{{true}}' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
icon: { value: 'IconHome2' },
iconVisibility: { value: false },
},
},
};

View file

@ -0,0 +1,78 @@
export const paginationConfig = {
name: 'Pagination',
displayName: 'Pagination',
description: 'Navigate pages',
component: 'Pagination',
defaultSize: {
width: 10,
height: 30,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
numberOfPages: {
type: 'code',
displayName: 'Number of pages',
validation: {
schema: { type: 'number' },
defaultValue: '{{5}}',
},
},
defaultPageIndex: {
type: 'code',
displayName: 'Default page index',
validation: {
schema: { type: 'number' },
defaultValue: '{{1}}',
},
},
},
validation: {},
events: {
onPageChange: { displayName: 'On Page Change' },
},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
exposedVariables: {
totalPages: null,
currentPageIndex: null,
},
definition: {
validation: {},
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
numberOfPages: {
value: '{{5}}',
},
defaultPageIndex: {
value: '{{1}}',
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,296 @@
export const passinputConfig = {
name: 'PasswordInput',
displayName: 'Password Input',
description: 'Secure text input',
component: 'PasswordInput',
defaultSize: {
width: 10,
height: 40,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: { schema: { type: 'string' }, defaultValue: 'Label' },
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
defaultValue: 'Password',
},
},
value: {
type: 'code',
displayName: 'Default value',
validation: {
schema: {
type: 'string',
},
defaultValue: 'default value',
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
validation: {
mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
regex: {
type: 'code',
displayName: 'Regex',
placeholder: '^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]{8,}$',
},
minLength: { type: 'code', displayName: 'Min length', placeholder: 'Enter min length' },
maxLength: { type: 'code', displayName: 'Max length', placeholder: 'Enter max length' },
customRule: {
type: 'code',
displayName: 'Custom validation',
placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
},
},
events: {
onChange: { displayName: 'On change' },
onFocus: { displayName: 'On focus' },
onBlur: { displayName: 'On blur' },
onEnterPressed: { displayName: 'On enter pressed' },
},
styles: {
color: {
type: 'color',
displayName: 'Text',
validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
accordian: 'label',
},
alignment: {
type: 'switch',
displayName: 'Alignment',
validation: { schema: { type: 'string' }, defaultValue: 'side' },
options: [
{ displayName: 'Side', value: 'side' },
{ displayName: 'Top', value: 'top' },
],
accordian: 'label',
},
direction: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' }, defaultValue: 'left' },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
{ displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
],
accordian: 'label',
isFxNotRequired: true,
},
width: {
type: 'slider',
displayName: 'Width',
accordian: 'label',
conditionallyRender: {
key: 'alignment',
value: 'side',
},
isFxNotRequired: true,
},
auto: {
type: 'checkbox',
displayName: 'auto',
showLabel: false,
validation: { schema: { type: 'boolean' }, defaultValue: true },
accordian: 'label',
conditionallyRender: {
key: 'alignment',
value: 'side',
},
isFxNotRequired: true,
},
backgroundColor: {
type: 'color',
displayName: 'Background',
validation: { schema: { type: 'string' }, defaultValue: '#fff' },
accordian: 'field',
},
accentColor: {
type: 'color',
displayName: 'Accent',
validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
accordian: 'field',
},
borderColor: {
type: 'color',
displayName: 'Border',
validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
accordian: 'field',
},
textColor: {
type: 'color',
displayName: 'Text',
validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
accordian: 'field',
},
errTextColor: {
type: 'color',
displayName: 'Error text',
validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
accordian: 'field',
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' }, defaultValue: 'IconLock' },
accordian: 'field',
visibility: false,
},
iconColor: {
type: 'color',
displayName: 'Icon color',
validation: { schema: { type: 'string' }, defaultValue: '#CFD3D859' },
accordian: 'field',
visibility: false,
showLabel: false,
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
accordian: 'field',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000040',
},
accordian: 'field',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: 'default',
},
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
isFxNotRequired: true,
accordian: 'container',
},
},
exposedVariables: {
value: '',
isMandatory: false,
isVisible: true,
isDisabled: false,
isLoading: false,
},
actions: [
{
handle: 'setText',
displayName: 'Set text',
params: [{ handle: 'text', displayName: 'text', defaultValue: 'New Text' }],
},
{
handle: 'clear',
displayName: 'Clear',
},
{
handle: 'setFocus',
displayName: 'Set focus',
},
{
handle: 'setBlur',
displayName: 'Set blur',
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
placeholder: { value: 'Password' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
loadingState: { value: '{{false}}' },
tooltip: { value: '' },
label: { value: 'Label' },
value: { value: '' },
},
validation: {
mandatory: { value: false },
regex: { value: '' },
minLength: { value: '' },
maxLength: { value: '' },
customRule: { value: '' },
},
events: [],
styles: {
borderRadius: { value: '{{6}}' },
backgroundColor: { value: '#fff' },
borderColor: { value: '#CCD1D5' },
accentColor: { value: '#4368E3' },
errTextColor: { value: '#D72D39' },
textColor: { value: '#1B1F24' },
iconColor: { value: '#CFD3D859' },
direction: { value: 'left' },
width: { value: '{{33}}' },
alignment: { value: 'side' },
color: { value: '#1B1F24' },
auto: { value: '{{true}}' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
icon: { value: 'IconLock' },
iconVisibility: { value: true },
},
},
};

View file

@ -0,0 +1,73 @@
export const pdfConfig = {
name: 'PDF',
displayName: 'PDF',
description: 'Embed PDF documents',
component: 'PDF',
properties: {
url: {
type: 'code',
displayName: 'File URL',
validation: {
schema: { type: 'string' },
defaultValue: 'https://upload.wikimedia.org/wikipedia/commons/general.pdf',
},
},
scale: {
type: 'toggle',
displayName: 'Scale page to width',
validation: { schema: { type: 'boolean' }, defaultValue: true },
},
pageControls: {
type: 'toggle',
displayName: 'Show page controls',
validation: { schema: { type: 'boolean' }, defaultValue: true },
},
showDownloadOption: {
type: 'toggle',
displayName: 'Show download button',
validation: { schema: { type: 'boolean' }, defaultValue: true },
},
},
defaultSize: {
width: 20,
height: 640,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
},
},
exposedVariables: {},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
url: {
value:
'https://upload.wikimedia.org/wikipedia/commons/e/ee/Guideline_No._GD-Ed-2214_Marman_Clamp_Systems_Design_Guidelines.pdf',
},
scale: {
value: '{{true}}',
},
pageControls: {
value: `{{true}}`,
},
showDownloadOption: {
value: `{{true}}`,
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,51 @@
export const qrscannerConfig = {
name: 'QrScanner',
displayName: 'QR Scanner',
description: 'Scan QR codes and hold its data',
component: 'QrScanner',
defaultSize: {
width: 10,
height: 300,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {},
events: {
onDetect: { displayName: 'On detect' },
},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
exposedVariables: {
lastDetectedValue: '',
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{true}}' },
},
properties: {},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,123 @@
export const radiobuttonConfig = {
name: 'RadioButton',
displayName: 'Radio Button',
description: 'Select one from multiple choices',
component: 'RadioButton',
defaultSize: {
width: 6,
height: 60,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: {
schema: { type: 'string' },
defaultValue: 'Select',
},
},
value: {
type: 'code',
displayName: 'Default value',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] },
defaultValue: true,
},
},
values: {
type: 'code',
displayName: 'Option values',
validation: {
schema: {
type: 'array',
element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] },
},
defaultValue: [true, false],
},
},
display_values: {
type: 'code',
displayName: 'Option labels',
validation: {
schema: { type: 'array', element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: ['yes', 'no'],
},
},
},
events: {
onSelectionChange: { displayName: 'On select' },
},
styles: {
textColor: {
type: 'color',
displayName: 'Text color',
validation: {
schema: { type: 'string' },
defaultValue: '#000000',
},
},
activeColor: {
type: 'color',
displayName: 'Active color',
validation: {
schema: { type: 'string' },
defaultValue: '#000000',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
actions: [
{
handle: 'selectOption',
displayName: 'Select Option',
params: [
{
handle: 'option',
displayName: 'Option',
},
],
},
],
exposedVariables: {
value: true,
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: { value: 'Select' },
value: { value: '{{true}}' },
values: { value: '{{[true,false]}}' },
display_values: { value: '{{["yes", "no"]}}' },
visible: { value: '{{true}}' },
},
events: [],
styles: {
textColor: { value: '' },
activeColor: { value: '' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,113 @@
export const rangeSliderConfig = {
name: 'RangeSlider',
displayName: 'Range Slider',
description: 'Adjust value range',
component: 'RangeSlider',
defaultSize: {
width: 9,
height: 30,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
min: {
type: 'number',
displayName: 'Min',
validation: {
schema: { type: 'number' },
defaultValue: 0,
},
},
max: {
type: 'number',
displayName: 'Max',
validation: {
schema: { type: 'number' },
defaultValue: 100,
},
},
value: {
type: 'code',
displayName: 'Value',
validation: {
schema: { type: 'number' },
defaultValue: 50,
},
},
enableTwoHandle: {
type: 'toggle',
displayName: 'Two handles',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
events: {
onChange: { displayName: 'On change' },
},
styles: {
lineColor: {
type: 'color',
displayName: 'Line color',
validation: {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
},
handleColor: {
type: 'color',
displayName: 'Handle color',
validation: {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
},
trackColor: {
type: 'color',
displayName: 'Track color',
validation: {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
},
visibility: {
type: 'code',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {
value: null,
},
definition: {
others: {
showOnDesktop: { value: true },
showOnMobile: { value: false },
},
properties: {
min: {
value: '{{0}}',
},
max: {
value: '{{100}}',
},
value: {
value: '{{50}}',
},
enableTwoHandle: { value: false },
},
events: [],
styles: {
lineColor: { value: '' },
handleColor: { value: '' },
trackColor: { value: '' },
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,73 @@
export const richtextareaConfig = {
name: 'RichTextEditor',
displayName: 'Text Editor',
description: 'Rich text editor',
component: 'RichTextEditor',
defaultSize: {
width: 16,
height: 210,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
defaultValue: 'Placeholder text',
},
},
defaultValue: {
type: 'code',
displayName: 'Default value',
validation: {
schema: { type: 'string' },
defaultValue: 'Default text',
},
},
},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: {
type: 'boolean',
},
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
},
exposedVariables: {
value: '',
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
placeholder: { value: 'Placeholder text' },
defaultValue: { value: '' },
},
events: [],
styles: {
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,60 @@
export const spinnerConfig = {
name: 'Spinner',
displayName: 'Spinner',
description: 'Indicate loading state',
component: 'Spinner',
defaultSize: {
width: 4,
height: 30,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
colour: {
type: 'color',
displayName: 'Colour',
validation: {
schema: { type: 'string' },
defaultValue: '#0565ff',
},
},
size: {
type: 'select',
displayName: 'Size',
options: [
{ name: 'small', value: 'sm' },
{ name: 'large', value: 'lg' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'sm',
},
},
},
exposedVariables: {},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {},
events: [],
styles: {
visibility: { value: '{{true}}' },
size: { value: 'sm' },
colour: { value: '#0565ff' },
},
},
};

View file

@ -0,0 +1,117 @@
export const starratingConfig = {
name: 'StarRating',
displayName: 'Rating',
description: 'Star rating',
component: 'StarRating',
defaultSize: {
width: 10,
height: 30,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
label: {
type: 'code',
displayName: 'Label',
validation: {
schema: { type: 'string' },
defaultValue: 'Select your rating',
},
},
maxRating: {
type: 'code',
displayName: 'Number of stars',
validation: {
schema: { type: 'number' },
defaultValue: 5,
},
},
defaultSelected: {
type: 'code',
displayName: 'Default no of selected stars',
validation: {
schema: { type: 'number' },
defaultValue: 5,
},
},
allowHalfStar: {
type: 'toggle',
displayName: 'Enable half star',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
tooltips: {
type: 'code',
displayName: 'Tooltips',
validation: {
schema: { type: 'array', element: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
defaultValue: '[]',
},
},
},
events: {
onChange: { displayName: 'On Change' },
},
styles: {
textColor: {
type: 'color',
displayName: 'Star color',
validation: {
schema: { type: 'string' },
defaultValue: '#ffb400',
},
},
labelColor: {
type: 'color',
displayName: 'Label color',
validation: {
schema: { type: 'string' },
defaultValue: '#000000',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
exposedVariables: {
value: 0,
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
label: { value: 'Select your rating' },
maxRating: { value: '5' },
defaultSelected: { value: '5' },
allowHalfStar: { value: '{{false}}' },
visible: { value: '{{true}}' },
tooltips: { value: '{{[]}}' },
},
events: [],
styles: {
textColor: { value: '#ffb400' },
labelColor: { value: '' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
},
};

View file

@ -0,0 +1,105 @@
export const statisticsConfig = {
name: 'Statistics',
displayName: 'Statistics',
description: 'Show key metrics',
component: 'Statistics',
defaultSize: {
width: 9,
height: 152,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
primaryValueLabel: {
type: 'code',
displayName: 'Primary value label',
validation: { schema: { type: 'string' }, defaultValue: 'This months earnings' },
},
primaryValue: {
type: 'code',
displayName: 'Primary value',
validation: {
schema: { type: 'string' },
defaultValue: '682.3',
},
},
hideSecondary: {
type: 'toggle',
displayName: 'Hide secondary value',
validation: { schema: { type: 'boolean' }, defaultValue: false },
},
secondaryValueLabel: {
type: 'code',
displayName: 'Secondary value label',
validation: { schema: { type: 'string' }, defaultValue: 'Last month' },
},
secondaryValue: {
type: 'code',
displayName: 'Secondary value',
validation: { schema: { type: 'string' }, defaultValue: '2.85' },
},
secondarySignDisplay: {
type: 'code',
displayName: 'Secondary sign display',
validation: { schema: { type: 'string' }, defaultValue: 'positive' },
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
},
},
events: {},
styles: {
primaryLabelColour: {
type: 'color',
displayName: 'Primary label colour',
validation: { schema: { type: 'string' }, defaultValue: '#8092AB' },
},
primaryTextColour: {
type: 'color',
displayName: 'Primary text colour',
validation: { schema: { type: 'string' }, defaultValue: '#000000' },
},
secondaryLabelColour: {
type: 'color',
displayName: 'Secondary label colour',
validation: { schema: { type: 'string' }, defaultValue: '#8092AB' },
},
secondaryTextColour: {
type: 'color',
displayName: 'Secondary text colour',
validation: { schema: { type: 'string' }, defaultValue: '#36AF8B' },
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: { schema: { type: 'boolean' }, defaultValue: true },
},
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
primaryValueLabel: { value: 'This months earnings' },
primaryValue: { value: '682.3' },
secondaryValueLabel: { value: 'Last month' },
secondaryValue: { value: '2.85' },
secondarySignDisplay: { value: 'positive' },
loadingState: { value: `{{false}}` },
},
events: [],
styles: {
primaryLabelColour: { value: '#8092AB' },
primaryTextColour: { value: '#000000' },
secondaryLabelColour: { value: '#8092AB' },
secondaryTextColour: { value: '#36AF8B' },
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,108 @@
export const stepsConfig = {
name: 'Steps',
displayName: 'Steps',
description: 'Step-by-step navigation aid',
component: 'Steps',
properties: {
steps: {
type: 'code',
displayName: 'Steps',
validation: {
schema: {
type: 'array',
element: { type: 'object', object: { id: { type: 'number' } } },
},
defaultValue: `[{ name: 'step 1'}, {name: 'step 2'}]`,
},
},
currentStep: {
type: 'code',
displayName: 'Current step',
validation: {
schema: { type: 'number' },
defaultValue: 1,
},
},
stepsSelectable: {
type: 'toggle',
displayName: 'Steps selectable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
},
defaultSize: {
width: 22,
height: 38,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
events: {
onSelect: { displayName: 'On select' },
},
styles: {
color: {
type: 'color',
displayName: 'Color',
validation: {
schema: { type: 'string' },
defaultValue: '#000000',
},
},
textColor: {
type: 'color',
displayName: 'Text color',
validation: {
schema: { type: 'string' },
defaultValue: '#000000',
},
},
theme: {
type: 'select',
displayName: 'Theme',
options: [
{ name: 'titles', value: 'titles' },
{ name: 'numbers', value: 'numbers' },
{ name: 'plain', value: 'plain' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'titles',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {
currentStepId: '3',
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
steps: {
value: `{{ [{ name: 'step 1', tooltip: 'some tooltip', id: 1},{ name: 'step 2', tooltip: 'some tooltip', id: 2},{ name: 'step 3', tooltip: 'some tooltip', id: 3},{ name: 'step 4', tooltip: 'some tooltip', id: 4},{ name: 'step 5', tooltip: 'some tooltip', id: 5}]}}`,
},
currentStep: { value: '{{3}}' },
stepsSelectable: { value: true },
},
events: [],
styles: {
visibility: { value: '{{true}}' },
theme: { value: 'titles' },
color: { value: '' },
textColor: { value: '' },
},
},
};

View file

@ -0,0 +1,55 @@
export const svgImageConfig = {
name: 'SvgImage',
displayName: 'Svg Image',
description: 'Display SVG graphics',
component: 'SvgImage',
properties: {
data: {
type: 'code',
displayName: 'Svg data',
validation: {
schema: { type: 'string' },
defaultValue:
"<svg xmlns='http://www.w3.org/2000/svg' class='icon' width='24' height='24' viewBox='0 0 24 24' stroke-width='2' stroke='currentColor' fill='none' stroke-linecap='round' stroke-linejoin='round'><path stroke='none' d='M0 0h24v24H0z' fill='none'/><rect x='4' y='4' width='6' height='6' rx='1' /><rect x='4' y='14' width='6' height='6' rx='1' /><rect x='14' y='14' width='6' height='6' rx='1' /><line x1='14' y1='7' x2='20' y2='7' /><line x1='17' y1='4' x2='17' y2='10' /></svg>",
},
},
},
defaultSize: {
width: 4,
height: 50,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {
value: {},
},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
data: {
value:
'<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><rect x="4" y="4" width="6" height="6" rx="1" /><rect x="4" y="14" width="6" height="6" rx="1" /><rect x="14" y="14" width="6" height="6" rx="1" /><line x1="14" y1="7" x2="20" y2="7" /><line x1="17" y1="4" x2="17" y2="10" /></svg>',
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,667 @@
export const tableConfig = {
name: 'Table',
displayName: 'Table',
description: 'Display paginated tabular data',
component: 'Table',
properties: {
title: {
type: 'string',
displayName: 'Title',
validation: {
schema: { type: 'string' },
},
},
data: {
type: 'code',
displayName: 'Table data',
validation: {
schema: {
type: 'array',
element: { type: 'object' },
},
defaultValue: "[{ id: 1, name: 'Sarah', email: 'sarah@mail.com' }]",
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
columns: {
type: 'array',
displayName: 'Table Columns',
},
useDynamicColumn: {
type: 'toggle',
displayName: 'Use dynamic column',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
columnData: {
type: 'code',
displayName: 'Column data',
validation: {
schema: { type: 'array', element: { type: 'object' } },
defaultValue:
"{{[{name: 'email', key: 'email', id: '1'}, {name: 'Full name', key: 'name', id: '2', isEditable: true}]}}}",
},
},
rowsPerPage: {
type: 'code',
displayName: 'Number of rows per page',
validation: {
schema: { type: 'number' },
defaultValue: 10,
},
},
enableNextButton: {
type: 'toggle',
displayName: 'Enable next page button',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
enabledSort: {
type: 'toggle',
displayName: 'Enable column sorting',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
hideColumnSelectorButton: {
type: 'toggle',
displayName: 'Hide column selector button',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
enablePrevButton: {
type: 'toggle',
displayName: 'Enable previous page button',
validation: {
schema: { type: 'boolean' },
},
},
totalRecords: {
type: 'code',
displayName: 'Total records server side',
validation: {
schema: { type: 'number' },
defaultValue: 10,
},
},
enablePagination: {
type: 'toggle',
displayName: 'Enable pagination',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
serverSidePagination: {
type: 'clientServerSwitch',
displayName: 'Type',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
options: [
{ displayName: 'Client side', value: 'clientSide' },
{ displayName: 'Server side', value: 'serverSide' },
],
},
serverSideSearch: {
type: 'clientServerSwitch',
displayName: 'Type',
options: [
{ displayName: 'Client side', value: 'clientSide' },
{ displayName: 'Server side', value: 'serverSide' },
],
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
serverSideSort: {
type: 'clientServerSwitch',
displayName: 'Type',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
options: [
{ displayName: 'Client side', value: 'clientSide' },
{ displayName: 'Server side', value: 'serverSide' },
],
},
serverSideFilter: {
type: 'clientServerSwitch',
displayName: 'Type',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
options: [
{ displayName: 'Client side', value: 'clientSide' },
{ displayName: 'Server side', value: 'serverSide' },
],
defaultValue: 'clientSide',
},
actionButtonBackgroundColor: {
type: 'color',
displayName: 'Background color',
validation: {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
},
actionButtonTextColor: {
type: 'color',
displayName: 'Text color',
validation: {
schema: { type: 'string' },
defaultValue: '#fff',
},
},
displaySearchBox: {
type: 'toggle',
displayName: 'Show search',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
showDownloadButton: {
type: 'toggle',
displayName: 'Show download button',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
showFilterButton: {
type: 'toggle',
displayName: 'Enable filtering',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
showBulkUpdateActions: {
type: 'toggle',
displayName: 'Show update buttons',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
allowSelection: {
type: 'toggle',
displayName: 'Allow selection',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
showBulkSelector: {
type: 'toggle',
displayName: 'Bulk selection',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
highlightSelectedRow: {
type: 'toggle',
displayName: 'Highlight selected row',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
defaultSelectedRow: {
type: 'code',
displayName: 'Default selected row',
validation: {
schema: {
type: 'object',
},
defaultValue: { id: 1 },
},
},
showAddNewRowButton: {
type: 'toggle',
displayName: 'Show add new row button',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
selectRowOnCellEdit: {
type: 'toggle',
displayName: 'Select row on cell edit',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
},
},
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop ' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
defaultSize: {
width: 35,
height: 456,
},
events: {
onRowHovered: { displayName: 'Row hovered' },
onRowClicked: { displayName: 'Row clicked' },
onBulkUpdate: { displayName: 'Save changes' },
onPageChanged: { displayName: 'Page changed' },
onSearch: { displayName: 'Search' },
onCancelChanges: { displayName: 'Cancel changes' },
onSort: { displayName: 'Sort applied' },
onCellValueChanged: { displayName: 'Cell value changed' },
onFilterChanged: { displayName: 'Filter changed' },
onNewRowsAdded: { displayName: 'Add new rows' },
},
styles: {
textColor: {
type: 'color',
displayName: 'Text Color',
validation: {
schema: { type: 'string' },
defaultValue: '#000',
},
accordian: 'Data',
},
columnHeaderWrap: {
type: 'switch',
displayName: 'Column header',
validation: { schema: { type: 'string' } },
accordian: 'Data',
options: [
{ displayName: 'Fixed', value: 'fixed' },
{ displayName: 'Wrap', value: 'wrap' },
],
},
tableType: {
type: 'select',
displayName: 'Row style',
options: [
{ name: 'Regular', value: 'table-classic' },
{ name: 'Bordered', value: 'table-bordered' },
{ name: 'Striped', value: 'table-striped' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'table-classic',
},
accordian: 'Data',
},
cellSize: {
type: 'select',
displayName: 'Cell height',
options: [
{ name: 'Regular', value: 'regular' },
{ name: 'Condensed', value: 'condensed' },
],
validation: {
schema: { type: 'string' },
defaultValue: 'regular',
},
accordian: 'Data',
},
contentWrap: {
type: 'toggle',
showLabel: false,
toggleLabel: 'Content wrap',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'boolean' }] },
},
accordian: 'Data',
},
maxRowHeight: {
type: 'switch',
displayName: 'Max row height',
validation: { schema: { type: 'string' } },
accordian: 'Data',
options: [
{ displayName: 'Auto', value: 'auto' },
{ displayName: 'Custom', value: 'custom' },
],
conditionallyRender: {
key: 'contentWrap',
value: true,
},
},
maxRowHeightValue: {
type: 'tableRowHeightInput',
isFxNotRequired: true,
showLabel: false,
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
},
accordian: 'Data',
conditionallyRender: [
{
key: 'maxRowHeight',
value: 'custom',
},
{
key: 'contentWrap',
value: true,
},
],
},
actionButtonRadius: {
type: 'numberInput',
displayName: 'Button radius',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'boolean' }] },
},
accordian: 'Action button',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
accordian: 'Container',
},
borderColor: {
type: 'color',
displayName: 'Border',
validation: {
schema: { type: 'string' },
defaultValue: false,
},
accordian: 'Container',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box Shadow',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
accordian: 'Container',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] } },
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'Container',
},
},
exposedVariables: {
selectedRow: {},
changeSet: {},
dataUpdates: [],
pageIndex: 1,
searchText: '',
selectedRows: [],
filters: [],
},
actions: [
{
handle: 'setPage',
displayName: 'Set page',
params: [
{
handle: 'page',
displayName: 'Page',
defaultValue: '{{1}}',
},
],
},
{
handle: 'selectRow',
displayName: 'Select row',
params: [
{ handle: 'key', displayName: 'Key' },
{ handle: 'value', displayName: 'Value' },
],
},
{
handle: 'deselectRow',
displayName: 'Deselect row',
},
{
handle: 'discardChanges',
displayName: 'Discard Changes',
},
{
handle: 'discardNewlyAddedRows',
displayName: 'Discard newly added rows',
},
{
displayName: 'Download table data',
handle: 'downloadTableData',
params: [
{
handle: 'type',
displayName: 'Type',
options: [
{ name: 'Download as Excel', value: 'xlsx' },
{ name: 'Download as CSV', value: 'csv' },
{ name: 'Download as PDF', value: 'pdf' },
],
defaultValue: `{{Download as Excel}}`,
type: 'select',
},
],
},
{
handle: 'selectAllRows',
displayName: 'Select all rows',
},
{
handle: 'deselectAllRows',
displayName: 'Deselect all rows',
},
{
handle: 'setFilters',
displayName: 'Set filters',
params: [{ handle: 'parameters', displayName: 'Parameters' }],
},
{
handle: 'clearFilters',
displayName: 'Clear filters',
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
title: { value: 'Table' },
visible: { value: '{{true}}' },
loadingState: { value: '{{false}}' },
data: {
value:
"{{ [ \n\t\t{ id: 1, name: 'Olivia Nguyen', email: 'olivia.nguyen@example.com', date: '15/05/2022', phone: 9876543210, interest: ['Reading', 'Traveling','Photography'], photo: 'https://reqres.in/img/faces/7-image.jpg' }, \n\t\t{ id: 2, name: 'Liam Patel', email: 'liam.patel@example.com', date: '20/09/2021', phone: 8765432109, interest: ['Cooking','Gardening','Hiking'], photo: 'https://reqres.in/img/faces/5-image.jpg' }, \n\t\t{ id: 3, name: 'Sophia Reyes', email: 'sophia.reyes@example.com', date: '01/01/2023', phone: 7654321098, interest: ['Music','Dancing','Crafting'], photo: 'https://reqres.in/img/faces/3-image.jpg' }, \n\t\t{ id: 4, name: 'Jacob Hernandez', email: 'jacob.hernandez@example.com', date: '10/11/2022', phone: 6543210987, interest: ['Reading', 'Traveling', 'Volunteering'], photo: 'https://reqres.in/img/faces/1-image.jpg' }, \n\t\t{ id: 5, name: 'William Sanchez', email: 'william.sanchez@example.com', date: '07/01/2021', phone: 4321098765, interest: ['Music', 'Dancing', 'Hiking'], photo: 'https://reqres.in/img/faces/4-image.jpg' }, \n\t\t{ id: 6, name: 'Ethan Morales', email: 'ethan.morales@example.com', date: '05/11/2021', phone: 2109876543, interest: ['Cooking', 'Traveling', 'Photography'], photo: 'https://reqres.in/img/faces/6-image.jpg' }, \n\t\t{ id: 7, name: 'Mia Tiana', email: 'mia.tiana@example.com', date: '21/11/2022', phone: 1098705217, interest: ['Music', 'Gardening', 'Hiking'], photo: 'https://reqres.in/img/faces/2-image.jpg' }, \n\t\t{ id: 8, name: 'Lucas Ramirez', email: 'lucas.ramirez@example.com', date: '31/03/2023', phone: 9876543210, interest: ['Reading', 'Dancing', 'Crafting'], photo: 'https://reqres.in/img/faces/9-image.jpg' }, \n\t\t{ id: 9, name: 'Alexander Vela', email: 'alexander.vela@example.com', date: '07/09/2022', phone: 7654321098, interest: ['Music','Gardening','Photography'], photo: 'https://reqres.in/img/faces/8-image.jpg' }, \n\t\t{ id: 10, name: 'Michael Reyes', email: 'michael.reyes@example.com', date: '25/12/2021', phone: 5432109876, interest: ['Cooking','Crafting','Volunteering'], photo: 'https://reqres.in/img/faces/10-image.jpg' } \n] }}",
},
useDynamicColumn: { value: '{{false}}' },
columnData: {
value:
"{{[{name: 'email', key: 'email', id: '1'}, {name: 'Full name', key: 'name', id: '2', isEditable: true}]}}",
},
rowsPerPage: { value: '{{10}}' },
serverSidePagination: { value: '{{false}}' },
enableNextButton: { value: '{{true}}' },
enablePrevButton: { value: '{{true}}' },
totalRecords: { value: '{{10}}' },
enablePagination: { value: '{{true}}' },
serverSideSort: { value: '{{false}}' },
serverSideFilter: { value: '{{false}}' },
displaySearchBox: { value: '{{true}}' },
showDownloadButton: { value: '{{true}}' },
showFilterButton: { value: '{{true}}' },
autogenerateColumns: { value: true, generateNestedColumns: true },
isAllColumnsEditable: { value: '{{false}}' },
columns: {
value: [
{
name: 'id',
id: 'e3ecbf7fa52c4d7210a93edb8f43776267a489bad52bd108be9588f790126737',
autogenerated: true,
fxActiveFields: [],
columnSize: 30,
columnType: 'string',
},
{
name: 'photo',
key: 'photo',
id: 'f23b7d134b2e490ea41e3bb8eeb8c8e37472af243bf6b70d5af294482097e3a2',
autogenerated: true,
fxActiveFields: [],
columnType: 'image',
objectFit: 'contain',
borderRadius: '100',
columnSize: 70,
},
{
name: 'name',
id: '5d2a3744a006388aadd012fcc15cc0dbcb5f9130e0fbb64c558561c97118754a',
autogenerated: true,
fxActiveFields: [],
columnSize: 130,
columnType: 'string',
},
{
name: 'email',
id: 'afc9a5091750a1bd4760e38760de3b4be11a43452ae8ae07ce2eebc569fe9a7f',
autogenerated: true,
fxActiveFields: [],
columnSize: 230,
columnType: 'string',
},
{
name: 'date',
id: '27b75c8af9d34d1eaa1f9bb7f8f9f7b0abf1823e799748c8bb57e74f53b2c1dc',
autogenerated: true,
fxActiveFields: [],
columnType: 'datepicker',
isTimeChecked: false,
dateFormat: 'DD/MM/YYYY',
parseDateFormat: 'DD/MM/YYYY',
isDateSelectionEnabled: true,
columnSize: 130,
},
{
name: 'phone',
id: '9c2e3c40572a4aefb8e179ee39a0e1ac9dc2b2e6634be56e1c05be13c3d1de56',
autogenerated: true,
fxActiveFields: [],
columnType: 'number',
columnSize: 140,
},
{
name: 'interest',
key: 'interest',
id: 'f23b7d134b2e490ea41e3bb8eeb8c8e37472af243bf6b70d5af294482097e3a1',
autogenerated: true,
fxActiveFields: [],
columnType: 'newMultiSelect',
columnSize: 300,
options: [
{
label: 'Reading',
value: 'Reading',
},
{
label: 'Traveling',
value: 'Traveling',
},
{
label: 'Photography',
value: 'Photography',
},
{
label: 'Music',
value: 'Music',
},
{
label: 'Cooking',
value: 'Cooking',
},
{
label: 'Crafting',
value: 'Crafting',
},
{
label: 'Voluntering',
value: 'Voluntering',
},
{
label: 'Garndening',
value: 'Garndening',
},
{
label: 'Dancing',
value: 'Dancing',
},
{
label: 'Hiking',
value: 'Hiking',
},
],
},
],
},
showBulkUpdateActions: { value: '{{true}}' },
showBulkSelector: { value: '{{false}}' },
highlightSelectedRow: { value: '{{false}}' },
columnSizes: { value: '{{({})}}' },
actions: { value: [] },
enabledSort: { value: '{{true}}' },
hideColumnSelectorButton: { value: '{{false}}' },
defaultSelectedRow: { value: '{{{"id":1}}}' },
showAddNewRowButton: { value: '{{true}}' },
allowSelection: { value: '{{true}}' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
},
events: [],
styles: {
textColor: { value: '#000' },
columnHeaderWrap: { value: 'fixed' },
actionButtonRadius: { value: '0' },
cellSize: { value: 'regular' },
borderRadius: { value: '8' },
tableType: { value: 'table-classic' },
maxRowHeight: { value: 'auto' },
maxRowHeightValue: { value: '{{0}}' }, // Setting it here as 0 since TableRowHeightInput component will set the value
contentWrap: { value: '{{true}}' },
boxShadow: { value: '0px 0px 0px 0px #00000090' },
padding: { value: 'default' },
},
},
};

View file

@ -0,0 +1,183 @@
export const tabsConfig = {
name: 'Tabs',
displayName: 'Tabs',
description: 'Organize content in tabs',
defaultSize: {
width: 30,
height: 300,
},
defaultChildren: [
{
componentName: 'Image',
layout: {
top: 60,
left: 17,
height: 100,
},
tab: 0,
properties: ['source'],
defaultValue: {
source: 'https://uploads-ssl.webflow.com/6266634263b9179f76b2236e/62666392f32677b5cb2fb84b_logo.svg',
},
},
{
componentName: 'Text',
layout: {
top: 100,
left: 5,
height: 50,
width: 34,
},
tab: 1,
properties: ['text'],
defaultValue: {
text: 'Open-source low-code framework to build & deploy internal tools within minutes.',
},
},
{
componentName: 'Table',
layout: {
top: 0,
left: 1,
width: 41,
height: 250,
},
tab: 2,
},
],
component: 'Tabs',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
tabs: {
type: 'code',
displayName: 'Tabs',
validation: {
schema: {
type: 'array',
element: {
type: 'object',
object: {
id: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }],
},
},
},
},
defaultValue: [
{ title: 'Home', id: '0' },
{ title: 'Profile', id: '1' },
{ title: 'Settings', id: '2' },
],
},
},
defaultTab: {
type: 'code',
displayName: 'Default tab',
validation: {
schema: {
type: 'union',
schemas: [{ type: 'string' }, { type: 'number' }],
},
defaultValue: '0',
},
},
hideTabs: {
type: 'toggle',
displayName: 'Hide tabs',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
renderOnlyActiveTab: {
type: 'toggle',
displayName: 'Render only active tab',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
},
events: { onTabSwitch: { displayName: 'On tab switch' } },
styles: {
highlightColor: {
type: 'color',
displayName: 'Highlight color',
validation: {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: {
type: 'boolean',
},
defaultValue: false,
},
},
tabWidth: {
type: 'select',
displayName: 'Tab width',
options: [
{ name: 'Auto', value: 'auto' },
{ name: 'Equally split', value: 'split' },
],
},
},
actions: [
{
handle: 'setTab',
displayName: 'Set current tab',
params: [
{
handle: 'id',
displayName: 'Id',
},
],
},
],
exposedVariables: { currentTab: '' },
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
tabs: {
value:
"{{[ \n\t\t{ title: 'Home', id: '0' }, \n\t\t{ title: 'Profile', id: '1' }, \n\t\t{ title: 'Settings', id: '2' } \n ]}}",
},
defaultTab: { value: '0' },
hideTabs: { value: false },
renderOnlyActiveTab: { value: false },
},
events: [],
styles: {
highlightColor: { value: '#375FCF' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
tabWidth: { value: 'auto' },
},
},
};

View file

@ -0,0 +1,59 @@
export const tagsConfig = {
name: 'Tags',
displayName: 'Tags',
description: 'Display tag labels',
component: 'Tags',
defaultSize: {
width: 8,
height: 30,
},
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
data: {
type: 'code',
displayName: 'Tags',
validation: {
schema: {
type: 'array',
element: {
type: 'object',
object: { title: { type: 'string' }, color: { type: 'string' }, textColor: { type: 'string' } },
},
},
defaultValue:
"{{ [{ title: 'success', color: '#2fb344', textColor: '#fff' }, { title: 'info', color: '#206bc4', textColor: '#fff' }] }}",
},
},
},
events: {},
styles: {
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
},
},
exposedVariables: {},
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
data: {
value:
"{{ [ \n\t\t{ title: 'success', color: '#2fb344', textColor: '#fff' }, \n\t\t{ title: 'info', color: '#206bc4', textColor: '#fff' }, \n\t\t{ title: 'warning', color: '#f59f00', textColor: '#fff' }, \n\t\t{ title: 'danger', color: '#d63939', textColor: '#fff' } ] }}",
},
},
events: [],
styles: {
visibility: { value: '{{true}}' },
},
},
};

View file

@ -0,0 +1,298 @@
export const textConfig = {
name: 'Text',
displayName: 'Text',
description: 'Display text or HTML',
component: 'Text',
others: {
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
textFormat: {
type: 'switch',
displayName: 'Text Format',
options: [
{ displayName: 'Plain text', value: 'plainText' },
{ displayName: 'Markdown', value: 'markdown' },
{ displayName: 'HTML', value: 'html' },
],
isFxNotRequired: true,
defaultValue: { value: 'plainText' },
fullWidth: true,
},
text: {
type: 'code',
displayName: 'TextComponentTextInput', // Keeping this name unique so that we can filter it in Codehinter
validation: {
schema: { type: 'string' },
defaultValue: 'Hello, there!',
},
showLabel: false,
},
loadingState: {
type: 'toggle',
displayName: 'Show loading state',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: {
schema: { type: 'boolean' },
defaultValue: false,
},
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
defaultSize: {
width: 6,
height: 40,
},
events: {
onClick: { displayName: 'On click' },
onHover: { displayName: 'On hover' },
},
styles: {
textSize: {
type: 'numberInput',
displayName: 'Size',
validation: {
schema: [{ type: 'string' }, { type: 'number' }],
defaultValue: 14,
},
accordian: 'Text',
},
fontWeight: {
type: 'select',
displayName: 'Weight',
options: [
{ name: 'normal', value: 'normal' },
{ name: 'bold', value: 'bold' },
{ name: 'lighter', value: 'lighter' },
{ name: 'bolder', value: 'bolder' },
],
accordian: 'Text',
},
fontStyle: {
type: 'switch',
displayName: 'Style',
options: [
{ displayName: 'Normal', value: 'normal', iconName: 'minus' },
{ displayName: 'Oblique', value: 'oblique', iconName: 'oblique' },
{ displayName: 'Italic', value: 'italic', iconName: 'italic' },
],
isIcon: true,
accordian: 'Text',
},
textColor: {
type: 'color',
displayName: 'Color',
validation: {
schema: { type: 'string' },
},
accordian: 'Text',
},
isScrollRequired: {
type: 'switch',
displayName: 'Scroll',
options: [
{ displayName: 'Enable', value: 'enabled' },
{ displayName: 'Disable', value: 'disabled' },
],
accordian: 'Text',
},
lineHeight: { type: 'numberInput', displayName: 'Line height', accordian: 'Text' },
textIndent: { type: 'numberInput', displayName: 'Text indent', accordian: 'Text' },
textAlign: {
type: 'alignButtons',
displayName: 'Alignment',
validation: {
schema: { type: 'string' },
defaultValue: 'left',
},
accordian: 'Text',
},
verticalAlignment: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' }, defaultValue: 'center' },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignverticallytop', value: 'top', iconName: 'alignverticallytop' },
{ displayName: 'alignverticallycenter', value: 'center', iconName: 'alignverticallycenter' },
{ displayName: 'alignverticallybottom', value: 'bottom', iconName: 'alignverticallybottom' },
],
accordian: 'Text',
isFxNotRequired: true,
},
decoration: {
type: 'switch',
displayName: 'Decoration',
isIcon: true,
options: [
{ displayName: 'none', value: 'none', iconName: 'minus' },
{ displayName: 'underline', value: 'underline', iconName: 'underline' },
{ displayName: 'overline', value: 'overline', iconName: 'overline' },
{ displayName: 'line-through', value: 'line-through', iconName: 'linethrough' },
],
accordian: 'Text',
},
transformation: {
type: 'switch',
displayName: 'Transformation',
isIcon: true,
options: [
{ displayName: 'none', value: 'none', iconName: 'minus' },
{ displayName: 'uppercase', value: 'uppercase', iconName: 'uppercase' },
{ displayName: 'lowercase', value: 'lowercase', iconName: 'lowercase' },
{ displayName: 'capitalize', value: 'capitalize', iconName: 'capitalize' },
],
accordian: 'Text',
},
letterSpacing: { type: 'numberInput', displayName: 'Letter spacing', accordian: 'Text' },
wordSpacing: { type: 'numberInput', displayName: 'Word spacing', accordian: 'Text' },
fontVariant: {
type: 'select',
displayName: 'Font variant',
options: [
{ name: 'normal', value: 'normal' },
{ name: 'small-caps', value: 'small-caps' },
{ name: 'initial', value: 'initial' },
{ name: 'inherit', value: 'inherit' },
],
accordian: 'Text',
},
backgroundColor: {
type: 'color',
displayName: 'Background',
validation: {
schema: { type: 'string' },
defaultValue: '#fff00000',
},
accordian: 'Container',
colorPickerPosition: 'top',
},
borderColor: {
type: 'color',
displayName: 'Border',
validation: {
schema: { type: 'string' },
defaultValue: '#000000',
},
accordian: 'Container',
colorPickerPosition: 'top',
},
borderRadius: {
type: 'numberInput',
displayName: 'Border radius',
validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
accordian: 'Container',
},
boxShadow: {
type: 'boxShadow',
displayName: 'Box shadow',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000090',
},
accordian: 'Container',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: { schema: { type: 'string' }, defaultValue: 'default' },
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'Container',
isFxNotRequired: true,
},
},
exposedVariables: {
text: 'Hello, there!',
},
actions: [
{
handle: 'setText',
displayName: 'Set text',
params: [{ handle: 'text', displayName: 'Text', defaultValue: 'New text' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'setVisibility', displayName: 'Value', defaultValue: `{{true}}`, type: 'toggle' }],
},
{
handle: 'clear',
displayName: 'Clear',
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'setLoading', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'setDisable', displayName: 'Value', defaultValue: `{{false}}`, type: 'toggle' }],
},
],
definition: {
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
textFormat: { value: 'html' },
text: { value: `Hello {{globals.currentUser.firstName}}👋` },
loadingState: { value: `{{false}}` },
disabledState: { value: '{{false}}' },
visibility: { value: '{{true}}' },
},
events: [],
styles: {
backgroundColor: { value: '#fff00000' },
textColor: { value: '#000000' },
textSize: { value: '{{14}}' },
textAlign: { value: 'left' },
fontWeight: { value: 'normal' },
decoration: { value: 'none' },
transformation: { value: 'none' },
fontStyle: { value: 'normal' },
lineHeight: { value: '{{1.5}}' },
textIndent: { value: '{{0}}' },
letterSpacing: { value: '{{0}}' },
wordSpacing: { value: '{{0}}' },
fontVariant: { value: 'normal' },
verticalAlignment: { value: 'center' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000090' },
borderColor: { value: '' },
borderRadius: { value: '{{6}}' },
isScrollRequired: { value: 'enabled' },
},
},
};

Some files were not shown because too many files have changed in this diff Show more