UNPKG

@birhaus/tools

Version:

BIRHAUS Developer Tools - CognitiveLoadMeter, linting rules, and validation tools

2 lines 17.1 kB
import {useState,useRef,useCallback,useEffect}from'react';import {Brain,Maximize2,Settings,Minimize2,BarChart3,AlertTriangle,Zap}from'lucide-react';import {jsx,jsxs}from'react/jsx-runtime';function I(...e){return e.filter(Boolean).join(" ")}function ye({_targetSelector:e="body",targetElement:i,enableRealTimeAnalysis:t=true,analysisInterval:s=2e3,position:d="top-right",minimized:g=false,showDetails:x=true,showOnlyViolations:p=false,minSeverity:O="medium",onAnalysisComplete:h,onViolationDetected:X,className:J}){let[r,K]=useState(null),[Q,H]=useState(false),[Y,$]=useState(g),[ee,ie]=useState(false),P=useRef(),te=()=>{let o=i||document.querySelector(e||"body")||document.body,l={navigationItems:0,formFields:0,choiceOptions:0,colorCount:0,fontVariations:0,visualLayers:0,textDensity:0,actionDensity:0,interactionComplexity:0,contrastIssues:0,missingAltText:0,keyboardNavigation:true,spanishLabelCoverage:0,bilingualSupport:false,undoPatternUsage:0,confirmationDialogs:0,progressiveDisclosure:0},u=[],b=[],S=o.querySelectorAll('nav a, nav button, [role="navigation"] a, [role="navigation"] button');l.navigationItems=S.length,S.length>7&&(u.push({id:"nav-miller-law",type:"miller-law",severity:S.length>10?"critical":"high",messageEs:`Navegaci\xF3n con ${S.length} elementos (m\xE1ximo recomendado: 7)`,messageEn:`Navigation has ${S.length} items (recommended max: 7)`,birhausPrinciple:1}),b.push({id:"nav-reduce-items",priority:"high",actionEs:"Agrupa elementos de navegaci\xF3n o usa men\xFAs desplegables",actionEn:"Group navigation items or use dropdown menus",estimatedImpact:85,birhausPrinciple:1}));let C=o.querySelectorAll("input, select, textarea");l.formFields=C.length,C.length>7&&(u.push({id:"form-miller-law",type:"miller-law",severity:C.length>10?"high":"medium",messageEs:`Formulario con ${C.length} campos (considera dividir en pasos)`,messageEn:`Form has ${C.length} fields (consider multi-step)`,birhausPrinciple:4}),b.push({id:"form-progressive",priority:"medium",actionEs:"Usa MinimalForm para dividir en pasos de m\xE1ximo 7 campos",actionEn:"Use MinimalForm to split into steps of max 7 fields",estimatedImpact:75,birhausPrinciple:4})),o.querySelectorAll("select").forEach(m=>{let c=m.querySelectorAll("option").length;l.choiceOptions=Math.max(l.choiceOptions,c),c>7&&u.push({id:`select-miller-law-${Math.random()}`,type:"miller-law",severity:c>12?"high":"medium",messageEs:`Lista desplegable con ${c} opciones (m\xE1ximo recomendado: 7)`,messageEn:`Dropdown has ${c} options (recommended max: 7)`,element:m,birhausPrinciple:1});});let y=new Set;o.querySelectorAll("*").forEach(m=>{let c=getComputedStyle(m);y.add(c.color),y.add(c.backgroundColor),y.add(c.fontFamily),y.add(c.fontSize);}),l.colorCount=Array.from(y).filter(m=>m.includes("rgb")||m.includes("#")).length,l.fontVariations=Array.from(y).filter(m=>m.includes("px")||m.includes("rem")).length;let A=o.querySelectorAll('button, a, [onclick], [role="button"]');l.actionDensity=A.length,A.length>12&&u.push({id:"action-density",type:"visual-complexity",severity:"medium",messageEs:`Demasiadas acciones disponibles (${A.length})`,messageEn:`Too many available actions (${A.length})`,birhausPrinciple:3});let B=o.querySelectorAll("[aria-label], label, button, a, h1, h2, h3, h4, h5, h6"),G=0,_=0;B.forEach(m=>{let c=m.textContent||m.getAttribute("aria-label")||"",W=/[ñáéíóúü]|[Ññ]|ción|dad|mente/i.test(c),re=m.hasAttribute("data-label-es")||m.hasAttribute("labelEs"),le=m.hasAttribute("data-label-en")||m.hasAttribute("labelEn");W&&G++,re&&le&&_++;}),l.spanishLabelCoverage=B.length>0?G/B.length*100:100,l.bilingualSupport=_>0,l.spanishLabelCoverage<70&&(u.push({id:"spanish-first",type:"birhaus-principle",severity:"medium",messageEs:`Solo ${Math.round(l.spanishLabelCoverage)}% de elementos usan espa\xF1ol`,messageEn:`Only ${Math.round(l.spanishLabelCoverage)}% of elements use Spanish`,birhausPrinciple:7}),b.push({id:"add-spanish-labels",priority:"medium",actionEs:"Agrega propiedades labelEs a los componentes",actionEn:"Add labelEs properties to components",estimatedImpact:60,birhausPrinciple:7}));let ne=["\xBFest\xE1 seguro?","confirmar","are you sure?","confirm"],L=Array.from(o.querySelectorAll("*")).filter(m=>ne.some(c=>m.textContent?.toLowerCase().includes(c)));l.confirmationDialogs=L.length,L.length>0&&(u.push({id:"confirmation-dialogs",type:"birhaus-principle",severity:"high",messageEs:`${L.length} di\xE1logo(s) de confirmaci\xF3n detectado(s)`,messageEn:`${L.length} confirmation dialog(s) detected`,birhausPrinciple:5}),b.push({id:"replace-with-undo",priority:"high",actionEs:"Reemplaza confirmaciones con funcionalidad de deshacer",actionEn:"Replace confirmations with undo functionality",estimatedImpact:90,birhausPrinciple:5}));let N=o.querySelectorAll("img:not([alt])");l.missingAltText=N.length,N.length>0&&u.push({id:"missing-alt-text",type:"accessibility",severity:"high",messageEs:`${N.length} imagen(es) sin texto alternativo`,messageEn:`${N.length} image(s) missing alt text`,birhausPrinciple:6});let ae=u.reduce((m,c)=>m+{low:1,medium:2,high:4,critical:8}[c.severity],0),w=Math.max(0,100-ae*5),oe=w>=80?"low":w>=60?"medium":w>=40?"high":"critical";return {score:w,level:oe,violations:u,suggestions:b.slice(0,5),metrics:l}},E=useCallback(()=>{H(true),setTimeout(()=>{let o=te();K(o),H(false),h?.(o),o.violations.forEach(l=>{se(l)&&X?.(l);});},100);},[h,X]),se=o=>{let l={low:1,medium:2,high:3,critical:4},u=l[O];return l[o.severity]>=u};useEffect(()=>{if(t)return E(),P.current=setInterval(()=>{E();},s),()=>{P.current&&clearInterval(P.current);}},[t,s,E]);let V={"top-right":"fixed top-4 right-4 z-50","bottom-right":"fixed bottom-4 right-4 z-50","bottom-left":"fixed bottom-4 left-4 z-50","top-left":"fixed top-4 left-4 z-50",floating:"fixed top-1/2 right-4 -translate-y-1/2 z-50"},j={low:"text-green-600 bg-green-100",medium:"text-yellow-600 bg-yellow-100",high:"text-orange-600 bg-orange-100",critical:"text-red-600 bg-red-100"};return Y?jsx("div",{className:I(V[d],J),children:jsxs("button",{onClick:()=>$(false),className:I("flex items-center gap-2 px-3 py-2 bg-white border border-gray-300 rounded-lg shadow-lg hover:shadow-xl transition-shadow",r&&j[r.level]),children:[jsx(Brain,{className:"h-4 w-4"}),r&&jsx("span",{className:"text-sm font-medium",children:r.score}),jsx(Maximize2,{className:"h-3 w-3"})]})}):jsx("div",{className:I(V[d],"w-80 max-w-[calc(100vw-2rem)]",J),children:jsxs("div",{className:"bg-white border border-gray-300 rounded-lg shadow-lg",children:[jsxs("div",{className:"flex items-center justify-between p-4 border-b border-gray-200",children:[jsxs("div",{className:"flex items-center gap-2",children:[jsx(Brain,{className:"h-5 w-5 text-blue-600"}),jsx("span",{className:"font-medium text-gray-900",children:"Cognitive Load"}),Q&&jsx("div",{className:"animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600"})]}),jsxs("div",{className:"flex items-center gap-1",children:[jsx("button",{onClick:()=>ie(!ee),className:"p-1 text-gray-400 hover:text-gray-600 rounded",children:jsx(Settings,{className:"h-4 w-4"})}),jsx("button",{onClick:()=>$(true),className:"p-1 text-gray-400 hover:text-gray-600 rounded",children:jsx(Minimize2,{className:"h-4 w-4"})}),jsx("button",{onClick:E,className:"p-1 text-gray-400 hover:text-gray-600 rounded",children:jsx(BarChart3,{className:"h-4 w-4"})})]})]}),r&&jsx("div",{className:"p-4",children:jsxs("div",{className:I("flex items-center justify-between p-3 rounded-lg",j[r.level]),children:[jsxs("div",{children:[jsx("div",{className:"text-2xl font-bold",children:r.score}),jsxs("div",{className:"text-sm capitalize",children:[r.level," load"]})]}),jsxs("div",{className:"text-right text-sm",children:[jsxs("div",{children:[r.violations.length," violaciones"]}),jsxs("div",{children:[r.suggestions.length," sugerencias"]})]})]})}),r&&x&&jsx("div",{className:"p-4 border-t border-gray-200",children:jsxs("div",{className:"space-y-3",children:[jsxs("div",{className:"grid grid-cols-2 gap-2 text-sm",children:[jsxs("div",{className:"flex justify-between",children:[jsx("span",{children:"Navegaci\xF3n:"}),jsxs("span",{className:r.metrics.navigationItems>7?"text-red-600":"text-green-600",children:[r.metrics.navigationItems,"/7"]})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{children:"Campos:"}),jsx("span",{className:r.metrics.formFields>7?"text-red-600":"text-green-600",children:r.metrics.formFields})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{children:"Espa\xF1ol:"}),jsxs("span",{className:r.metrics.spanishLabelCoverage<70?"text-yellow-600":"text-green-600",children:[Math.round(r.metrics.spanishLabelCoverage),"%"]})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{children:"Confirmaciones:"}),jsx("span",{className:r.metrics.confirmationDialogs>0?"text-red-600":"text-green-600",children:r.metrics.confirmationDialogs})]})]}),r.violations.slice(0,3).map(o=>jsxs("div",{className:"flex items-start gap-2 p-2 bg-gray-50 rounded text-xs",children:[jsx(AlertTriangle,{className:"h-3 w-3 text-red-500 mt-0.5 flex-shrink-0"}),jsxs("div",{className:"flex-1",children:[jsx("div",{className:"font-medium text-gray-900",children:o.messageEs}),o.birhausPrinciple&&jsxs("div",{className:"text-gray-500",children:["Principio BIRHAUS #",o.birhausPrinciple]})]})]},o.id)),r.suggestions.slice(0,2).map(o=>jsxs("div",{className:"flex items-start gap-2 p-2 bg-blue-50 rounded text-xs",children:[jsx(Zap,{className:"h-3 w-3 text-blue-500 mt-0.5 flex-shrink-0"}),jsxs("div",{className:"flex-1",children:[jsx("div",{className:"font-medium text-gray-900",children:o.actionEs}),jsxs("div",{className:"text-gray-500",children:["Impacto estimado: ",o.estimatedImpact,"%"]})]})]},o.id))]})})]})})}function ve(e="body"){let[i,t]=useState(null),[s,d]=useState(false);return {analysis:i,isAnalyzing:s,runAnalysis:()=>{d(true),setTimeout(()=>{t({score:85,level:"low",violations:[],suggestions:[],metrics:{navigationItems:4,formFields:3,choiceOptions:5,colorCount:8,fontVariations:3,visualLayers:4,textDensity:45,actionDensity:6,interactionComplexity:3,contrastIssues:0,missingAltText:0,keyboardNavigation:true,spanishLabelCoverage:85,bilingualSupport:true,undoPatternUsage:2,confirmationDialogs:0,progressiveDisclosure:3}}),d(false);},500);}}}var F={maxNavigationItems:7,maxFormFields:7,maxSelectOptions:7,requireSpanishLabels:true,spanishLabelSuffix:"Es",forbidConfirmationDialogs:true,requireUndoPattern:false,requireAltText:true,requireAriaLabels:true,maxActionsPerView:4,maxColorsPerComponent:5},M={meta:{type:"problem",docs:{description:"Enforce Miller's Law (7\xB12) for navigation items",category:"BIRHAUS Principles",recommended:true},fixable:void 0,schema:[{type:"object",properties:{maxItems:{type:"integer",minimum:1,maximum:10,default:7}},additionalProperties:false}],messages:{tooManyNavItems:"Navigation has {{count}} items (BIRHAUS Principle #1: max {{max}} recommended for cognitive load)",suggestGrouping:"Consider grouping navigation items or using dropdown menus to reduce cognitive load"}},create(e){let i={...F,...e.options[0]};return {JSXElement(t){if(xe(t)){let s=Le(t);s>i.maxNavigationItems&&e.report({node:t,messageId:"tooManyNavItems",data:{count:s.toString(),max:i.maxNavigationItems.toString()}});}}}}},D={meta:{type:"problem",docs:{description:"Require Spanish-first labeling for BIRHAUS components",category:"BIRHAUS Principles",recommended:true},fixable:"code",schema:[{type:"object",properties:{suffix:{type:"string",default:"Es"},components:{type:"array",items:{type:"string"},default:["BirhausButton","BirhausInput","BirhausSelect","BirhausCard"]}},additionalProperties:false}],messages:{missingSpanishLabel:"BIRHAUS component missing Spanish label (BIRHAUS Principle #7: Bilingual by design)",addSpanishLabel:"Add {{labelName}} prop for Spanish-first internationalization"}},create(e){let i={...F,...e.options[0]},t=i.components||["BirhausButton","BirhausInput","BirhausSelect","BirhausCard"];return {JSXElement(s){Se(s,t)&&we(s,i.spanishLabelSuffix).forEach(g=>{e.report({node:s,messageId:"missingSpanishLabel",data:{labelName:g},fix(x){return x.insertTextBefore(s,`${g}="[TODO: Spanish text]" `)}});});}}}},q={meta:{type:"problem",docs:{description:"Forbid confirmation dialogs in favor of undo patterns",category:"BIRHAUS Principles",recommended:true},fixable:void 0,schema:[],messages:{confirmationDialogFound:"Confirmation dialog detected (BIRHAUS Principle #5: Undo over confirm)",useUndoPattern:"Replace confirmation with undo functionality for better UX"}},create(e){let i=[/confirm/i,/¿está\s+seguro\?/i,/are\s+you\s+sure\?/i,/delete.*confirmation/i,/eliminar.*confirmación/i];return {JSXElement(t){Ie(t,i)&&e.report({node:t,messageId:"confirmationDialogFound"});},CallExpression(t){t.callee.type==="MemberExpression"&&t.callee.object.name==="window"&&t.callee.property.name==="confirm"&&e.report({node:t,messageId:"confirmationDialogFound"}),t.callee.type==="Identifier"&&t.callee.name==="confirm"&&e.report({node:t,messageId:"confirmationDialogFound"});}}}},U={meta:{type:"problem",docs:{description:"Limit form fields per form to reduce cognitive load",category:"BIRHAUS Principles",recommended:true},fixable:void 0,schema:[{type:"object",properties:{maxFields:{type:"integer",minimum:1,maximum:15,default:7}},additionalProperties:false}],messages:{tooManyFormFields:"Form has {{count}} fields (BIRHAUS Principle #4: max {{max}} recommended)",useProgressiveDisclosure:"Consider using multi-step forms or progressive disclosure for better UX"}},create(e){let i={...F,...e.options[0]};return {JSXElement(t){if(Ce(t)){let s=Ne(t);s>i.maxFormFields&&e.report({node:t,messageId:"tooManyFormFields",data:{count:s.toString(),max:i.maxFormFields.toString()}});}}}}},T={meta:{type:"problem",docs:{description:"Enforce accessibility requirements for BIRHAUS components",category:"BIRHAUS Principles",recommended:true},fixable:"code",schema:[],messages:{missingAltText:"Image missing alt text (BIRHAUS Principle #6: Accessibility = dignity)",missingAriaLabel:"Interactive element missing aria-label (BIRHAUS Principle #6)",addAccessibilityAttribute:"Add {{attribute}} for accessibility compliance"}},create(e){return {JSXElement(i){Ee(i)&&!R(i,"alt")&&e.report({node:i,messageId:"missingAltText",fix(t){return t.insertTextBefore(i,'alt="[TODO: Describe image]" ')}}),Ae(i)&&!Re(i)&&e.report({node:i,messageId:"missingAriaLabel",data:{attribute:"aria-label"}});}}}};function xe(e){let i=f(e);return ["nav","Navigation","NavBar","Menu"].includes(i)}function Se(e,i){let t=f(e);return i.includes(t)}function Ce(e){let i=f(e);return ["form","Form","BirhausForm"].includes(i)}function Ee(e){return f(e)==="img"}function Ae(e){let i=f(e);return ["button","a","input","select","textarea"].includes(i.toLowerCase())}function f(e){return e.openingElement.name.type==="JSXIdentifier"?e.openingElement.name.name:""}function Le(e){let i=0;function t(s){if(s.type==="JSXElement"){let d=f(s);["a","button","Link","NavLink"].includes(d)&&i++,s.children&&s.children.forEach(t);}}return e.children&&e.children.forEach(t),i}function Ne(e){let i=0;function t(s){if(s.type==="JSXElement"){let d=f(s);["input","select","textarea","BirhausInput","BirhausSelect"].includes(d)&&i++,s.children&&s.children.forEach(t);}}return e.children&&e.children.forEach(t),i}function we(e,i){let t=[],s=e.openingElement.attributes||[];return ["label","title","placeholder","buttonText","description"].forEach(g=>{if(s.some(p=>p.type==="JSXAttribute"&&p.name?.type==="JSXIdentifier"&&p.name?.name===g)){let p=g+i;s.some(h=>h.type==="JSXAttribute"&&h.name?.type==="JSXIdentifier"&&h.name?.name===p)||t.push(p);}}),t}function Ie(e,i){let t=Pe(e);return i.some(s=>s.test(t))}function R(e,i){return (e.openingElement.attributes||[]).some(s=>s.type==="JSXAttribute"&&s.name?.type==="JSXIdentifier"&&s.name?.name===i)}function Re(e){return R(e,"aria-label")||R(e,"aria-labelledby")||R(e,"title")}function Pe(e){let i="";function t(s){s.type==="JSXText"?i+=s.value:s.type==="JSXElement"&&s.children&&s.children.forEach(t);}return e.children&&e.children.forEach(t),i}var z={"miller-law-navigation":M,"spanish-first-labels":D,"forbid-confirmation-dialogs":q,"form-field-limits":U,"accessibility-requirements":T},k={rules:z,configs:{recommended:{plugins:["@birhaus"],rules:{"@birhaus/miller-law-navigation":"error","@birhaus/spanish-first-labels":"warn","@birhaus/forbid-confirmation-dialogs":"error","@birhaus/form-field-limits":"warn","@birhaus/accessibility-requirements":"error"}},strict:{plugins:["@birhaus"],rules:{"@birhaus/miller-law-navigation":"error","@birhaus/spanish-first-labels":"error","@birhaus/forbid-confirmation-dialogs":"error","@birhaus/form-field-limits":"error","@birhaus/accessibility-requirements":"error"}}}};export{ye as CognitiveLoadMeter,T as accessibilityRequirementsRule,k as birhausPlugin,z as birhausRules,q as forbidConfirmationDialogsRule,U as formFieldLimitsRule,M as millerLawNavigationRule,D as spanishFirstLabelsRule,ve as useCognitiveLoadAnalysis};//# sourceMappingURL=index.mjs.map //# sourceMappingURL=index.mjs.map