@ably/ui 15.5.0-dev.4b8dd74 → 15.6.0-dev.1a416b3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import React,{createContext,useContext,useState,useEffect,useRef}from"react";import Icon from"./Icon";const DropdownMenuContext=createContext({isOpen:false,setOpen:()=>{}});const DropdownMenu=({children})=>{const[isOpen,setOpen]=useState(false);const ref=useRef(null);useEffect(()=>{const clickHandler=e=>{if(ref.current?.contains(e.target))return;setOpen(false)};document.addEventListener("click",clickHandler);return()=>{document.removeEventListener("click",clickHandler)}},[]);return React.createElement(DropdownMenuContext.Provider,{value:{isOpen,setOpen}},React.createElement("div",{id:"dropdown-menu",className:"relative",ref:ref},children))};const Trigger=({children,additionalTriggerCSS=""})=>{const{isOpen,setOpen}=useContext(DropdownMenuContext);return React.createElement("button",{id:"menu-trigger",onClick:()=>setOpen(!isOpen),className:`${additionalTriggerCSS} flex items-center p-8 ml-8 group hover:text-gui-hover hover:bg-light-grey active:text-gui-active focus:text-gui-focus focus:outline-none rounded-lg`},React.createElement("span",{className:"leading-normal"},children),React.createElement(Icon,{name:"icon-gui-chevron-down-micro",color:"text-cool-black",size:"1.25rem",additionalCSS:"group-hover:text-gui-hover group-active:text-gui-active group-focus:text-gui-focus"}))};const Content=({children,anchorPosition="right",additionalContentCSS})=>{const{isOpen}=useContext(DropdownMenuContext);if(!isOpen){return null}const anchorPositionClasses=anchorPosition==="right"?"right-0":"left-0";return React.createElement("div",{id:"menu-content",className:`${additionalContentCSS} absolute p-8 z-10 border border-mid-grey bg-white rounded shadow-container ${anchorPositionClasses}`,style:{minWidth:275,top:44}},children)};const Link=({url,title,subtitle,iconName,children})=>{return React.createElement("a",{href:url,className:"menu-link group block p-8 hover:bg-light-grey hover:text-cool-black rounded"},React.createElement("p",{className:"mb-4"},title,iconName?React.createElement(Icon,{name:iconName,size:"1rem",color:"text-cool-black",additionalCSS:"align-middle ml-8 relative -top-1 -left-4"}):null),subtitle?React.createElement("p",{className:"ui-text-p3 mb-16"},subtitle):null,children)};DropdownMenu.Trigger=Trigger;DropdownMenu.Content=Content;DropdownMenu.Link=Link;export default DropdownMenu;
1
+ import React,{createContext,useContext,useState,useEffect,useRef}from"react";import Icon from"./Icon";import cn from"./utils/cn";const DropdownMenuContext=createContext({isOpen:false,setOpen:()=>{}});const DropdownMenu=({children})=>{const[isOpen,setOpen]=useState(false);const ref=useRef(null);useEffect(()=>{const clickHandler=e=>{if(ref.current?.contains(e.target))return;setOpen(false)};document.addEventListener("click",clickHandler);return()=>document.removeEventListener("click",clickHandler)},[]);return React.createElement(DropdownMenuContext.Provider,{value:{isOpen,setOpen}},React.createElement("div",{id:"dropdown-menu",className:"relative",ref:ref},children))};const Trigger=({children,triggerClassNames="",description})=>{const{isOpen,setOpen}=useContext(DropdownMenuContext);return React.createElement("button",{id:"menu-trigger","aria-label":description,role:"button",onClick:()=>setOpen(!isOpen),className:cn("flex items-center ui-text-button3 text-neutral-1000 dark:text-neutral-300 focus:outline-none",triggerClassNames)},children,React.createElement(Icon,{name:isOpen?"icon-gui-chevron-up-mini":"icon-gui-chevron-down-mini",size:"1.25rem",additionalCSS:"text-neutral-1300 dark:text-neutral-000"}))};const Content=({children,anchorPosition="right",contentClassNames})=>{const{isOpen}=useContext(DropdownMenuContext);if(!isOpen){return null}return React.createElement("div",{id:"menu-content",role:"menu",className:cn("flex flex-col absolute z-10 border border-neutral-400 dark:border-neutral-900 bg-neutral-000 dark:bg-neutral-1300 rounded-lg ui-shadow-md-soft",anchorPosition==="right"?"right-0":"left-0",contentClassNames)},children)};const Link=({url,title,subtitle,iconName,children})=>{return React.createElement("a",{href:url,className:"menu-link group block p-8 rounded-lg hover:bg-neutral-100 dark:hover:bg-neutral-1200 active:bg-neutral-200 dark:active:bg-neutral-1100 text-neutral-1000 dark:text-neutral-300 hover:text-neutral-1300 hover:dark:text-neutral-000"},React.createElement("p",{className:"mb-4"},title,iconName?React.createElement(Icon,{name:iconName,size:"1rem",additionalCSS:"align-middle ml-8 relative -top-1 -left-4 text-neutral-1300 dark:text-neutral-000"}):null),subtitle?React.createElement("p",{className:"ui-text-p3 mb-16"},subtitle):null,children)};DropdownMenu.Trigger=Trigger;DropdownMenu.Content=Content;DropdownMenu.Link=Link;export default DropdownMenu;
2
2
  //# sourceMappingURL=DropdownMenu.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/DropdownMenu.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useRef,\n ReactNode,\n Dispatch,\n} from \"react\";\nimport Icon from \"./Icon\";\nimport { IconName } from \"./Icon/types\";\n\nconst DropdownMenuContext = createContext<{\n isOpen: boolean;\n setOpen: Dispatch<React.SetStateAction<boolean>>;\n}>({\n isOpen: false,\n setOpen: () => {},\n});\n\ntype DropdownMenuProps = {\n children: ReactNode;\n};\n\ntype TriggerProps = {\n children: ReactNode;\n additionalTriggerCSS?: string;\n};\n\ntype ContentProps = {\n children: ReactNode;\n anchorPosition?: string;\n additionalContentCSS?: string;\n};\n\ntype LinkProps = {\n url: string;\n title: string;\n subtitle: string;\n iconName: IconName;\n children: ReactNode;\n};\n\nconst DropdownMenu = ({ children }: DropdownMenuProps) => {\n const [isOpen, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const clickHandler = (e: MouseEvent) => {\n if (ref.current?.contains(e.target as Node)) return;\n setOpen(false);\n };\n\n document.addEventListener(\"click\", clickHandler);\n\n return () => {\n document.removeEventListener(\"click\", clickHandler);\n };\n }, []);\n\n return (\n <DropdownMenuContext.Provider value={{ isOpen, setOpen }}>\n <div id=\"dropdown-menu\" className=\"relative\" ref={ref}>\n {children}\n </div>\n </DropdownMenuContext.Provider>\n );\n};\n\nconst Trigger = ({ children, additionalTriggerCSS = \"\" }: TriggerProps) => {\n const { isOpen, setOpen } = useContext(DropdownMenuContext);\n\n return (\n <button\n id=\"menu-trigger\"\n onClick={() => setOpen(!isOpen)}\n className={`${additionalTriggerCSS} flex items-center p-8 ml-8 group hover:text-gui-hover hover:bg-light-grey active:text-gui-active focus:text-gui-focus focus:outline-none rounded-lg`}\n >\n <span className=\"leading-normal\">{children}</span>\n <Icon\n name=\"icon-gui-chevron-down-micro\"\n color=\"text-cool-black\"\n size=\"1.25rem\"\n additionalCSS=\"group-hover:text-gui-hover group-active:text-gui-active group-focus:text-gui-focus\"\n />\n </button>\n );\n};\n\nconst Content = ({\n children,\n anchorPosition = \"right\",\n additionalContentCSS,\n}: ContentProps) => {\n const { isOpen } = useContext(DropdownMenuContext);\n\n if (!isOpen) {\n return null;\n }\n\n const anchorPositionClasses =\n anchorPosition === \"right\" ? \"right-0\" : \"left-0\";\n\n return (\n <div\n id=\"menu-content\"\n className={`${additionalContentCSS} absolute p-8 z-10 border border-mid-grey bg-white rounded shadow-container ${anchorPositionClasses}`}\n style={{ minWidth: 275, top: 44 }}\n >\n {children}\n </div>\n );\n};\n\nconst Link = ({ url, title, subtitle, iconName, children }: LinkProps) => {\n return (\n <a\n href={url}\n className=\"menu-link group block p-8 hover:bg-light-grey hover:text-cool-black rounded\"\n >\n <p className=\"mb-4\">\n {title}\n {iconName ? (\n <Icon\n name={iconName}\n size=\"1rem\"\n color=\"text-cool-black\"\n additionalCSS=\"align-middle ml-8 relative -top-1 -left-4\"\n />\n ) : null}\n </p>\n {subtitle ? <p className=\"ui-text-p3 mb-16\">{subtitle}</p> : null}\n {children}\n </a>\n );\n};\n\nDropdownMenu.Trigger = Trigger;\nDropdownMenu.Content = Content;\nDropdownMenu.Link = Link;\nexport default DropdownMenu;\n"],"names":["React","createContext","useContext","useState","useEffect","useRef","Icon","DropdownMenuContext","isOpen","setOpen","DropdownMenu","children","ref","clickHandler","e","current","contains","target","document","addEventListener","removeEventListener","Provider","value","div","id","className","Trigger","additionalTriggerCSS","button","onClick","span","name","color","size","additionalCSS","Content","anchorPosition","additionalContentCSS","anchorPositionClasses","style","minWidth","top","Link","url","title","subtitle","iconName","a","href","p"],"mappings":"AAAA,OAAOA,OACLC,aAAa,CACbC,UAAU,CACVC,QAAQ,CACRC,SAAS,CACTC,MAAM,KAGD,OAAQ,AACf,QAAOC,SAAU,QAAS,CAG1B,MAAMC,oBAAsBN,cAGzB,CACDO,OAAQ,MACRC,QAAS,KAAO,CAClB,GAyBA,MAAMC,aAAe,CAAC,CAAEC,QAAQ,CAAqB,IACnD,KAAM,CAACH,OAAQC,QAAQ,CAAGN,SAAS,OACnC,MAAMS,IAAMP,OAAuB,MAEnCD,UAAU,KACR,MAAMS,aAAe,AAACC,IACpB,GAAIF,IAAIG,OAAO,EAAEC,SAASF,EAAEG,MAAM,EAAW,OAC7CR,QAAQ,MACV,EAEAS,SAASC,gBAAgB,CAAC,QAASN,cAEnC,MAAO,KACLK,SAASE,mBAAmB,CAAC,QAASP,aACxC,CACF,EAAG,EAAE,EAEL,OACE,oBAACN,oBAAoBc,QAAQ,EAACC,MAAO,CAAEd,OAAQC,OAAQ,GACrD,oBAACc,OAAIC,GAAG,gBAAgBC,UAAU,WAAWb,IAAKA,KAC/CD,UAIT,EAEA,MAAMe,QAAU,CAAC,CAAEf,QAAQ,CAAEgB,qBAAuB,EAAE,CAAgB,IACpE,KAAM,CAAEnB,MAAM,CAAEC,OAAO,CAAE,CAAGP,WAAWK,qBAEvC,OACE,oBAACqB,UACCJ,GAAG,eACHK,QAAS,IAAMpB,QAAQ,CAACD,QACxBiB,UAAW,CAAC,EAAEE,qBAAqB,oJAAoJ,CAAC,EAExL,oBAACG,QAAKL,UAAU,kBAAkBd,UAClC,oBAACL,MACCyB,KAAK,8BACLC,MAAM,kBACNC,KAAK,UACLC,cAAc,uFAItB,EAEA,MAAMC,QAAU,CAAC,CACfxB,QAAQ,CACRyB,eAAiB,OAAO,CACxBC,oBAAoB,CACP,IACb,KAAM,CAAE7B,MAAM,CAAE,CAAGN,WAAWK,qBAE9B,GAAI,CAACC,OAAQ,CACX,OAAO,IACT,CAEA,MAAM8B,sBACJF,iBAAmB,QAAU,UAAY,SAE3C,OACE,oBAACb,OACCC,GAAG,eACHC,UAAW,CAAC,EAAEY,qBAAqB,4EAA4E,EAAEC,sBAAsB,CAAC,CACxIC,MAAO,CAAEC,SAAU,IAAKC,IAAK,EAAG,GAE/B9B,SAGP,EAEA,MAAM+B,KAAO,CAAC,CAAEC,GAAG,CAAEC,KAAK,CAAEC,QAAQ,CAAEC,QAAQ,CAAEnC,QAAQ,CAAa,IACnE,OACE,oBAACoC,KACCC,KAAML,IACNlB,UAAU,+EAEV,oBAACwB,KAAExB,UAAU,QACVmB,MACAE,SACC,oBAACxC,MACCyB,KAAMe,SACNb,KAAK,OACLD,MAAM,kBACNE,cAAc,8CAEd,MAELW,SAAW,oBAACI,KAAExB,UAAU,oBAAoBoB,UAAgB,KAC5DlC,SAGP,CAEAD,CAAAA,aAAagB,OAAO,CAAGA,OACvBhB,CAAAA,aAAayB,OAAO,CAAGA,OACvBzB,CAAAA,aAAagC,IAAI,CAAGA,IACpB,gBAAehC,YAAa"}
