@@ -522,51 +522,57 @@ private function buildNextJSComponent(
522522 // Build props object for element
523523 $ tsx .= " const elementProps: React.HTMLAttributes<HTMLElement> & Record<string, any> = {}; \n\n" ;
524524
525- // Handle className
526- if (isset ($ props ['className ' ])) {
527- $ tsx .= " if (className) { \n" ;
528- $ tsx .= " elementProps.className = className; \n" ;
529- $ tsx .= " } \n\n" ;
530- }
531-
532- // Handle data attributes
525+ // Handle data attributes specially
533526 if (isset ($ props ['data ' ])) {
527+ $ tsx .= " // Handle data attributes specially \n" ;
534528 $ tsx .= " if (data) { \n" ;
535529 $ tsx .= " Object.entries(data).forEach(([key, value]) => { \n" ;
536530 $ tsx .= " elementProps[`data- \${key}`] = value; \n" ;
537531 $ tsx .= " }); \n" ;
538532 $ tsx .= " } \n\n" ;
539533 }
540534
541- // Handle other props
542- foreach ($ props as $ prop ) {
543- if (in_array ($ prop ['name ' ], ['children ' , 'className ' , 'data ' ], true )) {
544- continue ;
545- }
535+ $ tsx .= " // Convert camelCase to kebab-case for attribute names \n" ;
536+ $ tsx .= " const toKebabCase = (str: string): string => { \n" ;
537+ $ tsx .= " return str.replace(/([a-z])([A-Z])/g, ' \$1- \$2').toLowerCase(); \n" ;
538+ $ tsx .= " }; \n\n" ;
546539
547- $ propName = $ prop [ ' name ' ] ;
548- $ htmlAttr = $ prop [ ' htmlAttr ' ] ?? $ this -> camelToKebab ( $ propName ) ;
540+ $ tsx .= " // Props to exclude from automatic mapping \n" ;
541+ $ tsx .= " const excludedProps = new Set(['children', 'data']); \n\n" ;
549542
550- // Special handling for boolean attributes - React expects boolean values
551- if ($ prop ['type ' ] === 'boolean ' ) {
552- $ tsx .= " if ( {$ propName } !== undefined) { \n" ;
553- if (strpos ($ htmlAttr , '- ' ) !== false ) {
554- $ tsx .= " elementProps[' {$ htmlAttr }'] = {$ propName } ? true : false; \n" ;
555- } else {
556- $ tsx .= " elementProps. {$ htmlAttr } = {$ propName } ? true : false; \n" ;
557- }
558- $ tsx .= " } \n\n" ;
559- } else {
560- $ tsx .= " if ( {$ propName } !== undefined) { \n" ;
561- if (strpos ($ htmlAttr , '- ' ) !== false ) {
562- $ tsx .= " elementProps[' {$ htmlAttr }'] = {$ propName }; \n" ;
563- } else {
564- $ tsx .= " elementProps. {$ htmlAttr } = {$ propName }; \n" ;
565- }
566- $ tsx .= " } \n\n" ;
543+ $ tsx .= " // Iterate over all props and map them to element attributes \n" ;
544+ $ tsx .= " Object.entries(props).forEach(([key, value]) => { \n" ;
545+ $ tsx .= " if (excludedProps.has(key) || value === undefined) { \n" ;
546+ $ tsx .= " return; \n" ;
547+ $ tsx .= " } \n\n" ;
548+
549+ $ tsx .= " // Convert camelCase aria/contenteditable/autocapitalize to kebab-case \n" ;
550+ $ tsx .= " const attrName = key.startsWith('aria') || key === 'contenteditable' || key === 'autocapitalize' \n" ;
551+ $ tsx .= " ? toKebabCase(key) \n" ;
552+ $ tsx .= " : key; \n\n" ;
553+
554+ // Collect boolean prop names for the generated code
555+ $ booleanProps = [];
556+ foreach ($ props as $ prop ) {
557+ if ($ prop ['type ' ] === 'boolean ' && ! in_array ($ prop ['name ' ], ['children ' , 'data ' ], true )) {
558+ $ booleanProps [] = $ prop ['name ' ];
567559 }
568560 }
569561
562+ if (! empty ($ booleanProps )) {
563+ $ tsx .= " // Handle boolean props that need explicit conversion \n" ;
564+ $ booleanPropsStr = implode ("' || key === ' " , $ booleanProps );
565+ $ tsx .= " if (typeof value === 'boolean' && (key === ' {$ booleanPropsStr }')) { \n" ;
566+ $ tsx .= " elementProps[attrName] = value ? true : false; \n" ;
567+ $ tsx .= " } else { \n" ;
568+ $ tsx .= " elementProps[attrName] = value; \n" ;
569+ $ tsx .= " } \n" ;
570+ } else {
571+ $ tsx .= " elementProps[attrName] = value; \n" ;
572+ }
573+
574+ $ tsx .= " }); \n\n" ;
575+
570576 // Render element
571577 $ tsx .= " return React.createElement( \n" ;
572578 $ tsx .= " ' {$ elementName }', \n" ;
0 commit comments