PluginProbe
The Innovative Form Builder – IvyForms / 1.1.1
The Innovative Form Builder – IvyForms v1.1.1
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / utils / nameFieldKeyUtils.ts

nameFieldKeyUtils.ts in The Innovative Form Builder – IvyForms 1.1.1, at frontend/src/utils/nameFieldKeyUtils.ts

156 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import type { Field } from '@/types/field'
2
3 const NAME_FIELD_KEY_PATTERN = /^nameField(\d+)$/i
4
5 export function isNameFieldKey(value: unknown): value is string {
6 return typeof value === 'string' && NAME_FIELD_KEY_PATTERN.test(value)
7 }
8
9 function parseNameFieldKeyIndex(value: string): number | null {
10 const match = NAME_FIELD_KEY_PATTERN.exec(value)
11 return match ? parseInt(match[1], 10) : null
12 }
13
14 export function getChildFieldSettings(field: Field): Record<string, unknown> {
15 let settings: Record<string, unknown> = {}
16
17 if (typeof field.settings === 'string') {
18 try {
19 settings = JSON.parse(field.settings) as Record<string, unknown>
20 } catch {
21 settings = {}
22 }
23 } else if (typeof field.settings === 'object' && field.settings !== null) {
24 settings = { ...(field.settings as Record<string, unknown>) }
25 }
26
27 if (field.nameFieldType) {
28 settings.nameFieldType = field.nameFieldType
29 }
30 if (field.subFieldIndex !== undefined) {
31 settings.subFieldIndex = field.subFieldIndex
32 }
33
34 return settings
35 }
36
37 function inferNameFieldKeyFromLabel(label: unknown): string | null {
38 if (typeof label !== 'string' || label.trim() === '') {
39 return null
40 }
41
42 const nameFieldMatch = /^nameField(\d+)$/i.exec(label.trim())
43 if (nameFieldMatch) {
44 return `nameField${nameFieldMatch[1]}`
45 }
46
47 const labelMatch = /name\s*field\s*(\d+)/i.exec(label.trim())
48 if (labelMatch) {
49 return `nameField${labelMatch[1]}`
50 }
51
52 return null
53 }
54
55 export function resolveNameFieldKey(
56 subfield: { type?: string; label?: string },
57 settings?: Record<string, unknown>,
58 fallbackIndex?: number,
59 ): string {
60 if (isNameFieldKey(subfield.type)) {
61 return subfield.type
62 }
63
64 const fromSettings = settings?.nameFieldType
65 if (isNameFieldKey(fromSettings)) {
66 return fromSettings
67 }
68
69 const fromLabel = inferNameFieldKeyFromLabel(subfield.label)
70 if (fromLabel) {
71 return fromLabel
72 }
73
74 if (fallbackIndex !== undefined) {
75 return `nameField${fallbackIndex + 1}`
76 }
77
78 return 'nameField1'
79 }
80
81 function canonicalizeNameFieldKey(key: string): string {
82 const index = parseNameFieldKeyIndex(key)
83 return index !== null ? `nameField${index}` : key
84 }
85
86 function ensureUniqueNameFieldKey(key: string, usedKeys: readonly string[]): string {
87 const normalizedKey = isNameFieldKey(key) ? canonicalizeNameFieldKey(key) : key
88 const normalizedUsed = usedKeys.map((usedKey) =>
89 isNameFieldKey(usedKey) ? canonicalizeNameFieldKey(usedKey) : usedKey,
90 )
91
92 if (!normalizedUsed.includes(normalizedKey)) {
93 return normalizedKey
94 }
95
96 let candidate = 1
97 while (normalizedUsed.includes(`nameField${candidate}`)) {
98 candidate++
99 }
100
101 return `nameField${candidate}`
102 }
103
104 export function resolveUniqueNameFieldKey(
105 subfield: { type?: string; label?: string },
106 settings: Record<string, unknown> | undefined,
107 fallbackIndex: number,
108 usedKeys: readonly string[],
109 ): string {
110 const key = resolveNameFieldKey(subfield, settings, fallbackIndex)
111 return ensureUniqueNameFieldKey(key, usedKeys)
112 }
113
114 function getSubFieldDisplayIndex(settings?: Record<string, unknown>): number | null {
115 const value = settings?.subFieldIndex
116 if (typeof value === 'number' && Number.isFinite(value)) {
117 return value
118 }
119 if (typeof value === 'string' && value.trim() !== '') {
120 const parsed = Number(value)
121 return Number.isFinite(parsed) ? parsed : null
122 }
123 return null
124 }
125
126 export function sortChildrenByDisplayOrder<T extends Field>(
127 children: T[],
128 apiOrder: Map<T, number>,
129 ): T[] {
130 return [...children].sort((a, b) => {
131 const aSettings = getChildFieldSettings(a)
132 const bSettings = getChildFieldSettings(b)
133 const aIndex = getSubFieldDisplayIndex(aSettings)
134 const bIndex = getSubFieldDisplayIndex(bSettings)
135 const aHas = aIndex !== null
136 const bHas = bIndex !== null
137 if (aHas && bHas) return aIndex - bIndex
138 if (aHas) return -1
139 if (bHas) return 1
140 return (apiOrder.get(a) || 0) - (apiOrder.get(b) || 0)
141 })
142 }
143
144 export function assignLegacyNameFieldKeysIfMissing<T extends { type?: string; label?: string }>(
145 subfields: T[],
146 settingsList: Array<Record<string, unknown>> = [],
147 ): Array<T & { type: string }> {
148 const usedKeys: string[] = []
149
150 return subfields.map((subfield, idx) => {
151 const type = resolveUniqueNameFieldKey(subfield, settingsList[idx], idx, usedKeys)
152 usedKeys.push(type)
153 return { ...subfield, type }
154 })
155 }
156