Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit
- Updated fcTemplateLang to utilize new helper functions for improved template type handling. - Introduced typedScope for better autocompletion support in JavaScript language integration. - Enhanced CodeBlockEditor and TemplateEditor components to support cursor change events and template type management. - Added fetchTemplateIntellisense function in ApiServerContext for improved intellisense capabilities based on cursor position. - Refactored related components to ensure consistent handling of template types and cursor changes, enhancing user experience.
1184 lines
33 KiB
JavaScript
1184 lines
33 KiB
JavaScript
import PropTypes from 'prop-types'
|
|
import {
|
|
Typography,
|
|
Badge,
|
|
Input,
|
|
InputNumber,
|
|
Form,
|
|
Select,
|
|
Switch,
|
|
Tree
|
|
} from 'antd'
|
|
import IdDisplay from './IdDisplay'
|
|
import TimeDisplay from './TimeDisplay'
|
|
import TimeEdit from './TimeEdit'
|
|
import dayjs from 'dayjs'
|
|
import EmailDisplay from './EmailDisplay'
|
|
import UrlDisplay from './UrlDisplay'
|
|
import CountryDisplay from './CountryDisplay'
|
|
import CountrySelect from './CountrySelect'
|
|
import TagsDisplay from './TagsDisplay'
|
|
import TagsInput from './TagsInput'
|
|
import BoolDisplay from './BoolDisplay'
|
|
import ColorSelector from './ColorSelector'
|
|
import SecretDisplay from './SecretDisplay'
|
|
import EyeIcon from '../../Icons/EyeIcon'
|
|
import EyeSlashIcon from '../../Icons/EyeSlashIcon'
|
|
import { getPropertyValue } from '../../../database/ObjectModels'
|
|
import PropertyChanges from './PropertyChanges'
|
|
import NetGrossDisplay from './NetGrossDisplay'
|
|
import NetGrossInput from './NetGrossInput'
|
|
import MinMaxDisplay from './MinMaxDisplay'
|
|
import MinMaxInput from './MinMaxInput'
|
|
import InputNumberCal from './InputNumberCal'
|
|
import ObjectList from './ObjectList'
|
|
import VarianceDisplay from './VarianceDisplay'
|
|
import OperationDisplay from './OperationDisplay'
|
|
import MarkdownDisplay from './MarkdownDisplay'
|
|
import MarkdownInput from './MarkdownInput'
|
|
import ObjectSelect from './ObjectSelect'
|
|
import ObjectDisplay from './ObjectDisplay'
|
|
import ObjectTypeSelect from './ObjectTypeSelect'
|
|
import ObjectTypeDisplay from './ObjectTypeDisplay'
|
|
import CodeBlockEditor from './CodeBlockEditor'
|
|
import CustomSelect from './CustomSelect'
|
|
import CustomTreeSelect, { findTreeLabel } from './CustomTreeSelect'
|
|
import StateDisplay from './StateDisplay'
|
|
import AlertsDisplay from './AlertsDisplay'
|
|
import FileUpload from './FileUpload'
|
|
import DataTree from './DataTree'
|
|
import FileList from './FileList'
|
|
import ObjectChildTable from './ObjectChildTable'
|
|
import MiscId from './MiscId'
|
|
import AddressDisplay from './AddressDisplay'
|
|
import ArrayDisplay from './ArrayDisplay'
|
|
import ArrayInput from './ArrayInput'
|
|
import ElipsisText from './ElipsisText'
|
|
import PermissionsMatrix from './PermissionsMatrix'
|
|
import { round } from '../utils/Utils'
|
|
|
|
const { Text } = Typography
|
|
|
|
const getTextValue = (value) => {
|
|
if (value == null || typeof value === 'string' || typeof value === 'number') {
|
|
return value
|
|
}
|
|
|
|
if (typeof value === 'object') {
|
|
if (typeof value._id === 'string') {
|
|
return value._id
|
|
}
|
|
if (typeof value._id?._id === 'string') {
|
|
return value._id._id
|
|
}
|
|
|
|
try {
|
|
return JSON.stringify(value)
|
|
} catch {
|
|
return String(value)
|
|
}
|
|
}
|
|
|
|
return String(value)
|
|
}
|
|
|
|
const timeEditFormItemProps = {
|
|
getValueProps: (v) => ({ value: v ? dayjs(v) : null })
|
|
}
|
|
|
|
const ObjectProperty = ({
|
|
modelType = 'unknown',
|
|
type = 'text',
|
|
prefix,
|
|
size,
|
|
suffix,
|
|
value,
|
|
min,
|
|
max,
|
|
difference,
|
|
step,
|
|
isEditing = false,
|
|
formItemProps = {},
|
|
required = false,
|
|
name,
|
|
inTable = false,
|
|
label,
|
|
showLabel = false,
|
|
masterFilter = {},
|
|
language = '',
|
|
objectData = null,
|
|
parentData = null,
|
|
objectType = 'unknown',
|
|
readOnly = false,
|
|
disabled = false,
|
|
empty = false,
|
|
initial = false,
|
|
height = 'auto',
|
|
minimal = false,
|
|
autoCompleteObject = null,
|
|
errorLines = [],
|
|
templateType,
|
|
onCursorChange,
|
|
previewOpen = false,
|
|
showPreview = true,
|
|
useFormItem = true,
|
|
options = [],
|
|
roundNumber = false,
|
|
fixedNumber = false,
|
|
canAddRemove = true,
|
|
showHyperlink,
|
|
showSince,
|
|
properties = [],
|
|
onChange = null,
|
|
maxWidth = '100%',
|
|
loading = false,
|
|
rollups = [],
|
|
showCard = true,
|
|
showDownload = true,
|
|
columns = [],
|
|
style = {},
|
|
hiddenPropertyWidth = '100px',
|
|
inherit = true,
|
|
scrollHeight,
|
|
gallery = false,
|
|
...rest
|
|
}) => {
|
|
if (typeof value === 'function') {
|
|
value = value(objectData || {}, parentData)
|
|
}
|
|
|
|
if (max && typeof max == 'function' && objectData) {
|
|
max = max(objectData, parentData)
|
|
}
|
|
|
|
if (min && typeof min == 'function' && objectData) {
|
|
min = min(objectData, parentData)
|
|
}
|
|
|
|
if (objectType && typeof objectType == 'function' && objectData) {
|
|
objectType = objectType(objectData, parentData)
|
|
}
|
|
|
|
if (disabled && typeof disabled == 'function' && objectData) {
|
|
disabled = disabled(objectData, parentData)
|
|
}
|
|
|
|
if (empty && typeof empty == 'function' && objectData) {
|
|
empty = empty(objectData, parentData)
|
|
}
|
|
|
|
if (difference && typeof difference == 'function' && objectData) {
|
|
difference = difference(objectData, parentData)
|
|
}
|
|
|
|
if (prefix && typeof prefix == 'function' && objectData) {
|
|
prefix = prefix(objectData, parentData)
|
|
}
|
|
|
|
if (suffix && typeof suffix == 'function' && objectData) {
|
|
suffix = suffix(objectData, parentData)
|
|
}
|
|
|
|
if (masterFilter && typeof masterFilter == 'function' && objectData) {
|
|
masterFilter = masterFilter(objectData, parentData)
|
|
}
|
|
|
|
if (options && typeof options == 'function' && objectData) {
|
|
options = options(objectData, parentData)
|
|
}
|
|
|
|
if (readOnly && typeof readOnly == 'function' && objectData) {
|
|
readOnly = readOnly(objectData, parentData)
|
|
}
|
|
|
|
if (type && typeof type == 'function' && objectData) {
|
|
type = type(objectData, parentData)
|
|
}
|
|
|
|
if (columns && typeof columns == 'function' && objectData) {
|
|
columns = columns(objectData, parentData)
|
|
}
|
|
|
|
if (value == null) {
|
|
value = getPropertyValue(objectData, name)
|
|
}
|
|
|
|
// Split the name by "." to handle nested object properties
|
|
var formItemName = name
|
|
|
|
if (name?.includes('.')) {
|
|
formItemName = name ? name.split('.') : undefined
|
|
}
|
|
// For state type with options (editable), form field is state.type
|
|
if (type === 'state' && options?.length && !readOnly) {
|
|
formItemName = ['state', 'type']
|
|
}
|
|
|
|
var textParams = { style: { whiteSpace: 'nowrap', minWidth: '0' } }
|
|
|
|
if (disabled == true) {
|
|
textParams = { ...textParams, delete: true, type: 'secondary' }
|
|
}
|
|
|
|
const renderProperty = () => {
|
|
if (empty == true) {
|
|
return <Text type='secondary'>n/a</Text>
|
|
}
|
|
if (!isEditing || (readOnly && !initial)) {
|
|
switch (type) {
|
|
case 'netGross':
|
|
return (
|
|
<NetGrossDisplay value={value} prefix={prefix} suffix={suffix} />
|
|
)
|
|
case 'minMax':
|
|
return (
|
|
<MinMaxDisplay
|
|
value={value}
|
|
prefix={prefix}
|
|
suffix={suffix}
|
|
step={step}
|
|
/>
|
|
)
|
|
case 'secret':
|
|
if (value != null) {
|
|
return <SecretDisplay value={value} {...rest} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'wsprotocol':
|
|
switch (value) {
|
|
case 'ws':
|
|
return <Text {...textParams}>Websocket</Text>
|
|
case 'wss':
|
|
return <Text {...textParams}>Websocket Secure</Text>
|
|
default:
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'select': {
|
|
if (options && Array.isArray(options)) {
|
|
const selectValue =
|
|
options.find((option) => option.value === value) || 'n/a'
|
|
return <Text {...textParams}>{selectValue.label}</Text>
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'treeSelect': {
|
|
if (Array.isArray(value) && value.length) {
|
|
return (
|
|
<Tree
|
|
treeData={value}
|
|
fieldNames={{
|
|
title: 'title',
|
|
key: 'value',
|
|
children: 'children'
|
|
}}
|
|
height={320}
|
|
virtual
|
|
defaultExpandAll={false}
|
|
/>
|
|
)
|
|
}
|
|
const treeLabel = findTreeLabel(options, value)
|
|
if (treeLabel || (value != null && value !== '')) {
|
|
return <Text {...textParams}>{treeLabel || String(value)}</Text>
|
|
}
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'priceMode':
|
|
switch (value) {
|
|
case 'margin':
|
|
return <Text {...textParams}>Margin %</Text>
|
|
case 'amount':
|
|
return <Text {...textParams}>£ Amount</Text>
|
|
default:
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'bool': {
|
|
if (value != null) {
|
|
return <BoolDisplay value={value} yesNo={true} {...rest} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'dateTime': {
|
|
if (value != null) {
|
|
return (
|
|
<TimeDisplay dateTime={value} showSince={showSince} {...rest} />
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'country': {
|
|
if (value != null) {
|
|
return <CountryDisplay countryCode={value} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'color': {
|
|
if (value) {
|
|
return <Badge color={value} count={value.toUpperCase()} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'number': {
|
|
if (value != null) {
|
|
if (Array.isArray(value)) {
|
|
return (
|
|
<Text {...textParams}>
|
|
{prefix}
|
|
{value.length}
|
|
{suffix}
|
|
</Text>
|
|
)
|
|
} else {
|
|
var roundedValue = value
|
|
if (roundNumber != false && typeof value === 'number') {
|
|
roundedValue = round(value, roundNumber)
|
|
}
|
|
if (fixedNumber != false && typeof value === 'number') {
|
|
roundedValue = value.toFixed(fixedNumber)
|
|
}
|
|
|
|
return (
|
|
<Text {...textParams}>
|
|
{prefix}
|
|
{typeof value === 'number' ? roundedValue : value}
|
|
{suffix}
|
|
</Text>
|
|
)
|
|
}
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'numberList':
|
|
case 'stringList':
|
|
return <ArrayDisplay value={value} prefix={prefix} suffix={suffix} />
|
|
case 'variance': {
|
|
if (value != null) {
|
|
return (
|
|
<VarianceDisplay value={value} prefix={prefix} suffix={suffix} />
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'text':
|
|
if (value != null && value != '') {
|
|
return (
|
|
<ElipsisText style={style}>
|
|
{prefix}
|
|
{getTextValue(value)}
|
|
{suffix}
|
|
</ElipsisText>
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'codeBlock':
|
|
if (value != null && value != '') {
|
|
return (
|
|
<CodeBlockEditor
|
|
code={value}
|
|
language={language}
|
|
height={height}
|
|
readOnly={true}
|
|
minimal={minimal}
|
|
autoCompleteObject={autoCompleteObject}
|
|
errorLines={errorLines}
|
|
templateType={templateType}
|
|
onCursorChange={onCursorChange}
|
|
/>
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'markdown':
|
|
if (typeof value === 'string' && value !== '') {
|
|
return <MarkdownDisplay content={value} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'email':
|
|
if (value != null && value != '') {
|
|
return <EmailDisplay email={value} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'url':
|
|
if (value != null && value != '') {
|
|
return <UrlDisplay url={value} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'object': {
|
|
if (value && value._id) {
|
|
return (
|
|
<ObjectDisplay
|
|
object={value}
|
|
objectType={objectType}
|
|
showHyperlink={showHyperlink}
|
|
/>
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'objectType': {
|
|
if (value) {
|
|
return <ObjectTypeDisplay objectType={value} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'objectList': {
|
|
return (
|
|
<ObjectList
|
|
value={value}
|
|
objectType={objectType}
|
|
showHyperlink={showHyperlink}
|
|
scrollHorizontal={inTable}
|
|
/>
|
|
)
|
|
}
|
|
case 'objectChildren': {
|
|
return (
|
|
<ObjectChildTable
|
|
name={name}
|
|
value={value}
|
|
properties={properties}
|
|
objectData={objectData}
|
|
maxWidth={maxWidth}
|
|
loading={loading}
|
|
rollups={rollups}
|
|
size={size}
|
|
hiddenPropertyWidth={hiddenPropertyWidth}
|
|
minimal={minimal}
|
|
label={label}
|
|
columns={columns}
|
|
canAddRemove={canAddRemove}
|
|
/>
|
|
)
|
|
}
|
|
case 'state': {
|
|
if (value && value?.type) {
|
|
return <StateDisplay {...rest} state={value} />
|
|
} else if (value && value?.message) {
|
|
return <Text {...textParams}>{value.message}</Text>
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'id': {
|
|
if (value) {
|
|
return (
|
|
<IdDisplay
|
|
id={value}
|
|
type={objectType}
|
|
showHyperlink={showHyperlink}
|
|
{...rest}
|
|
/>
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'reference': {
|
|
if (value) {
|
|
return (
|
|
<IdDisplay
|
|
id={value}
|
|
reference={value}
|
|
type={objectType}
|
|
{...rest}
|
|
/>
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'miscId': {
|
|
return <MiscId value={value} {...rest} />
|
|
}
|
|
case 'density': {
|
|
if (value != null) {
|
|
return <Text {...textParams}>{`${value} g/cm³`}</Text>
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'alerts': {
|
|
if (value != null && value?.length != 0) {
|
|
return (
|
|
<AlertsDisplay
|
|
object={objectData}
|
|
objectType={objectType}
|
|
showDismiss={false}
|
|
showActions={false}
|
|
/>
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'tags': {
|
|
if (value != null || value?.length != 0) {
|
|
return (
|
|
<TagsDisplay
|
|
tags={value}
|
|
propertyName={name}
|
|
scrollHorizontal={inTable}
|
|
objectType={objectType}
|
|
/>
|
|
)
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'address': {
|
|
return (
|
|
<AddressDisplay
|
|
value={value}
|
|
isEditing={false}
|
|
disabled={disabled}
|
|
/>
|
|
)
|
|
}
|
|
case 'operation': {
|
|
if (value != null) {
|
|
return <OperationDisplay operation={value} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
case 'propertyChanges': {
|
|
return <PropertyChanges type={objectType} value={value} />
|
|
}
|
|
case 'data': {
|
|
return <DataTree data={value} />
|
|
}
|
|
case 'permissionsMatrix': {
|
|
return (
|
|
<PermissionsMatrix
|
|
value={value}
|
|
disabled={true}
|
|
inherit={inherit}
|
|
scrollHeight={scrollHeight}
|
|
/>
|
|
)
|
|
}
|
|
case 'file': {
|
|
if (value == null || value?.length == 0 || value == undefined) {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
} else {
|
|
return (
|
|
<FileList
|
|
files={value}
|
|
minimal={minimal}
|
|
multiple={false}
|
|
card={false}
|
|
maxWidth={maxWidth}
|
|
defaultPreviewOpen={previewOpen}
|
|
showPreview={showPreview}
|
|
showInfo={showHyperlink}
|
|
showDownload={showDownload}
|
|
gallery={gallery}
|
|
editing={false}
|
|
masterFilter={masterFilter}
|
|
/>
|
|
)
|
|
}
|
|
}
|
|
case 'fileList': {
|
|
return (
|
|
<FileList
|
|
files={value}
|
|
minimal={minimal}
|
|
multiple={true}
|
|
maxWidth={maxWidth}
|
|
defaultPreviewOpen={previewOpen}
|
|
showPreview={showPreview}
|
|
showInfo={showHyperlink}
|
|
gallery={gallery}
|
|
editing={false}
|
|
masterFilter={masterFilter}
|
|
/>
|
|
)
|
|
}
|
|
default: {
|
|
if (value != null && value !== '') {
|
|
return <Text {...textParams}>{getTextValue(value)}</Text>
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Editable mode: wrap in Form.Item
|
|
// Merge required rule if needed
|
|
let mergedFormItemProps = {
|
|
...formItemProps,
|
|
style: { flexGrow: 1, width: '100%', minWidth: 0 }
|
|
}
|
|
if (required && disabled == false) {
|
|
let rules
|
|
if (mergedFormItemProps.rules) {
|
|
rules = [...mergedFormItemProps.rules]
|
|
} else {
|
|
rules = []
|
|
}
|
|
const hasRequiredRule = rules.some((rule) => rule && rule.required)
|
|
if (!hasRequiredRule) {
|
|
rules.push({ required: true, message: '' })
|
|
}
|
|
mergedFormItemProps.rules = rules
|
|
}
|
|
if (type === 'minMax' && disabled == false) {
|
|
const rules = [...(mergedFormItemProps.rules || [])]
|
|
rules.push({
|
|
validator: (_, range) => {
|
|
if (range == null) return Promise.resolve()
|
|
if (typeof range !== 'object' || Array.isArray(range)) {
|
|
return Promise.reject(new Error('Value must be a min/max range'))
|
|
}
|
|
|
|
const values = [range.min, range.max].filter((item) => item != null)
|
|
const valid = values.every(
|
|
(item) =>
|
|
typeof item === 'number' &&
|
|
Number.isFinite(item) &&
|
|
(min == null || item >= min) &&
|
|
(max == null || item <= max)
|
|
)
|
|
|
|
return valid
|
|
? Promise.resolve()
|
|
: Promise.reject(new Error('Enter valid min and max values'))
|
|
}
|
|
})
|
|
mergedFormItemProps.rules = rules
|
|
}
|
|
if ((type === 'numberList' || type === 'stringList') && disabled == false) {
|
|
const rules = [...(mergedFormItemProps.rules || [])]
|
|
rules.push({
|
|
validator: (_, items) => {
|
|
if (items == null) return Promise.resolve()
|
|
if (!Array.isArray(items)) {
|
|
return Promise.reject(new Error('Value must be a list'))
|
|
}
|
|
|
|
const valid = items.every((item) => {
|
|
if (type === 'numberList') {
|
|
return (
|
|
typeof item === 'number' &&
|
|
Number.isFinite(item) &&
|
|
(min == null || item >= min) &&
|
|
(max == null || item <= max)
|
|
)
|
|
}
|
|
return typeof item === 'string' && item.trim().length > 0
|
|
})
|
|
|
|
return valid
|
|
? Promise.resolve()
|
|
: Promise.reject(new Error('Complete or remove empty list items'))
|
|
}
|
|
})
|
|
mergedFormItemProps.rules = rules
|
|
}
|
|
// Remove name from mergedFormItemProps if present
|
|
if (mergedFormItemProps.name) {
|
|
delete mergedFormItemProps.name
|
|
}
|
|
// If label is provided, set it on Form.Item
|
|
if (label && showLabel == true) {
|
|
mergedFormItemProps.label = label
|
|
}
|
|
// Always apply style: { margin: 0 } unless overridden
|
|
mergedFormItemProps.style = {
|
|
margin: 0,
|
|
...(mergedFormItemProps.style || {})
|
|
}
|
|
|
|
if (typeof onChange === 'function') {
|
|
mergedFormItemProps.onChange = onChange
|
|
}
|
|
|
|
const inputProps = useFormItem
|
|
? { disabled }
|
|
: {
|
|
value,
|
|
onChange,
|
|
disabled
|
|
}
|
|
|
|
const renderInput = () => {
|
|
switch (type) {
|
|
case 'netGross':
|
|
return (
|
|
<NetGrossInput
|
|
difference={difference}
|
|
prefix={prefix}
|
|
suffix={suffix}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'minMax':
|
|
return (
|
|
<MinMaxInput
|
|
prefix={prefix}
|
|
suffix={suffix}
|
|
min={min}
|
|
max={max}
|
|
step={step}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'secret':
|
|
return (
|
|
<Input.Password
|
|
placeholder={label}
|
|
iconRender={(visible) =>
|
|
visible ? <EyeSlashIcon /> : <EyeIcon />
|
|
}
|
|
{...mergedFormItemProps}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'wsprotocol':
|
|
return (
|
|
<Select
|
|
defaultValue='ws'
|
|
options={[
|
|
{ value: 'ws', label: 'Websocket' },
|
|
{ value: 'wss', label: 'Websocket Secure' }
|
|
]}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'select':
|
|
return (
|
|
<CustomSelect
|
|
placeholder={'Select a ' + label.toLowerCase() + '...'}
|
|
options={Array.isArray(options) ? options : []}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'treeSelect':
|
|
return (
|
|
<CustomTreeSelect
|
|
placeholder={'Select a ' + label.toLowerCase() + '...'}
|
|
options={Array.isArray(options) ? options : []}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'state':
|
|
if (options?.length && !readOnly) {
|
|
return (
|
|
<CustomSelect
|
|
placeholder={'Select a ' + label.toLowerCase() + '...'}
|
|
options={Array.isArray(options) ? options : []}
|
|
{...inputProps}
|
|
{...(useFormItem ? {} : { value: value?.type })}
|
|
/>
|
|
)
|
|
}
|
|
return null
|
|
case 'priceMode':
|
|
return (
|
|
<Select
|
|
defaultValue='margin'
|
|
options={[
|
|
{ value: 'margin', label: 'Margin %' },
|
|
{ value: 'amount', label: '£ Amount' }
|
|
]}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'bool':
|
|
return (
|
|
<Switch
|
|
{...inputProps}
|
|
{...(useFormItem ? {} : { checked: value })}
|
|
/>
|
|
)
|
|
case 'dateTime':
|
|
return <TimeEdit {...inputProps} />
|
|
case 'country':
|
|
return <CountrySelect {...inputProps} />
|
|
case 'color':
|
|
return (
|
|
<ColorSelector
|
|
required={required}
|
|
{...inputProps}
|
|
{...(useFormItem ? {} : { value })}
|
|
/>
|
|
)
|
|
case 'weight':
|
|
return (
|
|
<InputNumber
|
|
suffix='g'
|
|
style={{ width: '100%' }}
|
|
placeholder={label}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'number':
|
|
return (
|
|
<InputNumberCal
|
|
placeholder={label}
|
|
prefix={prefix}
|
|
suffix={suffix}
|
|
min={min}
|
|
max={max}
|
|
step={step}
|
|
style={{ width: '100%' }}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'numberList':
|
|
return (
|
|
<ArrayInput
|
|
numeric
|
|
min={min}
|
|
max={max}
|
|
step={step}
|
|
placeholder={label}
|
|
disabled={disabled}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'stringList':
|
|
return (
|
|
<ArrayInput
|
|
placeholder={label}
|
|
disabled={disabled}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'text':
|
|
return <Input placeholder={label} {...inputProps} />
|
|
case 'email':
|
|
if (Array.isArray(options) && options.length) {
|
|
return (
|
|
<CustomSelect
|
|
placeholder={'Select a ' + label.toLowerCase() + '...'}
|
|
options={options}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
}
|
|
return <Input type='email' placeholder={label} {...inputProps} />
|
|
case 'codeBlock':
|
|
return (
|
|
<CodeBlockEditor
|
|
code={value}
|
|
language={language}
|
|
height={height}
|
|
minimal={minimal}
|
|
autoCompleteObject={autoCompleteObject}
|
|
errorLines={errorLines}
|
|
templateType={templateType}
|
|
onCursorChange={onCursorChange}
|
|
{...(onChange && !useFormItem ? { onChange } : {})}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'markdown':
|
|
return (
|
|
<MarkdownInput {...inputProps} size={size} showCard={showCard} />
|
|
)
|
|
case 'id':
|
|
// id is not editable, just show view mode
|
|
if (value) {
|
|
return <IdDisplay id={value} type={objectType} {...rest} />
|
|
} else {
|
|
return (
|
|
<Text type='secondary' {...textParams}>
|
|
n/a
|
|
</Text>
|
|
)
|
|
}
|
|
case 'object':
|
|
return (
|
|
<ObjectSelect
|
|
type={objectType}
|
|
masterFilter={masterFilter}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'objectType':
|
|
return (
|
|
<ObjectTypeSelect masterFilter={masterFilter} {...inputProps} />
|
|
)
|
|
case 'objectList':
|
|
return (
|
|
<ObjectSelect
|
|
type={objectType}
|
|
multiple
|
|
showHyperlink={showHyperlink}
|
|
masterFilter={masterFilter}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'tags':
|
|
return (
|
|
<TagsInput
|
|
propertyName={name}
|
|
modelType={modelType}
|
|
disabled={disabled}
|
|
placeholder={label}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'address':
|
|
return (
|
|
<AddressDisplay
|
|
value={value}
|
|
isEditing={true}
|
|
onChange={onChange}
|
|
disabled={disabled}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'file':
|
|
return (
|
|
<FileUpload
|
|
multiple={false}
|
|
minimal={minimal}
|
|
defaultPreviewOpen={previewOpen}
|
|
showPreview={showPreview}
|
|
showInfo={showHyperlink}
|
|
gallery={gallery}
|
|
masterFilter={masterFilter}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'fileList':
|
|
return (
|
|
<FileUpload
|
|
multiple={true}
|
|
minimal={minimal}
|
|
defaultPreviewOpen={previewOpen}
|
|
showPreview={showPreview}
|
|
showInfo={showHyperlink}
|
|
gallery={gallery}
|
|
masterFilter={masterFilter}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
case 'objectChildren': {
|
|
return (
|
|
<ObjectChildTable
|
|
name={name}
|
|
properties={properties}
|
|
columns={columns}
|
|
objectData={objectData}
|
|
isEditing={true}
|
|
rollups={rollups}
|
|
hiddenPropertyWidth={hiddenPropertyWidth}
|
|
size={size}
|
|
canAddRemove={canAddRemove}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
}
|
|
case 'permissionsMatrix':
|
|
return (
|
|
<PermissionsMatrix
|
|
inherit={inherit}
|
|
scrollHeight={scrollHeight}
|
|
disabled={disabled}
|
|
{...inputProps}
|
|
/>
|
|
)
|
|
default:
|
|
return <Input placeholder={label} {...inputProps} />
|
|
}
|
|
}
|
|
|
|
if (!useFormItem) {
|
|
return renderInput()
|
|
}
|
|
|
|
return (
|
|
<Form.Item
|
|
name={formItemName}
|
|
{...mergedFormItemProps}
|
|
{...(type === 'bool' ? { valuePropName: 'checked' } : {})}
|
|
{...(type === 'color'
|
|
? { valuePropName: 'value', getValueFromEvent: (v) => v }
|
|
: {})}
|
|
{...(type === 'dateTime' ? timeEditFormItemProps : {})}
|
|
>
|
|
{renderInput()}
|
|
</Form.Item>
|
|
)
|
|
}
|
|
|
|
const property = renderProperty()
|
|
// Render the property directly (remove useDescriptions functionality)
|
|
return property
|
|
}
|
|
|
|
ObjectProperty.propTypes = {
|
|
type: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
|
|
modelType: PropTypes.string,
|
|
value: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
|
isEditing: PropTypes.bool,
|
|
formItemProps: PropTypes.object,
|
|
masterFilter: PropTypes.object,
|
|
required: PropTypes.bool,
|
|
name: PropTypes.string,
|
|
language: PropTypes.string,
|
|
autoCompleteObject: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
|
errorLines: PropTypes.arrayOf(PropTypes.number),
|
|
templateType: PropTypes.string,
|
|
onCursorChange: PropTypes.func,
|
|
prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
suffix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
min: PropTypes.number,
|
|
max: PropTypes.number,
|
|
step: PropTypes.number,
|
|
showLabel: PropTypes.bool,
|
|
objectType: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
readOnly: PropTypes.bool,
|
|
disabled: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
|
empty: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
|
difference: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
|
objectData: PropTypes.object,
|
|
height: PropTypes.string,
|
|
previewOpen: PropTypes.bool,
|
|
showPreview: PropTypes.bool,
|
|
useFormItem: PropTypes.bool,
|
|
showHyperlink: PropTypes.bool,
|
|
options: PropTypes.oneOfType([PropTypes.array, PropTypes.func]),
|
|
showSince: PropTypes.bool,
|
|
inTable: PropTypes.bool,
|
|
loading: PropTypes.bool,
|
|
rollups: PropTypes.arrayOf(PropTypes.object),
|
|
canAddRemove: PropTypes.bool,
|
|
showCard: PropTypes.bool,
|
|
showDownload: PropTypes.bool,
|
|
style: PropTypes.object,
|
|
hiddenPropertyWidth: PropTypes.string,
|
|
inherit: PropTypes.bool,
|
|
scrollHeight: PropTypes.string,
|
|
gallery: PropTypes.bool
|
|
}
|
|
|
|
export default ObjectProperty
|