1
+ {"version":3,"sources":["../../src/core/DropdownMenu.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useRef,\n ReactNode,\n Dispatch,\n} from \"react\";\nimport Icon from \"./Icon\";\nimport { IconName } from \"./Icon/types\";\nimport cn from \"./utils/cn\";\n\nconst DropdownMenuContext = createContext<{\n isOpen: boolean;\n setOpen: Dispatch<React.SetStateAction<boolean>>;\n}>({\n isOpen: false,\n setOpen: () => {},\n});\n\ntype DropdownMenuProps = {\n /**\n * The content to be displayed within the dropdown menu.\n */\n children: ReactNode;\n};\n\ntype TriggerProps = {\n /**\n * The content to be displayed within the trigger element.\n */\n children: ReactNode;\n /**\n * Additional class names to apply to the trigger element.\n */\n triggerClassNames?: string;\n /**\n * A description for the trigger element, used for accessibility purposes.\n */\n description?: string;\n};\n\ntype ContentProps = {\n /**\n * The content to be displayed within the dropdown menu.\n */\n children: ReactNode;\n /**\n * The position of the dropdown menu relative to the trigger element.\n * Defaults to \"right\".\n */\n anchorPosition?: string;\n /**\n * Additional class names to apply to the content container.\n */\n contentClassNames?: string;\n};\n\ntype LinkProps = {\n url: string;\n title: string;\n subtitle?: string;\n iconName?: IconName;\n children?: ReactNode;\n};\n\nconst DropdownMenu = ({ children }: DropdownMenuProps) => {\n const [isOpen, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const clickHandler = (e: MouseEvent) => {\n if (ref.current?.contains(e.target as Node)) return;\n setOpen(false);\n };\n document.addEventListener(\"click\", clickHandler);\n return () => document.removeEventListener(\"click\", clickHandler);\n }, []);\n\n return (\n <DropdownMenuContext.Provider value={{ isOpen, setOpen }}>\n <div id=\"dropdown-menu\" className=\"relative\" ref={ref}>\n {children}\n </div>\n </DropdownMenuContext.Provider>\n );\n};\n\nconst Trigger = ({\n children,\n triggerClassNames = \"\",\n description,\n}: TriggerProps) => {\n const { isOpen, setOpen } = useContext(DropdownMenuContext);\n\n return (\n <button\n id=\"menu-trigger\"\n aria-label={description}\n role=\"button\"\n onClick={() => setOpen(!isOpen)}\n className={cn(\n \"flex items-center ui-text-button3 text-neutral-1000 dark:text-neutral-300 focus:outline-none\",\n triggerClassNames,\n )}\n >\n {children}\n <Icon\n name={\n isOpen ? \"icon-gui-chevron-up-mini\" : \"icon-gui-chevron-down-mini\"\n }\n size=\"1.25rem\"\n additionalCSS=\"text-neutral-1300 dark:text-neutral-000\"\n />\n </button>\n );\n};\n\nconst Content = ({\n children,\n anchorPosition = \"right\",\n contentClassNames,\n}: ContentProps) => {\n const { isOpen } = useContext(DropdownMenuContext);\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <div\n id=\"menu-content\"\n role=\"menu\"\n className={cn(\n \"flex flex-col absolute z-10 border border-neutral-400 dark:border-neutral-900 bg-neutral-000 dark:bg-neutral-1300 rounded-lg ui-shadow-md-soft\",\n anchorPosition === \"right\" ? \"right-0\" : \"left-0\",\n contentClassNames,\n )}\n >\n {children}\n </div>\n );\n};\n\nconst Link = ({ url, title, subtitle, iconName, children }: LinkProps) => {\n return (\n <a\n href={url}\n className=\"menu-link group block p-8 rounded-lg\n hover:bg-neutral-100 dark:hover:bg-neutral-1200 active:bg-neutral-200 dark:active:bg-neutral-1100 text-neutral-1000 dark:text-neutral-300 hover:text-neutral-1300 hover:dark:text-neutral-000\"\n >\n <p className=\"mb-4\">\n {title}\n {iconName ? (\n <Icon\n name={iconName}\n size=\"1rem\"\n additionalCSS=\"align-middle ml-8 relative -top-1 -left-4 text-neutral-1300 dark:text-neutral-000\"\n />\n ) : null}\n </p>\n {subtitle ? <p className=\"ui-text-p3 mb-16\">{subtitle}</p> : null}\n {children}\n </a>\n );\n};\n\nDropdownMenu.Trigger = Trigger;\nDropdownMenu.Content = Content;\nDropdownMenu.Link = Link;\n\nexport default DropdownMenu;\n"],"names":["React","createContext","useContext","useState","useEffect","useRef","Icon","cn","DropdownMenuContext","isOpen","setOpen","DropdownMenu","children","ref","clickHandler","e","current","contains","target","document","addEventListener","removeEventListener","Provider","value","div","id","className","Trigger","triggerClassNames","description","button","aria-label","role","onClick","name","size","additionalCSS","Content","anchorPosition","contentClassNames","Link","url","title","subtitle","iconName","a","href","p"],"mappings":"AAAA,OAAOA,OACLC,aAAa,CACbC,UAAU,CACVC,QAAQ,CACRC,SAAS,CACTC,MAAM,KAGD,OAAQ,AACf,QAAOC,SAAU,QAAS,AAE1B,QAAOC,OAAQ,YAAa,CAE5B,MAAMC,oBAAsBP,cAGzB,CACDQ,OAAQ,MACRC,QAAS,KAAO,CAClB,GAgDA,MAAMC,aAAe,CAAC,CAAEC,QAAQ,CAAqB,IACnD,KAAM,CAACH,OAAQC,QAAQ,CAAGP,SAAS,OACnC,MAAMU,IAAMR,OAAuB,MAEnCD,UAAU,KACR,MAAMU,aAAe,AAACC,IACpB,GAAIF,IAAIG,OAAO,EAAEC,SAASF,EAAEG,MAAM,EAAW,OAC7CR,QAAQ,MACV,EACAS,SAASC,gBAAgB,CAAC,QAASN,cACnC,MAAO,IAAMK,SAASE,mBAAmB,CAAC,QAASP,aACrD,EAAG,EAAE,EAEL,OACE,oBAACN,oBAAoBc,QAAQ,EAACC,MAAO,CAAEd,OAAQC,OAAQ,GACrD,oBAACc,OAAIC,GAAG,gBAAgBC,UAAU,WAAWb,IAAKA,KAC/CD,UAIT,EAEA,MAAMe,QAAU,CAAC,CACff,QAAQ,CACRgB,kBAAoB,EAAE,CACtBC,WAAW,CACE,IACb,KAAM,CAAEpB,MAAM,CAAEC,OAAO,CAAE,CAAGR,WAAWM,qBAEvC,OACE,oBAACsB,UACCL,GAAG,eACHM,aAAYF,YACZG,KAAK,SACLC,QAAS,IAAMvB,QAAQ,CAACD,QACxBiB,UAAWnB,GACT,+FACAqB,oBAGDhB,SACD,oBAACN,MACC4B,KACEzB,OAAS,2BAA6B,6BAExC0B,KAAK,UACLC,cAAc,4CAItB,EAEA,MAAMC,QAAU,CAAC,CACfzB,QAAQ,CACR0B,eAAiB,OAAO,CACxBC,iBAAiB,CACJ,IACb,KAAM,CAAE9B,MAAM,CAAE,CAAGP,WAAWM,qBAE9B,GAAI,CAACC,OAAQ,CACX,OAAO,IACT,CAEA,OACE,oBAACe,OACCC,GAAG,eACHO,KAAK,OACLN,UAAWnB,GACT,iJACA+B,iBAAmB,QAAU,UAAY,SACzCC,oBAGD3B,SAGP,EAEA,MAAM4B,KAAO,CAAC,CAAEC,GAAG,CAAEC,KAAK,CAAEC,QAAQ,CAAEC,QAAQ,CAAEhC,QAAQ,CAAa,IACnE,OACE,oBAACiC,KACCC,KAAML,IACNf,UAAU,sOAGV,oBAACqB,KAAErB,UAAU,QACVgB,MACAE,SACC,oBAACtC,MACC4B,KAAMU,SACNT,KAAK,OACLC,cAAc,sFAEd,MAELO,SAAW,oBAACI,KAAErB,UAAU,oBAAoBiB,UAAgB,KAC5D/B,SAGP,CAEAD,CAAAA,aAAagB,OAAO,CAAGA,OACvBhB,CAAAA,aAAa0B,OAAO,CAAGA,OACvB1B,CAAAA,aAAa6B,IAAI,CAAGA,IAEpB,gBAAe7B,YAAa"}
@@ -1,2 +1,2 @@
1
- import React from"react";import Icon from"../Icon";import LinkButton from"../LinkButton";import cn from"../utils/cn";const testSessionState={signedIn:false,account:{links:{dashboard:{href:"/dashboard"}}}};export const HeaderLinks=({sessionState=testSessionState,headerLinks,searchButtonVisibility,searchButton,className})=>{const signedIn=sessionState.signedIn;const headerLinkClasses="ui-text-menu2 md:ui-text-menu3 !font-bold py-16 text-neutral-1300 dark:text-neutral-000 md:text-neutral-1000 dark:md:text-neutral-300 hover:text-neutral-1300 dark:hover:text-neutral-000 active:text-neutral-1300 dark:active:text-neutral-000 transition-colors";return React.createElement("nav",{className:cn("flex md:flex-1 md:items-center md:justify-end flex-col md:flex-row border-t-[1px] border-neutral-300 md:border-t-0 p-12 md:p-0 gap-12",className)},headerLinks?.map(({href,label,external})=>React.createElement("a",{key:href,className:cn(headerLinkClasses,"flex items-center gap-4 mt-8 md:mt-0"),href:href,target:external?"_blank":undefined,rel:external?"noreferrer noopener":undefined},label,external&&React.createElement(Icon,{name:"icon-gui-arrow-top-right-on-square-outline"}))),searchButtonVisibility!=="mobile"?searchButton:null,signedIn&&sessionState.account?React.createElement(LinkButton,{href:sessionState.account.links?.dashboard.href,variant:"secondary",className:"md:ui-button-secondary-xs","aria-label":"Access your dashboard"},"Dashboard"):React.createElement("div",{className:"flex gap-12"},React.createElement(LinkButton,{href:"/login",variant:"secondary",className:"flex-1 md:flex-none md:ui-button-secondary-xs"},"Login"),React.createElement(LinkButton,{href:"/sign-up",variant:"primary",className:"flex-1 md:flex-none md:ui-button-primary-xs"},"Start free")))};
1
+ import React,{useRef}from"react";import Icon from"../Icon";import LinkButton from"../LinkButton";import cn from"../utils/cn";import DropdownMenu from"../DropdownMenu";const testSessionState={signedIn:false,logOut:{token:"0000",href:"accounts/sign_out"},accountName:"Ably"};export const HeaderLinks=({sessionState=testSessionState,headerLinks,searchButtonVisibility,searchButton,className})=>{const{signedIn,logOut}=sessionState;const formRef=useRef(null);const headerLinkClasses="ui-text-menu2 md:ui-text-menu3 !font-bold py-16 text-neutral-1300 dark:text-neutral-000 md:text-neutral-1000 dark:md:text-neutral-300 hover:text-neutral-1300 dark:hover:text-neutral-000 active:text-neutral-1300 dark:active:text-neutral-000 transition-colors";const dropdownMenuLinkClasses="block p-8 ui-text-menu3 font-semibold text-neutral-1000 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-1200 active:bg-neutral-200 dark:active:bg-neutral-1100 rounded-lg";const onClickLogout=e=>{e.preventDefault();formRef.current?.submit()};const DashboardLink=({className})=>React.createElement("a",{href:"/dashboard",className:className},"Dashboard");const LogoutForm=React.createElement("form",{ref:formRef,method:"post",action:logOut.href,className:"hidden"},React.createElement("input",{name:"_method",value:"delete",type:"hidden"}),React.createElement("input",{name:"authenticity_token",value:logOut.token,type:"hidden"}));return React.createElement("nav",{className:cn("flex md:flex-1 md:items-center md:justify-end flex-col md:flex-row border-t-[1px] border-neutral-300 md:border-t-0 md:gap-12 py-12 md:py-0",className)},signedIn&&React.createElement(React.Fragment,null,LogoutForm,React.createElement("div",{className:"block md:hidden"},React.createElement("div",{className:"flex flex-col border-b-[1px] border-neutral-300 px-16 pb-12 mb-12"},React.createElement("span",{className:"py-12 ui-text-sub-header text-[18px] text-neutral-700 dark:text-neutral-600 font-bold"},sessionState.accountName),React.createElement(DashboardLink,{className:headerLinkClasses})))),headerLinks?.map(({href,label,external})=>React.createElement("a",{key:href,className:cn(headerLinkClasses,"flex items-center gap-4 px-16 md:px-0"),href:href,target:external?"_blank":undefined,rel:external?"noreferrer noopener":undefined},label,external&&React.createElement(Icon,{name:"icon-gui-arrow-top-right-on-square-outline"}))),searchButtonVisibility!=="mobile"?searchButton:null,signedIn?React.createElement(React.Fragment,null,React.createElement("div",{className:"hidden md:block relative"},React.createElement(DropdownMenu,null,React.createElement(DropdownMenu.Trigger,{description:`Account menu for ${sessionState.accountName}`},React.createElement("span",{className:"block text-ellipsis overflow-hidden whitespace-nowrap w-full max-w-[150px] leading-normal"},sessionState.accountName)),React.createElement(DropdownMenu.Content,{anchorPosition:"right",contentClassNames:"w-[240px] mt-[12px]"},React.createElement("div",{className:"p-8"},React.createElement(DashboardLink,{className:dropdownMenuLinkClasses}),React.createElement("a",{onClick:onClickLogout,href:"#",className:dropdownMenuLinkClasses},"Logout"))))),React.createElement("div",{className:"block md:hidden p-16"},React.createElement(LinkButton,{onClick:onClickLogout,variant:"secondary",className:"w-full md:ui-button-secondary-xs","aria-label":"Logout",rightIcon:"icon-gui-arrow-right-end-on-rectangle-outline"},"Logout"))):React.createElement("div",{className:"flex gap-12 py-12 md:py-0 px-16 md:px-0"},React.createElement(LinkButton,{href:"/login",variant:"secondary",className:"flex-1 md:flex-none md:ui-button-secondary-xs"},"Login"),React.createElement(LinkButton,{href:"/sign-up",variant:"primary",className:"flex-1 md:flex-none md:ui-button-primary-xs"},"Start free")))};
2
2
  //# sourceMappingURL=HeaderLinks.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/Header/HeaderLinks.tsx"],"sourcesContent":["import React from \"react\";\nimport { HeaderProps } from \"../Header\";\nimport Icon from \"../Icon\";\nimport LinkButton from \"../LinkButton\";\nimport cn from \"../utils/cn\";\n\nconst testSessionState = {\n signedIn: false,\n account: {\n links: {\n dashboard: {\n href: \"/dashboard\",\n },\n },\n },\n};\n\nexport const HeaderLinks: React.FC<\n Pick<\n HeaderProps,\n \"sessionState\" | \"headerLinks\" | \"searchButtonVisibility\" | \"searchButton\"\n > & {\n className?: string;\n }\n> = ({\n sessionState = testSessionState,\n headerLinks,\n searchButtonVisibility,\n searchButton,\n className,\n}) => {\n const signedIn = sessionState.signedIn;\n const headerLinkClasses =\n \"ui-text-menu2 md:ui-text-menu3 !font-bold py-16 text-neutral-1300 dark:text-neutral-000 md:text-neutral-1000 dark:md:text-neutral-300 hover:text-neutral-1300 dark:hover:text-neutral-000 active:text-neutral-1300 dark:active:text-neutral-000 transition-colors\";\n\n return (\n <nav\n className={cn(\n \"flex md:flex-1 md:items-center md:justify-end flex-col md:flex-row border-t-[1px] border-neutral-300 md:border-t-0 p-12 md:p-0 gap-12\",\n className,\n )}\n >\n {headerLinks?.map(({ href, label, external }) => (\n <a\n key={href}\n className={cn(\n headerLinkClasses,\n \"flex items-center gap-4 mt-8 md:mt-0\",\n )}\n href={href}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noreferrer noopener\" : undefined}\n >\n {label}\n {external && (\n <Icon name=\"icon-gui-arrow-top-right-on-square-outline\" />\n )}\n </a>\n ))}\n {searchButtonVisibility !== \"mobile\" ? searchButton : null}\n {signedIn && sessionState.account ? (\n <LinkButton\n href={sessionState.account.links?.dashboard.href}\n variant=\"secondary\"\n className=\"md:ui-button-secondary-xs\"\n aria-label=\"Access your dashboard\"\n >\n Dashboard\n </LinkButton>\n ) : (\n <div className=\"flex gap-12\">\n <LinkButton\n href=\"/login\"\n variant=\"secondary\"\n className=\"flex-1 md:flex-none md:ui-button-secondary-xs\"\n >\n Login\n </LinkButton>\n <LinkButton\n href=\"/sign-up\"\n variant=\"primary\"\n className=\"flex-1 md:flex-none md:ui-button-primary-xs\"\n >\n Start free\n </LinkButton>\n </div>\n )}\n </nav>\n );\n};\n"],"names":["React","Icon","LinkButton","cn","testSessionState","signedIn","account","links","dashboard","href","HeaderLinks","sessionState","headerLinks","searchButtonVisibility","searchButton","className","headerLinkClasses","nav","map","label","external","a","key","target","undefined","rel","name","variant","aria-label","div"],"mappings":"AAAA,OAAOA,UAAW,OAAQ,AAE1B,QAAOC,SAAU,SAAU,AAC3B,QAAOC,eAAgB,eAAgB,AACvC,QAAOC,OAAQ,aAAc,CAE7B,MAAMC,iBAAmB,CACvBC,SAAU,MACVC,QAAS,CACPC,MAAO,CACLC,UAAW,CACTC,KAAM,YACR,CACF,CACF,CACF,CAEA,QAAO,MAAMC,YAOT,CAAC,CACHC,aAAeP,gBAAgB,CAC/BQ,WAAW,CACXC,sBAAsB,CACtBC,YAAY,CACZC,SAAS,CACV,IACC,MAAMV,SAAWM,aAAaN,QAAQ,CACtC,MAAMW,kBACJ,oQAEF,OACE,oBAACC,OACCF,UAAWZ,GACT,wIACAY,YAGDH,aAAaM,IAAI,CAAC,CAAET,IAAI,CAAEU,KAAK,CAAEC,QAAQ,CAAE,GAC1C,oBAACC,KACCC,IAAKb,KACLM,UAAWZ,GACTa,kBACA,wCAEFP,KAAMA,KACNc,OAAQH,SAAW,SAAWI,UAC9BC,IAAKL,SAAW,sBAAwBI,WAEvCL,MACAC,UACC,oBAACnB,MAAKyB,KAAK,iDAIhBb,yBAA2B,SAAWC,aAAe,KACrDT,UAAYM,aAAaL,OAAO,CAC/B,oBAACJ,YACCO,KAAME,aAAaL,OAAO,CAACC,KAAK,EAAEC,UAAUC,KAC5CkB,QAAQ,YACRZ,UAAU,4BACVa,aAAW,yBACZ,aAID,oBAACC,OAAId,UAAU,eACb,oBAACb,YACCO,KAAK,SACLkB,QAAQ,YACRZ,UAAU,iDACX,SAGD,oBAACb,YACCO,KAAK,WACLkB,QAAQ,UACRZ,UAAU,+CACX,eAOX,CAAE"}
