setReadOnly metodu eklendi.

This commit is contained in:
Sedat ÖZTÜRK 2026-06-01 16:47:38 +03:00
parent 57dbb0d308
commit 2df3e359c4
4 changed files with 713 additions and 29 deletions

View file

@ -2,7 +2,7 @@ import { Button, Dialog } from '@/components/ui'
import { SelectBoxOption } from '@/types/shared'
import Editor from '@monaco-editor/react'
import { useEffect, useMemo, useState } from 'react'
import { FaBolt, FaCheck, FaCode, FaPlus, FaTimes, FaTrash } from 'react-icons/fa'
import { FaBolt, FaCheck, FaCode, FaMagic, FaPlus, FaTimes, FaTrash } from 'react-icons/fa'
type CopyMapping = {
id: string
@ -30,8 +30,10 @@ type ConditionalAction = {
| 'alert'
| 'confirm'
| 'calculate'
| 'setReadOnly'
targetField: string
textValue: string
booleanValue: 'true' | 'false'
apiUrl: string
apiMethod: 'GET' | 'POST'
responsePath: string
@ -60,6 +62,35 @@ const actionIconCellClass =
const makeId = () => `${Date.now()}_${Math.random().toString(36).slice(2)}`
const formatScriptFallback = (source: string) => {
const trimmed = source.trim()
if (!trimmed) return ''
let indent = 0
return trimmed
.replace(/\s*;\s*/g, ';\n')
.replace(/\s*\{\s*/g, ' {\n')
.replace(/\s*\}\s*/g, '\n}\n')
.replace(/\n\s*\n/g, '\n')
.split('\n')
.map((line) => line.trim())
.filter(Boolean)
.map((line) => {
if (line.startsWith('}')) {
indent = Math.max(0, indent - 1)
}
const formattedLine = `${' '.repeat(indent)}${line}`
if (line.endsWith('{')) {
indent += 1
}
return formattedLine
})
.join('\n')
}
const createConditionalAction = (): ConditionalAction => ({
id: makeId(),
source: '',
@ -68,6 +99,7 @@ const createConditionalAction = (): ConditionalAction => ({
actionType: 'setField',
targetField: '',
textValue: '',
booleanValue: 'true',
apiUrl: '',
apiMethod: 'GET',
responsePath: '',
@ -124,6 +156,7 @@ function isConditionalActionReady(action: ConditionalAction) {
if (action.actionType === 'alert' || action.actionType === 'confirm')
return Boolean(action.message.trim())
if (action.actionType === 'calculate') return Boolean(action.targetField && action.formula.trim())
if (action.actionType === 'setReadOnly') return Boolean(action.targetField)
return false
}
@ -233,6 +266,9 @@ function buildScript({
const needsRollbackCurrentValue = activeConditionalActions.some(
(action) => action.actionType === 'confirm',
)
const needsSetEditorReadOnly = activeConditionalActions.some(
(action) => action.actionType === 'setReadOnly',
)
const lines: string[] = ['(async () => {']
@ -269,6 +305,12 @@ function buildScript({
)
}
if (needsSetEditorReadOnly) {
lines.push(
' const setEditorReadOnly = (field, readOnly) => { if (typeof runtimeSetEditorReadOnly === "function") runtimeSetEditorReadOnly(field, readOnly); };',
)
}
activeCopyMappings.forEach((mapping) => {
const source = mapping.source.includes('.')
? `getByPath(selectedItem, ${quote(mapping.source)})`
@ -345,6 +387,16 @@ function buildScript({
activeConditionalActions.forEach((action) => {
const condition = buildGenericCondition(action)
if (action.actionType === 'setReadOnly' && action.targetField) {
const enabledValue = action.booleanValue === 'true'
const readOnlyExpression = enabledValue ? condition : `!(${condition})`
lines.push(
` setEditorReadOnly(${quote(action.targetField)}, ${readOnlyExpression});`,
)
return
}
lines.push(` if (${condition}) {`)
if (action.actionType === 'setField' && action.targetField) {
@ -467,6 +519,10 @@ function EditorScriptBuilderDialog({
const findField = (fieldName: string) =>
availableFields.find((field) => field.toLowerCase() === fieldName.toLowerCase()) || ''
const formatScriptPreview = () => {
setScriptEditorValue((current) => formatScriptFallback(current))
}
const fillAmountDefaults = () => {
setAmountQuantityField((current) => current || findField('Quantity'))
setAmountUnitPriceField((current) => current || findField('UnitPrice'))
@ -582,14 +638,19 @@ function EditorScriptBuilderDialog({
],
)
const formattedGeneratedScript = useMemo(
() => formatScriptFallback(generatedScript),
[generatedScript],
)
useEffect(() => {
if (!isOpen) return
setScriptEditorValue((current) => {
if (current === lastGeneratedScript || !current.trim()) return generatedScript
if (current === lastGeneratedScript || !current.trim()) return formattedGeneratedScript
return current
})
setLastGeneratedScript(generatedScript)
}, [generatedScript, isOpen, lastGeneratedScript])
setLastGeneratedScript(formattedGeneratedScript)
}, [formattedGeneratedScript, isOpen, lastGeneratedScript])
const renderFieldSelect = (value: string, onChange: (value: string) => void) => (
<select
@ -664,6 +725,9 @@ function EditorScriptBuilderDialog({
if (action.actionType === 'calculate') {
return `${condition}, formül sonucunu ${target} alanına yaz.`
}
if (action.actionType === 'setReadOnly') {
return `${condition}, ${target} alanını readOnly ${action.booleanValue} yap.`
}
return condition
}
@ -888,6 +952,7 @@ function EditorScriptBuilderDialog({
<option value="alert">Uyarı göster</option>
<option value="confirm">Onay iste</option>
<option value="calculate">Formül hesapla</option>
<option value="setReadOnly">ReadOnly ayarla</option>
<option value="apiToField">API sonucunu field'a yaz</option>
</select>
</label>
@ -896,6 +961,7 @@ function EditorScriptBuilderDialog({
'setField',
'apiToField',
'calculate',
'setReadOnly',
].includes(action.actionType) && (
<div className="col-span-12 md:col-span-4">
{renderLabeledFieldSelect('Hedef field', action.targetField, (value) =>
@ -919,6 +985,26 @@ function EditorScriptBuilderDialog({
</label>
)}
{action.actionType === 'setReadOnly' && (
<label className={`${fieldLabelClass} col-span-12 md:col-span-4`}>
ReadOnly
<select
className={baseInputClass}
value={action.booleanValue}
title="true seçilirse alan salt okunur olur, false seçilirse tekrar düzenlenebilir olur."
onChange={(event) =>
updateConditionalAction(action.id, {
booleanValue: event.target
.value as ConditionalAction['booleanValue'],
})
}
>
<option value="true">true</option>
<option value="false">false</option>
</select>
</label>
)}
{action.actionType === 'apiToField' && (
<>
<label className={`${fieldLabelClass} col-span-12 md:col-span-2`}>
@ -1121,9 +1207,20 @@ function EditorScriptBuilderDialog({
<FaCode />
Script Önizleme
</span>
<div className="flex items-center gap-2">
<span className="rounded bg-gray-100 px-2 py-1 text-[11px] font-medium text-gray-500 dark:bg-gray-800 dark:text-gray-300">
TypeScript
</span>
<Button
size="xs"
type="button"
variant="plain"
icon={<FaMagic />}
onClick={formatScriptPreview}
>
Formatla
</Button>
</div>
</div>
<div className="flex-1 min-h-[350px] overflow-hidden rounded border border-gray-200 dark:border-gray-700">
<Editor

View file

@ -14,6 +14,133 @@ import { RowMode, SimpleItemWithColData } from './types'
import { PlatformEditorTypes } from '@/proxy/form/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
const flattenFormItems = (items: any[] = []): SimpleItemWithColData[] =>
items.flatMap((item) => [
...(item?.dataField ? [item] : []),
...flattenFormItems(item?.items || []),
...(item?.tabs || []).flatMap((tab: any) => flattenFormItems(tab?.items || [])),
])
const updateReadOnlyInFormItems = (items: any[] = [], field: string, readOnly: boolean) => {
let changed = false
const expected = String(field || '').toLowerCase()
const nextItems = items.map((item) => {
const key = item?.dataField || item?.name
let nextItem = item
if (key && String(key).toLowerCase() === expected) {
const editorOptions = nextItem.editorOptions || {}
if (editorOptions.readOnly !== readOnly) {
changed = true
nextItem = {
...nextItem,
editorOptions: {
...editorOptions,
readOnly,
},
}
}
}
if (nextItem?.items?.length) {
const childResult = updateReadOnlyInFormItems(nextItem.items, field, readOnly)
if (childResult.changed) {
changed = true
nextItem = { ...nextItem, items: childResult.items }
}
}
if (nextItem?.tabs?.length) {
const tabs = nextItem.tabs.map((tab: any) => {
const tabResult = updateReadOnlyInFormItems(tab.items, field, readOnly)
if (tabResult.changed) {
changed = true
return { ...tab, items: tabResult.items }
}
return tab
})
nextItem = tabs === nextItem.tabs ? nextItem : { ...nextItem, tabs }
}
return nextItem
})
return { items: nextItems, changed }
}
const findFormFieldKey = (items: any[] = [], field: string): string => {
const expected = String(field || '').toLowerCase()
for (const item of items || []) {
const key = item?.dataField || item?.name
if (key && String(key).toLowerCase() === expected) {
return key
}
const childKey = findFormFieldKey(item?.items || [], field)
if (childKey) {
return childKey
}
for (const tab of item?.tabs || []) {
const tabKey = findFormFieldKey(tab?.items || [], field)
if (tabKey) {
return tabKey
}
}
}
return field
}
const setFormEditorReadOnly = (form: any, field: string, readOnly: boolean) => {
if (!form?.option) return false
const apply = () => {
const formItems = form.option('items') || []
const resolvedField = findFormFieldKey(formItems, field)
const editor = form.getEditor?.(resolvedField) ?? form.getEditor?.(field)
const result = updateReadOnlyInFormItems(formItems, resolvedField, readOnly)
if (result.changed) {
try {
const item = form.itemOption?.(resolvedField) ?? form.itemOption?.(field)
if (item) {
form.itemOption?.(resolvedField, 'editorOptions', {
...(item.editorOptions || {}),
readOnly,
})
} else {
form.option('items', result.items)
}
} catch {
form.option('items', result.items)
}
}
const activeEditor = editor ?? form.getEditor?.(resolvedField) ?? form.getEditor?.(field)
if (activeEditor?.option?.('readOnly') !== readOnly) {
activeEditor?.option?.('readOnly', readOnly)
}
}
apply()
return true
}
const getValueByField = (data: Record<string, any> = {}, field?: string) => {
if (!field) return undefined
if (Object.prototype.hasOwnProperty.call(data, field)) return data[field]
const key = Object.keys(data).find(
(itemKey) => itemKey.toLowerCase() === String(field).toLowerCase(),
)
return key ? data[key] : undefined
}
const shouldRunEditorScriptOnContentReady = (script?: string) =>
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
const FormDevExpress = (props: {
listFormCode: string
isSubForm?: boolean
@ -28,6 +155,7 @@ const FormDevExpress = (props: {
const formDataRef = useRef(formData)
const formItemsRef = useRef(formItems)
const formInstanceRef = useRef<any>()
useEffect(() => {
formDataRef.current = formData
@ -41,7 +169,7 @@ const FormDevExpress = (props: {
useEffect(() => {
if (!refForm.current?.instance()) return
const allItems = formItems.flatMap((group) => (group.items as SimpleItemWithColData[]) || [])
const allItems = formItems.flatMap((group) => flattenFormItems([group]))
allItems.forEach((item) => {
if (item.colData?.lookupDto?.dataSourceType && item.editorOptions?.dataSource) {
@ -62,9 +190,7 @@ const FormDevExpress = (props: {
const updateCascadeDisabledStates = () => {
if (!refForm.current?.instance()) return
const allItems = formItemsRef.current.flatMap(
(group) => (group.items as SimpleItemWithColData[]) || [],
)
const allItems = formItemsRef.current.flatMap((group) => flattenFormItems([group]))
allItems.forEach((item) => {
const cascadeParentFields = item.colData?.lookupDto?.cascadeParentFields
@ -107,9 +233,7 @@ const FormDevExpress = (props: {
const newFormData = { ...formData, [e.dataField!]: e.value }
// Cascading child field'leri temizle (parent field değiştiğinde)
const allItems = formItemsRef.current.flatMap(
(group) => (group.items as SimpleItemWithColData[]) || [],
)
const allItems = formItemsRef.current.flatMap((group) => flattenFormItems([group]))
const cascadingChildren = allItems.filter((item) => {
const parentFields = item.colData?.lookupDto?.cascadeParentFields?.split(',') || []
return parentFields.some((field) => field.trim() === e.dataField)
@ -129,11 +253,24 @@ const FormDevExpress = (props: {
//Dinamik script
const changeItem = formItemsRef.current
.flatMap((group) => (group.items as SimpleItemWithColData[]) || [])
.find((i: SimpleItemWithColData) => i.dataField === e.dataField)
.flatMap((group) => flattenFormItems([group]))
.find(
(i: SimpleItemWithColData) =>
String(i.dataField || '').toLowerCase() === String(e.dataField || '').toLowerCase(),
)
if (changeItem?.editorScript) {
try {
const form = e.component
const editor = {
dataField: changeItem.dataField,
component: form,
}
const formData = newFormData
formDataRef.current = newFormData
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(formInstanceRef.current ?? form, field, readOnly)
//setFormData({...formData, Path: e.value});
//UiEvalService.ApiGenerateBackgroundWorkers();
//setFormData({ ...formData, Path: (v => v === '1' ? '1-deneme' : v === '0' ? '0-deneme' : '')(e.value) })
@ -142,8 +279,47 @@ const FormDevExpress = (props: {
console.error('Script execution failed for', changeItem.name, err)
}
}
}}
onContentReady={(e) => {
formInstanceRef.current = e.component
const form = e.component
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(form, field, readOnly)
const runReadOnlyScripts = () => {
const currentFormData = {
...(formDataRef.current || {}),
...(form?.option?.('formData') || {}),
}
formDataRef.current = currentFormData
formItemsRef.current
.flatMap((group) => flattenFormItems([group]))
.filter((formItem) => shouldRunEditorScriptOnContentReady(formItem.editorScript))
.forEach((formItem) => {
try {
const editor = {
dataField: formItem.dataField,
component: form,
}
const formData = currentFormData
const e = {
component: form,
dataField: formItem.dataField,
value: getValueByField(currentFormData, formItem.dataField),
}
eval(formItem.editorScript!)
} catch (err) {
console.error('Script execution failed on contentReady for', formItem.name, err)
}
})
}
runReadOnlyScripts()
const groupItems = e.component.option('items') as any[]
const firstItem = groupItems?.[0]?.items?.[0]

View file

@ -106,6 +106,138 @@ const getPersistedInsertedKey = (
return undefined
}
const updateReadOnlyInFormItems = (items: any[] = [], field: string, readOnly: boolean) => {
let changed = false
const expected = String(field || '').toLowerCase()
const nextItems = items.map((item) => {
const key = item?.dataField || item?.name
let nextItem = item
if (key && String(key).toLowerCase() === expected) {
const editorOptions = nextItem.editorOptions || {}
if (editorOptions.readOnly !== readOnly) {
changed = true
nextItem = {
...nextItem,
editorOptions: {
...editorOptions,
readOnly,
},
}
}
}
if (nextItem?.items?.length) {
const childResult = updateReadOnlyInFormItems(nextItem.items, field, readOnly)
if (childResult.changed) {
changed = true
nextItem = { ...nextItem, items: childResult.items }
}
}
if (nextItem?.tabs?.length) {
const tabs = nextItem.tabs.map((tab: any) => {
const tabResult = updateReadOnlyInFormItems(tab.items, field, readOnly)
if (tabResult.changed) {
changed = true
return { ...tab, items: tabResult.items }
}
return tab
})
nextItem = tabs === nextItem.tabs ? nextItem : { ...nextItem, tabs }
}
return nextItem
})
return { items: nextItems, changed }
}
const findFormFieldKey = (items: any[] = [], field: string): string => {
const expected = String(field || '').toLowerCase()
for (const item of items || []) {
const key = item?.dataField || item?.name
if (key && String(key).toLowerCase() === expected) {
return key
}
const childKey = findFormFieldKey(item?.items || [], field)
if (childKey) {
return childKey
}
for (const tab of item?.tabs || []) {
const tabKey = findFormFieldKey(tab?.items || [], field)
if (tabKey) {
return tabKey
}
}
}
return field
}
const setFormEditorReadOnly = (form: any, field: string, readOnly: boolean) => {
if (!form?.option) return false
const apply = () => {
const formItems = form.option('items') || []
const resolvedField = findFormFieldKey(formItems, field)
const editor = form.getEditor?.(resolvedField) ?? form.getEditor?.(field)
const result = updateReadOnlyInFormItems(formItems, resolvedField, readOnly)
if (result.changed) {
try {
const item = form.itemOption?.(resolvedField) ?? form.itemOption?.(field)
if (item) {
form.itemOption?.(resolvedField, 'editorOptions', {
...(item.editorOptions || {}),
readOnly,
})
} else {
form.option('items', result.items)
}
} catch {
form.option('items', result.items)
}
}
const activeEditor = editor ?? form.getEditor?.(resolvedField) ?? form.getEditor?.(field)
if (activeEditor?.option?.('readOnly') !== readOnly) {
activeEditor?.option?.('readOnly', readOnly)
}
}
apply()
return true
}
const getActiveEditingForm = (component: any) => {
const editForm = component?.getView?.('editingView')?._editForm
return editForm?.getEditor || editForm?.itemOption ? editForm : undefined
}
const flattenEditingFormItems = (items: any[] = []): EditingFormItemDto[] =>
items.flatMap((item) => [
...(item?.dataField ? [item] : []),
...flattenEditingFormItems(item?.items || []),
...(item?.tabs || []).flatMap((tab: any) => flattenEditingFormItems(tab?.items || [])),
])
const getValueByField = (data: Record<string, any> = {}, field?: string) => {
if (!field) return undefined
if (Object.prototype.hasOwnProperty.call(data, field)) return data[field]
const key = Object.keys(data).find(
(itemKey) => itemKey.toLowerCase() === String(field).toLowerCase(),
)
return key ? data[key] : undefined
}
const shouldRunEditorScriptOnContentReady = (script?: string) =>
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
const Grid = (props: GridProps) => {
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
const { translate } = useLocalization()
@ -116,6 +248,7 @@ const Grid = (props: GridProps) => {
const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const editingFormDataRef = useRef<Record<string, any>>({})
const editingFormInstanceRef = useRef<any>()
// Edit popup state kaydetmeyi engellemek için flag
const isEditingRef = useRef(false)
@ -571,7 +704,7 @@ const Grid = (props: GridProps) => {
(editor: DataGridTypes.EditorPreparingEvent<any, any>) => {
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => group.items || [])
.flatMap((group) => flattenEditingFormItems([group]))
.find((i) => i.dataField === editor.dataField)
const fieldName = editor.dataField.split(':')[0]
const columnFormat = gridDto.columnFormats.find((column) => column.fieldName === fieldName)
@ -632,7 +765,7 @@ const Grid = (props: GridProps) => {
)
try {
const childFormItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => group.items || [])
.flatMap((group) => flattenEditingFormItems([group]))
.find((i) => i.dataField === childFieldName)
const childEditorOptions = childFormItem?.editorOptions
? JSON.parse(childFormItem.editorOptions)
@ -665,6 +798,7 @@ const Grid = (props: GridProps) => {
if (formItem?.editorScript) {
const prevHandler = editor.editorOptions.onValueChanged
const editorDataField = editor.dataField
editor.editorOptions.onValueChanged = (e: any) => {
if (prevHandler) prevHandler(e)
@ -674,8 +808,16 @@ const Grid = (props: GridProps) => {
const rowKey = grid.option('editing.editRowKey')
const rowIndex = grid.getRowIndexByKey(rowKey)
const formData = grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}
const formData = {
...(grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}),
[editorDataField]: e.value,
}
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(
editingFormInstanceRef.current ?? getActiveEditingForm(grid),
field,
readOnly,
)
const setFormData = (newData: any) => {
if (rowIndex >= 0) {
Object.keys(newData).forEach((field) => {
@ -1388,6 +1530,58 @@ const Grid = (props: GridProps) => {
}}
form={{
colCount: 1,
onContentReady: (e) => {
editingFormInstanceRef.current = e.component
const form = e.component
const grid = gridRef.current?.instance()
const rowKey = grid?.option('editing.editRowKey')
const rowIndex = rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(form, field, readOnly)
const setFormData = (newData: any) => {
form?.option?.('formData', newData)
editingFormDataRef.current = { ...newData }
if (grid && rowIndex !== undefined && rowIndex >= 0) {
Object.keys(newData).forEach((field) => {
grid.cellValue(rowIndex, field, newData[field])
})
}
}
const runReadOnlyScripts = () => {
const formData = {
...editingFormDataRef.current,
...(form?.option?.('formData') || {}),
}
editingFormDataRef.current = { ...formData }
gridDto.gridOptions.editingFormDto
.flatMap((group) => flattenEditingFormItems([group]))
.filter((formItem) =>
shouldRunEditorScriptOnContentReady(formItem.editorScript),
)
.forEach((formItem) => {
try {
const editor = {
dataField: formItem.dataField,
component: grid,
}
const e = {
component: form,
dataField: formItem.dataField,
value: getValueByField(formData, formItem.dataField),
}
eval(formItem.editorScript!)
} catch (err) {
console.error('Script exec error on contentReady', formItem.dataField, err)
}
})
}
runReadOnlyScripts()
},
onFieldDataChanged: (e) => {
if (e.dataField) {
const previousValue = editingFormDataRef.current?.[e.dataField]
@ -1396,15 +1590,25 @@ const Grid = (props: GridProps) => {
}
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => group.items || [])
.find((i) => i.dataField === e.dataField)
.flatMap((group) => flattenEditingFormItems([group]))
.find(
(i) =>
String(i.dataField || '').toLowerCase() ===
String(e.dataField || '').toLowerCase(),
)
if (formItem?.editorScript) {
try {
const grid = gridRef.current?.instance()
const rowKey = grid?.option('editing.editRowKey')
const rowIndex =
rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1
const formData = e.component?.option?.('formData') || {}
const formData = {
...(e.component?.option?.('formData') || {}),
[e.dataField]: e.value,
}
editingFormDataRef.current = { ...formData }
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(e.component, field, readOnly)
const setFormData = (newData: any) => {
e.component?.option?.('formData', newData)
editingFormDataRef.current = { ...newData }
@ -1420,6 +1624,7 @@ const Grid = (props: GridProps) => {
console.error('Script exec error', err)
}
}
}
},
items:

View file

@ -94,6 +94,138 @@ const getPersistedInsertedKey = (e: any, keyFieldName?: string) => {
return undefined
}
const updateReadOnlyInFormItems = (items: any[] = [], field: string, readOnly: boolean) => {
let changed = false
const expected = String(field || '').toLowerCase()
const nextItems = items.map((item) => {
const key = item?.dataField || item?.name
let nextItem = item
if (key && String(key).toLowerCase() === expected) {
const editorOptions = nextItem.editorOptions || {}
if (editorOptions.readOnly !== readOnly) {
changed = true
nextItem = {
...nextItem,
editorOptions: {
...editorOptions,
readOnly,
},
}
}
}
if (nextItem?.items?.length) {
const childResult = updateReadOnlyInFormItems(nextItem.items, field, readOnly)
if (childResult.changed) {
changed = true
nextItem = { ...nextItem, items: childResult.items }
}
}
if (nextItem?.tabs?.length) {
const tabs = nextItem.tabs.map((tab: any) => {
const tabResult = updateReadOnlyInFormItems(tab.items, field, readOnly)
if (tabResult.changed) {
changed = true
return { ...tab, items: tabResult.items }
}
return tab
})
nextItem = tabs === nextItem.tabs ? nextItem : { ...nextItem, tabs }
}
return nextItem
})
return { items: nextItems, changed }
}
const findFormFieldKey = (items: any[] = [], field: string): string => {
const expected = String(field || '').toLowerCase()
for (const item of items || []) {
const key = item?.dataField || item?.name
if (key && String(key).toLowerCase() === expected) {
return key
}
const childKey = findFormFieldKey(item?.items || [], field)
if (childKey) {
return childKey
}
for (const tab of item?.tabs || []) {
const tabKey = findFormFieldKey(tab?.items || [], field)
if (tabKey) {
return tabKey
}
}
}
return field
}
const setFormEditorReadOnly = (form: any, field: string, readOnly: boolean) => {
if (!form?.option) return false
const apply = () => {
const formItems = form.option('items') || []
const resolvedField = findFormFieldKey(formItems, field)
const editor = form.getEditor?.(resolvedField) ?? form.getEditor?.(field)
const result = updateReadOnlyInFormItems(formItems, resolvedField, readOnly)
if (result.changed) {
try {
const item = form.itemOption?.(resolvedField) ?? form.itemOption?.(field)
if (item) {
form.itemOption?.(resolvedField, 'editorOptions', {
...(item.editorOptions || {}),
readOnly,
})
} else {
form.option('items', result.items)
}
} catch {
form.option('items', result.items)
}
}
const activeEditor = editor ?? form.getEditor?.(resolvedField) ?? form.getEditor?.(field)
if (activeEditor?.option?.('readOnly') !== readOnly) {
activeEditor?.option?.('readOnly', readOnly)
}
}
apply()
return true
}
const getActiveEditingForm = (component: any) => {
const editForm = component?.getView?.('editingView')?._editForm
return editForm?.getEditor || editForm?.itemOption ? editForm : undefined
}
const flattenEditingFormItems = (items: any[] = []): EditingFormItemDto[] =>
items.flatMap((item) => [
...(item?.dataField ? [item] : []),
...flattenEditingFormItems(item?.items || []),
...(item?.tabs || []).flatMap((tab: any) => flattenEditingFormItems(tab?.items || [])),
])
const getValueByField = (data: Record<string, any> = {}, field?: string) => {
if (!field) return undefined
if (Object.prototype.hasOwnProperty.call(data, field)) return data[field]
const key = Object.keys(data).find(
(itemKey) => itemKey.toLowerCase() === String(field).toLowerCase(),
)
return key ? data[key] : undefined
}
const shouldRunEditorScriptOnContentReady = (script?: string) =>
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
const Tree = (props: TreeProps) => {
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
const { translate } = useLocalization()
@ -103,6 +235,7 @@ const Tree = (props: TreeProps) => {
const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const editingFormDataRef = useRef<Record<string, any>>({})
const editingFormInstanceRef = useRef<any>()
// Edit popup state kaydetmeyi engellemek için flag
const isEditingRef = useRef(false)
@ -520,7 +653,7 @@ const Tree = (props: TreeProps) => {
function onEditorPreparing(editor: TreeListTypes.EditorPreparingEvent) {
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => group.items || [])
.flatMap((group) => flattenEditingFormItems([group]))
.find((i) => i.dataField === editor.dataField)
const fieldName = editor.dataField.split(':')[0]
const columnFormat = gridDto.columnFormats.find((column) => column.fieldName === fieldName)
@ -589,7 +722,7 @@ const Tree = (props: TreeProps) => {
const editorInstance = formInstance.getEditor(col.fieldName!)
if (editorInstance) {
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => group.items || [])
.flatMap((group) => flattenEditingFormItems([group]))
.find((i) => i.dataField === col.fieldName)
const editorOptions = formItem?.editorOptions
? JSON.parse(formItem.editorOptions)
@ -622,6 +755,7 @@ const Tree = (props: TreeProps) => {
if (formItem?.editorScript) {
const prevHandler = editor.editorOptions.onValueChanged
const editorDataField = editor.dataField
editor.editorOptions.onValueChanged = (e: any) => {
if (prevHandler) prevHandler(e)
@ -631,7 +765,16 @@ const Tree = (props: TreeProps) => {
const rowKey = grid.option('editing.editRowKey')
const rowIndex = grid.getRowIndexByKey(rowKey)
const formData = grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}
const formData = {
...(grid.getVisibleRows().find((r) => r.key === rowKey)?.data || {}),
[editorDataField]: e.value,
}
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(
editingFormInstanceRef.current ?? getActiveEditingForm(grid),
field,
readOnly,
)
const setFormData = (newData: any) => {
if (rowIndex >= 0) {
@ -1049,6 +1192,58 @@ const Tree = (props: TreeProps) => {
}}
form={{
colCount: 1,
onContentReady: (e) => {
editingFormInstanceRef.current = e.component
const form = e.component
const grid = gridRef.current?.instance()
const rowKey = grid?.option('editing.editRowKey')
const rowIndex = rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(form, field, readOnly)
const setFormData = (newData: any) => {
form?.option?.('formData', newData)
editingFormDataRef.current = { ...newData }
if (grid && rowIndex !== undefined && rowIndex >= 0) {
Object.keys(newData).forEach((field) => {
grid.cellValue(rowIndex, field, newData[field])
})
}
}
const runReadOnlyScripts = () => {
const formData = {
...editingFormDataRef.current,
...(form?.option?.('formData') || {}),
}
editingFormDataRef.current = { ...formData }
gridDto.gridOptions.editingFormDto
.flatMap((group) => flattenEditingFormItems([group]))
.filter((formItem) =>
shouldRunEditorScriptOnContentReady(formItem.editorScript),
)
.forEach((formItem) => {
try {
const editor = {
dataField: formItem.dataField,
component: grid,
}
const e = {
component: form,
dataField: formItem.dataField,
value: getValueByField(formData, formItem.dataField),
}
eval(formItem.editorScript!)
} catch (err) {
console.error('Script exec error on contentReady', formItem.dataField, err)
}
})
}
runReadOnlyScripts()
},
onFieldDataChanged: (e) => {
if (e.dataField) {
const previousValue = editingFormDataRef.current?.[e.dataField]
@ -1057,15 +1252,25 @@ const Tree = (props: TreeProps) => {
}
const formItem = gridDto.gridOptions.editingFormDto
.flatMap((group) => group.items || [])
.find((i) => i.dataField === e.dataField)
.flatMap((group) => flattenEditingFormItems([group]))
.find(
(i) =>
String(i.dataField || '').toLowerCase() ===
String(e.dataField || '').toLowerCase(),
)
if (formItem?.editorScript) {
try {
const grid = gridRef.current?.instance()
const rowKey = grid?.option('editing.editRowKey')
const rowIndex =
rowKey !== undefined ? grid?.getRowIndexByKey(rowKey) : -1
const formData = e.component?.option?.('formData') || {}
const formData = {
...(e.component?.option?.('formData') || {}),
[e.dataField]: e.value,
}
editingFormDataRef.current = { ...formData }
const runtimeSetEditorReadOnly = (field: string, readOnly: boolean) =>
setFormEditorReadOnly(e.component, field, readOnly)
const setFormData = (newData: any) => {
e.component?.option?.('formData', newData)
editingFormDataRef.current = { ...newData }
@ -1081,6 +1286,7 @@ const Tree = (props: TreeProps) => {
console.error('Script exec error', err)
}
}
}
},
items: