sozsoft-platform/ui/src/views/list/Tree.tsx

748 lines
26 KiB
TypeScript
Raw Normal View History

2026-02-24 20:44:16 +00:00
import Container from '@/components/shared/Container'
2026-07-11 20:35:32 +00:00
import '@/utils/registerDevExtremeEditors'
2026-02-24 20:44:16 +00:00
import { Dialog, Notification, toast } from '@/components/ui'
import { DX_CLASSNAMES } from '@/constants/app.constant'
import { GridDto, SubFormTabTypeEnum } from '@/proxy/form/models'
2026-02-24 20:44:16 +00:00
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Template } from 'devextreme-react/core/template'
import TreeListDx, {
ColumnChooser,
ColumnFixing,
Editing,
FilterPanel,
FilterRow,
HeaderFilter,
LoadPanel,
Pager,
Paging,
RemoteOperations,
Scrolling,
SearchPanel,
Selection,
Sorting,
Toolbar,
2026-07-13 08:39:13 +00:00
type TreeListRef,
type TreeListTypes,
2026-02-24 20:44:16 +00:00
} from 'devextreme-react/tree-list'
import { Item } from 'devextreme-react/toolbar'
import CustomStore from 'devextreme/data/custom_store'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import PageTitle from '@/components/shared/PageTitle'
import { RowMode } from '../form/types'
2026-02-24 20:44:16 +00:00
import { GridColumnData } from './GridColumnData'
import GridFilterDialogs from './GridFilterDialogs'
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
2026-02-24 20:44:16 +00:00
import { TagBoxEditorComponent } from './editors/TagBoxEditorComponent'
import { useFilters } from './useFilters'
import { updateWorkflowApprovalToolbarItems, useToolbar } from './useToolbar'
2026-02-24 20:44:16 +00:00
import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
import { GridExtraFilterToolbar } from './GridExtraFilterToolbar'
import { layoutTypes } from '../admin/listForm/edit/types'
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
import { useListFormColumns } from './useListFormColumns'
import { orderListToolbarItems } from './toolbarOrder'
2026-02-24 20:44:16 +00:00
import { useStoreState } from '@/store/store'
import SubForms from '../form/SubForms'
2026-05-13 19:07:50 +00:00
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
import { NotePanel } from '../form/notes/NotePanel'
2026-07-10 09:22:54 +00:00
import { useListFormStateStoring } from './useListFormStateStoring'
import { buildEditPopupOptions, shouldUseMobileEditPopup } from './shared/editPopup'
import {
applyNewRowDefaults,
buildEditingFormItems,
buildInsertPayload,
buildUpdatePayload,
createFormItemMapper,
expandJsonFormFields,
getActiveEditingForm,
getValueByField,
} from './shared/editingForm'
import {
attachEditorButtonContext,
attachEditorValueChangedScript,
createEditingFormHandlers,
} from './shared/editingFormScripts'
import { attachCascadeBehavior, createCascadeMaps } from './shared/cascade'
import { createConditionalCellStyleHandler } from './shared/cellStyles'
import {
useExtraFilters,
useListFormCustomSources,
useListFormGridDto,
useWidgetGroupHeight,
} from './shared/hooks'
import { startWorkflowForInsertedRow } from './shared/workflowRows'
2026-02-24 20:44:16 +00:00
interface TreeProps {
listFormCode: string
searchParams?: URLSearchParams
isSubForm?: boolean
level?: number
refreshData?: () => Promise<void>
gridDto?: GridDto
}
const defaultPageSizes = [10, 20, 50, 100]
2026-02-24 20:44:16 +00:00
const Tree = (props: TreeProps) => {
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
const { translate } = useLocalization()
2026-06-06 18:31:03 +00:00
const currentUser = useStoreState((state) => state.auth.user)
const currentCulture = useStoreState(
(state) => state.abpConfig.config?.localization.currentCulture.cultureName,
)
const localizationTexts = useStoreState((state) => state.abpConfig.texts)
const useMobileEditPopup = shouldUseMobileEditPopup()
2026-02-24 20:44:16 +00:00
const gridRef = useRef<TreeListRef | undefined>(undefined)
2026-02-24 20:44:16 +00:00
const widgetGroupRef = useRef<HTMLDivElement>(null)
const editingFormDataRef = useRef<Record<string, any>>({})
const editingFormInstanceRef = useRef<any | undefined>(undefined)
// Edit popup açıkken grid state'inin kaydedilmesini engeller.
2026-02-24 20:44:16 +00:00
const isEditingRef = useRef(false)
const [treeListDataSource, setTreeListDataSource] = useState<CustomStore<any, any>>()
const [columnData, setColumnData] = useState<GridColumnData[]>()
const [formData, setFormData] = useState<any>()
const [mode, setMode] = useState<RowMode>('view')
const gridDto = useListFormGridDto(listFormCode, extGridDto)
2026-02-24 20:44:16 +00:00
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
const [notePanelTarget, setNotePanelTarget] = useState<{
entityName: string
entityId: string
} | null>(null)
2026-02-24 20:44:16 +00:00
const widgetGroupHeight = useWidgetGroupHeight(widgetGroupRef, gridDto?.widgets)
useListFormCustomSources(gridDto)
2026-02-24 20:44:16 +00:00
const getTreeInstance = useCallback(() => gridRef.current?.instance(), [])
2026-02-24 20:44:16 +00:00
2026-07-10 09:22:54 +00:00
const { customSaveState, customLoadState } = useListFormStateStoring({
listFormCode,
storageKey: gridDto?.gridOptions.stateStoringDto?.storageKey,
filterPrefix: 'tree',
skipSaveRef: isEditingRef,
})
2026-02-24 20:44:16 +00:00
const { filterToolbarData, ...filterData } = useFilters({
gridDto,
gridRef,
listFormCode,
saveGridState: customSaveState,
searchParams,
2026-02-24 20:44:16 +00:00
})
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
const refreshTree = useCallback(() => {
getTreeInstance()?.refresh()
}, [getTreeInstance])
const { extraFilters, setExtraFilters } = useExtraFilters({
gridDto,
listFormCode,
searchParams,
onFilterChanged: refreshTree,
})
const openNotePanel = useCallback(
(rowData: Record<string, any>) => {
const entityId = getValueByField(rowData, gridDto?.gridOptions.keyFieldName)
if (entityId === undefined || entityId === null || entityId === '') {
return
}
setNotePanelTarget({
entityName: gridDto?.gridOptions.listFormCode ?? listFormCode,
entityId: String(entityId),
})
},
[gridDto, listFormCode],
)
2026-02-24 20:44:16 +00:00
const { getBandedColumns } = useListFormColumns({
gridDto,
listFormCode,
isSubForm,
gridRef,
onShowNote: openNotePanel,
2026-02-24 20:44:16 +00:00
})
/* =======================
TOOLBAR
======================= */
2026-02-24 20:44:16 +00:00
const updateWorkflowApprovalButtons = useCallback(
(component?: any, selectedRowsData?: Record<string, unknown>[]) => {
const tree = component ?? getTreeInstance()
if (!tree) return
updateWorkflowApprovalToolbarItems(
tree,
gridDto?.gridOptions.workflowDto,
selectedRowsData ?? tree.getSelectedRowsData(),
2026-06-06 18:31:03 +00:00
currentUser,
)
},
[currentUser, getTreeInstance, gridDto],
)
2026-02-24 20:44:16 +00:00
const refreshData = useCallback(() => {
const tree = getTreeInstance()
Promise.resolve(tree?.refresh()).finally(() => updateWorkflowApprovalButtons(tree))
}, [getTreeInstance, updateWorkflowApprovalButtons])
2026-02-24 20:44:16 +00:00
const getSelectedRowKeys = useCallback(
async () => (await getTreeInstance()?.getSelectedRowKeys()) ?? [],
[getTreeInstance],
)
const getSelectedRowsData = useCallback(
() => getTreeInstance()?.getSelectedRowsData() ?? [],
[getTreeInstance],
)
const getFilter = useCallback(() => getTreeInstance()?.getCombinedFilter(), [getTreeInstance])
const toggleAllNodes = useCallback(
(expand: boolean) => {
const tree = getTreeInstance()
if (!tree) return
tree.forEachNode((node: any) => {
if (!node.hasChildren) return
if (expand) {
tree.expandRow(node.key)
} else {
tree.collapseRow(node.key)
}
})
},
[getTreeInstance],
)
const expandAll = useCallback(() => toggleAllNodes(true), [toggleAllNodes])
const collapseAll = useCallback(() => toggleAllNodes(false), [toggleAllNodes])
2026-02-24 20:44:16 +00:00
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
gridDto,
listFormCode,
getSelectedRowKeys,
getSelectedRowsData,
refreshData,
expandAll,
collapseAll,
getFilter,
layout: layoutTypes.tree,
2026-02-24 20:44:16 +00:00
})
const orderedToolbarData = useMemo(
() => orderListToolbarItems([...toolbarData, ...filterToolbarData]),
[toolbarData, filterToolbarData],
)
2026-02-24 20:44:16 +00:00
/* =======================
TREE EVENTS
======================= */
2026-02-24 20:44:16 +00:00
const onSelectionChanged = useCallback(
(data: any) => {
const tree = getTreeInstance()
if (!gridDto?.gridOptions || !tree) return
if (gridDto.gridOptions.editingOptionDto?.allowDeleting) {
const items = tree.option('toolbar')?.items ?? []
const deleteSelectedIndex = items.findIndex(
(item: any) => item.name === 'deleteSelectedRecords',
)
if (deleteSelectedIndex >= 0) {
tree.option(
`toolbar.items[${deleteSelectedIndex}].options.visible`,
data.selectedRowsData.length > 1,
)
2026-02-24 20:44:16 +00:00
}
}
updateWorkflowApprovalButtons(tree, data.selectedRowsData)
2026-02-24 20:44:16 +00:00
if (data.selectedRowsData.length) {
setFormData(data.selectedRowsData[0])
}
},
[getTreeInstance, gridDto, updateWorkflowApprovalButtons],
2026-02-24 20:44:16 +00:00
)
const onCellPrepared = useMemo(() => createConditionalCellStyleHandler(gridDto), [gridDto])
const onInitNewRow = useCallback(
(e: TreeListTypes.InitNewRowEvent) => {
if (!gridDto?.columnFormats) {
editingFormDataRef.current = { ...(e.data || {}) }
2026-02-24 20:44:16 +00:00
return
}
setMode('new')
setIsPopupFullScreen(
useMobileEditPopup || (gridDto.gridOptions.editingOptionDto?.popup?.fullScreen ?? false),
)
e.promise = applyNewRowDefaults(
gridDto,
e.data,
searchParams?.get('filter'),
extraFilters,
).then((data) => {
editingFormDataRef.current = { ...data }
})
2026-02-24 20:44:16 +00:00
},
[gridDto, searchParams, extraFilters, useMobileEditPopup],
2026-02-24 20:44:16 +00:00
)
const onRowInserting = useCallback(
(e: TreeListTypes.RowInsertingEvent) => {
e.data = buildInsertPayload(gridDto, e.data)
},
[gridDto],
)
2026-02-24 20:44:16 +00:00
const onRowUpdating = useCallback(
(e: TreeListTypes.RowUpdatingEvent) => {
if (!gridDto?.columnFormats) return
e.newData = buildUpdatePayload(gridDto, e.oldData, e.newData)
2026-02-24 20:44:16 +00:00
},
[gridDto],
)
const onEditingStart = useCallback(
(e: TreeListTypes.EditingStartEvent) => {
isEditingRef.current = true
editingFormDataRef.current = { ...(e.data || {}) }
2026-02-24 20:44:16 +00:00
setMode('edit')
2026-06-11 11:38:48 +00:00
setIsPopupFullScreen(
useMobileEditPopup || (gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false),
)
expandJsonFormFields(e.component.option('columns') as GridColumnData[], e.data)
2026-02-24 20:44:16 +00:00
},
[gridDto, useMobileEditPopup],
2026-02-24 20:44:16 +00:00
)
const onEditFinished = useCallback(() => {
isEditingRef.current = false
setMode('view')
setIsPopupFullScreen(false)
}, [])
2026-02-24 20:44:16 +00:00
const onDataErrorOccurred = useCallback((e: TreeListTypes.DataErrorOccurredEvent) => {
toast.push(
<Notification type="danger" duration={2000}>
{e.error?.message}
</Notification>,
{ placement: 'bottom-end' },
2026-02-24 20:44:16 +00:00
)
}, [])
const cascadeMaps = useMemo(() => createCascadeMaps(gridDto), [gridDto])
const onEditorPreparing = useCallback(
(editor: TreeListTypes.EditorPreparingEvent) => {
if (editor.parentType !== 'dataRow' || !editor.dataField || !gridDto) return
const columnFormat = gridDto.columnFormats.find(
(column) => column.fieldName === editor.dataField,
)
const isNewRow = Boolean((editor as any).row?.isNewRow) || mode === 'new'
if (columnFormat?.editorOptions) {
try {
editor.editorOptions = {
...editor.editorOptions,
...JSON.parse(columnFormat.editorOptions),
}
} catch (err) {
console.error('EditorOptions parse error:', columnFormat.fieldName, err)
}
}
2026-05-31 18:16:41 +00:00
const hasReadOnlyEditorOption = editor.editorOptions?.readOnly === true
const hasDisabledEditorOption = editor.editorOptions?.disabled === true
if (
(isNewRow && columnFormat?.allowAdding === false) ||
(!isNewRow && columnFormat?.allowEditing === false)
) {
editor.editorOptions.readOnly = true
} else if (isNewRow && columnFormat?.allowAdding === true) {
if (!hasReadOnlyEditorOption) editor.editorOptions.readOnly = false
if (!hasDisabledEditorOption) editor.editorOptions.disabled = false
2026-02-24 20:44:16 +00:00
}
const cascadeInfo = cascadeMaps.cascadeFieldsMap.get(editor.dataField)
if (cascadeInfo) {
attachCascadeBehavior({ editor, gridDto, cascadeInfo, maps: cascadeMaps })
2026-02-24 20:44:16 +00:00
}
if (columnFormat?.editOrderNo != null && columnFormat.editorScript) {
attachEditorValueChangedScript({
editor,
editorScript: columnFormat.editorScript,
fieldName: columnFormat.fieldName,
editingFormInstanceRef,
getActiveEditingForm,
2026-02-24 20:44:16 +00:00
})
}
attachEditorButtonContext(editor)
},
[gridDto, cascadeMaps, mode],
)
2026-02-24 20:44:16 +00:00
/* =======================
COLUMNS & DATASOURCE
======================= */
2026-02-24 20:44:16 +00:00
useEffect(() => {
setTreeListDataSource(undefined)
setColumnData(undefined)
2026-02-24 20:44:16 +00:00
const instance = getTreeInstance()
if (instance) {
instance.option('columns', undefined)
instance.option('dataSource', undefined)
instance.state(null)
2026-02-24 20:44:16 +00:00
}
}, [listFormCode, getTreeInstance])
2026-02-24 20:44:16 +00:00
useEffect(() => {
2026-06-06 18:31:03 +00:00
if (!gridDto) return
2026-02-24 20:44:16 +00:00
const columns = getBandedColumns()
setColumnData(columns)
setTreeListDataSource(
createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
layoutTypes.tree,
columns,
),
2026-02-24 20:44:16 +00:00
)
// getBandedColumns her render'da yeni referans üretebildiği için bağımlılığa alınmaz.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [gridDto, listFormCode, searchParams, currentCulture, localizationTexts])
2026-02-24 20:44:16 +00:00
// Başlangıçta açık olacak düğümler.
2026-02-24 20:44:16 +00:00
useEffect(() => {
setExpandedRowKeys(gridDto?.gridOptions.treeOptionDto?.expandedRowKeys ?? [])
}, [gridDto])
2026-02-24 20:44:16 +00:00
useEffect(() => {
const instance = getTreeInstance()
if (columnData && instance) {
2026-02-24 20:44:16 +00:00
instance.option('columns', columnData as any)
}
}, [columnData, getTreeInstance])
2026-02-24 20:44:16 +00:00
useEffect(() => {
const instance = getTreeInstance()
if (treeListDataSource && instance) {
2026-02-24 20:44:16 +00:00
instance.option('dataSource', treeListDataSource)
}
}, [treeListDataSource, getTreeInstance])
2026-02-24 20:44:16 +00:00
// Otomatik state kaydetme/yükleme kapalı; manuel akış kullanılır.
2026-02-24 20:44:16 +00:00
useEffect(() => {
if (!gridDto) return
getTreeInstance()?.option('stateStoring', { enabled: false })
}, [gridDto, getTreeInstance])
2026-02-24 20:44:16 +00:00
useEffect(() => {
const instance = getTreeInstance()
if (!gridDto || !instance || !treeListDataSource || !columnData) return
2026-02-24 20:44:16 +00:00
customLoadState()
.then((state) => {
if (state) instance.state(state)
})
.catch((err) => console.error('State load error:', err))
}, [gridDto, treeListDataSource, columnData, customLoadState, getTreeInstance])
/* =======================
EDITING FORM
======================= */
const mapFormItem = useMemo(
() =>
createFormItemMapper({
mode,
translate,
rawFilter: searchParams?.get('filter'),
extraFilters,
getEditingFormData: () => editingFormDataRef.current,
}),
[mode, translate, searchParams, extraFilters],
)
2026-02-24 20:44:16 +00:00
const editingFormItems = useMemo(
() => buildEditingFormItems(gridDto, mapFormItem, mode),
[gridDto, mapFormItem, mode],
)
2026-02-24 20:44:16 +00:00
const editingFormHandlers = useMemo(
() =>
createEditingFormHandlers({
getGridDto: () => gridDto,
getGridInstance: getTreeInstance,
editingFormDataRef,
editingFormInstanceRef,
}),
[gridDto, getTreeInstance],
)
const editPopupOptions = useMemo(
() =>
buildEditPopupOptions({
popupOptions: gridDto?.gridOptions.editingOptionDto?.popup,
mode,
isPopupFullScreen,
useMobileEditPopup,
translate,
handlers: {
onSave: () => getTreeInstance()?.saveEditData(),
onCancel: () => getTreeInstance()?.cancelEditData(),
onToggleFullScreen: () => setIsPopupFullScreen((current) => !current),
},
}),
[gridDto, mode, isPopupFullScreen, useMobileEditPopup, translate, getTreeInstance],
)
/* =======================
RENDER
======================= */
const gridOptions = gridDto?.gridOptions
const treeOptions = gridOptions?.treeOptionDto
const autoExpandAll = treeOptions?.autoExpandAll ?? false
2026-02-24 20:44:16 +00:00
return (
<>
2026-06-18 14:03:22 +00:00
<div ref={widgetGroupRef} className={(gridDto?.widgets?.length ?? 0) > 0 ? 'mt-2' : ''}>
2026-02-24 20:44:16 +00:00
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} />
</div>
<Container className={DX_CLASSNAMES}>
2026-08-14 11:32:31 +00:00
{!isSubForm && <PageTitle title={translate('::' + gridOptions?.title)} />}
{gridDto && gridOptions && columnData && (
<div className="p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700">
2026-02-24 20:44:16 +00:00
<TreeListDx
ref={gridRef as any}
id={'TreeList-' + listFormCode}
2026-07-30 14:37:15 +00:00
className="list-view-toolbar-host"
2026-02-24 20:44:16 +00:00
height={
gridOptions.height > 0
? gridOptions.height
: gridOptions.fullHeight
2026-02-24 20:44:16 +00:00
? `calc(100vh - ${170 + widgetGroupHeight}px)`
: undefined
}
width={gridOptions.width || '100%'}
2026-02-24 20:44:16 +00:00
dataStructure="plain"
keyExpr={treeOptions?.keyExpr}
parentIdExpr={treeOptions?.parentIdExpr}
hasItemsExpr={treeOptions?.hasItemsExpr}
2026-02-24 20:44:16 +00:00
rootValue={
treeOptions?.rootValue === '' || treeOptions?.rootValue === undefined
2026-02-24 20:44:16 +00:00
? null
: treeOptions.rootValue
2026-02-24 20:44:16 +00:00
}
{...(!autoExpandAll && {
expandedRowKeys,
onRowExpanding: (e: any) => setExpandedRowKeys((prev) => [...prev, e.key]),
onRowCollapsing: (e: any) =>
setExpandedRowKeys((prev) => prev.filter((key) => key !== e.key)),
2026-02-24 20:44:16 +00:00
})}
autoExpandAll={autoExpandAll}
allowColumnResizing={gridOptions.columnOptionDto?.allowColumnResizing}
allowColumnReordering={gridOptions.columnOptionDto?.allowColumnReordering}
showBorders={gridOptions.columnOptionDto?.showBorders}
showRowLines={gridOptions.columnOptionDto?.showRowLines}
showColumnLines={gridOptions.columnOptionDto?.showColumnLines}
columnResizingMode={gridOptions.columnOptionDto?.columnResizingMode}
columnAutoWidth={gridOptions.columnOptionDto?.columnAutoWidth}
rtlEnabled={gridOptions.columnOptionDto?.rtlEnabled}
rowAlternationEnabled={gridOptions.columnOptionDto?.rowAlternationEnabled}
hoverStateEnabled={gridOptions.columnOptionDto?.hoverStateEnabled}
columnHidingEnabled={gridOptions.columnOptionDto?.columnHidingEnabled}
focusedRowEnabled={gridOptions.columnOptionDto?.focusedRowEnabled}
showColumnHeaders={gridOptions.columnOptionDto?.showColumnHeaders}
filterSyncEnabled
2026-02-24 20:44:16 +00:00
onRowPrepared={(e) => {
if (e.rowType === 'data') {
e.rowElement.style.height = gridOptions.rowDto.rowHeight
e.rowElement.style.whiteSpace = gridOptions.rowDto.whiteSpace
e.rowElement.style.overflowWrap = gridOptions.rowDto.overflowWrap
2026-02-24 20:44:16 +00:00
}
}}
onSelectionChanged={onSelectionChanged}
onInitNewRow={onInitNewRow}
onCellPrepared={onCellPrepared}
onRowInserting={onRowInserting}
onRowUpdating={onRowUpdating}
onEditingStart={onEditingStart}
onDataErrorOccurred={onDataErrorOccurred}
onEditCanceled={onEditFinished}
onSaved={onEditFinished}
onRowInserted={(e) => {
startWorkflowForInsertedRow(e, gridDto, listFormCode, refreshTree)
2026-02-24 20:44:16 +00:00
props.refreshData?.()
}}
onRowUpdated={() => props.refreshData?.()}
onRowRemoved={() => props.refreshData?.()}
2026-02-24 20:44:16 +00:00
onEditorPreparing={onEditorPreparing}
onContentReady={(e) => {
updateWorkflowApprovalButtons(e.component)
// Veri yenilendikten sonra açık düğümleri geri yükle.
if (!autoExpandAll && expandedRowKeys.length > 0) {
2026-02-24 20:44:16 +00:00
e.component.option('expandedRowKeys', expandedRowKeys)
}
}}
>
<RemoteOperations filtering sorting grouping={false} />
2026-02-24 20:44:16 +00:00
<Editing
refreshMode={gridOptions.editingOptionDto?.refreshMode}
mode={gridOptions.editingOptionDto?.mode}
allowDeleting={gridOptions.editingOptionDto?.allowDeleting}
allowUpdating={gridOptions.editingOptionDto?.allowUpdating}
allowAdding={gridOptions.editingOptionDto?.allowAdding}
useIcons={gridOptions.editingOptionDto?.useIcons}
confirmDelete={gridOptions.editingOptionDto?.confirmDelete}
selectTextOnEditStart={gridOptions.editingOptionDto?.selectTextOnEditStart}
startEditAction={gridOptions.editingOptionDto?.startEditAction}
popup={editPopupOptions}
2026-02-24 20:44:16 +00:00
form={{
colCount: 1,
onContentReady: editingFormHandlers.onContentReady,
onFieldDataChanged: editingFormHandlers.onFieldDataChanged,
items: editingFormItems,
2026-02-24 20:44:16 +00:00
}}
/>
<Template name="cellEditTagBox" render={TagBoxEditorComponent} />
<Template name="cellEditGridBox" render={GridBoxEditorComponent} />
<Template name="cellEditImageUpload" render={ImageUploadEditorComponent} />
<Template
name="cellEditImageViewer"
2026-06-11 11:38:48 +00:00
render={(data: any) => (
<ImageViewerEditorComponent
{...data}
fallbackFormData={editingFormDataRef.current}
/>
)}
/>
2026-02-24 20:44:16 +00:00
<Template name="extraFilters">
<GridExtraFilterToolbar
filters={gridOptions.extraFilterDto ?? []}
2026-02-24 20:44:16 +00:00
extraFilters={extraFilters}
setExtraFilters={setExtraFilters}
/>
</Template>
<Toolbar visible={orderedToolbarData.length > 0}>
{orderedToolbarData.map((item) => (
<Item key={item.name} {...item} />
2026-02-24 20:44:16 +00:00
))}
{gridOptions.extraFilterDto?.length ? (
2026-02-24 20:44:16 +00:00
<Item location="before" template="extraFilters" cssClass="no-default" />
) : null}
</Toolbar>
<Sorting mode={gridOptions.sortMode} />
2026-02-24 20:44:16 +00:00
<FilterRow
visible={gridOptions.filterRowDto?.visible}
applyFilter={gridOptions.filterRowDto?.applyFilter}
/>
<FilterPanel visible={gridOptions.filterPanelDto.visible} />
<HeaderFilter visible={gridOptions.headerFilterDto.visible} />
2026-02-24 20:44:16 +00:00
<SearchPanel
visible={gridOptions.searchPanelDto.visible}
width={gridOptions.searchPanelDto.width}
placeholder={translate('::App.Platform.Search2')}
/>
2026-02-24 20:44:16 +00:00
<Selection
mode={gridOptions.selectionDto?.mode}
recursive={treeOptions?.recursiveSelection || false}
/>
<Paging enabled defaultPageSize={gridOptions.pageSize ?? 10} />
2026-02-24 20:44:16 +00:00
<Pager
visible={gridOptions.pagerOptionDto?.visible ?? true}
2026-02-24 20:44:16 +00:00
allowedPageSizes={
gridOptions.pagerOptionDto?.allowedPageSizes?.split(',').map(Number) ??
defaultPageSizes
2026-02-24 20:44:16 +00:00
}
showPageSizeSelector={gridOptions.pagerOptionDto?.showPageSizeSelector ?? true}
showInfo={gridOptions.pagerOptionDto?.showInfo ?? true}
showNavigationButtons={gridOptions.pagerOptionDto?.showNavigationButtons ?? true}
infoText={gridOptions.pagerOptionDto?.infoText}
displayMode={gridOptions.pagerOptionDto?.displayMode ?? 'full'}
/>
2026-02-24 20:44:16 +00:00
<ColumnChooser
enabled={gridOptions.columnOptionDto?.columnChooserEnabled}
mode={gridOptions.columnOptionDto?.columnChooserMode}
/>
<ColumnFixing enabled={gridOptions.columnOptionDto?.columnFixingEnabled} />
<Scrolling mode={gridOptions.pagerOptionDto?.scrollingMode} />
2026-02-24 20:44:16 +00:00
<LoadPanel
enabled={gridOptions.pagerOptionDto?.loadPanelEnabled}
text={gridOptions.pagerOptionDto?.loadPanelText}
/>
2026-02-24 20:44:16 +00:00
</TreeListDx>
{gridOptions.subFormsDto?.length > 0 &&
gridOptions.subFormsListFormType === SubFormTabTypeEnum.List && (
2026-02-24 20:44:16 +00:00
<>
<hr className="my-2" />
<SubForms gridDto={gridDto} formData={formData} level={level ?? 0} />
2026-02-24 20:44:16 +00:00
</>
)}
2026-05-13 19:07:50 +00:00
<Dialog
width={useMobileEditPopup ? '100%' : 1000}
2026-05-13 19:07:50 +00:00
isOpen={filterData.isImportModalOpen || false}
onClose={() => filterData.setIsImportModalOpen(false)}
onRequestClose={() => filterData.setIsImportModalOpen(false)}
>
2026-06-08 11:57:43 +00:00
<Dialog.Header className="-mx-6 -mt-6 flex h-11 items-center px-6 pr-20 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-lg font-semibold text-gray-800 dark:text-gray-100">
{translate('::App.ListForm.ImportManager')}
2026-06-08 11:57:43 +00:00
</h2>
</Dialog.Header>
2026-05-13 19:07:50 +00:00
<Dialog.Body className="flex flex-col">
<ImportDashboard gridDto={gridDto} />
</Dialog.Body>
</Dialog>
2026-02-24 20:44:16 +00:00
</div>
)}
<Dialog
isOpen={toolbarModalData?.open || false}
onClose={() => setToolbarModalData(undefined)}
onRequestClose={() => setToolbarModalData(undefined)}
>
{toolbarModalData?.content}
</Dialog>
<GridFilterDialogs gridRef={gridRef as any} listFormCode={listFormCode} {...filterData} />
{notePanelTarget && (
<NotePanel
entityName={notePanelTarget.entityName}
entityId={notePanelTarget.entityId}
isVisible
onToggle={() => setNotePanelTarget(null)}
/>
)}
2026-02-24 20:44:16 +00:00
</Container>
</>
)
}
export default Tree