1
+ {"version":3,"sources":["../../../src/core/Header/HeaderLinks.tsx"],"sourcesContent":["import React, { MouseEvent, useRef } from \"react\";\nimport { HeaderProps } from \"../Header\";\nimport Icon from \"../Icon\";\nimport LinkButton from \"../LinkButton\";\nimport cn from \"../utils/cn\";\nimport DropdownMenu from \"../DropdownMenu\";\n\nconst testSessionState = {\n signedIn: false,\n logOut: {\n token: \"0000\",\n href: \"accounts/sign_out\",\n },\n accountName: \"Ably\",\n};\n\nexport const HeaderLinks: React.FC<\n Pick<\n HeaderProps,\n \"sessionState\" | \"headerLinks\" | \"searchButtonVisibility\" | \"searchButton\"\n > & {\n className?: string;\n }\n> = ({\n sessionState = testSessionState,\n headerLinks,\n searchButtonVisibility,\n searchButton,\n className,\n}) => {\n const { signedIn, logOut } = sessionState;\n const formRef = useRef<HTMLFormElement>(null);\n\n const headerLinkClasses =\n \"ui-text-menu2 md:ui-text-menu3 !font-bold py-16 text-neutral-1300 dark:text-neutral-000 md:text-neutral-1000 dark:md:text-neutral-300 hover:text-neutral-1300 dark:hover:text-neutral-000 active:text-neutral-1300 dark:active:text-neutral-000 transition-colors\";\n\n const dropdownMenuLinkClasses =\n \"block p-8 ui-text-menu3 font-semibold text-neutral-1000 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-1200 active:bg-neutral-200 dark:active:bg-neutral-1100 rounded-lg\";\n\n const onClickLogout = (e: MouseEvent) => {\n e.preventDefault();\n formRef.current?.submit();\n };\n\n const DashboardLink = ({ className }: { className: string }) => (\n <a href=\"/dashboard\" className={className}>\n Dashboard\n </a>\n );\n\n const LogoutForm = (\n <form ref={formRef} method=\"post\" action={logOut.href} className=\"hidden\">\n <input name=\"_method\" value=\"delete\" type=\"hidden\" />\n <input name=\"authenticity_token\" value={logOut.token} type=\"hidden\" />\n </form>\n );\n\n return (\n <nav\n className={cn(\n \"flex md:flex-1 md:items-center md:justify-end flex-col md:flex-row border-t-[1px] border-neutral-300 md:border-t-0 md:gap-12 py-12 md:py-0\",\n className,\n )}\n >\n {signedIn && (\n <>\n {LogoutForm}\n <div className=\"block md:hidden\">\n <div className=\"flex flex-col border-b-[1px] border-neutral-300 px-16 pb-12 mb-12\">\n <span className=\"py-12 ui-text-sub-header text-[18px] text-neutral-700 dark:text-neutral-600 font-bold\">\n {sessionState.accountName}\n </span>\n {<DashboardLink className={headerLinkClasses} />}\n </div>\n </div>\n </>\n )}\n\n {headerLinks?.map(({ href, label, external }) => (\n <a\n key={href}\n className={cn(\n headerLinkClasses,\n \"flex items-center gap-4 px-16 md:px-0\",\n )}\n href={href}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noreferrer noopener\" : undefined}\n >\n {label}\n {external && (\n <Icon name=\"icon-gui-arrow-top-right-on-square-outline\" />\n )}\n </a>\n ))}\n\n {searchButtonVisibility !== \"mobile\" ? searchButton : null}\n {signedIn ? (\n <>\n <div className=\"hidden md:block relative\">\n <DropdownMenu>\n <DropdownMenu.Trigger\n description={`Account menu for ${sessionState.accountName}`}\n >\n <span className=\"block text-ellipsis overflow-hidden whitespace-nowrap w-full max-w-[150px] leading-normal\">\n {sessionState.accountName}\n </span>\n </DropdownMenu.Trigger>\n <DropdownMenu.Content\n anchorPosition=\"right\"\n contentClassNames=\"w-[240px] mt-[12px]\"\n >\n <div className=\"p-8\">\n {<DashboardLink className={dropdownMenuLinkClasses} />}\n <a\n onClick={onClickLogout}\n href=\"#\"\n className={dropdownMenuLinkClasses}\n >\n Logout\n </a>\n </div>\n </DropdownMenu.Content>\n </DropdownMenu>\n </div>\n <div className=\"block md:hidden p-16\">\n <LinkButton\n onClick={onClickLogout}\n variant=\"secondary\"\n className=\"w-full md:ui-button-secondary-xs\"\n aria-label=\"Logout\"\n rightIcon=\"icon-gui-arrow-right-end-on-rectangle-outline\"\n >\n Logout\n </LinkButton>\n </div>\n </>\n ) : (\n <div className=\"flex gap-12 py-12 md:py-0 px-16 md:px-0\">\n <LinkButton\n href=\"/login\"\n variant=\"secondary\"\n className=\"flex-1 md:flex-none md:ui-button-secondary-xs\"\n >\n Login\n </LinkButton>\n <LinkButton\n href=\"/sign-up\"\n variant=\"primary\"\n className=\"flex-1 md:flex-none md:ui-button-primary-xs\"\n >\n Start free\n </LinkButton>\n </div>\n )}\n </nav>\n );\n};\n"],"names":["React","useRef","Icon","LinkButton","cn","DropdownMenu","testSessionState","signedIn","logOut","token","href","accountName","HeaderLinks","sessionState","headerLinks","searchButtonVisibility","searchButton","className","formRef","headerLinkClasses","dropdownMenuLinkClasses","onClickLogout","e","preventDefault","current","submit","DashboardLink","a","LogoutForm","form","ref","method","action","input","name","value","type","nav","div","span","map","label","external","key","target","undefined","rel","Trigger","description","Content","anchorPosition","contentClassNames","onClick","variant","aria-label","rightIcon"],"mappings":"AAAA,OAAOA,OAAqBC,MAAM,KAAQ,OAAQ,AAElD,QAAOC,SAAU,SAAU,AAC3B,QAAOC,eAAgB,eAAgB,AACvC,QAAOC,OAAQ,aAAc,AAC7B,QAAOC,iBAAkB,iBAAkB,CAE3C,MAAMC,iBAAmB,CACvBC,SAAU,MACVC,OAAQ,CACNC,MAAO,OACPC,KAAM,mBACR,EACAC,YAAa,MACf,CAEA,QAAO,MAAMC,YAOT,CAAC,CACHC,aAAeP,gBAAgB,CAC/BQ,WAAW,CACXC,sBAAsB,CACtBC,YAAY,CACZC,SAAS,CACV,IACC,KAAM,CAAEV,QAAQ,CAAEC,MAAM,CAAE,CAAGK,aAC7B,MAAMK,QAAUjB,OAAwB,MAExC,MAAMkB,kBACJ,oQAEF,MAAMC,wBACJ,6LAEF,MAAMC,cAAgB,AAACC,IACrBA,EAAEC,cAAc,EAChBL,CAAAA,QAAQM,OAAO,EAAEC,QACnB,EAEA,MAAMC,cAAgB,CAAC,CAAET,SAAS,CAAyB,GACzD,oBAACU,KAAEjB,KAAK,aAAaO,UAAWA,WAAW,aAK7C,MAAMW,WACJ,oBAACC,QAAKC,IAAKZ,QAASa,OAAO,OAAOC,OAAQxB,OAAOE,IAAI,CAAEO,UAAU,UAC/D,oBAACgB,SAAMC,KAAK,UAAUC,MAAM,SAASC,KAAK,WAC1C,oBAACH,SAAMC,KAAK,qBAAqBC,MAAO3B,OAAOC,KAAK,CAAE2B,KAAK,YAI/D,OACE,oBAACC,OACCpB,UAAWb,GACT,6IACAa,YAGDV,UACC,wCACGqB,WACD,oBAACU,OAAIrB,UAAU,mBACb,oBAACqB,OAAIrB,UAAU,qEACb,oBAACsB,QAAKtB,UAAU,yFACbJ,aAAaF,WAAW,EAE1B,oBAACe,eAAcT,UAAWE,uBAMlCL,aAAa0B,IAAI,CAAC,CAAE9B,IAAI,CAAE+B,KAAK,CAAEC,QAAQ,CAAE,GAC1C,oBAACf,KACCgB,IAAKjC,KACLO,UAAWb,GACTe,kBACA,yCAEFT,KAAMA,KACNkC,OAAQF,SAAW,SAAWG,UAC9BC,IAAKJ,SAAW,sBAAwBG,WAEvCJ,MACAC,UACC,oBAACxC,MAAKgC,KAAK,iDAKhBnB,yBAA2B,SAAWC,aAAe,KACrDT,SACC,wCACE,oBAAC+B,OAAIrB,UAAU,4BACb,oBAACZ,kBACC,oBAACA,aAAa0C,OAAO,EACnBC,YAAa,CAAC,iBAAiB,EAAEnC,aAAaF,WAAW,CAAC,CAAC,EAE3D,oBAAC4B,QAAKtB,UAAU,6FACbJ,aAAaF,WAAW,GAG7B,oBAACN,aAAa4C,OAAO,EACnBC,eAAe,QACfC,kBAAkB,uBAElB,oBAACb,OAAIrB,UAAU,OACZ,oBAACS,eAAcT,UAAWG,0BAC3B,oBAACO,KACCyB,QAAS/B,cACTX,KAAK,IACLO,UAAWG,yBACZ,cAOT,oBAACkB,OAAIrB,UAAU,wBACb,oBAACd,YACCiD,QAAS/B,cACTgC,QAAQ,YACRpC,UAAU,mCACVqC,aAAW,SACXC,UAAU,iDACX,YAML,oBAACjB,OAAIrB,UAAU,2CACb,oBAACd,YACCO,KAAK,SACL2C,QAAQ,YACRpC,UAAU,iDACX,SAGD,oBAACd,YACCO,KAAK,WACL2C,QAAQ,UACRpC,UAAU,+CACX,eAOX,CAAE"}
package/core/Header.js CHANGED
@@ -1,2 +1,2 @@
1
- import React,{useState,useEffect,useRef,useMemo}from"react";import Icon from"./Icon";import cn from"./utils/cn";import Logo from"./Logo";import{componentMaxHeight,HEADER_BOTTOM_MARGIN,HEADER_HEIGHT}from"./utils/heights";import{HeaderLinks}from"./Header/HeaderLinks";import throttle from"lodash.throttle";const FLEXIBLE_DESKTOP_CLASSES="hidden md:flex flex-1 items-center h-full";const Header=({searchBar,searchButton,logoHref,headerLinks,nav,mobileNav,sessionState,themedScrollpoints=[],searchButtonVisibility="all"})=>{const[showMenu,setShowMenu]=useState(false);const[fadingOut,setFadingOut]=useState(false);const[scrollpointClasses,setScrollpointClasses]=useState("");const menuRef=useRef(null);const closeMenu=()=>{setFadingOut(true);setTimeout(()=>{setShowMenu(false);setFadingOut(false)},150)};useEffect(()=>{const handleResize=()=>{if(window.innerWidth>=1040){setShowMenu(false)}};window.addEventListener("resize",handleResize);return()=>window.removeEventListener("resize",handleResize)},[]);useEffect(()=>{if(showMenu){document.body.classList.add("overflow-hidden")}else{document.body.classList.remove("overflow-hidden")}return()=>{document.body.classList.remove("overflow-hidden")}},[showMenu]);useEffect(()=>{const handleScroll=()=>{for(const scrollpoint of themedScrollpoints){const element=document.getElementById(scrollpoint.id);if(element){const rect=element.getBoundingClientRect();if(rect.top<=HEADER_HEIGHT&&rect.bottom>=HEADER_HEIGHT){setScrollpointClasses(scrollpoint.className);return}}}};const throttledHandleScroll=throttle(handleScroll,150);handleScroll();window.addEventListener("scroll",throttledHandleScroll);return()=>window.removeEventListener("scroll",throttledHandleScroll)},[themedScrollpoints]);const wrappedSearchButton=useMemo(()=>searchButton?React.createElement("div",{className:"text-neutral-1300 dark:text-neutral-000 flex items-center p-6"},searchButton):null,[searchButton]);return React.createElement(React.Fragment,null,React.createElement("header",{role:"banner",className:cn("fixed top-0 left-0 w-full z-10 bg-neutral-000 dark:bg-neutral-1300 border-b border-neutral-300 transition-colors px-24 md:px-64",scrollpointClasses),style:{height:HEADER_HEIGHT}},React.createElement("div",{className:"flex items-center h-full"},React.createElement("nav",{className:"flex flex-1 h-full items-center"},["light","dark"].map(theme=>React.createElement(Logo,{key:theme,href:logoHref,theme:theme,additionalLinkAttrs:{className:cn("h-full focus-base rounded mr-32 w-[108px]",{"flex dark:hidden":theme==="light","hidden dark:flex":theme==="dark"})}})),React.createElement("div",{className:FLEXIBLE_DESKTOP_CLASSES},nav)),React.createElement("div",{className:"flex md:hidden flex-1 items-center justify-end gap-24 h-full"},searchButtonVisibility!=="desktop"?wrappedSearchButton:null,React.createElement("button",{className:"cursor-pointer focus-base rounded flex items-center",onClick:()=>setShowMenu(!showMenu),"aria-expanded":showMenu,"aria-controls":"mobile-menu","aria-label":"Toggle menu"},React.createElement(Icon,{name:showMenu?"icon-gui-x-mark-outline":"icon-gui-bars-3-outline",additionalCSS:"text-neutral-1300 dark:text-neutral-000",size:"1.5rem"}))),searchBar?React.createElement("div",{className:cn(FLEXIBLE_DESKTOP_CLASSES,"justify-center")},searchBar):null,React.createElement(HeaderLinks,{className:FLEXIBLE_DESKTOP_CLASSES,headerLinks:headerLinks,sessionState:sessionState,searchButton:wrappedSearchButton,searchButtonVisibility:searchButtonVisibility}))),showMenu?React.createElement(React.Fragment,null,React.createElement("div",{className:cn("fixed inset-0 bg-neutral-1300 dark:bg-neutral-1300",{"animate-[fade-in-ten-percent_150ms_ease-in-out_forwards]":!fadingOut,"animate-[fade-out-ten-percent_150ms_ease-in-out_forwards]":fadingOut}),onClick:closeMenu,onKeyDown:e=>e.key==="Escape"&&closeMenu(),role:"presentation"}),React.createElement("div",{id:"mobile-menu",className:"md:hidden fixed flex flex-col top-[76px] overflow-y-hidden left-0 right-0 mx-12 bg-neutral-000 dark:bg-neutral-1300 rounded-2xl ui-shadow-lg-medium z-20",style:{maxHeight:componentMaxHeight(HEADER_HEIGHT,HEADER_BOTTOM_MARGIN)},ref:menuRef,role:"navigation"},mobileNav,React.createElement(HeaderLinks,{headerLinks:headerLinks,sessionState:sessionState}))):null)};export default Header;
1
+ import React,{useState,useEffect,useRef,useMemo}from"react";import Icon from"./Icon";import cn from"./utils/cn";import Logo from"./Logo";import{componentMaxHeight,HEADER_BOTTOM_MARGIN,HEADER_HEIGHT}from"./utils/heights";import{HeaderLinks}from"./Header/HeaderLinks";import throttle from"lodash.throttle";const FLEXIBLE_DESKTOP_CLASSES="hidden md:flex flex-1 items-center h-full";const MAX_MOBILE_MENU_WIDTH="560px";const Header=({searchBar,searchButton,logoHref,headerLinks,nav,mobileNav,sessionState,themedScrollpoints=[],searchButtonVisibility="all"})=>{const[showMenu,setShowMenu]=useState(false);const[fadingOut,setFadingOut]=useState(false);const[scrollpointClasses,setScrollpointClasses]=useState("");const menuRef=useRef(null);const closeMenu=()=>{setFadingOut(true);setTimeout(()=>{setShowMenu(false);setFadingOut(false)},150)};useEffect(()=>{const handleResize=()=>{if(window.innerWidth>=1040){setShowMenu(false)}};window.addEventListener("resize",handleResize);return()=>window.removeEventListener("resize",handleResize)},[]);useEffect(()=>{if(showMenu){document.body.classList.add("overflow-hidden")}else{document.body.classList.remove("overflow-hidden")}return()=>{document.body.classList.remove("overflow-hidden")}},[showMenu]);useEffect(()=>{const handleScroll=()=>{for(const scrollpoint of themedScrollpoints){const element=document.getElementById(scrollpoint.id);if(element){const rect=element.getBoundingClientRect();if(rect.top<=HEADER_HEIGHT&&rect.bottom>=HEADER_HEIGHT){setScrollpointClasses(scrollpoint.className);return}}}};const throttledHandleScroll=throttle(handleScroll,150);handleScroll();window.addEventListener("scroll",throttledHandleScroll);return()=>window.removeEventListener("scroll",throttledHandleScroll)},[themedScrollpoints]);const wrappedSearchButton=useMemo(()=>searchButton?React.createElement("div",{className:"text-neutral-1300 dark:text-neutral-000 flex items-center p-6"},searchButton):null,[searchButton]);return React.createElement(React.Fragment,null,React.createElement("header",{role:"banner",className:cn("fixed top-0 left-0 w-full z-10 bg-neutral-000 dark:bg-neutral-1300 border-b border-neutral-300 dark:border-neutral-1000 transition-colors px-24 md:px-64",scrollpointClasses),style:{height:HEADER_HEIGHT}},React.createElement("div",{className:"flex items-center h-full"},React.createElement("nav",{className:"flex flex-1 h-full items-center"},["light","dark"].map(theme=>React.createElement(Logo,{key:theme,href:logoHref,theme:theme,additionalLinkAttrs:{className:cn("h-full focus-base rounded mr-32 w-[108px]",{"flex dark:hidden":theme==="light","hidden dark:flex":theme==="dark"})}})),React.createElement("div",{className:FLEXIBLE_DESKTOP_CLASSES},nav)),React.createElement("div",{className:"flex md:hidden flex-1 items-center justify-end gap-24 h-full"},searchButtonVisibility!=="desktop"?wrappedSearchButton:null,React.createElement("button",{className:"cursor-pointer focus-base rounded flex items-center",onClick:()=>setShowMenu(!showMenu),"aria-expanded":showMenu,"aria-controls":"mobile-menu","aria-label":"Toggle menu"},React.createElement(Icon,{name:showMenu?"icon-gui-x-mark-outline":"icon-gui-bars-3-outline",additionalCSS:"text-neutral-1300 dark:text-neutral-000",size:"1.5rem"}))),searchBar?React.createElement("div",{className:cn(FLEXIBLE_DESKTOP_CLASSES,"justify-center")},searchBar):null,React.createElement(HeaderLinks,{className:FLEXIBLE_DESKTOP_CLASSES,headerLinks:headerLinks,sessionState:sessionState,searchButton:wrappedSearchButton,searchButtonVisibility:searchButtonVisibility}))),showMenu?React.createElement(React.Fragment,null,React.createElement("div",{className:cn("fixed inset-0 bg-neutral-1300 dark:bg-neutral-1300",{"animate-[fade-in-ten-percent_150ms_ease-in-out_forwards]":!fadingOut,"animate-[fade-out-ten-percent_150ms_ease-in-out_forwards]":fadingOut}),onClick:closeMenu,onKeyDown:e=>e.key==="Escape"&&closeMenu(),role:"presentation"}),React.createElement("div",{id:"mobile-menu",className:"md:hidden fixed flex flex-col top-[76px] overflow-y-hidden mx-12 right-0 w-[calc(100%-24px)] bg-neutral-000 dark:bg-neutral-1300 rounded-2xl ui-shadow-lg-medium z-20",style:{maxWidth:MAX_MOBILE_MENU_WIDTH,maxHeight:componentMaxHeight(HEADER_HEIGHT,HEADER_BOTTOM_MARGIN)},ref:menuRef,role:"navigation"},mobileNav,React.createElement(HeaderLinks,{headerLinks:headerLinks,sessionState:sessionState}))):null)};export default Header;
2
2
  //# sourceMappingURL=Header.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/Header.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, ReactNode, useMemo } from \"react\";\nimport Icon from \"./Icon\";\nimport cn from \"./utils/cn\";\nimport Logo from \"./Logo\";\nimport {\n componentMaxHeight,\n HEADER_BOTTOM_MARGIN,\n HEADER_HEIGHT,\n} from \"./utils/heights\";\nimport { HeaderLinks } from \"./Header/HeaderLinks\";\nimport throttle from \"lodash.throttle\";\nimport { Theme } from \"./styles/colors/types\";\n\nexport type ThemedScrollpoint = {\n id: string;\n className: string;\n};\n\n/**\n * Props for the Header component.\n */\nexport type HeaderProps = {\n /**\n * Optional search bar element.\n */\n searchBar?: ReactNode;\n\n /**\n * Optional search button element.\n */\n searchButton?: ReactNode;\n\n /**\n * URL for the logo link.\n */\n logoHref?: string;\n\n /**\n * Array of header links.\n */\n headerLinks?: {\n /**\n * URL for the link.\n */\n href: string;\n\n /**\n * Label for the link.\n */\n label: string;\n\n /**\n * Indicates if the link should open in a new tab.\n */\n external?: boolean;\n }[];\n\n /**\n * Optional desktop navigation element.\n */\n nav?: ReactNode;\n\n /**\n * Optional mobile navigation element.\n */\n mobileNav?: ReactNode;\n\n /**\n * State of the user session.\n */\n sessionState?: {\n /**\n * Indicates if the user is signed in.\n */\n signedIn: boolean;\n\n /**\n * Account information.\n */\n account: {\n /**\n * Links related to the account.\n */\n links: {\n /**\n * Dashboard link information.\n */\n dashboard: {\n /**\n * URL for the dashboard link.\n */\n href: string;\n };\n };\n };\n };\n\n /**\n * Array of themed scrollpoints. The header will change its appearance based on the scrollpoint in view.\n */\n themedScrollpoints?: ThemedScrollpoint[];\n\n /**\n * Visibility setting for the search button.\n * - \"all\": Visible on all devices.\n * - \"desktop\": Visible only on desktop devices.\n * - \"mobile\": Visible only on mobile devices.\n */\n searchButtonVisibility?: \"all\" | \"desktop\" | \"mobile\";\n};\n\nconst FLEXIBLE_DESKTOP_CLASSES = \"hidden md:flex flex-1 items-center h-full\";\n\nconst Header: React.FC<HeaderProps> = ({\n searchBar,\n searchButton,\n logoHref,\n headerLinks,\n nav,\n mobileNav,\n sessionState,\n themedScrollpoints = [],\n searchButtonVisibility = \"all\",\n}) => {\n const [showMenu, setShowMenu] = useState(false);\n const [fadingOut, setFadingOut] = useState(false);\n const [scrollpointClasses, setScrollpointClasses] = useState<string>(\"\");\n const menuRef = useRef<HTMLDivElement>(null);\n\n const closeMenu = () => {\n setFadingOut(true);\n\n setTimeout(() => {\n setShowMenu(false);\n setFadingOut(false);\n }, 150);\n };\n\n useEffect(() => {\n const handleResize = () => {\n if (window.innerWidth >= 1040) {\n setShowMenu(false);\n }\n };\n\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n useEffect(() => {\n if (showMenu) {\n document.body.classList.add(\"overflow-hidden\");\n } else {\n document.body.classList.remove(\"overflow-hidden\");\n }\n\n // Cleanup on unmount\n return () => {\n document.body.classList.remove(\"overflow-hidden\");\n };\n }, [showMenu]);\n\n useEffect(() => {\n const handleScroll = () => {\n for (const scrollpoint of themedScrollpoints) {\n const element = document.getElementById(scrollpoint.id);\n if (element) {\n const rect = element.getBoundingClientRect();\n if (rect.top <= HEADER_HEIGHT && rect.bottom >= HEADER_HEIGHT) {\n setScrollpointClasses(scrollpoint.className);\n return;\n }\n }\n }\n };\n\n const throttledHandleScroll = throttle(handleScroll, 150);\n\n handleScroll();\n\n window.addEventListener(\"scroll\", throttledHandleScroll);\n return () => window.removeEventListener(\"scroll\", throttledHandleScroll);\n }, [themedScrollpoints]);\n\n const wrappedSearchButton = useMemo(\n () =>\n searchButton ? (\n <div className=\"text-neutral-1300 dark:text-neutral-000 flex items-center p-6\">\n {searchButton}\n </div>\n ) : null,\n [searchButton],\n );\n\n return (\n <>\n <header\n role=\"banner\"\n className={cn(\n \"fixed top-0 left-0 w-full z-10 bg-neutral-000 dark:bg-neutral-1300 border-b border-neutral-300 transition-colors px-24 md:px-64\",\n scrollpointClasses,\n )}\n style={{ height: HEADER_HEIGHT }}\n >\n <div className=\"flex items-center h-full\">\n <nav className=\"flex flex-1 h-full items-center\">\n {([\"light\", \"dark\"] as Theme[]).map((theme) => (\n <Logo\n key={theme}\n href={logoHref}\n theme={theme}\n additionalLinkAttrs={{\n className: cn(\"h-full focus-base rounded mr-32 w-[108px]\", {\n \"flex dark:hidden\": theme === \"light\",\n \"hidden dark:flex\": theme === \"dark\",\n }),\n }}\n />\n ))}\n <div className={FLEXIBLE_DESKTOP_CLASSES}>{nav}</div>\n </nav>\n <div className=\"flex md:hidden flex-1 items-center justify-end gap-24 h-full\">\n {searchButtonVisibility !== \"desktop\" ? wrappedSearchButton : null}\n <button\n className=\"cursor-pointer focus-base rounded flex items-center\"\n onClick={() => setShowMenu(!showMenu)}\n aria-expanded={showMenu}\n aria-controls=\"mobile-menu\"\n aria-label=\"Toggle menu\"\n >\n <Icon\n name={\n showMenu\n ? \"icon-gui-x-mark-outline\"\n : \"icon-gui-bars-3-outline\"\n }\n additionalCSS=\"text-neutral-1300 dark:text-neutral-000\"\n size=\"1.5rem\"\n />\n </button>\n </div>\n {searchBar ? (\n <div className={cn(FLEXIBLE_DESKTOP_CLASSES, \"justify-center\")}>\n {searchBar}\n </div>\n ) : null}\n <HeaderLinks\n className={FLEXIBLE_DESKTOP_CLASSES}\n headerLinks={headerLinks}\n sessionState={sessionState}\n searchButton={wrappedSearchButton}\n searchButtonVisibility={searchButtonVisibility}\n />\n </div>\n </header>\n {showMenu ? (\n <>\n <div\n className={cn(\n \"fixed inset-0 bg-neutral-1300 dark:bg-neutral-1300\",\n {\n \"animate-[fade-in-ten-percent_150ms_ease-in-out_forwards]\":\n !fadingOut,\n \"animate-[fade-out-ten-percent_150ms_ease-in-out_forwards]\":\n fadingOut,\n },\n )}\n onClick={closeMenu}\n onKeyDown={(e) => e.key === \"Escape\" && closeMenu()}\n role=\"presentation\"\n />\n <div\n id=\"mobile-menu\"\n className=\"md:hidden fixed flex flex-col top-[76px] overflow-y-hidden left-0 right-0 mx-12 bg-neutral-000 dark:bg-neutral-1300 rounded-2xl ui-shadow-lg-medium z-20\"\n style={{\n maxHeight: componentMaxHeight(\n HEADER_HEIGHT,\n HEADER_BOTTOM_MARGIN,\n ),\n }}\n ref={menuRef}\n role=\"navigation\"\n >\n {mobileNav}\n <HeaderLinks\n headerLinks={headerLinks}\n sessionState={sessionState}\n />\n </div>\n </>\n ) : null}\n </>\n );\n};\n\nexport default Header;\n"],"names":["React","useState","useEffect","useRef","useMemo","Icon","cn","Logo","componentMaxHeight","HEADER_BOTTOM_MARGIN","HEADER_HEIGHT","HeaderLinks","throttle","FLEXIBLE_DESKTOP_CLASSES","Header","searchBar","searchButton","logoHref","headerLinks","nav","mobileNav","sessionState","themedScrollpoints","searchButtonVisibility","showMenu","setShowMenu","fadingOut","setFadingOut","scrollpointClasses","setScrollpointClasses","menuRef","closeMenu","setTimeout","handleResize","window","innerWidth","addEventListener","removeEventListener","document","body","classList","add","remove","handleScroll","scrollpoint","element","getElementById","id","rect","getBoundingClientRect","top","bottom","className","throttledHandleScroll","wrappedSearchButton","div","header","role","style","height","map","theme","key","href","additionalLinkAttrs","button","onClick","aria-expanded","aria-controls","aria-label","name","additionalCSS","size","onKeyDown","e","maxHeight","ref"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/Header.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, ReactNode, useMemo } from \"react\";\nimport Icon from \"./Icon\";\nimport cn from \"./utils/cn\";\nimport Logo from \"./Logo\";\nimport {\n componentMaxHeight,\n HEADER_BOTTOM_MARGIN,\n HEADER_HEIGHT,\n} from \"./utils/heights\";\nimport { HeaderLinks } from \"./Header/HeaderLinks\";\nimport throttle from \"lodash.throttle\";\nimport { Theme } from \"./styles/colors/types\";\n\nexport type ThemedScrollpoint = {\n id: string;\n className: string;\n};\n\n/**\n * Represents the state of the user session in the header.\n */\nexport type HeaderSessionState = {\n /**\n * Indicates if the user is signed in.\n */\n signedIn: boolean;\n\n /**\n * Information required to log out the user.\n */\n logOut: {\n /**\n * Token used for logging out.\n */\n token: string;\n\n /**\n * URL to log out the user.\n */\n href: string;\n };\n\n /**\n * Name of the user's account.\n */\n accountName: string;\n};\n\n/**\n * Props for the Header component.\n */\nexport type HeaderProps = {\n /**\n * Optional search bar element.\n */\n searchBar?: ReactNode;\n\n /**\n * Optional search button element.\n */\n searchButton?: ReactNode;\n\n /**\n * URL for the logo link.\n */\n logoHref?: string;\n\n /**\n * Array of header links.\n */\n headerLinks?: {\n /**\n * URL for the link.\n */\n href: string;\n\n /**\n * Label for the link.\n */\n label: string;\n\n /**\n * Indicates if the link should open in a new tab.\n */\n external?: boolean;\n }[];\n\n /**\n * Optional desktop navigation element.\n */\n nav?: ReactNode;\n\n /**\n * Optional mobile navigation element.\n */\n mobileNav?: ReactNode;\n\n /**\n * State of the user session.\n */\n sessionState?: HeaderSessionState;\n\n /**\n * Array of themed scrollpoints. The header will change its appearance based on the scrollpoint in view.\n */\n themedScrollpoints?: ThemedScrollpoint[];\n\n /**\n * Visibility setting for the search button.\n * - \"all\": Visible on all devices.\n * - \"desktop\": Visible only on desktop devices.\n * - \"mobile\": Visible only on mobile devices.\n */\n searchButtonVisibility?: \"all\" | \"desktop\" | \"mobile\";\n};\n\nconst FLEXIBLE_DESKTOP_CLASSES = \"hidden md:flex flex-1 items-center h-full\";\n\n/**\n * Maximum width before the menu expanded into full width\n */\nconst MAX_MOBILE_MENU_WIDTH = \"560px\";\n\nconst Header: React.FC<HeaderProps> = ({\n searchBar,\n searchButton,\n logoHref,\n headerLinks,\n nav,\n mobileNav,\n sessionState,\n themedScrollpoints = [],\n searchButtonVisibility = \"all\",\n}) => {\n const [showMenu, setShowMenu] = useState(false);\n const [fadingOut, setFadingOut] = useState(false);\n const [scrollpointClasses, setScrollpointClasses] = useState<string>(\"\");\n const menuRef = useRef<HTMLDivElement>(null);\n\n const closeMenu = () => {\n setFadingOut(true);\n\n setTimeout(() => {\n setShowMenu(false);\n setFadingOut(false);\n }, 150);\n };\n\n useEffect(() => {\n const handleResize = () => {\n if (window.innerWidth >= 1040) {\n setShowMenu(false);\n }\n };\n\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n useEffect(() => {\n if (showMenu) {\n document.body.classList.add(\"overflow-hidden\");\n } else {\n document.body.classList.remove(\"overflow-hidden\");\n }\n\n // Cleanup on unmount\n return () => {\n document.body.classList.remove(\"overflow-hidden\");\n };\n }, [showMenu]);\n\n useEffect(() => {\n const handleScroll = () => {\n for (const scrollpoint of themedScrollpoints) {\n const element = document.getElementById(scrollpoint.id);\n if (element) {\n const rect = element.getBoundingClientRect();\n if (rect.top <= HEADER_HEIGHT && rect.bottom >= HEADER_HEIGHT) {\n setScrollpointClasses(scrollpoint.className);\n return;\n }\n }\n }\n };\n\n const throttledHandleScroll = throttle(handleScroll, 150);\n\n handleScroll();\n\n window.addEventListener(\"scroll\", throttledHandleScroll);\n return () => window.removeEventListener(\"scroll\", throttledHandleScroll);\n }, [themedScrollpoints]);\n\n const wrappedSearchButton = useMemo(\n () =>\n searchButton ? (\n <div className=\"text-neutral-1300 dark:text-neutral-000 flex items-center p-6\">\n {searchButton}\n </div>\n ) : null,\n [searchButton],\n );\n\n return (\n <>\n <header\n role=\"banner\"\n className={cn(\n \"fixed top-0 left-0 w-full z-10 bg-neutral-000 dark:bg-neutral-1300 border-b border-neutral-300 dark:border-neutral-1000 transition-colors px-24 md:px-64\",\n scrollpointClasses,\n )}\n style={{ height: HEADER_HEIGHT }}\n >\n <div className=\"flex items-center h-full\">\n <nav className=\"flex flex-1 h-full items-center\">\n {([\"light\", \"dark\"] as Theme[]).map((theme) => (\n <Logo\n key={theme}\n href={logoHref}\n theme={theme}\n additionalLinkAttrs={{\n className: cn(\"h-full focus-base rounded mr-32 w-[108px]\", {\n \"flex dark:hidden\": theme === \"light\",\n \"hidden dark:flex\": theme === \"dark\",\n }),\n }}\n />\n ))}\n <div className={FLEXIBLE_DESKTOP_CLASSES}>{nav}</div>\n </nav>\n <div className=\"flex md:hidden flex-1 items-center justify-end gap-24 h-full\">\n {searchButtonVisibility !== \"desktop\" ? wrappedSearchButton : null}\n <button\n className=\"cursor-pointer focus-base rounded flex items-center\"\n onClick={() => setShowMenu(!showMenu)}\n aria-expanded={showMenu}\n aria-controls=\"mobile-menu\"\n aria-label=\"Toggle menu\"\n >\n <Icon\n name={\n showMenu\n ? \"icon-gui-x-mark-outline\"\n : \"icon-gui-bars-3-outline\"\n }\n additionalCSS=\"text-neutral-1300 dark:text-neutral-000\"\n size=\"1.5rem\"\n />\n </button>\n </div>\n {searchBar ? (\n <div className={cn(FLEXIBLE_DESKTOP_CLASSES, \"justify-center\")}>\n {searchBar}\n </div>\n ) : null}\n <HeaderLinks\n className={FLEXIBLE_DESKTOP_CLASSES}\n headerLinks={headerLinks}\n sessionState={sessionState}\n searchButton={wrappedSearchButton}\n searchButtonVisibility={searchButtonVisibility}\n />\n </div>\n </header>\n {showMenu ? (\n <>\n <div\n className={cn(\n \"fixed inset-0 bg-neutral-1300 dark:bg-neutral-1300\",\n {\n \"animate-[fade-in-ten-percent_150ms_ease-in-out_forwards]\":\n !fadingOut,\n \"animate-[fade-out-ten-percent_150ms_ease-in-out_forwards]\":\n fadingOut,\n },\n )}\n onClick={closeMenu}\n onKeyDown={(e) => e.key === \"Escape\" && closeMenu()}\n role=\"presentation\"\n />\n <div\n id=\"mobile-menu\"\n className=\"md:hidden fixed flex flex-col top-[76px] overflow-y-hidden mx-12 right-0 w-[calc(100%-24px)] bg-neutral-000 dark:bg-neutral-1300 rounded-2xl ui-shadow-lg-medium z-20\"\n style={{\n maxWidth: MAX_MOBILE_MENU_WIDTH,\n maxHeight: componentMaxHeight(\n HEADER_HEIGHT,\n HEADER_BOTTOM_MARGIN,\n ),\n }}\n ref={menuRef}\n role=\"navigation\"\n >\n {mobileNav}\n <HeaderLinks\n headerLinks={headerLinks}\n sessionState={sessionState}\n />\n </div>\n </>\n ) : null}\n </>\n );\n};\n\nexport default Header;\n"],"names":["React","useState","useEffect","useRef","useMemo","Icon","cn","Logo","componentMaxHeight","HEADER_BOTTOM_MARGIN","HEADER_HEIGHT","HeaderLinks","throttle","FLEXIBLE_DESKTOP_CLASSES","MAX_MOBILE_MENU_WIDTH","Header","searchBar","searchButton","logoHref","headerLinks","nav","mobileNav","sessionState","themedScrollpoints","searchButtonVisibility","showMenu","setShowMenu","fadingOut","setFadingOut","scrollpointClasses","setScrollpointClasses","menuRef","closeMenu","setTimeout","handleResize","window","innerWidth","addEventListener","removeEventListener","document","body","classList","add","remove","handleScroll","scrollpoint","element","getElementById","id","rect","getBoundingClientRect","top","bottom","className","throttledHandleScroll","wrappedSearchButton","div","header","role","style","height","map","theme","key","href","additionalLinkAttrs","button","onClick","aria-expanded","aria-controls","aria-label","name","additionalCSS","size","onKeyDown","e","maxWidth","maxHeight","ref"],"mappings":"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"}
