Mobil Grid ve Tree Popup EditForm problemi
This commit is contained in:
parent
48894d2bda
commit
88c838aac8
4 changed files with 437 additions and 216 deletions
|
|
@ -140,7 +140,9 @@ const getValueByField = (data: Record<string, any> = {}, field?: string) => {
|
|||
}
|
||||
|
||||
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
||||
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
|
||||
Boolean(
|
||||
script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')),
|
||||
)
|
||||
|
||||
const FormDevExpress = (props: {
|
||||
listFormCode: string
|
||||
|
|
@ -158,9 +160,15 @@ const FormDevExpress = (props: {
|
|||
const formItemsRef = useRef(formItems)
|
||||
const formInstanceRef = useRef<any>()
|
||||
const lastContentReadyScriptKeyRef = useRef<string>()
|
||||
const didAutoFocusRef = useRef(false)
|
||||
const [runtimeReadOnlyFields, setRuntimeReadOnlyFields] = useState<Record<string, boolean>>({})
|
||||
const runtimeReadOnlyFieldsRef = useRef<Record<string, boolean>>({})
|
||||
|
||||
const isTouchLikeDevice = () =>
|
||||
typeof window !== 'undefined' &&
|
||||
(window.matchMedia?.('(pointer: coarse)').matches ||
|
||||
window.matchMedia?.('(hover: none)').matches)
|
||||
|
||||
useEffect(() => {
|
||||
formDataRef.current = formData
|
||||
}, [formData])
|
||||
|
|
@ -173,6 +181,10 @@ const FormDevExpress = (props: {
|
|||
runtimeReadOnlyFieldsRef.current = runtimeReadOnlyFields
|
||||
}, [runtimeReadOnlyFields])
|
||||
|
||||
useEffect(() => {
|
||||
didAutoFocusRef.current = false
|
||||
}, [listFormCode, mode])
|
||||
|
||||
const setRuntimeEditorReadOnly = (field: string, readOnly: boolean) => {
|
||||
const resolvedField = findFormFieldKey(formItemsRef.current, field)
|
||||
const key = String(resolvedField || field || '').toLowerCase()
|
||||
|
|
@ -202,11 +214,7 @@ const FormDevExpress = (props: {
|
|||
setTimeout(() => setFormEditorReadOnly(formInstanceRef.current ?? form, field, readOnly), 0)
|
||||
}
|
||||
|
||||
const runEditorScript = (
|
||||
formItem: SimpleItemWithColData,
|
||||
eventValue: any,
|
||||
component?: any,
|
||||
) => {
|
||||
const runEditorScript = (formItem: SimpleItemWithColData, eventValue: any, component?: any) => {
|
||||
if (!formItem?.editorScript) {
|
||||
return
|
||||
}
|
||||
|
|
@ -250,7 +258,9 @@ const FormDevExpress = (props: {
|
|||
const prevOnValueChanged = formItem.editorOptions?.onValueChanged
|
||||
|
||||
return {
|
||||
...(index !== undefined && mode !== 'view' ? { autoFocus: index === 1 } : {}),
|
||||
...(index !== undefined && mode !== 'view' && !isTouchLikeDevice()
|
||||
? { autoFocus: index === 1 }
|
||||
: {}),
|
||||
...(formItem.editorType === 'dxDateBox'
|
||||
? {
|
||||
useMaskBehavior: true,
|
||||
|
|
@ -466,7 +476,6 @@ const FormDevExpress = (props: {
|
|||
setTimeout(() => {
|
||||
updateCascadeDisabledStates()
|
||||
}, 0)
|
||||
|
||||
}}
|
||||
onContentReady={(e) => {
|
||||
formInstanceRef.current = e.component
|
||||
|
|
@ -478,7 +487,8 @@ const FormDevExpress = (props: {
|
|||
const groupItems = e.component.option('items') as any[]
|
||||
const firstItem = groupItems?.[0]?.items?.[0]
|
||||
|
||||
if (firstItem?.dataField) {
|
||||
if (!didAutoFocusRef.current && firstItem?.dataField && !isTouchLikeDevice()) {
|
||||
didAutoFocusRef.current = true
|
||||
const editor = e.component.getEditor(firstItem.dataField)
|
||||
mode !== 'view' && editor?.focus()
|
||||
}
|
||||
|
|
@ -492,98 +502,100 @@ const FormDevExpress = (props: {
|
|||
colSpan={formGroupItem.colSpan}
|
||||
caption={formGroupItem.caption}
|
||||
>
|
||||
{(formGroupItem.items as SimpleItemWithColData[])?.filter((formItem) => {
|
||||
if (mode === 'edit') return formItem.allowEditing !== false
|
||||
if (mode === 'new') return formItem.allowAdding !== false
|
||||
return true
|
||||
}).map((formItem, i) => {
|
||||
return formItem.editorType2 === PlatformEditorTypes.dxTagBox ? (
|
||||
<SimpleItemDx
|
||||
cssClass="font-semibold"
|
||||
key={getFormItemKey(formItem, i)}
|
||||
{...formItem}
|
||||
render={() => (
|
||||
<TagBoxEditorComponent
|
||||
value={formData[formItem.dataField!] || []}
|
||||
setDefaultValue={false}
|
||||
values={formData}
|
||||
options={formItem.tagBoxOptions}
|
||||
col={formItem.colData}
|
||||
onValueChanged={(e: any) => {
|
||||
const newData = { ...formDataRef.current, [formItem.dataField!]: e }
|
||||
formDataRef.current = newData
|
||||
setFormData(newData)
|
||||
runEditorScript(formItem, e, formInstanceRef.current)
|
||||
}}
|
||||
editorOptions={getEditorOptions(formItem)}
|
||||
></TagBoxEditorComponent>
|
||||
)}
|
||||
label={{
|
||||
text: translate('::' + formItem.colData?.captionName),
|
||||
className: 'font-semibold',
|
||||
}}
|
||||
></SimpleItemDx>
|
||||
) : formItem.editorType2 === PlatformEditorTypes.dxGridBox ? (
|
||||
<SimpleItemDx
|
||||
cssClass="font-semibold"
|
||||
key={getFormItemKey(formItem, i)}
|
||||
{...formItem}
|
||||
render={() => (
|
||||
<GridBoxEditorComponent
|
||||
value={formData[formItem.dataField!] || []}
|
||||
values={formData}
|
||||
options={formItem.gridBoxOptions}
|
||||
col={formItem.colData}
|
||||
onValueChanged={(e: any) => {
|
||||
const newData = { ...formDataRef.current, [formItem.dataField!]: e }
|
||||
formDataRef.current = newData
|
||||
setFormData(newData)
|
||||
runEditorScript(formItem, e, formInstanceRef.current)
|
||||
}}
|
||||
editorOptions={getEditorOptions(formItem)}
|
||||
></GridBoxEditorComponent>
|
||||
)}
|
||||
label={{
|
||||
text: translate('::' + formItem.colData?.captionName),
|
||||
className: 'font-semibold',
|
||||
}}
|
||||
></SimpleItemDx>
|
||||
) : formItem.editorType2 === PlatformEditorTypes.dxImageUpload ? (
|
||||
<SimpleItemDx
|
||||
cssClass="font-semibold"
|
||||
key={getFormItemKey(formItem, i)}
|
||||
dataField={formItem.dataField}
|
||||
name={formItem.name}
|
||||
colSpan={formItem.colSpan}
|
||||
isRequired={formItem.isRequired}
|
||||
render={() => (
|
||||
<ImageUploadEditorComponent
|
||||
value={formData[formItem.dataField!]}
|
||||
options={formItem.imageUploadOptions}
|
||||
onValueChanged={(val: any) => {
|
||||
const newData = { ...formDataRef.current, [formItem.dataField!]: val }
|
||||
formDataRef.current = newData
|
||||
setFormData(newData)
|
||||
runEditorScript(formItem, val, formInstanceRef.current)
|
||||
}}
|
||||
editorOptions={getEditorOptions(formItem)}
|
||||
/>
|
||||
)}
|
||||
label={{
|
||||
text: translate('::' + formItem.colData?.captionName),
|
||||
className: 'font-semibold',
|
||||
}}
|
||||
></SimpleItemDx>
|
||||
) : (
|
||||
<SimpleItemDx
|
||||
cssClass="font-semibold"
|
||||
key={getFormItemKey(formItem, i)}
|
||||
{...formItem}
|
||||
editorOptions={getEditorOptions(formItem, i)}
|
||||
label={{ text: translate('::' + formItem.colData?.captionName) }}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{(formGroupItem.items as SimpleItemWithColData[])
|
||||
?.filter((formItem) => {
|
||||
if (mode === 'edit') return formItem.allowEditing !== false
|
||||
if (mode === 'new') return formItem.allowAdding !== false
|
||||
return true
|
||||
})
|
||||
.map((formItem, i) => {
|
||||
return formItem.editorType2 === PlatformEditorTypes.dxTagBox ? (
|
||||
<SimpleItemDx
|
||||
cssClass="font-semibold"
|
||||
key={getFormItemKey(formItem, i)}
|
||||
{...formItem}
|
||||
render={() => (
|
||||
<TagBoxEditorComponent
|
||||
value={formData[formItem.dataField!] || []}
|
||||
setDefaultValue={false}
|
||||
values={formData}
|
||||
options={formItem.tagBoxOptions}
|
||||
col={formItem.colData}
|
||||
onValueChanged={(e: any) => {
|
||||
const newData = { ...formDataRef.current, [formItem.dataField!]: e }
|
||||
formDataRef.current = newData
|
||||
setFormData(newData)
|
||||
runEditorScript(formItem, e, formInstanceRef.current)
|
||||
}}
|
||||
editorOptions={getEditorOptions(formItem)}
|
||||
></TagBoxEditorComponent>
|
||||
)}
|
||||
label={{
|
||||
text: translate('::' + formItem.colData?.captionName),
|
||||
className: 'font-semibold',
|
||||
}}
|
||||
></SimpleItemDx>
|
||||
) : formItem.editorType2 === PlatformEditorTypes.dxGridBox ? (
|
||||
<SimpleItemDx
|
||||
cssClass="font-semibold"
|
||||
key={getFormItemKey(formItem, i)}
|
||||
{...formItem}
|
||||
render={() => (
|
||||
<GridBoxEditorComponent
|
||||
value={formData[formItem.dataField!] || []}
|
||||
values={formData}
|
||||
options={formItem.gridBoxOptions}
|
||||
col={formItem.colData}
|
||||
onValueChanged={(e: any) => {
|
||||
const newData = { ...formDataRef.current, [formItem.dataField!]: e }
|
||||
formDataRef.current = newData
|
||||
setFormData(newData)
|
||||
runEditorScript(formItem, e, formInstanceRef.current)
|
||||
}}
|
||||
editorOptions={getEditorOptions(formItem)}
|
||||
></GridBoxEditorComponent>
|
||||
)}
|
||||
label={{
|
||||
text: translate('::' + formItem.colData?.captionName),
|
||||
className: 'font-semibold',
|
||||
}}
|
||||
></SimpleItemDx>
|
||||
) : formItem.editorType2 === PlatformEditorTypes.dxImageUpload ? (
|
||||
<SimpleItemDx
|
||||
cssClass="font-semibold"
|
||||
key={getFormItemKey(formItem, i)}
|
||||
dataField={formItem.dataField}
|
||||
name={formItem.name}
|
||||
colSpan={formItem.colSpan}
|
||||
isRequired={formItem.isRequired}
|
||||
render={() => (
|
||||
<ImageUploadEditorComponent
|
||||
value={formData[formItem.dataField!]}
|
||||
options={formItem.imageUploadOptions}
|
||||
onValueChanged={(val: any) => {
|
||||
const newData = { ...formDataRef.current, [formItem.dataField!]: val }
|
||||
formDataRef.current = newData
|
||||
setFormData(newData)
|
||||
runEditorScript(formItem, val, formInstanceRef.current)
|
||||
}}
|
||||
editorOptions={getEditorOptions(formItem)}
|
||||
/>
|
||||
)}
|
||||
label={{
|
||||
text: translate('::' + formItem.colData?.captionName),
|
||||
className: 'font-semibold',
|
||||
}}
|
||||
></SimpleItemDx>
|
||||
) : (
|
||||
<SimpleItemDx
|
||||
cssClass="font-semibold"
|
||||
key={getFormItemKey(formItem, i)}
|
||||
{...formItem}
|
||||
editorOptions={getEditorOptions(formItem, i)}
|
||||
label={{ text: translate('::' + formItem.colData?.captionName) }}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</GroupItemDx>
|
||||
)
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@ import { layoutTypes } from '../admin/listForm/edit/types'
|
|||
import { useListFormCustomDataSource } from '../list/useListFormCustomDataSource'
|
||||
import { useListFormColumns } from '../list/useListFormColumns'
|
||||
|
||||
const flattenFormItems = (items: any[] = []): SimpleItemWithColData[] =>
|
||||
items.flatMap((item) => [
|
||||
...(item?.dataField ? [item] : []),
|
||||
...flattenFormItems(item?.items || []),
|
||||
...(item?.tabs || []).flatMap((tab: any) => flattenFormItems(tab?.items || [])),
|
||||
])
|
||||
|
||||
const useGridData = (props: {
|
||||
mode: RowMode
|
||||
listFormCode: string
|
||||
|
|
@ -41,6 +48,7 @@ const useGridData = (props: {
|
|||
const [permissionResults, setPermissionResults] = useState<PermissionResults>()
|
||||
|
||||
const refForm = useRef<FormRef>(null)
|
||||
const previousFormDataRef = useRef<any>()
|
||||
const [searchParams] = useSearchParams()
|
||||
const navigate = useNavigate()
|
||||
const { translate } = useLocalization()
|
||||
|
|
@ -306,18 +314,36 @@ const useGridData = (props: {
|
|||
setGridReady(true)
|
||||
}, [gridDto])
|
||||
|
||||
// formData değiştiğinde sadece lookup datasource'ları güncelle
|
||||
// formData değiştiğinde sadece etkilenen cascading lookup datasource'ları güncelle
|
||||
useEffect(() => {
|
||||
if (!gridDto || !formItems.length) {
|
||||
previousFormDataRef.current = formData
|
||||
return
|
||||
}
|
||||
|
||||
// View mode'da formData olsa da olmasa da cascading alanlar için dataSource oluşturulmalı
|
||||
const updatedItems = formItems.map((groupItem) => ({
|
||||
...groupItem,
|
||||
items: (groupItem.items as SimpleItemWithColData[])?.map((item) => {
|
||||
const previousFormData = previousFormDataRef.current
|
||||
const changedFields = previousFormData
|
||||
? Object.keys({ ...(previousFormData || {}), ...(formData || {}) }).filter(
|
||||
(field) => !Object.is(previousFormData?.[field], formData?.[field]),
|
||||
)
|
||||
: []
|
||||
|
||||
const shouldRefreshLookup = (item: SimpleItemWithColData) => {
|
||||
const cascadeParentFields = item.colData?.lookupDto?.cascadeParentFields
|
||||
?.split(',')
|
||||
.map((field: string) => field.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
return (
|
||||
!previousFormData ||
|
||||
cascadeParentFields?.some((field: string) => changedFields.includes(field))
|
||||
)
|
||||
}
|
||||
|
||||
const updateItems = (items: any[] = []) =>
|
||||
items.map((item) => {
|
||||
const colData = gridDto.columnFormats.find((x) => x.fieldName === item.dataField)
|
||||
if (colData?.lookupDto?.dataSourceType) {
|
||||
if (colData?.lookupDto?.dataSourceType && shouldRefreshLookup(item)) {
|
||||
const currentDataSource = item.editorOptions?.dataSource
|
||||
const keepCustomDataSource =
|
||||
currentDataSource !== undefined && typeof currentDataSource?.load !== 'function'
|
||||
|
|
@ -330,16 +356,55 @@ const useGridData = (props: {
|
|||
dataSource: keepCustomDataSource
|
||||
? currentDataSource
|
||||
: getLookupDataSource(colData?.editorOptions, colData, formData || null),
|
||||
valueExpr: item.editorOptions?.valueExpr ?? colData?.lookupDto?.valueExpr?.toLowerCase(),
|
||||
valueExpr:
|
||||
item.editorOptions?.valueExpr ?? colData?.lookupDto?.valueExpr?.toLowerCase(),
|
||||
displayExpr:
|
||||
item.editorOptions?.displayExpr ?? colData?.lookupDto?.displayExpr?.toLowerCase(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (item?.items?.length) {
|
||||
return {
|
||||
...item,
|
||||
items: updateItems(item.items),
|
||||
}
|
||||
}
|
||||
|
||||
if (item?.tabs?.length) {
|
||||
return {
|
||||
...item,
|
||||
tabs: item.tabs.map((tab: any) => ({
|
||||
...tab,
|
||||
items: updateItems(tab.items),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
return item
|
||||
}),
|
||||
})
|
||||
|
||||
const hasAffectedLookup =
|
||||
!previousFormData ||
|
||||
formItems
|
||||
.flatMap((group) => flattenFormItems([group]))
|
||||
.some((item) => item.colData?.lookupDto?.dataSourceType && shouldRefreshLookup(item))
|
||||
|
||||
if (!hasAffectedLookup) {
|
||||
previousFormDataRef.current = formData
|
||||
return
|
||||
}
|
||||
|
||||
const updatedItems = formItems.map((groupItem) => ({
|
||||
...groupItem,
|
||||
items: updateItems(groupItem.items as any[]),
|
||||
tabs: (groupItem as any).tabs?.map((tab: any) => ({
|
||||
...tab,
|
||||
items: updateItems(tab.items),
|
||||
})),
|
||||
}))
|
||||
|
||||
previousFormDataRef.current = formData
|
||||
setFormItems(updatedItems)
|
||||
}, [formData, gridDto])
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import {
|
|||
postListFormCustomization,
|
||||
} from '@/services/list-form-customization.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import useResponsive from '@/utils/hooks/useResponsive'
|
||||
import { executeEditorScript } from '@/utils/editorScriptRuntime'
|
||||
import { Template } from 'devextreme-react/core/template'
|
||||
import DataGrid, {
|
||||
|
|
@ -239,19 +238,29 @@ const getValueByField = (data: Record<string, any> = {}, field?: string) => {
|
|||
}
|
||||
|
||||
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
||||
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
|
||||
Boolean(
|
||||
script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')),
|
||||
)
|
||||
|
||||
const isTouchLikeDevice = () =>
|
||||
typeof window !== 'undefined' &&
|
||||
(window.matchMedia?.('(pointer: coarse)').matches || window.matchMedia?.('(hover: none)').matches)
|
||||
|
||||
const isMobileViewport = () =>
|
||||
typeof window !== 'undefined' && window.matchMedia?.('(max-width: 767px)').matches
|
||||
|
||||
const Grid = (props: GridProps) => {
|
||||
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
||||
const { translate } = useLocalization()
|
||||
const { smaller } = useResponsive()
|
||||
const currentUser = useStoreState((state) => state.auth.user)
|
||||
const useMobileEditPopup = useRef(isMobileViewport() || isTouchLikeDevice()).current
|
||||
|
||||
const gridRef = useRef<DataGridRef>()
|
||||
const refListFormCode = useRef('')
|
||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||
const editingFormInstanceRef = useRef<any>()
|
||||
const lastEditingContentReadyScriptKeyRef = useRef<string>()
|
||||
// Edit popup state kaydetmeyi engellemek için flag
|
||||
const isEditingRef = useRef(false)
|
||||
|
||||
|
|
@ -344,6 +353,29 @@ const Grid = (props: GridProps) => {
|
|||
|
||||
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
||||
|
||||
const applyMobileEditorFocusGuard = useCallback(
|
||||
(editorOptions: EditorOptionsWithButtons, dataField?: string) => {
|
||||
if (!useMobileEditPopup || !dataField) {
|
||||
return editorOptions
|
||||
}
|
||||
|
||||
const previousOnFocusIn = editorOptions.onFocusIn
|
||||
|
||||
return {
|
||||
...editorOptions,
|
||||
autoFocus: false,
|
||||
focusStateEnabled: false,
|
||||
selectTextOnFocus: false,
|
||||
onFocusIn: (e: any) => {
|
||||
if (typeof previousOnFocusIn === 'function') {
|
||||
previousOnFocusIn(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
[useMobileEditPopup],
|
||||
)
|
||||
|
||||
const openNotePanel = useCallback(
|
||||
(rowData: Record<string, any>) => {
|
||||
const keyFieldName = gridDto?.gridOptions.keyFieldName
|
||||
|
|
@ -728,6 +760,10 @@ const Grid = (props: GridProps) => {
|
|||
const onEditorPreparing = useCallback(
|
||||
(editor: DataGridTypes.EditorPreparingEvent<any, any>) => {
|
||||
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
|
||||
if (isTouchLikeDevice()) {
|
||||
editor.editorOptions = applyMobileEditorFocusGuard(editor.editorOptions, editor.dataField)
|
||||
}
|
||||
|
||||
const formItem = gridDto.gridOptions.editingFormDto
|
||||
.flatMap((group) => flattenEditingFormItems([group]))
|
||||
.find((i) => i.dataField === editor.dataField)
|
||||
|
|
@ -888,7 +924,13 @@ const Grid = (props: GridProps) => {
|
|||
}
|
||||
}
|
||||
},
|
||||
[gridDto, cascadeFieldsMap],
|
||||
[
|
||||
gridDto,
|
||||
cascadeFieldsMap,
|
||||
parentToChildrenMap,
|
||||
mode,
|
||||
applyMobileEditorFocusGuard,
|
||||
],
|
||||
)
|
||||
|
||||
const customLoadState = useCallback(() => {
|
||||
|
|
@ -1181,20 +1223,20 @@ const Grid = (props: GridProps) => {
|
|||
|
||||
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
||||
Object.assign(defaultEditorOptions, {
|
||||
type: 'date',
|
||||
dateSerializationFormat: 'yyyy-MM-dd',
|
||||
displayFormat: 'shortDate',
|
||||
})
|
||||
type: 'date',
|
||||
dateSerializationFormat: 'yyyy-MM-dd',
|
||||
displayFormat: 'shortDate',
|
||||
})
|
||||
} else if (
|
||||
listFormField?.sourceDbType === DbTypeEnum.DateTime ||
|
||||
listFormField?.sourceDbType === DbTypeEnum.DateTime2 ||
|
||||
listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset
|
||||
) {
|
||||
Object.assign(defaultEditorOptions, {
|
||||
type: 'datetime',
|
||||
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
||||
displayFormat: 'shortDateShortTime',
|
||||
})
|
||||
type: 'datetime',
|
||||
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
||||
displayFormat: 'shortDateShortTime',
|
||||
})
|
||||
}
|
||||
|
||||
// Her item'a placeholder olarak captionName ekle
|
||||
|
|
@ -1217,6 +1259,8 @@ const Grid = (props: GridProps) => {
|
|||
...forcedEditorOptions,
|
||||
}
|
||||
|
||||
editorOptions = applyMobileEditorFocusGuard(editorOptions, i.dataField)
|
||||
|
||||
if (editorOptions?.buttons) {
|
||||
editorOptions.buttons = (editorOptions?.buttons || []).map((btn: any) => {
|
||||
if (btn?.options?.onClick && typeof btn.options.onClick === 'string') {
|
||||
|
|
@ -1261,7 +1305,7 @@ const Grid = (props: GridProps) => {
|
|||
|
||||
return item
|
||||
},
|
||||
[gridDto, mode, searchParams, extraFilters],
|
||||
[gridDto, mode, searchParams, extraFilters, applyMobileEditorFocusGuard],
|
||||
)
|
||||
|
||||
// WidgetGroup yüksekliğini hesapla
|
||||
|
|
@ -1514,7 +1558,7 @@ const Grid = (props: GridProps) => {
|
|||
/>
|
||||
<Editing
|
||||
refreshMode={gridDto.gridOptions.editingOptionDto?.refreshMode}
|
||||
mode={smaller.md ? 'form' : gridDto.gridOptions.editingOptionDto?.mode}
|
||||
mode={gridDto.gridOptions.editingOptionDto?.mode}
|
||||
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
|
||||
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
||||
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
||||
|
|
@ -1534,10 +1578,19 @@ const Grid = (props: GridProps) => {
|
|||
showTitle: gridDto.gridOptions.editingOptionDto?.popup?.showTitle,
|
||||
hideOnOutsideClick:
|
||||
gridDto.gridOptions.editingOptionDto?.popup?.hideOnOutsideClick,
|
||||
width: gridDto.gridOptions.editingOptionDto?.popup?.width,
|
||||
height: gridDto.gridOptions.editingOptionDto?.popup?.height,
|
||||
resizeEnabled: gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
|
||||
fullScreen: isPopupFullScreen,
|
||||
width: useMobileEditPopup
|
||||
? '100%'
|
||||
: gridDto.gridOptions.editingOptionDto?.popup?.width,
|
||||
height: useMobileEditPopup
|
||||
? '100dvh'
|
||||
: gridDto.gridOptions.editingOptionDto?.popup?.height,
|
||||
resizeEnabled:
|
||||
!useMobileEditPopup &&
|
||||
gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
|
||||
fullScreen: useMobileEditPopup || isPopupFullScreen,
|
||||
dragEnabled: !useMobileEditPopup,
|
||||
focusStateEnabled: !useMobileEditPopup,
|
||||
restorePosition: !useMobileEditPopup,
|
||||
toolbarItems: [
|
||||
{
|
||||
widget: 'dxButton',
|
||||
|
|
@ -1581,6 +1634,7 @@ const Grid = (props: GridProps) => {
|
|||
}}
|
||||
form={{
|
||||
colCount: 1,
|
||||
focusStateEnabled: !useMobileEditPopup,
|
||||
onContentReady: (e) => {
|
||||
editingFormInstanceRef.current = e.component
|
||||
|
||||
|
|
@ -1601,15 +1655,27 @@ const Grid = (props: GridProps) => {
|
|||
}
|
||||
|
||||
const runReadOnlyScripts = () => {
|
||||
const editorValues = gridDto.gridOptions.editingFormDto
|
||||
.flatMap((group) => flattenEditingFormItems([group]))
|
||||
.reduce<Record<string, any>>((values, formItem) => {
|
||||
const formItems = gridDto.gridOptions.editingFormDto.flatMap((group) =>
|
||||
flattenEditingFormItems([group]),
|
||||
)
|
||||
const scriptItems = formItems.filter((formItem) =>
|
||||
shouldRunEditorScriptOnContentReady(formItem.editorScript),
|
||||
)
|
||||
|
||||
if (!scriptItems.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const editorValues = formItems.reduce<Record<string, any>>(
|
||||
(values, formItem) => {
|
||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||
if (editorInstance?.option) {
|
||||
values[formItem.dataField] = editorInstance.option('value')
|
||||
}
|
||||
return values
|
||||
}, {})
|
||||
},
|
||||
{},
|
||||
)
|
||||
const formData = {
|
||||
...editingFormDataRef.current,
|
||||
...(form?.option?.('formData') || {}),
|
||||
|
|
@ -1617,38 +1683,47 @@ const Grid = (props: GridProps) => {
|
|||
}
|
||||
editingFormDataRef.current = { ...formData }
|
||||
|
||||
gridDto.gridOptions.editingFormDto
|
||||
.flatMap((group) => flattenEditingFormItems([group]))
|
||||
.filter((formItem) =>
|
||||
shouldRunEditorScriptOnContentReady(formItem.editorScript),
|
||||
)
|
||||
.forEach((formItem) => {
|
||||
try {
|
||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||
const editorValue =
|
||||
editorInstance?.option?.('value') ??
|
||||
getValueByField(formData, formItem.dataField)
|
||||
const editor = {
|
||||
dataField: formItem.dataField,
|
||||
component: grid,
|
||||
}
|
||||
const e = {
|
||||
component: form,
|
||||
dataField: formItem.dataField,
|
||||
value: editorValue,
|
||||
}
|
||||
const scriptKey = `${mode}|${String(rowKey)}|${scriptItems
|
||||
.map((formItem) => formItem.dataField)
|
||||
.join('|')}|${JSON.stringify(formData)}`
|
||||
|
||||
executeEditorScript(formItem.editorScript!, {
|
||||
formData,
|
||||
e,
|
||||
editor,
|
||||
runtimeSetEditorReadOnly,
|
||||
setFormData,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Script exec error on contentReady', formItem.dataField, err)
|
||||
if (lastEditingContentReadyScriptKeyRef.current === scriptKey) {
|
||||
return
|
||||
}
|
||||
|
||||
lastEditingContentReadyScriptKeyRef.current = scriptKey
|
||||
|
||||
scriptItems.forEach((formItem) => {
|
||||
try {
|
||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||
const editorValue =
|
||||
editorInstance?.option?.('value') ??
|
||||
getValueByField(formData, formItem.dataField)
|
||||
const editor = {
|
||||
dataField: formItem.dataField,
|
||||
component: grid,
|
||||
}
|
||||
})
|
||||
const e = {
|
||||
component: form,
|
||||
dataField: formItem.dataField,
|
||||
value: editorValue,
|
||||
}
|
||||
|
||||
executeEditorScript(formItem.editorScript!, {
|
||||
formData,
|
||||
e,
|
||||
editor,
|
||||
runtimeSetEditorReadOnly,
|
||||
setFormData,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'Script exec error on contentReady',
|
||||
formItem.dataField,
|
||||
err,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
runReadOnlyScripts()
|
||||
|
|
@ -1704,7 +1779,6 @@ const Grid = (props: GridProps) => {
|
|||
console.error('Script exec error', err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
items:
|
||||
|
|
@ -1896,7 +1970,7 @@ const Grid = (props: GridProps) => {
|
|||
)}
|
||||
|
||||
<Dialog
|
||||
width={smaller.md ? '100%' : 1000}
|
||||
width={useMobileEditPopup ? '100%' : 1000}
|
||||
isOpen={filterData.isImportModalOpen || false}
|
||||
onClose={() => filterData.setIsImportModalOpen(false)}
|
||||
onRequestClose={() => filterData.setIsImportModalOpen(false)}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import {
|
|||
postListFormCustomization,
|
||||
} from '@/services/list-form-customization.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import useResponsive from '@/utils/hooks/useResponsive'
|
||||
import { captionize } from 'devextreme/core/utils/inflector'
|
||||
import { Template } from 'devextreme-react/core/template'
|
||||
import TreeListDx, {
|
||||
|
|
@ -227,19 +226,29 @@ const getValueByField = (data: Record<string, any> = {}, field?: string) => {
|
|||
}
|
||||
|
||||
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
||||
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
|
||||
Boolean(
|
||||
script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')),
|
||||
)
|
||||
|
||||
const isTouchLikeDevice = () =>
|
||||
typeof window !== 'undefined' &&
|
||||
(window.matchMedia?.('(pointer: coarse)').matches || window.matchMedia?.('(hover: none)').matches)
|
||||
|
||||
const isMobileViewport = () =>
|
||||
typeof window !== 'undefined' && window.matchMedia?.('(max-width: 767px)').matches
|
||||
|
||||
const Tree = (props: TreeProps) => {
|
||||
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
||||
const { translate } = useLocalization()
|
||||
const { smaller } = useResponsive()
|
||||
const currentUser = useStoreState((state) => state.auth.user)
|
||||
const useMobileEditPopup = useRef(isMobileViewport() || isTouchLikeDevice()).current
|
||||
|
||||
const gridRef = useRef<TreeListRef>()
|
||||
const refListFormCode = useRef('')
|
||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||
const editingFormInstanceRef = useRef<any>()
|
||||
const lastEditingContentReadyScriptKeyRef = useRef<string>()
|
||||
// Edit popup state kaydetmeyi engellemek için flag
|
||||
const isEditingRef = useRef(false)
|
||||
|
||||
|
|
@ -349,6 +358,29 @@ const Tree = (props: TreeProps) => {
|
|||
|
||||
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
||||
|
||||
const applyMobileEditorFocusGuard = useCallback(
|
||||
(editorOptions: EditorOptionsWithButtons, dataField?: string) => {
|
||||
if (!useMobileEditPopup || !dataField) {
|
||||
return editorOptions
|
||||
}
|
||||
|
||||
const previousOnFocusIn = editorOptions.onFocusIn
|
||||
|
||||
return {
|
||||
...editorOptions,
|
||||
autoFocus: false,
|
||||
focusStateEnabled: false,
|
||||
selectTextOnFocus: false,
|
||||
onFocusIn: (e: any) => {
|
||||
if (typeof previousOnFocusIn === 'function') {
|
||||
previousOnFocusIn(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
[useMobileEditPopup],
|
||||
)
|
||||
|
||||
const openNotePanel = useCallback(
|
||||
(rowData: Record<string, any>) => {
|
||||
const keyFieldName = gridDto?.gridOptions.keyFieldName
|
||||
|
|
@ -677,6 +709,10 @@ const Tree = (props: TreeProps) => {
|
|||
|
||||
function onEditorPreparing(editor: TreeListTypes.EditorPreparingEvent) {
|
||||
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
|
||||
if (isTouchLikeDevice()) {
|
||||
editor.editorOptions = applyMobileEditorFocusGuard(editor.editorOptions, editor.dataField)
|
||||
}
|
||||
|
||||
const formItem = gridDto.gridOptions.editingFormDto
|
||||
.flatMap((group) => flattenEditingFormItems([group]))
|
||||
.find((i) => i.dataField === editor.dataField)
|
||||
|
|
@ -1180,7 +1216,7 @@ const Tree = (props: TreeProps) => {
|
|||
<RemoteOperations filtering={true} sorting={true} grouping={false} />
|
||||
<Editing
|
||||
refreshMode={gridDto.gridOptions.editingOptionDto?.refreshMode}
|
||||
mode={smaller.md ? 'form' : gridDto.gridOptions.editingOptionDto?.mode}
|
||||
mode={gridDto.gridOptions.editingOptionDto?.mode}
|
||||
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
|
||||
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
||||
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
||||
|
|
@ -1196,10 +1232,19 @@ const Tree = (props: TreeProps) => {
|
|||
showTitle: gridDto.gridOptions.editingOptionDto?.popup?.showTitle,
|
||||
hideOnOutsideClick:
|
||||
gridDto.gridOptions.editingOptionDto?.popup?.hideOnOutsideClick,
|
||||
width: gridDto.gridOptions.editingOptionDto?.popup?.width,
|
||||
height: gridDto.gridOptions.editingOptionDto?.popup?.height,
|
||||
resizeEnabled: gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
|
||||
fullScreen: isPopupFullScreen,
|
||||
width: useMobileEditPopup
|
||||
? '100%'
|
||||
: gridDto.gridOptions.editingOptionDto?.popup?.width,
|
||||
height: useMobileEditPopup
|
||||
? '100dvh'
|
||||
: gridDto.gridOptions.editingOptionDto?.popup?.height,
|
||||
resizeEnabled:
|
||||
!useMobileEditPopup &&
|
||||
gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
|
||||
fullScreen: useMobileEditPopup || isPopupFullScreen,
|
||||
dragEnabled: !useMobileEditPopup,
|
||||
focusStateEnabled: !useMobileEditPopup,
|
||||
restorePosition: !useMobileEditPopup,
|
||||
toolbarItems: [
|
||||
{
|
||||
widget: 'dxButton',
|
||||
|
|
@ -1243,6 +1288,7 @@ const Tree = (props: TreeProps) => {
|
|||
}}
|
||||
form={{
|
||||
colCount: 1,
|
||||
focusStateEnabled: !useMobileEditPopup,
|
||||
onContentReady: (e) => {
|
||||
editingFormInstanceRef.current = e.component
|
||||
|
||||
|
|
@ -1263,15 +1309,27 @@ const Tree = (props: TreeProps) => {
|
|||
}
|
||||
|
||||
const runReadOnlyScripts = () => {
|
||||
const editorValues = gridDto.gridOptions.editingFormDto
|
||||
.flatMap((group) => flattenEditingFormItems([group]))
|
||||
.reduce<Record<string, any>>((values, formItem) => {
|
||||
const formItems = gridDto.gridOptions.editingFormDto.flatMap((group) =>
|
||||
flattenEditingFormItems([group]),
|
||||
)
|
||||
const scriptItems = formItems.filter((formItem) =>
|
||||
shouldRunEditorScriptOnContentReady(formItem.editorScript),
|
||||
)
|
||||
|
||||
if (!scriptItems.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const editorValues = formItems.reduce<Record<string, any>>(
|
||||
(values, formItem) => {
|
||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||
if (editorInstance?.option) {
|
||||
values[formItem.dataField] = editorInstance.option('value')
|
||||
}
|
||||
return values
|
||||
}, {})
|
||||
},
|
||||
{},
|
||||
)
|
||||
const formData = {
|
||||
...editingFormDataRef.current,
|
||||
...(form?.option?.('formData') || {}),
|
||||
|
|
@ -1279,38 +1337,47 @@ const Tree = (props: TreeProps) => {
|
|||
}
|
||||
editingFormDataRef.current = { ...formData }
|
||||
|
||||
gridDto.gridOptions.editingFormDto
|
||||
.flatMap((group) => flattenEditingFormItems([group]))
|
||||
.filter((formItem) =>
|
||||
shouldRunEditorScriptOnContentReady(formItem.editorScript),
|
||||
)
|
||||
.forEach((formItem) => {
|
||||
try {
|
||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||
const editorValue =
|
||||
editorInstance?.option?.('value') ??
|
||||
getValueByField(formData, formItem.dataField)
|
||||
const editor = {
|
||||
dataField: formItem.dataField,
|
||||
component: grid,
|
||||
}
|
||||
const e = {
|
||||
component: form,
|
||||
dataField: formItem.dataField,
|
||||
value: editorValue,
|
||||
}
|
||||
const scriptKey = `${mode}|${String(rowKey)}|${scriptItems
|
||||
.map((formItem) => formItem.dataField)
|
||||
.join('|')}|${JSON.stringify(formData)}`
|
||||
|
||||
executeEditorScript(formItem.editorScript!, {
|
||||
formData,
|
||||
e,
|
||||
editor,
|
||||
runtimeSetEditorReadOnly,
|
||||
setFormData,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Script exec error on contentReady', formItem.dataField, err)
|
||||
if (lastEditingContentReadyScriptKeyRef.current === scriptKey) {
|
||||
return
|
||||
}
|
||||
|
||||
lastEditingContentReadyScriptKeyRef.current = scriptKey
|
||||
|
||||
scriptItems.forEach((formItem) => {
|
||||
try {
|
||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||
const editorValue =
|
||||
editorInstance?.option?.('value') ??
|
||||
getValueByField(formData, formItem.dataField)
|
||||
const editor = {
|
||||
dataField: formItem.dataField,
|
||||
component: grid,
|
||||
}
|
||||
})
|
||||
const e = {
|
||||
component: form,
|
||||
dataField: formItem.dataField,
|
||||
value: editorValue,
|
||||
}
|
||||
|
||||
executeEditorScript(formItem.editorScript!, {
|
||||
formData,
|
||||
e,
|
||||
editor,
|
||||
runtimeSetEditorReadOnly,
|
||||
setFormData,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'Script exec error on contentReady',
|
||||
formItem.dataField,
|
||||
err,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
runReadOnlyScripts()
|
||||
|
|
@ -1366,7 +1433,6 @@ const Tree = (props: TreeProps) => {
|
|||
console.error('Script exec error', err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
items:
|
||||
|
|
@ -1388,7 +1454,9 @@ const Tree = (props: TreeProps) => {
|
|||
let parsedEditorOptions: EditorOptionsWithButtons = {}
|
||||
const forcedEditorOptions: EditorOptionsWithButtons = {}
|
||||
try {
|
||||
parsedEditorOptions = i.editorOptions ? JSON.parse(i.editorOptions) : {}
|
||||
parsedEditorOptions = i.editorOptions
|
||||
? JSON.parse(i.editorOptions)
|
||||
: {}
|
||||
|
||||
const rawFilter = searchParams?.get('filter')
|
||||
if (rawFilter) {
|
||||
|
|
@ -1420,20 +1488,20 @@ const Tree = (props: TreeProps) => {
|
|||
|
||||
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
||||
Object.assign(defaultEditorOptions, {
|
||||
type: 'date',
|
||||
dateSerializationFormat: 'yyyy-MM-dd',
|
||||
displayFormat: 'shortDate',
|
||||
})
|
||||
type: 'date',
|
||||
dateSerializationFormat: 'yyyy-MM-dd',
|
||||
displayFormat: 'shortDate',
|
||||
})
|
||||
} else if (
|
||||
listFormField?.sourceDbType === DbTypeEnum.DateTime ||
|
||||
listFormField?.sourceDbType === DbTypeEnum.DateTime2 ||
|
||||
listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset
|
||||
) {
|
||||
Object.assign(defaultEditorOptions, {
|
||||
type: 'datetime',
|
||||
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
||||
displayFormat: 'shortDateShortTime',
|
||||
})
|
||||
type: 'datetime',
|
||||
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
||||
displayFormat: 'shortDateShortTime',
|
||||
})
|
||||
}
|
||||
|
||||
editorOptions = {
|
||||
|
|
@ -1442,6 +1510,8 @@ const Tree = (props: TreeProps) => {
|
|||
...forcedEditorOptions,
|
||||
}
|
||||
|
||||
editorOptions = applyMobileEditorFocusGuard(editorOptions, i.dataField)
|
||||
|
||||
if (editorOptions?.buttons) {
|
||||
editorOptions.buttons = (editorOptions?.buttons || []).map(
|
||||
(btn: any) => {
|
||||
|
|
@ -1636,7 +1706,7 @@ const Tree = (props: TreeProps) => {
|
|||
)}
|
||||
|
||||
<Dialog
|
||||
width={smaller.md ? '100%' : 1000}
|
||||
width={useMobileEditPopup ? '100%' : 1000}
|
||||
isOpen={filterData.isImportModalOpen || false}
|
||||
onClose={() => filterData.setIsImportModalOpen(false)}
|
||||
onRequestClose={() => filterData.setIsImportModalOpen(false)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue