diff --git a/structures-frontend-next/src/pages/EntityList.vue b/structures-frontend-next/src/pages/EntityList.vue index ce8390b1..d356f5e0 100644 --- a/structures-frontend-next/src/pages/EntityList.vue +++ b/structures-frontend-next/src/pages/EntityList.vue @@ -12,6 +12,7 @@ import { Structure, type IStructureService, Structures, type IEntitiesService } import DatetimeUtil from '@/util/DatetimeUtil' import { StructureUtil } from '@/util/StructureUtil' +import { rowColors } from '@/util/rowColors' @Component({ components: { @@ -22,7 +23,7 @@ import { StructureUtil } from '@/util/StructureUtil' InputText } }) -export default class EntityList extends Vue { +class EntityList extends Vue { @Prop({ type: String }) structureId?: string loading = false @@ -36,6 +37,23 @@ export default class EntityList extends Vue { structureProperties: any = {} structure!: Structure + // Expansion state tracking + expandedColumns: Set = new Set() // Track which columns are expanded + expandedRows: Map> = new Map() // Track which rows have expanded cells (rowId -> Set of field names) + expandedNestedObjects: Set = new Set() // Track which nested objects are expanded (e.g., 'payment.method') + expandedDeepNested: Set = new Set() // Track 4th level expansion (e.g., 'qualifications.education.research') + expandedNestedArrays: Map>> = new Map() // Track nested array expansions: Map>> + expandedVeryDeepNested: Set = new Set() // Track 5th level expansion (e.g., 'qualifications.education.research.funding') + + // Column resize state + resizingColumn: any = null + startX = 0 + startWidth = 0 + wasExpanded = false + nestedColumnWidths: Map = new Map() // Store nested column widths persistently + deepNestedColumnWidths: Map = new Map() // Store 3rd level column widths + veryDeepNestedColumnWidths: Map = new Map() // Store 4th level column widths + entitiesService: IEntitiesService = Structures.getEntitiesService() structureService: IStructureService = Structures.getStructureService() @@ -47,6 +65,7 @@ export default class EntityList extends Vue { } mounted() { + this.find() const paramId = this.$route.params.id const id = this.structureId || (Array.isArray(paramId) ? paramId[0] : paramId) @@ -75,7 +94,8 @@ if (!id) { field: property.name, sortable: sortable, width: property.name === 'id' ? 220 : (isComplex ? 240 : (sortable ? 120 : 160)), - isCollapsable: isComplex || property?.name === 'addresses' || property?.name === 'pet' + isCollapsable: isComplex || property?.name === 'addresses' || property?.name === 'pet', + expandedWidth: isComplex ? 600 : null // Width when expanded } this.headers.push(headerDef) this.keys.push(property.name) @@ -90,6 +110,19 @@ if (!id) { }) } + beforeUnmount() { + document.removeEventListener('mousemove', this.onColumnResize) + document.removeEventListener('mouseup', this.stopColumnResize) + document.removeEventListener('mousemove', this.onNestedColumnResize) + document.removeEventListener('mouseup', this.stopNestedColumnResize) + document.removeEventListener('mousemove', this.onDeepNestedColumnResize) + document.removeEventListener('mouseup', this.stopDeepNestedColumnResize) + document.removeEventListener('mousemove', this.onVeryDeepNestedColumnResize) + document.removeEventListener('mouseup', this.stopVeryDeepNestedColumnResize) + document.removeEventListener('mousemove', this.onUltraDeepNestedColumnResize) + document.removeEventListener('mouseup', this.stopUltraDeepNestedColumnResize) + } + formatDate(date: string): string { return DatetimeUtil.formatDate(date) } @@ -98,6 +131,593 @@ if (!id) { return StructureUtil.getPropertyDefinition(field, this.structureProperties)?.type?.type === 'date' } + // Toggle column expansion (header click) + toggleColumnExpansion(fieldName: string) { + if (this.expandedColumns.has(fieldName)) { + this.expandedColumns.delete(fieldName) + } else { + this.expandedColumns.add(fieldName) + } + } + + // Toggle row cell expansion (cell value click) - for arrays, this expands the list + toggleRowExpansion(rowId: string, fieldName: string) { + if (!this.expandedRows.has(rowId)) { + this.expandedRows.set(rowId, new Set()) + } + const rowExpansions = this.expandedRows.get(rowId)! + if (rowExpansions.has(fieldName)) { + rowExpansions.delete(fieldName) + } else { + rowExpansions.add(fieldName) + } + } + + + + // Check if a column is expanded + isColumnExpanded(fieldName: string): boolean { + return this.expandedColumns.has(fieldName) + } + + // Check if a row cell is expanded + isRowCellExpanded(rowId: string, fieldName: string): boolean { + return this.expandedRows.get(rowId)?.has(fieldName) || false + } + + // Get row colors + get rowColors() { + return rowColors + } + + // Check if field is an array of primitives (not objects) + isPrimitiveArray(fieldName: string): boolean { + const prop = this.structureProperties.find((p: any) => p.name === fieldName) + if (!prop || prop.type?.type !== 'array') return false + + const containsType = prop.type.contains?.type + return ['string', 'number', 'boolean', 'date'].includes(containsType) + } + + // Get nested properties for a field + getNestedProperties(fieldName: string): any[] { + const prop = this.structureProperties.find((p: any) => p.name === fieldName) + if (!prop) return [] + + const type = prop.type?.type + + let properties: any[] = [] + if (type === 'object') { + properties = prop.type?.properties || [] + } else if (type === 'array') { + const containsType = prop.type.contains?.type + if (containsType === 'union') { + const unionTypes = prop.type.contains?.types || [] + const allProperties = new Map() + unionTypes.forEach((unionType: any) => { + if (unionType.type === 'object' && unionType.properties) { + unionType.properties.forEach((p: any) => { + if (!allProperties.has(p.name)) { + allProperties.set(p.name, p) + } + }) + } + }) + properties = Array.from(allProperties.values()) + } + else if (containsType === 'object') { + const schemaProps = prop.type.contains?.properties || [] + const allProps = new Map() + + schemaProps.forEach((p: any) => { + allProps.set(p.name, p) + }) + + for (const item of this.items) { + const arr = item[fieldName] + if (Array.isArray(arr)) { + for (const obj of arr) { + if (obj && typeof obj === 'object') { + Object.keys(obj).forEach((key) => { + if (!allProps.has(key)) { + const value = obj[key] + const inferredType = Array.isArray(value) ? 'array' : typeof value + allProps.set(key, { name: key, type: { type: inferredType } }) + } + }) + } + } + } + } + properties = Array.from(allProps.values()) + } + } + + if (properties.length > 0) { + const sortedProps = [...properties] + + const typeIndex = sortedProps.findIndex(p => p.name === 'type') + if (typeIndex > 0) { + const typeProp = sortedProps.splice(typeIndex, 1)[0] + sortedProps.unshift(typeProp) + } + + const nameIndex = sortedProps.findIndex(p => p.name === 'name') + if (nameIndex > 0) { + const nameProp = sortedProps.splice(nameIndex, 1)[0] + const insertIndex = sortedProps[0]?.name === 'type' ? 1 : 0 + sortedProps.splice(insertIndex, 0, nameProp) + } + + return sortedProps.map(p => { + const isArray = p.type?.type === 'array' + const containsType = p.type?.contains?.type + const isExpandableArray = isArray && (containsType === 'union' || (containsType === 'object' && p.type.contains?.properties)) + + return { + ...p, + isObject: p.type?.type === 'object', + isArray: isExpandableArray, + isUnionArray: isArray && containsType === 'union' + } + }) + } + + return [] + } + + // Toggle nested object expansion (3rd level) + toggleNestedObjectExpansion(fieldName: string, nestedProp: string) { + const key = `${fieldName}.${nestedProp}` + if (this.expandedNestedObjects.has(key)) { + this.expandedNestedObjects.delete(key) + } else { + this.expandedNestedObjects.add(key) + } + } + + // Check if nested object is expanded + isNestedObjectExpanded(fieldName: string, nestedProp: string): boolean { + return this.expandedNestedObjects.has(`${fieldName}.${nestedProp}`) + } + + // Toggle deeply nested expansion (4th level - e.g., research within education) + toggleDeepNestedExpansion(fieldName: string, nestedProp: string, deepProp: string) { + const key = `${fieldName}.${nestedProp}.${deepProp}` + if (this.expandedDeepNested.has(key)) { + this.expandedDeepNested.delete(key) + } else { + this.expandedDeepNested.add(key) + } + } + + // Check if deeply nested item is expanded + isDeepNestedExpanded(fieldName: string, nestedProp: string, deepProp: string): boolean { + return this.expandedDeepNested.has(`${fieldName}.${nestedProp}.${deepProp}`) + } + + // Toggle very deeply nested expansion (5th level - e.g., funding within research) + toggleVeryDeepNestedExpansion(fieldName: string, nestedProp: string, deepProp: string, veryDeepProp: string) { + const key = `${fieldName}.${nestedProp}.${deepProp}.${veryDeepProp}` + if (this.expandedVeryDeepNested.has(key)) { + this.expandedVeryDeepNested.delete(key) + } else { + this.expandedVeryDeepNested.add(key) + } + } + + // Check if very deeply nested item is expanded + isVeryDeepNestedExpanded(fieldName: string, nestedProp: string, deepProp: string, veryDeepProp: string): boolean { + return this.expandedVeryDeepNested.has(`${fieldName}.${nestedProp}.${deepProp}.${veryDeepProp}`) + } + + // Get properties of a very deeply nested array (5th level - e.g., funding within research) + getVeryDeepNestedProperties(fieldName: string, nestedProp: string, deepProp: string, veryDeepProp: string): any[] { + const deepProps = this.getDeepNestedProperties(fieldName, nestedProp, deepProp) + const veryDeepProperty = deepProps.find((p: any) => p.name === veryDeepProp) + if (!veryDeepProperty) return [] + + let properties: any[] = [] + const veryDeepType = veryDeepProperty.type?.type + + if (veryDeepType === 'array') { + const containsType = veryDeepProperty.type.contains?.type + if (containsType === 'union') { + const unionTypes = veryDeepProperty.type.contains?.types || [] + const allProperties = new Map() + unionTypes.forEach((unionType: any) => { + if (unionType.type === 'object' && unionType.properties) { + unionType.properties.forEach((p: any) => { + if (!allProperties.has(p.name)) { + allProperties.set(p.name, p) + } + }) + } + }) + properties = Array.from(allProperties.values()) + } + else if (containsType === 'object') { + properties = veryDeepProperty.type.contains?.properties || [] + } + } else if (veryDeepType === 'object') { + properties = veryDeepProperty.type?.properties || [] + } + + if (properties.length > 0) { + const sortedProps = [...properties] + const typeIndex = sortedProps.findIndex((p: any) => p.name === 'type') + if (typeIndex > 0) { + const typeProp = sortedProps.splice(typeIndex, 1)[0] + sortedProps.unshift(typeProp) + } + return sortedProps.map((p: any) => ({ name: p.name, ...p })) + } + + return properties.map((p: any) => ({ name: p.name, ...p })) + } + + // Toggle nested array expansion (for +n click in nested arrays like research) + toggleNestedArrayExpansion(rowId: string, parentPath: string, arrayField: string) { + if (!this.expandedNestedArrays.has(rowId)) { + this.expandedNestedArrays.set(rowId, new Map()) + } + + const rowMap = this.expandedNestedArrays.get(rowId)! + if (!rowMap.has(parentPath)) { + rowMap.set(parentPath, new Set()) + } + + const fieldSet = rowMap.get(parentPath)! + if (fieldSet.has(arrayField)) { + fieldSet.delete(arrayField) + } else { + fieldSet.add(arrayField) + } + } + + // Check if nested array is expanded for row + isNestedArrayExpanded(rowId: string, parentPath: string, arrayField: string): boolean { + return this.expandedNestedArrays.get(rowId)?.get(parentPath)?.has(arrayField) || false + } + + // Get properties of a deeply nested array (4th level) + getDeepNestedProperties(fieldName: string, nestedProp: string, deepProp: string): any[] { + const prop = this.structureProperties.find((p: any) => p.name === fieldName) + if (!prop) return [] + + const type = prop.type?.type + let parentProperties: any[] = [] + if (type === 'object') { + parentProperties = prop.type?.properties || [] + } else if (type === 'array') { + const containsType = prop.type.contains?.type + if (containsType === 'object') { + parentProperties = prop.type.contains?.properties || [] + } + } + + const nestedProperty = parentProperties.find((p: any) => p.name === nestedProp) + if (!nestedProperty) return [] + + let nestedProperties: any[] = [] + const nestedType = nestedProperty.type?.type + + if (nestedType === 'object') { + nestedProperties = nestedProperty.type?.properties || [] + } else if (nestedType === 'array') { + const containsType = nestedProperty.type.contains?.type + if (containsType === 'object') { + nestedProperties = nestedProperty.type.contains?.properties || [] + } + } + + const deepProperty = nestedProperties.find((p: any) => p.name === deepProp) + if (!deepProperty) return [] + + let properties: any[] = [] + const deepType = deepProperty.type?.type + + if (deepType === 'object') { + properties = deepProperty.type?.properties || [] + } else if (deepType === 'array') { + const containsType = deepProperty.type.contains?.type + if (containsType === 'object') { + properties = deepProperty.type.contains?.properties || [] + } else if (containsType === 'union') { + const unionTypes = deepProperty.type.contains?.types || [] + const allProperties = new Map() + + unionTypes.forEach((unionType: any) => { + if (unionType.type === 'object' && unionType.properties) { + unionType.properties.forEach((prop: any) => { + if (!allProperties.has(prop.name)) { + allProperties.set(prop.name, prop) + } + }) + } + }) + + properties = Array.from(allProperties.values()) + } + } + + return properties.map((p: any) => ({ + name: p.name, + type: p.type, + isArray: p.type?.type === 'array', + ...p + })) + } + + // Get properties of a nested object (3rd level) + getNestedObjectProperties(fieldName: string, nestedProp: string): any[] { + const prop = this.structureProperties.find((p: any) => p.name === fieldName) + if (!prop) return [] + + const type = prop.type?.type + + let parentProperties: any[] = [] + if (type === 'object') { + parentProperties = prop.type?.properties || [] + } else if (type === 'array') { + const containsType = prop.type.contains?.type + if (containsType === 'object') { + parentProperties = prop.type.contains?.properties || [] + } + } + + // Find the nested property + const nestedProperty = parentProperties.find((p: any) => p.name === nestedProp) + if (!nestedProperty) return [] + + let properties: any[] = [] + const nestedType = nestedProperty.type?.type + + if (nestedType === 'object') { + properties = nestedProperty.type?.properties || [] + } + else if (nestedType === 'array') { + const containsType = nestedProperty.type.contains?.type + if (containsType === 'object') { + properties = nestedProperty.type.contains?.properties || [] + } + } + + return properties.map((p: any) => { + const isArray = p.type?.type === 'array' + const containsType = p.type?.contains?.type + + return { + name: p.name, + type: p.type, + isArray: isArray, + isUnionArray: isArray && containsType === 'union', + ...p + } + }) + } + + // Calculate width for a nested property (accounts for expanded nested objects and arrays) + getNestedPropWidth(fieldName: string, nestedProp: any): number { + const key = `${fieldName}.${nestedProp.name}` + + if ((nestedProp.isObject || nestedProp.isArray) && this.isNestedObjectExpanded(fieldName, nestedProp.name)) { + const subProps = this.getNestedObjectProperties(fieldName, nestedProp.name) + let totalWidth = 0 + for (const subProp of subProps) { + if (subProp.type?.type === 'array' && this.isDeepNestedExpanded(fieldName, nestedProp.name, subProp.name)) { + const deepProps = this.getDeepNestedProperties(fieldName, nestedProp.name, subProp.name) + for (const deepProp of deepProps) { + // Check if this deepProp is an array and is expanded to 5th level + if (deepProp.type?.type === 'array' && this.isVeryDeepNestedExpanded(fieldName, nestedProp.name, subProp.name, deepProp.name)) { + const veryDeepProps = this.getVeryDeepNestedProperties(fieldName, nestedProp.name, subProp.name, deepProp.name) + for (const vdp of veryDeepProps) { + totalWidth += Math.max(vdp.name.length * 9 + 32, 100) + } + } else { + totalWidth += this.getDeepNestedSubColumnWidth(fieldName, nestedProp.name, subProp.name, deepProp.name) + } + } + } else { + totalWidth += this.getNestedObjectSubColumnWidth(fieldName, nestedProp.name, subProp.name) + } + } + const minWidthForAllColumns = Math.max(totalWidth, subProps.length * 80) + + if (this.nestedColumnWidths.has(key)) { + const manualWidth = this.nestedColumnWidths.get(key)! + return Math.max(manualWidth, minWidthForAllColumns) + } + + return minWidthForAllColumns + } + + if (this.nestedColumnWidths.has(key)) { + return this.nestedColumnWidths.get(key)! + } + + if (nestedProp.name === 'name') return 220 + if (nestedProp.name === 'sku') return 120 + if (nestedProp.name === 'productId') return 80 + if (['street', 'city', 'state'].includes(nestedProp.name)) return 150 + if (['postalCode', 'country'].includes(nestedProp.name)) return 120 + return 100 + } + + // Calculate total dynamic width for expanded parent column + getExpandedColumnWidth(fieldName: string): number { + if (!this.isColumnExpanded(fieldName)) return 240 + + const nestedProps = this.getNestedProperties(fieldName) + let totalWidth = 0 + + for (const nestedProp of nestedProps) { + totalWidth += this.getNestedPropWidth(fieldName, nestedProp) + } + + const hasNestedCustomWidths = nestedProps.some(p => + this.nestedColumnWidths.has(`${fieldName}.${p.name}`) + ) + + const hasExpandedNestedColumns = nestedProps.some(p => + (p.isObject || p.isArray) && this.isNestedObjectExpanded(fieldName, p.name) + ) + + if (hasExpandedNestedColumns) { + return Math.max(totalWidth, 120) + } + + const header = this.headers.find(h => h.field === fieldName) + if (header?.expandedWidth && !hasNestedCustomWidths) { + return Math.max(header.expandedWidth, totalWidth) + } + + return Math.max(totalWidth, 120) + } + + // Calculate consistent width for sub-columns of nested objects/arrays (same width for all rows) + getNestedObjectSubColumnWidth(fieldName: string, nestedProp: string, subPropName: string): number { + const key = `${fieldName}.${nestedProp}.${subPropName}` + if (this.deepNestedColumnWidths.has(key)) { + return this.deepNestedColumnWidths.get(key)! + } + + if (!this.items || this.items.length === 0) return 80 + + let maxLength = subPropName.length // Start with header length + + for (const item of this.items) { + const nestedValue = item[fieldName]?.[nestedProp] + let value + + if (Array.isArray(nestedValue) && nestedValue.length > 0) { + value = nestedValue[0]?.[subPropName] + } + else { + value = nestedValue?.[subPropName] + } + + if (value !== null && value !== undefined) { + const strValue = String(value) + maxLength = Math.max(maxLength, strValue.length) + } + } + + return Math.max(Math.min(maxLength * 9 + 40, 300), 100) + } + + // Get width for deeply nested properties (4th level - orange headers) + getDeepNestedSubColumnWidth(fieldName: string, nestedProp: string, arrayProp: string, subPropName: string): number { + const key = `${fieldName}.${nestedProp}.${arrayProp}.${subPropName}` + if (this.veryDeepNestedColumnWidths.has(key)) { + return this.veryDeepNestedColumnWidths.get(key)! + } + + if (!this.items || this.items.length === 0) return 80 + + let maxLength = subPropName.length + + for (const item of this.items) { + const nestedArray = item[fieldName]?.[nestedProp] + if (Array.isArray(nestedArray) && nestedArray.length > 0) { + const firstNested = nestedArray[0] + const deepArray = firstNested?.[arrayProp] + if (Array.isArray(deepArray)) { + for (const deepItem of deepArray) { + const value = deepItem?.[subPropName] + if (value !== null && value !== undefined) { + const strValue = Array.isArray(value) ? `[${value.length} items]` : String(value) + maxLength = Math.max(maxLength, strValue.length) + } + } + } + } + } + + return Math.max(Math.min(maxLength * 9 + 40, 300), 100) + } + + // Get all columns including expanded nested ones + get allColumns(): any[] { + const columns: any[] = [] + + for (const header of this.headers) { + if (this.isColumnExpanded(header.field)) { + // Add nested properties as separate columns + const nestedProps = this.getNestedProperties(header.field) + for (const nestedProp of nestedProps) { + columns.push({ + header: nestedProp.name, + field: `${header.field}.${nestedProp.name}`, + parentField: header.field, + sortable: false, + width: 120, + isNested: true, + level: 1 + }) + } + } else { + columns.push(header) + } + } + + return columns + } + + // Get display value for a cell + getCellDisplayValue(data: any, field: string): string { + const value = data[field] + if (value === null || value === undefined) return 'null' + + if (Array.isArray(value)) { + if (value.length === 0) return '[]' + const firstItem = value[0] + if (typeof firstItem !== 'object') { + return value.length > 1 ? `${firstItem} +${value.length - 1}` : String(firstItem) + } + const itemName = firstItem?.name || firstItem?.title || firstItem?.id + if (itemName) { + return value.length > 1 ? `${itemName} +${value.length - 1}` : itemName + } + const keys = Object.keys(firstItem) + if (keys.length > 0) { + const firstValue = firstItem[keys[0]] + return value.length > 1 ? `${firstValue} +${value.length - 1}` : String(firstValue) + } + return value.length > 1 ? `Item +${value.length - 1}` : 'Item' + } else if (typeof value === 'object') { + const keys = Object.keys(value) + if (keys.length === 0) return '{}' + + for (const key of keys) { + const propValue = value[key] + if (Array.isArray(propValue) && propValue.length > 0) { + const firstItem = propValue[0] + if (typeof firstItem === 'object' && firstItem !== null) { + const itemKeys = Object.keys(firstItem) + if (itemKeys.length > 0) { + return String(firstItem[itemKeys[0]] ?? 'Item') + } + } + } + } + + const firstKey = keys[0] + const firstValue = value[firstKey] + + if (typeof firstValue === 'object' && firstValue !== null && !Array.isArray(firstValue)) { + const nestedKeys = Object.keys(firstValue) + if (nestedKeys.length > 0) { + return String(firstValue[nestedKeys[0]] || nestedKeys[0]) + } + } + + return String(firstValue || firstKey) + } + + return String(value) + } + onPage(event: any) { this.options.rows = event.rows this.options.first = event.first @@ -122,8 +742,240 @@ if (!id) { } displayAlert(text: string) { - console.log(text) - // alert(text) + } + + startColumnResize(event: MouseEvent, header: any) { + this.resizingColumn = header + this.startX = event.pageX + this.wasExpanded = this.isColumnExpanded(header.field) + this.startWidth = this.wasExpanded + ? this.getExpandedColumnWidth(header.field) + : header.width + + document.addEventListener('mousemove', this.onColumnResize) + document.addEventListener('mouseup', this.stopColumnResize) + document.body.style.cursor = 'col-resize' + document.body.style.userSelect = 'none' + } + + onColumnResize(event: MouseEvent) { + if (!this.resizingColumn) return + + const diff = event.pageX - this.startX + const newWidth = Math.max(50, this.startWidth + diff) + + if (this.wasExpanded) { + this.resizingColumn.expandedWidth = newWidth + this.resizingColumn.width = newWidth + } else { + this.resizingColumn.width = newWidth + this.resizingColumn.expandedWidth = newWidth + } + } + + stopColumnResize() { + document.removeEventListener('mousemove', this.onColumnResize) + document.removeEventListener('mouseup', this.stopColumnResize) + document.body.style.cursor = '' + document.body.style.userSelect = '' + this.resizingColumn = null + } + + // Nested column resize methods + startNestedColumnResize(event: MouseEvent, parentField: string, nestedProp: any) { + this.resizingColumn = { parentField, nestedProp } + this.startX = event.pageX + this.startWidth = this.getNestedPropWidth(parentField, nestedProp) + + document.addEventListener('mousemove', this.onNestedColumnResize) + document.addEventListener('mouseup', this.stopNestedColumnResize) + document.body.style.cursor = 'col-resize' + document.body.style.userSelect = 'none' + } + + onNestedColumnResize(event: MouseEvent) { + if (!this.resizingColumn || !this.resizingColumn.nestedProp) return + + const diff = event.pageX - this.startX + const newWidth = Math.max(80, this.startWidth + diff) + + const key = `${this.resizingColumn.parentField}.${this.resizingColumn.nestedProp.name}` + this.nestedColumnWidths.set(key, newWidth) + + const parentField = this.resizingColumn.parentField + const header = this.headers.find(h => h.field === parentField) + if (header) { + const nestedProps = this.getNestedProperties(parentField) + let totalWidth = 0 + for (const prop of nestedProps) { + totalWidth += this.getNestedPropWidth(parentField, prop) + } + header.expandedWidth = totalWidth + header.width = totalWidth + } + } + + stopNestedColumnResize() { + document.removeEventListener('mousemove', this.onNestedColumnResize) + document.removeEventListener('mouseup', this.stopNestedColumnResize) + document.body.style.cursor = '' + document.body.style.userSelect = '' + this.resizingColumn = null + } + + // Deep nested column resize methods (3rd level) + startDeepNestedColumnResize(event: MouseEvent, parentField: string, nestedProp: string, deepProp: any) { + this.resizingColumn = { parentField, nestedProp, deepProp, level: 'deep' } + this.startX = event.pageX + this.startWidth = this.getNestedObjectSubColumnWidth(parentField, nestedProp, deepProp.name) + + document.addEventListener('mousemove', this.onDeepNestedColumnResize) + document.addEventListener('mouseup', this.stopDeepNestedColumnResize) + document.body.style.cursor = 'col-resize' + document.body.style.userSelect = 'none' + } + + onDeepNestedColumnResize(event: MouseEvent) { + if (!this.resizingColumn || this.resizingColumn.level !== 'deep') return + + const diff = event.pageX - this.startX + const newWidth = Math.max(80, this.startWidth + diff) + + const key = `${this.resizingColumn.parentField}.${this.resizingColumn.nestedProp}.${this.resizingColumn.deepProp.name}` + this.deepNestedColumnWidths.set(key, newWidth) + + const nestedKey = `${this.resizingColumn.parentField}.${this.resizingColumn.nestedProp}` + const parentField = this.resizingColumn.parentField + const nestedPropName = this.resizingColumn.nestedProp + const deepProps = this.getNestedObjectProperties(parentField, nestedPropName) + let totalWidth = 0 + for (const prop of deepProps) { + totalWidth += this.getNestedObjectSubColumnWidth(parentField, nestedPropName, prop.name) + } + this.nestedColumnWidths.set(nestedKey, totalWidth) + + const header = this.headers.find(h => h.field === parentField) + if (header) { + const nestedProps = this.getNestedProperties(parentField) + let parentTotalWidth = 0 + for (const prop of nestedProps) { + parentTotalWidth += this.getNestedPropWidth(parentField, prop) + } + header.expandedWidth = parentTotalWidth + header.width = parentTotalWidth + } + } + + stopDeepNestedColumnResize() { + document.removeEventListener('mousemove', this.onDeepNestedColumnResize) + document.removeEventListener('mouseup', this.stopDeepNestedColumnResize) + document.body.style.cursor = '' + document.body.style.userSelect = '' + this.resizingColumn = null + } + + // Very deep nested column resize methods (4th level) + startVeryDeepNestedColumnResize(event: MouseEvent, parentField: string, nestedProp: string, deepProp: string, veryDeepProp: any) { + this.resizingColumn = { parentField, nestedProp, deepProp, veryDeepProp, level: 'veryDeep' } + this.startX = event.pageX + this.startWidth = this.getDeepNestedSubColumnWidth(parentField, nestedProp, deepProp, veryDeepProp.name) + + document.addEventListener('mousemove', this.onVeryDeepNestedColumnResize) + document.addEventListener('mouseup', this.stopVeryDeepNestedColumnResize) + document.body.style.cursor = 'col-resize' + document.body.style.userSelect = 'none' + } + + onVeryDeepNestedColumnResize(event: MouseEvent) { + if (!this.resizingColumn || this.resizingColumn.level !== 'veryDeep') return + + const diff = event.pageX - this.startX + const newWidth = Math.max(80, this.startWidth + diff) + + const key = `${this.resizingColumn.parentField}.${this.resizingColumn.nestedProp}.${this.resizingColumn.deepProp}.${this.resizingColumn.veryDeepProp.name}` + this.veryDeepNestedColumnWidths.set(key, newWidth) + + // Cascade width updates up the hierarchy + this.updateParentWidthsAfterDeepResize( + this.resizingColumn.parentField, + this.resizingColumn.nestedProp, + this.resizingColumn.deepProp + ) + } + + stopVeryDeepNestedColumnResize() { + document.removeEventListener('mousemove', this.onVeryDeepNestedColumnResize) + document.removeEventListener('mouseup', this.stopVeryDeepNestedColumnResize) + document.body.style.cursor = '' + document.body.style.userSelect = '' + this.resizingColumn = null + } + + // Ultra deep nested column resize methods (5th level) + startUltraDeepNestedColumnResize(event: MouseEvent, parentField: string, nestedProp: string, deepProp: string, veryDeepProp: string, ultraDeepProp: any) { + this.resizingColumn = { parentField, nestedProp, deepProp, veryDeepProp, ultraDeepProp, level: 'ultraDeep' } + this.startX = event.pageX + this.startWidth = Math.max(ultraDeepProp.name.length * 9 + 32, 100) + + document.addEventListener('mousemove', this.onUltraDeepNestedColumnResize) + document.addEventListener('mouseup', this.stopUltraDeepNestedColumnResize) + document.body.style.cursor = 'col-resize' + document.body.style.userSelect = 'none' + } + + onUltraDeepNestedColumnResize(event: MouseEvent) { + if (!this.resizingColumn || this.resizingColumn.level !== 'ultraDeep') return + + const diff = event.pageX - this.startX + const newWidth = Math.max(80, this.startWidth + diff) + + const key = `${this.resizingColumn.parentField}.${this.resizingColumn.nestedProp}.${this.resizingColumn.deepProp}.${this.resizingColumn.veryDeepProp}.${this.resizingColumn.ultraDeepProp.name}` + this.veryDeepNestedColumnWidths.set(key, newWidth) + + // Cascade width updates up the hierarchy + this.updateParentWidthsAfterDeepResize( + this.resizingColumn.parentField, + this.resizingColumn.nestedProp, + this.resizingColumn.deepProp + ) + } + + stopUltraDeepNestedColumnResize() { + document.removeEventListener('mousemove', this.onUltraDeepNestedColumnResize) + document.removeEventListener('mouseup', this.stopUltraDeepNestedColumnResize) + document.body.style.cursor = '' + document.body.style.userSelect = '' + this.resizingColumn = null + } + + // Helper method to update parent widths after deep nested resize + updateParentWidthsAfterDeepResize(parentField: string, nestedProp: string, deepProp: string) { + const deepKey = `${parentField}.${nestedProp}.${deepProp}` + const veryDeepProps = this.getDeepNestedProperties(parentField, nestedProp, deepProp) + let deepTotalWidth = 0 + for (const prop of veryDeepProps) { + deepTotalWidth += this.getDeepNestedSubColumnWidth(parentField, nestedProp, deepProp, prop.name) + } + this.deepNestedColumnWidths.set(deepKey, deepTotalWidth) + + const nestedKey = `${parentField}.${nestedProp}` + const deepProps = this.getNestedObjectProperties(parentField, nestedProp) + let nestedTotalWidth = 0 + for (const prop of deepProps) { + nestedTotalWidth += this.getNestedObjectSubColumnWidth(parentField, nestedProp, prop.name) + } + this.nestedColumnWidths.set(nestedKey, nestedTotalWidth) + + const header = this.headers.find(h => h.field === parentField) + if (header) { + const nestedProps = this.getNestedProperties(parentField) + let parentTotalWidth = 0 + for (const prop of nestedProps) { + parentTotalWidth += this.getNestedPropWidth(parentField, prop) + } + header.expandedWidth = parentTotalWidth + header.width = parentTotalWidth + } } find() { @@ -165,10 +1017,12 @@ if (!id) { }) } } + +export default EntityList