Binary file
@@ -1,2 +1,2 @@
1
- import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";let debugMode=false;export const initInsights=({mixpanelToken,mixpanelAutoCapture,posthogApiKey,posthogApiHost,debug=false})=>{debugMode=!!debug;try{mixpanel.initMixpanel(mixpanelToken,mixpanelAutoCapture,debugMode)}catch(e){if(debugMode){console.error("Failed to initialize Mixpanel",e)}}try{posthog.initPosthog(posthogApiKey,posthogApiHost)}catch(e){if(debugMode){console.error("Failed to initialize Posthog",e)}}};export const enableDebugMode=()=>{debugMode=true;try{mixpanel.enableDebugMode();posthog.enableDebugMode()}catch(e){console.error("Failed to enable debug mode",e)}};export const disableDebugMode=()=>{debugMode=false;try{mixpanel.disableDebugMode();posthog.disableDebugMode()}catch(e){console.error("Failed to disable debug mode",e)}};export const identify=({userId,accountId,organisationId,email,name})=>{try{mixpanel.identify(userId,accountId,organisationId,email,name)}catch(e){if(debugMode){console.error("Failed to identify user in Mixpanel",e)}}try{posthog.identify(userId,accountId,organisationId,email,name)}catch(e){if(debugMode){console.error("Failed to identify user in Posthog",e)}}};export const trackPageView=()=>{try{mixpanel.trackPageView()}catch(e){if(debugMode){console.error("Failed to track page view in Mixpanel",e)}}try{posthog.trackPageView()}catch(e){if(debugMode){console.error("Failed to track page view in Posthog",e)}}};export const track=(event,properties)=>{try{mixpanel.track(event,properties)}catch(e){if(debugMode){console.error("Failed to track event in Mixpanel",e)}}try{posthog.track(event,properties)}catch(e){if(debugMode){console.error("Failed to track event in Posthog",e)}}};export const setupObserver=()=>{const getInsightAttributes=element=>{const MAX_ATTRIBUTES=10;let count=0;const attributes={};for(const attr of element.attributes){if(count>=MAX_ATTRIBUTES)break;if(attr.name.startsWith("data-insight-")){if(!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name))continue;if(typeof attr.value!=="string"||attr.value.length>100)continue;const key=attr.name.replace("data-insight-","").split("-").map((part,index)=>index===0?part:part.charAt(0).toUpperCase()+part.slice(1)).join("");attributes[key]=attr.value;count++}}return attributes};const findClosestElementWithInsights=element=>{let current=element;while(current&&current!==document.body){const insights=getInsightAttributes(current);if(Object.keys(insights).length>0){return insights}current=current.parentElement}return null};const handleClick=event=>{if(!(event.target instanceof HTMLElement))return;const insights=findClosestElementWithInsights(event.target);if(insights){const{event:eventName,...properties}=insights;track(eventName||"element_clicked",properties)}};document.body.addEventListener("click",handleClick);return()=>{document.body.removeEventListener("click",handleClick)}};
1
+ import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";let debugMode=false;export const initInsights=({mixpanelToken,mixpanelAutoCapture,posthogApiKey,posthogApiHost,debug=false})=>{debugMode=!!debug;try{mixpanel.initMixpanel(mixpanelToken,mixpanelAutoCapture,debugMode)}catch(e){if(debugMode){console.error("Failed to initialize Mixpanel",e)}}try{posthog.initPosthog(posthogApiKey,posthogApiHost)}catch(e){if(debugMode){console.error("Failed to initialize Posthog",e)}}};export const enableDebugMode=()=>{debugMode=true;try{mixpanel.enableDebugMode();posthog.enableDebugMode()}catch(e){console.error("Failed to enable debug mode",e)}};export const disableDebugMode=()=>{debugMode=false;try{mixpanel.disableDebugMode();posthog.disableDebugMode()}catch(e){console.error("Failed to disable debug mode",e)}};export const identify=({userId,accountId,organisationId,email,name})=>{if(!userId){if(debugMode){console.warn("User ID not provided, skipping identify")}return}try{mixpanel.identify({userId,accountId,organisationId,email,name})}catch(e){if(debugMode){console.error("Failed to identify user in Mixpanel",e)}}try{posthog.identify({userId,accountId,organisationId,email,name})}catch(e){if(debugMode){console.error("Failed to identify user in Posthog",e)}}};export const trackPageView=()=>{try{mixpanel.trackPageView()}catch(e){if(debugMode){console.error("Failed to track page view in Mixpanel",e)}}try{posthog.trackPageView()}catch(e){if(debugMode){console.error("Failed to track page view in Posthog",e)}}};export const track=(event,properties)=>{try{mixpanel.track(event,properties)}catch(e){if(debugMode){console.error("Failed to track event in Mixpanel",e)}}try{posthog.track(event,properties)}catch(e){if(debugMode){console.error("Failed to track event in Posthog",e)}}};export const setupObserver=()=>{const getInsightAttributes=element=>{const MAX_ATTRIBUTES=10;let count=0;const attributes={};for(const attr of element.attributes){if(count>=MAX_ATTRIBUTES)break;if(attr.name.startsWith("data-insight-")){if(!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name))continue;if(typeof attr.value!=="string"||attr.value.length>100)continue;const key=attr.name.replace("data-insight-","").split("-").map((part,index)=>index===0?part:part.charAt(0).toUpperCase()+part.slice(1)).join("");attributes[key]=attr.value;count++}}return attributes};const findClosestElementWithInsights=element=>{let current=element;while(current&&current!==document.body){const insights=getInsightAttributes(current);if(Object.keys(insights).length>0){return insights}current=current.parentElement}return null};const handleClick=event=>{if(!(event.target instanceof HTMLElement))return;const insights=findClosestElementWithInsights(event.target);if(insights){const{event:eventName,...properties}=insights;track(eventName||"element_clicked",properties)}};document.body.addEventListener("click",handleClick);return()=>{document.body.removeEventListener("click",handleClick)}};
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/index.ts"],"sourcesContent":["import * as mixpanel from './mixpanel';\nimport * as posthog from './posthog';\n\nexport type InsightsConfig = {\n debug: boolean;\n mixpanelToken: string;\n mixpanelAutoCapture: boolean;\n posthogApiKey: string;\n posthogApiHost: string;\n};\n\nlet debugMode = false;\n\nexport const initInsights = ({mixpanelToken, mixpanelAutoCapture, posthogApiKey, posthogApiHost, debug = false}: InsightsConfig) => {\n debugMode = !!debug;\n\n try {\n mixpanel.initMixpanel(mixpanelToken, mixpanelAutoCapture, debugMode);\n } catch (e) {\n if (debugMode) {\n console.error('Failed to initialize Mixpanel', e);\n }\n }\n\n try {\n posthog.initPosthog(posthogApiKey, posthogApiHost);\n } catch (e) {\n if (debugMode) {\n console.error('Failed to initialize Posthog', e);\n }\n }\n};\n\nexport const enableDebugMode = () => {\n debugMode = true;\n try {\n mixpanel.enableDebugMode();\n posthog.enableDebugMode();\n } catch (e) {\n console.error('Failed to enable debug mode', e);\n }\n};\n\nexport const disableDebugMode = () => {\n debugMode = false;\n try {\n mixpanel.disableDebugMode();\n posthog.disableDebugMode();\n } catch (e) {\n console.error('Failed to disable debug mode', e);\n }\n};\n\nexport const identify = ({userId, accountId, organisationId, email, name}: {userId: string, accountId: string, organisationId?: string, email?: string, name?: string}) => {\n try {\n mixpanel.identify(userId, accountId, organisationId, email, name);\n } catch (e) {\n if (debugMode) {\n console.error('Failed to identify user in Mixpanel', e);\n }\n }\n\n try {\n posthog.identify(userId, accountId, organisationId, email, name);\n } catch (e) {\n if (debugMode) {\n console.error('Failed to identify user in Posthog', e);\n }\n }\n};\n\nexport const trackPageView = () => {\n try {\n mixpanel.trackPageView();\n } catch (e) {\n if (debugMode) {\n console.error('Failed to track page view in Mixpanel', e);\n }\n }\n\n try {\n posthog.trackPageView();\n } catch (e) {\n if (debugMode) {\n console.error('Failed to track page view in Posthog', e);\n }\n }\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n try {\n mixpanel.track(event, properties);\n } catch (e) {\n if (debugMode) {\n console.error('Failed to track event in Mixpanel', e);\n }\n }\n\n try {\n posthog.track(event, properties);\n } catch (e) {\n if (debugMode) {\n console.error('Failed to track event in Posthog', e);\n }\n }\n};\n\ntype InsightAttributes = {\n event?: string;\n [key: string]: string | undefined;\n};\n\nexport const setupObserver = () => {\n // Helper to get all data-insight-* attributes from an element\n const getInsightAttributes = (element): InsightAttributes => {\n // limit how many data attributes we'll process\n const MAX_ATTRIBUTES = 10;\n let count = 0;\n\n const attributes: InsightAttributes = {};\n\n for (const attr of element.attributes) {\n if (count >= MAX_ATTRIBUTES) break;\n if (attr.name.startsWith('data-insight-')) {\n // Validate attribute name format\n if (!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name)) continue;\n\n // Sanitize attribute value\n if (typeof attr.value !== 'string' || attr.value.length > 100) continue;\n\n // Convert data-insight-event-name to eventName\n const key = attr.name\n .replace('data-insight-', '')\n .split('-')\n .map((part, index) =>\n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)\n )\n .join('');\n attributes[key] = attr.value;\n count++;\n }\n }\n return attributes;\n };\n\n // Helper to find closest element with data-insight attributes\n const findClosestElementWithInsights = (element) => {\n let current = element;\n while (current && current !== document.body) {\n const insights = getInsightAttributes(current);\n if (Object.keys(insights).length > 0) {\n return insights;\n }\n current = current.parentElement;\n }\n return null;\n };\n\n // Global click handler\n const handleClick = (event: MouseEvent): void => {\n if (!(event.target instanceof HTMLElement)) return;\n const insights = findClosestElementWithInsights(event.target);\n if (insights) {\n // Extract special properties if they exist\n const { event: eventName, ...properties } = insights;\n track(eventName || 'element_clicked', properties);\n }\n };\n\n // Add listener to document body to catch all clicks\n document.body.addEventListener('click', handleClick);\n\n // Return cleanup function in case it's needed\n return () => {\n document.body.removeEventListener('click', handleClick);\n };\n};\n"],"names":["mixpanel","posthog","debugMode","initInsights","mixpanelToken","mixpanelAutoCapture","posthogApiKey","posthogApiHost","debug","initMixpanel","e","console","error","initPosthog","enableDebugMode","disableDebugMode","identify","userId","accountId","organisationId","email","name","trackPageView","track","event","properties","setupObserver","getInsightAttributes","element","MAX_ATTRIBUTES","count","attributes","attr","startsWith","test","value","length","key","replace","split","map","part","index","charAt","toUpperCase","slice","join","findClosestElementWithInsights","current","document","body","insights","Object","keys","parentElement","handleClick","target","HTMLElement","eventName","addEventListener","removeEventListener"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/insights/index.ts"],"sourcesContent":["import * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport { InsightsIdentity } from \"./types\";\n\nexport type InsightsConfig = {\n debug: boolean;\n mixpanelToken: string;\n mixpanelAutoCapture: boolean;\n posthogApiKey: string;\n posthogApiHost: string;\n};\n\nlet debugMode = false;\n\nexport const initInsights = ({\n mixpanelToken,\n mixpanelAutoCapture,\n posthogApiKey,\n posthogApiHost,\n debug = false,\n}: InsightsConfig) => {\n debugMode = !!debug;\n\n try {\n mixpanel.initMixpanel(mixpanelToken, mixpanelAutoCapture, debugMode);\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to initialize Mixpanel\", e);\n }\n }\n\n try {\n posthog.initPosthog(posthogApiKey, posthogApiHost);\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to initialize Posthog\", e);\n }\n }\n};\n\nexport const enableDebugMode = () => {\n debugMode = true;\n try {\n mixpanel.enableDebugMode();\n posthog.enableDebugMode();\n } catch (e) {\n console.error(\"Failed to enable debug mode\", e);\n }\n};\n\nexport const disableDebugMode = () => {\n debugMode = false;\n try {\n mixpanel.disableDebugMode();\n posthog.disableDebugMode();\n } catch (e) {\n console.error(\"Failed to disable debug mode\", e);\n }\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n if (debugMode) {\n console.warn(\"User ID not provided, skipping identify\");\n }\n return;\n }\n\n try {\n mixpanel.identify({ userId, accountId, organisationId, email, name });\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to identify user in Mixpanel\", e);\n }\n }\n\n try {\n posthog.identify({ userId, accountId, organisationId, email, name });\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to identify user in Posthog\", e);\n }\n }\n};\n\nexport const trackPageView = () => {\n try {\n mixpanel.trackPageView();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track page view in Mixpanel\", e);\n }\n }\n\n try {\n posthog.trackPageView();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track page view in Posthog\", e);\n }\n }\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n try {\n mixpanel.track(event, properties);\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track event in Mixpanel\", e);\n }\n }\n\n try {\n posthog.track(event, properties);\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track event in Posthog\", e);\n }\n }\n};\n\ntype InsightAttributes = {\n event?: string;\n [key: string]: string | undefined;\n};\n\nexport const setupObserver = () => {\n // Helper to get all data-insight-* attributes from an element\n const getInsightAttributes = (element): InsightAttributes => {\n // limit how many data attributes we'll process\n const MAX_ATTRIBUTES = 10;\n let count = 0;\n\n const attributes: InsightAttributes = {};\n\n for (const attr of element.attributes) {\n if (count >= MAX_ATTRIBUTES) break;\n if (attr.name.startsWith(\"data-insight-\")) {\n // Validate attribute name format\n if (!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name)) continue;\n\n // Sanitize attribute value\n if (typeof attr.value !== \"string\" || attr.value.length > 100) continue;\n\n // Convert data-insight-event-name to eventName\n const key = attr.name\n .replace(\"data-insight-\", \"\")\n .split(\"-\")\n .map((part, index) =>\n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1),\n )\n .join(\"\");\n attributes[key] = attr.value;\n count++;\n }\n }\n return attributes;\n };\n\n // Helper to find closest element with data-insight attributes\n const findClosestElementWithInsights = (element) => {\n let current = element;\n while (current && current !== document.body) {\n const insights = getInsightAttributes(current);\n if (Object.keys(insights).length > 0) {\n return insights;\n }\n current = current.parentElement;\n }\n return null;\n };\n\n // Global click handler\n const handleClick = (event: MouseEvent): void => {\n if (!(event.target instanceof HTMLElement)) return;\n const insights = findClosestElementWithInsights(event.target);\n if (insights) {\n // Extract special properties if they exist\n const { event: eventName, ...properties } = insights;\n track(eventName || \"element_clicked\", properties);\n }\n };\n\n // Add listener to document body to catch all clicks\n document.body.addEventListener(\"click\", handleClick);\n\n // Return cleanup function in case it's needed\n return () => {\n document.body.removeEventListener(\"click\", handleClick);\n };\n};\n"],"names":["mixpanel","posthog","debugMode","initInsights","mixpanelToken","mixpanelAutoCapture","posthogApiKey","posthogApiHost","debug","initMixpanel","e","console","error","initPosthog","enableDebugMode","disableDebugMode","identify","userId","accountId","organisationId","email","name","warn","trackPageView","track","event","properties","setupObserver","getInsightAttributes","element","MAX_ATTRIBUTES","count","attributes","attr","startsWith","test","value","length","key","replace","split","map","part","index","charAt","toUpperCase","slice","join","findClosestElementWithInsights","current","document","body","insights","Object","keys","parentElement","handleClick","target","HTMLElement","eventName","addEventListener","removeEventListener"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import mixpanel from"mixpanel-browser";export const initMixpanel=(token,autoCapture=false,debug=false)=>{const blockSelectors=["[ph-no-capture]",'[data-sl="mask"]'];if(!token){console.warn("Mixpanel token not provided, skipping initialization");return}mixpanel.init(token,{debug:debug,persistence:"localStorage",autocapture:autoCapture?{block_selectors:blockSelectors}:false,track_pageview:false})};export const enableDebugMode=()=>{mixpanel.set_config({debug:true})};export const disableDebugMode=()=>{mixpanel.set_config({debug:false})};export const identify=(userId,accountId,organisationId,email,name)=>{mixpanel.identify(userId);mixpanel.people.set({$email:email,$name:name});mixpanel.people.union({account_id:[accountId]});if(organisationId){mixpanel.people.set({organisation_id:[organisationId]})}};export const trackPageView=()=>{mixpanel.track_pageview()};export const track=(event,properties)=>{mixpanel.track(event,properties)};
1
+ import mixpanel from"mixpanel-browser";export const initMixpanel=(token,autoCapture=false,debug=false)=>{const blockSelectors=["[ph-no-capture]",'[data-sl="mask"]'];if(!token){console.warn("Mixpanel token not provided, skipping initialization");return}mixpanel.init(token,{debug:debug,persistence:"localStorage",autocapture:autoCapture?{block_selectors:blockSelectors}:false,track_pageview:false})};export const enableDebugMode=()=>{mixpanel.set_config({debug:true})};export const disableDebugMode=()=>{mixpanel.set_config({debug:false})};export const identify=({userId,accountId,organisationId,email,name})=>{if(!userId){return}mixpanel.identify(userId);if(email||name){mixpanel.people.set({$email:email,$name:name})}if(accountId){mixpanel.people.union({account_id:[accountId]})}if(organisationId){mixpanel.people.set({organisation_id:[organisationId]})}};export const trackPageView=mixpanel.track_pageview;export const track=mixpanel.track;
2
2
  //# sourceMappingURL=mixpanel.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/mixpanel.ts"],"sourcesContent":["import mixpanel from \"mixpanel-browser\";\n\nexport const initMixpanel = (\n token: string,\n autoCapture: boolean = false,\n debug: boolean = false,\n) => {\n const blockSelectors = [\"[ph-no-capture]\", '[data-sl=\"mask\"]'];\n if (!token) {\n console.warn(\"Mixpanel token not provided, skipping initialization\");\n return;\n }\n\n mixpanel.init(token, {\n debug: debug,\n persistence: \"localStorage\",\n autocapture: autoCapture\n ? {\n block_selectors: blockSelectors,\n }\n : false,\n track_pageview: false, // We'll track page views manually\n });\n};\n\nexport const enableDebugMode = () => {\n mixpanel.set_config({ debug: true });\n};\n\nexport const disableDebugMode = () => {\n mixpanel.set_config({ debug: false });\n};\n\nexport const identify = (\n userId: string,\n accountId: string,\n organisationId?: string,\n email?: string,\n name?: string,\n) => {\n mixpanel.identify(userId);\n mixpanel.people.set({ $email: email, $name: name });\n\n mixpanel.people.union({ account_id: [accountId] });\n\n if (organisationId) {\n mixpanel.people.set({ organisation_id: [organisationId] });\n }\n};\n\nexport const trackPageView = () => {\n mixpanel.track_pageview();\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n mixpanel.track(event, properties);\n};\n"],"names":["mixpanel","initMixpanel","token","autoCapture","debug","blockSelectors","console","warn","init","persistence","autocapture","block_selectors","track_pageview","enableDebugMode","set_config","disableDebugMode","identify","userId","accountId","organisationId","email","name","people","set","$email","$name","union","account_id","organisation_id","trackPageView","track","event","properties"],"mappings":"AAAA,OAAOA,aAAc,kBAAmB,AAExC,QAAO,MAAMC,aAAe,CAC1BC,MACAC,YAAuB,KAAK,CAC5BC,MAAiB,KAAK,IAEtB,MAAMC,eAAiB,CAAC,kBAAmB,mBAAmB,CAC9D,GAAI,CAACH,MAAO,CACVI,QAAQC,IAAI,CAAC,wDACb,MACF,CAEAP,SAASQ,IAAI,CAACN,MAAO,CACnBE,MAAOA,MACPK,YAAa,eACbC,YAAaP,YACT,CACEQ,gBAAiBN,cACnB,EACA,MACJO,eAAgB,KAClB,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7Bb,SAASc,UAAU,CAAC,CAAEV,MAAO,IAAK,EACpC,CAAE,AAEF,QAAO,MAAMW,iBAAmB,KAC9Bf,SAASc,UAAU,CAAC,CAAEV,MAAO,KAAM,EACrC,CAAE,AAEF,QAAO,MAAMY,SAAW,CACtBC,OACAC,UACAC,eACAC,MACAC,QAEArB,SAASgB,QAAQ,CAACC,QAClBjB,SAASsB,MAAM,CAACC,GAAG,CAAC,CAAEC,OAAQJ,MAAOK,MAAOJ,IAAK,GAEjDrB,SAASsB,MAAM,CAACI,KAAK,CAAC,CAAEC,WAAY,CAACT,UAAU,AAAC,GAEhD,GAAIC,eAAgB,CAClBnB,SAASsB,MAAM,CAACC,GAAG,CAAC,CAAEK,gBAAiB,CAACT,eAAe,AAAC,EAC1D,CACF,CAAE,AAEF,QAAO,MAAMU,cAAgB,KAC3B7B,SAASY,cAAc,EACzB,CAAE,AAEF,QAAO,MAAMkB,MAAQ,CAACC,MAAeC,cACnChC,SAAS8B,KAAK,CAACC,MAAOC,WACxB,CAAE"}
1
+ {"version":3,"sources":["../../../src/core/insights/mixpanel.ts"],"sourcesContent":["import mixpanel from \"mixpanel-browser\";\n\nimport { InsightsIdentity } from \"./types\";\n\nexport const initMixpanel = (\n token: string,\n autoCapture: boolean = false,\n debug: boolean = false,\n) => {\n const blockSelectors = [\"[ph-no-capture]\", '[data-sl=\"mask\"]'];\n if (!token) {\n console.warn(\"Mixpanel token not provided, skipping initialization\");\n return;\n }\n\n mixpanel.init(token, {\n debug: debug,\n persistence: \"localStorage\",\n autocapture: autoCapture\n ? {\n block_selectors: blockSelectors,\n }\n : false,\n track_pageview: false, // We'll track page views manually\n });\n};\n\nexport const enableDebugMode = () => {\n mixpanel.set_config({ debug: true });\n};\n\nexport const disableDebugMode = () => {\n mixpanel.set_config({ debug: false });\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n return;\n }\n\n mixpanel.identify(userId);\n\n if (email || name) {\n mixpanel.people.set({ $email: email, $name: name });\n }\n\n if (accountId) {\n mixpanel.people.union({ account_id: [accountId] });\n }\n\n if (organisationId) {\n mixpanel.people.set({ organisation_id: [organisationId] });\n }\n};\n\nexport const trackPageView = mixpanel.track_pageview;\n\nexport const track = mixpanel.track;\n"],"names":["mixpanel","initMixpanel","token","autoCapture","debug","blockSelectors","console","warn","init","persistence","autocapture","block_selectors","track_pageview","enableDebugMode","set_config","disableDebugMode","identify","userId","accountId","organisationId","email","name","people","set","$email","$name","union","account_id","organisation_id","trackPageView","track"],"mappings":"AAAA,OAAOA,aAAc,kBAAmB,AAIxC,QAAO,MAAMC,aAAe,CAC1BC,MACAC,YAAuB,KAAK,CAC5BC,MAAiB,KAAK,IAEtB,MAAMC,eAAiB,CAAC,kBAAmB,mBAAmB,CAC9D,GAAI,CAACH,MAAO,CACVI,QAAQC,IAAI,CAAC,wDACb,MACF,CAEAP,SAASQ,IAAI,CAACN,MAAO,CACnBE,MAAOA,MACPK,YAAa,eACbC,YAAaP,YACT,CACEQ,gBAAiBN,cACnB,EACA,MACJO,eAAgB,KAClB,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7Bb,SAASc,UAAU,CAAC,CAAEV,MAAO,IAAK,EACpC,CAAE,AAEF,QAAO,MAAMW,iBAAmB,KAC9Bf,SAASc,UAAU,CAAC,CAAEV,MAAO,KAAM,EACrC,CAAE,AAEF,QAAO,MAAMY,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACa,IAGjB,GAAI,CAACJ,OAAQ,CACX,MACF,CAEAjB,SAASgB,QAAQ,CAACC,QAElB,GAAIG,OAASC,KAAM,CACjBrB,SAASsB,MAAM,CAACC,GAAG,CAAC,CAAEC,OAAQJ,MAAOK,MAAOJ,IAAK,EACnD,CAEA,GAAIH,UAAW,CACblB,SAASsB,MAAM,CAACI,KAAK,CAAC,CAAEC,WAAY,CAACT,UAAU,AAAC,EAClD,CAEA,GAAIC,eAAgB,CAClBnB,SAASsB,MAAM,CAACC,GAAG,CAAC,CAAEK,gBAAiB,CAACT,eAAe,AAAC,EAC1D,CACF,CAAE,AAEF,QAAO,MAAMU,cAAgB7B,SAASY,cAAc,AAAC,AAErD,QAAO,MAAMkB,MAAQ9B,SAAS8B,KAAK,AAAC"}
@@ -1,2 +1,2 @@
1
- import posthog from"posthog-js";export const initPosthog=(apiKey,apiHost)=>{posthog.init(apiKey,{api_host:apiHost,capture_pageview:false})};export const enableDebugMode=()=>{posthog.debug()};export const disableDebugMode=()=>{posthog.debug(false)};export const identify=(userId,accountId,organisationId,email,name)=>{if(userId!==posthog.get_distinct_id()){posthog.identify(userId,{email,name})}posthog.group("account",accountId);if(organisationId){posthog.group("organisation",organisationId)}};export const trackPageView=()=>{posthog.capture("$pageview")};export const track=(event,properties)=>{posthog.capture(event,properties)};
1
+ import posthog from"posthog-js";export const initPosthog=(apiKey,apiHost)=>{posthog.init(apiKey,{api_host:apiHost,capture_pageview:false})};export const enableDebugMode=()=>{posthog.debug()};export const disableDebugMode=()=>{posthog.debug(false)};export const identify=({userId,accountId,organisationId,email,name})=>{if(!userId){return}if(userId!==posthog.get_distinct_id()){posthog.identify(userId,{email,name})}if(accountId){posthog.group("account",accountId)}if(organisationId){posthog.group("organisation",organisationId)}};export const trackPageView=()=>{posthog.capture("$pageview")};export const track=(event,properties)=>{posthog.capture(event,properties)};
2
2
  //# sourceMappingURL=posthog.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/posthog.ts"],"sourcesContent":["import posthog from \"posthog-js\";\n\nexport const initPosthog = (apiKey: string, apiHost: string) => {\n posthog.init(apiKey, {\n api_host: apiHost,\n capture_pageview: false,\n });\n};\n\nexport const enableDebugMode = () => {\n posthog.debug();\n};\n\nexport const disableDebugMode = () => {\n posthog.debug(false);\n};\n\nexport const identify = (\n userId: string,\n accountId: string,\n organisationId?: string,\n email?: string,\n name?: string,\n) => {\n if (userId !== posthog.get_distinct_id()) {\n posthog.identify(userId, { email, name });\n }\n\n // Associate all events in this session with this account\n posthog.group(\"account\", accountId);\n\n // Associate all events in this session with this organisation (if available)\n if (organisationId) {\n posthog.group(\"organisation\", organisationId);\n }\n};\n\nexport const trackPageView = () => {\n posthog.capture(\"$pageview\");\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n posthog.capture(event, properties);\n};\n"],"names":["posthog","initPosthog","apiKey","apiHost","init","api_host","capture_pageview","enableDebugMode","debug","disableDebugMode","identify","userId","accountId","organisationId","email","name","get_distinct_id","group","trackPageView","capture","track","event","properties"],"mappings":"AAAA,OAAOA,YAAa,YAAa,AAEjC,QAAO,MAAMC,YAAc,CAACC,OAAgBC,WAC1CH,QAAQI,IAAI,CAACF,OAAQ,CACnBG,SAAUF,QACVG,iBAAkB,KACpB,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BP,QAAQQ,KAAK,EACf,CAAE,AAEF,QAAO,MAAMC,iBAAmB,KAC9BT,QAAQQ,KAAK,CAAC,MAChB,CAAE,AAEF,QAAO,MAAME,SAAW,CACtBC,OACAC,UACAC,eACAC,MACAC,QAEA,GAAIJ,SAAWX,QAAQgB,eAAe,GAAI,CACxChB,QAAQU,QAAQ,CAACC,OAAQ,CAAEG,MAAOC,IAAK,EACzC,CAGAf,QAAQiB,KAAK,CAAC,UAAWL,WAGzB,GAAIC,eAAgB,CAClBb,QAAQiB,KAAK,CAAC,eAAgBJ,eAChC,CACF,CAAE,AAEF,QAAO,MAAMK,cAAgB,KAC3BlB,QAAQmB,OAAO,CAAC,YAClB,CAAE,AAEF,QAAO,MAAMC,MAAQ,CAACC,MAAeC,cACnCtB,QAAQmB,OAAO,CAACE,MAAOC,WACzB,CAAE"}
1
+ {"version":3,"sources":["../../../src/core/insights/posthog.ts"],"sourcesContent":["import posthog from \"posthog-js\";\n\nimport { InsightsIdentity } from \"./types\";\n\nexport const initPosthog = (apiKey: string, apiHost: string) => {\n posthog.init(apiKey, {\n api_host: apiHost,\n capture_pageview: false,\n });\n};\n\nexport const enableDebugMode = () => {\n posthog.debug();\n};\n\nexport const disableDebugMode = () => {\n posthog.debug(false);\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n return;\n }\n\n if (userId !== posthog.get_distinct_id()) {\n posthog.identify(userId, { email, name });\n }\n\n // Associate all events in this session with this account\n if (accountId) {\n posthog.group(\"account\", accountId);\n }\n\n // Associate all events in this session with this organisation (if available)\n if (organisationId) {\n posthog.group(\"organisation\", organisationId);\n }\n};\n\nexport const trackPageView = () => {\n posthog.capture(\"$pageview\");\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n posthog.capture(event, properties);\n};\n"],"names":["posthog","initPosthog","apiKey","apiHost","init","api_host","capture_pageview","enableDebugMode","debug","disableDebugMode","identify","userId","accountId","organisationId","email","name","get_distinct_id","group","trackPageView","capture","track","event","properties"],"mappings":"AAAA,OAAOA,YAAa,YAAa,AAIjC,QAAO,MAAMC,YAAc,CAACC,OAAgBC,WAC1CH,QAAQI,IAAI,CAACF,OAAQ,CACnBG,SAAUF,QACVG,iBAAkB,KACpB,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BP,QAAQQ,KAAK,EACf,CAAE,AAEF,QAAO,MAAMC,iBAAmB,KAC9BT,QAAQQ,KAAK,CAAC,MAChB,CAAE,AAEF,QAAO,MAAME,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACa,IAGjB,GAAI,CAACJ,OAAQ,CACX,MACF,CAEA,GAAIA,SAAWX,QAAQgB,eAAe,GAAI,CACxChB,QAAQU,QAAQ,CAACC,OAAQ,CAAEG,MAAOC,IAAK,EACzC,CAGA,GAAIH,UAAW,CACbZ,QAAQiB,KAAK,CAAC,UAAWL,UAC3B,CAGA,GAAIC,eAAgB,CAClBb,QAAQiB,KAAK,CAAC,eAAgBJ,eAChC,CACF,CAAE,AAEF,QAAO,MAAMK,cAAgB,KAC3BlB,QAAQmB,OAAO,CAAC,YAClB,CAAE,AAEF,QAAO,MAAMC,MAAQ,CAACC,MAAeC,cACnCtB,QAAQmB,OAAO,CAACE,MAAOC,WACzB,CAAE"}
@@ -0,0 +1,2 @@
1
+ export{};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core/insights/types.ts"],"sourcesContent":["export type InsightsIdentity = {\n userId: string;\n accountId: string;\n organisationId?: string;\n email?: string;\n name?: string;\n};\n"],"names":[],"mappings":"AAAA,QAME"}
@@ -1,2 +1,2 @@
1
- import{isJsonResponse}from"./remote-data-util";const fetchBlogPosts=async(store,blogUrl)=>{try{if(!blogUrl){console.log(`Skipping fetching blog posts, invalid blogUrl: "${blogUrl}"`);return}const options={headers:{accept:"application/json"},cache:"no-cache"};if(true){options.credentials="include"}const res=await fetch(blogUrl,options);if(isJsonResponse(res.headers.get("content-type"))){const payload=await res.json();store.dispatch({type:"blog/loaded",payload})}else{throw new Error("Blog posts url is not serving json")}}catch(e){console.warn("Could not fetch blog posts due to error:",e)}};const initialState={recent:null};const REDUCER_KEY="blogPosts";const reducerBlogPosts={[REDUCER_KEY]:(state=initialState,action)=>{switch(action.type){case"blog/loaded":return{...state,recent:action.payload};default:return state}}};const selectRecentBlogPosts=store=>store.getState()[REDUCER_KEY]?.recent;export{fetchBlogPosts,reducerBlogPosts,selectRecentBlogPosts};
1
+ import{isJsonResponse}from"./remote-data-util";const fetchBlogPosts=async(store,blogUrl)=>{try{if(!blogUrl){console.log(`Skipping fetching blog posts, invalid blogUrl: "${blogUrl}"`);return}const options={headers:{accept:"application/json"},cache:"no-cache"};if(false){options.credentials="include"}const res=await fetch(blogUrl,options);if(isJsonResponse(res.headers.get("content-type"))){const payload=await res.json();store.dispatch({type:"blog/loaded",payload})}else{throw new Error("Blog posts url is not serving json")}}catch(e){console.warn("Could not fetch blog posts due to error:",e)}};const initialState={recent:null};const REDUCER_KEY="blogPosts";const reducerBlogPosts={[REDUCER_KEY]:(state=initialState,action)=>{switch(action.type){case"blog/loaded":return{...state,recent:action.payload};default:return state}}};const selectRecentBlogPosts=store=>store.getState()[REDUCER_KEY]?.recent;export{fetchBlogPosts,reducerBlogPosts,selectRecentBlogPosts};
2
2
  //# sourceMappingURL=remote-blogs-posts.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/remote-blogs-posts.js"],"sourcesContent":["/* global __ENABLE_FETCH_WITH_CREDENTIALS__ */\n\nimport { isJsonResponse } from \"./remote-data-util\";\n\nconst fetchBlogPosts = async (store, blogUrl) => {\n try {\n if (!blogUrl) {\n console.log(\n `Skipping fetching blog posts, invalid blogUrl: \"${blogUrl}\"`,\n );\n return;\n }\n\n const options = {\n headers: {\n accept: \"application/json\",\n },\n cache: \"no-cache\",\n };\n\n if (__ENABLE_FETCH_WITH_CREDENTIALS__) {\n options.credentials = \"include\";\n }\n\n const res = await fetch(blogUrl, options);\n\n if (isJsonResponse(res.headers.get(\"content-type\"))) {\n const payload = await res.json();\n store.dispatch({ type: \"blog/loaded\", payload });\n } else {\n throw new Error(\"Blog posts url is not serving json\");\n }\n } catch (e) {\n console.warn(\"Could not fetch blog posts due to error:\", e);\n }\n};\n\nconst initialState = { recent: null };\n\nconst REDUCER_KEY = \"blogPosts\";\n\nconst reducerBlogPosts = {\n [REDUCER_KEY]: (state = initialState, action) => {\n switch (action.type) {\n case \"blog/loaded\":\n return { ...state, recent: action.payload };\n default:\n return state;\n }\n },\n};\n\nconst selectRecentBlogPosts = (store) => store.getState()[REDUCER_KEY]?.recent;\n\nexport { fetchBlogPosts, reducerBlogPosts, selectRecentBlogPosts };\n"],"names":["isJsonResponse","fetchBlogPosts","store","blogUrl","console","log","options","headers","accept","cache","credentials","res","fetch","get","payload","json","dispatch","type","Error","e","warn","initialState","recent","REDUCER_KEY","reducerBlogPosts","state","action","selectRecentBlogPosts","getState"],"mappings":"AAEA,OAASA,cAAc,KAAQ,oBAAqB,CAEpD,MAAMC,eAAiB,MAAOC,MAAOC,WACnC,GAAI,CACF,GAAI,CAACA,QAAS,CACZC,QAAQC,GAAG,CACT,CAAC,gDAAgD,EAAEF,QAAQ,CAAC,CAAC,EAE/D,MACF,CAEA,MAAMG,QAAU,CACdC,QAAS,CACPC,OAAQ,kBACV,EACAC,MAAO,UACT,EAEA,GApBJ,KAoB2C,CACrCH,QAAQI,WAAW,CAAG,SACxB,CAEA,MAAMC,IAAM,MAAMC,MAAMT,QAASG,SAEjC,GAAIN,eAAeW,IAAIJ,OAAO,CAACM,GAAG,CAAC,iBAAkB,CACnD,MAAMC,QAAU,MAAMH,IAAII,IAAI,GAC9Bb,MAAMc,QAAQ,CAAC,CAAEC,KAAM,cAAeH,OAAQ,EAChD,KAAO,CACL,MAAM,IAAII,MAAM,qCAClB,CACF,CAAE,MAAOC,EAAG,CACVf,QAAQgB,IAAI,CAAC,2CAA4CD,EAC3D,CACF,EAEA,MAAME,aAAe,CAAEC,OAAQ,IAAK,EAEpC,MAAMC,YAAc,YAEpB,MAAMC,iBAAmB,CACvB,CAACD,YAAY,CAAE,CAACE,MAAQJ,YAAY,CAAEK,UACpC,OAAQA,OAAOT,IAAI,EACjB,IAAK,cACH,MAAO,CAAE,GAAGQ,KAAK,CAAEH,OAAQI,OAAOZ,OAAO,AAAC,CAC5C,SACE,OAAOW,KACX,CACF,CACF,EAEA,MAAME,sBAAwB,AAACzB,OAAUA,MAAM0B,QAAQ,EAAE,CAACL,YAAY,EAAED,MAExE,QAASrB,cAAc,CAAEuB,gBAAgB,CAAEG,qBAAqB,CAAG"}
1
+ {"version":3,"sources":["../../src/core/remote-blogs-posts.js"],"sourcesContent":["/* global __ENABLE_FETCH_WITH_CREDENTIALS__ */\n\nimport { isJsonResponse } from \"./remote-data-util\";\n\nconst fetchBlogPosts = async (store, blogUrl) => {\n try {\n if (!blogUrl) {\n console.log(\n `Skipping fetching blog posts, invalid blogUrl: \"${blogUrl}\"`,\n );\n return;\n }\n\n const options = {\n headers: {\n accept: \"application/json\",\n },\n cache: \"no-cache\",\n };\n\n if (__ENABLE_FETCH_WITH_CREDENTIALS__) {\n options.credentials = \"include\";\n }\n\n const res = await fetch(blogUrl, options);\n\n if (isJsonResponse(res.headers.get(\"content-type\"))) {\n const payload = await res.json();\n store.dispatch({ type: \"blog/loaded\", payload });\n } else {\n throw new Error(\"Blog posts url is not serving json\");\n }\n } catch (e) {\n console.warn(\"Could not fetch blog posts due to error:\", e);\n }\n};\n\nconst initialState = { recent: null };\n\nconst REDUCER_KEY = \"blogPosts\";\n\nconst reducerBlogPosts = {\n [REDUCER_KEY]: (state = initialState, action) => {\n switch (action.type) {\n case \"blog/loaded\":\n return { ...state, recent: action.payload };\n default:\n return state;\n }\n },\n};\n\nconst selectRecentBlogPosts = (store) => store.getState()[REDUCER_KEY]?.recent;\n\nexport { fetchBlogPosts, reducerBlogPosts, selectRecentBlogPosts };\n"],"names":["isJsonResponse","fetchBlogPosts","store","blogUrl","console","log","options","headers","accept","cache","credentials","res","fetch","get","payload","json","dispatch","type","Error","e","warn","initialState","recent","REDUCER_KEY","reducerBlogPosts","state","action","selectRecentBlogPosts","getState"],"mappings":"AAEA,OAASA,cAAc,KAAQ,oBAAqB,CAEpD,MAAMC,eAAiB,MAAOC,MAAOC,WACnC,GAAI,CACF,GAAI,CAACA,QAAS,CACZC,QAAQC,GAAG,CACT,CAAC,gDAAgD,EAAEF,QAAQ,CAAC,CAAC,EAE/D,MACF,CAEA,MAAMG,QAAU,CACdC,QAAS,CACPC,OAAQ,kBACV,EACAC,MAAO,UACT,EAEA,GApBJ,MAoB2C,CACrCH,QAAQI,WAAW,CAAG,SACxB,CAEA,MAAMC,IAAM,MAAMC,MAAMT,QAASG,SAEjC,GAAIN,eAAeW,IAAIJ,OAAO,CAACM,GAAG,CAAC,iBAAkB,CACnD,MAAMC,QAAU,MAAMH,IAAII,IAAI,GAC9Bb,MAAMc,QAAQ,CAAC,CAAEC,KAAM,cAAeH,OAAQ,EAChD,KAAO,CACL,MAAM,IAAII,MAAM,qCAClB,CACF,CAAE,MAAOC,EAAG,CACVf,QAAQgB,IAAI,CAAC,2CAA4CD,EAC3D,CACF,EAEA,MAAME,aAAe,CAAEC,OAAQ,IAAK,EAEpC,MAAMC,YAAc,YAEpB,MAAMC,iBAAmB,CACvB,CAACD,YAAY,CAAE,CAACE,MAAQJ,YAAY,CAAEK,UACpC,OAAQA,OAAOT,IAAI,EACjB,IAAK,cACH,MAAO,CAAE,GAAGQ,KAAK,CAAEH,OAAQI,OAAOZ,OAAO,AAAC,CAC5C,SACE,OAAOW,KACX,CACF,CACF,EAEA,MAAME,sBAAwB,AAACzB,OAAUA,MAAM0B,QAAQ,EAAE,CAACL,YAAY,EAAED,MAExE,QAASrB,cAAc,CAAEuB,gBAAgB,CAAEG,qBAAqB,CAAG"}
@@ -1,2 +1,2 @@
1
- import{isJsonResponse}from"./remote-data-util";const NOT_FOUND_ERROR_CODE="not-found";const fetchSessionData=async(store,sessionUrl)=>{const sessionLoaded=(payload={})=>store.dispatch({type:"session/loaded",payload});try{if(!sessionUrl){console.log(`Skipping fetching session, invalid sessionUrl: "${sessionUrl}"`);sessionLoaded();return}const options={headers:{accept:"application/json"},cache:"no-cache"};if(true){options.credentials="include"}const res=await fetch(sessionUrl,options);const jsonResponse=isJsonResponse(res.headers.get("content-type"));if(!jsonResponse){throw new Error("Session endpoint is not serving json")}const payload=await res.json();if(payload.error===NOT_FOUND_ERROR_CODE){sessionLoaded()}else{sessionLoaded(payload)}}catch(e){sessionLoaded();console.warn("Could not fetch session data due to error:",e)}};const initialState={data:null};const REDUCER_KEY="session";const reducerSessionData={[REDUCER_KEY]:(state=initialState,action)=>{switch(action.type){case"session/loaded":return{...state,data:action.payload};default:return state}}};const selectSessionData=store=>store.getState()[REDUCER_KEY]?.data;export{fetchSessionData,reducerSessionData,selectSessionData};
1
+ import{isJsonResponse}from"./remote-data-util";const NOT_FOUND_ERROR_CODE="not-found";const fetchSessionData=async(store,sessionUrl)=>{const sessionLoaded=(payload={})=>store.dispatch({type:"session/loaded",payload});try{if(!sessionUrl){console.log(`Skipping fetching session, invalid sessionUrl: "${sessionUrl}"`);sessionLoaded();return}const options={headers:{accept:"application/json"},cache:"no-cache"};if(false){options.credentials="include"}const res=await fetch(sessionUrl,options);const jsonResponse=isJsonResponse(res.headers.get("content-type"));if(!jsonResponse){throw new Error("Session endpoint is not serving json")}const payload=await res.json();if(payload.error===NOT_FOUND_ERROR_CODE){sessionLoaded()}else{sessionLoaded(payload)}}catch(e){sessionLoaded();console.warn("Could not fetch session data due to error:",e)}};const initialState={data:null};const REDUCER_KEY="session";const reducerSessionData={[REDUCER_KEY]:(state=initialState,action)=>{switch(action.type){case"session/loaded":return{...state,data:action.payload};default:return state}}};const selectSessionData=store=>store.getState()[REDUCER_KEY]?.data;export{fetchSessionData,reducerSessionData,selectSessionData};
2
2
  //# sourceMappingURL=remote-session-data.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/remote-session-data.js"],"sourcesContent":["/* global __ENABLE_FETCH_WITH_CREDENTIALS__ */\n\n// Fetches current users session data\n// Assumes an authenticated session, so will only work when used on ably.com/ably.io\n\nimport { isJsonResponse } from \"./remote-data-util\";\n\nconst NOT_FOUND_ERROR_CODE = \"not-found\";\n\nconst fetchSessionData = async (store, sessionUrl) => {\n const sessionLoaded = (payload = {}) =>\n store.dispatch({ type: \"session/loaded\", payload });\n\n try {\n if (!sessionUrl) {\n console.log(\n `Skipping fetching session, invalid sessionUrl: \"${sessionUrl}\"`,\n );\n sessionLoaded();\n return;\n }\n\n const options = {\n headers: {\n accept: \"application/json\",\n },\n cache: \"no-cache\",\n };\n\n if (__ENABLE_FETCH_WITH_CREDENTIALS__) {\n options.credentials = \"include\";\n }\n\n const res = await fetch(sessionUrl, options);\n const jsonResponse = isJsonResponse(res.headers.get(\"content-type\"));\n\n if (!jsonResponse) {\n throw new Error(\"Session endpoint is not serving json\");\n }\n\n const payload = await res.json();\n\n if (payload.error === NOT_FOUND_ERROR_CODE) {\n sessionLoaded();\n } else {\n sessionLoaded(payload);\n }\n } catch (e) {\n sessionLoaded();\n console.warn(\"Could not fetch session data due to error:\", e);\n }\n};\n\nconst initialState = { data: null };\n\nconst REDUCER_KEY = \"session\";\n\nconst reducerSessionData = {\n [REDUCER_KEY]: (state = initialState, action) => {\n switch (action.type) {\n case \"session/loaded\":\n return { ...state, data: action.payload };\n default:\n return state;\n }\n },\n};\n\nconst selectSessionData = (store) => store.getState()[REDUCER_KEY]?.data;\n\nexport { fetchSessionData, reducerSessionData, selectSessionData };\n"],"names":["isJsonResponse","NOT_FOUND_ERROR_CODE","fetchSessionData","store","sessionUrl","sessionLoaded","payload","dispatch","type","console","log","options","headers","accept","cache","credentials","res","fetch","jsonResponse","get","Error","json","error","e","warn","initialState","data","REDUCER_KEY","reducerSessionData","state","action","selectSessionData","getState"],"mappings":"AAKA,OAASA,cAAc,KAAQ,oBAAqB,CAEpD,MAAMC,qBAAuB,YAE7B,MAAMC,iBAAmB,MAAOC,MAAOC,cACrC,MAAMC,cAAgB,CAACC,QAAU,CAAC,CAAC,GACjCH,MAAMI,QAAQ,CAAC,CAAEC,KAAM,iBAAkBF,OAAQ,GAEnD,GAAI,CACF,GAAI,CAACF,WAAY,CACfK,QAAQC,GAAG,CACT,CAAC,gDAAgD,EAAEN,WAAW,CAAC,CAAC,EAElEC,gBACA,MACF,CAEA,MAAMM,QAAU,CACdC,QAAS,CACPC,OAAQ,kBACV,EACAC,MAAO,UACT,EAEA,GA7BJ,KA6B2C,CACrCH,QAAQI,WAAW,CAAG,SACxB,CAEA,MAAMC,IAAM,MAAMC,MAAMb,WAAYO,SACpC,MAAMO,aAAelB,eAAegB,IAAIJ,OAAO,CAACO,GAAG,CAAC,iBAEpD,GAAI,CAACD,aAAc,CACjB,MAAM,IAAIE,MAAM,uCAClB,CAEA,MAAMd,QAAU,MAAMU,IAAIK,IAAI,GAE9B,GAAIf,QAAQgB,KAAK,GAAKrB,qBAAsB,CAC1CI,eACF,KAAO,CACLA,cAAcC,QAChB,CACF,CAAE,MAAOiB,EAAG,CACVlB,gBACAI,QAAQe,IAAI,CAAC,6CAA8CD,EAC7D,CACF,EAEA,MAAME,aAAe,CAAEC,KAAM,IAAK,EAElC,MAAMC,YAAc,UAEpB,MAAMC,mBAAqB,CACzB,CAACD,YAAY,CAAE,CAACE,MAAQJ,YAAY,CAAEK,UACpC,OAAQA,OAAOtB,IAAI,EACjB,IAAK,iBACH,MAAO,CAAE,GAAGqB,KAAK,CAAEH,KAAMI,OAAOxB,OAAO,AAAC,CAC1C,SACE,OAAOuB,KACX,CACF,CACF,EAEA,MAAME,kBAAoB,AAAC5B,OAAUA,MAAM6B,QAAQ,EAAE,CAACL,YAAY,EAAED,IAEpE,QAASxB,gBAAgB,CAAE0B,kBAAkB,CAAEG,iBAAiB,CAAG"}
1
+ {"version":3,"sources":["../../src/core/remote-session-data.js"],"sourcesContent":["/* global __ENABLE_FETCH_WITH_CREDENTIALS__ */\n\n// Fetches current users session data\n// Assumes an authenticated session, so will only work when used on ably.com/ably.io\n\nimport { isJsonResponse } from \"./remote-data-util\";\n\nconst NOT_FOUND_ERROR_CODE = \"not-found\";\n\nconst fetchSessionData = async (store, sessionUrl) => {\n const sessionLoaded = (payload = {}) =>\n store.dispatch({ type: \"session/loaded\", payload });\n\n try {\n if (!sessionUrl) {\n console.log(\n `Skipping fetching session, invalid sessionUrl: \"${sessionUrl}\"`,\n );\n sessionLoaded();\n return;\n }\n\n const options = {\n headers: {\n accept: \"application/json\",\n },\n cache: \"no-cache\",\n };\n\n if (__ENABLE_FETCH_WITH_CREDENTIALS__) {\n options.credentials = \"include\";\n }\n\n const res = await fetch(sessionUrl, options);\n const jsonResponse = isJsonResponse(res.headers.get(\"content-type\"));\n\n if (!jsonResponse) {\n throw new Error(\"Session endpoint is not serving json\");\n }\n\n const payload = await res.json();\n\n if (payload.error === NOT_FOUND_ERROR_CODE) {\n sessionLoaded();\n } else {\n sessionLoaded(payload);\n }\n } catch (e) {\n sessionLoaded();\n console.warn(\"Could not fetch session data due to error:\", e);\n }\n};\n\nconst initialState = { data: null };\n\nconst REDUCER_KEY = \"session\";\n\nconst reducerSessionData = {\n [REDUCER_KEY]: (state = initialState, action) => {\n switch (action.type) {\n case \"session/loaded\":\n return { ...state, data: action.payload };\n default:\n return state;\n }\n },\n};\n\nconst selectSessionData = (store) => store.getState()[REDUCER_KEY]?.data;\n\nexport { fetchSessionData, reducerSessionData, selectSessionData };\n"],"names":["isJsonResponse","NOT_FOUND_ERROR_CODE","fetchSessionData","store","sessionUrl","sessionLoaded","payload","dispatch","type","console","log","options","headers","accept","cache","credentials","res","fetch","jsonResponse","get","Error","json","error","e","warn","initialState","data","REDUCER_KEY","reducerSessionData","state","action","selectSessionData","getState"],"mappings":"AAKA,OAASA,cAAc,KAAQ,oBAAqB,CAEpD,MAAMC,qBAAuB,YAE7B,MAAMC,iBAAmB,MAAOC,MAAOC,cACrC,MAAMC,cAAgB,CAACC,QAAU,CAAC,CAAC,GACjCH,MAAMI,QAAQ,CAAC,CAAEC,KAAM,iBAAkBF,OAAQ,GAEnD,GAAI,CACF,GAAI,CAACF,WAAY,CACfK,QAAQC,GAAG,CACT,CAAC,gDAAgD,EAAEN,WAAW,CAAC,CAAC,EAElEC,gBACA,MACF,CAEA,MAAMM,QAAU,CACdC,QAAS,CACPC,OAAQ,kBACV,EACAC,MAAO,UACT,EAEA,GA7BJ,MA6B2C,CACrCH,QAAQI,WAAW,CAAG,SACxB,CAEA,MAAMC,IAAM,MAAMC,MAAMb,WAAYO,SACpC,MAAMO,aAAelB,eAAegB,IAAIJ,OAAO,CAACO,GAAG,CAAC,iBAEpD,GAAI,CAACD,aAAc,CACjB,MAAM,IAAIE,MAAM,uCAClB,CAEA,MAAMd,QAAU,MAAMU,IAAIK,IAAI,GAE9B,GAAIf,QAAQgB,KAAK,GAAKrB,qBAAsB,CAC1CI,eACF,KAAO,CACLA,cAAcC,QAChB,CACF,CAAE,MAAOiB,EAAG,CACVlB,gBACAI,QAAQe,IAAI,CAAC,6CAA8CD,EAC7D,CACF,EAEA,MAAME,aAAe,CAAEC,KAAM,IAAK,EAElC,MAAMC,YAAc,UAEpB,MAAMC,mBAAqB,CACzB,CAACD,YAAY,CAAE,CAACE,MAAQJ,YAAY,CAAEK,UACpC,OAAQA,OAAOtB,IAAI,EACjB,IAAK,iBACH,MAAO,CAAE,GAAGqB,KAAK,CAAEH,KAAMI,OAAOxB,OAAO,AAAC,CAC1C,SACE,OAAOuB,KACX,CACF,CACF,EAEA,MAAME,kBAAoB,AAAC5B,OAAUA,MAAM6B,QAAQ,EAAE,CAACL,YAAY,EAAED,IAEpE,QAASxB,gBAAgB,CAAE0B,kBAAkB,CAAEG,iBAAiB,CAAG"}
package/index.d.ts CHANGED
@@ -343,28 +343,51 @@ declare module '@ably/ui/core/DropdownMenu' {
343
343
  import { ReactNode } from "react";
344
344
  import { IconName } from "@ably/ui/core/Icon/types";
345
345
  type DropdownMenuProps = {
346
+ /**
347
+ * The content to be displayed within the dropdown menu.
348
+ */
346
349
  children: ReactNode;
347
350
  };
348
351
  type TriggerProps = {
352
+ /**
353
+ * The content to be displayed within the trigger element.
354
+ */
349
355
  children: ReactNode;
350
- additionalTriggerCSS?: string;
356
+ /**
357
+ * Additional class names to apply to the trigger element.
358
+ */
359
+ triggerClassNames?: string;
360
+ /**
361
+ * A description for the trigger element, used for accessibility purposes.
362
+ */
363
+ description?: string;
351
364
  };
352
365
  type ContentProps = {
366
+ /**
367
+ * The content to be displayed within the dropdown menu.
368
+ */
353
369
  children: ReactNode;
370
+ /**
371
+ * The position of the dropdown menu relative to the trigger element.
372
+ * Defaults to "right".
373
+ */
354
374
  anchorPosition?: string;
355
- additionalContentCSS?: string;
375
+ /**
376
+ * Additional class names to apply to the content container.
377
+ */
378
+ contentClassNames?: string;
356
379
  };
357
380
  type LinkProps = {
358
381
  url: string;
359
382
  title: string;
360
- subtitle: string;
361
- iconName: IconName;
362
- children: ReactNode;
383
+ subtitle?: string;
384
+ iconName?: IconName;
385
+ children?: ReactNode;
363
386
  };
364
387
  const DropdownMenu: {
365
388
  ({ children }: DropdownMenuProps): import("react/jsx-runtime").JSX.Element;
366
- Trigger: ({ children, additionalTriggerCSS }: TriggerProps) => import("react/jsx-runtime").JSX.Element;
367
- Content: ({ children, anchorPosition, additionalContentCSS, }: ContentProps) => import("react/jsx-runtime").JSX.Element | null;
389
+ Trigger: ({ children, triggerClassNames, description, }: TriggerProps) => import("react/jsx-runtime").JSX.Element;
390
+ Content: ({ children, anchorPosition, contentClassNames, }: ContentProps) => import("react/jsx-runtime").JSX.Element | null;
368
391
  Link: ({ url, title, subtitle, iconName, children }: LinkProps) => import("react/jsx-runtime").JSX.Element;
369
392
  };
370
393
  export default DropdownMenu;
@@ -526,6 +549,32 @@ export type ThemedScrollpoint = {
526
549
  id: string;
527
550
  className: string;
528
551
  };
552
+ /**
553
+ * Represents the state of the user session in the header.
554
+ */
555
+ export type HeaderSessionState = {
556
+ /**
557
+ * Indicates if the user is signed in.
558
+ */
559
+ signedIn: boolean;
560
+ /**
561
+ * Information required to log out the user.
562
+ */
563
+ logOut: {
564
+ /**
565
+ * Token used for logging out.
566
+ */
567
+ token: string;
568
+ /**
569
+ * URL to log out the user.
570
+ */
571
+ href: string;
572
+ };
573
+ /**
574
+ * Name of the user's account.
575
+ */
576
+ accountName: string;
577
+ };
529
578
  /**
530
579
  * Props for the Header component.
531
580
  */
@@ -570,31 +619,7 @@ export type HeaderProps = {
570
619
  /**
571
620
  * State of the user session.
572
621
  */
573
- sessionState?: {
574
- /**
575
- * Indicates if the user is signed in.
576
- */
577
- signedIn: boolean;
578
- /**
579
- * Account information.
580
- */
581
- account: {
582
- /**
583
- * Links related to the account.
584
- */
585
- links: {
586
- /**
587
- * Dashboard link information.
588
- */
589
- dashboard: {
590
- /**
591
- * URL for the dashboard link.
592
- */
593
- href: string;
594
- };
595
- };
596
- };
597
- };
622
+ sessionState?: HeaderSessionState;
598
623
  /**
599
624
  * Array of themed scrollpoints. The header will change its appearance based on the scrollpoint in view.
600
625
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ably/ui",
3
- "version": "15.5.0-dev.4b8dd74",
3
+ "version": "15.6.0-dev.1a416b3",
4
4
  "description": "Home of the Ably design system library ([design.ably.com](https://design.ably.com)). It provides a showcase, development/test environment and a publishing pipeline for different distributables.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,18 +19,17 @@
19
19
  "workerDirectory": "./public"
20
20
  },
21
21
  "devDependencies": {
22
- "@storybook/addon-a11y": "^8.4.7",
23
- "@storybook/addon-essentials": "^8.4.7",
24
- "@storybook/addon-interactions": "^8.4.7",
25
- "@storybook/addon-links": "^8.4.7",
26
- "@storybook/blocks": "^8.4.7",
27
- "@storybook/react-vite": "^8.4.7",
28
- "@storybook/test": "^8.4.7",
29
- "@storybook/test-runner": "^0.21.0",
22
+ "@storybook/addon-a11y": "^8.6.0",
23
+ "@storybook/addon-essentials": "^8.6.0",
24
+ "@storybook/addon-interactions": "^8.6.0",
25
+ "@storybook/addon-links": "^8.6.0",
26
+ "@storybook/blocks": "^8.6.0",
27
+ "@storybook/react-vite": "^8.6.0",
28
+ "@storybook/test": "^8.6.0",
29
+ "@storybook/test-runner": "^0.21.3",
30
30
  "@swc/cli": "^0.6.0",
31
31
  "@swc/core": "^1.4.11",
32
32
  "@tailwindcss/container-queries": "^0.1.1",
33
- "@types/dompurify": "^3.0.5",
34
33
  "@types/js-cookie": "^3.0.6",
35
34
  "@types/lodash.throttle": "^4.1.9",
36
35
  "@types/react-dom": "^18.3.0",
@@ -43,23 +42,23 @@
43
42
  "eslint": "^8.57.0",
44
43
  "eslint-config-prettier": "^10.0.1",
45
44
  "eslint-plugin-react": "^7.34.3",
46
- "eslint-plugin-storybook": "^0.11.2",
45
+ "eslint-plugin-storybook": "^0.11.3",
47
46
  "heroicons": "^2.2.0",
48
47
  "http-server": "14.1.1",
49
48
  "mixpanel-browser": "^2.60.0",
50
- "msw": "2.7.0",
49
+ "msw": "2.7.1",
51
50
  "msw-storybook-addon": "^2.0.2",
52
51
  "playwright": "^1.49.1",
53
52
  "posthog-js": "^1.217.4",
54
53
  "prettier": "^3.2.5",
55
54
  "react-syntax-highlighter": "^15.6.1",
56
- "storybook": "^8.4.7",
55
+ "storybook": "^8.6.0",
57
56
  "storybook-dark-mode": "^4.0.2",
58
57
  "svg-sprite": "^2.0.4",
59
58
  "tailwindcss": "^3.3.6",
60
59
  "ts-node": "^10.9.2",
61
60
  "typescript": "5.7.3",
62
- "vite": "^6.0.0"
61
+ "vite": "^6.2.0"
63
62
  },
64
63
  "scripts": {
65
64
  "build:prebuild": "rm -rf core reset && mkdir -p dist/core",
@@ -78,8 +77,8 @@
78
77
  "start": "vite --port 5000",
79
78
  "storybook": "yarn build && storybook dev -p 6006",
80
79
  "build-storybook": "yarn build && storybook build --quiet -o preview",
81
- "test": "npx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook --test && yarn http-server preview --port 6007 --silent\" \"wait-on tcp:6007 && yarn test-storybook --url http://127.0.0.1:6007\"",
82
- "test:update-snapshots": "npx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook --test && yarn http-server preview --port 6007 --silent\" \"wait-on tcp:6007 && yarn test-storybook -u --url http://127.0.0.1:6007\""
80
+ "test": "npx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook && yarn http-server preview --port 6007 --silent\" \"wait-on tcp:6007 && yarn test-storybook --url http://127.0.0.1:6007\"",
81
+ "test:update-snapshots": "npx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook && yarn http-server preview --port 6007 --silent\" \"wait-on tcp:6007 && yarn test-storybook -u --url http://127.0.0.1:6007\""
83
82
  },
84
83
  "dependencies": {
85
84
  "@radix-ui/react-accordion": "^1.2.1",
@@ -89,7 +88,7 @@
89
88
  "addsearch-js-client": "^1.0.2",
90
89
  "array-flat-polyfill": "^1.0.1",
91
90
  "clsx": "^2.1.1",
92
- "dompurify": "^3.1.4",
91
+ "dompurify": "^3.2.4",
93
92
  "highlight.js": "^11.9.0",
94
93
  "highlightjs-curl": "^1.3.0",
95
94
  "js-cookie": "^3.0.5",
@@ -1 +0,0 @@
1
- ["bg-blue-400","bg-blue-100","bg-neutral-1300","bg-neutral-300","bg-neutral-200","bg-neutral-100","bg-neutral-000","bg-neutral-600","bg-orange-900","bg-orange-600","border-blue-400","border-neutral-200","border-neutral-600","border-neutral-500","border-orange-600","from-neutral-400","group-hover:bg-neutral-100","text-blue-600","text-blue-200","text-neutral-1300","text-neutral-300","text-neutral-000","text-neutral-1100","text-neutral-1000","text-neutral-800","text-neutral-700","text-neutral-600","text-neutral-500","text-orange-200","text-orange-600"]