sozsoft-platform/ui/src/views/list/shared/cascade.ts

125 lines
3.9 KiB
TypeScript
Raw Normal View History

/**
* Lookup cascade (bağımlı açılır liste) davranışı.
* Grid ve Tree aynı kuralları paylaşır.
*/
import type { GridDto } from '@/proxy/form/models'
export type CascadeInfo = {
parentFields: string[]
childFields?: string[]
}
export type CascadeMaps = {
/** fieldName -> cascade bilgisi */
cascadeFieldsMap: Map<string, CascadeInfo>
/** parentField -> etkilediği child alan adları */
parentToChildrenMap: Map<string, Set<string>>
}
const splitFields = (value?: string | null) =>
(value ?? '')
.split(',')
.map((field) => field.trim())
.filter(Boolean)
export const createCascadeMaps = (gridDto?: GridDto): CascadeMaps => {
const cascadeFieldsMap = new Map<string, CascadeInfo>()
const parentToChildrenMap = new Map<string, Set<string>>()
gridDto?.columnFormats.forEach((col) => {
if (!col.fieldName || !col.lookupDto?.cascadeParentFields) return
const parentFields = splitFields(col.lookupDto.cascadeParentFields)
const childFields = col.lookupDto.cascadeEmptyFields
? splitFields(col.lookupDto.cascadeEmptyFields)
: undefined
cascadeFieldsMap.set(col.fieldName, { parentFields, childFields })
parentFields.forEach((parentField) => {
const children = parentToChildrenMap.get(parentField) ?? new Set<string>()
children.add(col.fieldName!)
parentToChildrenMap.set(parentField, children)
})
})
return { cascadeFieldsMap, parentToChildrenMap }
}
type AttachCascadeParams = {
editor: any
gridDto: GridDto
cascadeInfo: CascadeInfo
maps: CascadeMaps
}
/**
* `onEditorPreparing` içinde cascade davranışını editöre bağlar:
* - parent değiştiğinde child alanları temizler,
* - etkilenen child editörlerin `disabled` durumunu günceller,
* - form ilk açıldığında parent'ı boş olan alanı devre dışı bırakır.
*/
export const attachCascadeBehavior = ({
editor,
gridDto,
cascadeInfo,
maps,
}: AttachCascadeParams) => {
const { cascadeFieldsMap, parentToChildrenMap } = maps
const affectedChildren = parentToChildrenMap.get(editor.dataField)
const previousHandler = editor.editorOptions.onValueChanged
editor.editorOptions.onValueChanged = (e: any) => {
previousHandler?.(e)
const grid = editor.component
const rowKey = grid.option('editing.editRowKey')
const rowIndex = grid.getRowIndexByKey(rowKey)
// Parent değiştiğinde bağımlı alanları temizle.
if (cascadeInfo.childFields && rowIndex >= 0) {
cascadeInfo.childFields.forEach((childField) => grid.cellValue(rowIndex, childField, null))
}
if (!affectedChildren?.size) return
const formInstance = grid.option('editing.form') as any
if (!formInstance?.getEditor) return
const rowData = grid.getVisibleRows().find((row: any) => row.key === rowKey)?.data
if (!rowData) return
affectedChildren.forEach((childFieldName) => {
const childInfo = cascadeFieldsMap.get(childFieldName)
if (!childInfo) return
try {
const childColumn = gridDto.columnFormats.find(
(column) => column.fieldName === childFieldName,
)
const childEditorOptions = childColumn?.editorOptions
? JSON.parse(childColumn.editorOptions)
: {}
const childEditor = formInstance.getEditor(childFieldName)
childEditor?.option(
'disabled',
childEditorOptions?.disabled === true ||
childInfo.parentFields.some((parentField) => !rowData[parentField]),
)
} catch (err) {
console.error(`Cascade child editor güncellenemedi: "${childFieldName}"`, err)
}
})
}
// İlk açılışta parent'ı dolu değilse editörü kilitle.
const grid = editor.component
const rowKey = grid.option('editing.editRowKey')
const rowData = grid.getVisibleRows().find((row: any) => row.key === rowKey)?.data
if (rowData && cascadeInfo.parentFields.some((parentField) => !rowData[parentField])) {
editor.editorOptions.disabled = true
}
}