@@ -35,63 +35,189 @@ public static function getDefaultConfig(): array
3535
3636 public static function make (string $ name , ?Field $ field = null ): Input
3737 {
38+ $ input = self ::createBaseInput ($ name , $ field );
39+ $ input = self ::configureToolbarButtons ($ input , $ field );
40+ $ input = self ::configureStateHandling ($ input , $ name );
41+ $ input = self ::configureCaptions ($ input , $ field );
3842
39- $ input = self ::applyDefaultSettings (Input::make ($ name ), $ field );
43+ return $ input ;
44+ }
45+
46+ private static function createBaseInput (string $ name , ?Field $ field ): Input
47+ {
48+ return self ::applyDefaultSettings (Input::make ($ name ), $ field )
49+ ->label ($ field ->name ?? null )
50+ ->default (null )
51+ ->placeholder ('' )
52+ ->statePath ($ name )
53+ ->live ()
54+ ->json (false )
55+ ->beforeStateDehydrated (function () {})
56+ ->saveRelationshipsUsing (function () {});
57+ }
4058
41- $ input = $ input ->label ($ field ->name ?? null )
42- ->toolbarButtons ([$ field ->config ['toolbarButtons ' ] ?? self ::getDefaultConfig ()['toolbarButtons ' ]])
43- ->disableToolbarButtons ($ field ->config ['disableToolbarButtons ' ] ?? self ::getDefaultConfig ()['disableToolbarButtons ' ]);
59+ private static function configureToolbarButtons (Input $ input , ?Field $ field ): Input
60+ {
61+ $ config = self ::getDefaultConfig ();
62+
63+ return $ input
64+ ->toolbarButtons ([$ field ->config ['toolbarButtons ' ] ?? $ config ['toolbarButtons ' ]])
65+ ->disableToolbarButtons ($ field ->config ['disableToolbarButtons ' ] ?? $ config ['disableToolbarButtons ' ]);
66+ }
4467
45- // Add data attribute for hiding captions if enabled
68+ private static function configureStateHandling (Input $ input , string $ name ): Input
69+ {
70+ return $ input ->formatStateUsing (function ($ state ) {
71+ return self ::formatRichEditorState ($ state );
72+ });
73+ }
74+
75+ private static function configureCaptions (Input $ input , ?Field $ field ): Input
76+ {
4677 $ hideCaptions = $ field ->config ['hideCaptions ' ] ?? self ::getDefaultConfig ()['hideCaptions ' ];
78+
4779 if ($ hideCaptions ) {
4880 $ input ->extraAttributes (['data-hide-captions ' => 'true ' ]);
4981 }
5082
51- // Add content processing to automatically clean HTML
52- $ autoCleanContent = $ field ->config ['autoCleanContent ' ] ?? self ::getDefaultConfig ()['autoCleanContent ' ];
83+ return $ input ;
84+ }
85+
86+ private static function formatRichEditorState ($ state )
87+ {
88+ if (empty ($ state )) {
89+ return null ;
90+ }
91+
92+ // If it's already a string (HTML), return it as is
93+ if (is_string ($ state )) {
94+ return $ state ;
95+ }
5396
54- if ( $ autoCleanContent ) {
55- $ options = [
56- ' preserveCustomCaptions ' => $ field -> config [ ' preserveCustomCaptions ' ] ?? self ::getDefaultConfig ()[ ' preserveCustomCaptions ' ],
57- ];
97+ // If it's an array (JSON format), handle it
98+ if ( is_array ( $ state )) {
99+ return self ::formatJsonState ( $ state );
100+ }
58101
59- // Clean content when state is updated (including file uploads)
60- $ input ->afterStateUpdated (function ($ state ) use ($ options ) {
61- if (! empty ($ state )) {
62- return ContentCleaningService::cleanHtmlContent ($ state , $ options );
63- }
102+ return null ;
103+ }
104+
105+ private static function formatJsonState (array $ state ): ?array
106+ {
107+ // Handle nested doc structure
108+ if (isset ($ state [0 ]) && is_array ($ state [0 ]) && isset ($ state [0 ]['type ' ]) && $ state [0 ]['type ' ] === 'doc ' ) {
109+ $ state = $ state [0 ];
110+ }
64111
65- return $ state ;
66- });
112+ // Clean up empty content arrays
113+ if (isset ($ state ['content ' ]) && is_array ($ state ['content ' ])) {
114+ $ state = self ::cleanContentArray ($ state );
115+ }
67116
68- // Ensure cleaned content is saved to database
69- $ input ->dehydrateStateUsing (function ($ state ) use ($ options ) {
70- if (! empty ($ state )) {
71- return ContentCleaningService::cleanHtmlContent ($ state , $ options );
72- }
117+ // Validate doc structure
118+ if (! isset ($ state ['type ' ]) || $ state ['type ' ] !== 'doc ' ) {
119+ return null ;
120+ }
73121
74- return $ state;
75- }) ;
122+ if (! isset ( $ state [ ' content ' ]) || ! is_array ( $ state[ ' content ' ])) {
123+ $ state [ ' content ' ] = [] ;
76124 }
77125
78- return $ input ;
126+ return $ state ;
127+ }
128+
129+ private static function cleanContentArray (array $ state ): array
130+ {
131+ $ content = $ state ['content ' ];
132+ if (count ($ content ) > 0 && is_array ($ content [0 ]) && empty ($ content [0 ])) {
133+ $ state ['content ' ] = [];
134+ }
135+
136+ return $ state ;
137+ }
138+
139+ public static function cleanRichEditorState ($ state , array $ options = [])
140+ {
141+ if (empty ($ state )) {
142+ return '' ;
143+ }
144+
145+ $ cleanedState = ContentCleaningService::cleanContent ($ state , $ options );
146+
147+ return $ cleanedState ;
79148 }
80149
81150 public static function mutateBeforeSaveCallback ($ record , $ field , array $ data ): array
82151 {
83- $ autoCleanContent = $ field ->config ['autoCleanContent ' ] ?? self ::getDefaultConfig ()['autoCleanContent ' ];
152+ $ data = self ::ensureRichEditorDataFormat ($ record , $ field , $ data );
153+
154+ if (self ::shouldAutoCleanContent ($ field )) {
155+ $ data = self ::applyContentCleaning ($ record , $ field , $ data );
156+ }
157+
158+ return $ data ;
159+ }
160+
161+ private static function shouldAutoCleanContent ($ field ): bool
162+ {
163+ return $ field ->config ['autoCleanContent ' ] ?? self ::getDefaultConfig ()['autoCleanContent ' ];
164+ }
84165
85- if ($ autoCleanContent && isset ($ data ['values ' ][$ field ->ulid ])) {
86- Log::info ('RichEditor mutateBeforeSaveCallback before cleaning: ' , ['content ' => $ data ['values ' ][$ field ->ulid ]]);
166+ private static function applyContentCleaning ($ record , $ field , array $ data ): array
167+ {
168+ $ options = self ::getCleaningOptions ($ field );
87169
88- $ options = [
89- 'preserveCustomCaptions ' => $ field ->config ['preserveCustomCaptions ' ] ?? self ::getDefaultConfig ()['preserveCustomCaptions ' ],
90- ];
170+ if (isset ($ data ['values ' ][$ field ->ulid ])) {
171+ // Called from ContentResource
172+ $ data ['values ' ][$ field ->ulid ] = self ::cleanRichEditorState ($ data ['values ' ][$ field ->ulid ], $ options );
173+ } elseif (isset ($ data [$ record ->valueColumn ][$ field ->ulid ])) {
174+ // Called from CanMapDynamicFields trait
175+ $ data [$ record ->valueColumn ][$ field ->ulid ] = self ::cleanRichEditorState ($ data [$ record ->valueColumn ][$ field ->ulid ], $ options );
176+ }
177+
178+ return $ data ;
179+ }
180+
181+ private static function getCleaningOptions ($ field ): array
182+ {
183+ return [
184+ 'preserveCustomCaptions ' => $ field ->config ['preserveCustomCaptions ' ] ?? self ::getDefaultConfig ()['preserveCustomCaptions ' ],
185+ ];
186+ }
91187
92- $ data ['values ' ][$ field ->ulid ] = ContentCleaningService::cleanHtmlContent ($ data ['values ' ][$ field ->ulid ], $ options );
188+ private static function ensureRichEditorDataFormat ($ record , $ field , array $ data ): array
189+ {
190+ $ data = self ::normalizeContentResourceValue ($ data , $ field );
191+ $ data = self ::normalizeDynamicFieldValue ($ record , $ data , $ field );
192+
193+ return $ data ;
194+ }
195+
196+ private static function normalizeContentResourceValue (array $ data , $ field ): array
197+ {
198+ if (isset ($ data ['values ' ][$ field ->ulid ]) && empty ($ data ['values ' ][$ field ->ulid ])) {
199+ $ data ['values ' ][$ field ->ulid ] = '' ;
200+ }
201+
202+ return $ data ;
203+ }
204+
205+ private static function normalizeDynamicFieldValue ($ record , array $ data , $ field ): array
206+ {
207+ if (isset ($ data [$ record ->valueColumn ][$ field ->ulid ]) && empty ($ data [$ record ->valueColumn ][$ field ->ulid ])) {
208+ $ data [$ record ->valueColumn ][$ field ->ulid ] = '' ;
209+ }
210+
211+ return $ data ;
212+ }
213+
214+ public static function mutateFormDataCallback ($ record , $ field , array $ data ): array
215+ {
216+ // Get the raw value from the database without JSON decoding
217+ $ rawValue = $ record ->values ()->where ('field_ulid ' , $ field ->ulid )->first ()?->value;
93218
94- Log::info ('RichEditor mutateBeforeSaveCallback after cleaning: ' , ['content ' => $ data ['values ' ][$ field ->ulid ]]);
219+ if ($ rawValue !== null ) {
220+ $ data [$ record ->valueColumn ][$ field ->ulid ] = $ rawValue ;
95221 }
96222
97223 return $ data ;
0 commit comments