@sikt/sds-tabs 1.0.0 → 2.0.0

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.
@@ -0,0 +1,2 @@
1
+ .sds-tabs{--tab-list-border-bottom:var(--sds-border-size-thin)}.sds-tabs__tab-list{border-bottom:var(--tab-list-border-bottom) solid var(--sds-color-background-action);display:flex;margin-bottom:var(--sds-size-base-xxs)}.sds-tabs__tab{align-items:center;border:0;border-bottom:var(--sds-border-size-bold) solid #0000;color:var(--sds-color-text-default);display:flex;font-size:var(--sds-size-text-s2);line-height:var(--sds-size-text-m);margin-bottom:calc(var(--tab-list-border-bottom)*-1);padding:var(--sds-size-base-m) var(--sds-size-base-s)}.sds-tabs__tab-icon{padding-right:var(--sds-size-base-s)}.sds-tabs__tab-badge{padding-left:var(--sds-size-base-s)}.sds-tabs__tab[aria-selected=true]{border-color:var(--sds-color-surface-action)}.sds-tabs__tab:active{border-color:var(--sds-color-surface-action-active)}.sds-tabs__tab:hover{border-color:var(--sds-color-surface-action-hover)}.sds-tabs__tab:focus-visible{border-color:#0000;outline:var(--sds-size-base-xxs) dashed var(--sds-color-text-info)}.sds-tabs__tab-panel:focus-visible{outline:var(--sds-size-base-xxs) dashed var(--sds-color-text-info)}.sds-tab-link{display:inline-flex;text-decoration:none}.sds-tab-link--selected{border-color:var(--sds-color-surface-action)}.sds-tab-link:focus-visible{outline-offset:0}
2
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["tabs.pcss","tab-link.pcss"],"names":[],"mappings":"AAAA,UACE,oDAmDF,CAjDE,oBACE,oFACoC,CACpC,YAAa,CACb,sCACF,CAEA,eACE,kBAAmB,CACnB,QAAc,CACd,qDAA4D,CAC5D,mCAAoC,CACpC,YAAa,CACb,iCAAkC,CAClC,kCAAmC,CACnC,oDAAuD,CACvD,qDA0BF,CAxBE,oBACE,oCACF,CAEA,qBACE,mCACF,CAEA,mCACE,4CACF,CAEA,sBACE,mDACF,CAEA,qBACE,kDACF,CAEA,6BACE,kBAAyB,CACzB,kEACF,CAIA,mCACE,kEACF,CClDJ,cACE,mBAAoB,CACpB,oBASF,CAPE,wBACE,4CACF,CAEA,4BACE,gBACF","file":"index.css","sourcesContent":[".sds-tabs {\n --tab-list-border-bottom: var(--sds-border-size-thin);\n\n &__tab-list {\n border-bottom: var(--tab-list-border-bottom) solid\n var(--sds-color-background-action);\n display: flex;\n margin-bottom: var(--sds-size-base-xxs);\n }\n\n &__tab {\n align-items: center;\n border: 0 none;\n border-bottom: var(--sds-border-size-bold) solid transparent;\n color: var(--sds-color-text-default);\n display: flex;\n font-size: var(--sds-size-text-s2);\n line-height: var(--sds-size-text-m);\n margin-bottom: calc(var(--tab-list-border-bottom) * -1);\n padding: var(--sds-size-base-m) var(--sds-size-base-s);\n\n &-icon {\n padding-right: var(--sds-size-base-s);\n }\n\n &-badge {\n padding-left: var(--sds-size-base-s);\n }\n\n &[aria-selected=\"true\"] {\n border-color: var(--sds-color-surface-action);\n }\n\n &:active {\n border-color: var(--sds-color-surface-action-active);\n }\n\n &:hover {\n border-color: var(--sds-color-surface-action-hover);\n }\n\n &:focus-visible {\n border-color: transparent;\n outline: var(--sds-size-base-xxs) dashed var(--sds-color-text-info);\n }\n }\n\n &__tab-panel {\n &:focus-visible {\n outline: var(--sds-size-base-xxs) dashed var(--sds-color-text-info);\n }\n }\n}\n",".sds-tab-link {\n display: inline-flex;\n text-decoration: none;\n\n &--selected {\n border-color: var(--sds-color-surface-action);\n }\n\n &:focus-visible {\n outline-offset: 0;\n }\n}\n"]}
@@ -0,0 +1,39 @@
1
+ /// <reference types="react" />
2
+ import React from "react";
3
+ import { ReactNode, HTMLAttributes, AnchorHTMLAttributes } from "react";
4
+ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
5
+ defaultIndex?: number;
6
+ isSelectOnFocus?: boolean;
7
+ onChange?: (index: number) => void;
8
+ children: ReactNode;
9
+ className?: string;
10
+ }
11
+ declare const Tabs: ({ defaultIndex, onChange, isSelectOnFocus, children, className, ...rest }: TabsProps) => JSX.Element;
12
+ interface TabListProps extends HTMLAttributes<HTMLDivElement> {
13
+ children: ReactNode;
14
+ "aria-label": string;
15
+ className?: string;
16
+ }
17
+ declare const TabList: ({ children, "aria-label": ariaLabel, className, ...rest }: TabListProps) => JSX.Element;
18
+ interface TabProps extends HTMLAttributes<HTMLButtonElement> {
19
+ children: ReactNode;
20
+ className?: string;
21
+ icon?: ReactNode;
22
+ badge?: ReactNode;
23
+ }
24
+ declare const Tab: ({ children, className, icon, badge, ...rest }: TabProps) => JSX.Element;
25
+ interface TabPanelProps {
26
+ children: ReactNode;
27
+ className?: string;
28
+ }
29
+ declare const TabPanel: ({ children, className, ...rest }: TabPanelProps) => JSX.Element;
30
+ interface TabLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
31
+ children: ReactNode;
32
+ className?: string;
33
+ icon?: ReactNode;
34
+ badge?: ReactNode;
35
+ isSelected?: boolean;
36
+ }
37
+ declare const TabLink: React.ForwardRefExoticComponent<TabLinkProps & React.RefAttributes<HTMLAnchorElement>>;
38
+ export type { TabsProps, TabListProps, TabProps, TabPanelProps, TabLinkProps };
39
+ export { Tabs, TabList, Tab, TabPanel, TabLink };
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("react/jsx-runtime"),s=require("react"),t=require("clsx");const a=s.createContext(null),n=s.forwardRef((({children:a,className:n,icon:l,badge:i,href:d,isSelected:r,...c},o)=>e.jsxs("a",{className:t("sds-tabs__tab","sds-tab-link",r&&"sds-tab-link--selected",n),href:d,ref:o,...c,children:[l&&e.jsx("div",{className:"sds-tabs__tab-icon",children:l}),a,i&&e.jsx("div",{className:"sds-tabs__tab-badge",children:r?s.isValidElement(i)&&s.cloneElement(i,{visibility:"high"}):i})]})));n.displayName="TabLink",exports.Tab=({children:n,className:l,icon:i,badge:d,...r})=>{const{index:c}=r,{id:o,count:b,isSelectOnFocus:u,selectedIndex:x,setSelectedIndex:h,setPreviousIndex:m}=s.useContext(a),v=c===x,f=e=>{u&&(m(x),h(e))};return e.jsxs("button",{className:t("sds-tabs__tab",l),role:"tab","aria-selected":v,"aria-controls":`panel-${o}-${c}`,id:`tab-${o}-${c}`,tabIndex:v?0:-1,onClick:()=>{m(x),h(c)},onKeyDown:e=>{var s,t;const a=b-1,n=e.currentTarget,l=null===(s=n.parentElement)||void 0===s?void 0:s.firstChild,i=null===(t=n.parentElement)||void 0===t?void 0:t.lastChild;if("ArrowLeft"===e.key){const e=n.previousSibling;c>0?e.focus():i.focus(),f(x>0?x-1:a)}else if("ArrowRight"===e.key){const e=n.nextSibling;c<a?e.focus():l.focus(),f(x<a?x+1:0)}else"Home"===e.key?(l.focus(),f(0)):"End"===e.key?(i.focus(),f(a)):"Enter"!==e.key&&"Space"!==e.code||h(c)},...r,children:[i&&e.jsx("div",{className:"sds-tabs__tab-icon",children:i}),n,d&&e.jsx("div",{className:"sds-tabs__tab-badge",children:v?s.isValidElement(d)&&s.cloneElement(d,{visibility:"high"}):d})]})},exports.TabLink=n,exports.TabList=({children:a,"aria-label":n,className:l,...i})=>{const d=s.Children.toArray(a);return e.jsx("div",{className:t("sds-tabs__tab-list",l),role:"tablist","aria-label":n,...i,children:s.Children.map(d,((t,a)=>{if(s.isValidElement(t))return e.jsx(e.Fragment,{children:s.cloneElement(t,{index:a})})}))})},exports.TabPanel=({children:n,className:l,...i})=>{const{index:d}=i,{id:r,selectedIndex:c}=s.useContext(a),o=d===c;return e.jsx("div",{className:t("sds-tabs__tab-panel",l),id:`panel-${r}-${d}`,role:"tabpanel",tabIndex:o?0:-1,"aria-labelledby":`tab-${r}-${d}`,hidden:!o,...i,children:n})},exports.Tabs=({defaultIndex:n=0,onChange:l,isSelectOnFocus:i=!1,children:d,className:r,...c})=>{const[o,b]=s.useState(n),[u,x]=s.useState(n),h=s.useId();s.useEffect((()=>{l&&u!==o&&l(o)}),[l,o]);const m=s.Children.toArray(d),v=m.length-1;return e.jsx(a.Provider,{value:{id:h,count:v,isSelectOnFocus:i,selectedIndex:o,setSelectedIndex:b,setPreviousIndex:x},children:e.jsx("div",{className:t("sds-tabs",r),...c,children:s.Children.map(m,((t,a)=>s.isValidElement(t)&&a>0?e.jsx(e.Fragment,{children:s.cloneElement(t,{index:a-1})}):t))})})};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../Tabs.tsx","../../TabLink.tsx","../../Tab.tsx","../../TabList.tsx","../../TabPanel.tsx"],"sourcesContent":["import React, {\n Children,\n ReactNode,\n cloneElement,\n useId,\n useState,\n isValidElement,\n createContext,\n ReactElement,\n useEffect,\n HTMLAttributes,\n} from \"react\";\nimport clsx from \"clsx\";\nimport \"./tabs.pcss\";\nimport { TabPanelProps } from \"./TabPanel\";\n\nexport type TabsContextType = {\n id: string;\n count: number;\n isSelectOnFocus: boolean;\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n setPreviousIndex: (index: number) => void;\n};\n\nexport const TabsContext = createContext<TabsContextType | null>(null);\n\nexport interface TabsProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n defaultIndex?: number;\n isSelectOnFocus?: boolean;\n onChange?: (index: number) => void;\n children: ReactNode;\n className?: string;\n}\n\nexport const Tabs = ({\n defaultIndex = 0,\n onChange,\n isSelectOnFocus = false,\n children,\n className,\n ...rest\n}: TabsProps) => {\n const [selectedIndex, setSelectedIndex] = useState(defaultIndex);\n const [previousIndex, setPreviousIndex] = useState(defaultIndex);\n const id = useId();\n\n useEffect(() => {\n if (onChange && previousIndex !== selectedIndex) {\n onChange(selectedIndex);\n }\n }, [onChange, selectedIndex]);\n\n const arrayChildren = Children.toArray(children);\n const count = arrayChildren.length - 1;\n\n return (\n <TabsContext.Provider\n value={{\n id,\n count,\n isSelectOnFocus,\n selectedIndex,\n setSelectedIndex,\n setPreviousIndex,\n }}\n >\n <div className={clsx(\"sds-tabs\", className)} {...rest}>\n {Children.map(arrayChildren, (child, index) => {\n if (isValidElement(child) && index > 0) {\n return (\n <>\n {cloneElement(\n child as ReactElement<TabPanelProps & { index: number }>,\n {\n index: index - 1,\n }\n )}\n </>\n );\n } else {\n return child;\n }\n })}\n </div>\n </TabsContext.Provider>\n );\n};\n","import React, {\n AnchorHTMLAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport clsx from \"clsx\";\nimport \"./tab-link.pcss\";\n\nexport interface TabLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n children: ReactNode;\n className?: string;\n icon?: ReactNode;\n badge?: ReactNode;\n isSelected?: boolean;\n}\n\nexport const TabLink = forwardRef<HTMLAnchorElement, TabLinkProps>(\n (\n {\n children,\n className,\n icon,\n badge,\n href,\n isSelected,\n ...rest\n }: TabLinkProps,\n ref\n ) => {\n return (\n <a\n className={clsx(\n \"sds-tabs__tab\",\n \"sds-tab-link\",\n isSelected && \"sds-tab-link--selected\",\n className\n )}\n href={href}\n ref={ref}\n {...rest}\n >\n {icon && <div className=\"sds-tabs__tab-icon\">{icon}</div>}\n {children}\n {badge && (\n <div className=\"sds-tabs__tab-badge\">\n {isSelected\n ? isValidElement(badge) &&\n cloneElement(badge as ReactElement, { visibility: \"high\" })\n : badge}\n </div>\n )}\n </a>\n );\n }\n);\n\nTabLink.displayName = \"TabLink\";\n","import React, {\n cloneElement,\n isValidElement,\n HTMLAttributes,\n KeyboardEvent,\n ReactElement,\n ReactNode,\n useContext,\n} from \"react\";\nimport clsx from \"clsx\";\nimport { TabsContext, TabsContextType } from \"./Tabs\";\n\nexport interface TabProps extends HTMLAttributes<HTMLButtonElement> {\n children: ReactNode;\n className?: string;\n icon?: ReactNode;\n badge?: ReactNode;\n}\n\nexport const Tab = ({\n children,\n className,\n icon,\n badge,\n ...rest\n}: TabProps) => {\n const { index } = rest as { index: number };\n const {\n id,\n count,\n isSelectOnFocus,\n selectedIndex,\n setSelectedIndex,\n setPreviousIndex,\n } = useContext(TabsContext) as TabsContextType;\n const isSelected = index === selectedIndex;\n const handleSelect = (index: number) => {\n if (isSelectOnFocus) {\n setPreviousIndex(selectedIndex);\n setSelectedIndex(index);\n }\n };\n const handleKeyPress = (event: KeyboardEvent<HTMLButtonElement>) => {\n const firstIndex = 0;\n const lastIndex = count - 1;\n const currentTarget = event.currentTarget;\n const firstChild = currentTarget.parentElement?.firstChild as HTMLElement;\n const lastChild = currentTarget.parentElement?.lastChild as HTMLElement;\n\n if (event.key === \"ArrowLeft\") {\n const previousSibling = currentTarget.previousSibling as HTMLElement;\n if (index > firstIndex) {\n previousSibling.focus();\n } else {\n lastChild.focus();\n }\n\n handleSelect(selectedIndex > firstIndex ? selectedIndex - 1 : lastIndex);\n } else if (event.key === \"ArrowRight\") {\n const nextSibling = currentTarget.nextSibling as HTMLElement;\n if (index < lastIndex) {\n nextSibling.focus();\n } else {\n firstChild.focus();\n }\n\n handleSelect(selectedIndex < lastIndex ? selectedIndex + 1 : firstIndex);\n } else if (event.key === \"Home\") {\n firstChild.focus();\n handleSelect(firstIndex);\n } else if (event.key === \"End\") {\n lastChild.focus();\n handleSelect(lastIndex);\n } else if (event.key === \"Enter\" || event.code === \"Space\") {\n setSelectedIndex(index);\n }\n };\n\n return (\n <button\n className={clsx(\"sds-tabs__tab\", className)}\n role=\"tab\"\n aria-selected={isSelected}\n aria-controls={`panel-${id}-${index}`}\n id={`tab-${id}-${index}`}\n tabIndex={isSelected ? 0 : -1}\n onClick={() => {\n setPreviousIndex(selectedIndex);\n setSelectedIndex(index);\n }}\n onKeyDown={handleKeyPress}\n {...rest}\n >\n {icon && <div className=\"sds-tabs__tab-icon\">{icon}</div>}\n {children}\n {badge && (\n <div className=\"sds-tabs__tab-badge\">\n {isSelected\n ? isValidElement(badge) &&\n cloneElement(badge as ReactElement, { visibility: \"high\" })\n : badge}\n </div>\n )}\n </button>\n );\n};\n","import React, {\n Children,\n ReactNode,\n ReactElement,\n cloneElement,\n isValidElement,\n HTMLAttributes,\n} from \"react\";\nimport clsx from \"clsx\";\nimport { TabProps } from \"./Tab\";\n\nexport interface TabListProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n \"aria-label\": string;\n className?: string;\n}\n\nexport const TabList = ({\n children,\n \"aria-label\": ariaLabel,\n className,\n ...rest\n}: TabListProps) => {\n const arrayChildren = Children.toArray(children);\n\n return (\n <div\n className={clsx(\"sds-tabs__tab-list\", className)}\n role=\"tablist\"\n aria-label={ariaLabel}\n {...rest}\n >\n {Children.map(arrayChildren, (child, index) => {\n if (isValidElement(child)) {\n return (\n <>\n {cloneElement(\n child as ReactElement<TabProps & { index: number }>,\n {\n index,\n }\n )}\n </>\n );\n }\n })}\n </div>\n );\n};\n","import React, { ReactNode, useContext } from \"react\";\nimport clsx from \"clsx\";\nimport { TabsContext, TabsContextType } from \"./Tabs\";\n\nexport interface TabPanelProps {\n children: ReactNode;\n className?: string;\n}\n\nexport const TabPanel = ({ children, className, ...rest }: TabPanelProps) => {\n const { index } = rest as { index: number };\n const { id, selectedIndex } = useContext(TabsContext) as TabsContextType;\n const isSelected = index === selectedIndex;\n return (\n <div\n className={clsx(\"sds-tabs__tab-panel\", className)}\n id={`panel-${id}-${index}`}\n role=\"tabpanel\"\n tabIndex={isSelected ? 0 : -1}\n aria-labelledby={`tab-${id}-${index}`}\n hidden={!isSelected}\n {...rest}\n >\n {children}\n </div>\n );\n};\n"],"names":["TabsContext","createContext","TabLink","forwardRef","children","className","icon","badge","href","isSelected","rest","ref","_jsxs","clsx","_jsx","isValidElement","cloneElement","visibility","displayName","index","id","count","isSelectOnFocus","selectedIndex","setSelectedIndex","setPreviousIndex","useContext","handleSelect","role","tabIndex","onClick","onKeyDown","event","lastIndex","currentTarget","firstChild","_a","parentElement","lastChild","_b","key","previousSibling","focus","nextSibling","code","jsx","ariaLabel","arrayChildren","Children","toArray","map","child","_Fragment","Fragment","hidden","defaultIndex","onChange","useState","previousIndex","useId","useEffect","length","Provider","value"],"mappings":"qFAyBO,MAAMA,EAAcC,EAAAA,cAAsC,MCNpDC,EAAUC,EAAUA,YAC/B,EAEIC,WACAC,YACAC,OACAC,QACAC,OACAC,gBACGC,GAELC,IAGEC,EAAAA,UACEP,UAAWQ,EACT,gBACA,eACAJ,GAAc,yBACdJ,GAEFG,KAAMA,EACNG,IAAKA,KACDD,YAEHJ,GAAQQ,aAAKT,UAAU,qBAAoBD,SAAEE,IAC7CF,EACAG,GACCO,EAAAA,WAAKT,UAAU,sBAAqBD,SACjCK,EACGM,EAAAA,eAAeR,IACfS,EAAAA,aAAaT,EAAuB,CAAEU,WAAY,SAClDV,SAQhBL,EAAQgB,YAAc,sBCxCH,EACjBd,WACAC,YACAC,OACAC,WACGG,MAEH,MAAMS,MAAEA,GAAUT,GACZU,GACJA,EAAEC,MACFA,EAAKC,gBACLA,EAAeC,cACfA,EAAaC,iBACbA,EAAgBC,iBAChBA,GACEC,aAAW1B,GACTS,EAAaU,IAAUI,EACvBI,EAAgBR,IAChBG,IACFG,EAAiBF,GACjBC,EAAiBL,GAClB,EAsCH,OACEP,EAAAA,eACEP,UAAWQ,EAAK,gBAAiBR,GACjCuB,KAAK,MACU,gBAAAnB,kBACA,SAASW,KAAMD,IAC9BC,GAAI,OAAOA,KAAMD,IACjBU,SAAUpB,EAAa,GAAK,EAC5BqB,QAAS,KACPL,EAAiBF,GACjBC,EAAiBL,EAAM,EAEzBY,UAhDoBC,YACtB,MACMC,EAAYZ,EAAQ,EACpBa,EAAgBF,EAAME,cACtBC,EAAwC,QAA3BC,EAAAF,EAAcG,qBAAa,IAAAD,OAAA,EAAAA,EAAED,WAC1CG,EAAuC,QAA3BC,EAAAL,EAAcG,qBAAa,IAAAE,OAAA,EAAAA,EAAED,UAE/C,GAAkB,cAAdN,EAAMQ,IAAqB,CAC7B,MAAMC,EAAkBP,EAAcO,gBAClCtB,EARa,EASfsB,EAAgBC,QAEhBJ,EAAUI,QAGZf,EAAaJ,EAdI,EAcyBA,EAAgB,EAAIU,EAC/D,MAAM,GAAkB,eAAdD,EAAMQ,IAAsB,CACrC,MAAMG,EAAcT,EAAcS,YAC9BxB,EAAQc,EACVU,EAAYD,QAEZP,EAAWO,QAGbf,EAAaJ,EAAgBU,EAAYV,EAAgB,EAvBxC,EAwBlB,KAAwB,SAAdS,EAAMQ,KACfL,EAAWO,QACXf,EA1BiB,IA2BM,QAAdK,EAAMQ,KACfF,EAAUI,QACVf,EAAaM,IACU,UAAdD,EAAMQ,KAAkC,UAAfR,EAAMY,MACxCpB,EAAiBL,EAClB,KAgBKT,EAAIN,SAAA,CAEPE,GAAQQ,EAAK+B,IAAA,MAAA,CAAAxC,UAAU,qBAAsBD,SAAAE,IAC7CF,EACAG,GACCO,EAAAA,IAAA,MAAA,CAAKT,UAAU,sBAAqBD,SACjCK,EACGM,EAAAA,eAAeR,IACfS,EAAAA,aAAaT,EAAuB,CAAEU,WAAY,SAClDV,MAIV,oCCvFmB,EACrBH,WACA,aAAc0C,EACdzC,eACGK,MAEH,MAAMqC,EAAgBC,EAAAA,SAASC,QAAQ7C,GAEvC,OACEU,EAAA+B,IAAA,MAAA,CACExC,UAAWQ,EAAK,qBAAsBR,GACtCuB,KAAK,UAAS,aACFkB,KACRpC,EAAIN,SAEP4C,EAAAA,SAASE,IAAIH,GAAe,CAACI,EAAOhC,KACnC,GAAIJ,EAAAA,eAAeoC,GACjB,OACErC,EAAA+B,IAAAO,EAAAC,SAAA,CAAAjD,SACGY,EAAAA,aACCmC,EACA,CACEhC,WAKT,KAGL,mBCtCoB,EAAGf,WAAUC,eAAcK,MACjD,MAAMS,MAAEA,GAAUT,GACZU,GAAEA,EAAEG,cAAEA,GAAkBG,EAAUA,WAAC1B,GACnCS,EAAaU,IAAUI,EAC7B,OACET,EAAAA,IACE,MAAA,CAAAT,UAAWQ,EAAK,sBAAuBR,GACvCe,GAAI,SAASA,KAAMD,IACnBS,KAAK,WACLC,SAAUpB,EAAa,GAAK,EAAC,kBACZ,OAAOW,KAAMD,IAC9BmC,QAAS7C,KACLC,EAEHN,SAAAA,GAEH,eJWgB,EAClBmD,eAAe,EACfC,WACAlC,mBAAkB,EAClBlB,WACAC,eACGK,MAEH,MAAOa,EAAeC,GAAoBiC,EAAQA,SAACF,IAC5CG,EAAejC,GAAoBgC,EAAQA,SAACF,GAC7CnC,EAAKuC,EAAAA,QAEXC,EAAAA,WAAU,KACJJ,GAAYE,IAAkBnC,GAChCiC,EAASjC,EACV,GACA,CAACiC,EAAUjC,IAEd,MAAMwB,EAAgBC,EAAAA,SAASC,QAAQ7C,GACjCiB,EAAQ0B,EAAcc,OAAS,EAErC,OACE/C,MAACd,EAAY8D,SAAQ,CACnBC,MAAO,CACL3C,KACAC,QACAC,kBACAC,gBACAC,mBACAC,oBAGFrB,SAAAU,EAAAA,IAAA,MAAA,CAAKT,UAAWQ,EAAK,WAAYR,MAAgBK,WAC9CsC,WAASE,IAAIH,GAAe,CAACI,EAAOhC,IAC/BJ,iBAAeoC,IAAUhC,EAAQ,EAEjCL,EAAA+B,IAAAO,EAAAC,SAAA,CAAAjD,SACGY,EAAAA,aACCmC,EACA,CACEhC,MAAOA,EAAQ,MAMhBgC,OAKf"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference types="react" />
2
+ import React from "react";
2
3
  import { ReactNode, HTMLAttributes, AnchorHTMLAttributes } from "react";
3
4
  interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
4
5
  defaultIndex?: number;
@@ -33,6 +34,6 @@ interface TabLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
33
34
  badge?: ReactNode;
34
35
  isSelected?: boolean;
35
36
  }
36
- declare const TabLink: ({ children, className, icon, badge, href, isSelected, ...rest }: TabLinkProps) => JSX.Element;
37
+ declare const TabLink: React.ForwardRefExoticComponent<TabLinkProps & React.RefAttributes<HTMLAnchorElement>>;
37
38
  export type { TabsProps, TabListProps, TabProps, TabPanelProps, TabLinkProps };
38
39
  export { Tabs, TabList, Tab, TabPanel, TabLink };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{jsx as e,Fragment as s,jsxs as a}from"react/jsx-runtime";import{createContext as t,useState as i,useId as l,useEffect as n,Children as d,isValidElement as c,cloneElement as r,useContext as o}from"react";import b from"clsx";const h=t(null),u=({defaultIndex:a=0,onChange:t,isSelectOnFocus:o=!1,children:u,className:m,...v})=>{const[x,f]=i(a),[_,p]=i(a),N=l();n((()=>{t&&_!==x&&t(x)}),[t,x]);const g=d.toArray(u),y=g.length-1;return e(h.Provider,{value:{id:N,count:y,isSelectOnFocus:o,selectedIndex:x,setSelectedIndex:f,setPreviousIndex:p},children:e("div",{className:b("sds-tabs",m),...v,children:d.map(g,((a,t)=>c(a)&&t>0?e(s,{children:r(a,{index:t-1})}):a))})})},m=({children:a,"aria-label":t,className:i,...l})=>{const n=d.toArray(a);return e("div",{className:b("sds-tabs__tab-list",i),role:"tablist","aria-label":t,...l,children:d.map(n,((a,t)=>{if(c(a))return e(s,{children:r(a,{index:t})})}))})},v=({children:s,className:t,icon:i,badge:l,...n})=>{const{index:d}=n,{id:u,count:m,isSelectOnFocus:v,selectedIndex:x,setSelectedIndex:f,setPreviousIndex:_}=o(h),p=d===x,N=e=>{v&&(_(x),f(e))};return a("button",{className:b("sds-tabs__tab",t),role:"tab","aria-selected":p,"aria-controls":`panel-${u}-${d}`,id:`tab-${u}-${d}`,tabIndex:p?0:-1,onClick:()=>{_(x),f(d)},onKeyDown:e=>{var s,a;const t=m-1,i=e.currentTarget,l=null===(s=i.parentElement)||void 0===s?void 0:s.firstChild,n=null===(a=i.parentElement)||void 0===a?void 0:a.lastChild;if("ArrowLeft"===e.key){const e=i.previousSibling;d>0?e.focus():n.focus(),N(x>0?x-1:t)}else if("ArrowRight"===e.key){const e=i.nextSibling;d<t?e.focus():l.focus(),N(x<t?x+1:0)}else"Home"===e.key?(l.focus(),N(0)):"End"===e.key?(n.focus(),N(t)):"Enter"!==e.key&&"Space"!==e.code||f(d)},...n,children:[i&&e("div",{className:"sds-tabs__tab-icon",children:i}),s,l&&e("div",{className:"sds-tabs__tab-badge",children:p?c(l)&&r(l,{visibility:"high"}):l})]})},x=({children:s,className:a,...t})=>{const{index:i}=t,{id:l,selectedIndex:n}=o(h),d=i===n;return e("div",{className:b("sds-tabs__tab-panel",a),id:`panel-${l}-${i}`,role:"tabpanel",tabIndex:d?0:-1,"aria-labelledby":`tab-${l}-${i}`,hidden:!d,...t,children:s})},f=({children:s,className:t,icon:i,badge:l,href:n,isSelected:d,...o})=>a("a",{className:b("sds-tabs__tab","sds-tab-link",d&&"sds-tab-link--selected",t),href:n,...o,children:[i&&e("div",{className:"sds-tabs__tab-icon",children:i}),s,l&&e("div",{className:"sds-tabs__tab-badge",children:d?c(l)&&r(l,{visibility:"high"}):l})]});export{v as Tab,f as TabLink,m as TabList,x as TabPanel,u as Tabs};
1
+ import{jsx as e,Fragment as s,jsxs as a}from"react/jsx-runtime";import{createContext as i,useState as t,useId as l,useEffect as n,Children as d,isValidElement as c,cloneElement as r,useContext as o,forwardRef as b}from"react";import h from"clsx";const m=i(null),u=({defaultIndex:a=0,onChange:i,isSelectOnFocus:o=!1,children:b,className:u,...v})=>{const[f,x]=t(a),[_,p]=t(a),N=l();n((()=>{i&&_!==f&&i(f)}),[i,f]);const g=d.toArray(b),y=g.length-1;return e(m.Provider,{value:{id:N,count:y,isSelectOnFocus:o,selectedIndex:f,setSelectedIndex:x,setPreviousIndex:p},children:e("div",{className:h("sds-tabs",u),...v,children:d.map(g,((a,i)=>c(a)&&i>0?e(s,{children:r(a,{index:i-1})}):a))})})},v=({children:a,"aria-label":i,className:t,...l})=>{const n=d.toArray(a);return e("div",{className:h("sds-tabs__tab-list",t),role:"tablist","aria-label":i,...l,children:d.map(n,((a,i)=>{if(c(a))return e(s,{children:r(a,{index:i})})}))})},f=({children:s,className:i,icon:t,badge:l,...n})=>{const{index:d}=n,{id:b,count:u,isSelectOnFocus:v,selectedIndex:f,setSelectedIndex:x,setPreviousIndex:_}=o(m),p=d===f,N=e=>{v&&(_(f),x(e))};return a("button",{className:h("sds-tabs__tab",i),role:"tab","aria-selected":p,"aria-controls":`panel-${b}-${d}`,id:`tab-${b}-${d}`,tabIndex:p?0:-1,onClick:()=>{_(f),x(d)},onKeyDown:e=>{var s,a;const i=u-1,t=e.currentTarget,l=null===(s=t.parentElement)||void 0===s?void 0:s.firstChild,n=null===(a=t.parentElement)||void 0===a?void 0:a.lastChild;if("ArrowLeft"===e.key){const e=t.previousSibling;d>0?e.focus():n.focus(),N(f>0?f-1:i)}else if("ArrowRight"===e.key){const e=t.nextSibling;d<i?e.focus():l.focus(),N(f<i?f+1:0)}else"Home"===e.key?(l.focus(),N(0)):"End"===e.key?(n.focus(),N(i)):"Enter"!==e.key&&"Space"!==e.code||x(d)},...n,children:[t&&e("div",{className:"sds-tabs__tab-icon",children:t}),s,l&&e("div",{className:"sds-tabs__tab-badge",children:p?c(l)&&r(l,{visibility:"high"}):l})]})},x=({children:s,className:a,...i})=>{const{index:t}=i,{id:l,selectedIndex:n}=o(m),d=t===n;return e("div",{className:h("sds-tabs__tab-panel",a),id:`panel-${l}-${t}`,role:"tabpanel",tabIndex:d?0:-1,"aria-labelledby":`tab-${l}-${t}`,hidden:!d,...i,children:s})},_=b((({children:s,className:i,icon:t,badge:l,href:n,isSelected:d,...o},b)=>a("a",{className:h("sds-tabs__tab","sds-tab-link",d&&"sds-tab-link--selected",i),href:n,ref:b,...o,children:[t&&e("div",{className:"sds-tabs__tab-icon",children:t}),s,l&&e("div",{className:"sds-tabs__tab-badge",children:d?c(l)&&r(l,{visibility:"high"}):l})]})));_.displayName="TabLink";export{f as Tab,_ as TabLink,v as TabList,x as TabPanel,u as Tabs};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../Tabs.tsx","../TabList.tsx","../Tab.tsx","../TabPanel.tsx","../TabLink.tsx"],"sourcesContent":["import React, {\n Children,\n ReactNode,\n cloneElement,\n useId,\n useState,\n isValidElement,\n createContext,\n ReactElement,\n useEffect,\n HTMLAttributes,\n} from \"react\";\nimport clsx from \"clsx\";\nimport \"./tabs.pcss\";\nimport { TabPanelProps } from \"./TabPanel\";\n\nexport type TabsContextType = {\n id: string;\n count: number;\n isSelectOnFocus: boolean;\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n setPreviousIndex: (index: number) => void;\n};\n\nexport const TabsContext = createContext<TabsContextType | null>(null);\n\nexport interface TabsProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n defaultIndex?: number;\n isSelectOnFocus?: boolean;\n onChange?: (index: number) => void;\n children: ReactNode;\n className?: string;\n}\n\nexport const Tabs = ({\n defaultIndex = 0,\n onChange,\n isSelectOnFocus = false,\n children,\n className,\n ...rest\n}: TabsProps) => {\n const [selectedIndex, setSelectedIndex] = useState(defaultIndex);\n const [previousIndex, setPreviousIndex] = useState(defaultIndex);\n const id = useId();\n\n useEffect(() => {\n if (onChange && previousIndex !== selectedIndex) {\n onChange(selectedIndex);\n }\n }, [onChange, selectedIndex]);\n\n const arrayChildren = Children.toArray(children);\n const count = arrayChildren.length - 1;\n\n return (\n <TabsContext.Provider\n value={{\n id,\n count,\n isSelectOnFocus,\n selectedIndex,\n setSelectedIndex,\n setPreviousIndex,\n }}\n >\n <div className={clsx(\"sds-tabs\", className)} {...rest}>\n {Children.map(arrayChildren, (child, index) => {\n if (isValidElement(child) && index > 0) {\n return (\n <>\n {cloneElement(\n child as ReactElement<TabPanelProps & { index: number }>,\n {\n index: index - 1,\n }\n )}\n </>\n );\n } else {\n return child;\n }\n })}\n </div>\n </TabsContext.Provider>\n );\n};\n","import React, {\n Children,\n ReactNode,\n ReactElement,\n cloneElement,\n isValidElement,\n HTMLAttributes,\n} from \"react\";\nimport clsx from \"clsx\";\nimport { TabProps } from \"./Tab\";\n\nexport interface TabListProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n \"aria-label\": string;\n className?: string;\n}\n\nexport const TabList = ({\n children,\n \"aria-label\": ariaLabel,\n className,\n ...rest\n}: TabListProps) => {\n const arrayChildren = Children.toArray(children);\n\n return (\n <div\n className={clsx(\"sds-tabs__tab-list\", className)}\n role=\"tablist\"\n aria-label={ariaLabel}\n {...rest}\n >\n {Children.map(arrayChildren, (child, index) => {\n if (isValidElement(child)) {\n return (\n <>\n {cloneElement(\n child as ReactElement<TabProps & { index: number }>,\n {\n index,\n }\n )}\n </>\n );\n }\n })}\n </div>\n );\n};\n","import React, {\n cloneElement,\n isValidElement,\n HTMLAttributes,\n KeyboardEvent,\n ReactElement,\n ReactNode,\n useContext,\n} from \"react\";\nimport clsx from \"clsx\";\nimport { TabsContext, TabsContextType } from \"./Tabs\";\n\nexport interface TabProps extends HTMLAttributes<HTMLButtonElement> {\n children: ReactNode;\n className?: string;\n icon?: ReactNode;\n badge?: ReactNode;\n}\n\nexport const Tab = ({\n children,\n className,\n icon,\n badge,\n ...rest\n}: TabProps) => {\n const { index } = rest as { index: number };\n const {\n id,\n count,\n isSelectOnFocus,\n selectedIndex,\n setSelectedIndex,\n setPreviousIndex,\n } = useContext(TabsContext) as TabsContextType;\n const isSelected = index === selectedIndex;\n const handleSelect = (index: number) => {\n if (isSelectOnFocus) {\n setPreviousIndex(selectedIndex);\n setSelectedIndex(index);\n }\n };\n const handleKeyPress = (event: KeyboardEvent<HTMLButtonElement>) => {\n const firstIndex = 0;\n const lastIndex = count - 1;\n const currentTarget = event.currentTarget;\n const firstChild = currentTarget.parentElement?.firstChild as HTMLElement;\n const lastChild = currentTarget.parentElement?.lastChild as HTMLElement;\n\n if (event.key === \"ArrowLeft\") {\n const previousSibling = currentTarget.previousSibling as HTMLElement;\n if (index > firstIndex) {\n previousSibling.focus();\n } else {\n lastChild.focus();\n }\n\n handleSelect(selectedIndex > firstIndex ? selectedIndex - 1 : lastIndex);\n } else if (event.key === \"ArrowRight\") {\n const nextSibling = currentTarget.nextSibling as HTMLElement;\n if (index < lastIndex) {\n nextSibling.focus();\n } else {\n firstChild.focus();\n }\n\n handleSelect(selectedIndex < lastIndex ? selectedIndex + 1 : firstIndex);\n } else if (event.key === \"Home\") {\n firstChild.focus();\n handleSelect(firstIndex);\n } else if (event.key === \"End\") {\n lastChild.focus();\n handleSelect(lastIndex);\n } else if (event.key === \"Enter\" || event.code === \"Space\") {\n setSelectedIndex(index);\n }\n };\n\n return (\n <button\n className={clsx(\"sds-tabs__tab\", className)}\n role=\"tab\"\n aria-selected={isSelected}\n aria-controls={`panel-${id}-${index}`}\n id={`tab-${id}-${index}`}\n tabIndex={isSelected ? 0 : -1}\n onClick={() => {\n setPreviousIndex(selectedIndex);\n setSelectedIndex(index);\n }}\n onKeyDown={handleKeyPress}\n {...rest}\n >\n {icon && <div className=\"sds-tabs__tab-icon\">{icon}</div>}\n {children}\n {badge && (\n <div className=\"sds-tabs__tab-badge\">\n {isSelected\n ? isValidElement(badge) &&\n cloneElement(badge as ReactElement, { visibility: \"high\" })\n : badge}\n </div>\n )}\n </button>\n );\n};\n","import React, { ReactNode, useContext } from \"react\";\nimport clsx from \"clsx\";\nimport { TabsContext, TabsContextType } from \"./Tabs\";\n\nexport interface TabPanelProps {\n children: ReactNode;\n className?: string;\n}\n\nexport const TabPanel = ({ children, className, ...rest }: TabPanelProps) => {\n const { index } = rest as { index: number };\n const { id, selectedIndex } = useContext(TabsContext) as TabsContextType;\n const isSelected = index === selectedIndex;\n return (\n <div\n className={clsx(\"sds-tabs__tab-panel\", className)}\n id={`panel-${id}-${index}`}\n role=\"tabpanel\"\n tabIndex={isSelected ? 0 : -1}\n aria-labelledby={`tab-${id}-${index}`}\n hidden={!isSelected}\n {...rest}\n >\n {children}\n </div>\n );\n};\n","import React, {\n AnchorHTMLAttributes,\n cloneElement,\n isValidElement,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport clsx from \"clsx\";\nimport \"./tab-link.pcss\";\n\nexport interface TabLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n children: ReactNode;\n className?: string;\n icon?: ReactNode;\n badge?: ReactNode;\n isSelected?: boolean;\n}\n\nexport const TabLink = ({\n children,\n className,\n icon,\n badge,\n href,\n isSelected,\n ...rest\n}: TabLinkProps) => {\n return (\n <a\n className={clsx(\n \"sds-tabs__tab\",\n \"sds-tab-link\",\n isSelected && \"sds-tab-link--selected\",\n className\n )}\n href={href}\n {...rest}\n >\n {icon && <div className=\"sds-tabs__tab-icon\">{icon}</div>}\n {children}\n {badge && (\n <div className=\"sds-tabs__tab-badge\">\n {isSelected\n ? isValidElement(badge) &&\n cloneElement(badge as ReactElement, { visibility: \"high\" })\n : badge}\n </div>\n )}\n </a>\n );\n};\n"],"names":["TabsContext","createContext","Tabs","defaultIndex","onChange","isSelectOnFocus","children","className","rest","selectedIndex","setSelectedIndex","useState","previousIndex","setPreviousIndex","id","useId","useEffect","arrayChildren","Children","toArray","count","length","_jsx","Provider","value","clsx","map","child","index","isValidElement","_Fragment","cloneElement","TabList","ariaLabel","role","Tab","icon","badge","useContext","isSelected","handleSelect","_jsxs","tabIndex","onClick","onKeyDown","event","lastIndex","currentTarget","firstChild","_a","parentElement","lastChild","_b","key","previousSibling","focus","nextSibling","code","visibility","TabPanel","hidden","TabLink","href"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../Tabs.tsx","../TabList.tsx","../Tab.tsx","../TabPanel.tsx","../TabLink.tsx"],"sourcesContent":["import React, {\n Children,\n ReactNode,\n cloneElement,\n useId,\n useState,\n isValidElement,\n createContext,\n ReactElement,\n useEffect,\n HTMLAttributes,\n} from \"react\";\nimport clsx from \"clsx\";\nimport \"./tabs.pcss\";\nimport { TabPanelProps } from \"./TabPanel\";\n\nexport type TabsContextType = {\n id: string;\n count: number;\n isSelectOnFocus: boolean;\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n setPreviousIndex: (index: number) => void;\n};\n\nexport const TabsContext = createContext<TabsContextType | null>(null);\n\nexport interface TabsProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n defaultIndex?: number;\n isSelectOnFocus?: boolean;\n onChange?: (index: number) => void;\n children: ReactNode;\n className?: string;\n}\n\nexport const Tabs = ({\n defaultIndex = 0,\n onChange,\n isSelectOnFocus = false,\n children,\n className,\n ...rest\n}: TabsProps) => {\n const [selectedIndex, setSelectedIndex] = useState(defaultIndex);\n const [previousIndex, setPreviousIndex] = useState(defaultIndex);\n const id = useId();\n\n useEffect(() => {\n if (onChange && previousIndex !== selectedIndex) {\n onChange(selectedIndex);\n }\n }, [onChange, selectedIndex]);\n\n const arrayChildren = Children.toArray(children);\n const count = arrayChildren.length - 1;\n\n return (\n <TabsContext.Provider\n value={{\n id,\n count,\n isSelectOnFocus,\n selectedIndex,\n setSelectedIndex,\n setPreviousIndex,\n }}\n >\n <div className={clsx(\"sds-tabs\", className)} {...rest}>\n {Children.map(arrayChildren, (child, index) => {\n if (isValidElement(child) && index > 0) {\n return (\n <>\n {cloneElement(\n child as ReactElement<TabPanelProps & { index: number }>,\n {\n index: index - 1,\n }\n )}\n </>\n );\n } else {\n return child;\n }\n })}\n </div>\n </TabsContext.Provider>\n );\n};\n","import React, {\n Children,\n ReactNode,\n ReactElement,\n cloneElement,\n isValidElement,\n HTMLAttributes,\n} from \"react\";\nimport clsx from \"clsx\";\nimport { TabProps } from \"./Tab\";\n\nexport interface TabListProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n \"aria-label\": string;\n className?: string;\n}\n\nexport const TabList = ({\n children,\n \"aria-label\": ariaLabel,\n className,\n ...rest\n}: TabListProps) => {\n const arrayChildren = Children.toArray(children);\n\n return (\n <div\n className={clsx(\"sds-tabs__tab-list\", className)}\n role=\"tablist\"\n aria-label={ariaLabel}\n {...rest}\n >\n {Children.map(arrayChildren, (child, index) => {\n if (isValidElement(child)) {\n return (\n <>\n {cloneElement(\n child as ReactElement<TabProps & { index: number }>,\n {\n index,\n }\n )}\n </>\n );\n }\n })}\n </div>\n );\n};\n","import React, {\n cloneElement,\n isValidElement,\n HTMLAttributes,\n KeyboardEvent,\n ReactElement,\n ReactNode,\n useContext,\n} from \"react\";\nimport clsx from \"clsx\";\nimport { TabsContext, TabsContextType } from \"./Tabs\";\n\nexport interface TabProps extends HTMLAttributes<HTMLButtonElement> {\n children: ReactNode;\n className?: string;\n icon?: ReactNode;\n badge?: ReactNode;\n}\n\nexport const Tab = ({\n children,\n className,\n icon,\n badge,\n ...rest\n}: TabProps) => {\n const { index } = rest as { index: number };\n const {\n id,\n count,\n isSelectOnFocus,\n selectedIndex,\n setSelectedIndex,\n setPreviousIndex,\n } = useContext(TabsContext) as TabsContextType;\n const isSelected = index === selectedIndex;\n const handleSelect = (index: number) => {\n if (isSelectOnFocus) {\n setPreviousIndex(selectedIndex);\n setSelectedIndex(index);\n }\n };\n const handleKeyPress = (event: KeyboardEvent<HTMLButtonElement>) => {\n const firstIndex = 0;\n const lastIndex = count - 1;\n const currentTarget = event.currentTarget;\n const firstChild = currentTarget.parentElement?.firstChild as HTMLElement;\n const lastChild = currentTarget.parentElement?.lastChild as HTMLElement;\n\n if (event.key === \"ArrowLeft\") {\n const previousSibling = currentTarget.previousSibling as HTMLElement;\n if (index > firstIndex) {\n previousSibling.focus();\n } else {\n lastChild.focus();\n }\n\n handleSelect(selectedIndex > firstIndex ? selectedIndex - 1 : lastIndex);\n } else if (event.key === \"ArrowRight\") {\n const nextSibling = currentTarget.nextSibling as HTMLElement;\n if (index < lastIndex) {\n nextSibling.focus();\n } else {\n firstChild.focus();\n }\n\n handleSelect(selectedIndex < lastIndex ? selectedIndex + 1 : firstIndex);\n } else if (event.key === \"Home\") {\n firstChild.focus();\n handleSelect(firstIndex);\n } else if (event.key === \"End\") {\n lastChild.focus();\n handleSelect(lastIndex);\n } else if (event.key === \"Enter\" || event.code === \"Space\") {\n setSelectedIndex(index);\n }\n };\n\n return (\n <button\n className={clsx(\"sds-tabs__tab\", className)}\n role=\"tab\"\n aria-selected={isSelected}\n aria-controls={`panel-${id}-${index}`}\n id={`tab-${id}-${index}`}\n tabIndex={isSelected ? 0 : -1}\n onClick={() => {\n setPreviousIndex(selectedIndex);\n setSelectedIndex(index);\n }}\n onKeyDown={handleKeyPress}\n {...rest}\n >\n {icon && <div className=\"sds-tabs__tab-icon\">{icon}</div>}\n {children}\n {badge && (\n <div className=\"sds-tabs__tab-badge\">\n {isSelected\n ? isValidElement(badge) &&\n cloneElement(badge as ReactElement, { visibility: \"high\" })\n : badge}\n </div>\n )}\n </button>\n );\n};\n","import React, { ReactNode, useContext } from \"react\";\nimport clsx from \"clsx\";\nimport { TabsContext, TabsContextType } from \"./Tabs\";\n\nexport interface TabPanelProps {\n children: ReactNode;\n className?: string;\n}\n\nexport const TabPanel = ({ children, className, ...rest }: TabPanelProps) => {\n const { index } = rest as { index: number };\n const { id, selectedIndex } = useContext(TabsContext) as TabsContextType;\n const isSelected = index === selectedIndex;\n return (\n <div\n className={clsx(\"sds-tabs__tab-panel\", className)}\n id={`panel-${id}-${index}`}\n role=\"tabpanel\"\n tabIndex={isSelected ? 0 : -1}\n aria-labelledby={`tab-${id}-${index}`}\n hidden={!isSelected}\n {...rest}\n >\n {children}\n </div>\n );\n};\n","import React, {\n AnchorHTMLAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport clsx from \"clsx\";\nimport \"./tab-link.pcss\";\n\nexport interface TabLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n children: ReactNode;\n className?: string;\n icon?: ReactNode;\n badge?: ReactNode;\n isSelected?: boolean;\n}\n\nexport const TabLink = forwardRef<HTMLAnchorElement, TabLinkProps>(\n (\n {\n children,\n className,\n icon,\n badge,\n href,\n isSelected,\n ...rest\n }: TabLinkProps,\n ref\n ) => {\n return (\n <a\n className={clsx(\n \"sds-tabs__tab\",\n \"sds-tab-link\",\n isSelected && \"sds-tab-link--selected\",\n className\n )}\n href={href}\n ref={ref}\n {...rest}\n >\n {icon && <div className=\"sds-tabs__tab-icon\">{icon}</div>}\n {children}\n {badge && (\n <div className=\"sds-tabs__tab-badge\">\n {isSelected\n ? isValidElement(badge) &&\n cloneElement(badge as ReactElement, { visibility: \"high\" })\n : badge}\n </div>\n )}\n </a>\n );\n }\n);\n\nTabLink.displayName = \"TabLink\";\n"],"names":["TabsContext","createContext","Tabs","defaultIndex","onChange","isSelectOnFocus","children","className","rest","selectedIndex","setSelectedIndex","useState","previousIndex","setPreviousIndex","id","useId","useEffect","arrayChildren","Children","toArray","count","length","_jsx","Provider","value","clsx","map","child","index","isValidElement","_Fragment","cloneElement","TabList","ariaLabel","role","Tab","icon","badge","useContext","isSelected","handleSelect","_jsxs","tabIndex","onClick","onKeyDown","event","lastIndex","currentTarget","firstChild","_a","parentElement","lastChild","_b","key","previousSibling","focus","nextSibling","code","visibility","TabPanel","hidden","TabLink","forwardRef","href","ref","displayName"],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@sikt/sds-tabs",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "license": "UNLICENSED",
5
- "main": "dist/index.js",
5
+ "main": "dist/cjs/index.js",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "style": "dist/index.css",
@@ -13,7 +13,7 @@
13
13
  "build": "rollup -c ../../rollup.config.mjs"
14
14
  },
15
15
  "dependencies": {
16
- "@sikt/sds-core": "^1.0.0"
16
+ "@sikt/sds-core": "^1.0.1"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "@types/react": "^18.0.0",