From 2df3e359c4b74ac652930ee481bf1e6b7556299a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?=
<76204082+iamsedatozturk@users.noreply.github.com>
Date: Mon, 1 Jun 2026 16:47:38 +0300
Subject: [PATCH] setReadOnly metodu eklendi.
---
.../EditorScriptBuilderDialog.tsx | 111 ++++++++-
ui/src/views/form/FormDevExpress.tsx | 194 +++++++++++++++-
ui/src/views/list/Grid.tsx | 219 +++++++++++++++++-
ui/src/views/list/Tree.tsx | 218 ++++++++++++++++-
4 files changed, 713 insertions(+), 29 deletions(-)
diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/EditorScriptBuilderDialog.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/EditorScriptBuilderDialog.tsx
index 83bff7a..d84e0c5 100644
--- a/ui/src/views/admin/listForm/edit/json-row-operations/EditorScriptBuilderDialog.tsx
+++ b/ui/src/views/admin/listForm/edit/json-row-operations/EditorScriptBuilderDialog.tsx
@@ -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) => (
@@ -896,6 +961,7 @@ function EditorScriptBuilderDialog({
'setField',
'apiToField',
'calculate',
+ 'setReadOnly',
].includes(action.actionType) && (
{renderLabeledFieldSelect('Hedef field', action.targetField, (value) =>
@@ -919,6 +985,26 @@ function EditorScriptBuilderDialog({
)}
+ {action.actionType === 'setReadOnly' && (
+
+ )}
+
{action.actionType === 'apiToField' && (
<>
+ 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 = {}, 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()
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]
diff --git a/ui/src/views/list/Grid.tsx b/ui/src/views/list/Grid.tsx
index 52c5180..cee682e 100644
--- a/ui/src/views/list/Grid.tsx
+++ b/ui/src/views/list/Grid.tsx
@@ -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 = {}, 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(null)
const editingFormDataRef = useRef>({})
+ const editingFormInstanceRef = useRef()
// Edit popup state kaydetmeyi engellemek için flag
const isEditingRef = useRef(false)
@@ -571,7 +704,7 @@ const Grid = (props: GridProps) => {
(editor: DataGridTypes.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)
@@ -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:
diff --git a/ui/src/views/list/Tree.tsx b/ui/src/views/list/Tree.tsx
index f752c11..e42c31f 100644
--- a/ui/src/views/list/Tree.tsx
+++ b/ui/src/views/list/Tree.tsx
@@ -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 = {}, 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(null)
const editingFormDataRef = useRef>({})
+ const editingFormInstanceRef = useRef()
// 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: