mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 17:08:34 +00:00
Extracted the common logic of input components and moved it to the AppBuilder folder
This commit is contained in:
parent
e4ffbfe21d
commit
9572644a53
6 changed files with 612 additions and 3 deletions
215
frontend/src/AppBuilder/Widgets/BaseComponents/BaseInput.jsx
Normal file
215
frontend/src/AppBuilder/Widgets/BaseComponents/BaseInput.jsx
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
import React from 'react';
|
||||
import Label from '@/_ui/Label';
|
||||
import Loader from '@/ToolJetUI/Loader/Loader';
|
||||
import * as Icons from '@tabler/icons-react';
|
||||
const tinycolor = require('tinycolor2');
|
||||
|
||||
export const BaseInput = ({
|
||||
height,
|
||||
styles,
|
||||
properties,
|
||||
darkMode,
|
||||
componentName,
|
||||
dataCy,
|
||||
// From useInput hook
|
||||
inputRef,
|
||||
labelRef,
|
||||
visibility,
|
||||
loading,
|
||||
labelWidth,
|
||||
validationError,
|
||||
showValidationError,
|
||||
isFocused,
|
||||
isMandatory,
|
||||
disable,
|
||||
value,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
handleFocus,
|
||||
handleKeyUp,
|
||||
isValid,
|
||||
// Input specific props
|
||||
inputType = 'text',
|
||||
additionalInputProps = {},
|
||||
rightIcon,
|
||||
getCustomStyles,
|
||||
}) => {
|
||||
const {
|
||||
padding,
|
||||
borderRadius,
|
||||
borderColor,
|
||||
backgroundColor,
|
||||
textColor,
|
||||
boxShadow,
|
||||
width,
|
||||
alignment,
|
||||
direction,
|
||||
color,
|
||||
auto,
|
||||
errTextColor,
|
||||
iconColor,
|
||||
accentColor,
|
||||
iconVisibility: showLeftIcon,
|
||||
icon,
|
||||
} = styles;
|
||||
|
||||
const { label, placeholder } = properties;
|
||||
const _width = (width / 100) * 70;
|
||||
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
|
||||
|
||||
const computedStyles = {
|
||||
height: height == 36 ? (padding == 'default' ? '36px' : '40px') : padding == 'default' ? height : height + 4,
|
||||
borderRadius: `${borderRadius}px`,
|
||||
color: !['#1B1F24', '#000', '#000000ff'].includes(textColor)
|
||||
? textColor
|
||||
: disable || loading
|
||||
? 'var(--text-disabled)'
|
||||
: 'var(--text-primary)',
|
||||
borderColor: isFocused
|
||||
? accentColor != '4368E3'
|
||||
? accentColor
|
||||
: 'var(--primary-accent-strong)'
|
||||
: borderColor != '#CCD1D5'
|
||||
? borderColor
|
||||
: disable || loading
|
||||
? '1px solid var(--borders-disabled-on-white)'
|
||||
: 'var(--borders-default)',
|
||||
'--tblr-input-border-color-darker': tinycolor(borderColor).darken(24).toString(),
|
||||
backgroundColor:
|
||||
backgroundColor != '#fff'
|
||||
? backgroundColor
|
||||
: disable || loading
|
||||
? darkMode
|
||||
? 'var(--surfaces-app-bg-default)'
|
||||
: 'var(--surfaces-surface-03)'
|
||||
: 'var(--surfaces-surface-01)',
|
||||
boxShadow,
|
||||
padding: showLeftIcon ? '8px 10px 8px 29px' : '8px 10px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
};
|
||||
|
||||
const loaderStyle = {
|
||||
right:
|
||||
direction === 'right' &&
|
||||
defaultAlignment === 'side' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
|
||||
? `${labelWidth + 11}px`
|
||||
: '11px',
|
||||
top:
|
||||
defaultAlignment === 'top'
|
||||
? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
'calc(50% + 10px)'
|
||||
: '',
|
||||
transform:
|
||||
defaultAlignment === 'top' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
' translateY(-50%)',
|
||||
zIndex: 3,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[icon] ?? Icons['IconHome2'];
|
||||
|
||||
const finalStyles = getCustomStyles ? getCustomStyles(computedStyles) : computedStyles;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
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))
|
||||
? 'flex-column'
|
||||
: 'align-items-center'
|
||||
} ${direction === 'right' && defaultAlignment === 'side' ? 'flex-row-reverse' : ''}
|
||||
${direction === 'right' && defaultAlignment === 'top' ? 'text-right' : ''}
|
||||
${visibility || 'invisible'}`}
|
||||
style={{
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Label
|
||||
label={label}
|
||||
width={width}
|
||||
labelRef={labelRef}
|
||||
darkMode={darkMode}
|
||||
color={color}
|
||||
defaultAlignment={defaultAlignment}
|
||||
direction={direction}
|
||||
auto={auto}
|
||||
isMandatory={isMandatory}
|
||||
_width={_width}
|
||||
labelWidth={labelWidth}
|
||||
/>
|
||||
|
||||
{showLeftIcon && (
|
||||
<IconElement
|
||||
data-cy={'text-input-icon'}
|
||||
style={{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
left:
|
||||
direction === 'right'
|
||||
? '11px'
|
||||
: defaultAlignment === 'top'
|
||||
? '11px'
|
||||
: (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
|
||||
? `${labelWidth + 11}px`
|
||||
: '11px',
|
||||
position: 'absolute',
|
||||
top:
|
||||
defaultAlignment === 'side'
|
||||
? '50%'
|
||||
: (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
|
||||
? 'calc(50% + 10px)'
|
||||
: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
color: iconColor !== '#CFD3D859' ? iconColor : 'var(--icons-weak-disabled)',
|
||||
zIndex: 3,
|
||||
}}
|
||||
stroke={1.5}
|
||||
/>
|
||||
)}
|
||||
|
||||
<input
|
||||
data-cy={dataCy}
|
||||
ref={inputRef}
|
||||
type={inputType}
|
||||
className={`tj-text-input-widget ${
|
||||
!isValid && showValidationError ? 'is-invalid' : ''
|
||||
} validation-without-icon`}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
onKeyUp={handleKeyUp}
|
||||
disabled={disable || loading}
|
||||
placeholder={placeholder}
|
||||
style={finalStyles}
|
||||
{...additionalInputProps}
|
||||
/>
|
||||
|
||||
{rightIcon}
|
||||
{loading && <Loader style={loaderStyle} width="16" />}
|
||||
</div>
|
||||
|
||||
{showValidationError && visibility && (
|
||||
<div
|
||||
data-cy={`${String(componentName).toLowerCase()}-invalid-feedback`}
|
||||
style={{
|
||||
color: errTextColor !== '#D72D39' ? errTextColor : 'var(--status-error-strong)',
|
||||
textAlign: direction == 'left' && 'end',
|
||||
fontSize: '11px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '16px',
|
||||
}}
|
||||
>
|
||||
{validationError}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
179
frontend/src/AppBuilder/Widgets/BaseComponents/hooks/useInput.js
Normal file
179
frontend/src/AppBuilder/Widgets/BaseComponents/hooks/useInput.js
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
|
||||
export const useInput = ({
|
||||
id,
|
||||
properties,
|
||||
styles,
|
||||
validation,
|
||||
validate,
|
||||
setExposedVariable,
|
||||
setExposedVariables,
|
||||
fireEvent,
|
||||
}) => {
|
||||
const isInitialRender = useRef(true);
|
||||
const inputRef = useRef();
|
||||
const labelRef = useRef();
|
||||
|
||||
const { loadingState, disabledState, label, visibility: initialVisibility } = properties;
|
||||
const isResizing = useGridStore((state) => state.resizingComponentId === id);
|
||||
|
||||
const [value, setValue] = useState(properties.value ?? '');
|
||||
const [visibility, setVisibility] = useState(initialVisibility);
|
||||
const [loading, setLoading] = useState(loadingState);
|
||||
const [disable, setDisable] = useState(disabledState || loadingState);
|
||||
const [validationStatus, setValidationStatus] = useState(validate(value));
|
||||
const [showValidationError, setShowValidationError] = useState(false);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const [labelWidth, setLabelWidth] = useState(0);
|
||||
const [iconVisibility, setIconVisibility] = useState(false);
|
||||
|
||||
const { isValid, validationError } = validationStatus;
|
||||
const isMandatory = validation?.mandatory ?? false;
|
||||
|
||||
useEffect(() => {
|
||||
if (labelRef?.current) {
|
||||
const absolutewidth = labelRef?.current?.getBoundingClientRect()?.width;
|
||||
setLabelWidth(absolutewidth);
|
||||
} else setLabelWidth(0);
|
||||
}, [
|
||||
isResizing,
|
||||
styles.width,
|
||||
styles.auto,
|
||||
styles.alignment,
|
||||
styles.iconVisibility,
|
||||
label?.length,
|
||||
isMandatory,
|
||||
styles.padding,
|
||||
styles.direction,
|
||||
labelRef?.current?.getBoundingClientRect()?.width,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setExposedVariable('label', label);
|
||||
}, [label]);
|
||||
|
||||
useEffect(() => {
|
||||
disable !== disabledState && setDisable(disabledState);
|
||||
}, [disabledState]);
|
||||
|
||||
useEffect(() => {
|
||||
visibility !== properties.visibility && setVisibility(properties.visibility);
|
||||
}, [properties.visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
loading !== loadingState && setLoading(loadingState);
|
||||
}, [loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
}, [validate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialRender.current) return;
|
||||
setInputValue(properties.value ?? '');
|
||||
}, [properties.value]);
|
||||
|
||||
useEffect(() => {
|
||||
const exposedVariables = {
|
||||
setText: async function (text) {
|
||||
setInputValue(text);
|
||||
fireEvent('onChange');
|
||||
},
|
||||
clear: async function () {
|
||||
setInputValue('');
|
||||
fireEvent('onChange');
|
||||
},
|
||||
setFocus: async function () {
|
||||
inputRef.current.focus();
|
||||
},
|
||||
setBlur: async function () {
|
||||
inputRef.current.blur();
|
||||
},
|
||||
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,
|
||||
isValid,
|
||||
value: properties.value ?? '',
|
||||
isMandatory,
|
||||
isLoading: loading,
|
||||
isVisible: visibility,
|
||||
isDisabled: disable,
|
||||
};
|
||||
|
||||
setExposedVariables(exposedVariables);
|
||||
isInitialRender.current = false;
|
||||
}, []);
|
||||
|
||||
const setInputValue = (value) => {
|
||||
setValue(value);
|
||||
setExposedVariable('value', value);
|
||||
const validationStatus = validate(value);
|
||||
setValidationStatus(validationStatus);
|
||||
setExposedVariable('isValid', validationStatus?.isValid);
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
setInputValue(e.target.value);
|
||||
fireEvent('onChange');
|
||||
};
|
||||
|
||||
const handleBlur = (e) => {
|
||||
setShowValidationError(true);
|
||||
setIsFocused(false);
|
||||
e.stopPropagation();
|
||||
fireEvent('onBlur');
|
||||
};
|
||||
|
||||
const handleFocus = (e) => {
|
||||
setIsFocused(true);
|
||||
e.stopPropagation();
|
||||
setTimeout(() => {
|
||||
fireEvent('onFocus');
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const handleKeyUp = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setInputValue(e.target.value);
|
||||
fireEvent('onEnterPressed');
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
inputRef,
|
||||
labelRef,
|
||||
value,
|
||||
visibility,
|
||||
loading,
|
||||
disable,
|
||||
validationStatus,
|
||||
showValidationError,
|
||||
isFocused,
|
||||
labelWidth,
|
||||
iconVisibility,
|
||||
setIconVisibility,
|
||||
isValid,
|
||||
validationError,
|
||||
isMandatory,
|
||||
setInputValue,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
handleFocus,
|
||||
handleKeyUp,
|
||||
};
|
||||
};
|
||||
163
frontend/src/AppBuilder/Widgets/NumberInput.jsx
Normal file
163
frontend/src/AppBuilder/Widgets/NumberInput.jsx
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import React from 'react';
|
||||
import { BaseInput } from './BaseComponents/BaseInput';
|
||||
import { useInput } from './BaseComponents/hooks/useInput';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
|
||||
export const NumberInput = (props) => {
|
||||
const inputLogic = useInput({
|
||||
...props,
|
||||
properties: {
|
||||
...props.properties,
|
||||
value: Number(parseFloat(props.properties.value).toFixed(props.properties.decimalPlaces)),
|
||||
},
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
if (e.target.value === '') {
|
||||
inputLogic.setInputValue(null);
|
||||
props.fireEvent('onChange');
|
||||
} else {
|
||||
const newValue = Number(parseFloat(e.target.value));
|
||||
inputLogic.setInputValue(newValue);
|
||||
if (!isNaN(newValue)) {
|
||||
props.fireEvent('onChange');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = (e) => {
|
||||
const value = Number(parseFloat(e.target.value).toFixed(props.properties.decimalPlaces));
|
||||
inputLogic.setInputValue(value);
|
||||
e.stopPropagation();
|
||||
props.fireEvent('onBlur');
|
||||
inputLogic.setIsFocused(false);
|
||||
};
|
||||
|
||||
const handleIncrement = (e) => {
|
||||
e.preventDefault();
|
||||
const newValue = (inputLogic.value || 0) + 1;
|
||||
inputLogic.setInputValue(newValue);
|
||||
if (!isNaN(newValue)) {
|
||||
props.fireEvent('onChange');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDecrement = (e) => {
|
||||
e.preventDefault();
|
||||
const newValue = (inputLogic.value || 0) - 1;
|
||||
inputLogic.setInputValue(newValue);
|
||||
if (!isNaN(newValue)) {
|
||||
props.fireEvent('onChange');
|
||||
}
|
||||
};
|
||||
|
||||
// Override the base input styles to account for number controls
|
||||
const getCustomStyles = (baseStyles) => {
|
||||
return {
|
||||
...baseStyles,
|
||||
paddingRight: '20px', // Make room for number controls
|
||||
};
|
||||
};
|
||||
|
||||
const numberControls = !inputLogic.isResizing && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right:
|
||||
inputLogic.labelWidth === 0
|
||||
? 0
|
||||
: props.styles.alignment === 'side' && props.styles.direction === 'right'
|
||||
? `${inputLogic.labelWidth}px`
|
||||
: 0,
|
||||
top: 0,
|
||||
height: '100%',
|
||||
width: '20px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={handleIncrement}
|
||||
style={{
|
||||
height: '50%',
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<SolidIcon
|
||||
width={props.styles.padding === 'default' ? `${props.height / 2 - 1}px` : `${props.height / 2 + 1}px`}
|
||||
height={props.styles.padding === 'default' ? `${props.height / 2 - 1}px` : `${props.height / 2 + 1}px`}
|
||||
fill={'var(--icons-default)'}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top:
|
||||
props.styles.alignment === 'top' && props.properties.label?.length > 0 && props.styles.width > 0
|
||||
? '21px'
|
||||
: '1px',
|
||||
right: '1px',
|
||||
borderLeft:
|
||||
inputLogic.disable || inputLogic.loading
|
||||
? '1px solid var(--borders-weak-disabled)'
|
||||
: '1px solid var(--borders-default)',
|
||||
borderBottom:
|
||||
inputLogic.disable || inputLogic.loading
|
||||
? '1px solid var(--borders-weak-disabled)'
|
||||
: '.5px solid var(--borders-default)',
|
||||
borderTopRightRadius: props.styles.borderRadius - 1,
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
className="numberinput-up-arrow arrow number-input-arrow"
|
||||
name="TriangleDownCenter"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
onClick={handleDecrement}
|
||||
style={{
|
||||
height: '50%',
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<SolidIcon
|
||||
fill={'var(--icons-default)'}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: '1px',
|
||||
bottom: '1px',
|
||||
borderLeft:
|
||||
inputLogic.disable || inputLogic.loading
|
||||
? '1px solid var(--borders-weak-disabled)'
|
||||
: '1px solid var(--borders-default)',
|
||||
borderTop:
|
||||
inputLogic.disable || inputLogic.loading
|
||||
? '1px solid var(--borders-weak-disabled)'
|
||||
: '.5px solid var(--borders-default)',
|
||||
borderBottomRightRadius: props.styles.borderRadius - 1,
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
width={props.styles.padding === 'default' ? `${props.height / 2 - 1}px` : `${props.height / 2 + 1}px`}
|
||||
height={props.styles.padding === 'default' ? `${props.height / 2 - 1}px` : `${props.height / 2 + 1}px`}
|
||||
className="numberinput-down-arrow arrow number-input-arrow"
|
||||
name="TriangleUpCenter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseInput
|
||||
{...props}
|
||||
{...inputLogic}
|
||||
inputType="number"
|
||||
handleChange={handleChange}
|
||||
handleBlur={handleBlur}
|
||||
additionalInputProps={{
|
||||
min: props.validation?.minValue ?? null,
|
||||
max: props.validation?.maxValue ?? null,
|
||||
}}
|
||||
rightIcon={numberControls}
|
||||
getCustomStyles={getCustomStyles}
|
||||
/>
|
||||
);
|
||||
};
|
||||
43
frontend/src/AppBuilder/Widgets/PasswordInput.jsx
Normal file
43
frontend/src/AppBuilder/Widgets/PasswordInput.jsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import { BaseInput } from './BaseComponents/BaseInput';
|
||||
import { useInput } from './BaseComponents/hooks/useInput';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
|
||||
export const PasswordInput = (props) => {
|
||||
const inputLogic = useInput(props);
|
||||
|
||||
const toggleVisibility = () => {
|
||||
inputLogic.setIconVisibility(!inputLogic.iconVisibility);
|
||||
};
|
||||
|
||||
const passwordIcon = (
|
||||
<div
|
||||
onClick={toggleVisibility}
|
||||
style={{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
position: 'absolute',
|
||||
right: '11px',
|
||||
display: 'flex',
|
||||
zIndex: 3,
|
||||
}}
|
||||
>
|
||||
<SolidIcon
|
||||
width={16}
|
||||
fill={'var(--icons-weak-disabled)'}
|
||||
className="password-component-eye"
|
||||
name={!inputLogic.iconVisibility ? 'eye1' : 'eyedisable'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseInput
|
||||
{...props}
|
||||
{...inputLogic}
|
||||
inputType={inputLogic.iconVisibility ? 'text' : 'password'}
|
||||
additionalInputProps={{ autoComplete: 'new-password' }}
|
||||
rightIcon={!inputLogic.loading && passwordIcon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
9
frontend/src/AppBuilder/Widgets/TextInput.jsx
Normal file
9
frontend/src/AppBuilder/Widgets/TextInput.jsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react';
|
||||
import { BaseInput } from './BaseComponents/BaseInput';
|
||||
import { useInput } from './BaseComponents/hooks/useInput';
|
||||
|
||||
export const TextInput = (props) => {
|
||||
const inputLogic = useInput(props);
|
||||
|
||||
return <BaseInput {...props} {...inputLogic} inputType="text" />;
|
||||
};
|
||||
|
|
@ -4,8 +4,8 @@ import { Text } from '@/Editor/Components/Text';
|
|||
// import { Table } from '@/Editor/Components/Table/Table';
|
||||
import { Table } from '@/AppBuilder/Widgets/Table/Table';
|
||||
|
||||
import { TextInput } from '@/Editor/Components/TextInput';
|
||||
import { NumberInput } from '@/Editor/Components/NumberInput';
|
||||
import { TextInput } from '@/AppBuilder/Widgets/TextInput';
|
||||
import { NumberInput } from '@/AppBuilder/Widgets/NumberInput';
|
||||
import { TextArea } from '@/Editor/Components/TextArea';
|
||||
import { RichTextEditor } from '@/Editor/Components/RichTextEditor';
|
||||
import { DropDown } from '@/Editor/Components/DropDown';
|
||||
|
|
@ -29,7 +29,7 @@ import { RadioButtonV2 } from '@/Editor/Components/RadioButtonV2/RadioButtonV2';
|
|||
import { StarRating } from '@/Editor/Components/StarRating';
|
||||
import { Divider } from '@/Editor/Components/Divider';
|
||||
import { FilePicker } from '@/Editor/Components/FilePicker';
|
||||
import { PasswordInput } from '@/Editor/Components/PasswordInput';
|
||||
import { PasswordInput } from '@/AppBuilder/Widgets/PasswordInput';
|
||||
// import { Calendar } from '@/Editor/Components/Calendar';
|
||||
// import { Listview } from '@/Editor/Components/Listview';
|
||||
import { IFrame } from '@/Editor/Components/IFrame';
|
||||
|
|
|
|||
Loading…
Reference in a new issue